8 #include <sys/socket.h>
16 /* TODO: prevent services from respawning too quickly */
17 /* TODO: detect/guard against dependency cycles */
18 /* TODO: optional automatic restart of services */
21 * "simpleinit" from util-linux package handles signals as follows:
22 * SIGTSTP - spawn no more gettys (in preparation for shutdown etc).
23 * In dinit terms this should probably mean "no more auto restarts"
24 * (for any service). (Actually the signal acts as a toggle, if
25 * respawn is disabled it will be re-enabled and init will
26 * act as if SIGHUP had also been sent)
27 * SIGTERM - kill spawned gettys (which are still alive)
28 * Interestingly, simpleinit just sends a SIGTERM to the gettys.
29 * "shutdown" however has already sent SIGTERM to every process...
30 * "/sbin/initctl -r" - rollback services (ran by "shutdown"/halt etc)
31 * shouldn't return until all services have been stopped.
32 * shutdown calls this *after* sending SIGTERM to all processes.
33 * I guess this allows user processes, if any are still around,
34 * to die before (or just as) the services fall out from underneath
35 * them. On the other hand it largely subverts the ordered service
36 * shutdown that init provides.
37 * SIGQUIT - init will exec() shutdown. shutdown will detect that it is
38 * running as pid 1 and will just loop and reap child processes.
39 * This is used by shutdown so that init will not hang on to its
40 * inode, allowing clean filesystem unmounting.
42 * Not sent by shutdown:
43 * SIGHUP - re-read inittab and spawn any new getty entries
44 * SIGINT - (ctrl+alt+del handler) - fork & exec "reboot"
46 * On the contrary dinit currently uses:
47 * SIGTERM - roll back services and then exec /sbin/halt
48 * SIGINT - roll back services and then exec /sbin/reboot
50 * It's an open question about whether dinit should roll back services *before*
51 * running halt/reboot, since those commands should prompt rollback of services
52 * anyway. But it seems safe to do so.
56 static bool got_sigterm = false;
58 static ServiceSet *service_set;
60 static bool am_system_init = false; // true if we are the system init process
61 static bool reboot = false; // whether to reboot (instead of halting)
63 static void sigint_reboot_cb(struct ev_loop *loop, ev_signal *w, int revents);
64 static void sigquit_cb(struct ev_loop *loop, ev_signal *w, int revents);
65 static void sigterm_cb(struct ev_loop *loop, ev_signal *w, int revents);
67 static void open_control_socket(struct ev_loop *loop);
69 struct ev_io control_socket_io;
72 int main(int argc, char **argv)
76 am_system_init = (getpid() == 1);
79 // setup STDIN, STDOUT, STDERR so that we can use them
80 int onefd = open("/dev/console", O_RDONLY, 0);
82 int twofd = open("/dev/console", O_RDWR, 0);
87 /* Set up signal handlers etc */
88 /* SIG_CHILD is ignored by default: good */
89 /* sigemptyset(&sigwait_set); */
90 /* sigaddset(&sigwait_set, SIGCHLD); */
91 /* sigaddset(&sigwait_set, SIGINT); */
92 /* sigaddset(&sigwait_set, SIGTERM); */
93 /* sigprocmask(SIG_BLOCK, &sigwait_set, NULL); */
95 /* list of services to start */
96 list<const char *> services_to_start;
98 /* service directory name */
99 const char * service_dir = "/etc/dinit.d";
101 /* arguments, if given, specify a list of services to start. */
102 /* if none are given the "boot" service is started. */
104 for (int i = 1; i < argc; i++) {
105 if (argv[i][0] == '-') {
107 if (strcmp(argv[i], "--services-dir") == 0 ||
108 strcmp(argv[i], "-d") == 0) {
111 service_dir = argv[i];
117 else if (strcmp(argv[i], "--help") == 0) {
118 cout << "dinit, an init with dependency management" << endl;
119 cout << " --help : display help" << endl;
120 cout << " --services-dir <dir>, -d <dir> : set base directory for service description files (-d <dir>)" << endl;
121 cout << " <service-name> : start service with name <service-name>" << endl;
126 if (! am_system_init) {
127 cerr << "Unrecognized option: " << argv[i] << endl;
133 services_to_start.push_back(argv[i]);
138 if (services_to_start.empty()) {
139 services_to_start.push_back("boot");
142 // Set up signal handlers
143 ev_signal sigint_ev_signal;
144 if (am_system_init) {
145 ev_signal_init(&sigint_ev_signal, sigint_reboot_cb, SIGINT);
148 ev_signal_init(&sigint_ev_signal, sigterm_cb, SIGINT);
151 ev_signal sigquit_ev_signal;
152 if (am_system_init) {
153 // PID 1: SIGQUIT exec's shutdown
154 ev_signal_init(&sigquit_ev_signal, sigquit_cb, SIGQUIT);
157 // Otherwise: SIGQUIT terminates dinit
158 ev_signal_init(&sigquit_ev_signal, sigterm_cb, SIGQUIT);
161 ev_signal sigterm_ev_signal;
162 ev_signal_init(&sigterm_ev_signal, sigterm_cb, SIGTERM);
165 struct ev_loop *loop = ev_default_loop(EVFLAG_AUTO /* | EVFLAG_SIGNALFD */);
166 ev_signal_start(loop, &sigint_ev_signal);
167 ev_signal_start(loop, &sigquit_ev_signal);
168 ev_signal_start(loop, &sigterm_ev_signal);
170 // Try to open control socket (may fail due to readonly filesystem)
171 open_control_socket(loop);
173 /* start requested services */
174 service_set = new ServiceSet(service_dir);
175 for (list<const char *>::iterator i = services_to_start.begin();
176 i != services_to_start.end();
179 service_set->startService(*i);
181 catch (ServiceNotFound &snf) {
182 // TODO log this better
183 cerr << "Could not find service description: " << snf.serviceName << endl;
185 catch (ServiceLoadExc &sle) {
186 // TODO log this better
187 cerr << "Problem loading service description: " << sle.serviceName << endl;
193 // Process events until all services have terminated.
194 while (! service_set->count_active_services() == 0) {
195 ev_loop(loop, EVLOOP_ONESHOT);
198 if (am_system_init) {
199 // TODO log this output properly
200 cout << "dinit: No more active services.";
202 cout << " Will reboot.";
204 else if (got_sigterm) {
205 cout << " Will halt.";
208 cout << " Re-initiating boot sequence.";
214 if (am_system_init) {
216 // TODO log error from fork
218 execl("/sbin/reboot", "/sbin/reboot", (char *) 0);
221 else if (got_sigterm) {
222 // TODO log error from fork
224 execl("/sbin/halt", "/sbin/halt", (char *) 0);
229 // It could be that we started in single user mode, and the
230 // user has now exited the shell. We'll try and re-start the
233 service_set->startService("boot");
234 goto event_loop; // yes, the "evil" goto
237 // TODO catch exceptions and log message as appropriate
238 // Now WTF do we do? try and reboot
240 execl("/sbin/reboot", "/sbin/reboot", (char *) 0);
245 // PID 1 should never exit:
254 // Callback for control socket
255 static void control_socket_cb(struct ev_loop *loop, ev_io *w, int revents)
257 // Accept a connection
260 int newfd = accept4(sockfd, nullptr, nullptr, SOCK_NONBLOCK | SOCK_CLOEXEC);
263 new ControlConn(loop, service_set, newfd); // will delete itself when it's finished
264 // TODO keep a set of control connections so that we can close them when
269 static void open_control_socket(struct ev_loop *loop)
271 // TODO make this use a per-user address if PID != 1, and make the address
272 // overridable from the command line
274 const char * saddrname = "/dev/dinitctl";
275 struct sockaddr_un name;
279 name.sun_family = AF_UNIX;
280 strcpy(name.sun_path, saddrname); // TODO make this safe for long names
281 int namelen = 2 + strlen(saddrname);
282 //int namelen = sizeof(name);
284 int sockfd = socket(AF_UNIX, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, 0);
291 if (bind(sockfd, (struct sockaddr *) &name, namelen) == -1) {
298 if (listen(sockfd, 10) == -1) {
305 ev_io_init(&control_socket_io, control_socket_cb, sockfd, EV_READ);
306 ev_io_start(loop, &control_socket_io);
309 /* handle SIGINT signal (generated by kernel when ctrl+alt+del pressed) */
310 static void sigint_reboot_cb(struct ev_loop *loop, ev_signal *w, int revents)
313 service_set->stop_all_services();
316 /* handle SIGQUIT (if we are system init) */
317 static void sigquit_cb(struct ev_loop *loop, ev_signal *w, int revents)
319 // This allows remounting the filesystem read-only if the dinit binary has been
320 // unlinked. In that case the kernel holds the binary open, so that it can't be
322 execl("/sbin/shutdown", "/sbin/shutdown", (char *) 0);
325 /* handle SIGTERM - stop all services */
326 static void sigterm_cb(struct ev_loop *loop, ev_signal *w, int revents)
329 service_set->stop_all_services();