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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20