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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20