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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20