#include <fcntl.h>
#include <pwd.h>
+#include "dasynq.h"
#include "service.h"
-#include "ev++.h"
#include "control.h"
#include "dinit-log.h"
*/
-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<NullMutex>;
-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
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
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;
}
+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;
/* 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
}
// 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) {
/* start requested services */
service_set = new ServiceSet(service_dir);
+
+ init_log(service_set);
+
for (list<const char *>::iterator i = services_to_start.begin();
i != services_to_start.end();
++i) {
// 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();
}
}
- 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) {
// PID 1 must not actually exit, although we should never reach this point:
while (true) {
- ev_loop(loop, EVLOOP_ONESHOT);
+ eventLoop.run();
}
}
}
// 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;
}
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;
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:
}
}
+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<sockaddr_un *>(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();
}