blob: 6cf806175f0ee602424510921c98af248bba5905 [file] [log] [blame]
Scott James Remnant50748842006-05-16 21:02:31 +01001/* upstart
2 *
Scott James Remnantb6270dd2006-07-13 02:16:38 +01003 * Copyright © 2006 Canonical Ltd.
4 * Author: Scott James Remnant <scott@ubuntu.com>.
Scott James Remnant50748842006-05-16 21:02:31 +01005 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif /* HAVE_CONFIG_H */
24
25
Scott James Remnantf43bdf32006-08-27 18:20:29 +010026#include <linux/kd.h>
27
28#include <sys/types.h>
29#include <sys/wait.h>
30#include <sys/ioctl.h>
31#include <sys/reboot.h>
32#include <sys/resource.h>
33
Scott James Remnant3401ab72006-09-01 02:14:47 +010034#include <errno.h>
35#include <stdio.h>
Scott James Remnantf43bdf32006-08-27 18:20:29 +010036#include <signal.h>
Scott James Remnant94d00982006-08-25 15:38:22 +020037#include <stdlib.h>
Scott James Remnant3401ab72006-09-01 02:14:47 +010038#include <string.h>
Scott James Remnant12a330f2006-08-24 02:19:09 +020039#include <syslog.h>
Scott James Remnant027dd7b2006-08-21 09:01:25 +020040#include <unistd.h>
Scott James Remnantff0d26a2006-08-31 20:49:43 +010041#include <termios.h>
Scott James Remnant027dd7b2006-08-21 09:01:25 +020042
Scott James Remnant77e8db32006-08-21 08:47:50 +020043#include <nih/macros.h>
44#include <nih/alloc.h>
45#include <nih/list.h>
46#include <nih/timer.h>
47#include <nih/signal.h>
48#include <nih/child.h>
Scott James Remnant3401ab72006-09-01 02:14:47 +010049#include <nih/option.h>
Scott James Remnant77e8db32006-08-21 08:47:50 +020050#include <nih/main.h>
51#include <nih/logging.h>
52
53#include "process.h"
54#include "job.h"
55#include "event.h"
56#include "control.h"
57#include "cfgfile.h"
Scott James Remnant50748842006-05-16 21:02:31 +010058
59
Scott James Remnantf43bdf32006-08-27 18:20:29 +010060/* Prototypes for static functions */
Scott James Remnant3401ab72006-09-01 02:14:47 +010061static void reset_console (void);
62static void segv_handler (int signum);
63static void cad_handler (void *data, NihSignal *signal);
64static void kbd_handler (void *data, NihSignal *signal);
65static void stop_handler (void *data, NihSignal *signal);
66static void usr1_handler (const char *prog, NihSignal *signal);
67static int state_fd_setter (NihOption *option, const char *arg);
68static void read_state (int fd);
69static void write_state (int fd);
70
71/**
72 * state_fd:
73 *
74 * This is set to the file descriptor we should read state from if we
75 * are being re-exec'd.
76 **/
77static int state_fd = -1;
78
79
80/**
81 * options:
82 *
83 * Command-line options we accept.
84 **/
85static NihOption options[] = {
86 { 0, "state-fd", N_("file descriptor to read state from"),
87 NULL, "FD", &state_fd, state_fd_setter },
88
89 /* Ignore invalid options */
90 { '-', "--", NULL, NULL, NULL, NULL, NULL },
91
92 NIH_OPTION_LAST
93};
Scott James Remnantff0d26a2006-08-31 20:49:43 +010094
Scott James Remnantf43bdf32006-08-27 18:20:29 +010095
Scott James Remnant50748842006-05-16 21:02:31 +010096int
97main (int argc,
98 char *argv[])
99{
Scott James Remnant3401ab72006-09-01 02:14:47 +0100100 char **args;
101 int ret, i;
Scott James Remnant50748842006-05-16 21:02:31 +0100102
Scott James Remnant77e8db32006-08-21 08:47:50 +0200103 nih_main_init (argv[0]);
104
Scott James Remnant3401ab72006-09-01 02:14:47 +0100105 args = nih_option_parser (NULL, argc, argv, options, FALSE);
106 if (! args)
107 exit (1);
Scott James Remnant12a330f2006-08-24 02:19:09 +0200108
Scott James Remnant3401ab72006-09-01 02:14:47 +0100109 /* Send all logging output to syslog */
110 openlog (program_name, LOG_CONS, LOG_DAEMON);
Scott James Remnant12a330f2006-08-24 02:19:09 +0200111 nih_log_set_logger (nih_logger_syslog);
Scott James Remnant77e8db32006-08-21 08:47:50 +0200112
Scott James Remnant77e8db32006-08-21 08:47:50 +0200113 /* Close any file descriptors we inherited, and open the console
Scott James Remnant3401ab72006-09-01 02:14:47 +0100114 * device instead. Normally we reset the console, unless we're
115 * inheriting one from another init process.
Scott James Remnant77e8db32006-08-21 08:47:50 +0200116 */
117 for (i = 0; i < 3; i++)
118 close (i);
119
120 process_setup_console (CONSOLE_OUTPUT);
Scott James Remnant3401ab72006-09-01 02:14:47 +0100121 if (state_fd < 0)
122 reset_console ();
Scott James Remnant77e8db32006-08-21 08:47:50 +0200123
Scott James Remnantf4970812006-08-27 18:27:19 +0100124 /* Check we're root */
125 if (getuid ()) {
126 nih_error (_("Need to be root"));
127 exit (1);
128 }
129
130 /* Check we're process #1 */
131 if (getpid () > 1) {
132 nih_error (_("Not being executed as init"));
133 exit (1);
134 }
135
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100136 /* Reset the signal state and install the signal handler for those
137 * signals we actually want to catch; this also sets those that
138 * can be sent to us, because we're special
139 */
140 nih_signal_reset ();
141 nih_signal_set_handler (SIGALRM, nih_signal_handler);
142 nih_signal_set_handler (SIGHUP, nih_signal_handler);
Scott James Remnanteabb7802006-08-31 15:39:04 +0100143 nih_signal_set_handler (SIGTSTP, nih_signal_handler);
144 nih_signal_set_handler (SIGCONT, nih_signal_handler);
Scott James Remnant3401ab72006-09-01 02:14:47 +0100145 nih_signal_set_handler (SIGUSR1, nih_signal_handler);
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100146 nih_signal_set_handler (SIGINT, nih_signal_handler);
147 nih_signal_set_handler (SIGWINCH, nih_signal_handler);
148 nih_signal_set_handler (SIGSEGV, segv_handler);
149
Scott James Remnanteabb7802006-08-31 15:39:04 +0100150 /* Ensure that we don't process events while paused */
Scott James Remnanteabb7802006-08-31 15:39:04 +0100151 nih_signal_add_callback (NULL, SIGTSTP, stop_handler, NULL);
152 nih_signal_add_callback (NULL, SIGCONT, stop_handler, NULL);
153
Scott James Remnant3401ab72006-09-01 02:14:47 +0100154 /* SIGUSR1 instructs us to re-exec ourselves */
155 nih_signal_add_callback (NULL, SIGUSR1,
156 (NihSignalCb)usr1_handler, argv[0]);
157
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100158 /* Ask the kernel to send us SIGINT when control-alt-delete is
159 * pressed; generate an event with the same name.
160 */
161 reboot (RB_DISABLE_CAD);
162 nih_signal_add_callback (NULL, SIGINT, cad_handler, NULL);
163
164 /* Ask the kernel to send us SIGWINCH when alt-uparrow is pressed;
165 * generate a kbdrequest event.
166 */
167 ioctl (0, KDSIGACCEPT, SIGWINCH);
168 nih_signal_add_callback (NULL, SIGWINCH, kbd_handler, NULL);
169
170
171 /* Reap all children that die */
172 nih_child_add_watch (NULL, -1, job_child_reaper, NULL);
173
Scott James Remnantdc8877d2006-08-29 16:56:48 +0100174 /* Process the event queue and check the jobs for idleness
175 * every time through the main loop */
Scott James Remnant0c7e72a2006-08-27 22:22:53 +0100176 nih_main_loop_add_func (NULL, (NihMainLoopCb)event_queue_run, NULL);
Scott James Remnantdc8877d2006-08-29 16:56:48 +0100177 nih_main_loop_add_func (NULL, (NihMainLoopCb)job_detect_idle, NULL);
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100178
Scott James Remnant94d00982006-08-25 15:38:22 +0200179
Scott James Remnant77e8db32006-08-21 08:47:50 +0200180 /* Become session and process group leader (should be already,
181 * but you never know what initramfs did
182 */
183 setsid ();
184
185 /* Open control socket */
186 control_open ();
187
Scott James Remnantceaaf4d2006-08-24 01:40:10 +0200188 /* Read configuration */
189 cfg_watch_dir (NULL, CFG_DIR, NULL);
190
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100191 /* Set the PATH environment variable */
192 setenv ("PATH", PATH, TRUE);
193
Scott James Remnant77e8db32006-08-21 08:47:50 +0200194
Scott James Remnant3401ab72006-09-01 02:14:47 +0100195 /* Generate and run the startup event or read the state from the
196 * init daemon that exec'd us
197 */
198 if (state_fd < 0) {
199 event_queue ("startup");
200 } else {
201 read_state (state_fd);
202 nih_child_poll ();
203 }
204
205 /* Run the event queue once, and detect anything idle */
Scott James Remnant0c7e72a2006-08-27 22:22:53 +0100206 event_queue_run ();
Scott James Remnant32de9222006-08-31 04:34:27 +0100207 job_detect_idle ();
Scott James Remnant77e8db32006-08-21 08:47:50 +0200208
209 /* Go! */
210 ret = nih_main_loop ();
211
212 return ret;
Scott James Remnant50748842006-05-16 21:02:31 +0100213}
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100214
215
216/**
Scott James Remnantff0d26a2006-08-31 20:49:43 +0100217 * reset_console:
218 *
219 * Set up the console flags to something sensible. Cribbed from sysvinit,
220 * initng, etc.
221 **/
222static void
223reset_console (void)
224{
Scott James Remnant4bc5fe32006-08-31 21:48:33 +0100225 struct termios tty;
226
Scott James Remnantff0d26a2006-08-31 20:49:43 +0100227 tcgetattr (0, &tty);
228
229 tty.c_cflag &= (CBAUD | CBAUDEX | CSIZE | CSTOPB | PARENB | PARODD);
230 tty.c_cflag |= (HUPCL | CLOCAL | CREAD);
231
232 /* Set up usual keys */
233 tty.c_cc[VINTR] = 3; /* ^C */
234 tty.c_cc[VQUIT] = 28; /* ^\ */
235 tty.c_cc[VERASE] = 127;
236 tty.c_cc[VKILL] = 24; /* ^X */
237 tty.c_cc[VEOF] = 4; /* ^D */
238 tty.c_cc[VTIME] = 0;
239 tty.c_cc[VMIN] = 1;
240 tty.c_cc[VSTART] = 17; /* ^Q */
241 tty.c_cc[VSTOP] = 19; /* ^S */
242 tty.c_cc[VSUSP] = 26; /* ^Z */
243
244 /* Pre and post processing */
245 tty.c_iflag = (IGNPAR | ICRNL | IXON | IXANY);
246 tty.c_oflag = (OPOST | ONLCR);
247 tty.c_lflag = (ISIG | ICANON | ECHO | ECHOCTL | ECHOPRT | ECHOKE);
248
249 /* Set the terminal line and flush it */
250 tcsetattr (0, TCSANOW, &tty);
251 tcflush (0, TCIOFLUSH);
252}
253
254
255/**
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100256 * segv_handler:
257 * @signum:
258 *
259 * Handle receiving the SEGV signal, usually caused by one of our own
260 * mistakes. We deal with it by dumping core in a child process and
261 * just carrying on in the parent.
262 **/
263static void
264segv_handler (int signum)
265{
266 pid_t pid;
267
268 pid = fork ();
269 if (pid == 0) {
270 struct sigaction act;
271 struct rlimit limit;
272 sigset_t mask;
273
274 /* Mask out all signals */
275 sigfillset (&mask);
276 sigprocmask (SIG_SETMASK, &mask, NULL);
277
278 /* Set the SEGV handler to the default so core is dumped */
279 act.sa_handler = SIG_DFL;
280 act.sa_flags = 0;
281 sigemptyset (&act.sa_mask);
282 sigaction (SIGSEGV, &act, NULL);
283
284 /* Dump in the root directory */
285 chdir ("/");
286
287 /* Don't limit the core dump size */
288 limit.rlim_cur = RLIM_INFINITY;
289 limit.rlim_max = RLIM_INFINITY;
290 setrlimit (RLIMIT_CORE, &limit);
291
292 /* Raise the signal */
293 raise (SIGSEGV);
294
295 /* Unmask so that we receive it */
296 sigdelset (&mask, SIGSEGV);
297 sigprocmask (SIG_SETMASK, &mask, NULL);
298
299 /* Wait for death */
300 pause ();
301 exit (0);
302 } else if (pid > 0) {
303 /* Wait for the core to be generated */
304 waitpid (pid, NULL, 0);
305
306 nih_error (_("Caught segmentation fault, core dumped"));
307 } else {
308 nih_error (_("Caught segmentation fault, unable to dump core"));
309 }
310}
311
312/**
313 * cad_handler:
314 * @data: unused,
315 * @signal: signal that called this handler.
316 *
317 * Handle having recieved the SIGINT signal, sent to us when somebody
318 * presses Ctrl-Alt-Delete on the console. We just generate a
319 * control-alt-delete event.
320 **/
321static void
322cad_handler (void *data,
323 NihSignal *signal)
324{
Scott James Remnantff5efb92006-08-31 01:45:19 +0100325 event_queue ("control-alt-delete");
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100326}
327
328/**
329 * kbd_handler:
330 * @data: unused,
331 * @signal: signal that called this handler.
332 *
333 * Handle having recieved the SIGWINCH signal, sent to us when somebody
334 * presses Alt-UpArrow on the console. We just generate a
335 * kbdrequest event.
336 **/
337static void
338kbd_handler (void *data,
339 NihSignal *signal)
340{
Scott James Remnantff5efb92006-08-31 01:45:19 +0100341 event_queue ("kbdrequest");
Scott James Remnantf43bdf32006-08-27 18:20:29 +0100342}
Scott James Remnanteabb7802006-08-31 15:39:04 +0100343
344/**
345 * stop_handler:
346 * @data: unused,
347 * @signal: signal caught.
348 *
349 * This is called when we receive the STOP, TSTP or CONT signals; we
350 * adjust the paused variable appropriately so that the event queue and
351 * job idle detection is not run.
352 **/
353static void
354stop_handler (void *data,
355 NihSignal *signal)
356{
357 nih_assert (signal != NULL);
358
359 if (signal->signum == SIGCONT) {
360 nih_info (_("Event queue resumed"));
361 paused = FALSE;
362 } else {
363 nih_info (_("Event queue paused"));
364 paused = TRUE;
365 }
366}
Scott James Remnant3401ab72006-09-01 02:14:47 +0100367
368
369/**
370 * usr1_handler:
371 * @argv0: program to run,
372 * @signal: signal caught.
373 *
374 * This is called when we receive the USR1 signal, which instructs us
375 * to reexec ourselves.
376 **/
377static void
378usr1_handler (const char *argv0,
379 NihSignal *signal)
380{
381 sigset_t mask, oldmask;
382 int fds[2] = { -1, -1 };
383 pid_t pid;
384 char buf[10];
385
386 nih_assert (argv0 != NULL);
387 nih_assert (signal != NULL);
388
389 nih_warn (_("Re-executing %s"), argv0);
390
391 /* Block signals while we work*/
392 sigfillset (&mask);
393 sigprocmask (SIG_BLOCK, &mask, &oldmask);
394
395 /* Create pipe */
396 if (pipe (fds) < 0)
397 goto error;
398
399 /* Fork a child that can send the state to the new init process */
400 pid = fork ();
401 if (pid < 0) {
402 close (fds[1]);
403 goto error;
404 } else if (pid == 0) {
405 close (fds[0]);
406 write_state (fds[1]);
407 exit (0);
408 } else {
409 close (fds[1]);
410 }
411
412 /* Argument list */
413 snprintf (buf, sizeof (buf), "%d", fds[0]);
414 execl (argv0, argv0, "--state-fd", buf, NULL);
415
416error:
417 /* Gnargh! */
418 nih_error (_("Failed to re-execute %s: %s"), argv0, strerror (errno));
419 close (fds[0]);
420 sigprocmask (SIG_SETMASK, &oldmask, NULL);
421}
422
423/**
424 * state_fd_setter:
425 * @option: option found,
426 * @arg: argument to option.
427 *
428 * This function is called when --state-fd is found on the command-line.
429 * It parses it as a file descriptor number.
430 *
431 * Returns: zero if valid descriptor found, negative value on error.
432 **/
433static int
434state_fd_setter (NihOption *option,
435 const char *arg)
436{
437 char *endptr;
438 int *value;
439
440 nih_assert (option != NULL);
441 nih_assert (option->value != NULL);
442 nih_assert (arg != NULL);
443
444 value = (int *)option->value;
445 *value = strtol (arg, &endptr, 10);
446
447 if (*endptr || (*value < 0)) {
448 fprintf (stderr, _("%s: invalid file descriptor: %s\n"),
449 program_name, arg);
450 nih_main_suggest_help ();
451 return -1;
452 }
453
454 return 0;
455}
456
457/**
458 * read_state:
459 * @fd: file descriptor to read from.
460 *
461 * Read event and job state from @fd, which is a trivial line-based
462 * protocol that we can keep the same without too much difficultly. It's
463 * tempting to use the control sockets for this, but they break too often.
464 **/
465static void
466read_state (int fd)
467{
468 Job *job = NULL;
469 Event *event = NULL;
470 FILE *state;
471 char buf[80];
472
473 nih_debug ("Reading state");
474
475 /* Use stdio as it's a light-weight thing that won't change */
476 state = fdopen (fd, "r");
477 if (! state) {
478 nih_warn (_("Unable to read from state descriptor: %s"),
479 strerror (errno));
480 return;
481 }
482
483 /* It's just a series of simple lines; if one begins Job then it
484 * indicates the start of a Job description, otherwise if it
485 * begins Event then it's the start of an Event description.
486 *
487 * Lines beginning "." are assumed to belong to the current job
488 * or event.
489 */
490 while (fgets (buf, sizeof (buf), state)) {
491 char *ptr;
492
493 /* Strip newline */
494 ptr = strchr (buf, '\n');
495 if (ptr)
496 *ptr = '\0';
497
498 if (! strncmp (buf, "Job ", 4)) {
499 job = job_read_state (NULL, buf);
500 event = NULL;
501 } else if (! strncmp (buf, "Event ", 5)) {
502 event = event_read_state (NULL, buf);
503 job = NULL;
504 } else if (buf[0] == '.') {
505 if (job) {
506 job = job_read_state (job, buf);
507 } else if (event) {
508 event = event_read_state (event, buf);
509 }
510 } else {
511 event = NULL;
512 job = NULL;
513 }
514 }
515
516 if (fclose (state))
517 nih_warn (_("Error after reading state: %s"),
518 strerror (errno));
519
520 nih_debug ("State read from parent");
521}
522
523/**
524 * write_state:
525 * @fd: file descriptor to write to.
526 *
527 * Write event and job state to @fd, which is a trivial line-based
528 * protocol that we can keep the same without too much difficultly. It's
529 * tempting to use the control sockets for this, but they break too often.
530 **/
531static void
532write_state (int fd)
533{
534 FILE *state;
535
536 /* Use stdio as it's a light-weight thing that won't change */
537 state = fdopen (fd, "w");
538 if (! state) {
539 nih_warn (_("Unable to write to state descriptor: %s"),
540 strerror (errno));
541 return;
542 }
543
544 event_write_state (state);
545 job_write_state (state);
546
547 if (fclose (state))
548 nih_warn (_("Error after writing state: %s"),
549 strerror (errno));
550}