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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20