winss
svscan.hpp
Go to the documentation of this file.
1 /*
2  * Copyright 2016-2017 Morgan Stanley
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #ifndef LIB_WINSS_SVSCAN_SVSCAN_HPP_
18 #define LIB_WINSS_SVSCAN_SVSCAN_HPP_
19 
20 #include <windows.h>
21 #include <filesystem>
22 #include <functional>
23 #include <utility>
24 #include <vector>
25 #include <string>
26 #include "easylogging/easylogging++.hpp"
27 #include "../windows_interface.hpp"
28 #include "../environment.hpp"
29 #include "../filesystem_interface.hpp"
30 #include "../not_owning_ptr.hpp"
31 #include "../wait_multiplexer.hpp"
32 #include "../path_mutex.hpp"
33 #include "../process.hpp"
34 #include "../utils.hpp"
35 #include "../handle_wrapper.hpp"
36 #include "../event_wrapper.hpp"
37 #include "../ctrl_handler.hpp"
38 #include "service.hpp"
39 
40 namespace fs = std::experimental::filesystem;
41 
42 namespace winss {
53 template<typename TService, typename TMutex, typename TProcess>
54 class SvScanTmpl {
55  protected:
58  fs::path scan_dir;
59  const DWORD rescan;
60  TMutex mutex;
61  bool exiting = false;
62  bool close_on_exit = true;
63  bool signals = false;
66  std::vector<TService> services;
71  void Init() {
72  if (mutex.HasLock()) {
73  return;
74  }
75 
76  if (!FILESYSTEM.ChangeDirectory(scan_dir)) {
77  LOG(ERROR)
78  << "The directory '"
79  << scan_dir
80  << "' does not exist.";
81  multiplexer->Stop(kFatalExitCode);
82  return;
83  }
84 
85  if (!mutex.Lock()) {
86  multiplexer->Stop(kMutexTaken);
87  return;
88  }
89 
90  FILESYSTEM.CreateDirectory(scan_dir / fs::path(kSvscanDir));
91 
92  ReadEnv();
93 
94  Scan(false);
95  }
96 
100  static void ReadEnv() {
101  auto env_dir = winss::EnvironmentDir(kEnvDir);
102  auto env = env_dir.ReadEnvSource();
103 
104  for (const auto& kv : env) {
105  if (kv.second.empty()) {
106  WINDOWS.SetEnvironmentVariable(
107  const_cast<char *>(kv.first.data()),
108  nullptr);
109  } else {
110  WINDOWS.SetEnvironmentVariable(
111  const_cast<char *>(kv.first.data()),
112  const_cast<char *>(kv.second.data()));
113  }
114  }
115  }
116 
126  void Check(const fs::path& service_dir) {
127  std::string name = service_dir.filename().string();
128 
129  /* Current, parent and hidden directories should be ignored */
130  if (name.empty() || name.front() == L'.') {
131  VLOG(4) << "Skipping directory " << name;
132  return;
133  }
134 
135  auto pred = [name](const TService& service) {
136  return service.GetName() == name;
137  };
138 
139  auto it = find_if(services.begin(), services.end(), pred);
140  if (it == services.end()) {
141  TService service(name);
142  VLOG(2) << "Found new service " << name;
143  service.Check();
144  services.push_back(std::move(service));
145  } else {
146  VLOG(3) << "Found existing service " << name;
147  it->Check();
148  }
149  }
150 
154  void Schedule() {
155  if (rescan > 0 && rescan != INFINITE) {
156  multiplexer->AddTimeoutCallback(rescan,
157  [this](winss::WaitMultiplexer&) {
158  this->Scan(true);
159  }, kTimeoutGroup);
160  }
161  }
162 
166  void Terminate() {
167  if (exiting) {
168  return;
169  }
170 
171  if (!signals) {
172  multiplexer->Stop(0);
173  return;
174  }
175 
176  /* Allow for other runs of the script. */
177  VLOG(5) << "Close event reset";
178  close_event.Reset();
179  multiplexer->AddTriggeredCallback(close_event.GetHandle(),
180  [this](winss::WaitMultiplexer& m, const winss::HandleWrapper& h) {
181  this->Terminate();
182  });
183 
184  fs::path svscan_dir = scan_dir / fs::path(kSvscanDir);
185  fs::path sigterm_file = svscan_dir / fs::path(kSigTermFile);
186 
187  std::string cmd = FILESYSTEM.Read(sigterm_file);
188  if (!cmd.empty()) {
189  VLOG(2) << "Starting SIGTERM process";
190 
191  std::string expanded =
193 
194  TProcess sigterm;
195  if (sigterm.Create(winss::ProcessParams{
196  expanded, false
197  })) {
198  return;
199  }
200  }
201 
202  LOG(WARNING) << "Unable to spawn .winss-svscan/SIGTERM";
203  }
204 
208  void Stop() {
209  if (exiting) {
210  return;
211  }
212 
213  multiplexer->RemoveTimeoutCallback(kTimeoutGroup);
214  exiting = true;
215  if (close_on_exit) {
216  CloseAllServices(true);
217  }
218 
219  fs::path svscan_dir = scan_dir / fs::path(kSvscanDir);
220  fs::path finish_file = svscan_dir / fs::path(kFinishFile);
221 
222  std::string cmd = FILESYSTEM.Read(finish_file);
223  if (!cmd.empty()) {
224  VLOG(2) << "Starting finish process";
225 
226  std::string expanded =
228 
229  auto finish = std::make_shared<TProcess>();
230  if (finish->Create(winss::ProcessParams{
231  expanded, false
232  })) {
233  multiplexer->AddTriggeredCallback(finish->GetHandle(),
234  [finish](winss::WaitMultiplexer&,
235  const winss::HandleWrapper&) {
236  VLOG(2)
237  << "Finished process exited with "
238  << finish->GetExitCode();
239  });
240  } else {
241  LOG(WARNING) << "Unable to spawn .winss-svscan/finish";
242  }
243  }
244  }
245 
246  public:
247  static const int kMutexTaken = 100;
248  static const int kFatalExitCode = 111;
249  static constexpr const char kMutexName[7] = "svscan";
251  static constexpr const char kTimeoutGroup[7] = "svscan";
253  static constexpr const char kSvscanDir[14] = ".winss-svscan";
254  static constexpr const char kFinishFile[7] = "finish";
256  static constexpr const char kSigTermFile[8] = "SIGTERM";
258  static constexpr const char kEnvDir[18] = ".winss-svscan\\env";
259 
270  const fs::path& scan_dir, DWORD rescan, bool signals,
271  winss::EventWrapper close_event) : multiplexer(multiplexer),
272  scan_dir(scan_dir), rescan(rescan), mutex(scan_dir, kMutexName),
273  signals(signals), close_event(close_event) {
274  multiplexer->AddInitCallback([this](winss::WaitMultiplexer&) {
275  this->Init();
276  });
277 
278  multiplexer->AddTriggeredCallback(close_event.GetHandle(),
279  [this](winss::WaitMultiplexer& m, const winss::HandleWrapper& h) {
280  this->Terminate();
281  });
282 
283  multiplexer->AddStopCallback(
284  [this, &close_event](winss::WaitMultiplexer& m) {
285  m.RemoveTriggeredCallback(close_event.GetHandle());
286  this->Stop();
287  });
288  }
289 
290  SvScanTmpl(const SvScanTmpl&) = delete;
291  SvScanTmpl(SvScanTmpl&&) = delete;
296  virtual void Scan(bool timeout) {
297  if (!mutex.HasLock() || exiting) {
298  return;
299  }
300 
301  if (!timeout) {
302  multiplexer->RemoveTimeoutCallback(kTimeoutGroup);
303  }
304 
305  VLOG(2) << "Scanning directory " << scan_dir;
306 
307  for (TService& service : services) {
308  service.Reset();
309  }
310 
311  for (auto dir : FILESYSTEM.GetDirectories(scan_dir)) {
312  Check(dir);
313  }
314 
315  Schedule();
316  }
317 
323  virtual void CloseAllServices(bool ignore_flagged) {
324  if (!mutex.HasLock()) {
325  return;
326  }
327 
328  VLOG(3) << "Closing all services (forced: " << ignore_flagged << ")";
329 
330  auto it = services.begin();
331  while (it != services.end()) {
332  bool flagged = it->Close(ignore_flagged);
333  if (!flagged) {
334  VLOG(2) << "Removing service " << it->GetName();
335  it = services.erase(it);
336  } else {
337  ++it;
338  }
339  }
340  }
341 
342 
348  virtual void Exit(bool close_services) {
349  close_on_exit = close_services;
350  multiplexer->Stop(0);
351  }
352 
353  SvScanTmpl& operator=(const SvScanTmpl&) = delete;
354  SvScanTmpl& operator=(SvScanTmpl&&) = delete;
355 };
356 
361 } // namespace winss
362 
363 #endif // LIB_WINSS_SVSCAN_SVSCAN_HPP_
The svscan template.
Definition: svscan.hpp:54
A wrapper for a Windows HANDLE.
Definition: handle_wrapper.hpp:39
Parameters to start a Windows process.
Definition: process.hpp:29
A directory where each file is an environment variable.
Definition: environment.hpp:58
fs::path scan_dir
The scan directory.
Definition: svscan.hpp:58
virtual void CloseAllServices(bool ignore_flagged)
Closes all the services.
Definition: svscan.hpp:323
#define WINDOWS
Definition: windows_interface.hpp:25
Wraps a windows event.
Definition: event_wrapper.hpp:27
SvScanTmpl< winss::Service, winss::PathMutex, winss::Process > SvScan
Concrete svscan implementation.
Definition: svscan.hpp:360
static constexpr const char kEnvDir[18]
Env directory.
Definition: svscan.hpp:258
static constexpr const char kFinishFile[7]
Finish file.
Definition: svscan.hpp:254
bool exiting
Exiting flag.
Definition: svscan.hpp:61
void Terminate()
Handles a signal to terminate.
Definition: svscan.hpp:166
TMutex mutex
The svscan global mutex.
Definition: svscan.hpp:60
virtual void AddStopCallback(Callback callback)
Add a stop callback.
Definition: wait_multiplexer.cpp:60
Definition: case_ignore.hpp:23
void Check(const fs::path &service_dir)
Checks the given service directory.
Definition: svscan.hpp:126
virtual void AddTimeoutCallback(DWORD timeout, Callback callback, std::string group="")
Add a timeout item which given the timeout period will call the callback if it is not removed before ...
Definition: wait_multiplexer.cpp:50
virtual void AddTriggeredCallback(const winss::HandleWrapper &handle, TriggeredCallback callback)
Add a triggered callback for when an event happens on the given handle.
Definition: wait_multiplexer.cpp:43
winss::EventWrapper close_event
Event when to stop.
Definition: svscan.hpp:64
static void ReadEnv()
Reads the env directory into the current environment.
Definition: svscan.hpp:100
bool Reset()
Resets the event.
Definition: event_wrapper.cpp:32
static const int kFatalExitCode
Something went wrong.
Definition: svscan.hpp:248
winss::HandleWrapper GetHandle() const
Gets a handle to the event.
Definition: event_wrapper.cpp:36
void Schedule()
Schedules the next scan of the scan directory.
Definition: svscan.hpp:154
static const int kMutexTaken
Scan dir in use error.
Definition: svscan.hpp:247
static constexpr const char kMutexName[7]
Mutex name.
Definition: svscan.hpp:249
static constexpr const char kTimeoutGroup[7]
The timeout group for the multiplexer.
Definition: svscan.hpp:251
void Stop()
Stops the svscan instance.
Definition: svscan.hpp:208
A HANDLE wait multiplexer.
Definition: wait_multiplexer.hpp:70
virtual void Stop(int code)
Stops the multiplexer with the given code if one has not already been set.
Definition: wait_multiplexer.cpp:189
void Init()
Initializes svscan.
Definition: svscan.hpp:71
virtual void AddInitCallback(Callback callback)
Add an initialization callback.
Definition: wait_multiplexer.cpp:37
static constexpr const char kSvscanDir[14]
The directory for svscan data.
Definition: svscan.hpp:253
SvScanTmpl & operator=(const SvScanTmpl &)=delete
No copy.
bool signals
Use handlers for signals.
Definition: svscan.hpp:63
bool close_on_exit
Option to close services on exit.
Definition: svscan.hpp:62
#define FILESYSTEM
Definition: filesystem_interface.hpp:26
const DWORD rescan
The directory scan period.
Definition: svscan.hpp:59
winss::NotOwningPtr< winss::WaitMultiplexer > multiplexer
The event multiplexer for svscan.
Definition: svscan.hpp:57
virtual void Exit(bool close_services)
Signals the scanner to exit.
Definition: svscan.hpp:348
virtual void Scan(bool timeout)
Does a scan of the scan directory.
Definition: svscan.hpp:296
virtual bool RemoveTriggeredCallback(const winss::HandleWrapper &handle)
Removes the triggered callback which matches the given handle.
Definition: wait_multiplexer.cpp:66
virtual bool RemoveTimeoutCallback(std::string group)
Removes the timeout call back for the given group.
Definition: wait_multiplexer.cpp:77
SvScanTmpl(winss::NotOwningPtr< winss::WaitMultiplexer > multiplexer, const fs::path &scan_dir, DWORD rescan, bool signals, winss::EventWrapper close_event)
SvScan constructor.
Definition: svscan.hpp:269
std::vector< TService > services
A list of services.
Definition: svscan.hpp:66
static constexpr const char kSigTermFile[8]
SIGTERM file.
Definition: svscan.hpp:256
static std::string ExpandEnvironmentVariables(const std::string &value)
Expand the given string with environment variables.
Definition: utils.cpp:31