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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2652 - (show annotations) (download) (as text)
Wed Apr 25 12:30:24 2007 UTC (7 years, 3 months ago) by uberlord
File MIME type: text/x-csrc
File size: 31966 byte(s)
    We now have an alternative to buffering stdout and stderr.
    RC_PREFIX="yes" will put the service name as a prefix to all output
    made by the service. Thanks to Ciaran McCreesh for the idea.
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 } else if (retval) {
381 ssize_t nr;
382
383 if (FD_ISSET (stdout_pipes[0], &fds)) {
384 if ((nr = read (stdout_pipes[0], buffer,
385 sizeof (buffer))) <= 0)
386 stdout_done = true;
387 else
388 write_prefix (fileno (stdout), buffer, nr,
389 &stdout_prefix_shown);
390 }
391
392 if (FD_ISSET (stderr_pipes[0], &fds)) {
393 if ((nr = read (stderr_pipes[0], buffer,
394 sizeof (buffer))) <= 0)
395 stderr_done = true;
396 else
397 write_prefix (fileno (stderr), buffer, nr,
398 &stderr_prefix_shown);
399 }
400 }
401 }
402
403 /* Done now, so close the pipes */
404 close(stdout_pipes[0]);
405 close(stderr_pipes[0]);
406 }
407
408 execok = rc_waitpid (service_pid) == 0 ? true : false;
409 service_pid = 0;
410
411 /* Done, so restore the signal handler */
412 signal (SIGCHLD, handle_signal);
413
414 return (execok);
415 }
416
417 static rc_service_state_t svc_status (const char *service)
418 {
419 char status[10];
420 int (*e) (const char *fmt, ...) = &einfo;
421
422 rc_service_state_t retval = rc_service_stopped;
423
424 if (rc_service_state (service, rc_service_stopping)) {
425 snprintf (status, sizeof (status), "stopping");
426 e = &ewarn;
427 retval = rc_service_stopping;
428 } else if (rc_service_state (service, rc_service_starting)) {
429 snprintf (status, sizeof (status), "starting");
430 e = &ewarn;
431 retval = rc_service_starting;
432 } else if (rc_service_state (service, rc_service_inactive)) {
433 snprintf (status, sizeof (status), "inactive");
434 e = &ewarn;
435 retval = rc_service_inactive;
436 } else if (rc_service_state (service, rc_service_crashed)) {
437 snprintf (status, sizeof (status), "crashed");
438 e = &eerror;
439 retval = rc_service_crashed;
440 } else if (rc_service_state (service, rc_service_started)) {
441 snprintf (status, sizeof (status), "started");
442 retval = rc_service_started;
443 } else
444 snprintf (status, sizeof (status), "stopped");
445
446 e ("status: %s", status);
447 return (retval);
448 }
449
450 static void make_exclusive (const char *service)
451 {
452 char *path;
453 int i;
454
455 /* We create a fifo so that other services can wait until we complete */
456 if (! exclusive)
457 exclusive = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
458
459 if (mkfifo (exclusive, 0600) != 0 && errno != EEXIST &&
460 (errno != EACCES || geteuid () == 0))
461 eerrorx ("%s: unable to create fifo `%s': %s",
462 applet, exclusive, strerror (errno));
463
464 path = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
465 i = strlen (path) + 16;
466 mtime_test = rc_xmalloc (sizeof (char *) * i);
467 snprintf (mtime_test, i, "%s.%d", path, getpid ());
468 free (path);
469
470 if (rc_exists (mtime_test) && unlink (mtime_test) != 0) {
471 eerror ("%s: unlink `%s': %s",
472 applet, mtime_test, strerror (errno));
473 free (mtime_test);
474 mtime_test = NULL;
475 return;
476 }
477
478 if (symlink (service, mtime_test) != 0) {
479 eerror ("%s: symlink `%s' to `%s': %s",
480 applet, service, mtime_test, strerror (errno));
481 free (mtime_test);
482 mtime_test = NULL;
483 }
484 }
485
486 static void unlink_mtime_test ()
487 {
488 if (unlink (mtime_test) != 0)
489 eerror ("%s: unlink `%s': %s", applet, mtime_test, strerror (errno));
490 free (mtime_test);
491 mtime_test = NULL;
492 }
493
494 static void get_started_services ()
495 {
496 char *service;
497 int i;
498
499 rc_strlist_free (tmplist);
500 tmplist = rc_services_in_state (rc_service_inactive);
501
502 rc_strlist_free (restart_services);
503 restart_services = rc_services_in_state (rc_service_started);
504
505 STRLIST_FOREACH (tmplist, service, i)
506 restart_services = rc_strlist_addsort (restart_services, service);
507
508 rc_strlist_free (tmplist);
509 tmplist = NULL;
510 }
511
512 static void svc_start (const char *service, bool deps)
513 {
514 bool started;
515 bool background = false;
516 char *svc;
517 char *svc2;
518 int i;
519 int j;
520 int depoptions = RC_DEP_TRACE;
521
522 rc_plugin_run (rc_hook_service_start_in, applet);
523 hook_out = rc_hook_service_start_out;
524
525 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
526 depoptions |= RC_DEP_STRICT;
527
528 if (rc_is_env ("IN_HOTPLUG", "1") || in_background) {
529 if (! rc_service_state (service, rc_service_inactive) &&
530 ! rc_service_state (service, rc_service_stopped))
531 exit (EXIT_FAILURE);
532 background = true;
533 }
534
535 if (rc_service_state (service, rc_service_started))
536 ewarnx ("WARNING: %s has already been started", applet);
537 else if (rc_service_state (service, rc_service_starting))
538 ewarnx ("WARNING: %s is already starting", applet);
539 else if (rc_service_state (service, rc_service_stopping))
540 ewarnx ("WARNING: %s is stopping", applet);
541 else if (rc_service_state (service, rc_service_inactive) && ! background)
542 ewarnx ("WARNING: %s has already started, but is inactive", applet);
543
544 if (! rc_mark_service (service, rc_service_starting))
545 eerrorx ("ERROR: %s has been started by something else", applet);
546
547 make_exclusive (service);
548
549 if (deps) {
550 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
551 eerrorx ("failed to load deptree");
552
553 rc_strlist_free (types);
554 types = rc_strlist_add (NULL, "broken");
555 rc_strlist_free (svclist);
556 svclist = rc_strlist_add (NULL, applet);
557 rc_strlist_free (services);
558 services = rc_get_depends (deptree, types, svclist, softlevel, 0);
559 if (services) {
560 eerrorn ("ERROR: `%s' needs ", applet);
561 STRLIST_FOREACH (services, svc, i) {
562 if (i > 0)
563 fprintf (stderr, ", ");
564 fprintf (stderr, "%s", svc);
565 }
566 exit (EXIT_FAILURE);
567 }
568 rc_strlist_free (services);
569 services = NULL;
570
571 rc_strlist_free (types);
572 types = rc_strlist_add (NULL, "ineed");
573 rc_strlist_free (need_services);
574 need_services = rc_get_depends (deptree, types, svclist,
575 softlevel, depoptions);
576 types = rc_strlist_add (types, "iuse");
577 if (! rc_runlevel_starting ()) {
578 services = rc_get_depends (deptree, types, svclist,
579 softlevel, depoptions);
580 STRLIST_FOREACH (services, svc, i)
581 if (rc_service_state (svc, rc_service_stopped)) {
582 pid_t pid = rc_start_service (svc);
583 if (! rc_is_env ("RC_PARALLEL", "yes"))
584 rc_waitpid (pid);
585 }
586
587 rc_strlist_free (services);
588 }
589
590 /* Now wait for them to start */
591 types = rc_strlist_add (types, "iafter");
592 services = rc_get_depends (deptree, types, svclist,
593 softlevel, depoptions);
594
595 /* We use tmplist to hold our scheduled by list */
596 rc_strlist_free (tmplist);
597 tmplist = NULL;
598
599 STRLIST_FOREACH (services, svc, i) {
600 if (rc_service_state (svc, rc_service_started))
601 continue;
602 if (! rc_wait_service (svc))
603 eerror ("%s: timed out waiting for %s", applet, svc);
604 if (rc_service_state (svc, rc_service_started))
605 continue;
606
607 STRLIST_FOREACH (need_services, svc2, j)
608 if (strcmp (svc, svc2) == 0) {
609 if (rc_service_state (svc, rc_service_inactive) ||
610 rc_service_state (svc, rc_service_wasinactive))
611 tmplist = rc_strlist_add (tmplist, svc);
612 else
613 eerrorx ("ERROR: cannot start %s as %s would not start",
614 applet, svc);
615 }
616 }
617
618 if (tmplist) {
619 int n = 0;
620 int len = 0;
621 char *p;
622
623 /* Set the state now, then unlink our exclusive so that
624 our scheduled list is preserved */
625 rc_mark_service (service, rc_service_stopped);
626 unlink_mtime_test ();
627
628 rc_strlist_free (types);
629 types = rc_strlist_add (NULL, "iprovide");
630 STRLIST_FOREACH (tmplist, svc, i) {
631 rc_schedule_start_service (svc, service);
632
633 rc_strlist_free (svclist);
634 svclist = rc_strlist_add (NULL, svc);
635 rc_strlist_free (providelist);
636 providelist = rc_get_depends (deptree, types, svclist,
637 softlevel, depoptions);
638 STRLIST_FOREACH (providelist, svc2, j)
639 rc_schedule_start_service (svc2, service);
640
641 len += strlen (svc) + 2;
642 n++;
643 }
644
645 len += 5;
646 tmp = rc_xmalloc (sizeof (char *) * len);
647 p = tmp;
648 STRLIST_FOREACH (tmplist, svc, i) {
649 if (i > 1) {
650 if (i == n - 1)
651 p += snprintf (p, len, " or ");
652 else
653 p += snprintf (p, len, ", ");
654 }
655 p += snprintf (p, len, "%s", svc);
656 }
657 ewarnx ("WARNING: %s is scheduled to start when %s has started",
658 applet, tmp);
659 }
660
661 rc_strlist_free (services);
662 services = NULL;
663 rc_strlist_free (types);
664 types = NULL;
665 rc_strlist_free (svclist);
666 svclist = NULL;
667 }
668
669 if (ibsave)
670 setenv ("IN_BACKGROUND", ibsave, 1);
671 rc_plugin_run (rc_hook_service_start_now, applet);
672 started = svc_exec (service, "start", NULL);
673 if (ibsave)
674 unsetenv ("IN_BACKGROUND");
675
676 if (in_control ()) {
677 if (! started) {
678 if (rc_service_state (service, rc_service_wasinactive))
679 rc_mark_service (service, rc_service_inactive);
680 else {
681 rc_mark_service (service, rc_service_stopped);
682 if (rc_runlevel_starting ())
683 rc_mark_service (service, rc_service_failed);
684 }
685 rc_plugin_run (rc_hook_service_start_done, applet);
686 eerrorx ("ERROR: %s failed to start", applet);
687 }
688 rc_mark_service (service, rc_service_started);
689 unlink_mtime_test ();
690 rc_plugin_run (rc_hook_service_start_done, applet);
691 } else {
692 rc_plugin_run (rc_hook_service_start_done, applet);
693 if (rc_service_state (service, rc_service_inactive))
694 ewarnx ("WARNING: %s has started, but is inactive", applet);
695 else
696 ewarnx ("WARNING: %s not under our control, aborting", applet);
697 }
698
699 /* Now start any scheduled services */
700 rc_strlist_free (services);
701 services = rc_services_scheduled (service);
702 STRLIST_FOREACH (services, svc, i)
703 if (rc_service_state (svc, rc_service_stopped))
704 rc_start_service (svc);
705 rc_strlist_free (services);
706 services = NULL;
707
708 /* Do the same for any services we provide */
709 rc_strlist_free (types);
710 types = rc_strlist_add (NULL, "iprovide");
711 rc_strlist_free (svclist);
712 svclist = rc_strlist_add (NULL, applet);
713 rc_strlist_free (tmplist);
714 tmplist = rc_get_depends (deptree, types, svclist, softlevel, depoptions);
715
716 STRLIST_FOREACH (tmplist, svc2, j) {
717 rc_strlist_free (services);
718 services = rc_services_scheduled (svc2);
719 STRLIST_FOREACH (services, svc, i)
720 if (rc_service_state (svc, rc_service_stopped))
721 rc_start_service (svc);
722 }
723
724 hook_out = 0;
725 rc_plugin_run (rc_hook_service_start_out, applet);
726 }
727
728 static void svc_stop (const char *service, bool deps)
729 {
730 bool stopped;
731
732 hook_out = rc_hook_service_stop_out;
733
734 if (rc_runlevel_stopping () &&
735 rc_service_state (service, rc_service_failed))
736 exit (EXIT_FAILURE);
737
738 if (rc_is_env ("IN_HOTPLUG", "1") || in_background)
739 if (! rc_service_state (service, rc_service_started) &&
740 ! rc_service_state (service, rc_service_inactive))
741 exit (EXIT_FAILURE);
742
743 if (rc_service_state (service, rc_service_stopped))
744 ewarnx ("WARNING: %s is already stopped", applet);
745 else if (rc_service_state (service, rc_service_stopping))
746 ewarnx ("WARNING: %s is already stopping", applet);
747
748 if (! rc_mark_service (service, rc_service_stopping))
749 eerrorx ("ERROR: %s has been stopped by something else", applet);
750
751 make_exclusive (service);
752
753 if (! rc_runlevel_stopping () &&
754 rc_service_in_runlevel (service, RC_LEVEL_BOOT))
755 ewarn ("WARNING: you are stopping a boot service");
756
757 if (deps || ! rc_service_state (service, rc_service_wasinactive)) {
758 int depoptions = RC_DEP_TRACE;
759 char *svc;
760 int i;
761
762 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
763 depoptions |= RC_DEP_STRICT;
764
765 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
766 eerrorx ("failed to load deptree");
767
768 rc_strlist_free (types);
769 types = rc_strlist_add (NULL, "needsme");
770 rc_strlist_free (svclist);
771 svclist = rc_strlist_add (NULL, applet);
772 rc_strlist_free (tmplist);
773 tmplist = NULL;
774 rc_strlist_free (services);
775 services = rc_get_depends (deptree, types, svclist,
776 softlevel, depoptions);
777 rc_strlist_reverse (services);
778 STRLIST_FOREACH (services, svc, i) {
779 if (rc_service_state (svc, rc_service_started) ||
780 rc_service_state (svc, rc_service_inactive))
781 {
782 rc_wait_service (svc);
783 if (rc_service_state (svc, rc_service_started) ||
784 rc_service_state (svc, rc_service_inactive))
785 {
786 pid_t pid = rc_stop_service (svc);
787 if (! rc_is_env ("RC_PARALLEL", "yes"))
788 rc_waitpid (pid);
789 tmplist = rc_strlist_add (tmplist, svc);
790 }
791 }
792 }
793 rc_strlist_free (services);
794 services = NULL;
795
796 STRLIST_FOREACH (tmplist, svc, i) {
797 if (rc_service_state (svc, rc_service_stopped))
798 continue;
799
800 /* We used to loop 3 times here - maybe re-do this if needed */
801 rc_wait_service (svc);
802 if (! rc_service_state (svc, rc_service_stopped)) {
803 if (rc_runlevel_stopping ())
804 rc_mark_service (svc, rc_service_failed);
805 eerrorx ("ERROR: cannot stop %s as %s is still up",
806 applet, svc);
807 }
808 }
809 rc_strlist_free (tmplist);
810 tmplist = NULL;
811
812 /* We now wait for other services that may use us and are stopping
813 This is important when a runlevel stops */
814 types = rc_strlist_add (types, "usesme");
815 types = rc_strlist_add (types, "ibefore");
816 services = rc_get_depends (deptree, types, svclist,
817 softlevel, depoptions);
818 STRLIST_FOREACH (services, svc, i) {
819 if (rc_service_state (svc, rc_service_stopped))
820 continue;
821 rc_wait_service (svc);
822 }
823
824 rc_strlist_free (services);
825 services = NULL;
826 rc_strlist_free (types);
827 types = NULL;
828 }
829
830 if (ibsave)
831 setenv ("IN_BACKGROUND", ibsave, 1);
832 rc_plugin_run (rc_hook_service_stop_now, applet);
833 stopped = svc_exec (service, "stop", NULL);
834 if (ibsave)
835 unsetenv ("IN_BACKGROUND");
836
837 if (! in_control ()) {
838 rc_plugin_run (rc_hook_service_stop_done, applet);
839 ewarnx ("WARNING: %s not under our control, aborting", applet);
840 }
841
842 if (! stopped) {
843 if (rc_service_state (service, rc_service_wasinactive))
844 rc_mark_service (service, rc_service_inactive);
845 else
846 rc_mark_service (service, rc_service_started);
847 rc_plugin_run (rc_hook_service_stop_done, applet);
848 eerrorx ("ERROR: %s failed to stop", applet);
849 }
850
851 if (in_background)
852 rc_mark_service (service, rc_service_inactive);
853 else
854 rc_mark_service (service, rc_service_stopped);
855
856 unlink_mtime_test ();
857 rc_plugin_run (rc_hook_service_stop_done, applet);
858 hook_out = 0;
859 rc_plugin_run (rc_hook_service_stop_out, applet);
860 }
861
862 static void svc_restart (const char *service, bool deps)
863 {
864 char *svc;
865 int i;
866 bool inactive = false;
867
868 /* This is hairly and a better way needs to be found I think!
869 The issue is this - openvpn need net and dns. net can restart
870 dns via resolvconf, so you could have openvpn trying to restart dnsmasq
871 which in turn is waiting on net which in turn is waiting on dnsmasq.
872 The work around is for resolvconf to restart it's services with --nodeps
873 which means just that. The downside is that there is a small window when
874 our status is invalid.
875 One workaround would be to introduce a new status, or status locking. */
876 if (! deps) {
877 if (rc_service_state (service, rc_service_started) ||
878 rc_service_state (service, rc_service_inactive))
879 svc_exec (service, "stop", "start");
880 else
881 svc_exec (service, "start", NULL);
882 return;
883 }
884
885 if (! rc_service_state (service, rc_service_stopped)) {
886 get_started_services ();
887 svc_stop (service, deps);
888
889 /* Flush our buffered output if any */
890 eflush ();
891 }
892
893 svc_start (service, deps);
894
895 inactive = rc_service_state (service, rc_service_inactive);
896 if (! inactive)
897 inactive = rc_service_state (service, rc_service_wasinactive);
898
899 if (inactive ||
900 rc_service_state (service, rc_service_starting) ||
901 rc_service_state (service, rc_service_started))
902 {
903 STRLIST_FOREACH (restart_services, svc, i) {
904 if (rc_service_state (svc, rc_service_stopped)) {
905 if (inactive) {
906 rc_schedule_start_service (service, svc);
907 ewarn ("WARNING: %s is scheduled to started when %s has started",
908 svc, applet);
909 } else
910 rc_start_service (svc);
911 }
912 }
913 }
914 }
915
916 #define getoptstring "dCDNqvh"
917 static struct option longopts[] = {
918 { "debug", 0, NULL, 'd'},
919 { "nocolor", 0, NULL, 'C'},
920 { "nocolour", 0, NULL, 'C'},
921 { "nodeps", 0, NULL, 'D'},
922 { "quiet", 0, NULL, 'q'},
923 { "verbose", 0, NULL, 'v'},
924 { "help", 0, NULL, 'h'},
925 { NULL, 0, NULL, 0}
926 };
927 // #include "_usage.c"
928
929 int main (int argc, char **argv)
930 {
931 char *service = argv[1];
932 int i;
933 bool deps = true;
934 bool doneone = false;
935 char pid[16];
936 int retval;
937 char c;
938
939 /* Show help if insufficient args */
940 if (argc < 3) {
941 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
942 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
943 applet, strerror (errno));
944 }
945
946 applet = rc_xstrdup (basename (service));
947 atexit (cleanup);
948
949 #ifdef __linux__
950 /* coldplug events can trigger init scripts, but we don't want to run them
951 until after rc sysinit has completed so we punt them to the boot runlevel */
952 if (rc_exists ("/dev/.rcsysinit")) {
953 eerror ("%s: cannot run until sysvinit completes", applet);
954 if (mkdir ("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
955 eerrorx ("%s: mkdir `/dev/.rcboot': %s", applet, strerror (errno));
956 tmp = rc_strcatpaths ("/dev/.rcboot", applet, (char *) NULL);
957 symlink (service, tmp);
958 exit (EXIT_FAILURE);
959 }
960 #endif
961
962 if ((softlevel = getenv ("RC_SOFTLEVEL")) == NULL) {
963 /* Ensure our environment is pure
964 Also, add our configuration to it */
965 env = rc_filter_env ();
966 env = rc_config_env (env);
967
968 if (env) {
969 char *p;
970
971 #ifdef __linux__
972 /* clearenv isn't portable, but there's no harm in using it
973 if we have it */
974 clearenv ();
975 #else
976 char *var;
977 /* No clearenv present here then.
978 We could manipulate environ directly ourselves, but it seems that
979 some kernels bitch about this according to the environ man pages
980 so we walk though environ and call unsetenv for each value. */
981 while (environ[0]) {
982 tmp = rc_xstrdup (environ[0]);
983 p = tmp;
984 var = strsep (&p, "=");
985 unsetenv (var);
986 free (tmp);
987 }
988 tmp = NULL;
989 #endif
990
991 STRLIST_FOREACH (env, p, i)
992 putenv (p);
993
994 /* We don't free our list as that would be null in environ */
995 }
996
997 softlevel = rc_get_runlevel ();
998
999 /* If not called from RC or another service then don't be parallel */
1000 unsetenv ("RC_PARALLEL");
1001 }
1002
1003 setenv ("RC_ELOG", service, 1);
1004 setenv ("SVCNAME", applet, 1);
1005
1006 /* Set an env var so that we always know our pid regardless of any
1007 subshells the init script may create so that our mark_service_*
1008 functions can always instruct us of this change */
1009 snprintf (pid, sizeof (pid), "%d", (int) getpid ());
1010 setenv ("RC_RUNSCRIPT_PID", pid, 1);
1011
1012 /* eprefix is kinda klunky, but it works for our purposes */
1013 if (rc_is_env ("RC_PREFIX", "yes")) {
1014 int l = strlen (applet);
1015 if (l < 13)
1016 l = 13;
1017 prefix = rc_xmalloc (sizeof (char *) * l);
1018 snprintf (prefix, l, "%s%s", applet, " ");
1019 eprefix (prefix);
1020 }
1021
1022 /* If we're in parallel and we're not prefixing then we need the ebuffer */
1023 if (rc_is_env ("RC_PARALLEL", "yes") && ! rc_is_env ("RC_PREFIX", "yes")) {
1024 char ebname[PATH_MAX];
1025 char *eb;
1026
1027 snprintf (ebname, sizeof (ebname), "%s.%s", applet, pid);
1028 eb = rc_strcatpaths (RC_SVCDIR "ebuffer", ebname, (char *) NULL);
1029 ebuffer (eb);
1030 free (eb);
1031 }
1032
1033 #ifdef __linux__
1034 /* Ok, we are ready to go, so setup selinux if applicable */
1035 setup_selinux (argc, argv);
1036 #endif
1037
1038 /* Punt the first arg as it's our service name */
1039 argc--;
1040 argv++;
1041
1042 /* Right then, parse any options there may be */
1043 while ((c = getopt_long (argc, argv, getoptstring,
1044 longopts, (int *) 0)) != -1)
1045 switch (c) {
1046 case 'd':
1047 setenv ("RC_DEBUG", "yes", 1);
1048 break;
1049 case 'h':
1050 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1051 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1052 applet, strerror (errno));
1053 case 'C':
1054 setenv ("RC_NOCOLOR", "yes", 1);
1055 break;
1056 case 'D':
1057 deps = false;
1058 break;
1059 case 'q':
1060 setenv ("RC_QUIET", "yes", 1);
1061 break;
1062 case 'v':
1063 setenv ("RC_VERBOSE", "yes", 1);
1064 break;
1065 default:
1066 exit (EXIT_FAILURE);
1067 }
1068
1069 /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service
1070 that is being called and not any dependents */
1071 if (getenv ("IN_BACKGROUND")) {
1072 in_background = rc_is_env ("IN_BACKGROUND", "true");
1073 ibsave = rc_xstrdup (getenv ("IN_BACKGROUND"));
1074 unsetenv ("IN_BACKGROUND");
1075 }
1076
1077 if (rc_is_env ("IN_HOTPLUG", "1")) {
1078 if (! rc_is_env ("RC_HOTPLUG", "yes") || ! rc_allow_plug (applet))
1079 eerrorx ("%s: not allowed to be hotplugged", applet);
1080 }
1081
1082 /* Setup a signal handler */
1083 signal (SIGHUP, handle_signal);
1084 signal (SIGINT, handle_signal);
1085 signal (SIGQUIT, handle_signal);
1086 signal (SIGTERM, handle_signal);
1087 signal (SIGCHLD, handle_signal);
1088
1089 /* Load our plugins */
1090 rc_plugin_load ();
1091
1092 /* Now run each option */
1093 retval = EXIT_SUCCESS;
1094 while (optind < argc) {
1095 optarg = argv[optind++];
1096
1097 /* Abort on a sighup here */
1098 if (sighup)
1099 exit (EXIT_FAILURE);
1100
1101 /* Export the command we're running.
1102 This is important as we stamp on the restart function now but
1103 some start/stop routines still need to behave differently if
1104 restarting. */
1105 unsetenv ("RC_CMD");
1106 setenv ("RC_CMD", optarg, 1);
1107
1108 doneone = true;
1109 if (strcmp (optarg, "conditionalrestart") == 0 ||
1110 strcmp (optarg, "condrestart") == 0)
1111 {
1112 if (rc_service_state (service, rc_service_started))
1113 svc_restart (service, deps);
1114 }
1115 else if (strcmp (optarg, "restart") == 0)
1116 svc_restart (service, deps);
1117 else if (strcmp (optarg, "start") == 0)
1118 svc_start (service, deps);
1119 else if (strcmp (optarg, "status") == 0) {
1120 rc_service_state_t r = svc_status (service);
1121 retval = (int) r;
1122 } else if (strcmp (optarg, "stop") == 0) {
1123 if (in_background)
1124 get_started_services ();
1125
1126 svc_stop (service, deps);
1127
1128 if (! in_background &&
1129 ! rc_runlevel_stopping () &&
1130 rc_service_state (service, rc_service_stopped))
1131 uncoldplug (applet);
1132
1133 if (in_background &&
1134 rc_service_state (service, rc_service_inactive))
1135 {
1136 char *svc;
1137 int j;
1138 STRLIST_FOREACH (restart_services, svc, j)
1139 if (rc_service_state (svc, rc_service_stopped))
1140 rc_schedule_start_service (service, svc);
1141 }
1142 } else if (strcmp (optarg, "zap") == 0) {
1143 einfo ("Manually resetting %s to stopped state", applet);
1144 rc_mark_service (applet, rc_service_stopped);
1145 uncoldplug (applet);
1146 } else if (strcmp (optarg, "help") == 0) {
1147 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", (char *) NULL);
1148 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1149 applet, strerror (errno));
1150 }else
1151 svc_exec (service, optarg, NULL);
1152
1153 /* Flush our buffered output if any */
1154 eflush ();
1155
1156 /* We should ensure this list is empty after an action is done */
1157 rc_strlist_free (restart_services);
1158 restart_services = NULL;
1159 }
1160
1161 if (! doneone) {
1162 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1163 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1164 applet, strerror (errno));
1165 }
1166
1167 return (retval);
1168 }

  ViewVC Help
Powered by ViewVC 1.1.20