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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20