1 /* This file is part of GNU Pies.
2 Copyright (C) 2016-2020 Sergey Poznyakoff
3
4 GNU Pies is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 GNU Pies is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with GNU Pies. If not, see <http://www.gnu.org/licenses/>. */
16
17 #include "pies.h"
18 #include "prog.h"
19 #include <assert.h>
20
21 struct complist
22 {
23 struct component *head;
24 struct component *tail;
25 };
26
27 static struct complist comp_list[2];
28 static int cur;
29
30 static struct component **comp_array;
31 static size_t comp_count;
32
33 static pies_depmap_t depmap;
34
35 static inline int
next_index(void)36 next_index (void)
37 {
38 return (cur + 1) % ARRAY_SIZE (comp_list);
39 }
40
41 static inline int
prev_index(void)42 prev_index (void)
43 {
44 return (cur + ARRAY_SIZE (comp_list) - 1) % ARRAY_SIZE (comp_list);
45 }
46
47 void
component_link(struct component * comp,struct component * ref)48 component_link (struct component *comp, struct component *ref)
49 {
50 if (!ref)
51 {
52 struct complist *list = &comp_list[comp->listidx];
53
54 comp->prev = NULL;
55 comp->next = list->head;
56 if (list->head)
57 list->head->prev = comp;
58 else
59 list->tail = comp;
60 list->head = comp;
61 }
62 else
63 {
64 struct complist *list = &comp_list[comp->listidx];
65 struct component *x;
66
67 assert (comp->listidx == ref->listidx);
68
69 comp->prev = ref;
70 comp->next = ref->next;
71
72 if ((x = ref->next))
73 x->prev = comp;
74 else
75 list->tail = comp;
76
77 ref->next = comp;
78 }
79 }
80
81 void
component_append(struct component * comp)82 component_append (struct component *comp)
83 {
84 component_link (comp, comp_list[comp->listidx].tail);
85 }
86
87 void
comp_array_remove(size_t i)88 comp_array_remove (size_t i)
89 {
90 struct component *comp = comp_array[i];
91
92 depmap_remove (depmap, i);
93 while (i < comp_count -1)
94 {
95 comp_array[i] = comp_array[i+1];
96 comp_array[i]->arridx = i;
97 i++;
98 }
99 component_free (comp);
100 comp_count--;
101 }
102
103 void
component_unlink(struct component * comp)104 component_unlink (struct component *comp)
105 {
106 struct complist *list = &comp_list[comp->listidx];
107 struct component *x;
108
109 if ((x = comp->prev))
110 x->next = comp->next;
111 else
112 list->head = comp->next;
113 if ((x = comp->next))
114 x->prev = comp->prev;
115 else
116 list->tail = comp->prev;
117 }
118
119 int
component_list_is_empty(void)120 component_list_is_empty (void)
121 {
122 return !comp_list[cur].head;
123 }
124
125 struct component *
component_lookup_tag(int idx,const char * tag)126 component_lookup_tag (int idx, const char *tag)
127 {
128 struct complist *list = &comp_list[idx];
129 struct component *comp;
130
131 for (comp = list->head; comp; comp = comp->next)
132 if (strcmp (comp->tag, tag) == 0)
133 break;
134
135 return comp;
136 }
137
138 ssize_t
component_lookup_index(const char * tag)139 component_lookup_index (const char *tag)
140 {
141 size_t i;
142
143 for (i = 0; i < comp_count; i++)
144 if (strcmp (comp_array[i]->tag, tag) == 0)
145 return i;
146 return -1;
147 }
148
149 struct component *
component_create(const char * name)150 component_create (const char *name)
151 {
152 struct component *comp = component_lookup_tag (cur, name);
153 if (!comp)
154 {
155 comp = grecs_zalloc (sizeof (*comp));
156 comp->listidx = cur;
157 comp->redir[RETR_OUT].type = comp->redir[RETR_ERR].type = redir_null;
158 comp->tag = grecs_strdup (name);
159 comp->socket_type = SOCK_STREAM;
160 component_append (comp);
161 }
162 return comp;
163 }
164
165 void
component_free(struct component * comp)166 component_free (struct component *comp)
167 {
168 component_unlink (comp);
169 free (comp->tag);
170 free (comp->program);
171 free (comp->command);
172 argv_free (comp->argv);
173 envop_free (comp->envop);
174 free (comp->dir);
175 grecs_list_free (comp->prereq);
176 grecs_list_free (comp->depend);
177 free (comp->rmfile);
178 pies_privs_free (&comp->privs);
179 free (comp->runlevels);
180 free_limits (comp->limits);
181 free (comp->service);
182 pies_url_destroy (&comp->socket_url);
183 free (comp->pass_fd_socket);
184 free (comp->tcpmux);
185 free (comp->access_denied_message);
186 free (comp->max_instances_message);
187 free (comp->max_ip_connections_message);
188 free_redirector (&comp->redir[0]);
189 free_redirector (&comp->redir[1]);
190 grecs_list_free (comp->act_list);
191 pies_acl_free (comp->acl);
192 pies_acl_free (comp->list_acl);
193 pies_acl_free (comp->adm_acl);
194 free (comp);
195 }
196
197 void
component_ref_incr(struct component * comp)198 component_ref_incr (struct component *comp)
199 {
200 ++comp->ref_count;
201 }
202
203 void
component_ref_decr(struct component * comp)204 component_ref_decr (struct component *comp)
205 {
206 assert (comp->ref_count > 0);
207 if (--comp->ref_count == 0)
208 {
209 if (component_is_active (comp))
210 comp_array_remove (comp->arridx);
211 else
212 component_free (comp);
213 }
214 }
215
216 static int
argvcmp(char ** a,char ** b)217 argvcmp (char **a, char **b)
218 {
219 size_t i;
220
221 if (!a != !b)
222 return 1;
223
224 for (i = 0; a[i]; i++)
225 if (!b[i] || strcmp (b[i], a[i]))
226 return 1;
227 return !!b[i];
228 }
229
230 static int
urlcmp(struct pies_url * a,struct pies_url * b)231 urlcmp (struct pies_url *a, struct pies_url *b)
232 {
233 if (!a)
234 return !!b;
235 else if (!b)
236 return 1;
237 return safe_strcmp (a->string, b->string);
238 }
239
240 static int
redirector_cmp(struct redirector const * a,struct redirector const * b)241 redirector_cmp (struct redirector const *a, struct redirector const *b)
242 {
243 if (a->type != b->type)
244 return 1;
245 switch (a->type)
246 {
247 case redir_null:
248 break;
249
250 case redir_syslog:
251 if (a->v.prio != b->v.prio)
252 return 1;
253 break;
254
255 case redir_file:
256 if (safe_strcmp (a->v.file, b->v.file))
257 return 1;
258 }
259
260 return 0;
261 }
262
263 static int
component_match(struct component * comp,struct component * ref)264 component_match (struct component *comp, struct component *ref)
265 {
266 #define MATCH(cond) do if (cond) return 1; while (0)
267 #define EQ(memb) MATCH (comp->memb != ref->memb)
268 #define FN(memb,fun) MATCH (fun (comp->memb, ref->memb))
269 #define FNP(memb,fun) MATCH (fun (&comp->memb, &ref->memb))
270
271 EQ (mode);
272 FN (tag, safe_strcmp);
273 FN (program, safe_strcmp);
274 EQ (argc);
275 FN (argv, argvcmp);
276 FN (dir, safe_strcmp);
277 FN (prereq, grecs_list_compare);
278 FN (depend, grecs_list_compare);
279 EQ (flags);
280 EQ (max_instances);
281 FN (rmfile, safe_strcmp);
282 FNP (privs, pies_privs_cmp);
283 FN (limits, limits_cmp);
284 FN (runlevels, safe_strcmp);
285 EQ (max_rate);
286 EQ (max_ip_connections);
287 EQ (socket_type);
288 EQ (builtin);
289 FN (service, safe_strcmp);
290 FN (socket_url, urlcmp);
291 FN (pass_fd_socket, safe_strcmp);
292 EQ (pass_fd_timeout);
293 FN (acl, pies_acl_cmp);
294 FN (tcpmux, safe_strcmp);
295 FNP (redir[0], redirector_cmp);
296 FNP (redir[1], redirector_cmp);
297 #undef MATCH
298 #undef EQ
299 #undef FN
300 #undef FNP
301
302 return 0;
303 }
304
305 static struct component *
complist_find_match(int idx,struct component * ref)306 complist_find_match (int idx, struct component *ref)
307 {
308 struct complist *list = &comp_list[idx];
309 struct component *comp;
310
311 for (comp = list->head; comp && component_match (comp, ref);
312 comp = comp->next)
313 ;
314 return comp;
315 }
316
317 static void
strasgn(char ** dst,char ** src)318 strasgn (char **dst, char **src)
319 {
320 free (*dst);
321 *dst = *src;
322 *src = NULL;
323 }
324
325 void
component_merge(struct component * comp,struct component * ref)326 component_merge (struct component *comp, struct component *ref)
327 {
328 strasgn (&comp->tag, &ref->tag);
329
330 strasgn (&comp->access_denied_message, &ref->access_denied_message);
331 strasgn (&comp->max_instances_message, &ref->max_instances_message);
332 strasgn (&comp->max_ip_connections_message,
333 &ref->max_ip_connections_message);
334
335 grecs_list_free (comp->act_list);
336 comp->act_list = ref->act_list;
337 ref->act_list = NULL;
338
339 pies_acl_free (comp->list_acl);
340 comp->list_acl = ref->list_acl;
341 ref->list_acl = NULL;
342
343 pies_acl_free (comp->adm_acl);
344 comp->adm_acl = ref->adm_acl;
345 ref->adm_acl = NULL;
346 }
347
348 int
component_is_active(struct component * comp)349 component_is_active (struct component *comp)
350 {
351 return comp->listidx == cur;
352 }
353
354 void
component_config_begin(void)355 component_config_begin (void)
356 {
357 cur = next_index ();
358 }
359
360 void
component_config_rollback(void)361 component_config_rollback (void)
362 {
363 struct complist *list = &comp_list[cur];
364 while (list->head)
365 component_free (list->head);
366 cur = prev_index ();
367 }
368
369 /* Return true if PROG is a leftover from previous configuration */
370 static int
prog_is_leftover(struct prog * prog)371 prog_is_leftover (struct prog *prog)
372 {
373 return IS_COMPONENT (prog) && !component_is_active (prog->v.p.comp);
374 }
375
376 /* If PROG is a leftover, mark it for termination. If it is a listener,
377 terminate it immediately. This ensures that all decommissioned sockets
378 are closed before the subsequent call to progman_create_sockets, which
379 might need to reopen some of them.
380 */
381 static int
mark_prog(struct prog * prog,void * data)382 mark_prog (struct prog *prog, void *data)
383 {
384 if (prog_is_leftover (prog))
385 {
386 prog->stop = 1;
387 if (prog->v.p.status == status_listener)
388 progman_stop_component (&prog);
389 }
390 return 0;
391 }
392
393 static int
list_str_cmp(const void * a,const void * b)394 list_str_cmp (const void *a, const void *b)
395 {
396 return safe_strcmp (a, b);
397 }
398
399 /* Report cyclic dependency starting at IDX. Mark each element with
400 CF_REMOVE for subsequent removal.
401 DP is a transitive closure of depmap.
402 */
403 static void
report_cyclic_dependency(struct depmap_path * path)404 report_cyclic_dependency (struct depmap_path *path)
405 {
406 struct depmap_path_elem *p;
407 for (p = path->head; p; p = p->next)
408 {
409 logmsg_printf (LOG_NOTICE, "%s -> ", comp_array[p->idx]->tag);
410 comp_array[p->idx]->flags |= CF_REMOVE;
411 }
412 logmsg_printf (LOG_NOTICE, "%s\n", comp_array[path->head->idx]->tag);
413 }
414
415 void
component_build_depmap(void)416 component_build_depmap (void)
417 {
418 struct depmap_path *path;
419 int i;
420
421 depmap_free (depmap);
422 depmap = depmap_alloc (comp_count);
423 for (i = 0; i < comp_count; )
424 {
425 struct component *comp = comp_array[i];
426 struct grecs_list_entry *ep;
427
428 if (comp->prereq)
429 for (ep = comp->prereq->head; ep; ep = ep->next)
430 {
431 char const *tag = ep->data;
432 ssize_t tgt = component_lookup_index (tag);
433 if (tgt < 0)
434 {
435 logmsg (LOG_ERR,
436 _("component %s depends on %s, "
437 "which is not declared"),
438 comp->tag, tag);
439 comp_array[i]->flags |= CF_REMOVE;
440 continue;
441 }
442 depmap_set (depmap, i, tgt);
443 }
444
445 if (comp->depend)
446 for (ep = comp->depend->head; ep; ep = ep->next)
447 {
448 char const *tag = ep->data;
449 ssize_t tgt = component_lookup_index (tag);
450 if (tgt < 0)
451 {
452 logmsg (LOG_ERR,
453 _("undefined component %s depends on %s"),
454 tag, comp->tag);
455 continue;
456 }
457 depmap_set (depmap, tgt, i);
458 }
459
460 i++;
461 }
462
463 path = depmap_cycle_detect (depmap);
464 if (path)
465 {
466 struct depmap_path *p;
467
468 logmsg (LOG_ERR, "%s", _("cyclic dependencies detected:"));
469 for (p = path; p; p = p->next)
470 report_cyclic_dependency (p);
471 depmap_path_free (path);
472
473 for (i = 0; i < comp_count;)
474 if (comp_array[i]->flags & CF_REMOVE)
475 comp_array_remove (i);
476 else
477 i++;
478 }
479 }
480
481 void
component_config_commit(void)482 component_config_commit (void)
483 {
484 struct complist *list = &comp_list[cur];
485 struct component *comp, *match;
486 int prev = prev_index ();
487 size_t i;
488
489 /* Count available components and allocate array for them */
490 for (comp = list->head, i = 0; comp; comp = comp->next, i++)
491 /* nothing */;
492
493 if (i == 0)
494 {
495 free (comp_array);
496 comp_array = NULL;
497 }
498 else
499 comp_array = grecs_realloc (comp_array, i * sizeof (comp_array[0]));
500 comp_count = i;
501
502 /* Rearrange components, registering entries for the new ones */
503 for (comp = list->head, i = 0; comp; comp = comp->next, i++)
504 {
505 match = complist_find_match (prev, comp);
506 if (match)
507 {
508 component_merge (match, comp);
509 component_unlink (match);
510 match->listidx = cur;
511 component_link (match, comp->prev);
512 component_free (comp);
513 comp = match;
514 }
515 comp_array[i] = comp;
516 comp->arridx = i;
517 }
518
519 /* Mark orphaned progs for termination */
520 list = &comp_list[prev];
521 if (list->head)
522 {
523 progman_foreach (mark_prog, NULL);
524 pies_schedule_children (PIES_CHLD_GC);
525 }
526
527 /* Build dependency map */
528 component_build_depmap ();
529
530 /* Register new progs */
531 for (comp = comp_list[cur].head; comp; comp = comp->next)
532 if (!comp->prog)
533 register_prog (comp);
534 }
535
536 static int
component_verify(struct component * comp,grecs_locus_t * locus)537 component_verify (struct component *comp, grecs_locus_t *locus)
538 {
539 int header = 0;
540 int i;
541 #define COMPERR(func, fmt, arg) \
542 do \
543 { \
544 if (!header) \
545 { \
546 grecs_warning (locus, 0, _("in component %s:"), comp->tag); \
547 header = 1; \
548 } \
549 func (locus, 0, fmt, arg); \
550 } \
551 while (0)
552
553 if (comp->flags & CF_INTERNAL)
554 {
555 comp->mode = pies_comp_inetd;
556 if (!comp->service)
557 /* TRANSLATORS: do not translate quoted words, they are keywords. */
558 COMPERR (grecs_error,
559 "%s", _("\"internal\" used without \"service\""));
560 else
561 {
562 comp->builtin = inetd_builtin_lookup (comp->service,
563 comp->socket_type);
564 if (!comp->builtin)
565 COMPERR (grecs_error,
566 "%s", _("unknown internal service"));
567 if (comp->argv)
568 /* TRANSLATORS: do not translate quoted words, they are
569 keywords. */
570 COMPERR (grecs_error,
571 "%s", _("\"internal\" used with \"command\""));
572 }
573 }
574 else if (!comp->command)
575 COMPERR (grecs_error, "%s", _("no 'command' statement"));
576
577 if (ISCF_TCPMUX (comp->flags))
578 {
579 comp->mode = pies_comp_inetd;
580 if ((comp->flags & (CF_TCPMUX | CF_TCPMUXPLUS))
581 == (CF_TCPMUX | CF_TCPMUXPLUS))
582 COMPERR (grecs_error,
583 "%s", _("both \"tcpmux\" and \"tcpmuxplus\" used"));
584 else if (!comp->service)
585 /* TRANSLATORS: do not translate quoted words, they are keywords. */
586 COMPERR (grecs_error,
587 "%s", _("\"internal\" used without \"service\""));
588 }
589
590 if (comp->pass_fd_socket && comp->mode != pies_comp_pass_fd)
591 COMPERR (grecs_error,
592 "%s", _("pass-fd-socket ignored: wrong mode"));
593 switch (comp->mode)
594 {
595 case pies_comp_exec:
596 if (comp->socket_url)
597 COMPERR (grecs_error,
598 "%s", _("socket ignored: wrong mode"));
599 break;
600
601 case pies_comp_pass_fd:
602 if (!comp->pass_fd_socket)
603 COMPERR (grecs_error,
604 "%s", _("must supply pass-fd-socket in this mode"));
605 else if (comp->pass_fd_socket[0] != '/')
606 {
607 if (comp->dir)
608 {
609 char *p = mkfilename (comp->dir, comp->pass_fd_socket, NULL);
610 /*free (comp->pass_fd_socket);*/
611 comp->pass_fd_socket = p;
612 }
613 else
614 COMPERR (grecs_error,
615 "%s", _("pass-fd-socket must be an absolute "
616 "file name or chdir must be specified"));
617 }
618 /* Fall through */
619
620 case pies_comp_accept:
621 if (!comp->socket_url)
622 {
623 COMPERR (grecs_error,
624 "%s", _("socket must be specified in this mode"));
625 return 1;
626 }
627 break;
628
629 case pies_comp_inetd:
630 if (ISCF_TCPMUX (comp->flags))
631 {
632 pies_url_destroy (&comp->socket_url);
633 if (!comp->tcpmux)
634 {
635 COMPERR (grecs_warning,
636 "%s",
637 _("TCPMUX master not specified, assuming \"tcpmux\""));
638 comp->tcpmux = grecs_strdup ("tcpmux");
639 }
640 }
641 else if (comp->tcpmux)
642 {
643 comp->flags |= CF_TCPMUX;
644 pies_url_destroy (&comp->socket_url);
645 }
646 else if (!comp->socket_url)
647 {
648 COMPERR (grecs_error,
649 "%s", _("socket must be specified in this mode"));
650 return 1;
651 }
652 default:
653 if (PIES_SYSVINIT_ENABLED && comp->mode >= pies_mark_sysvinit)
654 COMPERR (grecs_error,
655 "%s", _("SystemV init support is not compiled in"));
656 /* FIXME: more checks perhaps */
657 break;
658 }
659
660 if (comp->mode == pies_comp_inetd)
661 {
662 if ((comp->flags & CF_WAIT) && comp->socket_type == SOCK_STREAM)
663 {
664 if (comp->max_instances)
665 COMPERR (grecs_error, "%s", _("max-instances ignored"));
666 else
667 comp->max_instances = 1;
668 }
669 }
670 else if (comp->flags & CF_WAIT)
671 {
672 /* TRANSLATORS: `wait' is a keywords, do not translate. */
673 COMPERR (grecs_error, "%s", _("wait is useless in this mode"));
674 comp->flags &= ~CF_WAIT;
675 }
676
677 switch (comp->mode)
678 {
679 case pies_comp_accept:
680 case pies_comp_inetd:
681 if (comp->redir[RETR_OUT].type != redir_null)
682 {
683 COMPERR (grecs_error,
684 "%s", _("stdout redirection invalid in this mode"));
685 comp->redir[RETR_OUT].type = redir_null;
686 }
687 default:
688 break;
689 }
690
691 for (i = RETR_OUT; i <= RETR_ERR; i++)
692 {
693 if (comp->redir[i].type == redir_file
694 && comp->redir[i].v.file[0] != '/')
695 {
696 if (comp->dir)
697 {
698 char *p = mkfilename (comp->dir, comp->redir[i].v.file, NULL);
699 free (comp->redir[i].v.file);
700 comp->redir[i].v.file = p;
701 }
702 else
703 COMPERR (grecs_error,
704 _("%s: must be an absolute "
705 "file name or chdir must be specified"),
706 comp->redir[i].v.file);
707 }
708 }
709
710 return header;
711 #undef COMPERR
712 }
713
714 void
component_finish(struct component * comp,grecs_locus_t * locus)715 component_finish (struct component *comp, grecs_locus_t *locus)
716 {
717 if (component_verify (comp, locus))
718 {
719 component_free (comp);
720 return;
721 }
722
723 if (comp->flags & CF_SHELL)
724 {
725 if (comp->flags & CF_EXPANDENV)
726 {
727 grecs_warning (locus, 0,
728 "%s",
729 _("flags \"shell\" and \"expandenv\" used together: "
730 "ignoring \"expandenv\""));
731 comp->flags &= ~CF_EXPANDENV;
732 }
733 comp->argc = 3;
734 comp->argv = grecs_calloc (comp->argc + 1, sizeof (comp->argv[0]));
735 comp->argv[0] = grecs_strdup (comp->program ? comp->program : "/bin/sh");
736 comp->argv[1] = grecs_strdup ("-c");
737 comp->argv[2] = grecs_strdup (comp->command);
738 comp->argv[3] = NULL;
739 }
740 else if (comp->command && !(comp->flags & CF_EXPANDENV))
741 {
742 struct wordsplit ws;
743 if (wordsplit (comp->command, &ws, WRDSF_DEFFLAGS))
744 {
745 grecs_error (locus, 0, "wordsplit: %s",
746 wordsplit_strerror (&ws));
747 component_free (comp);
748 return;
749 }
750 wordsplit_get_words (&ws, &comp->argc, &comp->argv);
751 wordsplit_free (&ws);
752 }
753
754 if (comp->prereq)
755 comp->prereq->cmp = list_str_cmp;
756 if (comp->depend)
757 comp->depend->cmp = list_str_cmp;
758 if (comp->privs.groups)
759 comp->privs.groups->cmp = list_str_cmp;
760
761 if (grecs_list_size (comp->prereq) == 1)
762 {
763 const char *item = grecs_list_index (comp->prereq, 0);
764 if (strcmp (item, "all") == 0)
765 {
766 struct component *p;
767
768 grecs_list_clear (comp->prereq);
769 for (p = comp->prev; p; p = p->prev)
770 grecs_list_push (comp->prereq, grecs_strdup (comp->tag));
771 }
772 else if (strcmp (item, "none") == 0)
773 {
774 grecs_list_free (comp->prereq);
775 comp->prereq = NULL;
776 }
777 }
778 }
779
780 struct component *
component_get(size_t n)781 component_get (size_t n)
782 {
783 if (n >= comp_count)
784 return NULL;
785 return comp_array[n];
786 }
787
788 void
depmap_dump(pies_depmap_t dpm)789 depmap_dump (pies_depmap_t dpm)
790 {
791 size_t i, j;
792
793 if (depmap_dim (dpm) == 0)
794 {
795 printf ("%s\n", _("No components defined"));
796 return;
797 }
798
799 printf ("%s:\n", _("Dependency map"));
800 printf (" ");
801 for (i = 0; i < comp_count; i++)
802 printf (" %2lu", (unsigned long)i);
803 printf ("\n");
804 for (i = 0; i < comp_count; i++)
805 {
806 printf ("%2lu", (unsigned long)i);
807 for (j = 0; j < comp_count; j++)
808 printf (" %c", depmap_isset (dpm, i, j) ? 'X' : ' ');
809 printf ("\n");
810 }
811 printf ("\n%s:\n", _("Legend"));
812 for (i = 0; i < comp_count; i++)
813 printf ("%2lu: %s\n", (unsigned long)i, comp_array[i]->tag);
814 }
815
816 void
components_dump_depmap(void)817 components_dump_depmap (void)
818 {
819 depmap_dump (depmap);
820 }
821
822 void
component_trace(size_t idx,enum pies_depmap_direction dir)823 component_trace (size_t idx, enum pies_depmap_direction dir)
824 {
825 pies_depmap_pos_t pos;
826 size_t n;
827 int delim = ':';
828
829 logmsg_printf (LOG_NOTICE, "%s", comp_array[idx]->tag);
830 for (n = depmap_first (depmap, dir, idx, &pos);
831 n != (size_t)-1;
832 n = depmap_next (depmap, pos))
833 {
834 logmsg_printf (LOG_NOTICE, "%c %s", delim, comp_array[n]->tag);
835 delim = ',';
836 }
837 depmap_end (pos);
838 logmsg_printf (LOG_NOTICE, "\n");
839 }
840
841 void
components_trace(char ** argv,enum pies_depmap_direction dir)842 components_trace (char **argv, enum pies_depmap_direction dir)
843 {
844 if (*argv)
845 for (; *argv; ++argv)
846 {
847 ssize_t idx = component_lookup_index (*argv);
848 if (idx < 0)
849 logmsg (LOG_ERR, "%s: no such component", *argv);
850 else
851 component_trace (idx, dir);
852 }
853 else
854 {
855 size_t i;
856
857 for (i = 0; i < comp_count; i++)
858 component_trace (i, dir);
859 }
860 }
861
862 struct component *
component_depmap_first(enum pies_depmap_direction dir,size_t idx,pies_depmap_pos_t * ppos)863 component_depmap_first (enum pies_depmap_direction dir, size_t idx,
864 pies_depmap_pos_t *ppos)
865 {
866 size_t n = depmap_first (depmap, dir, idx, ppos);
867 if (n == (size_t)-1)
868 return NULL;
869 return comp_array[n];
870 }
871
872 struct component *
component_depmap_next(pies_depmap_pos_t pos)873 component_depmap_next (pies_depmap_pos_t pos)
874 {
875 size_t n = depmap_next (depmap, pos);
876 if (n == (size_t)-1)
877 return NULL;
878 return comp_array[n];
879 }
880
881 int
component_foreach(int (* filter)(struct component *,void *),void * data)882 component_foreach (int (*filter) (struct component *, void *), void *data)
883 {
884 struct component *comp;
885 int rc = 0;
886 struct complist *list = &comp_list[cur];
887
888 for (comp = list->head; comp; )
889 {
890 struct component *next = comp->next;
891 if ((rc = filter (comp, data)) != 0)
892 break;
893 comp = next;
894 }
895 return rc;
896 }
897