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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2641 - (show annotations) (download) (as text)
Fri Apr 20 18:58:42 2007 UTC (7 years, 3 months ago) by uberlord
File MIME type: text/x-csrc
File size: 17774 byte(s)
Re-work the starting and stopping of services so we always know the pid so we can kill our children without the need for process groups which we cannot use as keyboard will not work in init.
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 int e = 0;
472 execl (file, file, arg, (char *) NULL);
473 e = errno;
474 unlink (fifo);
475 free (fifo);
476 eerror ("unable to exec `%s': %s", file, strerror (errno));
477 free (file);
478 _exit (EXIT_FAILURE);
479 }
480
481 free (fifo);
482 free (file);
483
484 if (pid == -1)
485 eerror ("vfork: %s", strerror (errno));
486
487 return (pid);
488 }
489
490 int rc_waitpid (pid_t pid) {
491 int status = 0;
492 pid_t savedpid = pid;
493
494 errno = 0;
495 do {
496 pid = waitpid (savedpid, &status, 0);
497 if (pid < 0) {
498 if (errno != ECHILD)
499 eerror ("waitpid %d: %s", savedpid, strerror (errno));
500 return (-1);
501 }
502 } while (! WIFEXITED (status) && ! WIFSIGNALED (status));
503
504 return (WIFEXITED (status) ? WEXITSTATUS (status) : EXIT_FAILURE);
505 }
506
507 pid_t rc_stop_service (const char *service)
508 {
509 if (rc_service_state (service, rc_service_stopped))
510 return (0);
511
512 return (_exec_service (service, "stop"));
513 }
514 librc_hidden_def(rc_stop_service)
515
516 pid_t rc_start_service (const char *service)
517 {
518 if (! rc_service_state (service, rc_service_stopped))
519 return (0);
520
521 return (_exec_service (service, "start"));
522 }
523 librc_hidden_def(rc_start_service)
524
525 void rc_schedule_start_service (const char *service,
526 const char *service_to_start)
527 {
528 char *dir;
529 char *init;
530 char *file;
531 char *svc;
532
533 /* service may be a provided service, like net */
534 if (! service || ! rc_service_exists (service_to_start))
535 return;
536
537 svc = rc_xstrdup (service);
538 dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (svc),
539 (char *) NULL);
540 free (svc);
541 if (! rc_is_dir (dir))
542 if (mkdir (dir, 0755) != 0) {
543 eerror ("mkdir `%s': %s", dir, strerror (errno));
544 free (dir);
545 return;
546 }
547
548 init = rc_resolve_service (service_to_start);
549 svc = rc_xstrdup (service_to_start);
550 file = rc_strcatpaths (dir, basename (svc), (char *) NULL);
551 free (svc);
552 if (! rc_exists (file) && symlink (init, file) != 0)
553 eerror ("symlink `%s' to `%s': %s", init, file, strerror (errno));
554
555 free (init);
556 free (file);
557 free (dir);
558 }
559 librc_hidden_def(rc_schedule_start_service)
560
561 void rc_schedule_clear (const char *service)
562 {
563 char *svc = rc_xstrdup (service);
564 char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (svc),
565 (char *) NULL);
566
567 free (svc);
568 if (rc_is_dir (dir))
569 rc_rm_dir (dir, true);
570 free (dir);
571 }
572 librc_hidden_def(rc_schedule_clear)
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 librc_hidden_def(rc_wait_service)
619
620 char **rc_services_in_runlevel (const char *runlevel)
621 {
622 char *dir;
623 char **list = NULL;
624
625 if (! runlevel)
626 return (rc_ls_dir (NULL, RC_INITDIR, RC_LS_INITD));
627
628 /* These special levels never contain any services */
629 if (strcmp (runlevel, RC_LEVEL_SYSINIT) == 0 ||
630 strcmp (runlevel, RC_LEVEL_SINGLE) == 0)
631 return (NULL);
632
633 dir = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, (char *) NULL);
634 if (! rc_is_dir (dir))
635 eerror ("runlevel `%s' does not exist", runlevel);
636 else
637 list = rc_ls_dir (list, dir, RC_LS_INITD);
638
639 free (dir);
640 return (list);
641 }
642 librc_hidden_def(rc_services_in_runlevel)
643
644 char **rc_services_in_state (rc_service_state_t state)
645 {
646 char *dir = rc_strcatpaths (RC_SVCDIR, rc_service_state_names[state],
647 (char *) NULL);
648 char **list = NULL;
649
650 if (state == rc_service_scheduled) {
651 char **dirs = rc_ls_dir (NULL, dir, 0);
652 char *d;
653 int i;
654
655 STRLIST_FOREACH (dirs, d, i) {
656 char *p = rc_strcatpaths (dir, d, (char *) NULL);
657 char **entries = rc_ls_dir (NULL, p, RC_LS_INITD);
658 char *e;
659 int j;
660
661 STRLIST_FOREACH (entries, e, j)
662 list = rc_strlist_addsortu (list, e);
663
664 if (entries)
665 free (entries);
666 }
667
668 if (dirs)
669 free (dirs);
670 } else {
671 if (rc_is_dir (dir))
672 list = rc_ls_dir (list, dir, RC_LS_INITD);
673 }
674
675 free (dir);
676 return (list);
677 }
678 librc_hidden_def(rc_services_in_state)
679
680 bool rc_service_add (const char *runlevel, const char *service)
681 {
682 bool retval;
683 char *init;
684 char *file;
685 char *svc;
686
687 if (! rc_runlevel_exists (runlevel)) {
688 errno = ENOENT;
689 return (false);
690 }
691
692 if (rc_service_in_runlevel (service, runlevel)) {
693 errno = EEXIST;
694 return (false);
695 }
696
697 init = rc_resolve_service (service);
698 svc = rc_xstrdup (service);
699 file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (svc),
700 (char *) NULL);
701 free (svc);
702 retval = (symlink (init, file) == 0);
703 free (init);
704 free (file);
705 return (retval);
706 }
707 librc_hidden_def(rc_service_add)
708
709 bool rc_service_delete (const char *runlevel, const char *service)
710 {
711 char *file;
712 char *svc;
713 bool retval = false;
714
715 if (! runlevel || ! service)
716 return (false);
717
718 svc = rc_xstrdup (service);
719 file = rc_strcatpaths (RC_RUNLEVELDIR, runlevel, basename (svc),
720 (char *) NULL);
721 free (svc);
722 if (unlink (file) == 0)
723 retval = true;
724
725 free (file);
726 return (retval);
727 }
728 librc_hidden_def(rc_service_delete)
729
730 char **rc_services_scheduled_by (const char *service)
731 {
732 char **dirs = rc_ls_dir (NULL, RC_SVCDIR "scheduled", 0);
733 char **list = NULL;
734 char *dir;
735 int i;
736
737 STRLIST_FOREACH (dirs, dir, i) {
738 char *file = rc_strcatpaths (RC_SVCDIR "scheduled", dir, service,
739 (char *) NULL);
740 if (rc_exists (file))
741 list = rc_strlist_add (list, file);
742 free (file);
743 }
744 rc_strlist_free (dirs);
745
746 return (list);
747 }
748 librc_hidden_def(rc_services_scheduled_by)
749
750 char **rc_services_scheduled (const char *service)
751 {
752 char *svc = rc_xstrdup (service);
753 char *dir = rc_strcatpaths (RC_SVCDIR, "scheduled", basename (svc),
754 (char *) NULL);
755 char **list = NULL;
756
757 if (rc_is_dir (dir))
758 list = rc_ls_dir (list, dir, RC_LS_INITD);
759
760 free (svc);
761 free (dir);
762 return (list);
763 }
764 librc_hidden_def(rc_services_scheduled)
765
766 bool rc_allow_plug (char *service)
767 {
768 char *list;
769 char *p;
770 char *star;
771 char *token;
772 bool allow = true;
773 char *match = getenv ("RC_PLUG_SERVICES");
774 if (! match)
775 return true;
776
777 list = rc_xstrdup (match);
778 p = list;
779 while ((token = strsep (&p, " "))) {
780 bool truefalse = true;
781 if (token[0] == '!') {
782 truefalse = false;
783 token++;
784 }
785
786 star = strchr (token, '*');
787 if (star) {
788 if (strncmp (service, token, star - token) == 0) {
789 allow = truefalse;
790 break;
791 }
792 } else {
793 if (strcmp (service, token) == 0) {
794 allow = truefalse;
795 break;
796 }
797 }
798 }
799
800 free (list);
801 return (allow);
802 }
803 librc_hidden_def(rc_allow_plug)

  ViewVC Help
Powered by ViewVC 1.1.20