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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20