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