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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20