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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20