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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20