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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20