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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2549 - (hide annotations) (download) (as text)
Thu Apr 5 15:01:09 2007 UTC (7 years, 7 months ago) by uberlord
File MIME type: text/x-csrc
File size: 17019 byte(s)
We now compile on OpenBSD (no idea if it works or not yet)
1 uberlord 2547 /*
2     librc
3     core RC functions
4     Copyright 2007 Gentoo Foundation
5     Released under the GPLv2
6     */
7    
8     #include <sys/types.h>
9     #include <sys/select.h>
10     #include <sys/time.h>
11     #include <sys/stat.h>
12     #include <sys/wait.h>
13     #include <errno.h>
14     #ifndef __linux__
15     /* Although linux should work fine, gcc likes to bitch with our default
16     CFLAGS so we just don't include the file and use the GNU one defined
17     in string.h */
18     #include <libgen.h>
19     #endif
20     #include <limits.h>
21     #include <stdarg.h>
22     #include <stdbool.h>
23     #include <stdio.h>
24     #include <stdlib.h>
25     #include <string.h>
26     #include <unistd.h>
27    
28     #include "einfo.h"
29     #include "rc.h"
30     #include "rc-misc.h"
31     #include "strlist.h"
32    
33     /* usecs to wait while we poll the fifo */
34     #define WAIT_INTERVAL 20000
35    
36     /* max secs to wait until a service comes up */
37     #define WAIT_MAX 60
38    
39     #define SOFTLEVEL RC_SVCDIR "softlevel"
40    
41     static const char *rc_service_state_names[] = {
42     "started",
43     "stopped",
44     "starting",
45     "stopping",
46     "inactive",
47     "wasinactive",
48     "coldplugged",
49     "failed",
50     NULL
51     };
52    
53     bool rc_runlevel_starting (void)
54     {
55     return (rc_is_dir (RC_SVCDIR "softscripts.old"));
56     }
57    
58     bool rc_runlevel_stopping (void)
59     {
60     return (rc_is_dir (RC_SVCDIR "softscripts.new"));
61     }
62    
63     char **rc_get_runlevels (void)
64     {
65     char **dirs = rc_ls_dir (NULL, RC_RUNLEVELDIR, 0);
66     char **runlevels = NULL;
67     int i;
68     char *dir;
69    
70     STRLIST_FOREACH (dirs, dir, i)
71     {
72 uberlord 2549 char *path = rc_strcatpaths (RC_RUNLEVELDIR, dir, (char *) NULL);
73 uberlord 2547 if (rc_is_dir (path))
74     runlevels = rc_strlist_addsort (runlevels, dir);
75     free (path);
76     }
77     rc_strlist_free (dirs);
78    
79     return (runlevels);
80     }
81    
82     char *rc_get_runlevel (void)
83     {
84     FILE *fp;
85     static char buffer [PATH_MAX];
86    
87     if (! (fp = fopen (SOFTLEVEL, "r")))
88     {
89     strcpy (buffer, "sysinit");
90     return (buffer);
91     }
92    
93     if (fgets (buffer, PATH_MAX, fp))
94     {
95     int i = strlen (buffer) - 1;
96     if (buffer[i] == '\n')
97     buffer[i] = 0;
98     fclose (fp);
99     return (buffer);
100     }
101    
102     fclose (fp);
103     strcpy (buffer, "sysinit");
104     return (buffer);
105     }
106    
107     void rc_set_runlevel (const char *runlevel)
108     {
109     FILE *fp = fopen (SOFTLEVEL, "w");
110     if (! fp)
111     eerrorx ("failed to open `" SOFTLEVEL "': %s", strerror (errno));
112     fprintf (fp, "%s", runlevel);
113     fclose (fp);
114     }
115    
116     bool rc_runlevel_exists (const char *runlevel)
117     {
118     char *path;
119     bool retval;
120    
121     if (! runlevel)
122     return (false);
123    
124 uberlord 2549 path = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, (char *) NULL);
125 uberlord 2547 retval = rc_is_dir (path);
126     free (path);
127     return (retval);
128     }
129    
130     /* Resolve a service name to it's full path */
131     char *rc_resolve_service (const char *service)
132     {
133     char buffer[PATH_MAX];
134     char *file;
135     int r = 0;
136    
137     if (! service)
138     return (NULL);
139    
140     if (service[0] == '/')
141     return (strdup (service));
142    
143 uberlord 2549 file = rc_strcatpaths (RC_SVCDIR, "started", service, (char *) NULL);
144 uberlord 2547 if (! rc_is_link (file))
145     {
146     free (file);
147 uberlord 2549 file = rc_strcatpaths (RC_SVCDIR, "inactive", service, (char *) NULL);
148 uberlord 2547 if (! rc_is_link (file))
149     {
150     free (file);
151     file = NULL;
152     }
153     }
154    
155     memset (buffer, 0, sizeof (buffer));
156     if (file)
157     {
158     r = readlink (file, buffer, sizeof (buffer));
159     free (file);
160     if (r > 0)
161     return strdup (buffer);
162     }
163    
164     snprintf (buffer, sizeof (buffer), RC_INITDIR "%s", service);
165     return (strdup (buffer));
166     }
167    
168     bool rc_service_exists (const char *service)
169     {
170     char *file;
171     bool retval = false;
172     int len;
173    
174     if (! service)
175     return (false);
176    
177     len = strlen (service);
178    
179     /* .sh files are not init scripts */
180     if (len > 2 && service[len - 3] == '.' &&
181     service[len - 2] == 's' &&
182     service[len - 1] == 'h')
183     return (false);
184    
185     file = rc_resolve_service (service);
186     if (rc_exists (file))
187     retval = rc_is_exec (file);
188     free (file);
189     return (retval);
190     }
191    
192     bool rc_service_in_runlevel (const char *service, const char *runlevel)
193     {
194     char *file;
195     bool retval;
196    
197     if (! runlevel || ! service)
198     return (false);
199    
200     if (! rc_service_exists (service))
201     return (false);
202    
203 uberlord 2549 file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service),
204     (char *) NULL);
205 uberlord 2547 retval = rc_exists (file);
206     free (file);
207    
208     return (retval);
209     }
210    
211     bool rc_mark_service (const char *service, const rc_service_state_t state)
212     {
213     char *file;
214     int i = 0;
215     int skip_state = -1;
216     char *base;
217     char *init = rc_resolve_service (service);
218     bool skip_wasinactive = false;
219    
220     if (! service)
221     return (false);
222    
223     base = basename (service);
224    
225     if (state != rc_service_stopped)
226     {
227     if (! rc_is_file(init))
228     {
229     free (init);
230     return (false);
231     }
232    
233 uberlord 2549 file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state], base,
234     (char *) NULL);
235 uberlord 2547 if (rc_exists (file))
236     unlink (file);
237     i = symlink (init, file);
238     if (i != 0)
239     {
240     free (file);
241     free (init);
242     einfo ("%d %s %s", state, rc_service_state_names[state], base);
243     eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
244     return (false);
245     }
246    
247     free (file);
248     skip_state = state;
249     }
250    
251     if (state == rc_service_coldplugged)
252     {
253     free (init);
254     return (true);
255     }
256    
257     /* Remove any old states now */
258     i = 0;
259     while (rc_service_state_names[i])
260     {
261     if ((i != skip_state &&
262     i != rc_service_stopped &&
263     i != rc_service_coldplugged &&
264     i != rc_service_crashed) &&
265     (! skip_wasinactive || i != rc_service_wasinactive))
266     {
267 uberlord 2549 file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[i], base,
268     (char *) NULL);
269 uberlord 2547 if (rc_exists (file))
270     {
271     if ((state == rc_service_starting ||
272     state == rc_service_stopping) &&
273     i == rc_service_inactive)
274     {
275     char *wasfile = rc_strcatpaths (RC_SVCDIR,
276     rc_service_state_names[rc_service_wasinactive],
277 uberlord 2549 base, (char *) NULL);
278 uberlord 2547
279     if (symlink (init, wasfile) != 0)
280     eerror ("symlink `%s' to `%s': %s", init, wasfile,
281     strerror (errno));
282    
283     skip_wasinactive = true;
284     free (wasfile);
285     }
286    
287     errno = 0;
288     if (unlink (file) != 0 && errno != ENOENT)
289     eerror ("failed to delete `%s': %s", file,
290     strerror (errno));
291     }
292     free (file);
293     }
294     i++;
295     }
296    
297     /* Remove the exclusive state if we're inactive */
298     if (state == rc_service_started ||
299     state == rc_service_stopped ||
300     state == rc_service_inactive)
301     {
302 uberlord 2549 file = rc_strcatpaths (RC_SVCDIR, "exclusive", base, (char *) NULL);
303 uberlord 2547 if (rc_exists (file))
304     if (unlink (file) != 0)
305     eerror ("unlink `%s': %s", file, strerror (errno));
306     free (file);
307     }
308    
309     /* Remove any options and daemons the service may have stored */
310     if (state == rc_service_stopped)
311     {
312 uberlord 2549 char *dir = rc_strcatpaths (RC_SVCDIR, "options", base, (char *) NULL);
313 uberlord 2547
314     if (rc_is_dir (dir))
315     rc_rm_dir (dir, true);
316     free (dir);
317    
318 uberlord 2549 dir = rc_strcatpaths (RC_SVCDIR, "daemons", base, (char *) NULL);
319 uberlord 2547 if (rc_is_dir (dir))
320     rc_rm_dir (dir, true);
321     free (dir);
322    
323     rc_schedule_clear (service);
324     }
325    
326     /* These are final states, so remove us from scheduled */
327     if (state == rc_service_started || state == rc_service_stopped)
328     {
329 uberlord 2549 char *sdir = rc_strcatpaths (RC_SVCDIR, "scheduled", (char *) NULL);
330 uberlord 2547 char **dirs = rc_ls_dir (NULL, sdir, 0);
331     char *dir;
332     int serrno;
333    
334     STRLIST_FOREACH (dirs, dir, i)
335     {
336 uberlord 2549 char *bdir = rc_strcatpaths (sdir, dir, (char *) NULL);
337     file = rc_strcatpaths (bdir, base, (char *) NULL);
338 uberlord 2547 if (rc_exists (file))
339     if (unlink (file) != 0)
340     eerror ("unlink `%s': %s", file, strerror (errno));
341     free (file);
342    
343     /* Try and remove the dir - we don't care about errors */
344     serrno = errno;
345     rmdir (bdir);
346     errno = serrno;
347     free (bdir);
348     }
349     rc_strlist_free (dirs);
350     free (sdir);
351     }
352    
353     free (init);
354     return (true);
355     }
356    
357     bool rc_service_state (const char *service, const rc_service_state_t state)
358     {
359     char *file;
360     bool retval;
361    
362     /* If the init script does not exist then we are stopped */
363     if (! rc_service_exists (service))
364     return (state == rc_service_stopped ? true : false);
365    
366     /* We check stopped state by not being in any of the others */
367     if (state == rc_service_stopped)
368     return ( ! (rc_service_state (service, rc_service_started) ||
369     rc_service_state (service, rc_service_starting) ||
370     rc_service_state (service, rc_service_stopping) ||
371     rc_service_state (service, rc_service_inactive)));
372    
373     /* The crashed state and scheduled states are virtual */
374     if (state == rc_service_crashed)
375     return (rc_service_daemons_crashed (service));
376     else if (state == rc_service_scheduled)
377     {
378     char **services = rc_services_scheduled_by (service);
379     retval = (services);
380     if (services)
381     free (services);
382     return (retval);
383     }
384    
385     /* Now we just check if a file by the service name rc_exists
386     in the state dir */
387     file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state],
388 uberlord 2549 basename (service), (char*) NULL);
389 uberlord 2547 retval = rc_exists (file);
390     free (file);
391     return (retval);
392     }
393    
394     bool rc_get_service_option (const char *service, const char *option,
395     char *value)
396     {
397     FILE *fp;
398 uberlord 2549 char buffer[RC_LINEBUFFER];
399     char *file = rc_strcatpaths (RC_SVCDIR, "options", service, option,
400     (char *) NULL);
401 uberlord 2547 bool retval = false;
402    
403     if (rc_exists (file))
404     {
405     if ((fp = fopen (file, "r")) == NULL)
406     eerror ("fopen `%s': %s", file, strerror (errno));
407     else
408     {
409     memset (buffer, 0, sizeof (buffer));
410     while (fgets (buffer, RC_LINEBUFFER, fp))
411     {
412     memcpy (value, buffer, strlen (buffer));
413     value += strlen (buffer);
414     }
415     fclose (fp);
416     retval = true;
417     }
418     }
419    
420     free (file);
421     return (retval);
422     }
423    
424     bool rc_set_service_option (const char *service, const char *option,
425     const char *value)
426     {
427     FILE *fp;
428 uberlord 2549 char *path = rc_strcatpaths (RC_SVCDIR, "options", service, (char *) NULL);
429     char *file = rc_strcatpaths (path, option, (char *) NULL);
430 uberlord 2547 bool retval = false;
431    
432     if (! rc_is_dir (path))
433     {
434     if (mkdir (path, 0755) != 0)
435     {
436     eerror ("mkdir `%s': %s", path, strerror (errno));
437     free (path);
438     free (file);
439     return (false);
440     }
441     }
442    
443     if ((fp = fopen (file, "w")) == NULL)
444     eerror ("fopen `%s': %s", file, strerror (errno));
445     else
446     {
447     if (value)
448     fprintf (fp, "%s", value);
449     fclose (fp);
450     retval = true;
451     }
452    
453     free (path);
454     free (file);
455     return (retval);
456     }
457    
458     static pid_t _exec_service (const char *service, const char *arg)
459     {
460     char *file;
461     char *fifo;
462     pid_t pid = -1;
463     pid_t savedpid;
464     int status;
465    
466     file = rc_resolve_service (service);
467     if (! rc_is_file (file))
468     {
469     rc_mark_service (service, rc_service_stopped);
470     free (file);
471     return (0);
472     }
473    
474     /* We create a fifo so that other services can wait until we complete */
475 uberlord 2549 fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename (service),
476     (char *) NULL);
477 uberlord 2547
478     if (mkfifo (fifo, 0600) != 0 && errno != EEXIST)
479     {
480     eerror ("unable to create fifo `%s': %s", fifo, strerror (errno));
481     free (fifo);
482     free (file);
483     return (-1);
484     }
485    
486     if ((pid = fork ()) == 0)
487     {
488     char *myarg = strdup (arg);
489     int e = 0;
490 uberlord 2549 execl (file, file, myarg, (char *) NULL);
491 uberlord 2547 e = errno;
492     free (myarg);
493     unlink (fifo);
494     free (fifo);
495     eerrorx ("unable to exec `%s': %s", file, strerror (errno));
496     }
497    
498     free (fifo);
499     free (file);
500    
501     if (pid == -1)
502     {
503     eerror ("unable to fork: %s", strerror (errno));
504     return (pid);
505     }
506    
507     if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
508     return (pid);
509    
510     savedpid = pid;
511     errno = 0;
512     do
513     {
514     pid = waitpid (savedpid, &status, 0);
515     if (pid < 0)
516     {
517     if (errno != ECHILD)
518     eerror ("waitpid %d: %s", savedpid, strerror (errno));
519     return (-1);
520     }
521     } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
522    
523     return (0);
524     }
525    
526     pid_t rc_stop_service (const char *service)
527     {
528     if (rc_service_state (service, rc_service_stopped))
529     return (0);
530    
531     return (_exec_service (service, "stop"));
532     }
533    
534    
535     pid_t rc_start_service (const char *service)
536     {
537     if (! rc_service_state (service, rc_service_stopped))
538     return (0);
539    
540     return (_exec_service (service, "start"));
541     }
542    
543     void rc_schedule_start_service (const char *service,
544     const char *service_to_start)
545     {
546     char *dir;
547     char *init;
548     char *file;
549    
550     if (! rc_service_exists (service) || ! rc_service_exists (service_to_start))
551     return;
552    
553 uberlord 2549 dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service),
554     (char *) NULL);
555 uberlord 2547 if (! rc_is_dir (dir))
556     if (mkdir (dir, 0755) != 0)
557     {
558     eerror ("mkdir `%s': %s", dir, strerror (errno));
559     free (dir);
560     return;
561     }
562    
563     init = rc_resolve_service (service_to_start);
564 uberlord 2549 file = rc_strcatpaths (dir, basename (service_to_start), (char *) NULL);
565 uberlord 2547 if (! rc_exists (file) && symlink (init, file) != 0)
566     eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
567    
568     free (init);
569     free (file);
570     free (dir);
571     }
572    
573     void rc_schedule_clear (const char *service)
574     {
575 uberlord 2549 char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service),
576     (char *) NULL);
577 uberlord 2547
578     if (rc_is_dir (dir))
579     rc_rm_dir (dir, true);
580     free (dir);
581     }
582    
583     bool rc_wait_service (const char *service)
584     {
585 uberlord 2549 char *fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename (service),
586     (char *) NULL);
587 uberlord 2547 struct timeval tv;
588     struct timeval stopat;
589     struct timeval now;
590     bool retval = false;
591    
592     if (gettimeofday (&stopat, NULL) != 0)
593     {
594     eerror ("gettimeofday: %s", strerror (errno));
595     return (false);
596     }
597     stopat.tv_sec += WAIT_MAX;
598    
599     while (true)
600     {
601     if (! rc_exists (fifo))
602     {
603     retval = true;
604     break;
605     }
606    
607     tv.tv_sec = 0;
608     tv.tv_usec = WAIT_INTERVAL;
609     if (select (0, 0, 0, 0, &tv) < 0)
610     {
611     if (errno != EINTR)
612     eerror ("select: %s",strerror (errno));
613     break;
614     }
615    
616     /* Don't hang around forever */
617     if (gettimeofday (&now, NULL) != 0)
618     {
619     eerror ("gettimeofday: %s", strerror (errno));
620     break;
621     }
622     if (timercmp (&now, &stopat, >))
623     break;
624     }
625    
626     free (fifo);
627     return (retval);
628     }
629    
630     char **rc_services_in_runlevel (const char *runlevel)
631     {
632     char *dir;
633     char **list = NULL;
634    
635     if (! runlevel)
636     return (rc_ls_dir (NULL, RC_INITDIR, RC_LS_INITD));
637    
638     /* These special levels never contain any services */
639     if (strcmp (runlevel, RC_LEVEL_SYSINIT) == 0 ||
640     strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
641     return (NULL);
642    
643 uberlord 2549 dir = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, (char *) NULL);
644 uberlord 2547 if (! rc_is_dir (dir))
645     eerror ("runlevel `%s' does not exist", runlevel);
646     else
647     list = rc_ls_dir (list, dir, RC_LS_INITD);
648    
649     free (dir);
650     return (list);
651     }
652    
653     char **rc_services_in_state (rc_service_state_t state)
654     {
655 uberlord 2549 char *dir = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state],
656     (char *) NULL);
657 uberlord 2547 char **list = NULL;
658    
659     if (rc_is_dir (dir))
660     list = rc_ls_dir (list, dir, RC_LS_INITD);
661    
662     free (dir);
663     return (list);
664     }
665    
666     bool rc_service_add (const char *runlevel, const char *service)
667     {
668     bool retval;
669     char *init;
670     char *file;
671    
672     if (! rc_runlevel_exists (runlevel))
673     {
674     errno = ENOENT;
675     return (false);
676     }
677    
678     if (rc_service_in_runlevel (service, runlevel))
679     {
680     errno = EEXIST;
681     return (false);
682     }
683    
684     init = rc_resolve_service (service);
685 uberlord 2549 file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service),
686     (char *) NULL);
687 uberlord 2547 retval = (symlink (init, file) == 0);
688     free (init);
689     free (file);
690     return (retval);
691     }
692    
693     bool rc_service_delete (const char *runlevel, const char *service)
694     {
695     char *file;
696     bool retval = false;
697    
698     if (! runlevel || ! service)
699     return (false);
700    
701 uberlord 2549 file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service),
702     (char *) NULL);
703 uberlord 2547 if (unlink (file) == 0)
704     retval = true;
705    
706     free (file);
707     return (retval);
708     }
709    
710     char **rc_services_scheduled_by (const char *service)
711     {
712     char **dirs = rc_ls_dir (NULL, RC_SVCDIR "scheduled", 0);
713     char **list = NULL;
714     char *dir;
715     int i;
716    
717     STRLIST_FOREACH (dirs, dir, i)
718     {
719 uberlord 2549 char *file = rc_strcatpaths (RC_SVCDIR "scheduled", dir, service,
720     (char *) NULL);
721 uberlord 2547 if (rc_exists (file))
722     list = rc_strlist_add (list, file);
723     free (file);
724     }
725     rc_strlist_free (dirs);
726    
727     return (list);
728     }
729    
730     char **rc_services_scheduled (const char *service)
731     {
732 uberlord 2549 char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service),
733     (char *) NULL);
734 uberlord 2547 char **list = NULL;
735    
736     if (rc_is_dir (dir))
737     list = rc_ls_dir (list, dir, RC_LS_INITD);
738    
739     free (dir);
740     return (list);
741     }
742    
743     bool rc_allow_plug (char *service)
744     {
745     char *list;
746     char *p;
747     char *star;
748     char *token;
749     bool allow = true;
750     char *match = getenv ("RC_PLUG_SERVICES");
751     if (! match)
752     return true;
753    
754     list = strdup (match);
755     p = list;
756     while ((token = strsep (&p, " ")))
757     {
758     bool truefalse = true;
759     if (token[0] == '!')
760     {
761     truefalse = false;
762     token++;
763     }
764    
765     star = strchr (token, '*');
766     if (star)
767     {
768     if (strncmp (service, token, star - token) == 0)
769     {
770     allow = truefalse;
771     break;
772     }
773     }
774     else
775     {
776     if (strcmp (service, token) == 0)
777     {
778     allow = truefalse;
779     break;
780     }
781     }
782     }
783    
784     free (list);
785     return (allow);
786     }

  ViewVC Help
Powered by ViewVC 1.1.20