/[baselayout]/trunk/src/rc.c
Gentoo

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2684 - (show annotations) (download) (as text)
Thu May 3 13:46:38 2007 UTC (7 years, 6 months ago) by uberlord
File MIME type: text/x-csrc
File size: 34758 byte(s)
Always pass ttyname to sulogin
1 /*
2 rc.c
3 rc - manager for init scripts which control the startup, shutdown
4 and the running of daemons on a Gentoo system.
5
6 Also a multicall binary for various commands that can be used in shell
7 scripts to query service state, mark service state and provide the
8 Gentoo einfo family of informational functions.
9
10 Copyright 2007 Gentoo Foundation
11 Released under the GPLv2
12 */
13
14 #include <sys/types.h>
15 #include <sys/stat.h>
16 #include <sys/utsname.h>
17 #include <sys/wait.h>
18 #include <errno.h>
19 #include <ctype.h>
20 #include <libgen.h>
21 #include <limits.h>
22 #include <stdbool.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <termios.h>
28 #include <unistd.h>
29
30 #include "einfo.h"
31 #include "rc.h"
32 #include "rc-misc.h"
33 #include "rc-plugin.h"
34 #include "strlist.h"
35
36 #define INITSH RC_LIBDIR "sh/init.sh"
37 #define INITEARLYSH RC_LIBDIR "sh/init-early.sh"
38 #define HALTSH RC_INITDIR "halt.sh"
39 #define SULOGIN "/sbin/sulogin"
40
41 #define RC_SVCDIR_STARTING RC_SVCDIR "starting/"
42 #define RC_SVCDIR_INACTIVE RC_SVCDIR "inactive/"
43 #define RC_SVCDIR_STARTED RC_SVCDIR "started/"
44 #define RC_SVCDIR_COLDPLUGGED RC_SVCDIR "coldplugged/"
45
46 #define INTERACTIVE RC_SVCDIR "interactive"
47
48 #define DEVBOOT "/dev/.rcboot"
49
50 /* Cleanup anything in main */
51 #define CHAR_FREE(_item) if (_item) { \
52 free (_item); \
53 _item = NULL; \
54 }
55
56 extern char **environ;
57
58 static char *applet = NULL;
59 static char **env = NULL;
60 static char **newenv = NULL;
61 static char **coldplugged_services = NULL;
62 static char **stop_services = NULL;
63 static char **start_services = NULL;
64 static rc_depinfo_t *deptree = NULL;
65 static char **types = NULL;
66 static char *tmp = NULL;
67
68 struct termios *termios_orig = NULL;
69
70 typedef struct pidlist
71 {
72 pid_t pid;
73 struct pidlist *next;
74 } pidlist_t;
75 static pidlist_t *service_pids = NULL;
76
77 static void cleanup (void)
78 {
79 pidlist_t *pl = service_pids;
80
81 rc_plugin_unload ();
82
83 if (termios_orig) {
84 tcsetattr (STDIN_FILENO, TCSANOW, termios_orig);
85 free (termios_orig);
86 }
87
88 while (pl) {
89 pidlist_t *p = pl->next;
90 free (pl);
91 pl = p;
92 }
93
94 rc_strlist_free (env);
95 rc_strlist_free (newenv);
96 rc_strlist_free (coldplugged_services);
97 rc_strlist_free (stop_services);
98 rc_strlist_free (start_services);
99 rc_free_deptree (deptree);
100 rc_strlist_free (types);
101
102 /* Clean runlevel start, stop markers */
103 if (rc_is_dir (RC_SVCDIR "softscripts.new"))
104 rc_rm_dir (RC_SVCDIR "softscripts.new", true);
105 if (rc_is_dir (RC_SVCDIR "softscripts.old"))
106 rc_rm_dir (RC_SVCDIR "softscripts.old", true);
107
108 free (applet);
109 }
110
111 static int do_e (int argc, char **argv)
112 {
113 int retval = EXIT_SUCCESS;
114 int i;
115 int l = 0;
116 char *message = NULL;
117 char *p;
118 char *fmt = NULL;
119
120 if (strcmp (applet, "eval_ecolors") == 0) {
121 printf ("GOOD='%s'\nWARN='%s'\nBAD='%s'\nHILITE='%s'\nBRACKET='%s'\nNORMAL='%s'\n",
122 ecolor (ecolor_good),
123 ecolor (ecolor_warn),
124 ecolor (ecolor_bad),
125 ecolor (ecolor_hilite),
126 ecolor (ecolor_bracket),
127 ecolor (ecolor_normal));
128 exit (EXIT_SUCCESS);
129 }
130
131 if (strcmp (applet, "eend") == 0 ||
132 strcmp (applet, "ewend") == 0 ||
133 strcmp (applet, "veend") == 0 ||
134 strcmp (applet, "vweend") == 0)
135 {
136 if (argc > 0) {
137 errno = 0;
138 retval = strtol (argv[0], NULL, 0);
139 if (errno != 0)
140 retval = EXIT_FAILURE;
141 else {
142 argc--;
143 argv++;
144 }
145 }
146 else
147 retval = EXIT_FAILURE;
148 }
149
150 if (argc > 0) {
151 for (i = 0; i < argc; i++)
152 l += strlen (argv[i]) + 1;
153
154 message = rc_xmalloc (l);
155 p = message;
156
157 for (i = 0; i < argc; i++) {
158 if (i > 0)
159 *p++ = ' ';
160 memcpy (p, argv[i], strlen (argv[i]));
161 p += strlen (argv[i]);
162 }
163 *p = 0;
164 }
165
166 if (message)
167 fmt = rc_xstrdup ("%s");
168
169 if (strcmp (applet, "einfo") == 0)
170 einfo (fmt, message);
171 else if (strcmp (applet, "einfon") == 0)
172 einfon (fmt, message);
173 else if (strcmp (applet, "ewarn") == 0)
174 ewarn (fmt, message);
175 else if (strcmp (applet, "ewarnn") == 0)
176 ewarnn (fmt, message);
177 else if (strcmp (applet, "eerror") == 0) {
178 eerror (fmt, message);
179 retval = 1;
180 } else if (strcmp (applet, "eerrorn") == 0) {
181 eerrorn (fmt, message);
182 retval = 1;
183 } else if (strcmp (applet, "ebegin") == 0)
184 ebegin (fmt, message);
185 else if (strcmp (applet, "eend") == 0)
186 eend (retval, fmt, message);
187 else if (strcmp (applet, "ewend") == 0)
188 ewend (retval, fmt, message);
189 else if (strcmp (applet, "veinfo") == 0)
190 einfov (fmt, message);
191 else if (strcmp (applet, "veinfon") == 0)
192 einfovn (fmt, message);
193 else if (strcmp (applet, "vewarn") == 0)
194 ewarnv (fmt, message);
195 else if (strcmp (applet, "vewarnn") == 0)
196 ewarnvn (fmt, message);
197 else if (strcmp (applet, "vebegin") == 0)
198 ebeginv (fmt, message);
199 else if (strcmp (applet, "veend") == 0)
200 eendv (retval, fmt, message);
201 else if (strcmp (applet, "vewend") == 0)
202 ewendv (retval, fmt, message);
203 else if (strcmp (applet, "eindent") == 0)
204 eindent ();
205 else if (strcmp (applet, "eoutdent") == 0)
206 eoutdent ();
207 else if (strcmp (applet, "veindent") == 0)
208 eindentv ();
209 else if (strcmp (applet, "veoutdent") == 0)
210 eoutdentv ();
211 else if (strcmp (applet, "eflush") == 0)
212 eflush ();
213 else {
214 eerror ("%s: unknown applet", applet);
215 retval = EXIT_FAILURE;
216 }
217
218 if (fmt)
219 free (fmt);
220 if (message)
221 free (message);
222 return (retval);
223 }
224
225 static int do_service (int argc, char **argv)
226 {
227 bool ok = false;
228
229 if (argc < 1 || ! argv[0] || strlen (argv[0]) == 0)
230 eerrorx ("%s: no service specified", applet);
231
232 if (strcmp (applet, "service_started") == 0)
233 ok = rc_service_state (argv[0], rc_service_started);
234 else if (strcmp (applet, "service_stopped") == 0)
235 ok = rc_service_state (argv[0], rc_service_stopped);
236 else if (strcmp (applet, "service_inactive") == 0)
237 ok = rc_service_state (argv[0], rc_service_inactive);
238 else if (strcmp (applet, "service_starting") == 0)
239 ok = rc_service_state (argv[0], rc_service_starting);
240 else if (strcmp (applet, "service_stopping") == 0)
241 ok = rc_service_state (argv[0], rc_service_stopping);
242 else if (strcmp (applet, "service_coldplugged") == 0)
243 ok = rc_service_state (argv[0], rc_service_coldplugged);
244 else if (strcmp (applet, "service_wasinactive") == 0)
245 ok = rc_service_state (argv[0], rc_service_wasinactive);
246 else if (strcmp (applet, "service_started_daemon") == 0) {
247 int idx = 0;
248 if (argc > 2)
249 sscanf (argv[2], "%d", &idx);
250 exit (rc_service_started_daemon (argv[0], argv[1], idx)
251 ? 0 : 1);
252 } else
253 eerrorx ("%s: unknown applet", applet);
254
255 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
256 }
257
258 static int do_mark_service (int argc, char **argv)
259 {
260 bool ok = false;
261 char *svcname = getenv ("SVCNAME");
262
263 if (argc < 1 || ! argv[0] || strlen (argv[0]) == 0)
264 eerrorx ("%s: no service specified", applet);
265
266 if (strcmp (applet, "mark_service_started") == 0)
267 ok = rc_mark_service (argv[0], rc_service_started);
268 else if (strcmp (applet, "mark_service_stopped") == 0)
269 ok = rc_mark_service (argv[0], rc_service_stopped);
270 else if (strcmp (applet, "mark_service_inactive") == 0)
271 ok = rc_mark_service (argv[0], rc_service_inactive);
272 else if (strcmp (applet, "mark_service_starting") == 0)
273 ok = rc_mark_service (argv[0], rc_service_starting);
274 else if (strcmp (applet, "mark_service_stopping") == 0)
275 ok = rc_mark_service (argv[0], rc_service_stopping);
276 else if (strcmp (applet, "mark_service_coldplugged") == 0)
277 ok = rc_mark_service (argv[0], rc_service_coldplugged);
278 else
279 eerrorx ("%s: unknown applet", applet);
280
281 /* If we're marking ourselves then we need to inform our parent runscript
282 process so they do not mark us based on our exit code */
283 if (ok && svcname && strcmp (svcname, argv[0]) == 0) {
284 char *runscript_pid = getenv ("RC_RUNSCRIPT_PID");
285 char *mtime;
286 pid_t pid = 0;
287 int l;
288
289 if (runscript_pid && sscanf (runscript_pid, "%d", &pid) == 1)
290 if (kill (pid, SIGHUP) != 0)
291 eerror ("%s: failed to signal parent %d: %s",
292 applet, pid, strerror (errno));
293
294 /* Remove the exclsive time test. This ensures that it's not
295 in control as well */
296 l = strlen (RC_SVCDIR "exclusive") +
297 strlen (svcname) +
298 strlen (runscript_pid) +
299 4;
300 mtime = rc_xmalloc (l);
301 snprintf (mtime, l, RC_SVCDIR "exclusive/%s.%s",
302 svcname, runscript_pid);
303 if (rc_exists (mtime) && unlink (mtime) != 0)
304 eerror ("%s: unlink: %s", applet, strerror (errno));
305 free (mtime);
306 }
307
308 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
309 }
310
311 static int do_options (int argc, char **argv)
312 {
313 bool ok = false;
314 char *service = getenv ("SVCNAME");
315
316 if (! service)
317 eerrorx ("%s: no service specified", applet);
318
319 if (argc < 1 || ! argv[0] || strlen (argv[0]) == 0)
320 eerrorx ("%s: no option specified", applet);
321
322 if (strcmp (applet, "get_options") == 0) {
323 char buffer[1024];
324 memset (buffer, 0, 1024);
325 ok = rc_get_service_option (service, argv[0], buffer);
326 if (ok)
327 printf ("%s", buffer);
328 } else if (strcmp (applet, "save_options") == 0)
329 ok = rc_set_service_option (service, argv[0], argv[1]);
330 else
331 eerrorx ("%s: unknown applet", applet);
332
333 return (ok ? EXIT_SUCCESS : EXIT_FAILURE);
334 }
335
336 static char read_key (bool block)
337 {
338 struct termios termios;
339 char c = 0;
340
341 if (! isatty (STDIN_FILENO))
342 return (false);
343
344 /* Now save our terminal settings. We need to restore them at exit as we
345 will be changing it for non-blocking reads for Interactive */
346 if (! termios_orig) {
347 termios_orig = rc_xmalloc (sizeof (struct termios));
348 tcgetattr (STDIN_FILENO, termios_orig);
349 }
350
351 tcgetattr (STDIN_FILENO, &termios);
352 termios.c_lflag &= ~(ICANON | ECHO);
353 if (block)
354 termios.c_cc[VMIN] = 1;
355 else {
356 termios.c_cc[VMIN] = 0;
357 termios.c_cc[VTIME] = 0;
358 }
359 tcsetattr (STDIN_FILENO, TCSANOW, &termios);
360
361 read (STDIN_FILENO, &c, 1);
362
363 tcsetattr (STDIN_FILENO, TCSANOW, termios_orig);
364
365 return (c);
366 }
367
368 static bool want_interactive (void)
369 {
370 char c = read_key (false);
371 return ((c == 'I' || c == 'i') ? true : false);
372 }
373
374 static void mark_interactive (void)
375 {
376 FILE *fp = fopen (INTERACTIVE, "w");
377 if (fp)
378 fclose (fp);
379 }
380
381 static void sulogin (bool cont)
382 {
383 #ifdef __linux__
384 char *e = getenv ("RC_SYS");
385
386 /* VPS systems cannot do an sulogin */
387 if (e && strcmp (e, "VPS") == 0) {
388 execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
389 eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno));
390 }
391 #endif
392
393 newenv = rc_filter_env ();
394
395 if (cont) {
396 int status = 0;
397 #ifdef __linux__
398 char *tty = ttyname (fileno (stdout));
399 #endif
400
401 pid_t pid = vfork ();
402
403 if (pid == -1)
404 eerrorx ("%s: vfork: %s", applet, strerror (errno));
405 if (pid == 0) {
406 #ifdef __linux__
407 if (tty)
408 execle (SULOGIN, SULOGIN, tty, (char *) NULL, newenv);
409 else
410 execle (SULOGIN, SULOGIN, (char *) NULL, newenv);
411
412 eerror ("%s: unable to exec `%s': %s", applet, SULOGIN,
413 strerror (errno));
414 #else
415 execle ("/bin/sh", "/bin/sh", (char *) NULL, newenv);
416 eerror ("%s: unable to exec `/bin/sh': %s", applet,
417 strerror (errno));
418 #endif
419 _exit (EXIT_FAILURE);
420 }
421 waitpid (pid, &status, 0);
422 } else {
423 #ifdef __linux
424 execle ("/sbin/sulogin", "/sbin/sulogin", (char *) NULL, newenv);
425 eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno));
426 #else
427 exit (EXIT_SUCCESS);
428 #endif
429 }
430 }
431
432 static void single_user (void)
433 {
434 #ifdef __linux__
435 execl ("/sbin/telinit", "/sbin/telinit", "S", (char *) NULL);
436 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
437 applet, strerror (errno));
438 #else
439 if (kill (1, SIGTERM) != 0)
440 eerrorx ("%s: unable to send SIGTERM to init (pid 1): %s",
441 applet, strerror (errno));
442 exit (EXIT_SUCCESS);
443 #endif
444 }
445
446 static void set_ksoftlevel (const char *runlevel)
447 {
448 FILE *fp;
449
450 if (! runlevel ||
451 strcmp (runlevel, RC_LEVEL_BOOT) == 0 ||
452 strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
453 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0)
454 {
455 if (rc_exists (RC_SVCDIR "ksoftlevel") &&
456 unlink (RC_SVCDIR "ksoftlevel") != 0)
457 eerror ("unlink `%s': %s", RC_SVCDIR "ksoftlevel", strerror (errno));
458 return;
459 }
460
461 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "w"))) {
462 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel", strerror (errno));
463 return;
464 }
465
466 fprintf (fp, "%s", runlevel);
467 fclose (fp);
468 }
469
470 static void wait_for_services ()
471 {
472 int status = 0;
473 while (wait (&status) != -1);
474 }
475
476 static void add_pid (pid_t pid)
477 {
478 pidlist_t *sp = service_pids;
479 if (sp) {
480 while (sp->next)
481 sp = sp->next;
482 sp->next = rc_xmalloc (sizeof (pidlist_t));
483 sp = sp->next;
484 } else
485 sp = service_pids = rc_xmalloc (sizeof (pidlist_t));
486 memset (sp, 0, sizeof (pidlist_t));
487 sp->pid = pid;
488 }
489
490 static void remove_pid (pid_t pid)
491 {
492 pidlist_t *last = NULL;
493 pidlist_t *pl;
494
495 for (pl = service_pids; pl; pl = pl->next) {
496 if (pl->pid == pid) {
497 if (last)
498 last->next = pl->next;
499 else
500 service_pids = pl->next;
501 free (pl);
502 break;
503 }
504 last = pl;
505 }
506 }
507
508 static void handle_signal (int sig)
509 {
510 int serrno = errno;
511 char signame[10] = { '\0' };
512 char *run;
513 char *prev;
514 pidlist_t *pl;
515 pid_t pid;
516 int status = 0;
517
518 switch (sig) {
519 case SIGCHLD:
520 do {
521 pid = waitpid (-1, &status, WNOHANG);
522 if (pid < 0) {
523 if (errno != ECHILD)
524 eerror ("waitpid: %s", strerror (errno));
525 return;
526 }
527 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
528
529 /* Remove that pid from our list */
530 if (pid > 0)
531 remove_pid (pid);
532 break;
533
534 case SIGINT:
535 if (! signame[0])
536 snprintf (signame, sizeof (signame), "SIGINT");
537 case SIGTERM:
538 if (! signame[0])
539 snprintf (signame, sizeof (signame), "SIGTERM");
540 case SIGQUIT:
541 if (! signame[0])
542 snprintf (signame, sizeof (signame), "SIGQUIT");
543 eerrorx ("%s: caught %s, aborting", applet, signame);
544 case SIGUSR1:
545 eerror ("rc: Aborting!");
546 /* Kill any running services we have started */
547
548 signal (SIGCHLD, SIG_IGN);
549 for (pl = service_pids; pl; pl = pl->next)
550 kill (pl->pid, SIGTERM);
551
552 /* Notify plugins we are aborting */
553 rc_plugin_run (rc_hook_abort, NULL);
554
555 /* Only drop into single user mode if we're booting */
556 run = getenv ("RUNLEVEL");
557 prev = getenv ("PREVLEVEL");
558 if ((prev &&
559 (strcmp (prev, "S") == 0 ||
560 strcmp (prev, "1") == 0)) ||
561 (run &&
562 (strcmp (run, "S") == 0 ||
563 strcmp (run, "1") == 0)))
564 single_user ();
565
566 exit (EXIT_FAILURE);
567 break;
568
569 default:
570 eerror ("%s: caught unknown signal %d", applet, sig);
571 }
572
573 /* Restore errno */
574 errno = serrno;
575 }
576
577 int main (int argc, char **argv)
578 {
579 char *RUNLEVEL = NULL;
580 char *PREVLEVEL = NULL;
581 char *runlevel = NULL;
582 char *newlevel = NULL;
583 char *service = NULL;
584 char **deporder = NULL;
585 int i = 0;
586 int j = 0;
587 bool going_down = false;
588 bool interactive = false;
589 int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
590 char ksoftbuffer [PATH_MAX];
591 char pidstr[6];
592
593 if (argv[0])
594 applet = rc_xstrdup (basename (argv[0]));
595
596 if (! applet)
597 eerrorx ("arguments required");
598
599 argc--;
600 argv++;
601
602 /* Handle multicall stuff */
603 if (applet[0] == 'e' || (applet[0] == 'v' && applet[1] == 'e'))
604 exit (do_e (argc, argv));
605
606 if (strncmp (applet, "service_", strlen ("service_")) == 0)
607 exit (do_service (argc, argv));
608
609 if (strcmp (applet, "get_options") == 0 ||
610 strcmp (applet, "save_options") == 0)
611 exit (do_options (argc, argv));
612
613 if (strncmp (applet, "mark_service_", strlen ("mark_service_")) == 0)
614 exit (do_mark_service (argc, argv));
615
616 if (strcmp (applet, "is_runlevel_start") == 0)
617 exit (rc_runlevel_starting () ? 0 : 1);
618 else if (strcmp (applet, "is_runlevel_stop") == 0)
619 exit (rc_runlevel_stopping () ? 0 : 1);
620
621 if (strcmp (applet, "rc-abort") == 0) {
622 char *p = getenv ("RC_PID");
623 pid_t pid = 0;
624
625 if (p && sscanf (p, "%d", &pid) == 1) {
626 if (kill (pid, SIGUSR1) != 0)
627 eerrorx ("rc-abort: failed to signal parent %d: %s",
628 pid, strerror (errno));
629 exit (EXIT_SUCCESS);
630 }
631 exit (EXIT_FAILURE);
632 }
633
634 if (strcmp (applet, "rc" ) != 0)
635 eerrorx ("%s: unknown applet", applet);
636
637 /* OK, so we really are the main RC process
638 Only root should be able to run us */
639 if (geteuid () != 0)
640 eerrorx ("%s: root access required", applet);
641
642 atexit (cleanup);
643 newlevel = argv[0];
644
645 /* Setup a signal handler */
646 signal (SIGINT, handle_signal);
647 signal (SIGQUIT, handle_signal);
648 signal (SIGTERM, handle_signal);
649 signal (SIGUSR1, handle_signal);
650
651 /* Ensure our environment is pure
652 Also, add our configuration to it */
653 env = rc_filter_env ();
654 env = rc_config_env (env);
655
656 if (env) {
657 char *p;
658
659 #ifdef __linux__
660 /* clearenv isn't portable, but there's no harm in using it
661 if we have it */
662 clearenv ();
663 #else
664 char *var;
665 /* No clearenv present here then.
666 We could manipulate environ directly ourselves, but it seems that
667 some kernels bitch about this according to the environ man pages
668 so we walk though environ and call unsetenv for each value. */
669 while (environ[0]) {
670 tmp = rc_xstrdup (environ[0]);
671 p = tmp;
672 var = strsep (&p, "=");
673 unsetenv (var);
674 free (tmp);
675 }
676 tmp = NULL;
677 #endif
678
679 STRLIST_FOREACH (env, p, i)
680 if (strcmp (p, "RC_SOFTLEVEL") != 0 && strcmp (p, "SOFTLEVEL") != 0)
681 putenv (p);
682
683 /* We don't free our list as that would be null in environ */
684 }
685
686 /* Enable logging */
687 setenv ("RC_ELOG", "rc", 1);
688
689 /* Export our PID */
690 snprintf (pidstr, sizeof (pidstr), "%d", getpid ());
691 setenv ("RC_PID", pidstr, 1);
692
693 interactive = rc_exists (INTERACTIVE);
694 rc_plugin_load ();
695
696 /* RUNLEVEL is set by sysvinit as is a magic number
697 RC_SOFTLEVEL is set by us and is the name for this magic number
698 even though all our userland documentation refers to runlevel */
699 RUNLEVEL = getenv ("RUNLEVEL");
700 PREVLEVEL = getenv ("PREVLEVEL");
701
702 if (RUNLEVEL && newlevel) {
703 if (strcmp (RUNLEVEL, "S") == 0 || strcmp (RUNLEVEL, "1") == 0) {
704 /* OK, we're either in runlevel 1 or single user mode */
705 if (strcmp (newlevel, RC_LEVEL_SYSINIT) == 0) {
706 struct utsname uts;
707 pid_t pid;
708 pid_t wpid;
709 int status = 0;
710 #ifdef __linux__
711 FILE *fp;
712 #endif
713
714 /* exec init-early.sh if it exists
715 * This should just setup the console to use the correct
716 * font. Maybe it should setup the keyboard too? */
717 if (rc_exists (INITEARLYSH)) {
718 if ((pid = vfork ()) == -1)
719 eerrorx ("%s: vfork: %s", applet, strerror (errno));
720
721 if (pid == 0) {
722 execl (INITEARLYSH, INITEARLYSH, (char *) NULL);
723 eerror ("%s: unable to exec `" INITEARLYSH "': %s",
724 applet, strerror (errno));
725 _exit (EXIT_FAILURE);
726 }
727
728 do {
729 wpid = waitpid (pid, &status, 0);
730 if (wpid < 1)
731 eerror ("waitpid: %s", strerror (errno));
732 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
733 }
734
735 uname (&uts);
736
737 printf ("\n");
738 printf (" %sGentoo/%s; %shttp://www.gentoo.org/%s"
739 "\n Copyright 1999-2007 Gentoo Foundation; "
740 "Distributed under the GPLv2\n\n",
741 ecolor (ecolor_good), uts.sysname, ecolor (ecolor_bracket),
742 ecolor (ecolor_normal));
743
744 printf ("Press %sI%s to enter interactive boot mode\n\n",
745 ecolor (ecolor_good), ecolor (ecolor_normal));
746
747 setenv ("RC_SOFTLEVEL", newlevel, 1);
748 rc_plugin_run (rc_hook_runlevel_start_in, newlevel);
749
750 if ((pid = vfork ()) == -1)
751 eerrorx ("%s: vfork: %s", applet, strerror (errno));
752
753 if (pid == 0) {
754 execl (INITSH, INITSH, (char *) NULL);
755 eerror ("%s: unable to exec `" INITSH "': %s",
756 applet, strerror (errno));
757 _exit (EXIT_FAILURE);
758 }
759
760 do {
761 wpid = waitpid (pid, &status, 0);
762 if (wpid < 1)
763 eerror ("waitpid: %s", strerror (errno));
764 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
765
766 if (! WIFEXITED (status) || ! WEXITSTATUS (status) == 0)
767 exit (EXIT_FAILURE);
768
769 /* If we requested a softlevel, save it now */
770 #ifdef __linux__
771 set_ksoftlevel (NULL);
772
773 if ((fp = fopen ("/proc/cmdline", "r"))) {
774 char buffer[RC_LINEBUFFER];
775 char *soft;
776
777 memset (buffer, 0, sizeof (buffer));
778 if (fgets (buffer, RC_LINEBUFFER, fp) &&
779 (soft = strstr (buffer, "softlevel=")))
780 {
781 i = soft - buffer;
782 if (i == 0 || buffer[i - 1] == ' ') {
783 char *level;
784
785 /* Trim the trailing carriage return if present */
786 i = strlen (buffer) - 1;
787 if (buffer[i] == '\n')
788 buffer[i] = 0;
789
790 soft += strlen ("softlevel=");
791 level = strsep (&soft, " ");
792 set_ksoftlevel (level);
793 }
794 }
795 fclose (fp);
796 }
797 #endif
798 rc_plugin_run (rc_hook_runlevel_start_out, newlevel);
799
800 if (want_interactive ())
801 mark_interactive ();
802
803 exit (EXIT_SUCCESS);
804 }
805
806 #ifdef __linux__
807 /* Parse the inittab file so we can work out the level to telinit */
808 if (strcmp (newlevel, RC_LEVEL_BOOT) != 0 &&
809 strcmp (newlevel, RC_LEVEL_SINGLE) != 0)
810 {
811 char **inittab = rc_get_list (NULL, "/etc/inittab");
812 char *line;
813 char *p;
814 char *token;
815 char lvl[2] = {0, 0};
816
817 STRLIST_FOREACH (inittab, line, i) {
818 p = line;
819 token = strsep (&p, ":");
820 if (! token || token[0] != 'l')
821 continue;
822
823 if ((token = strsep (&p, ":")) == NULL)
824 continue;
825
826 /* Snag the level */
827 lvl[0] = token[0];
828
829 /* The name is spaced after this */
830 if ((token = strsep (&p, " ")) == NULL)
831 continue;
832
833 if ((token = strsep (&p, " ")) == NULL)
834 continue;
835
836 if (strcmp (token, newlevel) == 0)
837 break;
838 }
839 rc_strlist_free (inittab);
840
841 /* We have a level, so telinit into it */
842 if (lvl[0] == 0) {
843 eerrorx ("%s: couldn't find a runlevel called `%s'",
844 applet, newlevel);
845 } else {
846 execl ("/sbin/telinit", "/sbin/telinit", lvl, (char *) NULL);
847 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
848 applet, strerror (errno));
849 }
850 }
851 #endif
852 }
853 }
854
855 /* Check we're in the runlevel requested, ie from
856 rc single
857 rc shutdown
858 rc reboot
859 */
860 if (newlevel) {
861 if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0) {
862 if (! RUNLEVEL ||
863 (strcmp (RUNLEVEL, "S") != 0 &&
864 strcmp (RUNLEVEL, "1") != 0))
865 {
866 /* Remember the current runlevel for when we come back */
867 set_ksoftlevel (runlevel);
868 single_user ();
869 }
870 } else if (strcmp (newlevel, RC_LEVEL_REBOOT) == 0) {
871 if (! RUNLEVEL ||
872 strcmp (RUNLEVEL, "6") != 0)
873 {
874 execl ("/sbin/shutdown", "/sbin/shutdown", "-r", "now", (char *) NULL);
875 eerrorx ("%s: unable to exec `/sbin/shutdown': %s",
876 applet, strerror (errno));
877 }
878 } else if (strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0) {
879 if (! RUNLEVEL ||
880 strcmp (RUNLEVEL, "0") != 0)
881 {
882 execl ("/sbin/shutdown", "/sbin/shutdown",
883 #ifdef __linux
884 "-h",
885 #else
886 "-p",
887 #endif
888 "now", (char *) NULL);
889 eerrorx ("%s: unable to exec `/sbin/shutdown': %s",
890 applet, strerror (errno));
891 }
892 }
893 }
894
895 /* Export our current softlevel */
896 runlevel = rc_get_runlevel ();
897
898 /* Now we start handling our children */
899 signal (SIGCHLD, handle_signal);
900
901 /* If we're in the default runlevel and ksoftlevel exists, we should use
902 that instead */
903 if (newlevel &&
904 rc_exists (RC_SVCDIR "ksoftlevel") &&
905 strcmp (newlevel, RC_LEVEL_DEFAULT) == 0)
906 {
907 /* We should only use ksoftlevel if we were in single user mode
908 If not, we need to erase ksoftlevel now. */
909 if (PREVLEVEL &&
910 (strcmp (PREVLEVEL, "1") == 0 ||
911 strcmp (PREVLEVEL, "S") == 0 ||
912 strcmp (PREVLEVEL, "N") == 0))
913 {
914 FILE *fp;
915
916 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
917 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
918 strerror (errno));
919 else {
920 if (fgets (ksoftbuffer, sizeof (ksoftbuffer), fp)) {
921 i = strlen (ksoftbuffer) - 1;
922 if (ksoftbuffer[i] == '\n')
923 ksoftbuffer[i] = 0;
924 newlevel = ksoftbuffer;
925 }
926 fclose (fp);
927 }
928 } else
929 set_ksoftlevel (NULL);
930 }
931
932 if (newlevel &&
933 (strcmp (newlevel, RC_LEVEL_REBOOT) == 0 ||
934 strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0 ||
935 strcmp (newlevel, RC_LEVEL_SINGLE) == 0))
936 {
937 going_down = true;
938 rc_set_runlevel (newlevel);
939 setenv ("RC_SOFTLEVEL", newlevel, 1);
940 rc_plugin_run (rc_hook_runlevel_stop_in, newlevel);
941 } else {
942 rc_plugin_run (rc_hook_runlevel_stop_in, runlevel);
943 }
944
945 /* Check if runlevel is valid if we're changing */
946 if (newlevel && strcmp (runlevel, newlevel) != 0 && ! going_down) {
947 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel, (char *) NULL);
948 if (! rc_is_dir (tmp))
949 eerrorx ("%s: is not a valid runlevel", newlevel);
950 CHAR_FREE (tmp);
951 }
952
953 /* Load our deptree now */
954 if ((deptree = rc_load_deptree ()) == NULL)
955 eerrorx ("failed to load deptree");
956
957 /* Clean the failed services state dir now */
958 if (rc_is_dir (RC_SVCDIR "failed"))
959 rc_rm_dir (RC_SVCDIR "failed", false);
960
961 mkdir (RC_SVCDIR "/softscripts.new", 0755);
962
963 #ifdef __linux__
964 /* udev likes to start services before we're ready when it does
965 its coldplugging thing. runscript knows when we're not ready so it
966 stores a list of coldplugged services in DEVBOOT for us to pick up
967 here when we are ready for them */
968 if (rc_is_dir (DEVBOOT)) {
969 start_services = rc_ls_dir (NULL, DEVBOOT, RC_LS_INITD);
970 rc_rm_dir (DEVBOOT, true);
971
972 STRLIST_FOREACH (start_services, service, i)
973 if (rc_allow_plug (service))
974 rc_mark_service (service, rc_service_coldplugged);
975 /* We need to dump this list now.
976 This may seem redunant, but only Linux needs this and saves on
977 code bloat. */
978 rc_strlist_free (start_services);
979 start_services = NULL;
980 }
981 #else
982 /* BSD's on the other hand populate /dev automagically and use devd.
983 The only downside of this approach and ours is that we have to hard code
984 the device node to the init script to simulate the coldplug into
985 runlevel for our dependency tree to work. */
986 if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0 &&
987 (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
988 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) &&
989 rc_is_env ("RC_COLDPLUG", "yes"))
990 {
991 /* The net interfaces are easy - they're all in net /dev/net :) */
992 start_services = rc_ls_dir (NULL, "/dev/net", 0);
993 STRLIST_FOREACH (start_services, service, i) {
994 j = (strlen ("net.") + strlen (service) + 1);
995 tmp = rc_xmalloc (sizeof (char *) * j);
996 snprintf (tmp, j, "net.%s", service);
997 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
998 rc_mark_service (tmp, rc_service_coldplugged);
999 CHAR_FREE (tmp);
1000 }
1001 rc_strlist_free (start_services);
1002
1003 /* The mice are a little more tricky.
1004 If we coldplug anything else, we'll probably do it here. */
1005 start_services = rc_ls_dir (NULL, "/dev", 0);
1006 STRLIST_FOREACH (start_services, service, i) {
1007 if (strncmp (service, "psm", 3) == 0 ||
1008 strncmp (service, "ums", 3) == 0)
1009 {
1010 char *p = service + 3;
1011 if (p && isdigit (*p)) {
1012 j = (strlen ("moused.") + strlen (service) + 1);
1013 tmp = rc_xmalloc (sizeof (char *) * j);
1014 snprintf (tmp, j, "moused.%s", service);
1015 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
1016 rc_mark_service (tmp, rc_service_coldplugged);
1017 CHAR_FREE (tmp);
1018 }
1019 }
1020 }
1021 rc_strlist_free (start_services);
1022 start_services = NULL;
1023 }
1024 #endif
1025
1026 /* Build a list of all services to stop and then work out the
1027 correct order for stopping them */
1028 stop_services = rc_ls_dir (stop_services, RC_SVCDIR_STARTING, RC_LS_INITD);
1029 stop_services = rc_ls_dir (stop_services, RC_SVCDIR_INACTIVE, RC_LS_INITD);
1030 stop_services = rc_ls_dir (stop_services, RC_SVCDIR_STARTED, RC_LS_INITD);
1031
1032 types = rc_strlist_add (NULL, "ineed");
1033 types = rc_strlist_add (types, "iuse");
1034 types = rc_strlist_add (types, "iafter");
1035 deporder = rc_get_depends (deptree, types, stop_services,
1036 runlevel, depoptions);
1037 rc_strlist_free (stop_services);
1038 rc_strlist_free (types);
1039 stop_services = deporder;
1040 deporder = NULL;
1041 types = NULL;
1042 rc_strlist_reverse (stop_services);
1043
1044 /* Load our list of coldplugged services */
1045 coldplugged_services = rc_ls_dir (coldplugged_services,
1046 RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
1047
1048 /* Load our start services now.
1049 We have different rules dependent on runlevel. */
1050 if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0) {
1051 if (coldplugged_services) {
1052 einfon ("Device initiated services:");
1053 STRLIST_FOREACH (coldplugged_services, service, i) {
1054 printf (" %s", service);
1055 start_services = rc_strlist_add (start_services, service);
1056 }
1057 printf ("\n");
1058 }
1059 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel ? newlevel : runlevel,
1060 (char *) NULL);
1061 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
1062 CHAR_FREE (tmp);
1063 } else {
1064 /* Store our list of coldplugged services */
1065 coldplugged_services = rc_ls_dir (coldplugged_services, RC_SVCDIR_COLDPLUGGED,
1066 RC_LS_INITD);
1067 if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 &&
1068 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
1069 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0)
1070 {
1071 /* We need to include the boot runlevel services if we're not in it */
1072 start_services = rc_ls_dir (start_services, RC_RUNLEVELDIR RC_LEVEL_BOOT,
1073 RC_LS_INITD);
1074 STRLIST_FOREACH (coldplugged_services, service, i)
1075 start_services = rc_strlist_add (start_services, service);
1076
1077 tmp = rc_strcatpaths (RC_RUNLEVELDIR,
1078 newlevel ? newlevel : runlevel, (char *) NULL);
1079 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
1080 CHAR_FREE (tmp);
1081 }
1082 }
1083
1084 /* Save out softlevel now */
1085 if (going_down)
1086 rc_set_runlevel (newlevel);
1087
1088 types = rc_strlist_add (NULL, "needsme");
1089 types = rc_strlist_add (types, "usesme");
1090 /* Now stop the services that shouldn't be running */
1091 STRLIST_FOREACH (stop_services, service, i) {
1092 bool found = false;
1093 char *conf = NULL;
1094 char **stopdeps = NULL;
1095 char *svc1 = NULL;
1096 char *svc2 = NULL;
1097 int k;
1098
1099 if (rc_service_state (service, rc_service_stopped))
1100 continue;
1101
1102 /* We always stop the service when in these runlevels */
1103 if (going_down) {
1104 pid_t pid = rc_stop_service (service);
1105 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1106 rc_waitpid (pid);
1107 continue;
1108 }
1109
1110 /* If we're in the start list then don't bother stopping us */
1111 STRLIST_FOREACH (start_services, svc1, j)
1112 if (strcmp (svc1, service) == 0) {
1113 found = true;
1114 break;
1115 }
1116
1117 /* Unless we would use a different config file */
1118 if (found) {
1119 int len;
1120 if (! newlevel)
1121 continue;
1122
1123 len = strlen (service) + strlen (runlevel) + 2;
1124 tmp = rc_xmalloc (sizeof (char *) * len);
1125 snprintf (tmp, len, "%s.%s", service, runlevel);
1126 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1127 found = rc_exists (conf);
1128 CHAR_FREE (conf);
1129 CHAR_FREE (tmp);
1130 if (! found) {
1131 len = strlen (service) + strlen (newlevel) + 2;
1132 tmp = rc_xmalloc (sizeof (char *) * len);
1133 snprintf (tmp, len, "%s.%s", service, newlevel);
1134 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1135 found = rc_exists (conf);
1136 CHAR_FREE (conf);
1137 CHAR_FREE (tmp);
1138 if (!found)
1139 continue;
1140 }
1141 } else {
1142 /* Allow coldplugged services not to be in the runlevels list */
1143 if (rc_service_state (service, rc_service_coldplugged))
1144 continue;
1145 }
1146
1147 /* We got this far! Or last check is to see if any any service that
1148 going to be started depends on us */
1149 stopdeps = rc_strlist_add (stopdeps, service);
1150 deporder = rc_get_depends (deptree, types, stopdeps,
1151 runlevel, RC_DEP_STRICT);
1152 rc_strlist_free (stopdeps);
1153 stopdeps = NULL;
1154 found = false;
1155 STRLIST_FOREACH (deporder, svc1, j) {
1156 STRLIST_FOREACH (start_services, svc2, k)
1157 if (strcmp (svc1, svc2) == 0) {
1158 found = true;
1159 break;
1160 }
1161 if (found)
1162 break;
1163 }
1164 rc_strlist_free (deporder);
1165 deporder = NULL;
1166
1167 /* After all that we can finally stop the blighter! */
1168 if (! found) {
1169 pid_t pid = rc_stop_service (service);
1170 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1171 rc_waitpid (pid);
1172 }
1173 }
1174 rc_strlist_free (types);
1175 types = NULL;
1176
1177 /* Wait for our services to finish */
1178 wait_for_services ();
1179
1180 /* Notify the plugins we have finished */
1181 rc_plugin_run (rc_hook_runlevel_stop_out, runlevel);
1182
1183 rmdir (RC_SVCDIR "/softscripts.new");
1184
1185 /* Store the new runlevel */
1186 if (newlevel) {
1187 rc_set_runlevel (newlevel);
1188 runlevel = newlevel;
1189 setenv ("RC_SOFTLEVEL", runlevel, 1);
1190 }
1191
1192 /* Run the halt script if needed */
1193 if (strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
1194 strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
1195 {
1196 execl (HALTSH, HALTSH, runlevel, (char *) NULL);
1197 eerrorx ("%s: unable to exec `%s': %s",
1198 applet, HALTSH, strerror (errno));
1199 }
1200
1201 /* Single user is done now */
1202 if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0) {
1203 if (rc_exists (INTERACTIVE))
1204 unlink (INTERACTIVE);
1205 sulogin (false);
1206 }
1207
1208 mkdir (RC_SVCDIR "softscripts.old", 0755);
1209 rc_plugin_run (rc_hook_runlevel_start_in, runlevel);
1210
1211 /* Re-add our coldplugged services if they stopped */
1212 STRLIST_FOREACH (coldplugged_services, service, i)
1213 rc_mark_service (service, rc_service_coldplugged);
1214
1215 /* Order the services to start */
1216 types = rc_strlist_add (NULL, "ineed");
1217 types = rc_strlist_add (types, "iuse");
1218 types = rc_strlist_add (types, "iafter");
1219 deporder = rc_get_depends (deptree, types, start_services,
1220 runlevel, depoptions);
1221 rc_strlist_free (types);
1222 types = NULL;
1223 rc_strlist_free (start_services);
1224 start_services = deporder;
1225 deporder = NULL;
1226
1227 STRLIST_FOREACH (start_services, service, i) {
1228 if (rc_service_state (service, rc_service_stopped)) {
1229 pid_t pid;
1230
1231 if (! interactive)
1232 interactive = want_interactive ();
1233
1234 if (interactive) {
1235 interactive_retry:
1236 printf ("\n");
1237 einfo ("About to start the service %s", service);
1238 eindent ();
1239 einfo ("1) Start the service\t\t2) Skip the service");
1240 einfo ("3) Continue boot process\t\t4) Exit to shell");
1241 eoutdent ();
1242 interactive_option:
1243 switch (read_key (true)) {
1244 case '1': break;
1245 case '2': continue;
1246 case '3': interactive = false; break;
1247 case '4': sulogin (true); goto interactive_retry;
1248 default: goto interactive_option;
1249 }
1250 }
1251
1252 /* Remember the pid if we're running in parallel */
1253 if ((pid = rc_start_service (service)))
1254 add_pid (pid);
1255
1256 if (! rc_is_env ("RC_PARALLEL", "yes")) {
1257 rc_waitpid (pid);
1258 remove_pid (pid);
1259 }
1260 }
1261 }
1262
1263 /* Wait for our services to finish */
1264 wait_for_services ();
1265
1266 rc_plugin_run (rc_hook_runlevel_start_out, runlevel);
1267
1268 /* Store our interactive status for boot */
1269 if (interactive && strcmp (runlevel, RC_LEVEL_BOOT) == 0)
1270 mark_interactive ();
1271 else {
1272 if (rc_exists (INTERACTIVE))
1273 unlink (INTERACTIVE);
1274 }
1275
1276 return (EXIT_SUCCESS);
1277 }

  ViewVC Help
Powered by ViewVC 1.1.20