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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20