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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20