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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20