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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2547 - (show annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (7 years, 4 months ago) by uberlord
File MIME type: text/x-csrc
File size: 29258 byte(s)
    Rewrite the core parts in C. We now provide librc so other programs can
    query runlevels, services and state without using bash. We also provide
    libeinfo so other programs can easily use our informational functions.

    As such, we have dropped the requirement of using bash as the init script
    shell. We now use /bin/sh and have strived to make the scripts as portable
    as possible. Shells that work are bash and dash. busybox works provided
    you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
    should disable find too.
    zsh and ksh do not work at this time.

    Networking support is currently being re-vamped also as it was heavily bash
    array based. As such, a new config format is available like so
    config_eth0="1.2.3.4/24 5.6.7.8/16"
    or like so
    config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

    We will still support the old bash array format provided that /bin/sh IS
    a link it bash.

    ChangeLog for baselayout-1 can be found in our SVN repo.
1 /*
2 * runscript.c
3 * Handle launching of Gentoo init scripts.
4 *
5 * Copyright 1999-2007 Gentoo Foundation
6 * Distributed under the terms of the GNU General Public License v2
7 */
8
9 #include <sys/types.h>
10 #include <sys/signal.h>
11 #include <sys/stat.h>
12 #include <sys/wait.h>
13 #include <dlfcn.h>
14 #include <errno.h>
15 #include <limits.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <string.h>
19 #include <unistd.h>
20
21 #ifndef __linux__
22 #include <libgen.h>
23 #endif
24
25 #include "einfo.h"
26 #include "rc.h"
27 #include "rc-misc.h"
28 #include "rc-plugin.h"
29 #include "strlist.h"
30
31 #define RCSCRIPT_HELP RC_LIBDIR "/sh/rc-help.sh"
32 #define SELINUX_LIB RC_LIBDIR "/runscript_selinux.so"
33
34 static char *applet = NULL;
35 static char *exclusive = NULL;
36 static char *mtime_test = NULL;
37 static rc_depinfo_t *deptree = NULL;
38 static char **services = NULL;
39 static char **svclist = NULL;
40 static char **tmplist = NULL;
41 static char **providelist = NULL;
42 static char **types = NULL;
43 static char **restart_services = NULL;
44 static char **need_services = NULL;
45 static char **env = NULL;
46 static char *mycmd = NULL;
47 static char *myarg1 = NULL;
48 static char *myarg2 = NULL;
49 static char *tmp = NULL;
50 static char *softlevel = NULL;
51 static bool sighup = false;
52 static char *ibsave = NULL;
53 static bool in_background = false;
54 static rc_hook_t hook_out = 0;
55
56 extern char **environ;
57
58 #ifdef __linux__
59 static void (*selinux_run_init_old) (void);
60 static void (*selinux_run_init_new) (int argc, char **argv);
61
62 void setup_selinux (int argc, char **argv);
63 #endif
64
65 #ifdef __linux__
66 void setup_selinux (int argc, char **argv)
67 {
68 void *lib_handle = NULL;
69
70 lib_handle = dlopen (SELINUX_LIB, RTLD_NOW | RTLD_GLOBAL);
71 if (lib_handle)
72 {
73 /* FIXME: the below code generates the warning
74 ISO C forbids assignment between function pointer and 'void *'
75 which sucks ass
76 http://www.opengroup.org/onlinepubs/009695399/functions/dlsym.html */
77 selinux_run_init_old = dlsym (lib_handle, "selinux_runscript");
78 selinux_run_init_new = dlsym (lib_handle, "selinux_runscript2");
79
80 /* Use new run_init if it rc_exists, else fall back to old */
81 if (selinux_run_init_new)
82 selinux_run_init_new (argc, argv);
83 else if (selinux_run_init_old)
84 selinux_run_init_old ();
85 else
86 /* This shouldnt happen... probably corrupt lib */
87 eerrorx ("run_init is missing from runscript_selinux.so!");
88 }
89 }
90 #endif
91
92 static void handle_signal (int sig)
93 {
94 pid_t pid;
95 int status;
96 int serrno = errno;
97
98 switch (sig)
99 {
100 case SIGHUP:
101 sighup = true;
102 break;
103
104 case SIGCHLD:
105 do
106 {
107 pid = waitpid (-1, &status, WNOHANG);
108 if (pid < 0)
109 {
110 if (errno != ECHILD)
111 eerror ("waitpid: %s", strerror (errno));
112 return;
113 }
114 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
115 break;
116
117 case SIGINT:
118 case SIGTERM:
119 case SIGQUIT:
120 eerrorx ("%s: caught signal %d, aborting", applet, sig);
121
122 default:
123 eerror ("%s: caught unknown signal %d", applet, sig);
124 }
125
126 /* Restore errno */
127 errno = serrno;
128 }
129
130 static time_t get_mtime (const char *pathname, bool follow_link)
131 {
132 struct stat buf;
133 int retval;
134
135 if (! pathname)
136 return (0);
137
138 retval = follow_link ? stat (pathname, &buf) : lstat (pathname, &buf);
139 if (retval == 0)
140 return (buf.st_mtime);
141
142 errno = 0;
143 return (0);
144 }
145
146 static bool in_control ()
147 {
148 char *path;
149 time_t mtime;
150 const char *tests[] = { "starting", "started", "stopping",
151 "inactive", "wasinactive", NULL };
152 int i = 0;
153
154 if (sighup)
155 return (false);
156
157 if (mtime_test == NULL || ! rc_exists (mtime_test))
158 return (false);
159
160 if (rc_service_state (applet, rc_service_stopped))
161 return (false);
162
163 if ((mtime = get_mtime (mtime_test, false)) == 0)
164 return (false);
165
166 while (tests[i])
167 {
168 path = rc_strcatpaths (RC_SVCDIR, tests[i], applet, NULL);
169 if (rc_exists (path))
170 {
171 int m = get_mtime (path, false);
172 if (mtime < m && m != 0)
173 {
174 free (path);
175 return (false);
176 }
177 }
178 free (path);
179 i++;
180 }
181
182 return (true);
183 }
184
185 static void uncoldplug (char *service)
186 {
187 char *cold = rc_strcatpaths (RC_SVCDIR "coldplugged", basename (service), NULL);
188 if (rc_exists (cold) && unlink (cold) != 0)
189 eerror ("%s: unlink `%s': %s", applet, cold, strerror (errno));
190 free (cold);
191 }
192
193 static void cleanup (void)
194 {
195 /* Flush our buffered output if any */
196 eflush ();
197
198 if (hook_out)
199 rc_plugin_run (hook_out, applet);
200 rc_plugin_unload ();
201
202 if (deptree)
203 rc_free_deptree (deptree);
204 if (services)
205 rc_strlist_free (services);
206 if (types)
207 rc_strlist_free (types);
208 if (svclist)
209 rc_strlist_free (svclist);
210 if (providelist)
211 rc_strlist_free (providelist);
212 if (restart_services)
213 rc_strlist_free (restart_services);
214 if (need_services)
215 rc_strlist_free (need_services);
216 if (mycmd)
217 free (mycmd);
218 if (myarg1)
219 free (myarg1);
220 if (myarg2)
221 free (myarg2);
222 if (ibsave)
223 free (ibsave);
224
225 if (in_control ())
226 {
227 if (rc_service_state (applet, rc_service_starting))
228 {
229 if (rc_service_state (applet, rc_service_wasinactive))
230 rc_mark_service (applet, rc_service_inactive);
231 else
232 rc_mark_service (applet, rc_service_stopped);
233 }
234 else if (rc_service_state (applet, rc_service_stopping))
235 {
236 /* If the we're shutting down, do it cleanly */
237 if ((softlevel && rc_runlevel_stopping () &&
238 (strcmp (softlevel, RC_LEVEL_SHUTDOWN) == 0 ||
239 strcmp (softlevel, RC_LEVEL_REBOOT) == 0)) ||
240 ! rc_service_state (applet, rc_service_wasinactive))
241 rc_mark_service (applet, rc_service_stopped);
242 else
243 rc_mark_service (applet, rc_service_inactive);
244 }
245 if (exclusive && rc_exists (exclusive))
246 unlink (exclusive);
247 }
248
249 if (env)
250 rc_strlist_free (env);
251
252 if (mtime_test)
253 {
254 unlink (mtime_test);
255 free (mtime_test);
256 }
257 if (exclusive)
258 free (exclusive);
259
260 if (applet)
261 free (applet);
262 }
263
264 static bool svc_exec (const char *service, const char *arg1, const char *arg2)
265 {
266 int status = 0;
267 pid_t pid;
268
269 /* We need to disable our child signal handler now so we block
270 until our script returns. */
271 signal (SIGCHLD, NULL);
272
273 pid = fork();
274
275 if (pid == -1)
276 eerrorx ("%s: fork: %s", service, strerror (errno));
277 if (pid == 0)
278 {
279 mycmd = rc_xstrdup (service);
280 myarg1 = rc_xstrdup (arg1);
281 if (arg2)
282 myarg2 = rc_xstrdup (arg2);
283
284 if (rc_exists (RC_SVCDIR "runscript.sh"))
285 {
286 execl (RC_SVCDIR "runscript.sh", mycmd, mycmd, myarg1, myarg2, NULL);
287 eerrorx ("%s: exec `" RC_SVCDIR "runscript.sh': %s",
288 service, strerror (errno));
289 }
290 else
291 {
292 execl (RC_LIBDIR "sh/runscript.sh", mycmd, mycmd, myarg1, myarg2, NULL);
293 eerrorx ("%s: exec `" RC_LIBDIR "sh/runscript.sh': %s",
294 service, strerror (errno));
295 }
296 }
297
298 do
299 {
300 if (waitpid (pid, &status, 0) < 0)
301 {
302 if (errno != ECHILD)
303 eerror ("waitpid: %s", strerror (errno));
304 break;
305 }
306 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
307
308 /* Done, so restore the signal handler */
309 signal (SIGCHLD, handle_signal);
310
311 if (WIFEXITED (status))
312 return (WEXITSTATUS (status) == 0 ? true : false);
313
314 return (false);
315 }
316
317 static rc_service_state_t svc_status (const char *service)
318 {
319 char status[10];
320 int (*e) (const char *fmt, ...) = &einfo;
321
322 rc_service_state_t retval = rc_service_stopped;
323
324 if (rc_service_state (service, rc_service_stopping))
325 {
326 snprintf (status, sizeof (status), "stopping");
327 e = &ewarn;
328 retval = rc_service_stopping;
329 }
330 else if (rc_service_state (service, rc_service_starting))
331 {
332 snprintf (status, sizeof (status), "starting");
333 e = &ewarn;
334 retval = rc_service_starting;
335 }
336 else if (rc_service_state (service, rc_service_inactive))
337 {
338 snprintf (status, sizeof (status), "inactive");
339 e = &ewarn;
340 retval = rc_service_inactive;
341 }
342 else if (rc_service_state (service, rc_service_crashed))
343 {
344 snprintf (status, sizeof (status), "crashed");
345 e = &eerror;
346 retval = rc_service_crashed;
347 }
348 else if (rc_service_state (service, rc_service_started))
349 {
350 snprintf (status, sizeof (status), "started");
351 retval = rc_service_started;
352 }
353 else
354 snprintf (status, sizeof (status), "stopped");
355
356 e ("status: %s", status);
357 return (retval);
358 }
359
360 static void make_exclusive (const char *service)
361 {
362 char *path;
363 int i;
364
365 /* We create a fifo so that other services can wait until we complete */
366 if (! exclusive)
367 exclusive = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, NULL);
368
369 if (mkfifo (exclusive, 0600) != 0 && errno != EEXIST &&
370 (errno != EACCES || geteuid () == 0))
371 eerrorx ("%s: unable to create fifo `%s': %s",
372 applet, exclusive, strerror (errno));
373
374 path = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, NULL);
375 i = strlen (path) + 16;
376 mtime_test = rc_xmalloc (sizeof (char *) * i);
377 snprintf (mtime_test, i, "%s.%d", path, getpid ());
378 free (path);
379
380 if (rc_exists (mtime_test) && unlink (mtime_test) != 0)
381 {
382 eerror ("%s: unlink `%s': %s",
383 applet, mtime_test, strerror (errno));
384 free (mtime_test);
385 mtime_test = NULL;
386 return;
387 }
388
389 if (symlink (service, mtime_test) != 0)
390 {
391 eerror ("%s: symlink `%s' to `%s': %s",
392 applet, service, mtime_test, strerror (errno));
393 free (mtime_test);
394 mtime_test = NULL;
395 }
396 }
397
398 static void unlink_mtime_test ()
399 {
400 if (unlink (mtime_test) != 0)
401 eerror ("%s: unlink `%s': %s", applet, mtime_test, strerror (errno));
402 free (mtime_test);
403 mtime_test = NULL;
404 }
405
406 static void get_started_services ()
407 {
408 char *service;
409 int i;
410
411 rc_strlist_free (tmplist);
412 tmplist = rc_services_in_state (rc_service_inactive);
413
414 rc_strlist_free (restart_services);
415 restart_services = rc_services_in_state (rc_service_started);
416
417 STRLIST_FOREACH (tmplist, service, i)
418 restart_services = rc_strlist_addsort (restart_services, service);
419
420 rc_strlist_free (tmplist);
421 tmplist = NULL;
422 }
423
424 static void svc_start (const char *service, bool deps)
425 {
426 bool started;
427 bool background = false;
428 char *svc;
429 char *svc2;
430 int i;
431 int j;
432 int depoptions = RC_DEP_TRACE;
433
434 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
435 depoptions |= RC_DEP_STRICT;
436
437 if (rc_is_env ("IN_HOTPLUG", "1") || in_background)
438 {
439 if (! rc_service_state (service, rc_service_inactive))
440 exit (EXIT_FAILURE);
441 background = true;
442 }
443
444 if (rc_service_state (service, rc_service_started))
445 ewarnx ("WARNING: %s has already been started", applet);
446 else if (rc_service_state (service, rc_service_starting))
447 ewarnx ("WARNING: %s is already starting", applet);
448 else if (rc_service_state (service, rc_service_stopping))
449 ewarnx ("WARNING: %s is stopping", applet);
450 else if (rc_service_state (service, rc_service_inactive) && ! background)
451 ewarnx ("WARNING: %s has already started, but is inactive", applet);
452
453 if (! rc_mark_service (service, rc_service_starting))
454 eerrorx ("ERROR: %s has been started by something else", applet);
455
456 make_exclusive (service);
457
458 if (deps)
459 {
460 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
461 eerrorx ("failed to load deptree");
462
463 rc_strlist_free (types);
464 types = rc_strlist_add (NULL, "broken");
465 rc_strlist_free (svclist);
466 svclist = rc_strlist_add (NULL, applet);
467 rc_strlist_free (services);
468 services = rc_get_depends (deptree, types, svclist, softlevel, 0);
469 if (services)
470 {
471 eerrorn ("ERROR: `%s' needs ", applet);
472 STRLIST_FOREACH (services, svc, i)
473 {
474 if (i > 0)
475 fprintf (stderr, ", ");
476 fprintf (stderr, "%s", svc);
477 }
478 exit (EXIT_FAILURE);
479 }
480 rc_strlist_free (services);
481 services = NULL;
482
483 rc_strlist_free (types);
484 types = rc_strlist_add (NULL, "ineed");
485 rc_strlist_free (need_services);
486 need_services = rc_get_depends (deptree, types, svclist,
487 softlevel, depoptions);
488 types = rc_strlist_add (types, "iuse");
489 if (! rc_runlevel_starting ())
490 {
491 services = rc_get_depends (deptree, types, svclist,
492 softlevel, depoptions);
493 STRLIST_FOREACH (services, svc, i)
494 if (rc_service_state (svc, rc_service_stopped))
495 rc_start_service (svc);
496
497 rc_strlist_free (services);
498 }
499
500 /* Now wait for them to start */
501 types = rc_strlist_add (types, "iafter");
502 services = rc_get_depends (deptree, types, svclist,
503 softlevel, depoptions);
504
505 /* We use tmplist to hold our scheduled by list */
506 rc_strlist_free (tmplist);
507 tmplist = NULL;
508
509 STRLIST_FOREACH (services, svc, i)
510 {
511 if (rc_service_state (svc, rc_service_started))
512 continue;
513 if (! rc_wait_service (svc))
514 { eerror ("%s: timed out waiting for %s", applet, svc);
515 system ("ps ax > /tmp/$SVCNAME.waiting"); }
516 if (rc_service_state (svc, rc_service_started))
517 continue;
518
519 STRLIST_FOREACH (need_services, svc2, j)
520 if (strcmp (svc, svc2) == 0)
521 {
522 if (rc_service_state (svc, rc_service_inactive) ||
523 rc_service_state (svc, rc_service_wasinactive))
524 tmplist = rc_strlist_add (tmplist, svc);
525 else
526 eerrorx ("ERROR: cannot start %s as %s would not start",
527 applet, svc);
528 }
529 }
530
531 if (tmplist)
532 {
533 int n = 0;
534 int len = 0;
535 char *p;
536
537 /* Set the state now, then unlink our exclusive so that
538 our scheduled list is preserved */
539 rc_mark_service (service, rc_service_stopped);
540 unlink_mtime_test ();
541
542 rc_strlist_free (types);
543 types = rc_strlist_add (NULL, "iprovide");
544 STRLIST_FOREACH (tmplist, svc, i)
545 {
546 rc_schedule_start_service (svc, service);
547
548 rc_strlist_free (svclist);
549 svclist = rc_strlist_add (NULL, svc);
550 rc_strlist_free (providelist);
551 providelist = rc_get_depends (deptree, types, svclist,
552 softlevel, depoptions);
553 STRLIST_FOREACH (providelist, svc2, j)
554 rc_schedule_start_service (svc2, service);
555
556 len += strlen (svc) + 2;
557 n++;
558 }
559
560 tmp = rc_xmalloc (sizeof (char *) * len + 5);
561 p = tmp;
562 STRLIST_FOREACH (tmplist, svc, i)
563 {
564 if (i > 1)
565 {
566 if (i == n - 1)
567 p += sprintf (p, " or ");
568 else
569 p += sprintf (p, ", ");
570 }
571 p += sprintf (p, "%s", svc);
572 }
573 ewarnx ("WARNING: %s is scheduled to start when %s has started",
574 applet, tmp);
575 }
576
577 rc_strlist_free (services);
578 services = NULL;
579 rc_strlist_free (types);
580 types = NULL;
581 rc_strlist_free (svclist);
582 svclist = NULL;
583 }
584
585 if (ibsave)
586 setenv ("IN_BACKGROUND", ibsave, 1);
587 rc_plugin_run (rc_hook_service_start_in, applet);
588 hook_out = rc_hook_service_start_out;
589 started = svc_exec (service, "start", NULL);
590 if (ibsave)
591 unsetenv ("IN_BACKGROUND");
592
593 if (in_control ())
594 {
595 if (! started)
596 {
597 if (rc_service_state (service, rc_service_wasinactive))
598 rc_mark_service (service, rc_service_inactive);
599 else
600 {
601 rc_mark_service (service, rc_service_stopped);
602 if (rc_runlevel_starting ())
603 rc_mark_service (service, rc_service_failed);
604 }
605 eerrorx ("ERROR: %s failed to start", applet);
606 }
607
608 rc_mark_service (service, rc_service_started);
609 unlink_mtime_test ();
610
611 hook_out = 0;
612 rc_plugin_run (rc_hook_service_start_out, applet);
613 }
614 else
615 {
616 if (rc_service_state (service, rc_service_inactive))
617 ewarn ("WARNING: %s has started, but is inactive", applet);
618 else
619 ewarn ("WARNING: %s not under our control, aborting", applet);
620 }
621
622 /* Now start any scheduled services */
623 rc_strlist_free (services);
624 services = rc_services_scheduled (service);
625 STRLIST_FOREACH (services, svc, i)
626 if (rc_service_state (svc, rc_service_stopped))
627 rc_start_service (svc);
628 rc_strlist_free (services);
629 services = NULL;
630
631 /* Do the same for any services we provide */
632 rc_strlist_free (types);
633 types = rc_strlist_add (NULL, "iprovide");
634 rc_strlist_free (svclist);
635 svclist = rc_strlist_add (NULL, applet);
636 rc_strlist_free (tmplist);
637 tmplist = rc_get_depends (deptree, types, svclist, softlevel, depoptions);
638
639 STRLIST_FOREACH (tmplist, svc2, j)
640 {
641 rc_strlist_free (services);
642 services = rc_services_scheduled (svc2);
643 STRLIST_FOREACH (services, svc, i)
644 if (rc_service_state (svc, rc_service_stopped))
645 rc_start_service (svc);
646 }
647 }
648
649 static void svc_stop (const char *service, bool deps)
650 {
651 bool stopped;
652
653 if (rc_runlevel_stopping () &&
654 rc_service_state (service, rc_service_failed))
655 exit (EXIT_FAILURE);
656
657 if (rc_is_env ("IN_HOTPLUG", "1") || in_background)
658 if (! rc_service_state (service, rc_service_started))
659 exit (EXIT_FAILURE);
660
661 if (rc_service_state (service, rc_service_stopped))
662 ewarnx ("WARNING: %s is already stopped", applet);
663 else if (rc_service_state (service, rc_service_stopping))
664 ewarnx ("WARNING: %s is already stopping", applet);
665
666 if (! rc_mark_service (service, rc_service_stopping))
667 eerrorx ("ERROR: %s has been stopped by something else", applet);
668
669 make_exclusive (service);
670
671 if (! rc_runlevel_stopping () &&
672 rc_service_in_runlevel (service, RC_LEVEL_BOOT))
673 ewarn ("WARNING: you are stopping a boot service");
674
675 if (deps || ! rc_service_state (service, rc_service_wasinactive))
676 {
677 int depoptions = RC_DEP_TRACE;
678 char *svc;
679 int i;
680
681 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
682 depoptions |= RC_DEP_STRICT;
683
684 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
685 eerrorx ("failed to load deptree");
686
687 rc_strlist_free (types);
688 types = rc_strlist_add (NULL, "needsme");
689 rc_strlist_free (svclist);
690 svclist = rc_strlist_add (NULL, applet);
691 rc_strlist_free (tmplist);
692 tmplist = NULL;
693 rc_strlist_free (services);
694 services = rc_get_depends (deptree, types, svclist,
695 softlevel, depoptions);
696 rc_strlist_reverse (services);
697 STRLIST_FOREACH (services, svc, i)
698 {
699 if (rc_service_state (svc, rc_service_started) ||
700 rc_service_state (svc, rc_service_inactive))
701 {
702 rc_wait_service (svc);
703 if (rc_service_state (svc, rc_service_started) ||
704 rc_service_state (svc, rc_service_inactive))
705 {
706 rc_stop_service (svc);
707 tmplist = rc_strlist_add (tmplist, svc);
708 }
709 }
710 }
711 rc_strlist_free (services);
712 services = NULL;
713
714 STRLIST_FOREACH (tmplist, svc, i)
715 {
716 if (rc_service_state (svc, rc_service_stopped))
717 continue;
718
719 /* We used to loop 3 times here - maybe re-do this if needed */
720 rc_wait_service (svc);
721 if (! rc_service_state (svc, rc_service_stopped))
722 {
723 if (rc_runlevel_stopping ())
724 rc_mark_service (svc, rc_service_failed);
725 eerrorx ("ERROR: cannot stop %s as %s is still up",
726 applet, svc);
727 }
728 }
729 rc_strlist_free (tmplist);
730 tmplist = NULL;
731
732 /* We now wait for other services that may use us and are stopping
733 This is important when a runlevel stops */
734 types = rc_strlist_add (types, "usesme");
735 types = rc_strlist_add (types, "ibefore");
736 services = rc_get_depends (deptree, types, svclist,
737 softlevel, depoptions);
738 STRLIST_FOREACH (services, svc, i)
739 {
740 if (rc_service_state (svc, rc_service_stopped))
741 continue;
742 rc_wait_service (svc);
743 }
744
745 rc_strlist_free (services);
746 services = NULL;
747 rc_strlist_free (types);
748 types = NULL;
749 }
750
751 if (ibsave)
752 setenv ("IN_BACKGROUND", ibsave, 1);
753 rc_plugin_run (rc_hook_service_stop_in, applet);
754 hook_out = rc_hook_service_stop_out;
755 stopped = svc_exec (service, "stop", NULL);
756 if (ibsave)
757 unsetenv ("IN_BACKGROUND");
758
759 if (! in_control ())
760 ewarnx ("WARNING: %s not under our control, aborting", applet);
761
762 if (! stopped)
763 {
764 if (rc_service_state (service, rc_service_wasinactive))
765 rc_mark_service (service, rc_service_inactive);
766 else
767 rc_mark_service (service, rc_service_stopped);
768 eerrorx ("ERROR: %s failed to stop", applet);
769 }
770
771 if (in_background)
772 rc_mark_service (service, rc_service_inactive);
773 else
774 rc_mark_service (service, rc_service_stopped);
775
776 unlink_mtime_test ();
777 hook_out = 0;
778 rc_plugin_run (rc_hook_service_stop_out, applet);
779 }
780
781 static void svc_restart (const char *service, bool deps)
782 {
783 char *svc;
784 int i;
785 bool inactive = false;
786
787 /* This is hairly and a better way needs to be found I think!
788 The issue is this - openvpn need net and dns. net can restart
789 dns via resolvconf, so you could have openvpn trying to restart dnsmasq
790 which in turn is waiting on net which in turn is waiting on dnsmasq.
791 The work around is for resolvconf to restart it's services with --nodeps
792 which means just that. The downside is that there is a small window when
793 our status is invalid.
794 One workaround would be to introduce a new status, or status locking. */
795 if (! deps)
796 {
797 if (rc_service_state (service, rc_service_started) ||
798 rc_service_state (service, rc_service_inactive))
799 svc_exec (service, "stop", "start");
800 else
801 svc_exec (service, "start", NULL);
802 return;
803 }
804
805 if (! rc_service_state (service, rc_service_stopped))
806 {
807 get_started_services ();
808 svc_stop (service, deps);
809
810 /* Flush our buffered output if any */
811 eflush ();
812 }
813
814 svc_start (service, deps);
815
816 inactive = rc_service_state (service, rc_service_inactive);
817 if (! inactive)
818 inactive = rc_service_state (service, rc_service_wasinactive);
819
820 if (inactive ||
821 rc_service_state (service, rc_service_starting) ||
822 rc_service_state (service, rc_service_started))
823 {
824 STRLIST_FOREACH (restart_services, svc, i)
825 {
826 if (rc_service_state (svc, rc_service_stopped))
827 {
828 if (inactive)
829 {
830 rc_schedule_start_service (service, svc);
831 ewarn ("WARNING: %s is scheduled to started when %s has started",
832 svc, basename (service));
833 }
834 else
835 rc_start_service (svc);
836 }
837 }
838 }
839 }
840
841 int main (int argc, char **argv)
842 {
843 const char *service = argv[1];
844 int i;
845 bool deps = true;
846 bool doneone = false;
847 char pid[16];
848 int retval;
849 bool ifstarted = false;
850
851 applet = strdup (basename (service));
852 atexit (cleanup);
853
854 /* Show help if insufficient args */
855 if (argc < 3)
856 {
857 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, NULL);
858 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
859 applet, strerror (errno));
860 }
861
862 #ifdef __linux__
863 /* coldplug events can trigger init scripts, but we don't want to run them
864 until after rc sysinit has completed so we punt them to the boot runlevel */
865 if (rc_exists ("/dev/.rcsysinit"))
866 {
867 eerror ("%s: cannot run until sysvinit completes", applet);
868 if (mkdir ("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
869 eerrorx ("%s: mkdir `/dev/.rcboot': %s", applet, strerror (errno));
870 tmp = rc_strcatpaths ("/dev/.rcboot", applet, NULL);
871 symlink (service, tmp);
872 exit (EXIT_FAILURE);
873 }
874 #endif
875
876 if ((softlevel = getenv ("RC_SOFTLEVEL")) == NULL)
877 {
878 /* Ensure our environment is pure
879 Also, add our configuration to it */
880 env = rc_filter_env ();
881 env = rc_config_env (env);
882
883 if (env)
884 {
885 char *p;
886
887 #ifdef __linux__
888 /* clearenv isn't portable, but there's no harm in using it
889 if we have it */
890 clearenv ();
891 #else
892 char *var;
893 /* No clearenv present here then.
894 We could manipulate environ directly ourselves, but it seems that
895 some kernels bitch about this according to the environ man pages
896 so we walk though environ and call unsetenv for each value. */
897 while (environ[0])
898 {
899 tmp = rc_xstrdup (environ[0]);
900 p = tmp;
901 var = strsep (&p, "=");
902 unsetenv (var);
903 free (tmp);
904 }
905 tmp = NULL;
906 #endif
907
908 STRLIST_FOREACH (env, p, i)
909 putenv (p);
910
911 /* We don't free our list as that would be null in environ */
912 }
913
914 softlevel = rc_get_runlevel ();
915
916 /* If not called from RC or another service then don't be parallel */
917 unsetenv ("RC_PARALLEL_STARTUP");
918 }
919
920 setenv ("RC_ELOG", service, 1);
921 setenv ("SVCNAME", applet, 1);
922
923 /* Set an env var so that we always know our pid regardless of any
924 subshells the init script may create so that our mark_service_*
925 functions can always instruct us of this change */
926 snprintf (pid, sizeof (pid), "%d", (int) getpid ());
927 setenv ("RC_RUNSCRIPT_PID", pid, 1);
928
929 if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
930 {
931 char ebname[PATH_MAX];
932 char *eb;
933
934 snprintf (ebname, sizeof (ebname), "%s.%s", applet, pid);
935 eb = rc_strcatpaths (RC_SVCDIR "ebuffer", ebname, NULL);
936 setenv ("RC_EBUFFER", eb, 1);
937 free (eb);
938 }
939
940 /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service
941 that is being called and not any dependents */
942 if (getenv ("IN_BACKGROUND"))
943 {
944 in_background = rc_is_env ("IN_BACKGROUND", "true");
945 ibsave = strdup (getenv ("IN_BACKGROUND"));
946 unsetenv ("IN_BACKGROUND");
947
948 /* Don't hang around */
949 if (in_background)
950 setenv ("RC_PARALLEL_STARTUP", "yes", 1);
951 }
952
953 #ifdef __linux__
954 /* Ok, we are ready to go, so setup selinux if applicable */
955 setup_selinux (argc, argv);
956 #endif
957
958 /* Right then, parse any options there may be */
959 for (i = 2; i < argc; i++)
960 {
961 if (strlen (argv[i]) < 2 || argv[i][0] != '-' || argv[i][1] != '-')
962 continue;
963
964 if (strcmp (argv[i], "--debug") == 0)
965 setenv ("RC_DEBUG", "yes", 1);
966 else if (strcmp (argv[i], "--help") == 0)
967 {
968 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, NULL);
969 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
970 applet, strerror (errno));
971 }
972 else if (strcmp (argv[i],"--ifstarted") == 0)
973 ifstarted = true;
974 else if (strcmp (argv[i], "--nocolour") == 0 ||
975 strcmp (argv[i], "--nocolor") == 0)
976 setenv ("RC_NOCOLOR", "yes", 1);
977 else if (strcmp (argv[i], "--nodeps") == 0)
978 deps = false;
979 else if (strcmp (argv[i], "--quiet") == 0)
980 setenv ("RC_QUIET", "yes", 1);
981 else if (strcmp (argv[i], "--verbose") == 0)
982 setenv ("RC_VERBOSE", "yes", 1);
983 else if (strcmp (argv[i], "--version") == 0)
984 printf ("version me\n");
985 else
986 eerror ("%s: unknown option `%s'", applet, argv[i]);
987 }
988
989 if (ifstarted && ! rc_service_state (applet, rc_service_started))
990 {
991 if (! rc_is_env("RC_QUIET", "yes"))
992 eerror ("ERROR: %s is not started", applet);
993 exit (EXIT_FAILURE);
994 }
995
996 if (rc_is_env ("IN_HOTPLUG", "1"))
997 {
998 if (! rc_is_env ("RC_HOTPLUG", "yes") || ! rc_allow_plug (applet))
999 eerrorx ("%s: not allowed to be hotplugged", applet);
1000 }
1001
1002 /* Setup a signal handler */
1003 signal (SIGHUP, handle_signal);
1004 signal (SIGINT, handle_signal);
1005 signal (SIGQUIT, handle_signal);
1006 signal (SIGTERM, handle_signal);
1007 signal (SIGCHLD, handle_signal);
1008
1009 /* Load our plugins */
1010 rc_plugin_load ();
1011
1012 /* Now run each option */
1013 retval = EXIT_SUCCESS;
1014 for (i = 2; i < argc; i++)
1015 {
1016 /* Abort on a sighup here */
1017 if (sighup)
1018 exit (EXIT_FAILURE);
1019
1020 if (strlen (argv[i]) < 2 ||
1021 (argv[i][0] == '-' && argv[i][1] == '-'))
1022 continue;
1023
1024 /* Export the command we're running.
1025 This is important as we stamp on the restart function now but
1026 some start/stop routines still need to behave differently if
1027 restarting. */
1028 unsetenv ("RC_CMD");
1029 setenv ("RC_CMD", argv[i], 1);
1030
1031 doneone = true;
1032 if (strcmp (argv[i], "conditionalrestart") == 0 ||
1033 strcmp (argv[i], "condrestart") == 0)
1034 {
1035 if (rc_service_state (service, rc_service_started))
1036 svc_restart (service, deps);
1037 }
1038 else if (strcmp (argv[i], "restart") == 0)
1039 svc_restart (service, deps);
1040 else if (strcmp (argv[i], "start") == 0)
1041 svc_start (service, deps);
1042 else if (strcmp (argv[i], "status") == 0)
1043 {
1044 rc_service_state_t r = svc_status (service);
1045 retval = (int) r;
1046 }
1047 else if (strcmp (argv[i], "stop") == 0)
1048 {
1049 if (in_background)
1050 get_started_services ();
1051 else if (! rc_runlevel_stopping ())
1052 uncoldplug (applet);
1053
1054 svc_stop (service, deps);
1055
1056 if (in_background &&
1057 rc_service_state (service, rc_service_inactive))
1058 {
1059 char *svc;
1060 int j;
1061 STRLIST_FOREACH (restart_services, svc, j)
1062 if (rc_service_state (svc, rc_service_stopped))
1063 rc_schedule_start_service (service, svc);
1064 }
1065 }
1066 else if (strcmp (argv[i], "zap") == 0)
1067 {
1068 einfo ("Manually resetting %s to stopped state", applet);
1069 rc_mark_service (applet, rc_service_stopped);
1070 uncoldplug (applet);
1071 }
1072 else if (strcmp (argv[i], "help") == 0)
1073 {
1074 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", NULL);
1075 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1076 applet, strerror (errno));
1077 }
1078 else
1079 svc_exec (service, argv[i], NULL);
1080
1081 /* Flush our buffered output if any */
1082 eflush ();
1083
1084 /* We should ensure this list is empty after an action is done */
1085 rc_strlist_free (restart_services);
1086 restart_services = NULL;
1087 }
1088
1089 if (! doneone)
1090 {
1091 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, NULL);
1092 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1093 applet, strerror (errno));
1094 }
1095
1096 return (retval);
1097 }

  ViewVC Help
Powered by ViewVC 1.1.20