service.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. #include <string>
  2. #include <list>
  3. #include <vector>
  4. #include "ev.h"
  5. /*
  6. * Possible service states
  7. *
  8. * Services have both a current state and a desired state. The desired state can be
  9. * either STARTED or STOPPED. The current state can also be STARTING or STOPPING.
  10. *
  11. * The total state is a combination of the two, current and desired:
  12. * STOPPED/STOPPED : stopped and will remain stopped
  13. * STOPPED/STARTED : stopped and will be started; waiting for dependencies to start.
  14. * STARTING/STARTED : starting, but not yet started. All dependencies have started already.
  15. * STARTING/STOPPED : as above, but the service will be stopped again as soon as it has
  16. * completed startup.
  17. * STARTED/STARTED : running and will continue running.
  18. * STARTED/STOPPED : running but will stop; waiting for dependents to stop.
  19. * STOPPING/STOPPED : stopping and will stop. All dependents have stopped.
  20. * STOPPING/STARTED : as above, but the service will be re-started again once it stops.
  21. *
  22. * A scripted service is in the STARTING/STOPPING states during the script execution.
  23. * A process service is in the STOPPING state when it has been signalled to stop (and is never
  24. * in the STARTING state; it moves directly from STOPPED to STARTED).
  25. */
  26. enum class ServiceState {
  27. STOPPED, // service is not running.
  28. STARTING, // service is starting, and will start (or fail to start) in time. All dependencies have started.
  29. STARTED, // service is running,
  30. STOPPING // service script is stopping and will stop.
  31. };
  32. /* Service types */
  33. enum class ServiceType {
  34. DUMMY, // dummy service, used to detect cyclice dependencies
  35. PROCESS, // service runs as a process, and can be stopped by
  36. // sending the process a signal (SIGTERM)
  37. SCRIPTED, // service requires an external command to start,
  38. // and a second command to stop
  39. INTERNAL // internal service, runs no external process
  40. };
  41. struct OnstartFlags {
  42. bool release_console : 1;
  43. bool rw_ready : 1;
  44. };
  45. // Exception while loading a service
  46. class ServiceLoadExc
  47. {
  48. public:
  49. std::string serviceName;
  50. ServiceLoadExc(std::string serviceName)
  51. : serviceName(serviceName)
  52. {
  53. }
  54. };
  55. class ServiceNotFound : public ServiceLoadExc
  56. {
  57. public:
  58. ServiceNotFound(std::string serviceName)
  59. : ServiceLoadExc(serviceName)
  60. {
  61. }
  62. };
  63. class ServiceCyclicDependency : public ServiceLoadExc
  64. {
  65. public:
  66. ServiceCyclicDependency(std::string serviceName)
  67. : ServiceLoadExc(serviceName)
  68. {
  69. }
  70. };
  71. class ServiceDescriptionExc : public ServiceLoadExc
  72. {
  73. public:
  74. std::string extraInfo;
  75. ServiceDescriptionExc(std::string serviceName, std::string extraInfo)
  76. : ServiceLoadExc(serviceName), extraInfo(extraInfo)
  77. {
  78. }
  79. };
  80. class ServiceRecord; // forward declaration
  81. class ServiceSet; // forward declaration
  82. /* Service dependency record */
  83. class ServiceDep
  84. {
  85. ServiceRecord * from;
  86. ServiceRecord * to;
  87. public:
  88. /* Whether the 'from' service is waiting for the 'to' service to start */
  89. bool waiting_on;
  90. ServiceDep(ServiceRecord * from, ServiceRecord * to) : from(from), to(to), waiting_on(false)
  91. { }
  92. ServiceRecord * getFrom()
  93. {
  94. return from;
  95. }
  96. ServiceRecord * getTo()
  97. {
  98. return to;
  99. }
  100. };
  101. // Given a string and a list of pairs of (start,end) indices for each argument in that string,
  102. // store a null terminator for the argument. Return a `char *` array pointing at the beginning
  103. // of each argument. (The returned array is invalidated if the string is later modified).
  104. static const char ** separate_args(std::string &s, std::list<std::pair<unsigned,unsigned>> &arg_indices)
  105. {
  106. const char ** r = new const char *[arg_indices.size()];
  107. int i = 0;
  108. // First store nul terminator for each part:
  109. for (auto index_pair : arg_indices) {
  110. if (index_pair.second < s.length()) {
  111. s[index_pair.second] = 0;
  112. }
  113. }
  114. // Now we can get the C string (c_str) and store offsets into it:
  115. const char * cstr = s.c_str();
  116. for (auto index_pair : arg_indices) {
  117. r[i] = cstr + index_pair.first;
  118. i++;
  119. }
  120. return r;
  121. }
  122. class ServiceRecord
  123. {
  124. typedef std::string string;
  125. string service_name;
  126. ServiceType service_type; /* ServiceType::DUMMY, PROCESS, SCRIPTED, INTERNAL */
  127. ServiceState service_state; /* ServiceState::STOPPED, STARTING, STARTED, STOPPING */
  128. ServiceState desired_state; /* ServiceState::STOPPED / STARTED */
  129. bool force_stop; // true if the service must actually stop. This is the
  130. // case if for example the process dies; the service,
  131. // and all its dependencies, MUST be stopped.
  132. string program_name; /* storage for program/script and arguments */
  133. const char **exec_arg_parts; /* pointer to each argument/part of the program_name */
  134. int num_args; /* number of argumrnets (including program) */
  135. OnstartFlags onstart_flags;
  136. string logfile; /* log file name, empty string specifies /dev/null */
  137. bool auto_restart; /* whether to restart this (process) if it dies unexpectedly */
  138. typedef std::list<ServiceRecord *> sr_list;
  139. typedef sr_list::iterator sr_iter;
  140. // list of soft dependencies
  141. typedef std::list<ServiceDep> softdep_list;
  142. // list of soft dependents
  143. typedef std::list<ServiceDep *> softdpt_list;
  144. sr_list depends_on; // services this one depends on
  145. sr_list dependents; // services depending on this one
  146. softdep_list soft_deps; // services this one depends on via a soft dependency
  147. softdpt_list soft_dpts; // services depending on this one via a soft dependency
  148. // unsigned wait_count; /* if we are waiting for dependents/dependencies to
  149. // start/stop, this is how many we're waiting for */
  150. ServiceSet *service_set; // the set this service belongs to
  151. // Implementation details
  152. pid_t pid; /* PID of the process. If state is STARTING or STOPPING,
  153. this is PID of the service script; otherwise it is the
  154. PID of the process itself (process service).
  155. */
  156. ev_child child_listener;
  157. // Move service to STOPPING state. This can only be called once
  158. // all dependents have stopped.
  159. void stopping();
  160. // Service has actually stopped (includes having all dependents
  161. // reaching STOPPED state).
  162. void stopped();
  163. // Service has successfully started
  164. void started();
  165. // Service failed to start
  166. void failed_to_start();
  167. // A dependency of this service failed to start.
  168. void failed_dependency();
  169. // For process services, start the process, return true on success
  170. bool start_ps_process() noexcept;
  171. bool start_ps_process(const std::vector<std::string> &args) noexcept;
  172. // Callback from libev when a child process dies
  173. static void process_child_callback(struct ev_loop *loop, struct ev_child *w,
  174. int revents);
  175. // A dependent has reached STOPPED state
  176. void dependentStopped();
  177. // check if all dependents have stopped
  178. bool stopCheckDependents();
  179. // issue a stop to all dependents, return true if they are all already stopped
  180. bool stopDependents();
  181. void forceStop(); // force-stop this service and all dependents
  182. public:
  183. ServiceRecord(ServiceSet *set, string name)
  184. : service_state(ServiceState::STOPPED), desired_state(ServiceState::STOPPED), force_stop(false), auto_restart(false)
  185. {
  186. service_set = set;
  187. service_name = name;
  188. service_type = ServiceType::DUMMY;
  189. }
  190. ServiceRecord(ServiceSet *set, string name, ServiceType service_type, string &&command, std::list<std::pair<unsigned,unsigned>> &command_offsets,
  191. sr_list * pdepends_on, sr_list * pdepends_soft)
  192. : service_state(ServiceState::STOPPED), desired_state(ServiceState::STOPPED), force_stop(false), auto_restart(false)
  193. {
  194. service_set = set;
  195. service_name = name;
  196. this->service_type = service_type;
  197. this->depends_on = std::move(*pdepends_on);
  198. program_name = command;
  199. exec_arg_parts = separate_args(program_name, command_offsets);
  200. num_args = command_offsets.size();
  201. for (sr_iter i = depends_on.begin(); i != depends_on.end(); ++i) {
  202. (*i)->dependents.push_back(this);
  203. }
  204. // Soft dependencies
  205. auto b_iter = soft_deps.end();
  206. for (sr_iter i = pdepends_soft->begin(); i != pdepends_soft->end(); ++i) {
  207. b_iter = soft_deps.emplace(b_iter, this, *i);
  208. (*i)->soft_dpts.push_back(&(*b_iter));
  209. ++b_iter;
  210. }
  211. }
  212. // TODO write a destructor
  213. // Set logfile, should be done before service is started
  214. void setLogfile(string logfile)
  215. {
  216. this->logfile = logfile;
  217. }
  218. // Set whether this service should automatically restart when it dies
  219. void setAutoRestart(bool auto_restart)
  220. {
  221. this->auto_restart = auto_restart;
  222. }
  223. // Set "on start" flags (commands)
  224. void setOnstartFlags(OnstartFlags flags)
  225. {
  226. this->onstart_flags = flags;
  227. }
  228. const char *getServiceName() const { return service_name.c_str(); }
  229. ServiceState getState() const { return service_state; }
  230. void start(); // start the service
  231. void stop(); // stop the service
  232. bool isDummy()
  233. {
  234. return service_type == ServiceType::DUMMY;
  235. }
  236. };
  237. class ServiceSet
  238. {
  239. int active_services;
  240. std::list<ServiceRecord *> records;
  241. const char *service_dir; // directory containing service descriptions
  242. bool restart_enabled; // whether automatic restart is enabled (allowed)
  243. // Private methods
  244. // Locate an existing service record.
  245. ServiceRecord *findService(std::string name);
  246. // Load a service description, and dependencies, if there is no existing
  247. // record for the given name.
  248. ServiceRecord *loadServiceRecord(const char *name);
  249. // Public
  250. public:
  251. ServiceSet(const char *service_dir)
  252. {
  253. this->service_dir = service_dir;
  254. active_services = 0;
  255. restart_enabled = true;
  256. }
  257. // Start the service with the given name. The named service will begin
  258. // transition to the 'started' state.
  259. //
  260. // Throws an exception if the
  261. // service description cannot be loaded.
  262. void startService(const char *name);
  263. // Stop the service with the given name. The named service will begin
  264. // transition to the 'stopped' state.
  265. void stopService(const std::string &name);
  266. // Notification from service that it is active (state != STOPPED)
  267. // Only to be called on the transition from inactive to active.
  268. void service_active(ServiceRecord *);
  269. // Notification from service that it is inactive (STOPPED)
  270. // Only to be called on the transition from active to inactive.
  271. void service_inactive(ServiceRecord *);
  272. // Find out how many services are active (starting, running or stopping,
  273. // but not stopped).
  274. int count_active_services()
  275. {
  276. return active_services;
  277. }
  278. void stop_all_services()
  279. {
  280. restart_enabled = false;
  281. for (std::list<ServiceRecord *>::iterator i = records.begin(); i != records.end(); ++i) {
  282. (*i)->stop();
  283. }
  284. }
  285. void set_auto_restart(bool restart)
  286. {
  287. restart_enabled = restart;
  288. }
  289. bool get_auto_restart()
  290. {
  291. return restart_enabled;
  292. }
  293. };