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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20