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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2547 - (show annotations) (download) (as text)
Thu Apr 5 11:18:42 2007 UTC (7 years, 4 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 /*
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