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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20