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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20