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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20