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

Contents of /trunk/src/librc.c

Parent Directory Parent Directory | Revision Log Revision Log


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

  ViewVC Help
Powered by ViewVC 1.1.20