Update Dasynq library and API usage
[oweals/dinit.git] / src / dinit.cc
1 #include <iostream>
2 #include <list>
3 #include <cstring>
4 #include <csignal>
5 #include <cstddef>
6 #include <cstdlib>
7
8 #include <sys/types.h>
9 #include <sys/stat.h>
10 #include <sys/un.h>
11 #include <sys/socket.h>
12 #include <unistd.h>
13 #include <fcntl.h>
14 #include <pwd.h>
15
16 #include "dasynq.h"
17 #include "service.h"
18 #include "control.h"
19 #include "dinit-log.h"
20
21 #ifdef __linux__
22 #include <sys/klog.h>
23 #include <sys/reboot.h>
24 #endif
25
26 /*
27  * When running as the system init process, Dinit processes the following signals:
28  *
29  * SIGTERM - roll back services and then fork/exec /sbin/halt
30  * SIGINT - roll back services and then fork/exec /sbin/reboot
31  * SIGQUIT - exec() /sbin/shutdown without rolling back services
32  *
33  * It's an open question about whether dinit should roll back services *before*
34  * running halt/reboot, since those commands should prompt rollback of services
35  * anyway. But it seems safe to do so.
36  */
37
38
39 using namespace dasynq;
40 using EventLoop_t = event_loop<null_mutex>;
41
42 EventLoop_t eventLoop = EventLoop_t();
43
44 static void sigint_reboot_cb(EventLoop_t *eloop) noexcept;
45 static void sigquit_cb(EventLoop_t *eloop) noexcept;
46 static void sigterm_cb(EventLoop_t *eloop) noexcept;
47 static void close_control_socket() noexcept;
48
49 static void control_socket_cb(EventLoop_t *loop, int fd);
50
51 void open_control_socket(bool report_ro_failure = true) noexcept;
52 void setup_external_log() noexcept;
53
54
55 class ControlSocketWatcher : public EventLoop_t::fd_watcher_impl<ControlSocketWatcher>
56 {
57     public:
58     rearm fd_event(EventLoop_t &loop, int fd, int flags) noexcept
59     {
60         control_socket_cb(&loop, fd);
61         return rearm::REARM;
62     }
63 };
64
65 ControlSocketWatcher control_socket_io;
66
67
68 // Variables
69
70 static ServiceSet *service_set;
71
72 static bool am_system_init = false; // true if we are the system init process
73
74 static bool control_socket_open = false;
75 static bool external_log_open = false;
76 int active_control_conns = 0;
77
78 // Control socket path. We maintain a string (control_socket_str) in case we need
79 // to allocate storage, but control_socket_path is the authoritative value.
80 static const char *control_socket_path = "/dev/dinitctl";
81 static std::string control_socket_str;
82
83 static const char *log_socket_path = "/dev/log";
84
85 static const char *user_home_path = nullptr;
86
87
88 // Get user home (and set user_home_path). (The return may become invalid after
89 // changing the evironment (HOME variable) or using the getpwuid() function).
90 const char * get_user_home()
91 {
92     if (user_home_path == nullptr) {
93         user_home_path = getenv("HOME");
94         if (user_home_path == nullptr) {
95             struct passwd * pwuid_p = getpwuid(getuid());
96             if (pwuid_p != nullptr) {
97                 user_home_path = pwuid_p->pw_dir;
98             }
99         }
100     }
101     return user_home_path;
102 }
103
104
105 namespace {
106     class CallbackSignalHandler : public EventLoop_t::signal_watcher_impl<CallbackSignalHandler>
107     {
108         public:
109         typedef void (*cb_func_t)(EventLoop_t *);
110         
111         private:
112         cb_func_t cb_func;
113         
114         public:
115         CallbackSignalHandler() : cb_func(nullptr) { }
116         CallbackSignalHandler(cb_func_t pcb_func) :  cb_func(pcb_func) { }
117         
118         void setCbFunc(cb_func_t cb_func)
119         {
120             this->cb_func = cb_func;
121         }
122         
123         rearm received(EventLoop_t &eloop, int signo, siginfo_p siginfo)
124         {
125             service_set->stop_all_services(ShutdownType::REBOOT);
126             return rearm::REARM;
127         }
128     };
129
130     class ControlSocketWatcher : public EventLoop_t::fd_watcher_impl<ControlSocketWatcher>
131     {
132         rearm fd_event(EventLoop_t &loop, int fd, int flags)
133         {
134             control_socket_cb(&loop, fd);
135             return rearm::REARM;
136         }
137     };
138 }
139
140 int main(int argc, char **argv)
141 {
142     using namespace std;
143     
144     am_system_init = (getpid() == 1);
145     const char * service_dir = nullptr;
146     string service_dir_str; // to hold storage for above if necessary
147     bool control_socket_path_set = false;
148
149     // list of services to start
150     list<const char *> services_to_start;
151     
152     // Arguments, if given, specify a list of services to start.
153     // If we are running as init (PID=1), the kernel gives us any command line
154     // arguments it was given but didn't recognize, including "single" (usually
155     // for "boot to single user mode" aka just start the shell). We can treat
156     // them as service names. In the worst case we can't find any of the named
157     // services, and so we'll start the "boot" service by default.
158     if (argc > 1) {
159       for (int i = 1; i < argc; i++) {
160         if (argv[i][0] == '-') {
161             // An option...
162             if (strcmp(argv[i], "--services-dir") == 0 ||
163                     strcmp(argv[i], "-d") == 0) {
164                 if (++i < argc) {
165                     service_dir = argv[i];
166                 }
167                 else {
168                     cerr << "dinit: '--services-dir' (-d) requires an argument" << endl;
169                     return 1;
170                 }
171             }
172             else if (strcmp(argv[i], "--system") == 0 ||
173                     strcmp(argv[i], "-s") == 0) {
174                 am_system_init = true;
175             }
176             else if (strcmp(argv[i], "--socket-path") == 0 ||
177                     strcmp(argv[i], "-p") == 0) {
178                 if (++i < argc) {
179                     control_socket_path = argv[i];
180                     control_socket_path_set = true;
181                 }
182                 else {
183                     cerr << "dinit: '--socket-path' (-p) requires an argument" << endl;
184                     return 1;
185                 }
186             }
187             else if (strcmp(argv[i], "--help") == 0) {
188                 cout << "dinit, an init with dependency management" << endl;
189                 cout << " --help                       display help" << endl;
190                 cout << " --services-dir <dir>, -d <dir>" << endl;
191                 cout << "                              set base directory for service description" << endl;
192                 cout << "                              files (-d <dir>)" << endl;
193                 cout << " --system, -s                 run as the system init process" << endl;
194                 cout << " --socket-path <path>, -p <path>" << endl;
195                 cout << "                              path to control socket" << endl;
196                 cout << " <service-name>               start service with name <service-name>" << endl;
197                 return 0;
198             }
199             else {
200                 // unrecognized
201                 if (! am_system_init) {
202                     cerr << "dinit: Unrecognized option: " << argv[i] << endl;
203                     return 1;
204                 }
205             }
206         }
207         else {
208             // LILO puts "auto" on the kernel command line for unattended boots; we'll filter it.
209             if (! am_system_init || strcmp(argv[i], "auto") != 0) {
210                 services_to_start.push_back(argv[i]);
211             }
212         }
213       }
214     }
215     
216     if (am_system_init) {
217         // setup STDIN, STDOUT, STDERR so that we can use them
218         int onefd = open("/dev/console", O_RDONLY, 0);
219         dup2(onefd, 0);
220         int twofd = open("/dev/console", O_RDWR, 0);
221         dup2(twofd, 1);
222         dup2(twofd, 2);
223         
224         if (onefd > 2) close(onefd);
225         if (twofd > 2) close(twofd);
226     }
227     
228     /* Set up signal handlers etc */
229     /* SIG_CHILD is ignored by default: good */
230     sigset_t sigwait_set;
231     sigemptyset(&sigwait_set);
232     sigaddset(&sigwait_set, SIGCHLD);
233     sigaddset(&sigwait_set, SIGINT);
234     sigaddset(&sigwait_set, SIGTERM);
235     sigprocmask(SIG_BLOCK, &sigwait_set, NULL);
236     
237     // Terminal access control signals - we block these so that dinit can't be
238     // suspended if it writes to the terminal after some other process has claimed
239     // ownership of it.
240     signal(SIGTSTP, SIG_IGN);
241     signal(SIGTTIN, SIG_IGN);
242     signal(SIGTTOU, SIG_IGN);
243     
244     signal(SIGPIPE, SIG_IGN);
245     
246     if (! am_system_init && ! control_socket_path_set) {
247         const char * userhome = get_user_home();
248         if (userhome != nullptr) {
249             control_socket_str = userhome;
250             control_socket_str += "/.dinitctl";
251             control_socket_path = control_socket_str.c_str();
252         }
253     }
254     
255     /* service directory name */
256     if (service_dir == nullptr && ! am_system_init) {
257         const char * userhome = get_user_home();
258         if (userhome != nullptr) {
259             service_dir_str = get_user_home();
260             service_dir_str += "/dinit.d";
261             service_dir = service_dir_str.c_str();
262         }
263     }
264     
265     if (service_dir == nullptr) {
266         service_dir = "/etc/dinit.d";
267     }
268     
269     if (services_to_start.empty()) {
270         services_to_start.push_back("boot");
271     }
272
273     // Set up signal handlers
274     CallbackSignalHandler sigint_watcher;
275     if (am_system_init) {
276       sigint_watcher.setCbFunc(sigint_reboot_cb);
277     }
278     else {
279       sigint_watcher.setCbFunc(sigterm_cb);
280     }
281     
282     CallbackSignalHandler sigquit_watcher;
283     if (am_system_init) {
284         // PID 1: SIGQUIT exec's shutdown
285         sigquit_watcher.setCbFunc(sigquit_cb);
286     }
287     else {
288         // Otherwise: SIGQUIT terminates dinit
289         sigquit_watcher.setCbFunc(sigterm_cb);
290     }
291     
292     CallbackSignalHandler sigterm_watcher {sigterm_cb};
293     
294     sigint_watcher.add_watch(eventLoop, SIGINT);
295     sigquit_watcher.add_watch(eventLoop, SIGQUIT);
296     sigterm_watcher.add_watch(eventLoop, SIGTERM);
297
298     // Try to open control socket (may fail due to readonly filesystem)
299     open_control_socket(false);
300     
301 #ifdef __linux__
302     if (am_system_init) {
303         // Disable non-critical kernel output to console
304         klogctl(6 /* SYSLOG_ACTION_CONSOLE_OFF */, nullptr, 0);
305         // Make ctrl+alt+del combination send SIGINT to PID 1 (this process)
306         reboot(RB_DISABLE_CAD);
307     }
308 #endif
309     
310     /* start requested services */
311     service_set = new ServiceSet(service_dir);
312     
313     init_log(service_set);
314     
315     for (list<const char *>::iterator i = services_to_start.begin();
316             i != services_to_start.end();
317             ++i) {
318         try {
319             service_set->startService(*i);
320         }
321         catch (ServiceNotFound &snf) {
322             log(LogLevel::ERROR, snf.serviceName, ": Could not find service description.");
323         }
324         catch (ServiceLoadExc &sle) {
325             log(LogLevel::ERROR, sle.serviceName, ": ", sle.excDescription);
326         }
327         catch (std::bad_alloc &badalloce) {
328             log(LogLevel::ERROR, "Out of memory when trying to start service: ", *i, ".");
329         }
330     }
331     
332     event_loop:
333     
334     // Process events until all services have terminated.
335     while (service_set->count_active_services() != 0) {
336         eventLoop.run();
337     }
338
339     ShutdownType shutdown_type = service_set->getShutdownType();
340     
341     if (am_system_init) {
342         logMsgBegin(LogLevel::INFO, "No more active services.");
343         
344         if (shutdown_type == ShutdownType::REBOOT) {
345             logMsgEnd(" Will reboot.");
346         }
347         else if (shutdown_type == ShutdownType::HALT) {
348             logMsgEnd(" Will halt.");
349         }
350         else if (shutdown_type == ShutdownType::POWEROFF) {
351             logMsgEnd(" Will power down.");
352         }
353         else {
354             logMsgEnd(" Re-initiating boot sequence.");
355         }
356     }
357     
358     while (! is_log_flushed()) {
359         eventLoop.run();
360     }
361     
362     close_control_socket();
363     
364     if (am_system_init) {
365         if (shutdown_type == ShutdownType::CONTINUE) {
366             // It could be that we started in single user mode, and the
367             // user has now exited the shell. We'll try and re-start the
368             // boot process...
369             try {
370                 service_set->startService("boot");
371                 goto event_loop; // yes, the "evil" goto
372             }
373             catch (...) {
374                 // Now WTF do we do? try to reboot
375                 log(LogLevel::ERROR, "Could not start 'boot' service; rebooting.");
376                 shutdown_type = ShutdownType::REBOOT;
377             }
378         }
379         
380         const char * cmd_arg;
381         if (shutdown_type == ShutdownType::HALT) {
382             cmd_arg = "-h";
383         }
384         else if (shutdown_type == ShutdownType::REBOOT) {
385             cmd_arg = "-r";
386         }
387         else {
388             // power off.
389             cmd_arg = "-p";
390         }
391         
392         // Fork and execute dinit-reboot.
393         execl("/sbin/shutdown", "/sbin/shutdown", "--system", cmd_arg, nullptr);
394         log(LogLevel::ERROR, "Could not execute /sbin/shutdown: ", strerror(errno));
395         
396         // PID 1 must not actually exit, although we should never reach this point:
397         while (true) {
398             eventLoop.run();
399         }
400     }
401     
402     return 0;
403 }
404
405 // Callback for control socket
406 static void control_socket_cb(EventLoop_t *loop, int sockfd)
407 {
408     // TODO limit the number of active connections. Keep a tally, and disable the
409     // control connection listening socket watcher if it gets high, and re-enable
410     // it once it falls below the maximum.
411
412     // Accept a connection
413     int newfd = accept4(sockfd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
414
415     if (newfd != -1) {
416         try {
417             new ControlConn(loop, service_set, newfd);  // will delete itself when it's finished
418         }
419         catch (std::exception &exc) {
420             log(LogLevel::ERROR, "Accepting control connection: ", exc.what());
421             close(newfd);
422         }
423     }
424 }
425
426 void open_control_socket(bool report_ro_failure) noexcept
427 {
428     if (! control_socket_open) {
429         const char * saddrname = control_socket_path;
430         size_t saddrname_len = strlen(saddrname);
431         uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + saddrname_len + 1;
432         
433         struct sockaddr_un * name = static_cast<sockaddr_un *>(malloc(sockaddr_size));
434         if (name == nullptr) {
435             log(LogLevel::ERROR, "Opening control socket: out of memory");
436             return;
437         }
438
439         if (am_system_init) {
440             // Unlink any stale control socket file, but only if we are system init, since otherwise
441             // the 'stale' file may not be stale at all:
442             unlink(saddrname);
443         }
444
445         name->sun_family = AF_UNIX;
446         memcpy(name->sun_path, saddrname, saddrname_len + 1);
447
448         // OpenBSD and Linux both allow combining NONBLOCK/CLOEXEC flags with socket type, however
449         // it's not actually POSIX. (TODO).
450         int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
451         if (sockfd == -1) {
452             log(LogLevel::ERROR, "Error creating control socket: ", strerror(errno));
453             free(name);
454             return;
455         }
456
457         if (bind(sockfd, (struct sockaddr *) name, sockaddr_size) == -1) {
458             if (errno != EROFS || report_ro_failure) {
459                 log(LogLevel::ERROR, "Error binding control socket: ", strerror(errno));
460             }
461             close(sockfd);
462             free(name);
463             return;
464         }
465         
466         free(name);
467
468         // No connections can be made until we listen, so it is fine to change the permissions now
469         // (and anyway there is no way to atomically create the socket and set permissions):
470         if (chmod(saddrname, S_IRUSR | S_IWUSR) == -1) {
471             log(LogLevel::ERROR, "Error setting control socket permissions: ", strerror(errno));
472             close(sockfd);
473             return;
474         }
475
476         if (listen(sockfd, 10) == -1) {
477             log(LogLevel::ERROR, "Error listening on control socket: ", strerror(errno));
478             close(sockfd);
479             return;
480         }
481
482         try {
483             control_socket_io.add_watch(eventLoop, sockfd, IN_EVENTS);
484             control_socket_open = true;
485         }
486         catch (std::exception &e)
487         {
488             log(LogLevel::ERROR, "Could not setup I/O on control socket: ", e.what());
489             close(sockfd);
490         }
491     }
492 }
493
494 static void close_control_socket() noexcept
495 {
496     if (control_socket_open) {
497         int fd = control_socket_io.get_watched_fd();
498         control_socket_io.deregister(eventLoop);
499         close(fd);
500         
501         // Unlink the socket:
502         unlink(control_socket_path);
503     }
504 }
505
506 void setup_external_log() noexcept
507 {
508     if (! external_log_open) {
509     
510         const char * saddrname = log_socket_path;
511         size_t saddrname_len = strlen(saddrname);
512         uint sockaddr_size = offsetof(struct sockaddr_un, sun_path) + saddrname_len + 1;
513         
514         struct sockaddr_un * name = static_cast<sockaddr_un *>(malloc(sockaddr_size));
515         if (name == nullptr) {
516             log(LogLevel::ERROR, "Connecting to log socket: out of memory");
517             return;
518         }
519         
520         name->sun_family = AF_UNIX;
521         memcpy(name->sun_path, saddrname, saddrname_len + 1);
522         
523         int sockfd = socket(AF_UNIX, SOCK_DGRAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
524         if (sockfd == -1) {
525             log(LogLevel::ERROR, "Error creating log socket: ", strerror(errno));
526             free(name);
527             return;
528         }
529         
530         if (connect(sockfd, (struct sockaddr *) name, sockaddr_size) == 0 || errno == EINPROGRESS) {
531             // TODO for EINPROGRESS, set up a watcher so we can properly wait until
532             // connection is established (or fails) before we pass it to the logging subsystem.
533             try {
534                 setup_main_log(sockfd);
535             }
536             catch (std::exception &e) {
537                 log(LogLevel::ERROR, "Setting up log failed: ", e.what());
538                 close(sockfd);
539             }
540         }
541         else {
542             // Note if connect fails, we haven't warned at all, because the syslog server might not
543             // have started yet.
544             close(sockfd);
545         }
546         
547         free(name);
548     }
549 }
550
551 /* handle SIGINT signal (generated by kernel when ctrl+alt+del pressed) */
552 static void sigint_reboot_cb(EventLoop_t *eloop) noexcept
553 {
554     service_set->stop_all_services(ShutdownType::REBOOT);
555 }
556
557 /* handle SIGQUIT (if we are system init) */
558 static void sigquit_cb(EventLoop_t *eloop) noexcept
559 {
560     // This performs an immediate shutdown, without service rollback.
561     close_control_socket();
562     execl("/sbin/shutdown", "/sbin/shutdown", "--system", (char *) 0);
563     log(LogLevel::ERROR, "Error executing /sbin/shutdown: ", strerror(errno));
564 }
565
566 /* handle SIGTERM/SIGQUIT(non-system-daemon) - stop all services and shut down */
567 static void sigterm_cb(EventLoop_t *eloop) noexcept
568 {
569     service_set->stop_all_services();
570 }