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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2547 - (hide annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (7 years, 8 months ago) by uberlord
File MIME type: text/x-csrc
File size: 16659 byte(s)
    Rewrite the core parts in C. We now provide librc so other programs can
    query runlevels, services and state without using bash. We also provide
    libeinfo so other programs can easily use our informational functions.

    As such, we have dropped the requirement of using bash as the init script
    shell. We now use /bin/sh and have strived to make the scripts as portable
    as possible. Shells that work are bash and dash. busybox works provided
    you disable s-s-d. If you have WIPE_TMP set to yes in conf.d/bootmisc you
    should disable find too.
    zsh and ksh do not work at this time.

    Networking support is currently being re-vamped also as it was heavily bash
    array based. As such, a new config format is available like so
    config_eth0="1.2.3.4/24 5.6.7.8/16"
    or like so
    config_eth0="'1.2.3.4 netmask 255.255.255.0' '5.6.7.8 netmask 255.255.0.0'"

    We will still support the old bash array format provided that /bin/sh IS
    a link it bash.

    ChangeLog for baselayout-1 can be found in our SVN repo.
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     char *path = rc_strcatpaths (RC_RUNLEVELDIR, dir, NULL);
73     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     path = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, NULL);
125     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     file = rc_strcatpaths (RC_SVCDIR, "started", service, NULL);
144     if (! rc_is_link (file))
145     {
146     free (file);
147     file = rc_strcatpaths (RC_SVCDIR, "inactive", service, NULL);
148     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     file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service), NULL);
204     retval = rc_exists (file);
205     free (file);
206    
207     return (retval);
208     }
209    
210     bool rc_mark_service (const char *service, const rc_service_state_t state)
211     {
212     char *file;
213     int i = 0;
214     int skip_state = -1;
215     char *base;
216     char *init = rc_resolve_service (service);
217     bool skip_wasinactive = false;
218    
219     if (! service)
220     return (false);
221    
222     base = basename (service);
223    
224     if (state != rc_service_stopped)
225     {
226     if (! rc_is_file(init))
227     {
228     free (init);
229     return (false);
230     }
231    
232     file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state], base, NULL);
233     if (rc_exists (file))
234     unlink (file);
235     i = symlink (init, file);
236     if (i != 0)
237     {
238     free (file);
239     free (init);
240     einfo ("%d %s %s", state, rc_service_state_names[state], base);
241     eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
242     return (false);
243     }
244    
245     free (file);
246     skip_state = state;
247     }
248    
249     if (state == rc_service_coldplugged)
250     {
251     free (init);
252     return (true);
253     }
254    
255     /* Remove any old states now */
256     i = 0;
257     while (rc_service_state_names[i])
258     {
259     if ((i != skip_state &&
260     i != rc_service_stopped &&
261     i != rc_service_coldplugged &&
262     i != rc_service_crashed) &&
263     (! skip_wasinactive || i != rc_service_wasinactive))
264     {
265     file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[i], base, NULL);
266     if (rc_exists (file))
267     {
268     if ((state == rc_service_starting ||
269     state == rc_service_stopping) &&
270     i == rc_service_inactive)
271     {
272     char *wasfile = rc_strcatpaths (RC_SVCDIR,
273     rc_service_state_names[rc_service_wasinactive],
274     base, NULL);
275    
276     if (symlink (init, wasfile) != 0)
277     eerror ("symlink `%s' to `%s': %s", init, wasfile,
278     strerror (errno));
279    
280     skip_wasinactive = true;
281     free (wasfile);
282     }
283    
284     errno = 0;
285     if (unlink (file) != 0 && errno != ENOENT)
286     eerror ("failed to delete `%s': %s", file,
287     strerror (errno));
288     }
289     free (file);
290     }
291     i++;
292     }
293    
294     /* Remove the exclusive state if we're inactive */
295     if (state == rc_service_started ||
296     state == rc_service_stopped ||
297     state == rc_service_inactive)
298     {
299     file = rc_strcatpaths (RC_SVCDIR, "exclusive", base, NULL);
300     if (rc_exists (file))
301     if (unlink (file) != 0)
302     eerror ("unlink `%s': %s", file, strerror (errno));
303     free (file);
304     }
305    
306     /* Remove any options and daemons the service may have stored */
307     if (state == rc_service_stopped)
308     {
309     char *dir = rc_strcatpaths (RC_SVCDIR, "options", base, NULL);
310    
311     if (rc_is_dir (dir))
312     rc_rm_dir (dir, true);
313     free (dir);
314    
315     dir = rc_strcatpaths (RC_SVCDIR, "daemons", base, NULL);
316     if (rc_is_dir (dir))
317     rc_rm_dir (dir, true);
318     free (dir);
319    
320     rc_schedule_clear (service);
321     }
322    
323     /* These are final states, so remove us from scheduled */
324     if (state == rc_service_started || state == rc_service_stopped)
325     {
326     char *sdir = rc_strcatpaths (RC_SVCDIR, "scheduled", NULL);
327     char **dirs = rc_ls_dir (NULL, sdir, 0);
328     char *dir;
329     int serrno;
330    
331     STRLIST_FOREACH (dirs, dir, i)
332     {
333     char *bdir = rc_strcatpaths (sdir, dir, NULL);
334     file = rc_strcatpaths (bdir, base, NULL);
335     if (rc_exists (file))
336     if (unlink (file) != 0)
337     eerror ("unlink `%s': %s", file, strerror (errno));
338     free (file);
339    
340     /* Try and remove the dir - we don't care about errors */
341     serrno = errno;
342     rmdir (bdir);
343     errno = serrno;
344     free (bdir);
345     }
346     rc_strlist_free (dirs);
347     free (sdir);
348     }
349    
350     free (init);
351     return (true);
352     }
353    
354     bool rc_service_state (const char *service, const rc_service_state_t state)
355     {
356     char *file;
357     bool retval;
358    
359     /* If the init script does not exist then we are stopped */
360     if (! rc_service_exists (service))
361     return (state == rc_service_stopped ? true : false);
362    
363     /* We check stopped state by not being in any of the others */
364     if (state == rc_service_stopped)
365     return ( ! (rc_service_state (service, rc_service_started) ||
366     rc_service_state (service, rc_service_starting) ||
367     rc_service_state (service, rc_service_stopping) ||
368     rc_service_state (service, rc_service_inactive)));
369    
370     /* The crashed state and scheduled states are virtual */
371     if (state == rc_service_crashed)
372     return (rc_service_daemons_crashed (service));
373     else if (state == rc_service_scheduled)
374     {
375     char **services = rc_services_scheduled_by (service);
376     retval = (services);
377     if (services)
378     free (services);
379     return (retval);
380     }
381    
382     /* Now we just check if a file by the service name rc_exists
383     in the state dir */
384     file = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state],
385     basename (service), NULL);
386     retval = rc_exists (file);
387     free (file);
388     return (retval);
389     }
390    
391     bool rc_get_service_option (const char *service, const char *option,
392     char *value)
393     {
394     FILE *fp;
395     char buffer[1024];
396     char *file = rc_strcatpaths (RC_SVCDIR, "options", service, option, NULL);
397     bool retval = false;
398    
399     if (rc_exists (file))
400     {
401     if ((fp = fopen (file, "r")) == NULL)
402     eerror ("fopen `%s': %s", file, strerror (errno));
403     else
404     {
405     memset (buffer, 0, sizeof (buffer));
406     while (fgets (buffer, RC_LINEBUFFER, fp))
407     {
408     memcpy (value, buffer, strlen (buffer));
409     value += strlen (buffer);
410     }
411     fclose (fp);
412     retval = true;
413     }
414     }
415    
416     free (file);
417     return (retval);
418     }
419    
420     bool rc_set_service_option (const char *service, const char *option,
421     const char *value)
422     {
423     FILE *fp;
424     char *path = rc_strcatpaths (RC_SVCDIR, "options", service, NULL);
425     char *file = rc_strcatpaths (path, option, NULL);
426     bool retval = false;
427    
428     if (! rc_is_dir (path))
429     {
430     if (mkdir (path, 0755) != 0)
431     {
432     eerror ("mkdir `%s': %s", path, strerror (errno));
433     free (path);
434     free (file);
435     return (false);
436     }
437     }
438    
439     if ((fp = fopen (file, "w")) == NULL)
440     eerror ("fopen `%s': %s", file, strerror (errno));
441     else
442     {
443     if (value)
444     fprintf (fp, "%s", value);
445     fclose (fp);
446     retval = true;
447     }
448    
449     free (path);
450     free (file);
451     return (retval);
452     }
453    
454     static pid_t _exec_service (const char *service, const char *arg)
455     {
456     char *file;
457     char *fifo;
458     pid_t pid = -1;
459     pid_t savedpid;
460     int status;
461    
462     file = rc_resolve_service (service);
463     if (! rc_is_file (file))
464     {
465     rc_mark_service (service, rc_service_stopped);
466     free (file);
467     return (0);
468     }
469    
470     /* We create a fifo so that other services can wait until we complete */
471     fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename (service), NULL);
472    
473     if (mkfifo (fifo, 0600) != 0 && errno != EEXIST)
474     {
475     eerror ("unable to create fifo `%s': %s", fifo, strerror (errno));
476     free (fifo);
477     free (file);
478     return (-1);
479     }
480    
481     if ((pid = fork ()) == 0)
482     {
483     char *myarg = strdup (arg);
484     int e = 0;
485     execl (file, file, myarg, NULL);
486     e = errno;
487     free (myarg);
488     unlink (fifo);
489     free (fifo);
490     eerrorx ("unable to exec `%s': %s", file, strerror (errno));
491     }
492    
493     free (fifo);
494     free (file);
495    
496     if (pid == -1)
497     {
498     eerror ("unable to fork: %s", strerror (errno));
499     return (pid);
500     }
501    
502     if (rc_is_env ("RC_PARALLEL_STARTUP", "yes"))
503     return (pid);
504    
505     savedpid = pid;
506     errno = 0;
507     do
508     {
509     pid = waitpid (savedpid, &status, 0);
510     if (pid < 0)
511     {
512     if (errno != ECHILD)
513     eerror ("waitpid %d: %s", savedpid, strerror (errno));
514     return (-1);
515     }
516     } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
517    
518     return (0);
519     }
520    
521     pid_t rc_stop_service (const char *service)
522     {
523     if (rc_service_state (service, rc_service_stopped))
524     return (0);
525    
526     return (_exec_service (service, "stop"));
527     }
528    
529    
530     pid_t rc_start_service (const char *service)
531     {
532     if (! rc_service_state (service, rc_service_stopped))
533     return (0);
534    
535     return (_exec_service (service, "start"));
536     }
537    
538     void rc_schedule_start_service (const char *service,
539     const char *service_to_start)
540     {
541     char *dir;
542     char *init;
543     char *file;
544    
545     if (! rc_service_exists (service) || ! rc_service_exists (service_to_start))
546     return;
547    
548     dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service), NULL);
549     if (! rc_is_dir (dir))
550     if (mkdir (dir, 0755) != 0)
551     {
552     eerror ("mkdir `%s': %s", dir, strerror (errno));
553     free (dir);
554     return;
555     }
556    
557     init = rc_resolve_service (service_to_start);
558     file = rc_strcatpaths (dir, basename (service_to_start), NULL);
559     if (! rc_exists (file) && symlink (init, file) != 0)
560     eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
561    
562     free (init);
563     free (file);
564     free (dir);
565     }
566    
567     void rc_schedule_clear (const char *service)
568     {
569     char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service), NULL);
570    
571     if (rc_is_dir (dir))
572     rc_rm_dir (dir, true);
573     free (dir);
574     }
575    
576     bool rc_wait_service (const char *service)
577     {
578     char *fifo = rc_strcatpaths (RC_SVCDIR, "exclusive", basename (service), NULL);
579     struct timeval tv;
580     struct timeval stopat;
581     struct timeval now;
582     bool retval = false;
583    
584     if (gettimeofday (&stopat, NULL) != 0)
585     {
586     eerror ("gettimeofday: %s", strerror (errno));
587     return (false);
588     }
589     stopat.tv_sec += WAIT_MAX;
590    
591     while (true)
592     {
593     if (! rc_exists (fifo))
594     {
595     retval = true;
596     break;
597     }
598    
599     tv.tv_sec = 0;
600     tv.tv_usec = WAIT_INTERVAL;
601     if (select (0, 0, 0, 0, &tv) < 0)
602     {
603     if (errno != EINTR)
604     eerror ("select: %s",strerror (errno));
605     break;
606     }
607    
608     /* Don't hang around forever */
609     if (gettimeofday (&now, NULL) != 0)
610     {
611     eerror ("gettimeofday: %s", strerror (errno));
612     break;
613     }
614     if (timercmp (&now, &stopat, >))
615     break;
616     }
617    
618     free (fifo);
619     return (retval);
620     }
621    
622     char **rc_services_in_runlevel (const char *runlevel)
623     {
624     char *dir;
625     char **list = NULL;
626    
627     if (! runlevel)
628     return (rc_ls_dir (NULL, RC_INITDIR, RC_LS_INITD));
629    
630     /* These special levels never contain any services */
631     if (strcmp (runlevel, RC_LEVEL_SYSINIT) == 0 ||
632     strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
633     return (NULL);
634    
635     dir = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, NULL);
636     if (! rc_is_dir (dir))
637     eerror ("runlevel `%s' does not exist", runlevel);
638     else
639     list = rc_ls_dir (list, dir, RC_LS_INITD);
640    
641     free (dir);
642     return (list);
643     }
644    
645     char **rc_services_in_state (rc_service_state_t state)
646     {
647     char *dir = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state], NULL);
648     char **list = NULL;
649    
650     if (rc_is_dir (dir))
651     list = rc_ls_dir (list, dir, RC_LS_INITD);
652    
653     free (dir);
654     return (list);
655     }
656    
657     bool rc_service_add (const char *runlevel, const char *service)
658     {
659     bool retval;
660     char *init;
661     char *file;
662    
663     if (! rc_runlevel_exists (runlevel))
664     {
665     errno = ENOENT;
666     return (false);
667     }
668    
669     if (rc_service_in_runlevel (service, runlevel))
670     {
671     errno = EEXIST;
672     return (false);
673     }
674    
675     init = rc_resolve_service (service);
676     file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service), NULL);
677     retval = (symlink (init, file) == 0);
678     free (init);
679     free (file);
680     return (retval);
681     }
682    
683     bool rc_service_delete (const char *runlevel, const char *service)
684     {
685     char *file;
686     bool retval = false;
687    
688     if (! runlevel || ! service)
689     return (false);
690    
691     file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (service), NULL);
692     if (unlink (file) == 0)
693     retval = true;
694    
695     free (file);
696     return (retval);
697     }
698    
699     char **rc_services_scheduled_by (const char *service)
700     {
701     char **dirs = rc_ls_dir (NULL, RC_SVCDIR "scheduled", 0);
702     char **list = NULL;
703     char *dir;
704     int i;
705    
706     STRLIST_FOREACH (dirs, dir, i)
707     {
708     char *file = rc_strcatpaths (RC_SVCDIR "scheduled", dir, service, NULL);
709     if (rc_exists (file))
710     list = rc_strlist_add (list, file);
711     free (file);
712     }
713     rc_strlist_free (dirs);
714    
715     return (list);
716     }
717    
718     char **rc_services_scheduled (const char *service)
719     {
720     char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (service), NULL);
721     char **list = NULL;
722    
723     if (rc_is_dir (dir))
724     list = rc_ls_dir (list, dir, RC_LS_INITD);
725    
726     free (dir);
727     return (list);
728     }
729    
730     bool rc_allow_plug (char *service)
731     {
732     char *list;
733     char *p;
734     char *star;
735     char *token;
736     bool allow = true;
737     char *match = getenv ("RC_PLUG_SERVICES");
738     if (! match)
739     return true;
740    
741     list = strdup (match);
742     p = list;
743     while ((token = strsep (&p, " ")))
744     {
745     bool truefalse = true;
746     if (token[0] == '!')
747     {
748     truefalse = false;
749     token++;
750     }
751    
752     star = strchr (token, '*');
753     if (star)
754     {
755     if (strncmp (service, token, star - token) == 0)
756     {
757     allow = truefalse;
758     break;
759     }
760     }
761     else
762     {
763     if (strcmp (service, token) == 0)
764     {
765     allow = truefalse;
766     break;
767     }
768     }
769     }
770    
771     free (list);
772     return (allow);
773     }

  ViewVC Help
Powered by ViewVC 1.1.20