X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2Fdinit.cc;h=d7153a3e79a5e64b10c8006470421ca0402c21bb;hb=d1130cf7e5fbc71c7048ddd3f6cf968dd97c75a6;hp=6a2e78cef6f2674144f1dce63f3a91e47d2084e7;hpb=060a782b5f08d6fef8a57203427a6a1fe95263f3;p=oweals%2Fdinit.git diff --git a/src/dinit.cc b/src/dinit.cc index 6a2e78c..d7153a3 100644 --- a/src/dinit.cc +++ b/src/dinit.cc @@ -13,8 +13,8 @@ #include #include +#include "dasynq.h" #include "service.h" -#include "ev++.h" #include "control.h" #include "dinit-log.h" @@ -60,13 +60,42 @@ */ -static void sigint_reboot_cb(struct ev_loop *loop, ev_signal *w, int revents); -static void sigquit_cb(struct ev_loop *loop, ev_signal *w, int revents); -static void sigterm_cb(struct ev_loop *loop, ev_signal *w, int revents); -void open_control_socket(struct ev_loop *loop) noexcept; -void close_control_socket(struct ev_loop *loop) noexcept; +using namespace dasynq; +using EventLoop_t = EventLoop; -struct ev_io control_socket_io; +EventLoop_t eventLoop = EventLoop_t(); + +static void sigint_reboot_cb(EventLoop_t *eloop) noexcept; +static void sigquit_cb(EventLoop_t *eloop) noexcept; +static void sigterm_cb(EventLoop_t *eloop) noexcept; +static void close_control_socket() noexcept; + +static void control_socket_cb(EventLoop_t *loop, int fd); + +void open_control_socket(bool report_ro_failure = true) noexcept; +void setup_external_log() noexcept; + + +class ControlSocketWatcher : public EventLoop_t::FdWatcher +{ + Rearm fdEvent(EventLoop_t &loop, int fd, int flags) override + { + control_socket_cb(&loop, fd); + return Rearm::REARM; + } + + public: + // TODO the fd is already stored, must we really store it again... + int fd; + + void addWatch(EventLoop_t &loop, int fd, int flags) + { + this->fd = fd; + EventLoop_t::FdWatcher::addWatch(loop, fd, flags); + } +}; + +ControlSocketWatcher control_socket_io; // Variables @@ -76,6 +105,7 @@ static ServiceSet *service_set; static bool am_system_init = false; // true if we are the system init process static bool control_socket_open = false; +static bool external_log_open = false; int active_control_conns = 0; // Control socket path. We maintain a string (control_socket_str) in case we need @@ -83,6 +113,8 @@ int active_control_conns = 0; static const char *control_socket_path = "/dev/dinitctl"; static std::string control_socket_str; +static const char *log_socket_path = "/dev/log"; + static const char *user_home_path = nullptr; @@ -103,6 +135,41 @@ const char * get_user_home() } +namespace { + class CallbackSignalHandler : public EventLoop_t::SignalWatcher + { + public: + typedef void (*cb_func_t)(EventLoop_t *); + + private: + cb_func_t cb_func; + + public: + CallbackSignalHandler() : cb_func(nullptr) { } + CallbackSignalHandler(cb_func_t pcb_func) : cb_func(pcb_func) { } + + void setCbFunc(cb_func_t cb_func) + { + this->cb_func = cb_func; + } + + Rearm received(EventLoop_t &eloop, int signo, SigInfo_p siginfo) override + { + service_set->stop_all_services(ShutdownType::REBOOT); + return Rearm::REARM; + } + }; + + class ControlSocketWatcher : public EventLoop_t::FdWatcher + { + Rearm fdEvent(EventLoop_t &loop, int fd, int flags) + { + control_socket_cb(&loop, fd); + return Rearm::REARM; + } + }; +} + int main(int argc, char **argv) { using namespace std; @@ -193,11 +260,12 @@ int main(int argc, char **argv) /* Set up signal handlers etc */ /* SIG_CHILD is ignored by default: good */ - /* sigemptyset(&sigwait_set); */ - /* sigaddset(&sigwait_set, SIGCHLD); */ - /* sigaddset(&sigwait_set, SIGINT); */ - /* sigaddset(&sigwait_set, SIGTERM); */ - /* sigprocmask(SIG_BLOCK, &sigwait_set, NULL); */ + sigset_t sigwait_set; + sigemptyset(&sigwait_set); + sigaddset(&sigwait_set, SIGCHLD); + sigaddset(&sigwait_set, SIGINT); + sigaddset(&sigwait_set, SIGTERM); + sigprocmask(SIG_BLOCK, &sigwait_set, NULL); // Terminal access control signals - we block these so that dinit can't be // suspended if it writes to the terminal after some other process has claimed @@ -234,35 +302,32 @@ int main(int argc, char **argv) } // Set up signal handlers - ev_signal sigint_ev_signal; + CallbackSignalHandler sigint_watcher; if (am_system_init) { - ev_signal_init(&sigint_ev_signal, sigint_reboot_cb, SIGINT); + sigint_watcher.setCbFunc(sigint_reboot_cb); } else { - ev_signal_init(&sigint_ev_signal, sigterm_cb, SIGINT); + sigint_watcher.setCbFunc(sigterm_cb); } - ev_signal sigquit_ev_signal; + CallbackSignalHandler sigquit_watcher; if (am_system_init) { // PID 1: SIGQUIT exec's shutdown - ev_signal_init(&sigquit_ev_signal, sigquit_cb, SIGQUIT); + sigquit_watcher.setCbFunc(sigquit_cb); } else { // Otherwise: SIGQUIT terminates dinit - ev_signal_init(&sigquit_ev_signal, sigterm_cb, SIGQUIT); + sigquit_watcher.setCbFunc(sigterm_cb); } - ev_signal sigterm_ev_signal; - ev_signal_init(&sigterm_ev_signal, sigterm_cb, SIGTERM); + auto sigterm_watcher = CallbackSignalHandler(sigterm_cb); - /* Set up libev */ - struct ev_loop *loop = ev_default_loop(EVFLAG_AUTO /* | EVFLAG_SIGNALFD */); - ev_signal_start(loop, &sigint_ev_signal); - ev_signal_start(loop, &sigquit_ev_signal); - ev_signal_start(loop, &sigterm_ev_signal); + sigint_watcher.addWatch(eventLoop, SIGINT); + sigquit_watcher.addWatch(eventLoop, SIGQUIT); + sigterm_watcher.addWatch(eventLoop, SIGTERM); // Try to open control socket (may fail due to readonly filesystem) - open_control_socket(loop); + open_control_socket(false); #ifdef __linux__ if (am_system_init) { @@ -275,6 +340,9 @@ int main(int argc, char **argv) /* start requested services */ service_set = new ServiceSet(service_dir); + + init_log(service_set); + for (list::iterator i = services_to_start.begin(); i != services_to_start.end(); ++i) { @@ -296,7 +364,7 @@ int main(int argc, char **argv) // Process events until all services have terminated. while (service_set->count_active_services() != 0) { - ev_loop(loop, EVLOOP_ONESHOT); + eventLoop.run(); } ShutdownType shutdown_type = service_set->getShutdownType(); @@ -318,7 +386,11 @@ int main(int argc, char **argv) } } - close_control_socket(ev_default_loop(EVFLAG_AUTO)); + while (! is_log_flushed()) { + eventLoop.run(); + } + + close_control_socket(); if (am_system_init) { if (shutdown_type == ShutdownType::CONTINUE) { @@ -354,7 +426,7 @@ int main(int argc, char **argv) // PID 1 must not actually exit, although we should never reach this point: while (true) { - ev_loop(loop, EVLOOP_ONESHOT); + eventLoop.run(); } } @@ -362,29 +434,27 @@ int main(int argc, char **argv) } // Callback for control socket -static void control_socket_cb(struct ev_loop *loop, ev_io *w, int revents) +static void control_socket_cb(EventLoop_t *loop, int sockfd) { // TODO limit the number of active connections. Keep a tally, and disable the // control connection listening socket watcher if it gets high, and re-enable // it once it falls below the maximum. // Accept a connection - int sockfd = w->fd; - int newfd = accept4(sockfd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC); if (newfd != -1) { try { new ControlConn(loop, service_set, newfd); // will delete itself when it's finished } - catch (std::bad_alloc &bad_alloc_exc) { - log(LogLevel::ERROR, "Accepting control connection: Out of memory"); + catch (std::exception &exc) { + log(LogLevel::ERROR, "Accepting control connection: ", exc.what()); close(newfd); } } } -void open_control_socket(struct ev_loop *loop) noexcept +void open_control_socket(bool report_ro_failure) noexcept { if (! control_socket_open) { const char * saddrname = control_socket_path; @@ -415,7 +485,9 @@ void open_control_socket(struct ev_loop *loop) noexcept } if (bind(sockfd, (struct sockaddr *) name, sockaddr_size) == -1) { - log(LogLevel::ERROR, "Error binding control socket: ", strerror(errno)); + if (errno != EROFS || report_ro_failure) { + log(LogLevel::ERROR, "Error binding control socket: ", strerror(errno)); + } close(sockfd); free(name); return; @@ -437,17 +509,23 @@ void open_control_socket(struct ev_loop *loop) noexcept return; } - control_socket_open = true; - ev_io_init(&control_socket_io, control_socket_cb, sockfd, EV_READ); - ev_io_start(loop, &control_socket_io); + try { + control_socket_io.addWatch(eventLoop, sockfd, IN_EVENTS); + control_socket_open = true; + } + catch (std::exception &e) + { + log(LogLevel::ERROR, "Could not setup I/O on control socket: ", e.what()); + close(sockfd); + } } } -void close_control_socket(struct ev_loop *loop) noexcept +static void close_control_socket() noexcept { if (control_socket_open) { int fd = control_socket_io.fd; - ev_io_stop(loop, &control_socket_io); + control_socket_io.deregister(eventLoop); close(fd); // Unlink the socket: @@ -455,27 +533,70 @@ void close_control_socket(struct ev_loop *loop) noexcept } } +void setup_external_log() noexcept +{ + if (! external_log_open) { + + const char * saddrname = log_socket_path; + uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + strlen(saddrname) + 1; + + struct sockaddr_un * name = static_cast(malloc(sockaddr_size)); + if (name == nullptr) { + log(LogLevel::ERROR, "Connecting to log socket: out of memory"); + return; + } + + name->sun_family = AF_UNIX; + strcpy(name->sun_path, saddrname); + + int sockfd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0); + if (sockfd == -1) { + log(LogLevel::ERROR, "Error creating log socket: ", strerror(errno)); + free(name); + return; + } + + if (connect(sockfd, (struct sockaddr *) name, sockaddr_size) == 0 || errno == EINPROGRESS) { + // TODO for EINPROGRESS, set up a watcher so we can properly wait until + // connection is established (or fails) before we pass it to the logging subsystem. + try { + setup_main_log(sockfd); + } + catch (std::exception &e) { + log(LogLevel::ERROR, "Setting up log failed: ", e.what()); + close(sockfd); + } + } + else { + // Note if connect fails, we haven't warned at all, because the syslog server might not + // have started yet. TODO, have a special startup flag to indicate when syslog should + // be available. + close(sockfd); + } + + free(name); + } +} + /* handle SIGINT signal (generated by kernel when ctrl+alt+del pressed) */ -static void sigint_reboot_cb(struct ev_loop *loop, ev_signal *w, int revents) +static void sigint_reboot_cb(EventLoop_t *eloop) noexcept { - log_to_console = true; service_set->stop_all_services(ShutdownType::REBOOT); } /* handle SIGQUIT (if we are system init) */ -static void sigquit_cb(struct ev_loop *loop, ev_signal *w, int revents) +static void sigquit_cb(EventLoop_t *eloop) noexcept { // This allows remounting the filesystem read-only if the dinit binary has been // unlinked. In that case the kernel holds the binary open, so that it can't be // properly removed. - close_control_socket(ev_default_loop(EVFLAG_AUTO)); + close_control_socket(); execl("/sbin/shutdown", "/sbin/shutdown", (char *) 0); log(LogLevel::ERROR, "Error executing /sbin/shutdown: ", strerror(errno)); } /* handle SIGTERM/SIGQUIT - stop all services (not used for system daemon) */ -static void sigterm_cb(struct ev_loop *loop, ev_signal *w, int revents) +static void sigterm_cb(EventLoop_t *eloop) noexcept { - log_to_console = true; service_set->stop_all_services(); }