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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20