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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20