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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20