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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20