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