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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2641 - (show annotations) (download) (as text)
Fri Apr 20 18:58:42 2007 UTC (7 years, 5 months ago) by uberlord
File MIME type: text/x-csrc
File size: 28506 byte(s)
Re-work the starting and stopping of services so we always know the pid so we can kill our children without the need for process groups which we cannot use as keyboard will not work in init.
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 eflush ();
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 /* We need to disable our child signal handler now so we block
271 until our script returns. */
272 signal (SIGCHLD, NULL);
273
274 service_pid = vfork();
275
276 if (service_pid == -1)
277 eerrorx ("%s: vfork: %s", service, strerror (errno));
278 if (service_pid == 0) {
279 if (rc_exists (RC_SVCDIR "runscript.sh")) {
280 execl (RC_SVCDIR "runscript.sh", service, service, arg1, arg2,
281 (char *) NULL);
282 eerror ("%s: exec `" RC_SVCDIR "runscript.sh': %s",
283 service, strerror (errno));
284 _exit (EXIT_FAILURE);
285 } else {
286 execl (RC_LIBDIR "sh/runscript.sh", service, service, arg1, arg2,
287 (char *) NULL);
288 eerror ("%s: exec `" RC_LIBDIR "sh/runscript.sh': %s",
289 service, strerror (errno));
290 _exit (EXIT_FAILURE);
291 }
292 }
293
294 retval = rc_waitpid (service_pid) == 0 ? true : false;
295
296 service_pid = 0;
297 /* Done, so restore the signal handler */
298 signal (SIGCHLD, handle_signal);
299
300 return (retval);
301 }
302
303 static rc_service_state_t svc_status (const char *service)
304 {
305 char status[10];
306 int (*e) (const char *fmt, ...) = &einfo;
307
308 rc_service_state_t retval = rc_service_stopped;
309
310 if (rc_service_state (service, rc_service_stopping)) {
311 snprintf (status, sizeof (status), "stopping");
312 e = &ewarn;
313 retval = rc_service_stopping;
314 } else if (rc_service_state (service, rc_service_starting)) {
315 snprintf (status, sizeof (status), "starting");
316 e = &ewarn;
317 retval = rc_service_starting;
318 } else if (rc_service_state (service, rc_service_inactive)) {
319 snprintf (status, sizeof (status), "inactive");
320 e = &ewarn;
321 retval = rc_service_inactive;
322 } else if (rc_service_state (service, rc_service_crashed)) {
323 snprintf (status, sizeof (status), "crashed");
324 e = &eerror;
325 retval = rc_service_crashed;
326 } else if (rc_service_state (service, rc_service_started)) {
327 snprintf (status, sizeof (status), "started");
328 retval = rc_service_started;
329 } else
330 snprintf (status, sizeof (status), "stopped");
331
332 e ("status: %s", status);
333 return (retval);
334 }
335
336 static void make_exclusive (const char *service)
337 {
338 char *path;
339 int i;
340
341 /* We create a fifo so that other services can wait until we complete */
342 if (! exclusive)
343 exclusive = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
344
345 if (mkfifo (exclusive, 0600) != 0 && errno != EEXIST &&
346 (errno != EACCES || geteuid () == 0))
347 eerrorx ("%s: unable to create fifo `%s': %s",
348 applet, exclusive, strerror (errno));
349
350 path = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
351 i = strlen (path) + 16;
352 mtime_test = rc_xmalloc (sizeof (char *) * i);
353 snprintf (mtime_test, i, "%s.%d", path, getpid ());
354 free (path);
355
356 if (rc_exists (mtime_test) && unlink (mtime_test) != 0) {
357 eerror ("%s: unlink `%s': %s",
358 applet, mtime_test, strerror (errno));
359 free (mtime_test);
360 mtime_test = NULL;
361 return;
362 }
363
364 if (symlink (service, mtime_test) != 0) {
365 eerror ("%s: symlink `%s' to `%s': %s",
366 applet, service, mtime_test, strerror (errno));
367 free (mtime_test);
368 mtime_test = NULL;
369 }
370 }
371
372 static void unlink_mtime_test ()
373 {
374 if (unlink (mtime_test) != 0)
375 eerror ("%s: unlink `%s': %s", applet, mtime_test, strerror (errno));
376 free (mtime_test);
377 mtime_test = NULL;
378 }
379
380 static void get_started_services ()
381 {
382 char *service;
383 int i;
384
385 rc_strlist_free (tmplist);
386 tmplist = rc_services_in_state (rc_service_inactive);
387
388 rc_strlist_free (restart_services);
389 restart_services = rc_services_in_state (rc_service_started);
390
391 STRLIST_FOREACH (tmplist, service, i)
392 restart_services = rc_strlist_addsort (restart_services, service);
393
394 rc_strlist_free (tmplist);
395 tmplist = NULL;
396 }
397
398 static void svc_start (const char *service, bool deps)
399 {
400 bool started;
401 bool background = false;
402 char *svc;
403 char *svc2;
404 int i;
405 int j;
406 int depoptions = RC_DEP_TRACE;
407
408 rc_plugin_run (rc_hook_service_start_in, applet);
409 hook_out = rc_hook_service_start_out;
410
411 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
412 depoptions |= RC_DEP_STRICT;
413
414 if (rc_is_env ("IN_HOTPLUG", "1") || in_background) {
415 if (! rc_service_state (service, rc_service_inactive) &&
416 ! rc_service_state (service, rc_service_stopped))
417 exit (EXIT_FAILURE);
418 background = true;
419 }
420
421 if (rc_service_state (service, rc_service_started))
422 ewarnx ("WARNING: %s has already been started", applet);
423 else if (rc_service_state (service, rc_service_starting))
424 ewarnx ("WARNING: %s is already starting", applet);
425 else if (rc_service_state (service, rc_service_stopping))
426 ewarnx ("WARNING: %s is stopping", applet);
427 else if (rc_service_state (service, rc_service_inactive) && ! background)
428 ewarnx ("WARNING: %s has already started, but is inactive", applet);
429
430 if (! rc_mark_service (service, rc_service_starting))
431 eerrorx ("ERROR: %s has been started by something else", applet);
432
433 make_exclusive (service);
434
435 if (deps) {
436 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
437 eerrorx ("failed to load deptree");
438
439 rc_strlist_free (types);
440 types = rc_strlist_add (NULL, "broken");
441 rc_strlist_free (svclist);
442 svclist = rc_strlist_add (NULL, applet);
443 rc_strlist_free (services);
444 services = rc_get_depends (deptree, types, svclist, softlevel, 0);
445 if (services) {
446 eerrorn ("ERROR: `%s' needs ", applet);
447 STRLIST_FOREACH (services, svc, i) {
448 if (i > 0)
449 fprintf (stderr, ", ");
450 fprintf (stderr, "%s", svc);
451 }
452 exit (EXIT_FAILURE);
453 }
454 rc_strlist_free (services);
455 services = NULL;
456
457 rc_strlist_free (types);
458 types = rc_strlist_add (NULL, "ineed");
459 rc_strlist_free (need_services);
460 need_services = rc_get_depends (deptree, types, svclist,
461 softlevel, depoptions);
462 types = rc_strlist_add (types, "iuse");
463 if (! rc_runlevel_starting ()) {
464 services = rc_get_depends (deptree, types, svclist,
465 softlevel, depoptions);
466 STRLIST_FOREACH (services, svc, i)
467 if (rc_service_state (svc, rc_service_stopped)) {
468 pid_t pid = rc_start_service (svc);
469 if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
470 rc_waitpid (pid);
471 }
472
473 rc_strlist_free (services);
474 }
475
476 /* Now wait for them to start */
477 types = rc_strlist_add (types, "iafter");
478 services = rc_get_depends (deptree, types, svclist,
479 softlevel, depoptions);
480
481 /* We use tmplist to hold our scheduled by list */
482 rc_strlist_free (tmplist);
483 tmplist = NULL;
484
485 STRLIST_FOREACH (services, svc, i) {
486 if (rc_service_state (svc, rc_service_started))
487 continue;
488 if (! rc_wait_service (svc))
489 eerror ("%s: timed out waiting for %s", applet, svc);
490 if (rc_service_state (svc, rc_service_started))
491 continue;
492
493 STRLIST_FOREACH (need_services, svc2, j)
494 if (strcmp (svc, svc2) == 0) {
495 if (rc_service_state (svc, rc_service_inactive) ||
496 rc_service_state (svc, rc_service_wasinactive))
497 tmplist = rc_strlist_add (tmplist, svc);
498 else
499 eerrorx ("ERROR: cannot start %s as %s would not start",
500 applet, svc);
501 }
502 }
503
504 if (tmplist) {
505 int n = 0;
506 int len = 0;
507 char *p;
508
509 /* Set the state now, then unlink our exclusive so that
510 our scheduled list is preserved */
511 rc_mark_service (service, rc_service_stopped);
512 unlink_mtime_test ();
513
514 rc_strlist_free (types);
515 types = rc_strlist_add (NULL, "iprovide");
516 STRLIST_FOREACH (tmplist, svc, i) {
517 rc_schedule_start_service (svc, service);
518
519 rc_strlist_free (svclist);
520 svclist = rc_strlist_add (NULL, svc);
521 rc_strlist_free (providelist);
522 providelist = rc_get_depends (deptree, types, svclist,
523 softlevel, depoptions);
524 STRLIST_FOREACH (providelist, svc2, j)
525 rc_schedule_start_service (svc2, service);
526
527 len += strlen (svc) + 2;
528 n++;
529 }
530
531 len += 5;
532 tmp = rc_xmalloc (sizeof (char *) * len);
533 p = tmp;
534 STRLIST_FOREACH (tmplist, svc, i) {
535 if (i > 1) {
536 if (i == n - 1)
537 p += snprintf (p, len, " or ");
538 else
539 p += snprintf (p, len, ", ");
540 }
541 p += snprintf (p, len, "%s", svc);
542 }
543 ewarnx ("WARNING: %s is scheduled to start when %s has started",
544 applet, tmp);
545 }
546
547 rc_strlist_free (services);
548 services = NULL;
549 rc_strlist_free (types);
550 types = NULL;
551 rc_strlist_free (svclist);
552 svclist = NULL;
553 }
554
555 if (ibsave)
556 setenv ("IN_BACKGROUND", ibsave, 1);
557 rc_plugin_run (rc_hook_service_start_now, applet);
558 started = svc_exec (service, "start", NULL);
559 if (ibsave)
560 unsetenv ("IN_BACKGROUND");
561
562 if (in_control ()) {
563 if (! started) {
564 if (rc_service_state (service, rc_service_wasinactive))
565 rc_mark_service (service, rc_service_inactive);
566 else {
567 rc_mark_service (service, rc_service_stopped);
568 if (rc_runlevel_starting ())
569 rc_mark_service (service, rc_service_failed);
570 }
571 rc_plugin_run (rc_hook_service_start_done, applet);
572 eerrorx ("ERROR: %s failed to start", applet);
573 }
574 rc_mark_service (service, rc_service_started);
575 unlink_mtime_test ();
576 rc_plugin_run (rc_hook_service_start_done, applet);
577 } else {
578 rc_plugin_run (rc_hook_service_start_done, applet);
579 if (rc_service_state (service, rc_service_inactive))
580 ewarnx ("WARNING: %s has started, but is inactive", applet);
581 else
582 ewarnx ("WARNING: %s not under our control, aborting", applet);
583 }
584
585 /* Now start any scheduled services */
586 rc_strlist_free (services);
587 services = rc_services_scheduled (service);
588 STRLIST_FOREACH (services, svc, i)
589 if (rc_service_state (svc, rc_service_stopped))
590 rc_start_service (svc);
591 rc_strlist_free (services);
592 services = NULL;
593
594 /* Do the same for any services we provide */
595 rc_strlist_free (types);
596 types = rc_strlist_add (NULL, "iprovide");
597 rc_strlist_free (svclist);
598 svclist = rc_strlist_add (NULL, applet);
599 rc_strlist_free (tmplist);
600 tmplist = rc_get_depends (deptree, types, svclist, softlevel, depoptions);
601
602 STRLIST_FOREACH (tmplist, svc2, j) {
603 rc_strlist_free (services);
604 services = rc_services_scheduled (svc2);
605 STRLIST_FOREACH (services, svc, i)
606 if (rc_service_state (svc, rc_service_stopped))
607 rc_start_service (svc);
608 }
609
610 hook_out = 0;
611 rc_plugin_run (rc_hook_service_start_out, applet);
612 }
613
614 static void svc_stop (const char *service, bool deps)
615 {
616 bool stopped;
617
618 hook_out = rc_hook_service_stop_out;
619
620 if (rc_runlevel_stopping () &&
621 rc_service_state (service, rc_service_failed))
622 exit (EXIT_FAILURE);
623
624 if (rc_is_env ("IN_HOTPLUG", "1") || in_background)
625 if (! rc_service_state (service, rc_service_started) &&
626 ! rc_service_state (service, rc_service_inactive))
627 exit (EXIT_FAILURE);
628
629 if (rc_service_state (service, rc_service_stopped))
630 ewarnx ("WARNING: %s is already stopped", applet);
631 else if (rc_service_state (service, rc_service_stopping))
632 ewarnx ("WARNING: %s is already stopping", applet);
633
634 if (! rc_mark_service (service, rc_service_stopping))
635 eerrorx ("ERROR: %s has been stopped by something else", applet);
636
637 make_exclusive (service);
638
639 if (! rc_runlevel_stopping () &&
640 rc_service_in_runlevel (service, RC_LEVEL_BOOT))
641 ewarn ("WARNING: you are stopping a boot service");
642
643 if (deps || ! rc_service_state (service, rc_service_wasinactive)) {
644 int depoptions = RC_DEP_TRACE;
645 char *svc;
646 int i;
647
648 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
649 depoptions |= RC_DEP_STRICT;
650
651 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
652 eerrorx ("failed to load deptree");
653
654 rc_strlist_free (types);
655 types = rc_strlist_add (NULL, "needsme");
656 rc_strlist_free (svclist);
657 svclist = rc_strlist_add (NULL, applet);
658 rc_strlist_free (tmplist);
659 tmplist = NULL;
660 rc_strlist_free (services);
661 services = rc_get_depends (deptree, types, svclist,
662 softlevel, depoptions);
663 rc_strlist_reverse (services);
664 STRLIST_FOREACH (services, svc, i) {
665 if (rc_service_state (svc, rc_service_started) ||
666 rc_service_state (svc, rc_service_inactive))
667 {
668 rc_wait_service (svc);
669 if (rc_service_state (svc, rc_service_started) ||
670 rc_service_state (svc, rc_service_inactive))
671 {
672 pid_t pid = rc_stop_service (svc);
673 if (! rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
674 rc_waitpid (pid);
675 tmplist = rc_strlist_add (tmplist, svc);
676 }
677 }
678 }
679 rc_strlist_free (services);
680 services = NULL;
681
682 STRLIST_FOREACH (tmplist, svc, i) {
683 if (rc_service_state (svc, rc_service_stopped))
684 continue;
685
686 /* We used to loop 3 times here - maybe re-do this if needed */
687 rc_wait_service (svc);
688 if (! rc_service_state (svc, rc_service_stopped)) {
689 if (rc_runlevel_stopping ())
690 rc_mark_service (svc, rc_service_failed);
691 eerrorx ("ERROR: cannot stop %s as %s is still up",
692 applet, svc);
693 }
694 }
695 rc_strlist_free (tmplist);
696 tmplist = NULL;
697
698 /* We now wait for other services that may use us and are stopping
699 This is important when a runlevel stops */
700 types = rc_strlist_add (types, "usesme");
701 types = rc_strlist_add (types, "ibefore");
702 services = rc_get_depends (deptree, types, svclist,
703 softlevel, depoptions);
704 STRLIST_FOREACH (services, svc, i) {
705 if (rc_service_state (svc, rc_service_stopped))
706 continue;
707 rc_wait_service (svc);
708 }
709
710 rc_strlist_free (services);
711 services = NULL;
712 rc_strlist_free (types);
713 types = NULL;
714 }
715
716 if (ibsave)
717 setenv ("IN_BACKGROUND", ibsave, 1);
718 rc_plugin_run (rc_hook_service_stop_now, applet);
719 stopped = svc_exec (service, "stop", NULL);
720 if (ibsave)
721 unsetenv ("IN_BACKGROUND");
722
723 if (! in_control ()) {
724 rc_plugin_run (rc_hook_service_stop_done, applet);
725 ewarnx ("WARNING: %s not under our control, aborting", applet);
726 }
727
728 if (! stopped) {
729 if (rc_service_state (service, rc_service_wasinactive))
730 rc_mark_service (service, rc_service_inactive);
731 else
732 rc_mark_service (service, rc_service_started);
733 rc_plugin_run (rc_hook_service_stop_done, applet);
734 eerrorx ("ERROR: %s failed to stop", applet);
735 }
736
737 if (in_background)
738 rc_mark_service (service, rc_service_inactive);
739 else
740 rc_mark_service (service, rc_service_stopped);
741
742 unlink_mtime_test ();
743 rc_plugin_run (rc_hook_service_stop_done, applet);
744 hook_out = 0;
745 rc_plugin_run (rc_hook_service_stop_out, applet);
746 }
747
748 static void svc_restart (const char *service, bool deps)
749 {
750 char *svc;
751 int i;
752 bool inactive = false;
753
754 /* This is hairly and a better way needs to be found I think!
755 The issue is this - openvpn need net and dns. net can restart
756 dns via resolvconf, so you could have openvpn trying to restart dnsmasq
757 which in turn is waiting on net which in turn is waiting on dnsmasq.
758 The work around is for resolvconf to restart it's services with --nodeps
759 which means just that. The downside is that there is a small window when
760 our status is invalid.
761 One workaround would be to introduce a new status, or status locking. */
762 if (! deps) {
763 if (rc_service_state (service, rc_service_started) ||
764 rc_service_state (service, rc_service_inactive))
765 svc_exec (service, "stop", "start");
766 else
767 svc_exec (service, "start", NULL);
768 return;
769 }
770
771 if (! rc_service_state (service, rc_service_stopped)) {
772 get_started_services ();
773 svc_stop (service, deps);
774
775 /* Flush our buffered output if any */
776 eflush ();
777 }
778
779 svc_start (service, deps);
780
781 inactive = rc_service_state (service, rc_service_inactive);
782 if (! inactive)
783 inactive = rc_service_state (service, rc_service_wasinactive);
784
785 if (inactive ||
786 rc_service_state (service, rc_service_starting) ||
787 rc_service_state (service, rc_service_started))
788 {
789 STRLIST_FOREACH (restart_services, svc, i) {
790 if (rc_service_state (svc, rc_service_stopped)) {
791 if (inactive) {
792 rc_schedule_start_service (service, svc);
793 ewarn ("WARNING: %s is scheduled to started when %s has started",
794 svc, applet);
795 } else
796 rc_start_service (svc);
797 }
798 }
799 }
800 }
801
802 #define getoptstring "dCDNqvh"
803 static struct option longopts[] = {
804 { "debug", 0, NULL, 'd'},
805 { "nocolor", 0, NULL, 'C'},
806 { "nocolour", 0, NULL, 'C'},
807 { "nodeps", 0, NULL, 'D'},
808 { "quiet", 0, NULL, 'q'},
809 { "verbose", 0, NULL, 'v'},
810 { "help", 0, NULL, 'h'},
811 { NULL, 0, NULL, 0}
812 };
813 // #include "_usage.c"
814
815 int main (int argc, char **argv)
816 {
817 char *service = argv[1];
818 int i;
819 bool deps = true;
820 bool doneone = false;
821 char pid[16];
822 int retval;
823 char c;
824
825 /* Show help if insufficient args */
826 if (argc < 3) {
827 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
828 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
829 applet, strerror (errno));
830 }
831
832 applet = rc_xstrdup (basename (service));
833 atexit (cleanup);
834
835 #ifdef __linux__
836 /* coldplug events can trigger init scripts, but we don't want to run them
837 until after rc sysinit has completed so we punt them to the boot runlevel */
838 if (rc_exists ("/dev/.rcsysinit")) {
839 eerror ("%s: cannot run until sysvinit completes", applet);
840 if (mkdir ("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
841 eerrorx ("%s: mkdir `/dev/.rcboot': %s", applet, strerror (errno));
842 tmp = rc_strcatpaths ("/dev/.rcboot", applet, (char *) NULL);
843 symlink (service, tmp);
844 exit (EXIT_FAILURE);
845 }
846 #endif
847
848 if ((softlevel = getenv ("RC_SOFTLEVEL")) == NULL) {
849 /* Ensure our environment is pure
850 Also, add our configuration to it */
851 env = rc_filter_env ();
852 env = rc_config_env (env);
853
854 if (env) {
855 char *p;
856
857 #ifdef __linux__
858 /* clearenv isn't portable, but there's no harm in using it
859 if we have it */
860 clearenv ();
861 #else
862 char *var;
863 /* No clearenv present here then.
864 We could manipulate environ directly ourselves, but it seems that
865 some kernels bitch about this according to the environ man pages
866 so we walk though environ and call unsetenv for each value. */
867 while (environ[0]) {
868 tmp = rc_xstrdup (environ[0]);
869 p = tmp;
870 var = strsep (&p, "=");
871 unsetenv (var);
872 free (tmp);
873 }
874 tmp = NULL;
875 #endif
876
877 STRLIST_FOREACH (env, p, i)
878 putenv (p);
879
880 /* We don't free our list as that would be null in environ */
881 }
882
883 softlevel = rc_get_runlevel ();
884
885 /* If not called from RC or another service then don't be parallel */
886 unsetenv ("RC_PARALLEL_STARTUP");
887 }
888
889 setenv ("RC_ELOG", service, 1);
890 setenv ("SVCNAME", applet, 1);
891
892 /* Set an env var so that we always know our pid regardless of any
893 subshells the init script may create so that our mark_service_*
894 functions can always instruct us of this change */
895 snprintf (pid, sizeof (pid), "%d", (int) getpid ());
896 setenv ("RC_RUNSCRIPT_PID", pid, 1);
897
898 if (rc_is_env ("RC_PARALLEL_STARTUP", "yes")) {
899 char ebname[PATH_MAX];
900 char *eb;
901
902 snprintf (ebname, sizeof (ebname), "%s.%s", applet, pid);
903 eb = rc_strcatpaths (RC_SVCDIR "ebuffer", ebname, (char *) NULL);
904 setenv ("RC_EBUFFER", eb, 1);
905 free (eb);
906 }
907
908 #ifdef __linux__
909 /* Ok, we are ready to go, so setup selinux if applicable */
910 setup_selinux (argc, argv);
911 #endif
912
913 /* Punt the first arg as it's our service name */
914 argc--;
915 argv++;
916
917 /* Right then, parse any options there may be */
918 while ((c = getopt_long (argc, argv, getoptstring,
919 longopts, (int *) 0)) != -1)
920 switch (c) {
921 case 'd':
922 setenv ("RC_DEBUG", "yes", 1);
923 break;
924 case 'h':
925 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
926 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
927 applet, strerror (errno));
928 case 'C':
929 setenv ("RC_NOCOLOR", "yes", 1);
930 break;
931 case 'D':
932 deps = false;
933 break;
934 case 'q':
935 setenv ("RC_QUIET", "yes", 1);
936 break;
937 case 'v':
938 setenv ("RC_VERBOSE", "yes", 1);
939 break;
940 default:
941 exit (EXIT_FAILURE);
942 }
943
944 /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service
945 that is being called and not any dependents */
946 if (getenv ("IN_BACKGROUND")) {
947 in_background = rc_is_env ("IN_BACKGROUND", "true");
948 ibsave = rc_xstrdup (getenv ("IN_BACKGROUND"));
949 unsetenv ("IN_BACKGROUND");
950 }
951
952 if (rc_is_env ("IN_HOTPLUG", "1")) {
953 if (! rc_is_env ("RC_HOTPLUG", "yes") || ! rc_allow_plug (applet))
954 eerrorx ("%s: not allowed to be hotplugged", applet);
955 }
956
957 /* Setup a signal handler */
958 signal (SIGHUP, handle_signal);
959 signal (SIGINT, handle_signal);
960 signal (SIGQUIT, handle_signal);
961 signal (SIGTERM, handle_signal);
962 signal (SIGCHLD, handle_signal);
963
964 /* Load our plugins */
965 rc_plugin_load ();
966
967 /* Now run each option */
968 retval = EXIT_SUCCESS;
969 while (optind < argc) {
970 optarg = argv[optind++];
971
972 /* Abort on a sighup here */
973 if (sighup)
974 exit (EXIT_FAILURE);
975
976 /* Export the command we're running.
977 This is important as we stamp on the restart function now but
978 some start/stop routines still need to behave differently if
979 restarting. */
980 unsetenv ("RC_CMD");
981 setenv ("RC_CMD", optarg, 1);
982
983 doneone = true;
984 if (strcmp (optarg, "conditionalrestart") == 0 ||
985 strcmp (optarg, "condrestart") == 0)
986 {
987 if (rc_service_state (service, rc_service_started))
988 svc_restart (service, deps);
989 }
990 else if (strcmp (optarg, "restart") == 0)
991 svc_restart (service, deps);
992 else if (strcmp (optarg, "start") == 0)
993 svc_start (service, deps);
994 else if (strcmp (optarg, "status") == 0) {
995 rc_service_state_t r = svc_status (service);
996 retval = (int) r;
997 } else if (strcmp (optarg, "stop") == 0) {
998 if (in_background)
999 get_started_services ();
1000
1001 svc_stop (service, deps);
1002
1003 if (! in_background &&
1004 ! rc_runlevel_stopping () &&
1005 rc_service_state (service, rc_service_stopped))
1006 uncoldplug (applet);
1007
1008 if (in_background &&
1009 rc_service_state (service, rc_service_inactive))
1010 {
1011 char *svc;
1012 int j;
1013 STRLIST_FOREACH (restart_services, svc, j)
1014 if (rc_service_state (svc, rc_service_stopped))
1015 rc_schedule_start_service (service, svc);
1016 }
1017 } else if (strcmp (optarg, "zap") == 0) {
1018 einfo ("Manually resetting %s to stopped state", applet);
1019 rc_mark_service (applet, rc_service_stopped);
1020 uncoldplug (applet);
1021 } else if (strcmp (optarg, "help") == 0) {
1022 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", (char *) NULL);
1023 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1024 applet, strerror (errno));
1025 }else
1026 svc_exec (service, optarg, NULL);
1027
1028 /* Flush our buffered output if any */
1029 eflush ();
1030
1031 /* We should ensure this list is empty after an action is done */
1032 rc_strlist_free (restart_services);
1033 restart_services = NULL;
1034 }
1035
1036 if (! doneone) {
1037 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1038 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1039 applet, strerror (errno));
1040 }
1041
1042 return (retval);
1043 }

  ViewVC Help
Powered by ViewVC 1.1.20