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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20