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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20