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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20