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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20