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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20