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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20