X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fservice.h;h=eed59e621cb3094e34b33df119fa7c607bc1493f;hb=dff52e4261b762fbf7dd69b79b071693ebcf36c9;hp=4775fcf5b24a494d79f63ee4b931531941671cd5;hpb=ae8031f93e0804c912d69d909d7c3383d609a611;p=oweals%2Fdinit.git diff --git a/src/service.h b/src/service.h index 4775fcf..eed59e6 100644 --- a/src/service.h +++ b/src/service.h @@ -7,7 +7,7 @@ #include #include -#include "dasync.h" +#include "dasynq.h" #include "control.h" #include "service-listener.h" @@ -72,12 +72,13 @@ struct OnstartFlags { bool rw_ready : 1; + bool log_ready : 1; // Not actually "onstart" commands: bool no_sigterm : 1; // do not send SIGTERM bool runs_on_console : 1; // run "in the foreground" - OnstartFlags() noexcept : rw_ready(false), + OnstartFlags() noexcept : rw_ready(false), log_ready(false), no_sigterm(false), runs_on_console(false) { } @@ -180,30 +181,30 @@ static std::vector separate_args(std::string &s, std::list +class ServiceChildWatcher : public EventLoop_t::ChildProcWatcher { public: // TODO resolve clunkiness of storing this field ServiceRecord * service; - void gotTermStat(EventLoop_t * eloop, pid_t child, int status) noexcept; + Rearm childStatus(EventLoop_t &eloop, pid_t child, int status) noexcept; ServiceChildWatcher(ServiceRecord * sr) noexcept : service(sr) { } }; -class ServiceIoWatcher : public PosixFdWatcher +class ServiceIoWatcher : public EventLoop_t::FdWatcher { public: // TODO resolve clunkiness of storing these fields int fd; ServiceRecord * service; - Rearm gotEvent(EventLoop_t * eloop, int fd, int flags) noexcept; + Rearm fdEvent(EventLoop_t &eloop, int fd, int flags) noexcept; ServiceIoWatcher(ServiceRecord * sr) noexcept : service(sr) { } - void registerWith(EventLoop_t *loop, int fd, int flags) + void addWatch(EventLoop_t &loop, int fd, int flags) { this->fd = fd; - PosixFdWatcher::registerWith(loop, fd, flags); + EventLoop_t::FdWatcher::addWatch(loop, fd, flags); } }; @@ -284,10 +285,23 @@ class ServiceRecord int exit_status; // Exit status, if the process has exited (pid == -1). int socket_fd = -1; // For socket-activation services, this is the file // descriptor for the socket. - + ServiceChildWatcher child_listener; ServiceIoWatcher child_status_listener; + // Data for use by ServiceSet + public: + + // Next service (after this one) in the queue for the console. Intended to only be used by ServiceSet class. + ServiceRecord *next_for_console; + + // Start/stop queues + ServiceRecord *next_in_start_queue = nullptr; + ServiceRecord *next_in_stop_queue = nullptr; + + + private: + // All dependents have stopped. void allDepsStopped(); @@ -315,12 +329,6 @@ class ServiceRecord void handle_exit_status() noexcept; - // Called on transition of desired state from stopped to started (or unpinned stop) - void do_start() noexcept; - - // Called on transition of desired state from started to stopped (or unpinned start) - void do_stop() noexcept; - // A dependency has reached STARTED state void dependencyStarted() noexcept; @@ -427,8 +435,11 @@ class ServiceRecord // TODO write a destructor - // Next service (after this one) in the queue for the console. Intended to only be used by ServiceSet class. - ServiceRecord *next_for_console; + // Called on transition of desired state from stopped to started (or unpinned stop) + void do_start() noexcept; + + // Called on transition of desired state from started to stopped (or unpinned start) + void do_stop() noexcept; // Console is available. void acquiredConsole() noexcept; @@ -537,7 +548,25 @@ class ServiceRecord } }; - +/* + * A ServiceSet, as the name suggests, manages a set of services. + * + * Other than the ability to find services by name, the service set manages various queues. + * One is the queue for processes wishing to acquire the console. There is also a set of + * processes that want to start, and another set of those that want to stop. These latter + * two "queues" (not really queues since their order is not important) are used to prevent too + * much recursion and to prevent service states from "bouncing" too rapidly. + * + * A service that wishes to stop puts itself on the stop queue; a service that wishes to start + * puts itself on the start queue. Any operation that potentially manipulates the queues must + * be folloed by a "process queues" order (processQueues method, which can be instructed to + * process either the start queue or the stop queue first). + * + * Note that which queue it does process first, processQueues always repeatedly processes both + * queues until they are empty. The process is finite because starting a service can never + * cause services to be added to the stop queue, unless they fail to start, which should cause + * them to stop semi-permanently. + */ class ServiceSet { int active_services; @@ -549,6 +578,10 @@ class ServiceSet ServiceRecord * console_queue_head = nullptr; // first record in console queue ServiceRecord * console_queue_tail = nullptr; // last record in console queue + + // start/stop "queue" - list of services waiting to stop/start + ServiceRecord * first_start_queue = nullptr; + ServiceRecord * first_stop_queue = nullptr; // Private methods @@ -597,11 +630,57 @@ class ServiceSet // transition to the 'stopped' state. void stopService(const std::string &name) noexcept; + // Add a service record to the start queue + void addToStartQueue(ServiceRecord *service) noexcept + { + if (service->next_in_start_queue == nullptr && first_start_queue != service) { + service->next_in_start_queue = first_start_queue; + first_start_queue = service; + } + } + + // Add a service to the stop queue + void addToStopQueue(ServiceRecord *service) noexcept + { + if (service->next_in_stop_queue == nullptr && first_stop_queue != service) { + service->next_in_stop_queue = first_stop_queue; + first_stop_queue = service; + } + } + + void processQueues(bool do_start_first) noexcept + { + if (! do_start_first) { + while (first_stop_queue != nullptr) { + auto next = first_stop_queue; + first_stop_queue = next->next_in_stop_queue; + next->next_in_stop_queue = nullptr; + next->do_stop(); + } + } + + while (first_stop_queue != nullptr || first_start_queue != nullptr) { + while (first_start_queue != nullptr) { + auto next = first_start_queue; + first_start_queue = next->next_in_start_queue; + next->next_in_start_queue = nullptr; + next->do_start(); + } + while (first_stop_queue != nullptr) { + auto next = first_stop_queue; + first_stop_queue = next->next_in_stop_queue; + next->next_in_stop_queue = nullptr; + next->do_stop(); + } + } + } + // Set the console queue tail (returns previous tail) ServiceRecord * consoleQueueTail(ServiceRecord * newTail) noexcept { auto prev_tail = console_queue_tail; console_queue_tail = newTail; + newTail->next_for_console = nullptr; if (! prev_tail) { console_queue_head = newTail; enable_console_log(false); @@ -652,6 +731,7 @@ class ServiceSet (*i)->stop(false); (*i)->unpin(); } + processQueues(false); } void set_auto_restart(bool restart) noexcept