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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20