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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20