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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 3007 - (show annotations) (download) (as text)
Mon Oct 8 11:07:39 2007 UTC (6 years, 11 months ago) by uberlord
File MIME type: text/x-csrc
File size: 33983 byte(s)
Move rc_env_filter and rc_env_config out of librc and into rc
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 int wait_pid (pid_t pid)
335 {
336 int status = 0;
337 pid_t savedpid = pid;
338 int retval = -1;
339
340 errno = 0;
341 while ((pid = waitpid (savedpid, &status, 0)) > 0) {
342 if (pid == savedpid)
343 retval = WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE;
344 }
345
346 return (retval);
347 }
348
349 static bool svc_exec (const char *arg1, const char *arg2)
350 {
351 bool execok;
352 int fdout = fileno (stdout);
353 struct termios tt;
354 struct winsize ws;
355 int i;
356 int flags;
357 fd_set rset;
358 int s;
359 char buffer[RC_LINEBUFFER];
360 size_t bytes;
361 bool prefixed = false;
362 int selfd;
363 int slave_tty;
364
365 /* Setup our signal pipe */
366 if (pipe (signal_pipe) == -1)
367 eerrorx ("%s: pipe: %s", service, applet);
368 for (i = 0; i < 2; i++)
369 if ((flags = fcntl (signal_pipe[i], F_GETFD, 0) == -1 ||
370 fcntl (signal_pipe[i], F_SETFD, flags | FD_CLOEXEC) == -1))
371 eerrorx ("%s: fcntl: %s", service, strerror (errno));
372
373 /* Open a pty for our prefixed output
374 * We do this instead of mapping pipes to stdout, stderr so that
375 * programs can tell if they're attached to a tty or not.
376 * The only loss is that we can no longer tell the difference
377 * between the childs stdout or stderr */
378 master_tty = slave_tty = -1;
379 if (prefix && isatty (fdout)) {
380 tcgetattr (fdout, &tt);
381 ioctl (fdout, TIOCGWINSZ, &ws);
382
383 /* If the below call fails due to not enough ptys then we don't
384 * prefix the output, but we still work */
385 openpty (&master_tty, &slave_tty, NULL, &tt, &ws);
386 }
387
388 service_pid = vfork();
389 if (service_pid == -1)
390 eerrorx ("%s: vfork: %s", service, strerror (errno));
391 if (service_pid == 0) {
392 if (slave_tty >= 0) {
393 /* Hmmm, this shouldn't work in a vfork, but it does which is
394 * good for us */
395 close (master_tty);
396
397 dup2 (fileno (stdin), 0);
398 dup2 (slave_tty, 1);
399 dup2 (slave_tty, 2);
400 if (slave_tty > 2)
401 close (slave_tty);
402 }
403
404 if (rc_exists (RC_SVCDIR "/runscript.sh")) {
405 execl (RC_SVCDIR "/runscript.sh", service, service, arg1, arg2,
406 (char *) NULL);
407 eerror ("%s: exec `" RC_SVCDIR "/runscript.sh': %s",
408 service, strerror (errno));
409 _exit (EXIT_FAILURE);
410 } else {
411 execl (RC_LIBDIR "/sh/runscript.sh", service, service, arg1, arg2,
412 (char *) NULL);
413 eerror ("%s: exec `" RC_LIBDIR "/sh/runscript.sh': %s",
414 service, strerror (errno));
415 _exit (EXIT_FAILURE);
416 }
417 }
418
419 selfd = MAX (master_tty, signal_pipe[0]) + 1;
420 while (1) {
421 FD_ZERO (&rset);
422 FD_SET (signal_pipe[0], &rset);
423 if (master_tty >= 0)
424 FD_SET (master_tty, &rset);
425
426 if ((s = select (selfd, &rset, NULL, NULL, NULL)) == -1) {
427 if (errno != EINTR) {
428 eerror ("%s: select: %s", service, strerror (errno));
429 break;
430 }
431 }
432
433 if (s > 0) {
434 /* Only SIGCHLD signals come down this pipe */
435 if (FD_ISSET (signal_pipe[0], &rset))
436 break;
437
438 if (master_tty >= 0 && FD_ISSET (master_tty, &rset)) {
439 bytes = read (master_tty, buffer, sizeof (buffer));
440 write_prefix (buffer, bytes, &prefixed);
441 }
442 }
443 }
444
445 close (signal_pipe[0]);
446 close (signal_pipe[1]);
447 signal_pipe[0] = signal_pipe[1] = -1;
448
449 if (master_tty >= 0) {
450 signal (SIGWINCH, SIG_IGN);
451 close (master_tty);
452 master_tty = -1;
453 }
454
455 execok = wait_pid (service_pid) == 0 ? true : false;
456 service_pid = 0;
457
458 return (execok);
459 }
460
461 static rc_service_state_t svc_status ()
462 {
463 char status[10];
464 int (*e) (const char *fmt, ...) = &einfo;
465
466 rc_service_state_t state = rc_service_state (service);
467
468 if (state & RC_SERVICE_STOPPING) {
469 snprintf (status, sizeof (status), "stopping");
470 e = &ewarn;
471 } else if (state & RC_SERVICE_STOPPING) {
472 snprintf (status, sizeof (status), "starting");
473 e = &ewarn;
474 } else if (state & RC_SERVICE_INACTIVE) {
475 snprintf (status, sizeof (status), "inactive");
476 e = &ewarn;
477 } else if (state & RC_SERVICE_STARTED) {
478 if (geteuid () == 0 && rc_service_daemons_crashed (service)) {
479 snprintf (status, sizeof (status), "crashed");
480 e = &eerror;
481 } else
482 snprintf (status, sizeof (status), "started");
483 } else
484 snprintf (status, sizeof (status), "stopped");
485
486 e ("status: %s", status);
487 return (state);
488 }
489
490 static void make_exclusive ()
491 {
492 char *path;
493 int i;
494
495 /* We create a fifo so that other services can wait until we complete */
496 if (! exclusive)
497 exclusive = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
498
499 if (mkfifo (exclusive, 0600) != 0 && errno != EEXIST &&
500 (errno != EACCES || geteuid () == 0))
501 eerrorx ("%s: unable to create fifo `%s': %s",
502 applet, exclusive, strerror (errno));
503
504 path = rc_strcatpaths (RC_SVCDIR, "exclusive", applet, (char *) NULL);
505 i = strlen (path) + 16;
506 mtime_test = rc_xmalloc (sizeof (char *) * i);
507 snprintf (mtime_test, i, "%s.%d", path, getpid ());
508 free (path);
509
510 if (rc_exists (mtime_test) && unlink (mtime_test) != 0) {
511 eerror ("%s: unlink `%s': %s",
512 applet, mtime_test, strerror (errno));
513 free (mtime_test);
514 mtime_test = NULL;
515 return;
516 }
517
518 if (symlink (service, mtime_test) != 0) {
519 eerror ("%s: symlink `%s' to `%s': %s",
520 applet, service, mtime_test, strerror (errno));
521 free (mtime_test);
522 mtime_test = NULL;
523 }
524 }
525
526 static void unlink_mtime_test ()
527 {
528 if (unlink (mtime_test) != 0)
529 eerror ("%s: unlink `%s': %s", applet, mtime_test, strerror (errno));
530 free (mtime_test);
531 mtime_test = NULL;
532 }
533
534 static void get_started_services ()
535 {
536 char *svc;
537 int i;
538
539 rc_strlist_free (tmplist);
540 tmplist = rc_services_in_state (RC_SERVICE_INACTIVE);
541
542 rc_strlist_free (restart_services);
543 restart_services = rc_services_in_state (RC_SERVICE_STARTED);
544
545 STRLIST_FOREACH (tmplist, svc, i)
546 rc_strlist_addsort (&restart_services, svc);
547
548 rc_strlist_free (tmplist);
549 tmplist = NULL;
550 }
551
552 static void svc_start (bool deps)
553 {
554 bool started;
555 bool background = false;
556 char *svc;
557 char *svc2;
558 int i;
559 int j;
560 int depoptions = RC_DEP_TRACE;
561 rc_service_state_t state;
562
563 rc_plugin_run (RC_HOOK_SERVICE_START_IN, applet);
564 hook_out = RC_HOOK_SERVICE_START_OUT;
565 state = rc_service_state (service);
566
567 if (rc_env_bool ("IN_HOTPLUG") || in_background) {
568 if (! state & RC_SERVICE_INACTIVE &&
569 ! state & RC_SERVICE_STOPPED)
570 exit (EXIT_FAILURE);
571 background = true;
572 }
573
574 if (state & RC_SERVICE_STARTED) {
575 ewarn ("WARNING: %s has already been started", applet);
576 return;
577 } else if (state & RC_SERVICE_STOPPING)
578 ewarnx ("WARNING: %s is already starting", applet);
579 else if (state & RC_SERVICE_STOPPING)
580 ewarnx ("WARNING: %s is stopping", applet);
581 else if (state & RC_SERVICE_INACTIVE && ! background)
582 ewarnx ("WARNING: %s has already started, but is inactive", applet);
583
584 if (! rc_service_mark (service, RC_SERVICE_STOPPING))
585 eerrorx ("ERROR: %s has been started by something else", applet);
586
587 make_exclusive (service);
588
589 if (rc_env_bool ("RC_DEPEND_STRICT"))
590 depoptions |= RC_DEP_STRICT;
591
592 if (rc_runlevel_starting ())
593 depoptions |= RC_DEP_START;
594
595 if (deps) {
596 if (! deptree && ((deptree = _rc_deptree_load ()) == NULL))
597 eerrorx ("failed to load deptree");
598
599 rc_strlist_free (types);
600 types = NULL;
601 rc_strlist_add (&types, "broken");
602 rc_strlist_free (svclist);
603 svclist = NULL;
604 rc_strlist_add (&svclist, applet);
605 rc_strlist_free (services);
606 services = rc_deptree_depends (deptree, types, svclist, softlevel, 0);
607 if (services) {
608 eerrorn ("ERROR: `%s' needs ", applet);
609 STRLIST_FOREACH (services, svc, i) {
610 if (i > 0)
611 fprintf (stderr, ", ");
612 fprintf (stderr, "%s", svc);
613 }
614 exit (EXIT_FAILURE);
615 }
616 rc_strlist_free (services);
617 services = NULL;
618
619 rc_strlist_free (types);
620 types = NULL;
621 rc_strlist_add (&types, "ineed");
622 rc_strlist_free (need_services);
623 need_services = rc_deptree_depends (deptree, types, svclist,
624 softlevel, depoptions);
625
626 rc_strlist_add (&types, "iuse");
627 rc_strlist_free (use_services);
628 use_services = rc_deptree_depends (deptree, types, svclist,
629 softlevel, depoptions);
630
631 if (! rc_runlevel_starting ()) {
632 STRLIST_FOREACH (use_services, svc, i)
633 if (rc_service_state (svc) & RC_SERVICE_STOPPED) {
634 pid_t pid = rc_service_start (svc);
635 if (! rc_env_bool ("RC_PARALLEL"))
636 wait_pid (pid);
637 }
638 }
639
640 /* Now wait for them to start */
641 rc_strlist_add (&types, "iafter");
642 services = rc_deptree_depends (deptree, types, svclist,
643 softlevel, depoptions);
644
645 /* We use tmplist to hold our scheduled by list */
646 rc_strlist_free (tmplist);
647 tmplist = NULL;
648
649 STRLIST_FOREACH (services, svc, i) {
650 rc_service_state_t svcs = rc_service_state (svc);
651 if (svcs & RC_SERVICE_STARTED)
652 continue;
653
654 /* Don't wait for services which went inactive but are now in
655 * starting state which we are after */
656 if (svcs & RC_SERVICE_STOPPING &&
657 svcs & RC_SERVICE_WASINACTIVE) {
658 bool use = false;
659 STRLIST_FOREACH (use_services, svc2, j)
660 if (strcmp (svc, svc2) == 0) {
661 use = true;
662 break;
663 }
664 if (! use)
665 continue;
666 }
667
668 if (! rc_service_wait (svc))
669 eerror ("%s: timed out waiting for %s", applet, svc);
670 if ((svcs = rc_service_state (svc)) & RC_SERVICE_STARTED)
671 continue;
672
673 STRLIST_FOREACH (need_services, svc2, j)
674 if (strcmp (svc, svc2) == 0) {
675 if (svcs & RC_SERVICE_INACTIVE ||
676 svcs & RC_SERVICE_WASINACTIVE)
677 rc_strlist_add (&tmplist, svc);
678 else
679 eerrorx ("ERROR: cannot start %s as %s would not start",
680 applet, svc);
681 }
682 }
683
684 if (tmplist) {
685 int n = 0;
686 int len = 0;
687 char *p;
688
689 /* Set the state now, then unlink our exclusive so that
690 our scheduled list is preserved */
691 rc_service_mark (service, RC_SERVICE_STOPPED);
692 unlink_mtime_test ();
693
694 rc_strlist_free (types);
695 types = NULL;
696 rc_strlist_add (&types, "iprovide");
697 STRLIST_FOREACH (tmplist, svc, i) {
698 rc_service_schedule_start (svc, service);
699
700 rc_strlist_free (svclist);
701 svclist = NULL;
702 rc_strlist_add (&svclist, svc);
703 rc_strlist_free (providelist);
704 providelist = rc_deptree_depends (deptree, types, svclist,
705 softlevel, depoptions);
706 STRLIST_FOREACH (providelist, svc2, j)
707 rc_service_schedule_start (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_service_mark (service, RC_SERVICE_INACTIVE);
748 else {
749 rc_service_mark (service, RC_SERVICE_STOPPED);
750 if (rc_runlevel_starting ())
751 rc_service_mark (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_service_mark (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_service_start (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 = NULL;
779 rc_strlist_add (&types, "iprovide");
780 rc_strlist_free (svclist);
781 svclist = NULL;
782 rc_strlist_add (&svclist, applet);
783 rc_strlist_free (tmplist);
784 tmplist = rc_deptree_depends (deptree, types, svclist, softlevel, depoptions);
785
786 STRLIST_FOREACH (tmplist, svc2, j) {
787 rc_strlist_free (services);
788 services = rc_services_scheduled (svc2);
789 STRLIST_FOREACH (services, svc, i)
790 if (rc_service_state (svc) & RC_SERVICE_STOPPED)
791 rc_service_start (svc);
792 }
793
794 hook_out = 0;
795 rc_plugin_run (RC_HOOK_SERVICE_START_OUT, applet);
796 }
797
798 static void svc_stop (bool deps)
799 {
800 bool stopped;
801 rc_service_state_t state = rc_service_state (service);
802
803 hook_out = RC_HOOK_SERVICE_STOP_OUT;
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 ewarn ("WARNING: %s is already stopped", applet);
816 return;
817 } else if (state & RC_SERVICE_STOPPING)
818 ewarnx ("WARNING: %s is already stopping", applet);
819
820 if (! rc_service_mark (service, RC_SERVICE_STOPPING))
821 eerrorx ("ERROR: %s has been stopped by something else", applet);
822
823 make_exclusive (service);
824
825 if (! rc_runlevel_stopping () &&
826 rc_service_in_runlevel (service, RC_LEVEL_BOOT))
827 ewarn ("WARNING: you are stopping a boot service");
828
829 if (deps && ! (state & RC_SERVICE_WASINACTIVE)) {
830 int depoptions = RC_DEP_TRACE;
831 char *svc;
832 int i;
833
834 if (rc_env_bool ("RC_DEPEND_STRICT"))
835 depoptions |= RC_DEP_STRICT;
836
837 if (rc_runlevel_stopping ())
838 depoptions |= RC_DEP_STOP;
839
840 if (! deptree && ((deptree = _rc_deptree_load ()) == NULL))
841 eerrorx ("failed to load deptree");
842
843 rc_strlist_free (types);
844 types = NULL;
845 rc_strlist_add (&types, "needsme");
846 rc_strlist_free (svclist);
847 svclist = NULL;
848 rc_strlist_add (&svclist, applet);
849 rc_strlist_free (tmplist);
850 tmplist = NULL;
851 rc_strlist_free (services);
852 services = rc_deptree_depends (deptree, types, svclist,
853 softlevel, depoptions);
854 rc_strlist_reverse (services);
855 STRLIST_FOREACH (services, svc, i) {
856 rc_service_state_t svcs = rc_service_state (svc);
857 if (svcs & RC_SERVICE_STARTED ||
858 svcs & RC_SERVICE_INACTIVE)
859 {
860 rc_service_wait (svc);
861 svcs = rc_service_state (svc);
862 if (svcs & RC_SERVICE_STARTED ||
863 svcs & RC_SERVICE_INACTIVE)
864 {
865 pid_t pid = rc_service_stop (svc);
866 if (! rc_env_bool ("RC_PARALLEL"))
867 wait_pid (pid);
868 rc_strlist_add (&tmplist, svc);
869 }
870 }
871 }
872 rc_strlist_free (services);
873 services = NULL;
874
875 STRLIST_FOREACH (tmplist, svc, i) {
876 if (rc_service_state (svc) & RC_SERVICE_STOPPED)
877 continue;
878
879 /* We used to loop 3 times here - maybe re-do this if needed */
880 rc_service_wait (svc);
881 if (! (rc_service_state (svc) & RC_SERVICE_STOPPED)) {
882 if (rc_runlevel_stopping ()) {
883 /* If shutting down, we should stop even if a dependant failed */
884 if (softlevel &&
885 (strcmp (softlevel, RC_LEVEL_SHUTDOWN) == 0 ||
886 strcmp (softlevel, RC_LEVEL_REBOOT) == 0 ||
887 strcmp (softlevel, RC_LEVEL_SINGLE) == 0))
888 continue;
889 rc_service_mark (service, RC_SERVICE_FAILED);
890 }
891
892 eerrorx ("ERROR: cannot stop %s as %s is still up",
893 applet, svc);
894 }
895 }
896 rc_strlist_free (tmplist);
897 tmplist = NULL;
898
899 /* We now wait for other services that may use us and are stopping
900 This is important when a runlevel stops */
901 rc_strlist_add (&types, "usesme");
902 rc_strlist_add (&types, "ibefore");
903 services = rc_deptree_depends (deptree, types, svclist,
904 softlevel, depoptions);
905 STRLIST_FOREACH (services, svc, i) {
906 if (rc_service_state (svc) & RC_SERVICE_STOPPED)
907 continue;
908 rc_service_wait (svc);
909 }
910
911 rc_strlist_free (services);
912 services = NULL;
913 rc_strlist_free (types);
914 types = NULL;
915 }
916
917 if (ibsave)
918 setenv ("IN_BACKGROUND", ibsave, 1);
919 rc_plugin_run (RC_HOOK_SERVICE_STOP_NOW, applet);
920 stopped = svc_exec ("stop", NULL);
921 if (ibsave)
922 unsetenv ("IN_BACKGROUND");
923
924 if (! in_control ()) {
925 rc_plugin_run (RC_HOOK_SERVICE_STOP_DONE, applet);
926 ewarnx ("WARNING: %s not under our control, aborting", applet);
927 }
928
929 if (! stopped) {
930 if (rc_service_state (service) & RC_SERVICE_WASINACTIVE)
931 rc_service_mark (service, RC_SERVICE_INACTIVE);
932 else
933 rc_service_mark (service, RC_SERVICE_STARTED);
934 rc_plugin_run (RC_HOOK_SERVICE_STOP_DONE, applet);
935 eerrorx ("ERROR: %s failed to stop", applet);
936 }
937
938 if (in_background)
939 rc_service_mark (service, RC_SERVICE_INACTIVE);
940 else
941 rc_service_mark (service, RC_SERVICE_STOPPED);
942
943 unlink_mtime_test ();
944 rc_plugin_run (RC_HOOK_SERVICE_STOP_DONE, applet);
945 hook_out = 0;
946 rc_plugin_run (RC_HOOK_SERVICE_STOP_OUT, applet);
947 }
948
949 static void svc_restart (bool deps)
950 {
951 /* This is hairly and a better way needs to be found I think!
952 The issue is this - openvpn need net and dns. net can restart
953 dns via resolvconf, so you could have openvpn trying to restart dnsmasq
954 which in turn is waiting on net which in turn is waiting on dnsmasq.
955 The work around is for resolvconf to restart it's services with --nodeps
956 which means just that. The downside is that there is a small window when
957 our status is invalid.
958 One workaround would be to introduce a new status, or status locking. */
959 if (! deps) {
960 rc_service_state_t state = rc_service_state (service);
961 if (state & RC_SERVICE_STARTED || state & RC_SERVICE_INACTIVE)
962 svc_exec ("stop", "start");
963 else
964 svc_exec ("start", NULL);
965 return;
966 }
967
968 if (! (rc_service_state (service) & RC_SERVICE_STOPPED)) {
969 get_started_services ();
970 svc_stop (deps);
971 }
972
973 svc_start (deps);
974 start_services (restart_services);
975 rc_strlist_free (restart_services);
976 restart_services = NULL;
977 }
978
979 #include "_usage.h"
980 #define getoptstring "dDsv" getoptstring_COMMON
981 static struct option longopts[] = {
982 { "debug", 0, NULL, 'd'},
983 { "ifstarted", 0, NULL, 's'},
984 { "nodeps", 0, NULL, 'D'},
985 longopts_COMMON
986 { NULL, 0, NULL, 0}
987 };
988 static const char * const longopts_help[] = {
989 "",
990 "",
991 "",
992 longopts_help_COMMON
993 };
994 #undef case_RC_COMMON_getopt_case_h
995 #define case_RC_COMMON_getopt_case_h \
996 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL); \
997 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s", applet, strerror (errno));
998 #include "_usage.c"
999
1000 int runscript (int argc, char **argv)
1001 {
1002 int i;
1003 bool deps = true;
1004 bool doneone = false;
1005 char pid[16];
1006 int retval;
1007 int opt;
1008 char *svc;
1009
1010 /* We need the full path to the service */
1011 if (*argv[1] == '/')
1012 service = rc_xstrdup (argv[1]);
1013 else {
1014 char pwd[PATH_MAX];
1015 if (! getcwd (pwd, PATH_MAX))
1016 eerrorx ("getcwd: %s", strerror (errno));
1017 service = rc_strcatpaths (pwd, argv[1], (char *) NULL);
1018 }
1019
1020 applet = rc_xstrdup (basename (service));
1021 atexit (cleanup);
1022
1023 /* Change dir to / to ensure all init scripts don't use stuff in pwd */
1024 chdir ("/");
1025
1026 /* Show help if insufficient args */
1027 if (argc < 3) {
1028 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1029 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1030 applet, strerror (errno));
1031 }
1032
1033 #ifdef __linux__
1034 /* coldplug events can trigger init scripts, but we don't want to run them
1035 until after rc sysinit has completed so we punt them to the boot runlevel */
1036 if (rc_exists ("/dev/.rcsysinit")) {
1037 eerror ("%s: cannot run until sysvinit completes", applet);
1038 if (mkdir ("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
1039 eerrorx ("%s: mkdir `/dev/.rcboot': %s", applet, strerror (errno));
1040 tmp = rc_strcatpaths ("/dev/.rcboot", applet, (char *) NULL);
1041 symlink (service, tmp);
1042 exit (EXIT_FAILURE);
1043 }
1044 #endif
1045
1046 if ((softlevel = rc_xstrdup (getenv ("RC_SOFTLEVEL"))) == NULL) {
1047 /* Ensure our environment is pure
1048 Also, add our configuration to it */
1049 tmplist = env_config ();
1050 env = env_filter ();
1051 rc_strlist_join (&env, tmplist);
1052 rc_strlist_free (tmplist);
1053 tmplist = NULL;
1054
1055 if (env) {
1056 char *p;
1057
1058 #ifdef __linux__
1059 /* clearenv isn't portable, but there's no harm in using it
1060 if we have it */
1061 clearenv ();
1062 #else
1063 char *var;
1064 /* No clearenv present here then.
1065 We could manipulate environ directly ourselves, but it seems that
1066 some kernels bitch about this according to the environ man pages
1067 so we walk though environ and call unsetenv for each value. */
1068 while (environ[0]) {
1069 tmp = rc_xstrdup (environ[0]);
1070 p = tmp;
1071 var = strsep (&p, "=");
1072 unsetenv (var);
1073 free (tmp);
1074 }
1075 tmp = NULL;
1076 #endif
1077
1078 STRLIST_FOREACH (env, p, i)
1079 putenv (p);
1080 /* We don't free our list as that would be null in environ */
1081 }
1082
1083 softlevel = rc_runlevel_get ();
1084 }
1085
1086 setenv ("RC_ELOG", service, 1);
1087 setenv ("SVCNAME", applet, 1);
1088
1089 /* Set an env var so that we always know our pid regardless of any
1090 subshells the init script may create so that our mark_service_*
1091 functions can always instruct us of this change */
1092 snprintf (pid, sizeof (pid), "%d", (int) getpid ());
1093 setenv ("RC_RUNSCRIPT_PID", pid, 1);
1094
1095 /* eprefix is kinda klunky, but it works for our purposes */
1096 if (rc_env_bool ("RC_PARALLEL")) {
1097 int l = 0;
1098 int ll;
1099
1100 /* Get the longest service name */
1101 services = rc_services_in_runlevel (NULL);
1102 STRLIST_FOREACH (services, svc, i) {
1103 ll = strlen (svc);
1104 if (ll > l)
1105 l = ll;
1106 }
1107
1108 /* Make our prefix string */
1109 prefix = rc_xmalloc (sizeof (char *) * l);
1110 ll = strlen (applet);
1111 memcpy (prefix, applet, ll);
1112 memset (prefix + ll, ' ', l - ll);
1113 memset (prefix + l, 0, 1);
1114 eprefix (prefix);
1115 }
1116
1117 #ifdef __linux__
1118 /* Ok, we are ready to go, so setup selinux if applicable */
1119 setup_selinux (argc, argv);
1120 #endif
1121
1122 /* Punt the first arg as it's our service name */
1123 argc--;
1124 argv++;
1125
1126 /* Right then, parse any options there may be */
1127 while ((opt = getopt_long (argc, argv, getoptstring,
1128 longopts, (int *) 0)) != -1)
1129 switch (opt) {
1130 case 'd':
1131 setenv ("RC_DEBUG", "yes", 1);
1132 break;
1133 case 's':
1134 if (! (rc_service_state (service) & RC_SERVICE_STARTED))
1135 exit (EXIT_FAILURE);
1136 break;
1137 case 'D':
1138 deps = false;
1139 break;
1140 case_RC_COMMON_GETOPT
1141 }
1142
1143 /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service
1144 that is being called and not any dependents */
1145 if (getenv ("IN_BACKGROUND")) {
1146 in_background = rc_env_bool ("IN_BACKGROUND");
1147 ibsave = rc_xstrdup (getenv ("IN_BACKGROUND"));
1148 unsetenv ("IN_BACKGROUND");
1149 }
1150
1151 if (rc_env_bool ("IN_HOTPLUG")) {
1152 if (! rc_env_bool ("RC_HOTPLUG") || ! rc_service_plugable (applet))
1153 eerrorx ("%s: not allowed to be hotplugged", applet);
1154 }
1155
1156 /* Setup a signal handler */
1157 signal (SIGHUP, handle_signal);
1158 signal (SIGINT, handle_signal);
1159 signal (SIGQUIT, handle_signal);
1160 signal (SIGTERM, handle_signal);
1161 signal (SIGCHLD, handle_signal);
1162
1163 /* Load our plugins */
1164 rc_plugin_load ();
1165
1166 /* Now run each option */
1167 retval = EXIT_SUCCESS;
1168 while (optind < argc) {
1169 optarg = argv[optind++];
1170
1171 /* Abort on a sighup here */
1172 if (sighup)
1173 exit (EXIT_FAILURE);
1174
1175 if (strcmp (optarg, "status") != 0 &&
1176 strcmp (optarg, "help") != 0) {
1177 /* Only root should be able to run us */
1178 }
1179
1180 /* Export the command we're running.
1181 This is important as we stamp on the restart function now but
1182 some start/stop routines still need to behave differently if
1183 restarting. */
1184 unsetenv ("RC_CMD");
1185 setenv ("RC_CMD", optarg, 1);
1186
1187 doneone = true;
1188
1189 if (strcmp (optarg, "describe") == 0) {
1190 svc_exec (optarg, NULL);
1191 } else if (strcmp (optarg, "help") == 0) {
1192 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", (char *) NULL);
1193 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1194 applet, strerror (errno));
1195 } else if (strcmp (optarg, "ineed") == 0 ||
1196 strcmp (optarg, "iuse") == 0 ||
1197 strcmp (optarg, "needsme") == 0 ||
1198 strcmp (optarg, "usesme") == 0 ||
1199 strcmp (optarg, "iafter") == 0 ||
1200 strcmp (optarg, "ibefore") == 0 ||
1201 strcmp (optarg, "iprovide") == 0) {
1202 int depoptions = RC_DEP_TRACE;
1203
1204 if (rc_env_bool ("RC_DEPEND_STRICT"))
1205 depoptions |= RC_DEP_STRICT;
1206
1207 if (! deptree && ((deptree = _rc_deptree_load ()) == NULL))
1208 eerrorx ("failed to load deptree");
1209
1210 rc_strlist_free (types);
1211 types = NULL;
1212 rc_strlist_add (&types, optarg);
1213 rc_strlist_free (svclist);
1214 svclist = NULL;
1215 rc_strlist_add (&svclist, applet);
1216 rc_strlist_free (services);
1217 services = rc_deptree_depends (deptree, types, svclist,
1218 softlevel, depoptions);
1219 STRLIST_FOREACH (services, svc, i)
1220 printf ("%s%s", i == 1 ? "" : " ", svc);
1221 if (services)
1222 printf ("\n");
1223 } else if (strcmp (optarg, "status") == 0) {
1224 rc_service_state_t r = svc_status (service);
1225 retval = (int) r;
1226
1227 } else if (strcmp (optarg, "help") == 0) {
1228 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", (char *) NULL);
1229 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1230 applet, strerror (errno));
1231 } else {
1232 if (geteuid () != 0)
1233 eerrorx ("%s: root access required", applet);
1234
1235 if (strcmp (optarg, "conditionalrestart") == 0 ||
1236 strcmp (optarg, "condrestart") == 0)
1237 {
1238 if (rc_service_state (service) & RC_SERVICE_STARTED)
1239 svc_restart (deps);
1240 } else if (strcmp (optarg, "restart") == 0) {
1241 svc_restart (deps);
1242 } else if (strcmp (optarg, "start") == 0) {
1243 svc_start (deps);
1244 } else if (strcmp (optarg, "stop") == 0) {
1245 if (deps && in_background)
1246 get_started_services ();
1247
1248 svc_stop (deps);
1249
1250 if (deps) {
1251 if (! in_background &&
1252 ! rc_runlevel_stopping () &&
1253 rc_service_state (service) & RC_SERVICE_STOPPED)
1254 uncoldplug ();
1255
1256 if (in_background &&
1257 rc_service_state (service) & RC_SERVICE_INACTIVE)
1258 {
1259 int j;
1260 STRLIST_FOREACH (restart_services, svc, j)
1261 if (rc_service_state (svc) & RC_SERVICE_STOPPED)
1262 rc_service_schedule_start (service, svc);
1263 }
1264 }
1265 } else if (strcmp (optarg, "zap") == 0) {
1266 einfo ("Manually resetting %s to stopped state", applet);
1267 rc_service_mark (applet, RC_SERVICE_STOPPED);
1268 uncoldplug ();
1269 } else
1270 svc_exec (optarg, NULL);
1271
1272 /* We should ensure this list is empty after an action is done */
1273 rc_strlist_free (restart_services);
1274 restart_services = NULL;
1275 }
1276
1277 if (! doneone) {
1278 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1279 eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1280 applet, strerror (errno));
1281 }
1282 }
1283
1284 return (retval);
1285 }

  ViewVC Help
Powered by ViewVC 1.1.20