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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20