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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2700 - (show annotations) (download) (as text)
Mon May 14 12:48:37 2007 UTC (7 years, 7 months ago) by uberlord
File MIME type: text/x-csrc
File size: 34873 byte(s)
RC_INTERACTIVE now works, #178331.
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;
371
372 if (! rc_is_env ("RC_INTERACTIVE", "yes"))
373 return (false);
374
375 c = read_key (false);
376 return ((c == 'I' || c == 'i') ? true : false);
377 }
378
379 static void mark_interactive (void)
380 {
381 FILE *fp = fopen (INTERACTIVE, "w");
382 if (fp)
383 fclose (fp);
384 }
385
386 static void sulogin (bool cont)
387 {
388 #ifdef __linux__
389 char *e = getenv ("RC_SYS");
390
391 /* VPS systems cannot do an sulogin */
392 if (e && strcmp (e, "VPS") == 0) {
393 execl ("/sbin/halt", "/sbin/halt", "-f", (char *) NULL);
394 eerrorx ("%s: unable to exec `/sbin/halt': %s", applet, strerror (errno));
395 }
396 #endif
397
398 newenv = rc_filter_env ();
399
400 if (cont) {
401 int status = 0;
402 #ifdef __linux__
403 char *tty = ttyname (fileno (stdout));
404 #endif
405
406 pid_t pid = vfork ();
407
408 if (pid == -1)
409 eerrorx ("%s: vfork: %s", applet, strerror (errno));
410 if (pid == 0) {
411 #ifdef __linux__
412 if (tty)
413 execle (SULOGIN, SULOGIN, tty, (char *) NULL, newenv);
414 else
415 execle (SULOGIN, SULOGIN, (char *) NULL, newenv);
416
417 eerror ("%s: unable to exec `%s': %s", applet, SULOGIN,
418 strerror (errno));
419 #else
420 execle ("/bin/sh", "/bin/sh", (char *) NULL, newenv);
421 eerror ("%s: unable to exec `/bin/sh': %s", applet,
422 strerror (errno));
423 #endif
424 _exit (EXIT_FAILURE);
425 }
426 waitpid (pid, &status, 0);
427 } else {
428 #ifdef __linux
429 execle ("/sbin/sulogin", "/sbin/sulogin", (char *) NULL, newenv);
430 eerrorx ("%s: unable to exec `/sbin/sulogin': %s", applet, strerror (errno));
431 #else
432 exit (EXIT_SUCCESS);
433 #endif
434 }
435 }
436
437 static void single_user (void)
438 {
439 #ifdef __linux__
440 execl ("/sbin/telinit", "/sbin/telinit", "S", (char *) NULL);
441 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
442 applet, strerror (errno));
443 #else
444 if (kill (1, SIGTERM) != 0)
445 eerrorx ("%s: unable to send SIGTERM to init (pid 1): %s",
446 applet, strerror (errno));
447 exit (EXIT_SUCCESS);
448 #endif
449 }
450
451 static void set_ksoftlevel (const char *runlevel)
452 {
453 FILE *fp;
454
455 if (! runlevel ||
456 strcmp (runlevel, RC_LEVEL_BOOT) == 0 ||
457 strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
458 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0)
459 {
460 if (rc_exists (RC_SVCDIR "ksoftlevel") &&
461 unlink (RC_SVCDIR "ksoftlevel") != 0)
462 eerror ("unlink `%s': %s", RC_SVCDIR "ksoftlevel", strerror (errno));
463 return;
464 }
465
466 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "w"))) {
467 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel", strerror (errno));
468 return;
469 }
470
471 fprintf (fp, "%s", runlevel);
472 fclose (fp);
473 }
474
475 static void wait_for_services ()
476 {
477 int status = 0;
478 while (wait (&status) != -1);
479 }
480
481 static void add_pid (pid_t pid)
482 {
483 pidlist_t *sp = service_pids;
484 if (sp) {
485 while (sp->next)
486 sp = sp->next;
487 sp->next = rc_xmalloc (sizeof (pidlist_t));
488 sp = sp->next;
489 } else
490 sp = service_pids = rc_xmalloc (sizeof (pidlist_t));
491 memset (sp, 0, sizeof (pidlist_t));
492 sp->pid = pid;
493 }
494
495 static void remove_pid (pid_t pid)
496 {
497 pidlist_t *last = NULL;
498 pidlist_t *pl;
499
500 for (pl = service_pids; pl; pl = pl->next) {
501 if (pl->pid == pid) {
502 if (last)
503 last->next = pl->next;
504 else
505 service_pids = pl->next;
506 free (pl);
507 break;
508 }
509 last = pl;
510 }
511 }
512
513 static void handle_signal (int sig)
514 {
515 int serrno = errno;
516 char signame[10] = { '\0' };
517 char *run;
518 char *prev;
519 pidlist_t *pl;
520 pid_t pid;
521 int status = 0;
522
523 switch (sig) {
524 case SIGCHLD:
525 do {
526 pid = waitpid (-1, &status, WNOHANG);
527 if (pid < 0) {
528 if (errno != ECHILD)
529 eerror ("waitpid: %s", strerror (errno));
530 return;
531 }
532 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
533
534 /* Remove that pid from our list */
535 if (pid > 0)
536 remove_pid (pid);
537 break;
538
539 case SIGINT:
540 if (! signame[0])
541 snprintf (signame, sizeof (signame), "SIGINT");
542 case SIGTERM:
543 if (! signame[0])
544 snprintf (signame, sizeof (signame), "SIGTERM");
545 case SIGQUIT:
546 if (! signame[0])
547 snprintf (signame, sizeof (signame), "SIGQUIT");
548 eerrorx ("%s: caught %s, aborting", applet, signame);
549 case SIGUSR1:
550 eerror ("rc: Aborting!");
551 /* Kill any running services we have started */
552
553 signal (SIGCHLD, SIG_IGN);
554 for (pl = service_pids; pl; pl = pl->next)
555 kill (pl->pid, SIGTERM);
556
557 /* Notify plugins we are aborting */
558 rc_plugin_run (rc_hook_abort, NULL);
559
560 /* Only drop into single user mode if we're booting */
561 run = getenv ("RUNLEVEL");
562 prev = getenv ("PREVLEVEL");
563 if ((prev &&
564 (strcmp (prev, "S") == 0 ||
565 strcmp (prev, "1") == 0)) ||
566 (run &&
567 (strcmp (run, "S") == 0 ||
568 strcmp (run, "1") == 0)))
569 single_user ();
570
571 exit (EXIT_FAILURE);
572 break;
573
574 default:
575 eerror ("%s: caught unknown signal %d", applet, sig);
576 }
577
578 /* Restore errno */
579 errno = serrno;
580 }
581
582 int main (int argc, char **argv)
583 {
584 char *RUNLEVEL = NULL;
585 char *PREVLEVEL = NULL;
586 char *runlevel = NULL;
587 char *newlevel = NULL;
588 char *service = NULL;
589 char **deporder = NULL;
590 int i = 0;
591 int j = 0;
592 bool going_down = false;
593 bool interactive = false;
594 int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
595 char ksoftbuffer [PATH_MAX];
596 char pidstr[6];
597
598 if (argv[0])
599 applet = rc_xstrdup (basename (argv[0]));
600
601 if (! applet)
602 eerrorx ("arguments required");
603
604 argc--;
605 argv++;
606
607 /* Handle multicall stuff */
608 if (applet[0] == 'e' || (applet[0] == 'v' && applet[1] == 'e'))
609 exit (do_e (argc, argv));
610
611 if (strncmp (applet, "service_", strlen ("service_")) == 0)
612 exit (do_service (argc, argv));
613
614 if (strcmp (applet, "get_options") == 0 ||
615 strcmp (applet, "save_options") == 0)
616 exit (do_options (argc, argv));
617
618 if (strncmp (applet, "mark_service_", strlen ("mark_service_")) == 0)
619 exit (do_mark_service (argc, argv));
620
621 if (strcmp (applet, "is_runlevel_start") == 0)
622 exit (rc_runlevel_starting () ? 0 : 1);
623 else if (strcmp (applet, "is_runlevel_stop") == 0)
624 exit (rc_runlevel_stopping () ? 0 : 1);
625
626 if (strcmp (applet, "rc-abort") == 0) {
627 char *p = getenv ("RC_PID");
628 pid_t pid = 0;
629
630 if (p && sscanf (p, "%d", &pid) == 1) {
631 if (kill (pid, SIGUSR1) != 0)
632 eerrorx ("rc-abort: failed to signal parent %d: %s",
633 pid, strerror (errno));
634 exit (EXIT_SUCCESS);
635 }
636 exit (EXIT_FAILURE);
637 }
638
639 if (strcmp (applet, "rc" ) != 0)
640 eerrorx ("%s: unknown applet", applet);
641
642 /* OK, so we really are the main RC process
643 Only root should be able to run us */
644 if (geteuid () != 0)
645 eerrorx ("%s: root access required", applet);
646
647 atexit (cleanup);
648 newlevel = argv[0];
649
650 /* Setup a signal handler */
651 signal (SIGINT, handle_signal);
652 signal (SIGQUIT, handle_signal);
653 signal (SIGTERM, handle_signal);
654 signal (SIGUSR1, handle_signal);
655
656 /* Ensure our environment is pure
657 Also, add our configuration to it */
658 env = rc_filter_env ();
659 env = rc_config_env (env);
660
661 if (env) {
662 char *p;
663
664 #ifdef __linux__
665 /* clearenv isn't portable, but there's no harm in using it
666 if we have it */
667 clearenv ();
668 #else
669 char *var;
670 /* No clearenv present here then.
671 We could manipulate environ directly ourselves, but it seems that
672 some kernels bitch about this according to the environ man pages
673 so we walk though environ and call unsetenv for each value. */
674 while (environ[0]) {
675 tmp = rc_xstrdup (environ[0]);
676 p = tmp;
677 var = strsep (&p, "=");
678 unsetenv (var);
679 free (tmp);
680 }
681 tmp = NULL;
682 #endif
683
684 STRLIST_FOREACH (env, p, i)
685 if (strcmp (p, "RC_SOFTLEVEL") != 0 && strcmp (p, "SOFTLEVEL") != 0)
686 putenv (p);
687
688 /* We don't free our list as that would be null in environ */
689 }
690
691 /* Enable logging */
692 setenv ("RC_ELOG", "rc", 1);
693
694 /* Export our PID */
695 snprintf (pidstr, sizeof (pidstr), "%d", getpid ());
696 setenv ("RC_PID", pidstr, 1);
697
698 interactive = rc_exists (INTERACTIVE);
699 rc_plugin_load ();
700
701 /* RUNLEVEL is set by sysvinit as is a magic number
702 RC_SOFTLEVEL is set by us and is the name for this magic number
703 even though all our userland documentation refers to runlevel */
704 RUNLEVEL = getenv ("RUNLEVEL");
705 PREVLEVEL = getenv ("PREVLEVEL");
706
707 if (RUNLEVEL && newlevel) {
708 if (strcmp (RUNLEVEL, "S") == 0 || strcmp (RUNLEVEL, "1") == 0) {
709 /* OK, we're either in runlevel 1 or single user mode */
710 if (strcmp (newlevel, RC_LEVEL_SYSINIT) == 0) {
711 struct utsname uts;
712 pid_t pid;
713 pid_t wpid;
714 int status = 0;
715 #ifdef __linux__
716 FILE *fp;
717 #endif
718
719 /* exec init-early.sh if it exists
720 * This should just setup the console to use the correct
721 * font. Maybe it should setup the keyboard too? */
722 if (rc_exists (INITEARLYSH)) {
723 if ((pid = vfork ()) == -1)
724 eerrorx ("%s: vfork: %s", applet, strerror (errno));
725
726 if (pid == 0) {
727 execl (INITEARLYSH, INITEARLYSH, (char *) NULL);
728 eerror ("%s: unable to exec `" INITEARLYSH "': %s",
729 applet, strerror (errno));
730 _exit (EXIT_FAILURE);
731 }
732
733 do {
734 wpid = waitpid (pid, &status, 0);
735 if (wpid < 1)
736 eerror ("waitpid: %s", strerror (errno));
737 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
738 }
739
740 uname (&uts);
741
742 printf ("\n");
743 printf (" %sGentoo/%s; %shttp://www.gentoo.org/%s"
744 "\n Copyright 1999-2007 Gentoo Foundation; "
745 "Distributed under the GPLv2\n\n",
746 ecolor (ecolor_good), uts.sysname, ecolor (ecolor_bracket),
747 ecolor (ecolor_normal));
748
749 if (rc_is_env ("RC_INTERACTIVE", "yes"))
750 printf ("Press %sI%s to enter interactive boot mode\n\n",
751 ecolor (ecolor_good), ecolor (ecolor_normal));
752
753 setenv ("RC_SOFTLEVEL", newlevel, 1);
754 rc_plugin_run (rc_hook_runlevel_start_in, newlevel);
755
756 if ((pid = vfork ()) == -1)
757 eerrorx ("%s: vfork: %s", applet, strerror (errno));
758
759 if (pid == 0) {
760 execl (INITSH, INITSH, (char *) NULL);
761 eerror ("%s: unable to exec `" INITSH "': %s",
762 applet, strerror (errno));
763 _exit (EXIT_FAILURE);
764 }
765
766 do {
767 wpid = waitpid (pid, &status, 0);
768 if (wpid < 1)
769 eerror ("waitpid: %s", strerror (errno));
770 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
771
772 if (! WIFEXITED (status) || ! WEXITSTATUS (status) == 0)
773 exit (EXIT_FAILURE);
774
775 /* If we requested a softlevel, save it now */
776 #ifdef __linux__
777 set_ksoftlevel (NULL);
778
779 if ((fp = fopen ("/proc/cmdline", "r"))) {
780 char buffer[RC_LINEBUFFER];
781 char *soft;
782
783 memset (buffer, 0, sizeof (buffer));
784 if (fgets (buffer, RC_LINEBUFFER, fp) &&
785 (soft = strstr (buffer, "softlevel=")))
786 {
787 i = soft - buffer;
788 if (i == 0 || buffer[i - 1] == ' ') {
789 char *level;
790
791 /* Trim the trailing carriage return if present */
792 i = strlen (buffer) - 1;
793 if (buffer[i] == '\n')
794 buffer[i] = 0;
795
796 soft += strlen ("softlevel=");
797 level = strsep (&soft, " ");
798 set_ksoftlevel (level);
799 }
800 }
801 fclose (fp);
802 }
803 #endif
804 rc_plugin_run (rc_hook_runlevel_start_out, newlevel);
805
806 if (want_interactive ())
807 mark_interactive ();
808
809 exit (EXIT_SUCCESS);
810 }
811
812 #ifdef __linux__
813 /* Parse the inittab file so we can work out the level to telinit */
814 if (strcmp (newlevel, RC_LEVEL_BOOT) != 0 &&
815 strcmp (newlevel, RC_LEVEL_SINGLE) != 0)
816 {
817 char **inittab = rc_get_list (NULL, "/etc/inittab");
818 char *line;
819 char *p;
820 char *token;
821 char lvl[2] = {0, 0};
822
823 STRLIST_FOREACH (inittab, line, i) {
824 p = line;
825 token = strsep (&p, ":");
826 if (! token || token[0] != 'l')
827 continue;
828
829 if ((token = strsep (&p, ":")) == NULL)
830 continue;
831
832 /* Snag the level */
833 lvl[0] = token[0];
834
835 /* The name is spaced after this */
836 if ((token = strsep (&p, " ")) == NULL)
837 continue;
838
839 if ((token = strsep (&p, " ")) == NULL)
840 continue;
841
842 if (strcmp (token, newlevel) == 0)
843 break;
844 }
845 rc_strlist_free (inittab);
846
847 /* We have a level, so telinit into it */
848 if (lvl[0] == 0) {
849 eerrorx ("%s: couldn't find a runlevel called `%s'",
850 applet, newlevel);
851 } else {
852 execl ("/sbin/telinit", "/sbin/telinit", lvl, (char *) NULL);
853 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
854 applet, strerror (errno));
855 }
856 }
857 #endif
858 }
859 }
860
861 /* Check we're in the runlevel requested, ie from
862 rc single
863 rc shutdown
864 rc reboot
865 */
866 if (newlevel) {
867 if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0) {
868 if (! RUNLEVEL ||
869 (strcmp (RUNLEVEL, "S") != 0 &&
870 strcmp (RUNLEVEL, "1") != 0))
871 {
872 /* Remember the current runlevel for when we come back */
873 set_ksoftlevel (runlevel);
874 single_user ();
875 }
876 } else if (strcmp (newlevel, RC_LEVEL_REBOOT) == 0) {
877 if (! RUNLEVEL ||
878 strcmp (RUNLEVEL, "6") != 0)
879 {
880 execl ("/sbin/shutdown", "/sbin/shutdown", "-r", "now", (char *) NULL);
881 eerrorx ("%s: unable to exec `/sbin/shutdown': %s",
882 applet, strerror (errno));
883 }
884 } else if (strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0) {
885 if (! RUNLEVEL ||
886 strcmp (RUNLEVEL, "0") != 0)
887 {
888 execl ("/sbin/shutdown", "/sbin/shutdown",
889 #ifdef __linux
890 "-h",
891 #else
892 "-p",
893 #endif
894 "now", (char *) NULL);
895 eerrorx ("%s: unable to exec `/sbin/shutdown': %s",
896 applet, strerror (errno));
897 }
898 }
899 }
900
901 /* Export our current softlevel */
902 runlevel = rc_get_runlevel ();
903
904 /* Now we start handling our children */
905 signal (SIGCHLD, handle_signal);
906
907 /* If we're in the default runlevel and ksoftlevel exists, we should use
908 that instead */
909 if (newlevel &&
910 rc_exists (RC_SVCDIR "ksoftlevel") &&
911 strcmp (newlevel, RC_LEVEL_DEFAULT) == 0)
912 {
913 /* We should only use ksoftlevel if we were in single user mode
914 If not, we need to erase ksoftlevel now. */
915 if (PREVLEVEL &&
916 (strcmp (PREVLEVEL, "1") == 0 ||
917 strcmp (PREVLEVEL, "S") == 0 ||
918 strcmp (PREVLEVEL, "N") == 0))
919 {
920 FILE *fp;
921
922 if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
923 eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
924 strerror (errno));
925 else {
926 if (fgets (ksoftbuffer, sizeof (ksoftbuffer), fp)) {
927 i = strlen (ksoftbuffer) - 1;
928 if (ksoftbuffer[i] == '\n')
929 ksoftbuffer[i] = 0;
930 newlevel = ksoftbuffer;
931 }
932 fclose (fp);
933 }
934 } else
935 set_ksoftlevel (NULL);
936 }
937
938 if (newlevel &&
939 (strcmp (newlevel, RC_LEVEL_REBOOT) == 0 ||
940 strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0 ||
941 strcmp (newlevel, RC_LEVEL_SINGLE) == 0))
942 {
943 going_down = true;
944 rc_set_runlevel (newlevel);
945 setenv ("RC_SOFTLEVEL", newlevel, 1);
946 rc_plugin_run (rc_hook_runlevel_stop_in, newlevel);
947 } else {
948 rc_plugin_run (rc_hook_runlevel_stop_in, runlevel);
949 }
950
951 /* Check if runlevel is valid if we're changing */
952 if (newlevel && strcmp (runlevel, newlevel) != 0 && ! going_down) {
953 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel, (char *) NULL);
954 if (! rc_is_dir (tmp))
955 eerrorx ("%s: is not a valid runlevel", newlevel);
956 CHAR_FREE (tmp);
957 }
958
959 /* Load our deptree now */
960 if ((deptree = rc_load_deptree ()) == NULL)
961 eerrorx ("failed to load deptree");
962
963 /* Clean the failed services state dir now */
964 if (rc_is_dir (RC_SVCDIR "failed"))
965 rc_rm_dir (RC_SVCDIR "failed", false);
966
967 mkdir (RC_SVCDIR "/softscripts.new", 0755);
968
969 #ifdef __linux__
970 /* udev likes to start services before we're ready when it does
971 its coldplugging thing. runscript knows when we're not ready so it
972 stores a list of coldplugged services in DEVBOOT for us to pick up
973 here when we are ready for them */
974 if (rc_is_dir (DEVBOOT)) {
975 start_services = rc_ls_dir (NULL, DEVBOOT, RC_LS_INITD);
976 rc_rm_dir (DEVBOOT, true);
977
978 STRLIST_FOREACH (start_services, service, i)
979 if (rc_allow_plug (service))
980 rc_mark_service (service, rc_service_coldplugged);
981 /* We need to dump this list now.
982 This may seem redunant, but only Linux needs this and saves on
983 code bloat. */
984 rc_strlist_free (start_services);
985 start_services = NULL;
986 }
987 #else
988 /* BSD's on the other hand populate /dev automagically and use devd.
989 The only downside of this approach and ours is that we have to hard code
990 the device node to the init script to simulate the coldplug into
991 runlevel for our dependency tree to work. */
992 if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0 &&
993 (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
994 strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) &&
995 rc_is_env ("RC_COLDPLUG", "yes"))
996 {
997 /* The net interfaces are easy - they're all in net /dev/net :) */
998 start_services = rc_ls_dir (NULL, "/dev/net", 0);
999 STRLIST_FOREACH (start_services, service, i) {
1000 j = (strlen ("net.") + strlen (service) + 1);
1001 tmp = rc_xmalloc (sizeof (char *) * j);
1002 snprintf (tmp, j, "net.%s", service);
1003 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
1004 rc_mark_service (tmp, rc_service_coldplugged);
1005 CHAR_FREE (tmp);
1006 }
1007 rc_strlist_free (start_services);
1008
1009 /* The mice are a little more tricky.
1010 If we coldplug anything else, we'll probably do it here. */
1011 start_services = rc_ls_dir (NULL, "/dev", 0);
1012 STRLIST_FOREACH (start_services, service, i) {
1013 if (strncmp (service, "psm", 3) == 0 ||
1014 strncmp (service, "ums", 3) == 0)
1015 {
1016 char *p = service + 3;
1017 if (p && isdigit (*p)) {
1018 j = (strlen ("moused.") + strlen (service) + 1);
1019 tmp = rc_xmalloc (sizeof (char *) * j);
1020 snprintf (tmp, j, "moused.%s", service);
1021 if (rc_service_exists (tmp) && rc_allow_plug (tmp))
1022 rc_mark_service (tmp, rc_service_coldplugged);
1023 CHAR_FREE (tmp);
1024 }
1025 }
1026 }
1027 rc_strlist_free (start_services);
1028 start_services = NULL;
1029 }
1030 #endif
1031
1032 /* Build a list of all services to stop and then work out the
1033 correct order for stopping them */
1034 stop_services = rc_ls_dir (stop_services, RC_SVCDIR_STARTING, RC_LS_INITD);
1035 stop_services = rc_ls_dir (stop_services, RC_SVCDIR_INACTIVE, RC_LS_INITD);
1036 stop_services = rc_ls_dir (stop_services, RC_SVCDIR_STARTED, RC_LS_INITD);
1037
1038 types = rc_strlist_add (NULL, "ineed");
1039 types = rc_strlist_add (types, "iuse");
1040 types = rc_strlist_add (types, "iafter");
1041 deporder = rc_get_depends (deptree, types, stop_services,
1042 runlevel, depoptions);
1043 rc_strlist_free (stop_services);
1044 rc_strlist_free (types);
1045 stop_services = deporder;
1046 deporder = NULL;
1047 types = NULL;
1048 rc_strlist_reverse (stop_services);
1049
1050 /* Load our list of coldplugged services */
1051 coldplugged_services = rc_ls_dir (coldplugged_services,
1052 RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
1053
1054 /* Load our start services now.
1055 We have different rules dependent on runlevel. */
1056 if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0) {
1057 if (coldplugged_services) {
1058 einfon ("Device initiated services:");
1059 STRLIST_FOREACH (coldplugged_services, service, i) {
1060 printf (" %s", service);
1061 start_services = rc_strlist_add (start_services, service);
1062 }
1063 printf ("\n");
1064 }
1065 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel ? newlevel : runlevel,
1066 (char *) NULL);
1067 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
1068 CHAR_FREE (tmp);
1069 } else {
1070 /* Store our list of coldplugged services */
1071 coldplugged_services = rc_ls_dir (coldplugged_services, RC_SVCDIR_COLDPLUGGED,
1072 RC_LS_INITD);
1073 if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 &&
1074 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
1075 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0)
1076 {
1077 /* We need to include the boot runlevel services if we're not in it */
1078 start_services = rc_ls_dir (start_services, RC_RUNLEVELDIR RC_LEVEL_BOOT,
1079 RC_LS_INITD);
1080 STRLIST_FOREACH (coldplugged_services, service, i)
1081 start_services = rc_strlist_add (start_services, service);
1082
1083 tmp = rc_strcatpaths (RC_RUNLEVELDIR,
1084 newlevel ? newlevel : runlevel, (char *) NULL);
1085 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
1086 CHAR_FREE (tmp);
1087 }
1088 }
1089
1090 /* Save out softlevel now */
1091 if (going_down)
1092 rc_set_runlevel (newlevel);
1093
1094 types = rc_strlist_add (NULL, "needsme");
1095 types = rc_strlist_add (types, "usesme");
1096 /* Now stop the services that shouldn't be running */
1097 STRLIST_FOREACH (stop_services, service, i) {
1098 bool found = false;
1099 char *conf = NULL;
1100 char **stopdeps = NULL;
1101 char *svc1 = NULL;
1102 char *svc2 = NULL;
1103 int k;
1104
1105 if (rc_service_state (service, rc_service_stopped))
1106 continue;
1107
1108 /* We always stop the service when in these runlevels */
1109 if (going_down) {
1110 pid_t pid = rc_stop_service (service);
1111 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1112 rc_waitpid (pid);
1113 continue;
1114 }
1115
1116 /* If we're in the start list then don't bother stopping us */
1117 STRLIST_FOREACH (start_services, svc1, j)
1118 if (strcmp (svc1, service) == 0) {
1119 found = true;
1120 break;
1121 }
1122
1123 /* Unless we would use a different config file */
1124 if (found) {
1125 int len;
1126 if (! newlevel)
1127 continue;
1128
1129 len = strlen (service) + strlen (runlevel) + 2;
1130 tmp = rc_xmalloc (sizeof (char *) * len);
1131 snprintf (tmp, len, "%s.%s", service, runlevel);
1132 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1133 found = rc_exists (conf);
1134 CHAR_FREE (conf);
1135 CHAR_FREE (tmp);
1136 if (! found) {
1137 len = strlen (service) + strlen (newlevel) + 2;
1138 tmp = rc_xmalloc (sizeof (char *) * len);
1139 snprintf (tmp, len, "%s.%s", service, newlevel);
1140 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1141 found = rc_exists (conf);
1142 CHAR_FREE (conf);
1143 CHAR_FREE (tmp);
1144 if (!found)
1145 continue;
1146 }
1147 } else {
1148 /* Allow coldplugged services not to be in the runlevels list */
1149 if (rc_service_state (service, rc_service_coldplugged))
1150 continue;
1151 }
1152
1153 /* We got this far! Or last check is to see if any any service that
1154 going to be started depends on us */
1155 stopdeps = rc_strlist_add (stopdeps, service);
1156 deporder = rc_get_depends (deptree, types, stopdeps,
1157 runlevel, RC_DEP_STRICT);
1158 rc_strlist_free (stopdeps);
1159 stopdeps = NULL;
1160 found = false;
1161 STRLIST_FOREACH (deporder, svc1, j) {
1162 STRLIST_FOREACH (start_services, svc2, k)
1163 if (strcmp (svc1, svc2) == 0) {
1164 found = true;
1165 break;
1166 }
1167 if (found)
1168 break;
1169 }
1170 rc_strlist_free (deporder);
1171 deporder = NULL;
1172
1173 /* After all that we can finally stop the blighter! */
1174 if (! found) {
1175 pid_t pid = rc_stop_service (service);
1176 if (pid > 0 && ! rc_is_env ("RC_PARALLEL", "yes"))
1177 rc_waitpid (pid);
1178 }
1179 }
1180 rc_strlist_free (types);
1181 types = NULL;
1182
1183 /* Wait for our services to finish */
1184 wait_for_services ();
1185
1186 /* Notify the plugins we have finished */
1187 rc_plugin_run (rc_hook_runlevel_stop_out, runlevel);
1188
1189 rmdir (RC_SVCDIR "/softscripts.new");
1190
1191 /* Store the new runlevel */
1192 if (newlevel) {
1193 rc_set_runlevel (newlevel);
1194 runlevel = newlevel;
1195 setenv ("RC_SOFTLEVEL", runlevel, 1);
1196 }
1197
1198 /* Run the halt script if needed */
1199 if (strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
1200 strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
1201 {
1202 execl (HALTSH, HALTSH, runlevel, (char *) NULL);
1203 eerrorx ("%s: unable to exec `%s': %s",
1204 applet, HALTSH, strerror (errno));
1205 }
1206
1207 /* Single user is done now */
1208 if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0) {
1209 if (rc_exists (INTERACTIVE))
1210 unlink (INTERACTIVE);
1211 sulogin (false);
1212 }
1213
1214 mkdir (RC_SVCDIR "softscripts.old", 0755);
1215 rc_plugin_run (rc_hook_runlevel_start_in, runlevel);
1216
1217 /* Re-add our coldplugged services if they stopped */
1218 STRLIST_FOREACH (coldplugged_services, service, i)
1219 rc_mark_service (service, rc_service_coldplugged);
1220
1221 /* Order the services to start */
1222 types = rc_strlist_add (NULL, "ineed");
1223 types = rc_strlist_add (types, "iuse");
1224 types = rc_strlist_add (types, "iafter");
1225 deporder = rc_get_depends (deptree, types, start_services,
1226 runlevel, depoptions);
1227 rc_strlist_free (types);
1228 types = NULL;
1229 rc_strlist_free (start_services);
1230 start_services = deporder;
1231 deporder = NULL;
1232
1233 STRLIST_FOREACH (start_services, service, i) {
1234 if (rc_service_state (service, rc_service_stopped)) {
1235 pid_t pid;
1236
1237 if (! interactive)
1238 interactive = want_interactive ();
1239
1240 if (interactive) {
1241 interactive_retry:
1242 printf ("\n");
1243 einfo ("About to start the service %s", service);
1244 eindent ();
1245 einfo ("1) Start the service\t\t2) Skip the service");
1246 einfo ("3) Continue boot process\t\t4) Exit to shell");
1247 eoutdent ();
1248 interactive_option:
1249 switch (read_key (true)) {
1250 case '1': break;
1251 case '2': continue;
1252 case '3': interactive = false; break;
1253 case '4': sulogin (true); goto interactive_retry;
1254 default: goto interactive_option;
1255 }
1256 }
1257
1258 /* Remember the pid if we're running in parallel */
1259 if ((pid = rc_start_service (service)))
1260 add_pid (pid);
1261
1262 if (! rc_is_env ("RC_PARALLEL", "yes")) {
1263 rc_waitpid (pid);
1264 remove_pid (pid);
1265 }
1266 }
1267 }
1268
1269 /* Wait for our services to finish */
1270 wait_for_services ();
1271
1272 rc_plugin_run (rc_hook_runlevel_start_out, runlevel);
1273
1274 /* Store our interactive status for boot */
1275 if (interactive && strcmp (runlevel, RC_LEVEL_BOOT) == 0)
1276 mark_interactive ();
1277 else {
1278 if (rc_exists (INTERACTIVE))
1279 unlink (INTERACTIVE);
1280 }
1281
1282 return (EXIT_SUCCESS);
1283 }

  ViewVC Help
Powered by ViewVC 1.1.20