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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2547 - (show annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (11 years, 2 months ago) by uberlord
File MIME type: text/x-csrc
File size: 31640 byte(s)
    Rewrite the core parts in C. We now provide librc so other programs can
    query runlevels, services and state without using bash. We also provide
    libeinfo so other programs can easily use our informational functions.

    As such, we have dropped the requirement of using bash as the init script
    shell. We now use /bin/sh and have strived to make the scripts as portable
    as possible. Shells that work are bash and dash. busybox works provided
    you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
    should disable find too.
    zsh and ksh do not work at this time.

    Networking support is currently being re-vamped also as it was heavily bash
    array based. As such, a new config format is available like so
    config_eth0="1.2.3.4/24 5.6.7.8/16"
    or like so
    config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

    We will still support the old bash array format provided that /bin/sh IS
    a link it bash.

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

  ViewVC Help
Powered by ViewVC 1.1.20