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

Contents of /trunk/src/runscript.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2659 - (hide annotations) (download) (as text)
Thu Apr 26 11:24:07 2007 UTC (7 years, 4 months ago) by uberlord
File MIME type: text/x-csrc
File size: 32505 byte(s)
Set fonts in init-early.sh, don't stop services twice when shutting down and lock prefixed output.
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 2577 if (rc_service_state (service, rc_service_started))
561     ewarnx ("WARNING: %s has already been started", applet);
562     else if (rc_service_state (service, rc_service_starting))
563     ewarnx ("WARNING: %s is already starting", applet);
564     else if (rc_service_state (service, rc_service_stopping))
565     ewarnx ("WARNING: %s is stopping", applet);
566     else if (rc_service_state (service, rc_service_inactive) && ! background)
567     ewarnx ("WARNING: %s has already started, but is inactive", applet);
568 uberlord 2547
569 uberlord 2577 if (! rc_mark_service (service, rc_service_starting))
570     eerrorx ("ERROR: %s has been started by something else", applet);
571 uberlord 2547
572 uberlord 2577 make_exclusive (service);
573 uberlord 2547
574 uberlord 2577 if (deps) {
575     if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
576     eerrorx ("failed to load deptree");
577 uberlord 2547
578 uberlord 2577 rc_strlist_free (types);
579     types = rc_strlist_add (NULL, "broken");
580     rc_strlist_free (svclist);
581     svclist = rc_strlist_add (NULL, applet);
582     rc_strlist_free (services);
583     services = rc_get_depends (deptree, types, svclist, softlevel, 0);
584     if (services) {
585     eerrorn ("ERROR: `%s' needs ", applet);
586     STRLIST_FOREACH (services, svc, i) {
587     if (i > 0)
588     fprintf (stderr, ", ");
589     fprintf (stderr, "%s", svc);
590     }
591     exit (EXIT_FAILURE);
592     }
593     rc_strlist_free (services);
594     services = NULL;
595 uberlord 2547
596 uberlord 2577 rc_strlist_free (types);
597     types = rc_strlist_add (NULL, "ineed");
598     rc_strlist_free (need_services);
599     need_services = rc_get_depends (deptree, types, svclist,
600     softlevel, depoptions);
601     types = rc_strlist_add (types, "iuse");
602     if (! rc_runlevel_starting ()) {
603     services = rc_get_depends (deptree, types, svclist,
604     softlevel, depoptions);
605     STRLIST_FOREACH (services, svc, i)
606 uberlord 2641 if (rc_service_state (svc, rc_service_stopped)) {
607     pid_t pid = rc_start_service (svc);
608 uberlord 2650 if (! rc_is_env ("RC_PARALLEL", "yes"))
609 uberlord 2641 rc_waitpid (pid);
610     }
611 uberlord 2547
612 uberlord 2577 rc_strlist_free (services);
613     }
614 uberlord 2547
615 uberlord 2577 /* Now wait for them to start */
616     types = rc_strlist_add (types, "iafter");
617     services = rc_get_depends (deptree, types, svclist,
618     softlevel, depoptions);
619 uberlord 2547
620 uberlord 2577 /* We use tmplist to hold our scheduled by list */
621     rc_strlist_free (tmplist);
622     tmplist = NULL;
623 uberlord 2547
624 uberlord 2577 STRLIST_FOREACH (services, svc, i) {
625     if (rc_service_state (svc, rc_service_started))
626     continue;
627     if (! rc_wait_service (svc))
628     eerror ("%s: timed out waiting for %s", applet, svc);
629     if (rc_service_state (svc, rc_service_started))
630     continue;
631 uberlord 2547
632 uberlord 2577 STRLIST_FOREACH (need_services, svc2, j)
633     if (strcmp (svc, svc2) == 0) {
634     if (rc_service_state (svc, rc_service_inactive) ||
635     rc_service_state (svc, rc_service_wasinactive))
636     tmplist = rc_strlist_add (tmplist, svc);
637     else
638     eerrorx ("ERROR: cannot start %s as %s would not start",
639     applet, svc);
640     }
641     }
642 uberlord 2547
643 uberlord 2577 if (tmplist) {
644     int n = 0;
645     int len = 0;
646     char *p;
647 uberlord 2547
648 uberlord 2577 /* Set the state now, then unlink our exclusive so that
649     our scheduled list is preserved */
650     rc_mark_service (service, rc_service_stopped);
651     unlink_mtime_test ();
652 uberlord 2547
653 uberlord 2577 rc_strlist_free (types);
654     types = rc_strlist_add (NULL, "iprovide");
655     STRLIST_FOREACH (tmplist, svc, i) {
656     rc_schedule_start_service (svc, service);
657 uberlord 2547
658 uberlord 2577 rc_strlist_free (svclist);
659     svclist = rc_strlist_add (NULL, svc);
660     rc_strlist_free (providelist);
661     providelist = rc_get_depends (deptree, types, svclist,
662     softlevel, depoptions);
663     STRLIST_FOREACH (providelist, svc2, j)
664     rc_schedule_start_service (svc2, service);
665 uberlord 2547
666 uberlord 2577 len += strlen (svc) + 2;
667     n++;
668     }
669 uberlord 2547
670 uberlord 2577 len += 5;
671     tmp = rc_xmalloc (sizeof (char *) * len);
672     p = tmp;
673     STRLIST_FOREACH (tmplist, svc, i) {
674     if (i > 1) {
675     if (i == n - 1)
676     p += snprintf (p, len, " or ");
677     else
678     p += snprintf (p, len, ", ");
679     }
680     p += snprintf (p, len, "%s", svc);
681     }
682     ewarnx ("WARNING: %s is scheduled to start when %s has started",
683     applet, tmp);
684     }
685 uberlord 2547
686 uberlord 2577 rc_strlist_free (services);
687     services = NULL;
688     rc_strlist_free (types);
689     types = NULL;
690     rc_strlist_free (svclist);
691     svclist = NULL;
692     }
693 uberlord 2547
694 uberlord 2577 if (ibsave)
695     setenv ("IN_BACKGROUND", ibsave, 1);
696     rc_plugin_run (rc_hook_service_start_now, applet);
697     started = svc_exec (service, "start", NULL);
698     if (ibsave)
699     unsetenv ("IN_BACKGROUND");
700 uberlord 2547
701 uberlord 2577 if (in_control ()) {
702     if (! started) {
703     if (rc_service_state (service, rc_service_wasinactive))
704     rc_mark_service (service, rc_service_inactive);
705     else {
706     rc_mark_service (service, rc_service_stopped);
707     if (rc_runlevel_starting ())
708     rc_mark_service (service, rc_service_failed);
709     }
710     rc_plugin_run (rc_hook_service_start_done, applet);
711     eerrorx ("ERROR: %s failed to start", applet);
712     }
713     rc_mark_service (service, rc_service_started);
714     unlink_mtime_test ();
715     rc_plugin_run (rc_hook_service_start_done, applet);
716     } else {
717     rc_plugin_run (rc_hook_service_start_done, applet);
718     if (rc_service_state (service, rc_service_inactive))
719     ewarnx ("WARNING: %s has started, but is inactive", applet);
720     else
721     ewarnx ("WARNING: %s not under our control, aborting", applet);
722     }
723 uberlord 2547
724 uberlord 2577 /* Now start any scheduled services */
725     rc_strlist_free (services);
726     services = rc_services_scheduled (service);
727     STRLIST_FOREACH (services, svc, i)
728     if (rc_service_state (svc, rc_service_stopped))
729     rc_start_service (svc);
730     rc_strlist_free (services);
731     services = NULL;
732 uberlord 2547
733 uberlord 2577 /* Do the same for any services we provide */
734     rc_strlist_free (types);
735     types = rc_strlist_add (NULL, "iprovide");
736     rc_strlist_free (svclist);
737     svclist = rc_strlist_add (NULL, applet);
738     rc_strlist_free (tmplist);
739     tmplist = rc_get_depends (deptree, types, svclist, softlevel, depoptions);
740 uberlord 2547
741 uberlord 2577 STRLIST_FOREACH (tmplist, svc2, j) {
742     rc_strlist_free (services);
743     services = rc_services_scheduled (svc2);
744     STRLIST_FOREACH (services, svc, i)
745     if (rc_service_state (svc, rc_service_stopped))
746     rc_start_service (svc);
747     }
748 uberlord 2573
749 uberlord 2577 hook_out = 0;
750     rc_plugin_run (rc_hook_service_start_out, applet);
751 uberlord 2547 }
752    
753     static void svc_stop (const char *service, bool deps)
754     {
755 uberlord 2577 bool stopped;
756 uberlord 2547
757 uberlord 2577 hook_out = rc_hook_service_stop_out;
758 uberlord 2573
759 uberlord 2577 if (rc_runlevel_stopping () &&
760     rc_service_state (service, rc_service_failed))
761     exit (EXIT_FAILURE);
762 uberlord 2547
763 uberlord 2577 if (rc_is_env ("IN_HOTPLUG", "1") || in_background)
764 uberlord 2578 if (! rc_service_state (service, rc_service_started) &&
765 uberlord 2582 ! rc_service_state (service, rc_service_inactive))
766 uberlord 2577 exit (EXIT_FAILURE);
767 uberlord 2547
768 uberlord 2577 if (rc_service_state (service, rc_service_stopped))
769     ewarnx ("WARNING: %s is already stopped", applet);
770     else if (rc_service_state (service, rc_service_stopping))
771     ewarnx ("WARNING: %s is already stopping", applet);
772 uberlord 2547
773 uberlord 2577 if (! rc_mark_service (service, rc_service_stopping))
774     eerrorx ("ERROR: %s has been stopped by something else", applet);
775 uberlord 2547
776 uberlord 2577 make_exclusive (service);
777 uberlord 2547
778 uberlord 2577 if (! rc_runlevel_stopping () &&
779     rc_service_in_runlevel (service, RC_LEVEL_BOOT))
780     ewarn ("WARNING: you are stopping a boot service");
781 uberlord 2547
782 uberlord 2577 if (deps || ! rc_service_state (service, rc_service_wasinactive)) {
783     int depoptions = RC_DEP_TRACE;
784     char *svc;
785     int i;
786 uberlord 2569
787 uberlord 2577 if (rc_is_env ("RC_STRICT_DEPEND", "yes"))
788     depoptions |= RC_DEP_STRICT;
789 uberlord 2547
790 uberlord 2577 if (! deptree && ((deptree = rc_load_deptree ()) == NULL))
791     eerrorx ("failed to load deptree");
792 uberlord 2547
793 uberlord 2577 rc_strlist_free (types);
794     types = rc_strlist_add (NULL, "needsme");
795     rc_strlist_free (svclist);
796     svclist = rc_strlist_add (NULL, applet);
797     rc_strlist_free (tmplist);
798     tmplist = NULL;
799     rc_strlist_free (services);
800     services = rc_get_depends (deptree, types, svclist,
801     softlevel, depoptions);
802     rc_strlist_reverse (services);
803     STRLIST_FOREACH (services, svc, i) {
804     if (rc_service_state (svc, rc_service_started) ||
805     rc_service_state (svc, rc_service_inactive))
806     {
807     rc_wait_service (svc);
808     if (rc_service_state (svc, rc_service_started) ||
809     rc_service_state (svc, rc_service_inactive))
810     {
811 uberlord 2641 pid_t pid = rc_stop_service (svc);
812 uberlord 2650 if (! rc_is_env ("RC_PARALLEL", "yes"))
813 uberlord 2641 rc_waitpid (pid);
814 uberlord 2577 tmplist = rc_strlist_add (tmplist, svc);
815     }
816     }
817     }
818     rc_strlist_free (services);
819     services = NULL;
820 uberlord 2547
821 uberlord 2577 STRLIST_FOREACH (tmplist, svc, i) {
822     if (rc_service_state (svc, rc_service_stopped))
823     continue;
824 uberlord 2547
825 uberlord 2577 /* We used to loop 3 times here - maybe re-do this if needed */
826     rc_wait_service (svc);
827     if (! rc_service_state (svc, rc_service_stopped)) {
828     if (rc_runlevel_stopping ())
829     rc_mark_service (svc, rc_service_failed);
830     eerrorx ("ERROR: cannot stop %s as %s is still up",
831     applet, svc);
832     }
833     }
834     rc_strlist_free (tmplist);
835     tmplist = NULL;
836 uberlord 2547
837 uberlord 2577 /* We now wait for other services that may use us and are stopping
838     This is important when a runlevel stops */
839     types = rc_strlist_add (types, "usesme");
840     types = rc_strlist_add (types, "ibefore");
841     services = rc_get_depends (deptree, types, svclist,
842     softlevel, depoptions);
843     STRLIST_FOREACH (services, svc, i) {
844     if (rc_service_state (svc, rc_service_stopped))
845     continue;
846     rc_wait_service (svc);
847     }
848 uberlord 2547
849 uberlord 2577 rc_strlist_free (services);
850     services = NULL;
851     rc_strlist_free (types);
852     types = NULL;
853     }
854 uberlord 2547
855 uberlord 2577 if (ibsave)
856     setenv ("IN_BACKGROUND", ibsave, 1);
857     rc_plugin_run (rc_hook_service_stop_now, applet);
858     stopped = svc_exec (service, "stop", NULL);
859     if (ibsave)
860     unsetenv ("IN_BACKGROUND");
861 uberlord 2547
862 uberlord 2577 if (! in_control ()) {
863     rc_plugin_run (rc_hook_service_stop_done, applet);
864     ewarnx ("WARNING: %s not under our control, aborting", applet);
865     }
866 uberlord 2547
867 uberlord 2577 if (! stopped) {
868     if (rc_service_state (service, rc_service_wasinactive))
869     rc_mark_service (service, rc_service_inactive);
870     else
871     rc_mark_service (service, rc_service_started);
872     rc_plugin_run (rc_hook_service_stop_done, applet);
873     eerrorx ("ERROR: %s failed to stop", applet);
874     }
875 uberlord 2547
876 uberlord 2577 if (in_background)
877     rc_mark_service (service, rc_service_inactive);
878     else
879     rc_mark_service (service, rc_service_stopped);
880 uberlord 2547
881 uberlord 2577 unlink_mtime_test ();
882     rc_plugin_run (rc_hook_service_stop_done, applet);
883     hook_out = 0;
884     rc_plugin_run (rc_hook_service_stop_out, applet);
885 uberlord 2547 }
886    
887     static void svc_restart (const char *service, bool deps)
888     {
889 uberlord 2577 char *svc;
890     int i;
891     bool inactive = false;
892 uberlord 2569
893 uberlord 2577 /* This is hairly and a better way needs to be found I think!
894     The issue is this - openvpn need net and dns. net can restart
895     dns via resolvconf, so you could have openvpn trying to restart dnsmasq
896     which in turn is waiting on net which in turn is waiting on dnsmasq.
897     The work around is for resolvconf to restart it's services with --nodeps
898     which means just that. The downside is that there is a small window when
899     our status is invalid.
900     One workaround would be to introduce a new status, or status locking. */
901     if (! deps) {
902     if (rc_service_state (service, rc_service_started) ||
903     rc_service_state (service, rc_service_inactive))
904     svc_exec (service, "stop", "start");
905     else
906     svc_exec (service, "start", NULL);
907     return;
908     }
909 uberlord 2547
910 uberlord 2577 if (! rc_service_state (service, rc_service_stopped)) {
911     get_started_services ();
912     svc_stop (service, deps);
913 uberlord 2547
914 uberlord 2577 /* Flush our buffered output if any */
915     eflush ();
916     }
917 uberlord 2547
918 uberlord 2577 svc_start (service, deps);
919 uberlord 2547
920 uberlord 2577 inactive = rc_service_state (service, rc_service_inactive);
921     if (! inactive)
922     inactive = rc_service_state (service, rc_service_wasinactive);
923 uberlord 2569
924 uberlord 2577 if (inactive ||
925     rc_service_state (service, rc_service_starting) ||
926     rc_service_state (service, rc_service_started))
927     {
928     STRLIST_FOREACH (restart_services, svc, i) {
929     if (rc_service_state (svc, rc_service_stopped)) {
930     if (inactive) {
931     rc_schedule_start_service (service, svc);
932     ewarn ("WARNING: %s is scheduled to started when %s has started",
933 uberlord 2634 svc, applet);
934 uberlord 2577 } else
935     rc_start_service (svc);
936     }
937     }
938     }
939 uberlord 2547 }
940    
941 vapier 2618 #define getoptstring "dCDNqvh"
942     static struct option longopts[] = {
943     { "debug", 0, NULL, 'd'},
944     { "nocolor", 0, NULL, 'C'},
945     { "nocolour", 0, NULL, 'C'},
946     { "nodeps", 0, NULL, 'D'},
947     { "quiet", 0, NULL, 'q'},
948     { "verbose", 0, NULL, 'v'},
949     { "help", 0, NULL, 'h'},
950     { NULL, 0, NULL, 0}
951     };
952 uberlord 2634 // #include "_usage.c"
953 vapier 2618
954 uberlord 2547 int main (int argc, char **argv)
955     {
956 uberlord 2634 char *service = argv[1];
957 uberlord 2577 int i;
958     bool deps = true;
959     bool doneone = false;
960     char pid[16];
961     int retval;
962 uberlord 2580 char c;
963 uberlord 2547
964 uberlord 2577 /* Show help if insufficient args */
965     if (argc < 3) {
966     execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
967     eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
968     applet, strerror (errno));
969     }
970 uberlord 2547
971 uberlord 2634 applet = rc_xstrdup (basename (service));
972 uberlord 2581 atexit (cleanup);
973    
974 uberlord 2547 #ifdef __linux__
975 uberlord 2577 /* coldplug events can trigger init scripts, but we don't want to run them
976     until after rc sysinit has completed so we punt them to the boot runlevel */
977     if (rc_exists ("/dev/.rcsysinit")) {
978     eerror ("%s: cannot run until sysvinit completes", applet);
979     if (mkdir ("/dev/.rcboot", 0755) != 0 && errno != EEXIST)
980     eerrorx ("%s: mkdir `/dev/.rcboot': %s", applet, strerror (errno));
981     tmp = rc_strcatpaths ("/dev/.rcboot", applet, (char *) NULL);
982     symlink (service, tmp);
983     exit (EXIT_FAILURE);
984     }
985 uberlord 2547 #endif
986    
987 uberlord 2577 if ((softlevel = getenv ("RC_SOFTLEVEL")) == NULL) {
988     /* Ensure our environment is pure
989     Also, add our configuration to it */
990     env = rc_filter_env ();
991     env = rc_config_env (env);
992 uberlord 2547
993 uberlord 2577 if (env) {
994     char *p;
995 uberlord 2547
996     #ifdef __linux__
997 uberlord 2577 /* clearenv isn't portable, but there's no harm in using it
998     if we have it */
999     clearenv ();
1000 uberlord 2547 #else
1001 uberlord 2577 char *var;
1002     /* No clearenv present here then.
1003     We could manipulate environ directly ourselves, but it seems that
1004     some kernels bitch about this according to the environ man pages
1005     so we walk though environ and call unsetenv for each value. */
1006     while (environ[0]) {
1007     tmp = rc_xstrdup (environ[0]);
1008     p = tmp;
1009     var = strsep (&p, "=");
1010     unsetenv (var);
1011     free (tmp);
1012     }
1013     tmp = NULL;
1014 uberlord 2547 #endif
1015    
1016 uberlord 2577 STRLIST_FOREACH (env, p, i)
1017     putenv (p);
1018 uberlord 2547
1019 uberlord 2577 /* We don't free our list as that would be null in environ */
1020     }
1021 uberlord 2547
1022 uberlord 2577 softlevel = rc_get_runlevel ();
1023 uberlord 2547
1024 uberlord 2577 /* If not called from RC or another service then don't be parallel */
1025 uberlord 2650 unsetenv ("RC_PARALLEL");
1026 uberlord 2577 }
1027 uberlord 2547
1028 uberlord 2577 setenv ("RC_ELOG", service, 1);
1029     setenv ("SVCNAME", applet, 1);
1030 uberlord 2547
1031 uberlord 2577 /* Set an env var so that we always know our pid regardless of any
1032     subshells the init script may create so that our mark_service_*
1033     functions can always instruct us of this change */
1034     snprintf (pid, sizeof (pid), "%d", (int) getpid ());
1035     setenv ("RC_RUNSCRIPT_PID", pid, 1);
1036 uberlord 2547
1037 uberlord 2652 /* eprefix is kinda klunky, but it works for our purposes */
1038     if (rc_is_env ("RC_PREFIX", "yes")) {
1039     int l = strlen (applet);
1040     if (l < 13)
1041     l = 13;
1042     prefix = rc_xmalloc (sizeof (char *) * l);
1043     snprintf (prefix, l, "%s%s", applet, " ");
1044     eprefix (prefix);
1045     }
1046    
1047     /* If we're in parallel and we're not prefixing then we need the ebuffer */
1048     if (rc_is_env ("RC_PARALLEL", "yes") && ! rc_is_env ("RC_PREFIX", "yes")) {
1049 uberlord 2577 char ebname[PATH_MAX];
1050     char *eb;
1051 uberlord 2547
1052 uberlord 2577 snprintf (ebname, sizeof (ebname), "%s.%s", applet, pid);
1053     eb = rc_strcatpaths (RC_SVCDIR "ebuffer", ebname, (char *) NULL);
1054 uberlord 2650 ebuffer (eb);
1055 uberlord 2577 free (eb);
1056     }
1057 uberlord 2547
1058     #ifdef __linux__
1059 uberlord 2577 /* Ok, we are ready to go, so setup selinux if applicable */
1060     setup_selinux (argc, argv);
1061 uberlord 2547 #endif
1062    
1063 uberlord 2581 /* Punt the first arg as it's our service name */
1064     argc--;
1065     argv++;
1066    
1067 uberlord 2577 /* Right then, parse any options there may be */
1068 vapier 2618 while ((c = getopt_long (argc, argv, getoptstring,
1069 uberlord 2580 longopts, (int *) 0)) != -1)
1070     switch (c) {
1071     case 'd':
1072     setenv ("RC_DEBUG", "yes", 1);
1073     break;
1074     case 'h':
1075     execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1076     eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1077     applet, strerror (errno));
1078     case 'C':
1079     setenv ("RC_NOCOLOR", "yes", 1);
1080     break;
1081     case 'D':
1082     deps = false;
1083     break;
1084     case 'q':
1085     setenv ("RC_QUIET", "yes", 1);
1086     break;
1087     case 'v':
1088     setenv ("RC_VERBOSE", "yes", 1);
1089     break;
1090     default:
1091     exit (EXIT_FAILURE);
1092     }
1093 uberlord 2547
1094 uberlord 2581 /* Save the IN_BACKGROUND env flag so it's ONLY passed to the service
1095     that is being called and not any dependents */
1096     if (getenv ("IN_BACKGROUND")) {
1097     in_background = rc_is_env ("IN_BACKGROUND", "true");
1098 uberlord 2634 ibsave = rc_xstrdup (getenv ("IN_BACKGROUND"));
1099 uberlord 2581 unsetenv ("IN_BACKGROUND");
1100     }
1101    
1102 uberlord 2577 if (rc_is_env ("IN_HOTPLUG", "1")) {
1103     if (! rc_is_env ("RC_HOTPLUG", "yes") || ! rc_allow_plug (applet))
1104     eerrorx ("%s: not allowed to be hotplugged", applet);
1105     }
1106 uberlord 2547
1107 uberlord 2577 /* Setup a signal handler */
1108     signal (SIGHUP, handle_signal);
1109     signal (SIGINT, handle_signal);
1110     signal (SIGQUIT, handle_signal);
1111     signal (SIGTERM, handle_signal);
1112     signal (SIGCHLD, handle_signal);
1113 uberlord 2547
1114 uberlord 2577 /* Load our plugins */
1115     rc_plugin_load ();
1116 uberlord 2547
1117 uberlord 2577 /* Now run each option */
1118     retval = EXIT_SUCCESS;
1119 uberlord 2580 while (optind < argc) {
1120     optarg = argv[optind++];
1121    
1122 uberlord 2577 /* Abort on a sighup here */
1123     if (sighup)
1124     exit (EXIT_FAILURE);
1125 uberlord 2547
1126 uberlord 2577 /* Export the command we're running.
1127     This is important as we stamp on the restart function now but
1128     some start/stop routines still need to behave differently if
1129     restarting. */
1130     unsetenv ("RC_CMD");
1131 uberlord 2580 setenv ("RC_CMD", optarg, 1);
1132 uberlord 2569
1133 uberlord 2577 doneone = true;
1134 uberlord 2580 if (strcmp (optarg, "conditionalrestart") == 0 ||
1135     strcmp (optarg, "condrestart") == 0)
1136 uberlord 2577 {
1137     if (rc_service_state (service, rc_service_started))
1138     svc_restart (service, deps);
1139     }
1140 uberlord 2580 else if (strcmp (optarg, "restart") == 0)
1141 uberlord 2577 svc_restart (service, deps);
1142 uberlord 2580 else if (strcmp (optarg, "start") == 0)
1143 uberlord 2577 svc_start (service, deps);
1144 uberlord 2580 else if (strcmp (optarg, "status") == 0) {
1145 uberlord 2577 rc_service_state_t r = svc_status (service);
1146     retval = (int) r;
1147 uberlord 2580 } else if (strcmp (optarg, "stop") == 0) {
1148 uberlord 2577 if (in_background)
1149     get_started_services ();
1150 uberlord 2547
1151 uberlord 2577 svc_stop (service, deps);
1152 uberlord 2547
1153 uberlord 2577 if (! in_background &&
1154     ! rc_runlevel_stopping () &&
1155     rc_service_state (service, rc_service_stopped))
1156     uncoldplug (applet);
1157 uberlord 2560
1158 uberlord 2577 if (in_background &&
1159     rc_service_state (service, rc_service_inactive))
1160     {
1161     char *svc;
1162     int j;
1163     STRLIST_FOREACH (restart_services, svc, j)
1164     if (rc_service_state (svc, rc_service_stopped))
1165     rc_schedule_start_service (service, svc);
1166     }
1167 uberlord 2580 } else if (strcmp (optarg, "zap") == 0) {
1168 uberlord 2577 einfo ("Manually resetting %s to stopped state", applet);
1169     rc_mark_service (applet, rc_service_stopped);
1170     uncoldplug (applet);
1171 uberlord 2580 } else if (strcmp (optarg, "help") == 0) {
1172 uberlord 2577 execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, "help", (char *) NULL);
1173     eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1174     applet, strerror (errno));
1175     }else
1176 uberlord 2580 svc_exec (service, optarg, NULL);
1177 uberlord 2547
1178 uberlord 2577 /* Flush our buffered output if any */
1179     eflush ();
1180 uberlord 2547
1181 uberlord 2577 /* We should ensure this list is empty after an action is done */
1182     rc_strlist_free (restart_services);
1183     restart_services = NULL;
1184     }
1185 uberlord 2547
1186 uberlord 2577 if (! doneone) {
1187     execl (RCSCRIPT_HELP, RCSCRIPT_HELP, service, (char *) NULL);
1188     eerrorx ("%s: failed to exec `" RCSCRIPT_HELP "': %s",
1189     applet, strerror (errno));
1190     }
1191 uberlord 2547
1192 uberlord 2577 return (retval);
1193 uberlord 2547 }

  ViewVC Help
Powered by ViewVC 1.1.20