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

Contents of /trunk/src/rc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2563 - (hide annotations) (download) (as text)
Mon Apr 9 16:53:21 2007 UTC (11 years, 1 month ago) by uberlord
File MIME type: text/x-csrc
File size: 32934 byte(s)
Use names instead of numbers for reporting signals and trap signals in rc
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    
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 uberlord 2563 static char *applet = NULL;
59 uberlord 2547 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 uberlord 2563
107     if (applet)
108     free (applet);
109 uberlord 2547 }
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 uberlord 2549 execle (mycmd, mycmd, myarg, (char *) NULL, newenv);
402 uberlord 2547 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 uberlord 2549 execle (mycmd, mycmd, myarg, (char *) NULL, newenv);
413 uberlord 2547 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 uberlord 2563 static void handle_signal (int sig)
460     {
461     pid_t pid;
462     int status;
463     int serrno = errno;
464     char signame[10] = { '\0' };
465    
466     switch (sig)
467     {
468     case SIGCHLD:
469     do
470     {
471     pid = waitpid (-1, &status, WNOHANG);
472     if (pid < 0)
473     {
474     if (errno && errno != ECHILD)
475     eerror ("waitpid: %s", strerror (errno));
476     return;
477     }
478     } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
479     break;
480    
481     case SIGINT:
482     if (! signame[0])
483     snprintf (signame, sizeof (signame), "SIGINT");
484     case SIGTERM:
485     if (! signame[0])
486     snprintf (signame, sizeof (signame), "SIGTERM");
487     case SIGQUIT:
488     if (! signame[0])
489     snprintf (signame, sizeof (signame), "SIGQUIT");
490     eerrorx ("%s: caught %s, aborting", applet, signame);
491    
492     default:
493     eerror ("%s: caught unknown signal %d", applet, sig);
494     }
495    
496     /* Restore errno */
497     errno = serrno;
498     }
499    
500 uberlord 2547 int main (int argc, char **argv)
501     {
502     char *RUNLEVEL = NULL;
503     char *PREVLEVEL = NULL;
504     char *runlevel = NULL;
505     char *newlevel = NULL;
506     char *service = NULL;
507     char **deporder = NULL;
508     int i = 0;
509     int j = 0;
510     bool going_down = false;
511     bool interactive = false;
512     int depoptions = RC_DEP_STRICT | RC_DEP_TRACE;
513     char ksoftbuffer [PATH_MAX];
514    
515     if (argv[0])
516 uberlord 2563 applet = strdup (basename (argv[0]));
517 uberlord 2547
518     if (! applet)
519     eerrorx ("arguments required");
520    
521     argc--;
522     argv++;
523    
524     /* Handle multicall stuff */
525     if (applet[0] == 'e' || (applet[0] == 'v' && applet[1] == 'e'))
526     exit (do_e (argc, argv));
527    
528     if (strncmp (applet, "service_", strlen ("service_")) == 0)
529     exit (do_service (argc, argv));
530    
531     if (strcmp (applet, "get_options") == 0 ||
532     strcmp (applet, "save_options") == 0)
533     exit (do_options (argc, argv));
534    
535     if (strncmp (applet, "mark_service_", strlen ("mark_service_")) == 0)
536     exit (do_mark_service (argc, argv));
537    
538     if (strcmp (applet, "is_runlevel_start") == 0)
539     exit (rc_runlevel_starting () ? 0 : 1);
540     else if (strcmp (applet, "is_runlevel_stop") == 0)
541     exit (rc_runlevel_stopping () ? 0 : 1);
542     else if (strcmp (applet, "color_terminal") == 0)
543     exit (colour_terminal () ? 0 : 1);
544    
545     if (strcmp (applet, "rc" ) != 0)
546     eerrorx ("%s: unknown applet", applet);
547    
548     /* OK, so we really are the main RC process
549     Only root should be able to run us */
550     if (geteuid () != 0)
551     eerrorx ("%s: root access required", applet);
552    
553     atexit (cleanup);
554     newlevel = argv[0];
555    
556 uberlord 2563 /* Setup a signal handler */
557     signal (SIGINT, handle_signal);
558     signal (SIGQUIT, handle_signal);
559     signal (SIGTERM, handle_signal);
560     signal (SIGCHLD, handle_signal);
561    
562 uberlord 2547 /* Ensure our environment is pure
563     Also, add our configuration to it */
564     env = rc_filter_env ();
565     env = rc_config_env (env);
566    
567     if (env)
568     {
569     char *p;
570    
571     #ifdef __linux__
572     /* clearenv isn't portable, but there's no harm in using it
573     if we have it */
574     clearenv ();
575     #else
576     char *var;
577     /* No clearenv present here then.
578     We could manipulate environ directly ourselves, but it seems that
579     some kernels bitch about this according to the environ man pages
580     so we walk though environ and call unsetenv for each value. */
581     while (environ[0])
582     {
583     tmp = rc_xstrdup (environ[0]);
584     p = tmp;
585     var = strsep (&p, "=");
586     unsetenv (var);
587     free (tmp);
588     }
589     tmp = NULL;
590     #endif
591    
592     STRLIST_FOREACH (env, p, i)
593     if (strcmp (p, "RC_SOFTLEVEL") != 0 && strcmp (p, "SOFTLEVEL") != 0)
594     putenv (p);
595    
596     /* We don't free our list as that would be null in environ */
597     }
598    
599     /* Enable logging */
600     setenv ("RC_ELOG", "rc", 1);
601    
602     interactive = rc_exists (INTERACTIVE);
603     rc_plugin_load ();
604    
605     /* RUNLEVEL is set by sysvinit as is a magic number
606     RC_SOFTLEVEL is set by us and is the name for this magic number
607     even though all our userland documentation refers to runlevel */
608     RUNLEVEL = getenv ("RUNLEVEL");
609     PREVLEVEL = getenv ("PREVLEVEL");
610    
611     if (RUNLEVEL && newlevel)
612     {
613     if (strcmp (RUNLEVEL, "S") == 0 || strcmp (RUNLEVEL, "1") == 0)
614     {
615     /* OK, we're either in runlevel 1 or single user mode */
616     if (strcmp (newlevel, RC_LEVEL_SYSINIT) == 0)
617     {
618     struct utsname uts;
619     pid_t pid;
620     pid_t wpid;
621     int status = 0;
622     #ifdef __linux__
623     FILE *fp;
624     #endif
625    
626     uname (&uts);
627    
628     printf ("\n");
629     PEINFO_GOOD;
630     printf (" Gentoo/%s; ", uts.sysname);
631     PEINFO_BRACKET;
632     printf ("http://www.gentoo.org/");
633     PEINFO_NORMAL;
634     printf ("\n Copyright 1999-2007 Gentoo Foundation; "
635     "Distributed under the GPLv2\n\n");
636    
637     printf ("Press ");
638     PEINFO_GOOD;
639     printf ("I");
640     PEINFO_NORMAL;
641     printf (" to enter interactive boot mode\n\n");
642    
643     setenv ("RC_SOFTLEVEL", newlevel, 1);
644     rc_plugin_run (rc_hook_runlevel_start_in, newlevel);
645    
646     if ((pid = fork ()) == -1)
647     eerrorx ("%s: fork: %s", applet, strerror (errno));
648    
649     if (pid == 0)
650     {
651     mycmd = rc_xstrdup (INITSH);
652 uberlord 2549 execl (mycmd, mycmd, (char *) NULL);
653 uberlord 2547 eerrorx ("%s: unable to exec `" INITSH "': %s",
654     applet, strerror (errno));
655     }
656    
657     do
658     {
659     wpid = waitpid (pid, &status, 0);
660     if (wpid < 1)
661     eerror ("waitpid: %s", strerror (errno));
662     } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
663    
664     if (! WIFEXITED (status) || ! WEXITSTATUS (status) == 0)
665     exit (EXIT_FAILURE);
666    
667     /* If we requested a softlevel, save it now */
668     #ifdef __linux__
669     set_ksoftlevel (NULL);
670    
671     if ((fp = fopen ("/proc/cmdline", "r")))
672     {
673     char buffer[RC_LINEBUFFER];
674     char *soft;
675    
676     memset (buffer, 0, sizeof (buffer));
677     if (fgets (buffer, RC_LINEBUFFER, fp) &&
678     (soft = strstr (buffer, "softlevel=")))
679     {
680     i = soft - buffer;
681     if (i == 0 || buffer[i - 1] == ' ')
682     {
683     char *level;
684    
685     /* Trim the trailing carriage return if present */
686     i = strlen (buffer) - 1;
687     if (buffer[i] == '\n')
688     buffer[i] = 0;
689    
690     soft += strlen ("softlevel=");
691     level = strsep (&soft, " ");
692     set_ksoftlevel (level);
693     }
694     }
695     fclose (fp);
696     }
697     #endif
698     rc_plugin_run (rc_hook_runlevel_start_out, newlevel);
699    
700     if (want_interactive ())
701     mark_interactive ();
702    
703     exit (EXIT_SUCCESS);
704     }
705    
706     #ifdef __linux__
707     /* Parse the inittab file so we can work out the level to telinit */
708     if (strcmp (newlevel, RC_LEVEL_BOOT) != 0 &&
709     strcmp (newlevel, RC_LEVEL_SINGLE) != 0)
710     {
711     char **inittab = rc_get_list (NULL, "/etc/inittab");
712     char *line;
713     char *p;
714     char *token;
715     char lvl[2] = {0, 0};
716    
717     STRLIST_FOREACH (inittab, line, i)
718     {
719     p = line;
720     token = strsep (&p, ":");
721     if (! token || token[0] != 'l')
722     continue;
723    
724     if ((token = strsep (&p, ":")) == NULL)
725     continue;
726    
727     /* Snag the level */
728     lvl[0] = token[0];
729    
730     /* The name is spaced after this */
731     if ((token = strsep (&p, " ")) == NULL)
732     continue;
733    
734     if ((token = strsep (&p, " ")) == NULL)
735     continue;
736    
737     if (strcmp (token, newlevel) == 0)
738     break;
739     }
740     rc_strlist_free (inittab);
741    
742     /* We have a level, so telinit into it */
743     if (lvl[0] == 0)
744     {
745     eerrorx ("%s: couldn't find a runlevel called `%s'",
746     applet, newlevel);
747     }
748     else
749     {
750     mycmd = rc_xstrdup ("/sbin/telinit");
751     myarg = rc_xstrdup (lvl);
752 uberlord 2549 execl (mycmd, mycmd, myarg, (char *) NULL);
753 uberlord 2547 eerrorx ("%s: unable to exec `/sbin/telinit': %s",
754     applet, strerror (errno));
755     }
756     }
757     #endif
758     }
759     }
760    
761     /* Check we're in the runlevel requested, ie from
762     rc single
763     rc shutdown
764     rc reboot
765     */
766     if (newlevel)
767     {
768     if (myarg)
769     {
770     free (myarg);
771     myarg = NULL;
772     }
773    
774     if (strcmp (newlevel, RC_LEVEL_SINGLE) == 0)
775     {
776     if (! RUNLEVEL ||
777     (strcmp (RUNLEVEL, "S") != 0 &&
778     strcmp (RUNLEVEL, "1") != 0))
779     {
780     /* Remember the current runlevel for when we come back */
781     set_ksoftlevel (runlevel);
782     #ifdef __linux__
783     mycmd = rc_xstrdup ("/sbin/telinit");
784     myarg = rc_xstrdup ("S");
785 uberlord 2549 execl (mycmd, mycmd, myarg, (char *) NULL);
786 uberlord 2547 eerrorx ("%s: unable to exec `/%s': %s",
787     mycmd, applet, strerror (errno));
788     #else
789     if (kill (1, SIGTERM) != 0)
790     eerrorx ("%s: unable to send SIGTERM to init (pid 1): %s",
791     applet, strerror (errno));
792     exit (EXIT_SUCCESS);
793     #endif
794     }
795     }
796     else if (strcmp (newlevel, RC_LEVEL_REBOOT) == 0)
797     {
798     if (! RUNLEVEL ||
799     strcmp (RUNLEVEL, "6") != 0)
800     {
801     mycmd = rc_xstrdup ("/sbin/shutdown");
802     myarg = rc_xstrdup ("-r");
803     tmp = rc_xstrdup ("now");
804 uberlord 2549 execl (mycmd, mycmd, myarg, tmp, (char *) NULL);
805 uberlord 2547 eerrorx ("%s: unable to exec `%s': %s",
806     mycmd, applet, strerror (errno));
807     }
808     }
809     else if (strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0)
810     {
811     if (! RUNLEVEL ||
812     strcmp (RUNLEVEL, "0") != 0)
813     {
814     mycmd = rc_xstrdup ("/sbin/shutdown");
815     #ifdef __linux__
816     myarg = rc_xstrdup ("-h");
817     #else
818     myarg = rc_xstrdup ("-p");
819     #endif
820     tmp = rc_xstrdup ("now");
821 uberlord 2549 execl (mycmd, mycmd, myarg, tmp, (char *) NULL);
822 uberlord 2547 eerrorx ("%s: unable to exec `%s': %s",
823     mycmd, applet, strerror (errno));
824     }
825     }
826     }
827    
828     /* Export our current softlevel */
829     runlevel = rc_get_runlevel ();
830    
831     /* If we're in the default runlevel and ksoftlevel exists, we should use
832     that instead */
833     if (newlevel &&
834     rc_exists (RC_SVCDIR "ksoftlevel") &&
835     strcmp (newlevel, RC_LEVEL_DEFAULT) == 0)
836     {
837     /* We should only use ksoftlevel if we were in single user mode
838     If not, we need to erase ksoftlevel now. */
839     if (PREVLEVEL &&
840     (strcmp (PREVLEVEL, "1") == 0 ||
841     strcmp (PREVLEVEL, "S") == 0 ||
842     strcmp (PREVLEVEL, "N") == 0))
843     {
844     FILE *fp;
845    
846     if (! (fp = fopen (RC_SVCDIR "ksoftlevel", "r")))
847     eerror ("fopen `%s': %s", RC_SVCDIR "ksoftlevel",
848     strerror (errno));
849     else
850     {
851     if (fgets (ksoftbuffer, sizeof (ksoftbuffer), fp))
852     {
853     i = strlen (ksoftbuffer) - 1;
854     if (ksoftbuffer[i] == '\n')
855     ksoftbuffer[i] = 0;
856     newlevel = ksoftbuffer;
857     }
858     fclose (fp);
859     }
860     }
861     else
862     set_ksoftlevel (NULL);
863     }
864    
865     if (newlevel &&
866     (strcmp (newlevel, RC_LEVEL_REBOOT) == 0 ||
867     strcmp (newlevel, RC_LEVEL_SHUTDOWN) == 0 ||
868     strcmp (newlevel, RC_LEVEL_SINGLE) == 0))
869     {
870     going_down = true;
871     rc_set_runlevel (newlevel);
872     setenv ("RC_SOFTLEVEL", newlevel, 1);
873     rc_plugin_run (rc_hook_runlevel_stop_in, newlevel);
874     }
875     else
876     {
877     rc_plugin_run (rc_hook_runlevel_stop_in, runlevel);
878     }
879    
880     /* Check if runlevel is valid if we're changing */
881     if (newlevel && strcmp (runlevel, newlevel) != 0 && ! going_down)
882     {
883 uberlord 2549 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel, (char *) NULL);
884 uberlord 2547 if (! rc_is_dir (tmp))
885     eerrorx ("%s: is not a valid runlevel", newlevel);
886     CHAR_FREE (tmp);
887     }
888    
889     /* Load our deptree now */
890     if ((deptree = rc_load_deptree ()) == NULL)
891     eerrorx ("failed to load deptree");
892    
893     /* Clean the failed services state dir now */
894     if (rc_is_dir (RC_SVCDIR "failed"))
895     rc_rm_dir (RC_SVCDIR "failed", false);
896    
897     mkdir (RC_SVCDIR "/softscripts.new", 0755);
898    
899     #ifdef __linux__
900     /* udev likes to start services before we're ready when it does
901     its coldplugging thing. runscript knows when we're not ready so it
902     stores a list of coldplugged services in DEVBOOT for us to pick up
903     here when we are ready for them */
904     if (rc_is_dir (DEVBOOT))
905     {
906     start_services = rc_ls_dir (NULL, DEVBOOT, RC_LS_INITD);
907     rc_rm_dir (DEVBOOT, true);
908    
909     STRLIST_FOREACH (start_services, service, i)
910     if (rc_allow_plug (service))
911     rc_mark_service (service, rc_service_coldplugged);
912     /* We need to dump this list now.
913     This may seem redunant, but only Linux needs this and saves on
914     code bloat. */
915     rc_strlist_free (start_services);
916     start_services = NULL;
917     }
918     #else
919     /* BSD's on the other hand populate /dev automagically and use devd.
920     The only downside of this approach and ours is that we have to hard code
921     the device node to the init script to simulate the coldplug into
922     runlevel for our dependency tree to work. */
923     if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0 &&
924     (strcmp (runlevel, RC_LEVEL_SINGLE) == 0 ||
925     strcmp (runlevel, RC_LEVEL_SYSINIT) == 0) &&
926     rc_is_env ("RC_COLDPLUG", "yes"))
927     {
928     /* The net interfaces are easy - they're all in net /dev/net :) */
929     start_services = rc_ls_dir (NULL, "/dev/net", 0);
930     STRLIST_FOREACH (start_services, service, i)
931     {
932     j = (strlen ("net.") + strlen (service) + 1);
933     tmp = rc_xmalloc (sizeof (char *) * j);
934     snprintf (tmp, j, "net.%s", service);
935     if (rc_service_exists (tmp) && rc_allow_plug (tmp))
936     rc_mark_service (tmp, rc_service_coldplugged);
937     CHAR_FREE (tmp);
938     }
939     rc_strlist_free (start_services);
940    
941     /* The mice are a little more tricky.
942     If we coldplug anything else, we'll probably do it here. */
943     start_services = rc_ls_dir (NULL, "/dev", 0);
944     STRLIST_FOREACH (start_services, service, i)
945     {
946     if (strncmp (service, "psm", 3) == 0 ||
947     strncmp (service, "ums", 3) == 0)
948     {
949     char *p = service + 3;
950     if (p && isdigit (*p))
951     {
952     j = (strlen ("moused.") + strlen (service) + 1);
953     tmp = rc_xmalloc (sizeof (char *) * j);
954     snprintf (tmp, j, "moused.%s", service);
955     if (rc_service_exists (tmp) && rc_allow_plug (tmp))
956     rc_mark_service (tmp, rc_service_coldplugged);
957     CHAR_FREE (tmp);
958     }
959     }
960     }
961     rc_strlist_free (start_services);
962     start_services = NULL;
963     }
964     #endif
965    
966     /* Build a list of all services to stop and then work out the
967     correct order for stopping them */
968     stop_services = rc_ls_dir (stop_services, RC_SVCDIR_STARTING, RC_LS_INITD);
969     stop_services = rc_ls_dir (stop_services, RC_SVCDIR_INACTIVE, RC_LS_INITD);
970     stop_services = rc_ls_dir (stop_services, RC_SVCDIR_STARTED, RC_LS_INITD);
971    
972     types = rc_strlist_add (NULL, "ineed");
973     types = rc_strlist_add (types, "iuse");
974     types = rc_strlist_add (types, "iafter");
975     deporder = rc_get_depends (deptree, types, stop_services,
976     runlevel, depoptions);
977     rc_strlist_free (stop_services);
978     rc_strlist_free (types);
979     stop_services = deporder;
980     deporder = NULL;
981     types = NULL;
982     rc_strlist_reverse (stop_services);
983    
984     /* Load our list of coldplugged services */
985     coldplugged_services = rc_ls_dir (coldplugged_services,
986     RC_SVCDIR_COLDPLUGGED, RC_LS_INITD);
987    
988     /* Load our start services now.
989     We have different rules dependent on runlevel. */
990     if (newlevel && strcmp (newlevel, RC_LEVEL_BOOT) == 0)
991     {
992     if (coldplugged_services)
993     {
994     einfon ("Device initiated services:");
995     STRLIST_FOREACH (coldplugged_services, service, i)
996     {
997     printf (" %s", service);
998     start_services = rc_strlist_add (start_services, service);
999     }
1000     printf ("\n");
1001     }
1002 uberlord 2549 tmp = rc_strcatpaths (RC_RUNLEVELDIR, newlevel ? newlevel : runlevel,
1003     (char *) NULL);
1004 uberlord 2547 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
1005     CHAR_FREE (tmp);
1006     }
1007     else
1008     {
1009     /* Store our list of coldplugged services */
1010     coldplugged_services = rc_ls_dir (coldplugged_services, RC_SVCDIR_COLDPLUGGED,
1011     RC_LS_INITD);
1012     if (strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SINGLE) != 0 &&
1013     strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_SHUTDOWN) != 0 &&
1014     strcmp (newlevel ? newlevel : runlevel, RC_LEVEL_REBOOT) != 0)
1015     {
1016     /* We need to include the boot runlevel services if we're not in it */
1017     start_services = rc_ls_dir (start_services, RC_RUNLEVELDIR RC_LEVEL_BOOT,
1018     RC_LS_INITD);
1019     STRLIST_FOREACH (coldplugged_services, service, i)
1020     start_services = rc_strlist_add (start_services, service);
1021    
1022     tmp = rc_strcatpaths (RC_RUNLEVELDIR,
1023 uberlord 2549 newlevel ? newlevel : runlevel, (char *) NULL);
1024 uberlord 2547 start_services = rc_ls_dir (start_services, tmp, RC_LS_INITD);
1025     CHAR_FREE (tmp);
1026     }
1027     }
1028    
1029     /* Save out softlevel now */
1030     if (going_down)
1031     rc_set_runlevel (newlevel);
1032    
1033     types = rc_strlist_add (NULL, "needsme");
1034     types = rc_strlist_add (types, "usesme");
1035     /* Now stop the services that shouldn't be running */
1036     STRLIST_FOREACH (stop_services, service, i)
1037     {
1038     bool found = false;
1039     char *conf = NULL;
1040     char **stopdeps = NULL;
1041     char *svc1 = NULL;
1042     char *svc2 = NULL;
1043     int k;
1044    
1045     if (rc_service_state (service, rc_service_stopped))
1046     continue;
1047    
1048     /* We always stop the service when in these runlevels */
1049     if (going_down)
1050     {
1051     rc_stop_service (service);
1052     continue;
1053     }
1054    
1055     /* If we're in the start list then don't bother stopping us */
1056     STRLIST_FOREACH (start_services, svc1, j)
1057     if (strcmp (svc1, service) == 0)
1058     {
1059     found = true;
1060     break;
1061     }
1062    
1063     /* Unless we would use a different config file */
1064     if (found)
1065     {
1066 uberlord 2550 int len;
1067 uberlord 2547 if (! newlevel)
1068     continue;
1069 uberlord 2550
1070     len = strlen (service) + strlen (runlevel) + 2;
1071     tmp = rc_xmalloc (sizeof (char *) * len);
1072     snprintf (tmp, len, "%s.%s", service, runlevel);
1073 uberlord 2549 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1074 uberlord 2547 found = rc_exists (conf);
1075     CHAR_FREE (conf);
1076     CHAR_FREE (tmp);
1077     if (! found)
1078     {
1079 uberlord 2550 len = strlen (service) + strlen (newlevel) + 2;
1080     tmp = rc_xmalloc (sizeof (char *) * len);
1081     snprintf (tmp, len, "%s.%s", service, newlevel);
1082 uberlord 2549 conf = rc_strcatpaths (RC_CONFDIR, tmp, (char *) NULL);
1083 uberlord 2547 found = rc_exists (conf);
1084     CHAR_FREE (conf);
1085     CHAR_FREE (tmp);
1086     if (!found)
1087     continue;
1088     }
1089     }
1090     else
1091     /* Allow coldplugged services not to be in the runlevels list */
1092     {
1093     if (rc_service_state (service, rc_service_coldplugged))
1094     continue;
1095     }
1096    
1097     /* We got this far! Or last check is to see if any any service that
1098     going to be started depends on us */
1099     stopdeps = rc_strlist_add (stopdeps, service);
1100     deporder = rc_get_depends (deptree, types, stopdeps,
1101     runlevel, RC_DEP_STRICT);
1102     rc_strlist_free (stopdeps);
1103     stopdeps = NULL;
1104     found = false;
1105     STRLIST_FOREACH (deporder, svc1, j)
1106     {
1107     STRLIST_FOREACH (start_services, svc2, k)
1108     if (strcmp (svc1, svc2) == 0)
1109     {
1110     found = true;
1111     break;
1112     }
1113     if (found)
1114     break;
1115     }
1116     rc_strlist_free (deporder);
1117     deporder = NULL;
1118    
1119     /* After all that we can finally stop the blighter! */
1120     if (! found)
1121     rc_stop_service (service);
1122     }
1123     rc_strlist_free (types);
1124     types = NULL;
1125    
1126     /* Wait for our services to finish */
1127     if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
1128     wait_for_services ();
1129    
1130     /* Notify the plugins we have finished */
1131     rc_plugin_run (rc_hook_runlevel_stop_out, runlevel);
1132    
1133     rmdir (RC_SVCDIR "/softscripts.new");
1134    
1135     /* Store the new runlevel */
1136     if (newlevel)
1137     {
1138     rc_set_runlevel (newlevel);
1139     runlevel = newlevel;
1140     setenv ("RC_SOFTLEVEL", runlevel, 1);
1141     }
1142    
1143     /* Run the halt script if needed */
1144     if (strcmp (runlevel, RC_LEVEL_SHUTDOWN) == 0 ||
1145     strcmp (runlevel, RC_LEVEL_REBOOT) == 0)
1146     {
1147     mycmd = rc_xstrdup (HALTSH);
1148     myarg = rc_xstrdup (runlevel);
1149 uberlord 2549 execl (mycmd, mycmd, myarg, (char *) NULL);
1150 uberlord 2547 eerrorx ("%s: unable to exec `%s': %s",
1151     applet, HALTSH, strerror (errno));
1152     }
1153    
1154     /* Single user is done now */
1155     if (strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
1156     {
1157     if (rc_exists (INTERACTIVE))
1158     unlink (INTERACTIVE);
1159     sulogin (false);
1160     }
1161    
1162 uberlord 2560 mkdir (RC_SVCDIR "softscripts.old", 0755);
1163 uberlord 2547 rc_plugin_run (rc_hook_runlevel_start_in, runlevel);
1164    
1165     /* Re-add our coldplugged services if they stopped */
1166     STRLIST_FOREACH (coldplugged_services, service, i)
1167     rc_mark_service (service, rc_service_coldplugged);
1168    
1169     /* Order the services to start */
1170     types = rc_strlist_add (NULL, "ineed");
1171     types = rc_strlist_add (types, "iuse");
1172     types = rc_strlist_add (types, "iafter");
1173     deporder = rc_get_depends (deptree, types, start_services,
1174     runlevel, depoptions);
1175     rc_strlist_free (types);
1176     types = NULL;
1177     rc_strlist_free (start_services);
1178     start_services = deporder;
1179     deporder = NULL;
1180    
1181     STRLIST_FOREACH (start_services, service, i)
1182     {
1183     if (rc_service_state (service, rc_service_stopped))
1184     {
1185     if (! interactive)
1186     interactive = want_interactive ();
1187    
1188     if (interactive)
1189     {
1190     interactive_retry:
1191     printf ("\n");
1192     einfo ("About to start the service %s", service);
1193     eindent ();
1194     einfo ("1) Start the service\t\t2) Skip the service");
1195     einfo ("3) Continue boot process\t\t4) Exit to shell");
1196     eoutdent ();
1197     interactive_option:
1198     switch (read_key (true))
1199     {
1200     case '1': break;
1201     case '2': continue;
1202     case '3': interactive = false; break;
1203     case '4': sulogin (true); goto interactive_retry;
1204     default: goto interactive_option;
1205     }
1206     }
1207     rc_start_service (service);
1208     }
1209     }
1210    
1211     /* Wait for our services to finish */
1212     if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
1213     wait_for_services ();
1214    
1215     rc_plugin_run (rc_hook_runlevel_start_out, runlevel);
1216    
1217     /* Store our interactive status for boot */
1218     if (interactive && strcmp (runlevel, RC_LEVEL_BOOT) == 0)
1219     mark_interactive ();
1220     else
1221     {
1222     if (rc_exists (INTERACTIVE))
1223     unlink (INTERACTIVE);
1224     }
1225    
1226     return (EXIT_SUCCESS);
1227     }
1228    

  ViewVC Help
Powered by ViewVC 1.1.20