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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2550 - (show annotations) (download) (as text)
Fri Apr 6 01:04:07 2007 UTC (7 years, 6 months ago) by uberlord
File MIME type: text/x-csrc
File size: 31853 byte(s)
Misc fixes, plugged a memory leak in runscript.c and use va_copy to avoid nasty segfaults
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, (char *) 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, (char *) 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, (char *) 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, (char *) 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, (char *) 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, (char *) 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, (char *) 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, (char *) 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,
953 (char *) NULL);
954 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
955 CHAR_FREE (tmp);
956 }
957 else
958 {
959 /* Store our list of coldplugged services */
960 coldplugged_services = rc_ls_dir (coldplugged_services, RC_SVCDIR_COLDPLUGGED,
961 RC_LS_INITD);
962 if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 &&
963 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
964 strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0)
965 {
966 /* We need to include the boot runlevel services if we're not in it */
967 start_services = rc_ls_dir (start_services, RC_RUNLEVELDIR RC_LEVEL_BOOT,
968 RC_LS_INITD);
969 STRLIST_FOREACH (coldplugged_services, service, i)
970 start_services = rc_strlist_add (start_services, service);
971
972 tmp = rc_strcatpaths (RC_RUNLEVELDIR,
973 newlevel ? newlevel : runlevel, (char *) NULL);
974 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
975 CHAR_FREE (tmp);
976 }
977 }
978
979 /* Save out softlevel now */
980 if (going_down)
981 rc_set_runlevel (newlevel);
982
983 types = rc_strlist_add (NULL, "needsme");
984 types = rc_strlist_add (types, "usesme");
985 /* Now stop the services that shouldn't be running */
986 STRLIST_FOREACH (stop_services, service, i)
987 {
988 bool found = false;
989 char *conf = NULL;
990 char **stopdeps = NULL;
991 char *svc1 = NULL;
992 char *svc2 = NULL;
993 int k;
994
995 if (rc_service_state (service, rc_service_stopped))
996 continue;
997
998 /* We always stop the service when in these runlevels */
999 if (going_down)
1000 {
1001 rc_stop_service (service);
1002 continue;
1003 }
1004
1005 /* If we're in the start list then don't bother stopping us */
1006 STRLIST_FOREACH (start_services, svc1, j)
1007 if (strcmp (svc1, service) == 0)
1008 {
1009 found = true;
1010 break;
1011 }
1012
1013 /* Unless we would use a different config file */
1014 if (found)
1015 {
1016 int len;
1017 if (! newlevel)
1018 continue;
1019
1020 len = strlen (service) + strlen (runlevel) + 2;
1021 tmp = rc_xmalloc (sizeof (char *) * len);
1022 snprintf (tmp, len, "%s.%s", service, runlevel);
1023 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1024 found = rc_exists (conf);
1025 CHAR_FREE (conf);
1026 CHAR_FREE (tmp);
1027 if (! found)
1028 {
1029 len = strlen (service) + strlen (newlevel) + 2;
1030 tmp = rc_xmalloc (sizeof (char *) * len);
1031 snprintf (tmp, len, "%s.%s", service, newlevel);
1032 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1033 found = rc_exists (conf);
1034 CHAR_FREE (conf);
1035 CHAR_FREE (tmp);
1036 if (!found)
1037 continue;
1038 }
1039 }
1040 else
1041 /* Allow coldplugged services not to be in the runlevels list */
1042 {
1043 if (rc_service_state (service, rc_service_coldplugged))
1044 continue;
1045 }
1046
1047 /* We got this far! Or last check is to see if any any service that
1048 going to be started depends on us */
1049 stopdeps = rc_strlist_add (stopdeps, service);
1050 deporder = rc_get_depends (deptree, types, stopdeps,
1051 runlevel, RC_DEP_STRICT);
1052 rc_strlist_free (stopdeps);
1053 stopdeps = NULL;
1054 found = false;
1055 STRLIST_FOREACH (deporder, svc1, j)
1056 {
1057 STRLIST_FOREACH (start_services, svc2, k)
1058 if (strcmp (svc1, svc2) == 0)
1059 {
1060 found = true;
1061 break;
1062 }
1063 if (found)
1064 break;
1065 }
1066 rc_strlist_free (deporder);
1067 deporder = NULL;
1068
1069 /* After all that we can finally stop the blighter! */
1070 if (! found)
1071 rc_stop_service (service);
1072 }
1073 rc_strlist_free (types);
1074 types = NULL;
1075
1076 /* Wait for our services to finish */
1077 if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
1078 wait_for_services ();
1079
1080 /* Notify the plugins we have finished */
1081 rc_plugin_run (rc_hook_runlevel_stop_out, runlevel);
1082
1083 rmdir (RC_SVCDIR "/softscripts.new");
1084
1085 /* Store the new runlevel */
1086 if (newlevel)
1087 {
1088 rc_set_runlevel (newlevel);
1089 runlevel = newlevel;
1090 setenv ("RC_SOFTLEVEL", runlevel, 1);
1091 }
1092
1093 /* Run the halt script if needed */
1094 if (strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
1095 strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
1096 {
1097 mycmd = rc_xstrdup (HALTSH);
1098 myarg = rc_xstrdup (runlevel);
1099 execl (mycmd, mycmd, myarg, (char *) NULL);
1100 eerrorx ("%s: unable to exec `%s': %s",
1101 applet, HALTSH, strerror (errno));
1102 }
1103
1104 /* Single user is done now */
1105 if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
1106 {
1107 if (rc_exists (INTERACTIVE))
1108 unlink (INTERACTIVE);
1109 sulogin (false);
1110 }
1111
1112 mkdir (RC_SVCDIR "/softscripts.old", 0755);
1113 rc_plugin_run (rc_hook_runlevel_start_in, runlevel);
1114
1115 /* Re-add our coldplugged services if they stopped */
1116 STRLIST_FOREACH (coldplugged_services, service, i)
1117 rc_mark_service (service, rc_service_coldplugged);
1118
1119 /* Order the services to start */
1120 types = rc_strlist_add (NULL, "ineed");
1121 types = rc_strlist_add (types, "iuse");
1122 types = rc_strlist_add (types, "iafter");
1123 deporder = rc_get_depends (deptree, types, start_services,
1124 runlevel, depoptions);
1125 rc_strlist_free (types);
1126 types = NULL;
1127 rc_strlist_free (start_services);
1128 start_services = deporder;
1129 deporder = NULL;
1130
1131 STRLIST_FOREACH (start_services, service, i)
1132 {
1133 if (rc_service_state (service, rc_service_stopped))
1134 {
1135 if (! interactive)
1136 interactive = want_interactive ();
1137
1138 if (interactive)
1139 {
1140 interactive_retry:
1141 printf ("\n");
1142 einfo ("About to start the service %s", service);
1143 eindent ();
1144 einfo ("1) Start the service\t\t2) Skip the service");
1145 einfo ("3) Continue boot process\t\t4) Exit to shell");
1146 eoutdent ();
1147 interactive_option:
1148 switch (read_key (true))
1149 {
1150 case '1': break;
1151 case '2': continue;
1152 case '3': interactive = false; break;
1153 case '4': sulogin (true); goto interactive_retry;
1154 default: goto interactive_option;
1155 }
1156 }
1157 rc_start_service (service);
1158 }
1159 }
1160
1161 /* Wait for our services to finish */
1162 if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
1163 wait_for_services ();
1164
1165 rc_plugin_run (rc_hook_runlevel_start_out, runlevel);
1166
1167 /* Store our interactive status for boot */
1168 if (interactive && strcmp (runlevel, RC_LEVEL_BOOT) == 0)
1169 mark_interactive ();
1170 else
1171 {
1172 if (rc_exists (INTERACTIVE))
1173 unlink (INTERACTIVE);
1174 }
1175
1176 return (EXIT_SUCCESS);
1177 }

  ViewVC Help
Powered by ViewVC 1.1.20