1 /* This file is part of GNU Pies.
2    Copyright (C) 2008-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 <locale.h>
19 #include <configmake.h>
20 #include "meta1parse.h"
21 #include "grecsasrt.h"
22 
23 int preprocess_only;		/* Preprocess config, do nothing more */
24 int lint_mode;			/* Test configuration syntax and exit */
25 int log_to_stderr_only;                /* Use only stderr for logging */
26 struct pies_privs pies_privs;
27 int foreground;
28 int init_process;
29 
30 enum pies_command
31   {
32     COM_START,
33     COM_RESTART_COMPONENT,
34     COM_RELOAD,
35     COM_STATUS,
36     COM_STOP,
37     COM_DUMP_DEPMAP,
38     COM_TRACE_PREREQ,
39     COM_TRACE_DEPEND
40   };
41 
42 enum pies_command command;
43 char *statedir = DEFAULT_STATE_DIR;
44 char *instance;
45 char *pidfile;
46 char *qotdfile;
47 int inetd_mode;
48 mode_t pies_umask = 0;
49 unsigned long shutdown_timeout = 5;
50 size_t default_max_rate;
51 pies_acl_t pies_acl;
52 limits_record_t pies_limits;
53 int force_option;
54 char *mailer_program = "/usr/sbin/sendmail";
55 char *mailer_command_line = "/usr/sbin/sendmail -oi -t";
56 int mailer_argc;
57 char **mailer_argv;
58 
59 char *default_control_url[2] = {
60   DEFAULT_PIES_CONTROL_URL,
61   DEFAULT_INIT_CONTROL_URL
62 };
63 
64 static envop_t *pies_envop;
65 
66 struct config_syntax
67 {
68   const char *name;
69   int (*parser) (char const *);
70 };
71 
72 static int pies_config_parse (char const *);
73 
74 static struct config_syntax config_syntax_tab[] = {
75   [CONF_PIES]    = { "pies" , pies_config_parse },
76   [CONF_META1]   = { "meta1", meta1_config_parse },
77   [CONF_INETD]   = { "inetd", inetd_config_parse },
78 #if PIES_SYSVINIT_ENABLED
79   [CONF_INITTAB] = { "inittab", inittab_parse },
80 #endif
81 };
82 
83 struct config_file
84 {
85   struct config_syntax *syntax;
86   char *name;
87 };
88 
89 struct grecs_list *config_list;
90 
91 struct config_syntax *
str_to_config_syntax(const char * str)92 str_to_config_syntax (const char *str)
93 {
94   int i;
95 
96   for (i = 0; i < ARRAY_SIZE (config_syntax_tab); i++)
97     if (strcmp (config_syntax_tab[i].name, str) == 0)
98       return &config_syntax_tab[i];
99   return NULL;
100 }
101 
102 static void
config_file_free(void * ptr)103 config_file_free (void *ptr)
104 {
105   if (ptr)
106     {
107       struct config_file *file = ptr;
108       grecs_free (file->name);
109       grecs_free (file);
110     }
111 }
112 
113 void
config_file_add(struct config_syntax * syntax,const char * name)114 config_file_add (struct config_syntax *syntax, const char *name)
115 {
116   struct config_file *file = grecs_malloc (sizeof (file[0]));
117   file->syntax = syntax;
118   file->name = grecs_strdup (name);
119   if (!config_list)
120     {
121       config_list = grecs_list_create ();
122       config_list->free_entry = config_file_free;
123     }
124   grecs_list_append (config_list, file);
125 }
126 
127 void
config_file_add_type(enum config_syntax_type syntax,const char * name)128 config_file_add_type (enum config_syntax_type syntax, const char *name)
129 {
130   config_file_add (&config_syntax_tab[syntax], name);
131 }
132 
133 int
config_file_remove(const char * name)134 config_file_remove (const char *name)
135 {
136   struct grecs_list_entry *ep;
137 
138   for (ep = config_list->head; ep; ep = ep->next)
139     {
140       struct config_file *file = ep->data;
141       if (strcmp (file->name, name) == 0)
142 	{
143 	  grecs_list_remove_entry (config_list, ep);
144 	  config_file_free (file);
145 	  return 0;
146 	}
147     }
148   return 1;
149 }
150 
151 void
config_file_remove_all(void)152 config_file_remove_all (void)
153 {
154   grecs_list_clear (config_list);
155 }
156 
157 void
config_file_list_serialize(struct json_value * ar)158 config_file_list_serialize (struct json_value *ar)
159 {
160   struct grecs_list_entry *ep;
161 
162   for (ep = config_list->head; ep; ep = ep->next)
163     {
164       struct config_file *file = ep->data;
165       struct json_value *obj = json_new_object ();
166       json_object_set (obj, "syntax", json_new_string (file->syntax->name));
167       json_object_set (obj, "file", json_new_string (file->name));
168       json_array_append (ar, obj);
169     }
170 }
171 
172 /* Logging */
173 static int
stderr_closed_p(void)174 stderr_closed_p (void)
175 {
176   int fd = dup (0);
177   if (fd < 0)
178     return 1;
179   close (fd);
180   return fd <= 2;
181 }
182 
183 static int
_cb_action(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)184 _cb_action (enum grecs_callback_command cmd, grecs_node_t *node,
185 	    void *varptr, void *cb_data)
186 {
187   grecs_locus_t *locus = &node->locus;
188   grecs_value_t *value = node->v.value;
189 
190   enum return_action *pact = varptr;
191   static struct tokendef actab[] = {
192     {"disable", action_disable},
193     {"restart", action_restart},
194     {NULL}
195   };
196   int res;
197 
198   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
199     return 1;
200   if (strtotok (actab, value->v.string, &res))
201     {
202       grecs_error (locus, 0, _("unknown action code: %s"), value->v.string);
203       return 1;
204     }
205   *pact = res;
206   return 0;
207 }
208 
209 struct grecs_keyword return_code_keywords[] = {
210   {"action",
211    /* TRANSLATORS: disable and restart are keywords, do not translate them. */
212    N_("arg: {disable | restart}"),
213    N_("Specifies action to take when a component finishes with this "
214       "return code."),
215    grecs_type_string, GRECS_DFLT,
216    NULL, offsetof (struct action, act),
217    _cb_action,
218    },
219   {"notify",
220    N_("arg: emails"),
221    N_("Notify this address when a component terminates."),
222    grecs_type_string, GRECS_DFLT,
223    NULL, offsetof (struct action, addr)
224   },
225   {"message",
226    NULL,
227    N_("Notification message text (with headers)."),
228    grecs_type_string, GRECS_DFLT,
229    NULL, offsetof (struct action, message),
230    NULL},
231   {"exec",
232    NULL,
233    N_("Execute this command."),
234    grecs_type_string, GRECS_DFLT,
235    NULL, offsetof (struct action, command),
236    NULL,
237    },
238   {NULL}
239 };
240 
241 #define S(s) { #s, s }
242 static struct tokendef ex_tokendef[] = {
243   S (EX_OK),
244   S (EX_USAGE),
245   S (EX_DATAERR),
246   S (EX_NOINPUT),
247   S (EX_NOUSER),
248   S (EX_NOHOST),
249   S (EX_UNAVAILABLE),
250   S (EX_SOFTWARE),
251   S (EX_OSERR),
252   S (EX_OSFILE),
253   S (EX_CANTCREAT),
254   S (EX_IOERR),
255   S (EX_TEMPFAIL),
256   S (EX_PROTOCOL),
257   S (EX_NOPERM),
258   S (EX_CONFIG),
259   {NULL}
260 };
261 
262 static struct tokendef sig_tokendef[] = {
263   S (SIGHUP),
264   S (SIGINT),
265   S (SIGQUIT),
266   S (SIGILL),
267   S (SIGTRAP),
268   S (SIGABRT),
269   S (SIGIOT),
270   S (SIGBUS),
271   S (SIGFPE),
272   S (SIGKILL),
273   S (SIGUSR1),
274   S (SIGSEGV),
275   S (SIGUSR2),
276   S (SIGPIPE),
277   S (SIGALRM),
278   S (SIGTERM),
279 #ifdef SIGSTKFLT
280   S (SIGSTKFLT),
281 #endif
282   S (SIGCHLD),
283   S (SIGCONT),
284   S (SIGSTOP),
285   S (SIGTSTP),
286   S (SIGTTIN),
287   S (SIGTTOU),
288 #ifdef SIGURG
289   S (SIGURG),
290 #endif
291 #ifdef SIGXCPU
292   S (SIGXCPU),
293 #endif
294 #ifdef SIGXFSZ
295   S (SIGXFSZ),
296 #endif
297 #ifdef SIGVTALRM
298   S (SIGVTALRM),
299 #endif
300 #ifdef SIGPROF
301   S (SIGPROF),
302 #endif
303 #ifdef SIGWINCH
304   S (SIGWINCH),
305 #endif
306 #ifdef SIGPOLL
307   S (SIGPOLL),
308 #endif
309 #ifdef SIGIO
310   S (SIGIO),
311 #endif
312 #ifdef SIGPWR
313   S (SIGPWR),
314 #endif
315 #ifdef SIGSYS
316   S (SIGSYS),
317 #endif
318   {NULL}
319 };
320 
321 #undef S
322 
323 void
action_free(struct action * act)324 action_free (struct action *act)
325 {
326   if (!act)
327     return;
328   if (act->nstat > 0)
329     free (act->status);
330   free (act->addr);
331   free (act->message);
332   free (act->command);
333 
334   free (act);
335 }
336 
337 static void
free_entry_action(void * act)338 free_entry_action (void *act)
339 {
340   action_free (act);
341 }
342 
343 static struct action *
create_action(struct component * comp,grecs_locus_t * locus,grecs_value_t * val,int argc,const char * (* getarg)(grecs_value_t *,int,grecs_locus_t *))344 create_action (struct component *comp,
345 	       grecs_locus_t *locus,
346 	       grecs_value_t *val, int argc,
347 	       const char *(*getarg) (grecs_value_t *, int, grecs_locus_t *))
348 {
349   int i;
350   unsigned *retv;
351   int retc = 0;
352   int allflag = 0;
353   struct action *act;
354 
355   retv = grecs_calloc (argc, sizeof *retv);
356   if (argc == 0 || (argc == 1 && strcmp (getarg (val, 0, locus), "*") == 0))
357     allflag = 1;
358   else
359     {
360       for (i = 0; i < argc; i++)
361 	{
362 	  unsigned n;
363 	  const char *arg = getarg (val, i, locus);
364 	  size_t len = strlen (arg);
365 
366 	  if (isdigit (arg[0]))
367 	    {
368 	      char *p;
369 	      n = strtoul (arg, &p, 0);
370 	      if (*p)
371 		{
372 		  grecs_error (locus, 0, _("%s: not a number"), p);
373 		  continue;
374 		}
375 	    }
376 	  else if (len > 3 && memcmp (arg, "SIG", 3) == 0)
377 	    {
378 	      if (arg[4] == '+')
379 		{
380 		  char *p;
381 		  n = strtoul (arg + 4, &p, 0);
382 		  if (*p)
383 		    {
384 		      grecs_error (locus, 0, _("%s: not a number"), p);
385 		      continue;
386 		    }
387 		}
388 	      else if (strtotok_ci (sig_tokendef, arg, (int*) &n))
389 		{
390 		  grecs_error (locus, 0, _("%s: not a signal code"), arg);
391 		  continue;
392 		}
393 	      n |= STATUS_SIG_BIT;
394 	    }
395 	  else if (strtotok_ci (ex_tokendef, arg, (int *) &n))
396 	    {
397 	      grecs_error (locus, 0, _("%s: not a return code"), arg);
398 	      continue;
399 	    }
400 
401 	  /* Alles in ordnung */
402 	  retv[retc++] = n;
403 	}
404     }
405 
406   if (retc == 0 && !allflag)
407     {
408       free (retv);
409       return NULL;
410     }
411 
412   act = grecs_zalloc (sizeof *act);
413   if (!allflag)
414     {
415       act->nstat = retc;
416       act->status = retv;
417     }
418   if (!comp->act_list)
419     {
420       comp->act_list = grecs_list_create ();
421       comp->act_list->free_entry = free_entry_action;
422     }
423   grecs_list_append (comp->act_list, act);
424   return act;
425 }
426 
427 const char *
_get_string_arg(grecs_value_t * val,int num,grecs_locus_t * locus)428 _get_string_arg (grecs_value_t *val, int num, grecs_locus_t *locus)
429 {
430   if (num != 0)
431     return NULL;
432   return val->v.string;
433 }
434 
435 const char *
_get_array_arg(grecs_value_t * val,int num,grecs_locus_t * locus)436 _get_array_arg (grecs_value_t *val, int num, grecs_locus_t *locus)
437 {
438   if (num < val->v.arg.c)
439     {
440       if (grecs_assert_value_type (val->v.arg.v[num], GRECS_TYPE_STRING,
441 				   locus) == 0)
442 	return val->v.arg.v[num]->v.string;
443     }
444   return NULL;
445 }
446 
447 const char *
_get_list_arg(grecs_value_t * val,int num,grecs_locus_t * locus)448 _get_list_arg (grecs_value_t *val, int num, grecs_locus_t *locus)
449 {
450   grecs_value_t *elt = (grecs_value_t *) grecs_list_index (val->v.list, num);
451   if (!elt)
452     {
453       grecs_error (locus, 0, _("cannot get list item"));
454     }
455   else if (grecs_assert_value_type (elt, GRECS_TYPE_STRING, locus) == 0)
456     return elt->v.string;
457   return NULL;
458 }
459 
460 static int
return_code_section_parser(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)461 return_code_section_parser (enum grecs_callback_command cmd,
462 			    grecs_node_t *node,
463 			    void *varptr, void *cb_data)
464 {
465   grecs_locus_t *locus = &node->locus;
466   grecs_value_t *value = node->v.value;
467 
468   struct component *comp = varptr;
469   size_t count;
470   struct action *act;
471 
472   switch (cmd)
473     {
474     case grecs_callback_section_begin:
475       if (GRECS_VALUE_EMPTY_P (value))
476 	{
477 	  grecs_error (locus, 0, _("missing tag"));
478 	  return 1;
479 	}
480 
481       switch (value->type)
482 	{
483 	case GRECS_TYPE_STRING:
484 	  act = create_action (comp, locus, value, 1, _get_string_arg);
485 	  break;
486 
487 	case GRECS_TYPE_ARRAY:
488 	  act = create_action (comp, locus, value,
489 			       value->v.arg.c, _get_array_arg);
490 	  break;
491 
492 	case GRECS_TYPE_LIST:
493 	  count = grecs_list_size (value->v.list);
494 	  act = create_action (comp, locus, value, count, _get_list_arg);
495 	}
496       if (!act)
497 	return 1;
498       *(struct action **) cb_data = act;
499       break;
500 
501     case grecs_callback_section_end:
502       break;
503 
504     case grecs_callback_set_value:
505       grecs_error (locus, 0, _("invalid use of block statement"));
506     }
507   return 0;
508 }
509 
510 static char **
config_array_to_argv(grecs_value_t * val,grecs_locus_t * locus,size_t * pargc)511 config_array_to_argv (grecs_value_t *val, grecs_locus_t *locus, size_t *pargc)
512 {
513   int i, j;
514   int argc;
515   char **argv;
516 
517   argc = val->v.arg.c;
518   argv = grecs_calloc (argc + 1, sizeof (argv[0]));
519   for (i = j = 0; i < argc; i++)
520     {
521       if (grecs_assert_value_type (val->v.arg.v[i], GRECS_TYPE_STRING, locus)
522 	  == 0)
523 	argv[j++] = grecs_strdup (val->v.arg.v[i]->v.string);
524     }
525   argv[j] = NULL;
526   if (pargc)
527     *pargc = argc;
528   return argv;
529 }
530 
531 static int
_cb_umask(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)532 _cb_umask (enum grecs_callback_command cmd,
533 			    grecs_node_t *node,
534 			    void *varptr, void *cb_data)
535 {
536   grecs_locus_t *locus = &node->locus;
537   grecs_value_t *value = node->v.value;
538 
539   mode_t *pmode = varptr;
540   char *p;
541   unsigned long n;
542 
543   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
544     return 1;
545   n = strtoul (value->v.string, &p, 8);
546   if (*p)
547     {
548       grecs_error (locus, 0, _("invalid octal number"));
549       return 1;
550     }
551   *pmode = n;
552   return 0;
553 }
554 
555 void
argv_free(char ** argv)556 argv_free (char **argv)
557 {
558   if (argv)
559     {
560       size_t i;
561       for (i = 0; argv[i]; i++)
562 	free (argv[i]);
563       free (argv);
564     }
565 }
566 
567 static int
parse_legacy_env(char ** argv,envop_t ** envop)568 parse_legacy_env (char **argv, envop_t **envop)
569 {
570   size_t i = 0;
571   int rc;
572   char *name;
573 
574   if (strcmp (argv[0], "-") == 0)
575     {
576       rc = envop_entry_add (envop, envop_clear, NULL, NULL);
577       if (rc)
578 	return rc;
579       i++;
580     }
581   for (; (name = argv[i]) != NULL; i++)
582     {
583       char *name = argv[i];
584       size_t len = strcspn (name, "=");
585       char *value;
586       char *mem = NULL;
587       size_t msize = 0;
588       enum envop_code code;
589 
590       if (name[0] == '-')
591 	{
592 	  /* Unset directive */
593 	  name++;
594 	  len--;
595 
596 	  if (name[len])
597 	    {
598 	      name[len] = 0;
599 	      value = name + len + 1;
600 	    }
601 	  else
602 	    value = NULL;
603 
604 	  code = envop_unset;
605 	}
606       else if (name[len])
607 	{
608 	  size_t vlen;
609 
610 	  if (len == 0)
611 	    /* Skip erroneous entry */
612 	    continue;
613 	  value = name + len + 1;
614 	  vlen = strlen (value);
615 	  name[len] = 0;
616 	  if (name[len-1] == '+')
617 	    {
618 	      name[--len] = 0;
619 	      if (c_ispunct (value[0]))
620 		{
621 		  msize = 2*len + 9 + vlen + 1;
622 		  mem = grecs_malloc (msize);
623 		  snprintf (mem, msize, "${%s:-}${%s:+%c}%s",
624 			    name, name, value[0], value + 1);
625 		}
626 	      else
627 		{
628 		  msize = len + vlen + 6;
629 		  snprintf (mem, msize, "${%s:-}%s", name, value);
630 		}
631 	      value = mem;
632 	    }
633 	  else if (value[0] == '+')
634 	    {
635 	      value++;
636 	      vlen--;
637 
638 	      if (vlen > 0 && c_ispunct (value[vlen-1]))
639 		{
640 		  int c = value[vlen-1];
641 		  value[--vlen] = 0;
642 
643 		  msize = 2*len + 10 + vlen + 1;
644 		  mem = grecs_malloc (msize);
645 		  snprintf (mem, msize, "%s${%s:+%c}${%s:-}",
646 			    value, name, c, name);
647 		}
648 	      else
649 		{
650 		  msize = len + vlen + 6;
651 		  snprintf (mem, msize, "%s${%s:-}", value, name);
652 		}
653 	      value = mem;
654 	    }
655 	  code = envop_set;
656 	}
657       else
658 	{
659 	  value = NULL;
660 	  code = envop_keep;
661 	}
662       rc = envop_entry_add (envop, code, name, value);
663       free (mem);
664       if (rc)
665 	return rc;
666     }
667   return 0;
668 }
669 
670 static int
_cb_env(envop_t ** envop,grecs_value_t * value,grecs_locus_t * locus)671 _cb_env (envop_t **envop, grecs_value_t *value, grecs_locus_t *locus)
672 {
673   char **argv;
674   int rc;
675 
676   switch (value->type)
677     {
678     case GRECS_TYPE_STRING:
679       argv = grecs_calloc (2, sizeof (argv[0]));
680       argv[0] = grecs_strdup (value->v.string);
681       argv[1] = NULL;
682       break;
683 
684     case GRECS_TYPE_ARRAY:
685       argv = config_array_to_argv (value, locus, NULL);
686       break;
687 
688     case GRECS_TYPE_LIST:
689       grecs_error (locus, 0, _("unexpected list"));
690       return 1;
691     }
692 
693   rc = parse_legacy_env (argv, envop);
694   argv_free (argv);
695   if (rc)
696     {
697       grecs_error (locus, errno, _("can't parse legacy env statement"));
698       return 1;
699     }
700   return 0;
701 }
702 
703 static int
cb_env_section_parser(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)704 cb_env_section_parser (enum grecs_callback_command cmd,
705 		       grecs_node_t *node,
706 		       void *varptr, void *cb_data)
707 {
708   grecs_locus_t *locus = &node->locus;
709   grecs_value_t *value = node->v.value;
710   envop_t **envop_ptr = varptr;
711 
712   switch (cmd)
713     {
714     case grecs_callback_section_begin:
715       *(envop_t ***) cb_data = envop_ptr;
716       break;
717 
718     case grecs_callback_section_end:
719       break;
720 
721     case grecs_callback_set_value:
722       return _cb_env (envop_ptr, value, locus);
723     }
724   return 0;
725 }
726 
727 static int
_cb_env_clear(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)728 _cb_env_clear (enum grecs_callback_command cmd,
729 	       grecs_node_t *node,
730 	       void *varptr, void *cb_data)
731 {
732   grecs_locus_t *locus = &node->locus;
733   grecs_value_t *value = node->v.value;
734   envop_t **envop_ptr = varptr;
735 
736   if (!GRECS_VALUE_EMPTY_P (value))
737     {
738       grecs_error (&value->locus, 0, "%s", _("unexpected argument"));
739       return 1;
740     }
741 
742   if (envop_entry_add (envop_ptr, envop_clear, NULL, NULL))
743     grecs_error (locus, errno, "envop_entry_add");
744 
745   return 0;
746 }
747 
748 static int
_cb_env_keep(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)749 _cb_env_keep (enum grecs_callback_command cmd,
750 	      grecs_node_t *node,
751 	      void *varptr, void *cb_data)
752 {
753   grecs_locus_t *locus = &node->locus;
754   grecs_value_t *value = node->v.value;
755   envop_t **envop_ptr = varptr;
756   char *p;
757 
758   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
759     return 1;
760   p = strchr (value->v.string, '=');
761   if (p)
762     *p++ = 0;
763   if (envop_entry_add (envop_ptr, envop_clear, NULL, NULL))
764     grecs_error (locus, errno, "envop_entry_add");
765   if (envop_entry_add (envop_ptr, envop_keep, value->v.string, p))
766     grecs_error (locus, errno, "envop_entry_add");
767   return 0;
768 }
769 
770 static int
_cb_env_set(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)771 _cb_env_set (enum grecs_callback_command cmd,
772 	     grecs_node_t *node,
773 	     void *varptr, void *cb_data)
774 {
775   grecs_locus_t *locus = &node->locus;
776   grecs_value_t *value = node->v.value;
777   envop_t **envop_ptr = varptr;
778   char *p;
779 
780   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
781     return 1;
782   p = strchr (value->v.string, '=');
783   if (p)
784     *p++ = 0;
785   if (envop_entry_add (envop_ptr, envop_set, value->v.string, p))
786     grecs_error (locus, errno, "envop_entry_add");
787   return 0;
788 }
789 
790 static int
_cb_env_eval(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)791 _cb_env_eval (enum grecs_callback_command cmd,
792 	      grecs_node_t *node,
793 	      void *varptr, void *cb_data)
794 {
795   grecs_locus_t *locus = &node->locus;
796   grecs_value_t *value = node->v.value;
797   envop_t **envop_ptr = varptr;
798 
799   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
800     return 1;
801   if (envop_entry_add (envop_ptr, envop_set, NULL, value->v.string))
802     grecs_error (locus, errno, "envop_entry_add");
803   return 0;
804 }
805 
806 static int
_cb_env_unset(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)807 _cb_env_unset (enum grecs_callback_command cmd,
808 	       grecs_node_t *node,
809 	       void *varptr, void *cb_data)
810 {
811   grecs_locus_t *locus = &node->locus;
812   grecs_value_t *value = node->v.value;
813   envop_t **envop_ptr = varptr;
814   char *p;
815 
816   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
817     return 1;
818   p = strchr (value->v.string, '=');
819   if (p)
820     *p++ = 0;
821   if (envop_entry_add (envop_ptr, envop_unset, value->v.string, p))
822     grecs_error (locus, errno, "envop_entry_add");
823   return 0;
824 }
825 
826 struct grecs_keyword cb_env_keywords[] = {
827   { "clear",
828     N_("bool"),
829     N_("Clear environment."),
830     grecs_type_bool, GRECS_DFLT,
831     NULL, 0,
832     _cb_env_clear },
833   { "keep",
834     N_("name[=value]"),
835     N_("Keep this variable. Unless value is supplied, name can contain wildcards.\n"
836        "Implies \"clear\"."),
837     grecs_type_string, GRECS_DFLT,
838     NULL, 0,
839     _cb_env_keep },
840   { "set",
841     N_("name=value"),
842     N_("Set environment variable. Note, that argument must be quoted."),
843     grecs_type_string, GRECS_DFLT,
844     NULL, 0,
845     _cb_env_set },
846   { "eval",
847     N_("string"),
848     N_("Evaluate string. Useful for side-effects, e.g. eval ${X:=2}."),
849     grecs_type_string, GRECS_DFLT,
850     NULL, 0,
851     _cb_env_eval },
852   { "unset",
853     N_("name"),
854     N_("Unset environment variable. Name can contain wildcards."),
855     grecs_type_string, GRECS_DFLT,
856     NULL, 0,
857     _cb_env_unset },
858   { NULL }
859 };
860 
861 
862 int
string_to_syslog_priority(const char * key,int * pres)863 string_to_syslog_priority (const char *key, int *pres)
864 {
865   static struct tokendef tokdef_prio[] = {
866     {"EMERG", LOG_EMERG},
867     {"ALERT", LOG_ALERT},
868     {"CRIT", LOG_CRIT},
869     {"ERR", LOG_ERR},
870     {"WARNING", LOG_WARNING},
871     {"NOTICE", LOG_NOTICE},
872     {"INFO", LOG_INFO},
873     {"DEBUG", LOG_DEBUG},
874     {NULL}
875   };
876 
877   return strtotok_ci (tokdef_prio, key, pres);
878 }
879 
880 int
string_to_syslog_facility(const char * key,int len,int * pres)881 string_to_syslog_facility (const char *key, int len, int *pres)
882 {
883   static struct tokendef tokdef_fac[] = {
884     {"auth", LOG_AUTH},
885 #ifdef LOG_AUTHPRIV
886     {"authpriv", LOG_AUTHPRIV},
887 #endif
888     {"cron", LOG_CRON},
889     {"daemon", LOG_DAEMON},
890 #ifdef LOG_FTP
891     {"ftp", LOG_FTP},
892 #endif
893     {"kern", LOG_KERN},
894     {"lpr", LOG_LPR},
895     {"mail", LOG_MAIL},
896     {"news", LOG_NEWS},
897     {"syslog", LOG_SYSLOG},
898     {"user", LOG_USER},
899     {"uucp", LOG_UUCP},
900     {"local0", LOG_LOCAL0},
901     {"local1", LOG_LOCAL1},
902     {"local2", LOG_LOCAL2},
903     {"local3", LOG_LOCAL3},
904     {"local4", LOG_LOCAL4},
905     {"local5", LOG_LOCAL5},
906     {"local6", LOG_LOCAL6},
907     {"local7", LOG_LOCAL7},
908     {NULL}
909   };
910 
911   return len == 0
912 	  ? strtotok_ci (tokdef_fac, key, pres)
913 	  : strtotok_len_ci (tokdef_fac, key, len, pres);
914 }
915 
916 static int
string_to_syslog_fp(const char * str,grecs_locus_t * locus,int * pres)917 string_to_syslog_fp (const char *str, grecs_locus_t *locus, int *pres)
918 {
919   size_t len = strcspn (str, ".");
920   int f = pies_log_facility, p = LOG_ERR;
921 
922   if (string_to_syslog_facility (str, len, &f) == 0)
923     {
924       if (str[len])
925 	{
926 	  if (string_to_syslog_priority (str + len + 1, &p))
927 	    {
928 	      grecs_error (locus, 0, "%s", _("unknown syslog priority"));
929 	      return 1;
930 	    }
931 	}
932     }
933   else if (str[len])
934     {
935       grecs_error (locus, 0, "%s", _("unknown syslog facility"));
936       return 1;
937     }
938   else if (string_to_syslog_priority (str, &p))
939     {
940       grecs_error (locus, 0, "%s", _("unknown syslog priority"));
941       return 1;
942     }
943   *pres = f | p;
944   return 0;
945 }
946 
947 static int
cb_syslog_dev(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)948 cb_syslog_dev (enum grecs_callback_command cmd,
949 		    grecs_node_t *node,
950 		    void *varptr, void *cb_data)
951 {
952   grecs_value_t *value = node->v.value;
953 
954   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
955     return 1;
956   return pies_syslog_set_dev (value->v.string);
957 }
958 
959 static int
cb_syslog_facility(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)960 cb_syslog_facility (enum grecs_callback_command cmd,
961 		    grecs_node_t *node,
962 		    void *varptr, void *cb_data)
963 {
964   grecs_locus_t *locus = &node->locus;
965   grecs_value_t *value = node->v.value;
966   const char *str;
967 
968   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
969     return 1;
970   str = value->v.string;
971   if (c_isdigit (str[0]))
972     {
973       char *p;
974       int n = strtoul (str, &p, 10);
975       if (*p)
976 	grecs_error (locus, 0,
977 		     _("expected facility number or symbolic name"));
978       else
979 	*(int *) varptr = n;
980     }
981   else if (string_to_syslog_facility (str, 0, varptr))
982     grecs_error (locus, 0, _("unknown syslog facility %s"), str);
983   return 0;
984 }
985 
986 static int
_cb_redir(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)987 _cb_redir (enum grecs_callback_command cmd,
988 	   grecs_node_t *node,
989 	   void *varptr, void *cb_data)
990 {
991   grecs_locus_t *locus = &node->locus;
992   grecs_value_t *value = node->v.value;
993   struct redirector *rp = varptr;
994   static struct tokendef redirtab[] = {
995     {"null", redir_null},
996     {"syslog", redir_syslog},
997     {"file", redir_file},
998     {NULL}
999   };
1000   int res;
1001 
1002   switch (value->type)
1003     {
1004     case GRECS_TYPE_STRING:
1005       if (strcmp (value->v.string, "null") == 0)
1006 	{
1007 	  rp->type = redir_null;
1008 	  break;
1009 	}
1010       rp->type = redir_syslog;
1011       if (string_to_syslog_fp (value->v.string, locus, &rp->v.prio))
1012 	return 1;
1013       break;
1014 
1015     case GRECS_TYPE_ARRAY:
1016       if (grecs_assert_value_type (value->v.arg.v[0], GRECS_TYPE_STRING,
1017 				   locus))
1018 	return 1;
1019       if (strtotok (redirtab, value->v.arg.v[0]->v.string, &res))
1020 	grecs_error (locus, 0, _("%s: unrecognized redirector type"),
1021 		     value->v.arg.v[0]->v.string);
1022       else
1023 	{
1024 	  if (res != redir_null)
1025 	    {
1026 	      if (value->v.arg.c != 2)
1027 		{
1028 		  grecs_error (locus, 0, _("wrong number of arguments"));
1029 		  return 1;
1030 		}
1031 	      if (grecs_assert_value_type (value->v.arg.v[1],
1032 					   GRECS_TYPE_STRING, locus))
1033 		return 1;
1034 
1035 	      switch (res)
1036 		{
1037 		case redir_null:
1038 		  break;
1039 
1040 		case redir_syslog:
1041 		  if (string_to_syslog_fp (value->v.arg.v[1]->v.string,
1042 					   locus, &rp->v.prio))
1043 		    return 1;
1044 		  break;
1045 
1046 		case redir_file:
1047 		  rp->v.file = grecs_strdup (value->v.arg.v[1]->v.string);
1048 		  break;
1049 		}
1050 	    }
1051 	  rp->type = res;
1052 	}
1053       break;
1054 
1055     default:
1056       grecs_error (locus, 0, _("unexpected list"));
1057     }
1058 
1059   return 0;
1060 }
1061 
1062 static struct tokendef socktype_xtab[] = {
1063   { "stream",     SOCK_STREAM },
1064   { "dgram",      SOCK_DGRAM },
1065   { "seqpacket",  SOCK_SEQPACKET },
1066   { "raw",        SOCK_RAW },
1067   { "rdm",        SOCK_RDM },
1068 #ifdef SOCK_PACKET
1069   { "packet",     SOCK_PACKET },
1070 #endif
1071   { NULL }
1072 };
1073 
1074 int
str_to_socket_type(const char * str,int * pres)1075 str_to_socket_type (const char *str, int *pres)
1076 {
1077   return strtotok (socktype_xtab, str, pres);
1078 }
1079 
1080 int
socket_type_to_str(int socket_type,const char ** pres)1081 socket_type_to_str (int socket_type, const char **pres)
1082 {
1083   return toktostr (socktype_xtab, socket_type, pres);
1084 }
1085 
1086 static int
_cb_socket_type(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1087 _cb_socket_type (enum grecs_callback_command cmd,
1088 		 grecs_node_t *node,
1089 		 void *varptr, void *cb_data)
1090 {
1091   grecs_locus_t *locus = &node->locus;
1092   grecs_value_t *value = node->v.value;
1093 
1094   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
1095     return 1;
1096 
1097   if (str_to_socket_type (value->v.string, varptr))
1098     grecs_error (locus, 0, _("bad socket type"));
1099   return 0;
1100 }
1101 
1102 static struct tokendef modetab[] = {
1103   {"exec", pies_comp_exec},
1104   {"respawn", pies_comp_exec},
1105   {"wait", pies_comp_wait},
1106   {"once", pies_comp_once},
1107   {"accept", pies_comp_accept},
1108   {"inetd", pies_comp_inetd},
1109   {"nostartaccept", pies_comp_inetd},
1110   {"pass-fd", pies_comp_pass_fd},
1111   {"pass", pies_comp_pass_fd},
1112   {"startup", pies_comp_startup},
1113   {"shutdown", pies_comp_shutdown},
1114   {"boot", pies_comp_boot},
1115   {"bootwait", pies_comp_boot},
1116   {"powerfail", pies_comp_powerfail},
1117   {"powerwait", pies_comp_powerwait},
1118   {"powerokwait", pies_comp_powerokwait},
1119   {"ctrlaltdel", pies_comp_ctrlaltdel},
1120   {"ondemand", pies_comp_ondemand},
1121   {"sysinit", pies_comp_sysinit},
1122   {"powerfailnow", pies_comp_powerfailnow},
1123   {"kbrequest", pies_comp_kbrequest},
1124 
1125   {NULL}
1126 };
1127 
1128 static int
_cb_mode(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1129 _cb_mode (enum grecs_callback_command cmd,
1130 	  grecs_node_t *node,
1131 	  void *varptr, void *cb_data)
1132 {
1133   grecs_locus_t *locus = &node->locus;
1134   grecs_value_t *value = node->v.value;
1135   int res;
1136 
1137   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
1138     return 1;
1139   if (strtotok (modetab, value->v.string, &res))
1140     grecs_error (locus, 0, _("%s: unrecognized mode"), value->v.string);
1141   else
1142     *(enum pies_comp_mode *) varptr = res;
1143   return 0;
1144 }
1145 
1146 static int
_cb_limits(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1147 _cb_limits (enum grecs_callback_command cmd,
1148 	    grecs_node_t *node,
1149 	    void *varptr, void *cb_data)
1150 {
1151   grecs_locus_t *locus = &node->locus;
1152   grecs_value_t *value = node->v.value;
1153   limits_record_t *plrec = varptr;
1154   char *p;
1155 
1156   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
1157     return 1;
1158   if (parse_limits (plrec, (char *) value->v.string, &p))
1159     grecs_error (locus, 0, _("invalid limit string (near %s)"), p);
1160   return 0;
1161 }
1162 
1163 int
str_to_cf(const char * string,int * flags)1164 str_to_cf (const char *string, int *flags)
1165 {
1166   size_t len = strlen (string);
1167   int neg = 0;
1168   int mask;
1169 
1170   static struct tokendef cf_tab[] = {
1171     { "disable", CF_DISABLED },
1172     { "precious", CF_PRECIOUS },
1173     { "wait", CF_WAIT },
1174     { "tcpmux", CF_TCPMUX },
1175     { "tcpmuxplus", CF_TCPMUXPLUS },
1176     { "internal", CF_INTERNAL },
1177     { "sockenv", CF_SOCKENV },
1178     { "resolve", CF_RESOLVE },
1179     { "siggroup", CF_SIGGROUP },
1180     { "nullinput", CF_NULLINPUT },
1181     { "shell", CF_SHELL },
1182     { "expandenv", CF_EXPANDENV },
1183     { NULL }
1184   };
1185 
1186   if (len > 2 && memcmp (string, "no", 2) == 0)
1187     {
1188       neg++;
1189       string += 2;
1190     }
1191 
1192   if (strtotok (cf_tab, string, &mask))
1193     return 1;
1194 
1195   if (neg)
1196     *flags &= ~mask;
1197   else
1198     *flags |= mask;
1199   return 0;
1200 }
1201 
1202 static int
_cb_flags(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1203 _cb_flags (enum grecs_callback_command cmd,
1204 	   grecs_node_t *node,
1205 	   void *varptr, void *cb_data)
1206 {
1207   grecs_locus_t *locus = &node->locus;
1208   grecs_value_t *value = node->v.value;
1209   int *flags = varptr;
1210 
1211   switch (value->type)
1212     {
1213     case GRECS_TYPE_STRING:
1214       if (str_to_cf (value->v.string, flags))
1215 	{
1216 	  grecs_error (locus, 0, _("%s: unrecognized flag"), value->v.string);
1217 	  return 1;
1218 	}
1219       break;
1220 
1221     case GRECS_TYPE_LIST:
1222       {
1223 	struct grecs_list_entry *ep;
1224 
1225 	for (ep = value->v.list->head; ep; ep = ep->next)
1226 	  {
1227 	    const grecs_value_t *vp = ep->data;
1228 	    if (grecs_assert_value_type (vp, GRECS_TYPE_STRING, locus))
1229 	      return 1;
1230 	    if (str_to_cf (vp->v.string, flags))
1231 	      {
1232 		grecs_error (locus, 0, _("%s: unrecognized flag"),
1233 			     vp->v.string);
1234 		return 1;
1235 	      }
1236 	  }
1237       }
1238       break;
1239 
1240     case GRECS_TYPE_ARRAY:
1241       grecs_error (locus, 0, _("too many arguments"));
1242       return 1;
1243     }
1244   return 0;
1245 }
1246 
1247 struct grecs_keyword component_keywords[] = {
1248   {"mode",
1249    N_("mode"),
1250    N_("Component execution mode."),
1251    grecs_type_string, GRECS_DFLT,
1252    NULL, offsetof (struct component, mode),
1253    _cb_mode,
1254    },
1255   {"program",
1256    NULL,
1257    N_("Full name of the program."),
1258    grecs_type_string, GRECS_DFLT,
1259    NULL, offsetof (struct component, program),
1260    NULL,
1261    },
1262   {"command",
1263    NULL,
1264    N_("Command line."),
1265    grecs_type_string, GRECS_DFLT,
1266    NULL, offsetof (struct component, command),
1267    NULL,
1268   },
1269   {"prerequisites",
1270    N_("list"),
1271    N_("List of prerequisites."),
1272    grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, prereq),
1273    NULL,
1274    },
1275   {"dependents",
1276    N_("list"),
1277    N_("List of components for which this one is a prerequisite."),
1278    grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, depend),
1279    NULL,
1280    },
1281   {"flags",
1282    N_("list"),
1283    N_("List of flags."),
1284    grecs_type_string, GRECS_LIST, NULL, offsetof (struct component, flags),
1285    _cb_flags },
1286 #if PIES_SYSVINIT_ENABLED
1287   {"runlevels",
1288    N_("chars"),
1289    N_("Runlevels to start that component in."),
1290    grecs_type_string, GRECS_DFLT,
1291    NULL, offsetof (struct component, runlevels),
1292    cb_runlevels },
1293 #endif
1294   {"pass-fd-socket",
1295    N_("name"),
1296    N_("Pass fd through this socket."),
1297    grecs_type_string, GRECS_DFLT,
1298    NULL, offsetof (struct component, pass_fd_socket),
1299    NULL,
1300    },
1301   {"pass-fd-timeout",
1302    NULL,
1303    N_("Time to wait for pass-fd socket to become available."),
1304    grecs_type_uint, GRECS_DFLT,
1305    NULL, offsetof (struct component, pass_fd_timeout),
1306    NULL,
1307    },
1308   {"max-instances",
1309    NULL,
1310    N_("Maximum number of running instances."),
1311    grecs_type_size, GRECS_DFLT,
1312    NULL, offsetof (struct component, max_instances),
1313    NULL },
1314   {"max-instances-message",
1315    NULL,
1316    N_("Text to send back if max-instances is reached (inetd-components only)."),
1317    grecs_type_string, GRECS_DFLT,
1318    NULL, offsetof (struct component, max_instances_message),
1319    NULL },
1320   {"max-ip-connections",
1321    NULL,
1322    N_("Maximum number of simultaneous connections per IP address (inetd only)."),
1323    grecs_type_size, GRECS_DFLT,
1324    NULL, offsetof (struct component, max_ip_connections),
1325    NULL },
1326   {"max-ip-connections-message",
1327    NULL,
1328    N_("Text to send back if max-ip-connections is reached (inetd only)."),
1329    grecs_type_string, GRECS_DFLT,
1330    NULL, offsetof (struct component, max_ip_connections_message),
1331    NULL },
1332 
1333   {"max-rate",
1334    NULL,
1335    N_("Maximum number of times an inetd component can be invoked in one minute."),
1336    grecs_type_size, GRECS_DFLT,
1337    NULL, offsetof (struct component, max_rate),
1338    NULL },
1339   {"socket",
1340    N_("url: string"),
1341    N_("Listen on the given url."),
1342    grecs_type_string, GRECS_DFLT,
1343    NULL, offsetof (struct component, socket_url),
1344    conf_callback_url,
1345    },
1346   {"socket-type",
1347    /* TRANSLATORS: words after `type:' are keywords. */
1348    N_("type: {stream | dgram | raw | rdm | seqpacket}"),
1349    N_("Set socket type."),
1350    grecs_type_int, GRECS_DFLT,
1351    NULL, offsetof (struct component, socket_type),
1352    _cb_socket_type },
1353   {"acl",
1354    NULL,
1355    N_("Define connection ACL."),
1356    grecs_type_section, GRECS_DFLT,
1357    NULL, offsetof (struct component, acl),
1358    acl_section_parser, NULL, acl_keywords},
1359   {"list-acl",
1360    NULL,
1361    N_("Define who can list this component."),
1362    grecs_type_section, GRECS_DFLT,
1363    NULL, offsetof (struct component, list_acl),
1364    acl_section_parser, NULL, acl_keywords},
1365   {"admin-acl",
1366    NULL,
1367    N_("Define who can change this component."),
1368    grecs_type_section, GRECS_DFLT,
1369    NULL, offsetof (struct component, adm_acl),
1370    acl_section_parser, NULL, acl_keywords},
1371   {"access-denied-message",
1372    NULL,
1373    N_("Text to send back if access is denied (inetd-components only)."),
1374    grecs_type_string, GRECS_DFLT,
1375    NULL, offsetof (struct component, access_denied_message),
1376    NULL },
1377   {"remove-file",
1378    N_("file"),
1379    N_("Remove file before starting the component."),
1380    grecs_type_string, GRECS_DFLT,
1381    NULL, offsetof (struct component, rmfile),
1382    NULL,
1383    },
1384   {"stdout",
1385    /* TRANSLATORS: file and syslog are keywords. Do not translate them. */
1386    N_("type: {file | syslog}> <channel: string"),
1387    N_("Redirect program's standard output to the given file or "
1388       "syslog priority."),
1389    grecs_type_string, GRECS_DFLT,
1390    NULL, offsetof (struct component, redir[RETR_OUT]),
1391    _cb_redir,
1392    },
1393   {"stderr",
1394    /* TRANSLATORS: file and syslog are keywords. Do not translate them. */
1395    N_("type: {file | syslog}> <channel: string"),
1396    N_("Redirect program's standard error to the given file or "
1397       "syslog priority."),
1398    grecs_type_string, GRECS_DFLT,
1399    NULL, offsetof (struct component, redir[RETR_ERR]),
1400    _cb_redir,
1401    },
1402   {"user",
1403    NULL,
1404    N_("Run with this user privileges."),
1405    grecs_type_string, GRECS_DFLT,
1406    NULL, offsetof (struct component, privs.user),
1407    NULL,
1408    },
1409   {"group",
1410    NULL,
1411    N_("Retain supplementary group."),
1412    grecs_type_string, GRECS_LIST,
1413    NULL, offsetof (struct component, privs.groups),
1414    NULL,
1415    },
1416   {"allgroups",
1417    NULL,
1418    N_("Retain all supplementary groups of which user is a member."),
1419    grecs_type_bool, GRECS_DFLT,
1420    NULL, offsetof (struct component, privs.allgroups),
1421    NULL,
1422    },
1423   {"umask",
1424    N_("arg: number"),
1425    N_("Force this umask."),
1426    grecs_type_string, GRECS_DFLT,
1427    NULL, offsetof (struct component, umask),
1428    _cb_umask,
1429    },
1430   {"limits",
1431    NULL,
1432    N_("Set system limits"),
1433    grecs_type_string, GRECS_DFLT,
1434    NULL, offsetof (struct component, limits),
1435    _cb_limits,
1436    },
1437   {"env",
1438    NULL,
1439    N_("Modify program environment."),
1440    grecs_type_section, GRECS_DFLT,
1441    NULL, offsetof (struct component, envop),
1442    cb_env_section_parser, NULL, cb_env_keywords
1443    },
1444   {"env",
1445    N_("arg: list"),
1446    N_("Modify program environment (legacy syntax).\n"
1447       "Argument is a list of quoted assignments separated by white space."),
1448    grecs_type_string, GRECS_DFLT,
1449    NULL, offsetof (struct component, envop),
1450    cb_env_section_parser, NULL, NULL
1451    },
1452   {"chdir",
1453    N_("dir"),
1454    N_("Change to this directory before executing the component."),
1455    grecs_type_string, GRECS_DFLT,
1456    NULL, offsetof (struct component, dir),
1457    NULL,
1458    },
1459   {"return-code",
1460    N_("tag: exit-code-list"),
1461    N_("Define what to do when the component finishes."),
1462    grecs_type_section, GRECS_DFLT,
1463    NULL, 0,
1464    return_code_section_parser, NULL, return_code_keywords},
1465   {"service",
1466    N_("name"),
1467    N_("Service name for inetd component."),
1468    grecs_type_string, GRECS_DFLT,
1469    NULL, offsetof (struct component, service),
1470    NULL },
1471   {"tcpmux-master",
1472    N_("tag"),
1473    N_("Tag of master TCPMUX component."),
1474    grecs_type_string, GRECS_DFLT,
1475    NULL, offsetof (struct component, tcpmux),
1476    NULL },
1477   {NULL}
1478 };
1479 
1480 struct grecs_keyword *
find_component_keyword(const char * ident)1481 find_component_keyword (const char *ident)
1482 {
1483   struct grecs_keyword *kwp;
1484 
1485   for (kwp = component_keywords; kwp->ident; kwp++)
1486     if (strcmp (kwp->ident, ident) == 0)
1487       return kwp;
1488   return NULL;
1489 }
1490 
1491 static int
component_section_parser(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1492 component_section_parser (enum grecs_callback_command cmd,
1493 			  grecs_node_t *node,
1494 			  void *varptr, void *cb_data)
1495 {
1496   grecs_locus_t *locus = &node->locus;
1497   grecs_value_t *value = node->v.value;
1498   struct component *comp;
1499   void **section_data = cb_data;
1500 
1501   switch (cmd)
1502     {
1503     case grecs_callback_section_begin:
1504       if (GRECS_VALUE_EMPTY_P (value))
1505 	{
1506 	  grecs_error (locus, 0, _("missing tag"));
1507 	  return 1;
1508 	}
1509       if (grecs_assert_value_type (value, GRECS_TYPE_STRING, locus))
1510 	return 1;
1511       comp = component_create (value->v.string);
1512       *section_data = comp;
1513       break;
1514 
1515     case grecs_callback_section_end:
1516       comp = *(struct component **) section_data;
1517       component_finish (comp, locus);
1518       break;
1519 
1520     case grecs_callback_set_value:
1521       grecs_error (locus, 0, _("expected block statement"));
1522     }
1523   return 0;
1524 }
1525 
1526 struct grecs_keyword control_keywords[] = {
1527   {"socket",
1528    N_("url: string"),
1529    N_("Listen on the given url."),
1530    grecs_type_string, GRECS_DFLT,
1531    &control.url, 0, conf_callback_url},
1532   {"acl",
1533    NULL,
1534    N_("Set connection ACL."),
1535    grecs_type_section, GRECS_DFLT,
1536    &control.conn_acl, 0,
1537    acl_section_parser, NULL, acl_keywords},
1538   {"admin-acl",
1539    NULL,
1540    N_("Administrative access"),
1541    grecs_type_section, GRECS_DFLT,
1542    &control.adm_acl, 0,
1543    acl_section_parser, NULL, acl_keywords},
1544   {"user-acl",
1545    NULL,
1546    N_("User access"),
1547    grecs_type_section, GRECS_DFLT,
1548    &control.usr_acl, 0,
1549    acl_section_parser, NULL, acl_keywords},
1550   {"idle-timeout",
1551    "n",
1552    N_("Disconnect after <n> seconds of inaction (not implemented)."),
1553    grecs_type_uint, GRECS_DFLT,
1554    &control.idle_timeout, 0,
1555    NULL,
1556    },
1557   {"realm",
1558    N_("name"),
1559    N_("Authentication realm name"),
1560    grecs_type_string, GRECS_CONST,
1561    &control.realm, 0,
1562    NULL,
1563   },
1564   { NULL }
1565 };
1566 
1567 /* syslog */
1568 static struct grecs_keyword syslog_kw[] = {
1569   {"dev",
1570    N_("name"),
1571    N_("Syslog device: either absolute pathname of the socket file, "
1572       "or an IPv4 address and optional port, separated with a colon"),
1573    grecs_type_string, GRECS_CONST,
1574    NULL, 0, cb_syslog_dev },
1575   {"facility",
1576    N_("name"),
1577    N_("Set syslog facility. Arg is one of the following: user, daemon, "
1578       "auth, authpriv, mail, cron, local0 through local7 (case-insensitive), "
1579       "or a facility number."),
1580    grecs_type_string, GRECS_DFLT,
1581    &pies_log_facility, 0, cb_syslog_facility},
1582   {"tag", N_("string"), N_("Tag syslog messages with this string"),
1583    grecs_type_string, GRECS_DFLT,
1584    &pies_log_tag},
1585 #if 0
1586   /* This is reserved for future use */
1587   {
1588     "print-priority", N_("arg"), N_("Prefix each message with its priority"),
1589     grecs_type_bool, GRECS_DFLT,
1590     &syslog_include_prio},
1591 #endif
1592   {NULL},
1593 };
1594 
1595 
1596 struct component default_component;
1597 
1598 static int
_cb_include_meta1(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1599 _cb_include_meta1 (enum grecs_callback_command cmd,
1600 		   grecs_node_t *node,
1601 		   void *varptr, void *cb_data)
1602 {
1603   grecs_value_t *value = node->v.value;
1604   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
1605     return 1;
1606   meta1_config_parse (value->v.string);
1607   return 0;
1608 }
1609 
1610 static int
_cb_include_inetd(enum grecs_callback_command cmd,grecs_node_t * node,void * varptr,void * cb_data)1611 _cb_include_inetd (enum grecs_callback_command cmd,
1612 		   grecs_node_t *node,
1613 		   void *varptr, void *cb_data)
1614 {
1615   grecs_value_t *value = node->v.value;
1616   if (grecs_assert_node_value_type (cmd, node, GRECS_TYPE_STRING))
1617     return 1;
1618   return inetd_config_parse (value->v.string);
1619 }
1620 
1621 struct grecs_keyword pies_keywords[] = {
1622   {"component",
1623    N_("tag: string"),
1624    N_("Define a component"),
1625    grecs_type_section, GRECS_DFLT,
1626    NULL, 0,
1627    component_section_parser, NULL, component_keywords},
1628   {"env",
1629    NULL,
1630    N_("Modify program environment."),
1631    grecs_type_section, GRECS_DFLT,
1632    &pies_envop, 0,
1633    cb_env_section_parser, NULL, cb_env_keywords
1634    },
1635   {"control",
1636    NULL,
1637    N_("Define control socket"),
1638    grecs_type_section, GRECS_DFLT,
1639    NULL, 0,
1640    NULL, NULL, control_keywords },
1641   {"syslog",
1642    NULL,
1643    N_("Configure syslog logging"),
1644    grecs_type_section, GRECS_DFLT,
1645    NULL, 0, NULL, NULL, syslog_kw},
1646   {"debug",
1647    NULL,
1648    N_("Set debug verbosity level."),
1649    grecs_type_uint, GRECS_DFLT,
1650    &debug_level, 0, NULL},
1651   {"source-info",
1652    NULL,
1653    N_("Show source info with debugging messages."),
1654    grecs_type_bool, GRECS_DFLT,
1655    &source_info_option, 0, NULL},
1656   {"state-directory",
1657    NULL,
1658    N_("Full file name of the program state directory."),
1659    grecs_type_string, GRECS_CONST,
1660    &statedir, 0, NULL},
1661   {"pidfile",
1662    NULL,
1663    N_("Write PID to this file."),
1664    grecs_type_string, GRECS_CONST,
1665    &pidfile, 0,
1666    NULL,
1667    },
1668   {"control-file",
1669    NULL,
1670    N_("Ignored for compatibility with version 1.2."),
1671    grecs_type_string, GRECS_DFLT|GRECS_INAC,
1672    NULL, 0,
1673    NULL,
1674    },
1675   {"stat-file",
1676    NULL,
1677    N_("Ignored for compatibility with version 1.2."),
1678    grecs_type_string, GRECS_DFLT|GRECS_INAC,
1679    NULL, 0,
1680    NULL,
1681    },
1682   {"qotd-file",
1683    NULL,
1684    N_("Set location of the QOTD file."),
1685    grecs_type_string, GRECS_CONST,
1686    &qotdfile, 0,
1687    NULL },
1688   {"user",
1689    NULL,
1690    N_("Run with this user privileges."),
1691    grecs_type_string, GRECS_CONST,
1692    &pies_privs.user, 0,
1693    NULL,
1694    },
1695   {"group",
1696    NULL,
1697    N_("Retain supplementary group."),
1698    grecs_type_string, GRECS_LIST,
1699    &pies_privs.groups, 0,
1700    NULL,
1701    },
1702   {"allgroups",
1703    NULL,
1704    N_("Retain all supplementary groups of which user is a member."),
1705    grecs_type_bool, GRECS_DFLT,
1706    &pies_privs.allgroups, 0,
1707    NULL,
1708    },
1709   {"umask",
1710    N_("arg: number"),
1711    N_("Force this umask."),
1712    grecs_type_string, GRECS_DFLT,
1713    &pies_umask, 0,
1714    _cb_umask,
1715    },
1716   {"limits",
1717    NULL,
1718    N_("Set global system limits."),
1719    grecs_type_string, GRECS_DFLT,
1720    &pies_limits, 0, _cb_limits,
1721    },
1722 #if PIES_SYSVINIT_ENABLED
1723   {"initdefault",
1724    N_("arg: char"),
1725    N_("Default runlevel"),
1726    grecs_type_string, GRECS_DFLT,
1727    NULL, 0, cb_initdefault,
1728   },
1729 #endif
1730   {"shutdown-timeout",
1731    "n",
1732    N_("Wait <n> seconds for all components to shut down."),
1733    grecs_type_uint, GRECS_DFLT,
1734    &shutdown_timeout, 0,
1735    NULL,
1736    },
1737   {"return-code",
1738    N_("tag: exit-code-list"),
1739    N_("Define what to do when the component finishes."),
1740    grecs_type_section, GRECS_DFLT,
1741    &default_component, 0,
1742    return_code_section_parser, NULL, return_code_keywords},
1743   {"acl",
1744    NULL,
1745    N_("Set global ACL."),
1746    grecs_type_section, GRECS_DFLT,
1747    &pies_acl, 0,
1748    acl_section_parser, NULL, acl_keywords},
1749   {"defacl",
1750    N_("name: string"),
1751    N_("Define an ACL."),
1752    grecs_type_section, GRECS_DFLT,
1753    NULL, 0,
1754    defacl_section_parser, NULL, acl_keywords},
1755   {"include-inetd",
1756    N_("file-or-dir: string"),
1757    N_("Include inetd configuration file or directory"),
1758    grecs_type_string, GRECS_DFLT,
1759    NULL, 0,
1760    _cb_include_inetd },
1761   {"include-meta1",
1762    N_("file: string"),
1763    N_("Include components from the specified MeTA1 configuration file."),
1764    grecs_type_string, GRECS_DFLT,
1765    NULL, 0,
1766    _cb_include_meta1,
1767    },
1768   {"meta1-queue-dir",
1769    NULL,
1770    N_("Set the name of MeTA1 queue directory (default /var/spool/meta1)."),
1771    grecs_type_string, GRECS_CONST,
1772    &meta1_queue_dir, 0,
1773    NULL,
1774    },
1775   {"mailer-program",
1776    NULL,
1777    N_("Full path to the mailer binary."),
1778    grecs_type_string, GRECS_CONST,
1779    &mailer_program, 0,
1780    NULL
1781   },
1782   {"mailer-command-line",
1783    NULL,
1784    N_("Mailer command line (without recipient addresses)."),
1785    grecs_type_string, GRECS_CONST,
1786    &mailer_command_line, 0,
1787    NULL
1788   },
1789   { "identity-provider", "name: string", "Configure identity provider",
1790     grecs_type_section, GRECS_INAC | GRECS_HIDDEN },
1791   {NULL}
1792 };
1793 
1794 void
config_init(void)1795 config_init (void)
1796 {
1797   grecs_include_path_setup (DEFAULT_VERSION_INCLUDE_DIR,
1798 			    DEFAULT_INCLUDE_DIR, NULL);
1799   grecs_log_to_stderr = log_to_stderr_only;
1800   pies_identity_mechanism_register (&system_identity_mechanism);
1801 #ifdef WITH_PAM
1802   pies_identity_mechanism_register (&pam_identity_mechanism);
1803 #endif
1804 }
1805 
1806 int
pies_config_parse(char const * name)1807 pies_config_parse (char const *name)
1808 {
1809   struct grecs_node *node;
1810   struct grecs_node *tree = grecs_parse (name);
1811 
1812   if (!tree)
1813     return 1;
1814 
1815   for (node = tree; node; node = node->next)
1816     {
1817       node = grecs_find_node (node, "identity-provider");
1818       if (!node)
1819 	break;
1820       pies_config_provider (node);
1821     }
1822 
1823   if (grecs_tree_process (tree, pies_keywords))
1824     return 1;
1825 
1826   grecs_tree_free (tree);
1827 
1828   if (grecs_error_count)
1829     return 1;
1830 
1831   return 0;
1832 }
1833 
1834 void
config_help(void)1835 config_help (void)
1836 {
1837   static char docstring[] =
1838     /* TRANSLATORS: do not translate words in quotes */
1839     N_("Configuration file structure for pies.\n"
1840        "For more information, use command \"info pies configuration\".");
1841   grecs_print_docstring (docstring, 0, stdout);
1842   grecs_print_statement_array (pies_keywords, 1, 0, stdout);
1843   pies_config_identity_mechanisms_help ();
1844 }
1845 
1846 int
pies_read_config(void)1847 pies_read_config (void)
1848 {
1849   struct grecs_list_entry *ep;
1850   int err = 0;
1851 
1852   component_config_begin ();
1853 
1854   for (ep = config_list->head; ep; ep = ep->next)
1855     {
1856       struct config_file *file = ep->data;
1857       if (file->syntax->parser (file->name))
1858 	++err;
1859     }
1860 
1861   if (SYSVINIT_ACTIVE)
1862     err = 0;
1863 
1864   if (err)
1865     component_config_rollback ();
1866 
1867   return err;
1868 }
1869 
1870 int
pies_reread_config(void)1871 pies_reread_config (void)
1872 {
1873   logmsg (LOG_INFO, _("reading configuration"));
1874   return pies_read_config ();
1875 }
1876 
1877 static struct config_syntax *current_syntax = &config_syntax_tab[CONF_PIES];
1878 
1879 #include "cmdline.h"
1880 
1881 
1882 static int action = ACTION_CONT;
1883 static int children_op = PIES_CHLD_NONE;
1884 
1885 void
pies_schedule_action(int act)1886 pies_schedule_action (int act)
1887 {
1888   action = act;
1889 }
1890 
1891 void
pies_schedule_children(int op)1892 pies_schedule_children (int op)
1893 {
1894   children_op |= op;
1895 }
1896 
1897 RETSIGTYPE
sig_handler(int sig)1898 sig_handler (int sig)
1899 {
1900   if (SYSVINIT_ACTIVE && sysvinit_sigtrans (sig, &action))
1901     return;
1902   switch (sig)
1903     {
1904     case SIGCHLD:
1905       pies_schedule_children (PIES_CHLD_CLEANUP);
1906       break;
1907 
1908     case SIGINT:
1909     case SIGTERM:
1910     case SIGQUIT:
1911       logmsg (LOG_NOTICE, "received signal %d", sig);
1912       pies_schedule_action (ACTION_STOP);
1913       break;
1914 
1915     case SIGHUP:
1916       logmsg (LOG_NOTICE, "received signal %d", sig);
1917       pies_schedule_action (ACTION_RELOAD);
1918       break;
1919 
1920     case SIGUSR1:
1921       logmsg (LOG_NOTICE, "received signal %d", sig);
1922       pies_schedule_action (ACTION_RESTART);
1923       break;
1924 
1925     case SIGALRM:
1926       pies_schedule_children (PIES_CHLD_WAKEUP);
1927       break;
1928     }
1929 }
1930 
1931 RETSIGTYPE
sigchld_early(int sig)1932 sigchld_early (int sig)
1933 {
1934   while (waitpid (-1, NULL, WNOHANG) != -1)
1935     ;
1936 }
1937 
1938 void
setsigvhan(RETSIGTYPE (* handler)(int signo),int * sigv,int sigc)1939 setsigvhan (RETSIGTYPE (*handler) (int signo), int *sigv, int sigc)
1940 {
1941   int i;
1942   struct sigaction act;
1943 
1944   act.sa_flags = 0;
1945   sigemptyset (&act.sa_mask);
1946   for (i = 0; i < sigc; i++)
1947     sigaddset (&act.sa_mask, sigv[i]);
1948 
1949   act.sa_handler = handler;
1950   for (i = 0; i < sigc; i++)
1951     {
1952       sigaction (sigv[i], &act, NULL);
1953     }
1954 }
1955 
1956 #define PIES_MAXSIG 16
1957 static int default_sigv[] = {
1958   SIGCHLD,
1959   SIGTERM,
1960   SIGQUIT,
1961   SIGINT,
1962   SIGHUP,
1963   SIGUSR1,
1964   SIGALRM,
1965   SIGPIPE
1966 };
1967 
1968 static int extra_sigv[PIES_MAXSIG];
1969 static int extra_sigc;
1970 
1971 void
add_extra_sigv(int * sigv,int sigc)1972 add_extra_sigv (int *sigv, int sigc)
1973 {
1974   while (sigc--)
1975     {
1976       if (extra_sigc == ARRAY_SIZE(extra_sigv))
1977 	abort ();
1978       extra_sigv[extra_sigc++] = *sigv++;
1979     }
1980 }
1981 
1982 void
signal_setup(RETSIGTYPE (* sf)(int))1983 signal_setup (RETSIGTYPE (*sf) (int))
1984 {
1985   setsigvhan (sf, default_sigv, ARRAY_SIZE (default_sigv));
1986   if (extra_sigc)
1987     setsigvhan (sf, extra_sigv, extra_sigc);
1988 }
1989 
1990 
1991 pid_t
pidfile_read(int must_exist)1992 pidfile_read (int must_exist)
1993 {
1994   int c;
1995   pid_t n = 0;
1996   FILE *fp = fopen (pidfile, "r");
1997   if (!fp)
1998     {
1999       if (must_exist && errno != ENOENT)
2000 	logmsg (LOG_ERR, _("cannot open file %s: %s"),
2001 		pidfile, strerror (errno));
2002       return -1;
2003     }
2004 
2005   while ((c = fgetc (fp)) != EOF)
2006     {
2007       if (isdigit (c))
2008 	n = n * 10 + c - '0';
2009       else if (c == '\n')
2010 	break;
2011       else
2012 	{
2013 	  logmsg (LOG_ERR,
2014 		  _("unexpected character %#03o in pidfile %s"),
2015 		  c, pidfile);
2016 	  return -1;
2017 	}
2018     }
2019   fclose (fp);
2020   if (n && kill (n, 0))
2021     {
2022       if (errno != ESRCH)
2023 	logmsg (LOG_ERR,
2024 		_("cannot signal master process %lu: %s"),
2025 		(unsigned long) n, strerror (errno));
2026       if (errno == EPERM)
2027 	return n;		/* be on the safe side */
2028       return -1;
2029     }
2030   return n;
2031 }
2032 
2033 
2034 enum pies_status
2035 {
2036   pies_status_ctr,		/* clear to run */
2037   pies_status_stale,
2038   pies_status_noresp,
2039   pies_status_running
2040 };
2041 
2042 //FIXME: If telinit?
2043 enum pies_status
pies_check_status(pid_t * ppid)2044 pies_check_status (pid_t *ppid)
2045 {
2046   pid_t pid = pidfile_read (0);
2047 
2048   if (pid <= 0)
2049     return pies_status_ctr;
2050 
2051   *ppid = pid;
2052 
2053   if (kill (pid, 0))
2054     return pies_status_stale;
2055 
2056   return pies_status_running;
2057 }
2058 
2059 #define pies_control_url() control.url->string
2060 
2061 int
request_restart_components(size_t cc,char ** cv)2062 request_restart_components (size_t cc, char **cv)
2063 {
2064   char **argv;
2065   size_t i, j;
2066 
2067   argv = grecs_calloc (5 + 3 * cc - 1, sizeof (*argv));
2068   argv[0] = "piesctl";
2069   argv[1] = "--url";
2070   argv[2] = (char*) pies_control_url ();
2071   argv[3] = "restart";
2072   j = 4;
2073   for (i = 0; i < cc; i++)
2074     {
2075       if (i > 0)
2076 	argv[j++] = "or";
2077       argv[j++] = "component";
2078       argv[j++] = cv[i];
2079     }
2080   argv[j] = NULL;
2081   execvp (argv[0], argv);
2082   logmsg (LOG_ERR, "can't run piesctl: %s", strerror (errno));
2083   return EX_OSFILE;
2084 }
2085 
2086 void
list_components(void)2087 list_components (void)
2088 {
2089   char *argv[5];
2090 
2091   argv[0] = "piesctl";
2092   argv[1] = "--url";
2093   argv[2] = (char*) pies_control_url ();
2094   argv[3] = "list";
2095   argv[4] = NULL;
2096   execvp (argv[0], argv);
2097   logmsg (LOG_ERR, "can't run piesctl: %s", strerror (errno));
2098   exit (EX_OSFILE);
2099 }
2100 
2101 
2102 int
request_reload(void)2103 request_reload (void)
2104 {
2105   pid_t pid = pidfile_read (1);
2106 
2107   if (pid == -1)
2108     {
2109       logmsg (LOG_CRIT, _("pies is not running"));
2110       return 1;
2111     }
2112 
2113   logmsg (LOG_INFO, _("reloading pies at PID %lu"), (unsigned long) pid);
2114   return kill (pid, SIGHUP) ? EX_SOFTWARE : 0;
2115 }
2116 
2117 int
request_status(void)2118 request_status (void)
2119 {
2120   pid_t pid;
2121 
2122   switch (pies_check_status (&pid))
2123     {
2124     case pies_status_ctr:
2125       logmsg (LOG_INFO, _("pies is not running"));
2126       break;
2127 
2128     case pies_status_stale:
2129       logmsg (LOG_INFO,
2130 	      _("pies is not running, but a pidfile "
2131 		"is found (pid %lu)"), (unsigned long) pid);
2132       return 1;
2133 
2134     case pies_status_noresp:
2135       logmsg (LOG_INFO,
2136 	      _("pies seems to run with pid %lu, but is not responding"),
2137 	      (unsigned long) pid);
2138       return 2;
2139 
2140     case pies_status_running:
2141       list_components ();
2142       break;
2143     }
2144   return 0;
2145 }
2146 
2147 int
request_stop(void)2148 request_stop (void)
2149 {
2150   pid_t pid = pidfile_read (1);
2151 
2152   if (pid == -1)
2153     {
2154       logmsg (LOG_CRIT, _("pies is not running"));
2155       return EX_USAGE;
2156     }
2157 
2158   logmsg (LOG_INFO, _("stopping pies at PID %lu"), (unsigned long) pid);
2159   return kill (pid, SIGTERM) ? EX_SOFTWARE : 0;
2160 }
2161 
2162 
2163 /* Pidfile */
2164 /* Check whether pidfile NAME exists and if so, whether its PID is still
2165    active. Exit if it is. */
2166 void
check_pidfile(char * name)2167 check_pidfile (char *name)
2168 {
2169   unsigned long pid;
2170   FILE *fp = fopen (name, "r");
2171   if (!fp)
2172     {
2173       if (errno == ENOENT)
2174 	return;
2175       logmsg (LOG_ERR, _("cannot open file %s: %s"),
2176 	      name, strerror (errno));
2177       exit (EX_TEMPFAIL);
2178     }
2179   if (fscanf (fp, "%lu", &pid) != 1)
2180     {
2181       logmsg (LOG_ERR, ("cannot get pid from pidfile `%s'"), name);
2182     }
2183   else
2184     {
2185       if (kill (pid, 0) == 0)
2186 	{
2187 	  logmsg (LOG_ERR,
2188 		  _
2189 		  ("%s appears to run with pid %lu. If it does not, remove %s and retry."),
2190 		  program_name, pid, name);
2191 	  exit (EX_USAGE);
2192 	}
2193     }
2194   fclose (fp);
2195   if (unlink (pidfile))
2196     {
2197       logfuncall ("unlink", name, errno);
2198       exit (EX_USAGE);
2199     }
2200 }
2201 
2202 void
create_pidfile(char * name)2203 create_pidfile (char *name)
2204 {
2205   FILE *fp = fopen (name, "w");
2206   if (!fp)
2207     {
2208       logmsg (LOG_ERR, _("cannot create pidfile `%s': %s"),
2209 	      name, strerror (errno));
2210       exit (EX_TEMPFAIL);
2211     }
2212   fprintf (fp, "%lu", (unsigned long) getpid ());
2213   fclose (fp);
2214 }
2215 
2216 void
remove_pidfile(char * name)2217 remove_pidfile (char *name)
2218 {
2219   if (unlink (name))
2220     logfuncall ("unlink", name, errno);
2221 }
2222 
2223 
2224 static void
set_mailer_argcv(void)2225 set_mailer_argcv (void)
2226 {
2227   int i;
2228   struct wordsplit ws;
2229 
2230   if (wordsplit (mailer_command_line, &ws, WRDSF_DEFFLAGS))
2231     {
2232       logmsg (LOG_CRIT, _("cannot parse mailer command line: %s"),
2233 	      strerror (errno));
2234       exit (EX_CONFIG);
2235     }
2236   mailer_argc = ws.ws_wordc;
2237   mailer_argv = grecs_calloc (mailer_argc + 1, sizeof (mailer_argv[0]));
2238   for (i = 0; i < mailer_argc; i++)
2239     mailer_argv[i] = grecs_strdup (ws.ws_wordv[i]);
2240   mailer_argv[i] = NULL;
2241   wordsplit_free (&ws);
2242 }
2243 
2244 static inline int
init_emu(void)2245 init_emu (void)
2246 {
2247 #ifdef INIT_EMU
2248 # warning "pies compiled with init emulation code"
2249   char *emu = getenv ("INIT_EMU");
2250   if (emu)
2251     {
2252       char *inittab  = strtok (emu, ":");
2253       char *piesinit = strtok (NULL, ":");
2254 
2255       config_file_add_type (CONF_INITTAB, inittab);
2256       config_file_add_type (CONF_PIES,
2257 			    piesinit ? piesinit : "/etc/pies.init");
2258 
2259       init_fifo = getenv ("INIT_FIFO");
2260       if (!init_fifo)
2261 	init_fifo = "/tmp/initctl";
2262 
2263       init_process = 1;
2264       fprintf (stderr, "%s: running in init emulation mode\n", program_name);
2265       return 1;
2266     }
2267   else
2268     {
2269       fprintf (stderr, "%s: Notice:\n", program_name);
2270       fprintf (stderr,
2271 	       "    To enable init emulation code, define environment variable\n"
2272 	       "         INIT_EMU=<inittab>[:<config>]\n"
2273 	       "    where <inittab> and <config> are names of the inittab and\n"
2274 	       "    Pies configuration files, correspondingly.\n"
2275 	       "\n"
2276 	       "    To override the default FIFO name, define:\n"
2277 	       "         INIT_FIFO=<pathname>\n");
2278       fprintf (stderr, "%s: End of notice\n", program_name);
2279     }
2280 #endif
2281   return 0;
2282 }
2283 
2284 static void
set_conf_file_names(const char * base)2285 set_conf_file_names (const char *base)
2286 {
2287   if (SYSVINIT_ACTIVE)
2288     {
2289       config_file_add_type (CONF_INITTAB, "/etc/inittab");
2290       config_file_add_type (CONF_PIES, "/etc/pies.init");
2291     }
2292   else if (!config_list && !init_emu ())
2293     {
2294       char *name = mkfilename (SYSCONFDIR, base, ".conf");
2295       config_file_add (current_syntax, name);
2296       free (name);
2297     }
2298 }
2299 
2300 static void
set_state_file_names(const char * base)2301 set_state_file_names (const char *base)
2302 {
2303   if (!pidfile)
2304     pidfile = mkfilename (statedir, base, ".pid");
2305   if (!qotdfile)
2306     qotdfile = mkfilename (statedir, base, ".qotd");
2307 }
2308 
2309 /* Return 1 if pies is run from docker and 0 otherwise. */
2310 static int
is_docker(void)2311 is_docker (void)
2312 {
2313   FILE *fp;
2314   char *id = NULL;
2315   int res;
2316 
2317   fp = fopen ("/proc/self/cgroup", "r");
2318   if (!fp)
2319     return 0;
2320   res = fscanf (fp, "%*d:%*[^:]:/docker/%ms\n", &id) == 1;
2321   fclose (fp);
2322   free (id);
2323   return res;
2324 }
2325 
2326 /*
2327  * Check if `--no-init' option is present in argv/argc.  Return 1 if so,
2328  * 0 otherwise.
2329  */
2330 static int
no_init_option(int argc,char ** argv)2331 no_init_option (int argc, char **argv)
2332 {
2333   int i;
2334   for (i = 1; i < argc; i++)
2335     {
2336       if (strcmp (argv[i], "--no-init") == 0)
2337 	return 1;
2338     }
2339   return 0;
2340 }
2341 
2342 /*
2343  * init_detect - detect if we're run as init process.
2344  *
2345  * Pies runs as init process if (1) it has PID 1, (2) is not started as docker
2346  * entrypoint, and (3) the `--no-init' command line option is not given.
2347  *
2348  * The function sets init_process to 1 if pies runs as init process and 0
2349  * otherwise.
2350  *
2351  * No matter the result, if PID is 1 it installs an early SIGCHLD handler,
2352  * to ensure that the configuration preprocessor (if such is started) will be
2353  * cleaned up properly on exit.
2354  */
2355 static void
init_detect(int argc,char ** argv)2356 init_detect (int argc, char **argv)
2357 {
2358   init_process = getpid () == 1;
2359   if (init_process)
2360     {
2361       int s[] = { SIGCHLD };
2362       setsigvhan (sigchld_early, s, 1);
2363       if (no_init_option (argc, argv) || is_docker ())
2364 	init_process = 0;
2365     }
2366 }
2367 
2368 size_t pies_master_argc;
2369 char **pies_master_argv;
2370 
2371 int
main(int argc,char ** argv)2372 main (int argc, char **argv)
2373 {
2374   pid_t pid;
2375   extern char **environ;
2376   struct grecs_list_entry *ep;
2377   int diag_flags;
2378 
2379   set_program_name (argv[0]);
2380 #ifdef ENABLE_NLS
2381   setlocale (LC_ALL, "");
2382   bindtextdomain (PACKAGE, LOCALEDIR);
2383   textdomain (PACKAGE);
2384 #endif
2385   mf_proctitle_init (argc, argv, environ);
2386 
2387   grecs_print_diag_fun = pies_diag_printer;
2388 
2389   pies_master_argc = argc;
2390   pies_master_argv = argv;
2391 
2392   set_quoting_style (NULL, shell_quoting_style);
2393 
2394   init_detect (argc, argv);
2395 
2396   /* Set default logging */
2397   if (SYSVINIT_ACTIVE)
2398     {
2399       pies_log_facility = LOG_DAEMON;
2400       diag_flags = DIAG_TO_STDERR | DIAG_REOPEN_LOG;
2401     }
2402   else
2403     diag_flags = DIAG_TO_SYSLOG | (stderr_closed_p () ? 0 : DIAG_TO_STDERR);
2404 
2405   diag_setup (diag_flags);
2406 
2407   config_init ();
2408 
2409   parse_options (&argc, &argv);
2410 
2411   if (argc && !(command == COM_RESTART_COMPONENT
2412 		|| command == COM_TRACE_DEPEND
2413 		|| command == COM_TRACE_PREREQ))
2414     {
2415       logmsg (LOG_ERR, "extra command line arguments");
2416       exit (EX_USAGE);
2417     }
2418 
2419   if (!instance)
2420     {
2421       instance = strrchr (program_name, '/');
2422       if (!instance)
2423 	instance = (char*) program_name;
2424       else
2425 	instance++;
2426     }
2427   setenv ("PIES_INSTANCE", instance, 1);
2428   pies_log_tag = grecs_strdup (instance);
2429 
2430   set_conf_file_names (instance);
2431 
2432   if (SYSVINIT_ACTIVE || !DEFAULT_PREPROCESSOR)
2433     grecs_preprocessor = NULL;
2434   else
2435     grecs_preprocessor = pp_command_line ();
2436 
2437   if (preprocess_only)
2438     {
2439       for (ep = config_list->head; ep; ep = ep->next)
2440 	{
2441 	  struct config_file *file = ep->data;
2442 
2443 	  if (file->syntax == &config_syntax_tab[CONF_PIES]
2444 	      && grecs_preproc_run (file->name, grecs_preprocessor))
2445 	    exit (EX_CONFIG);
2446 	}
2447       exit (0);
2448     }
2449   else if (pies_read_config ())
2450     exit (EX_CONFIG);
2451 
2452   component_config_commit ();
2453 
2454   set_state_file_names (instance);
2455   set_mailer_argcv ();
2456 
2457   if (pies_envop)
2458     {
2459       environ_t *env;
2460 
2461       if ((env = environ_create (environ)) == NULL)
2462 	{
2463 	  logmsg (LOG_CRIT, "environ_create: %s", strerror (errno));
2464 	  exit (EX_OSERR);
2465 	}
2466       if (envop_exec (pies_envop, env))
2467 	{
2468 	  logmsg (LOG_CRIT, "environ_exec: %s", strerror (errno));
2469 	  exit (EX_OSERR);
2470 	}
2471       environ = environ_ptr (env);
2472 
2473       if (debug_level >= 4)
2474 	{
2475 	  int i;
2476 	  logmsg_printf (LOG_DEBUG, "environment: ");
2477 	  for (i = 0; environ[i]; i++)
2478 	    logmsg_printf (LOG_DEBUG, "%s ", environ[i]);
2479 	  logmsg_printf (LOG_DEBUG, "\n");
2480 	}
2481     }
2482 
2483   if (lint_mode)
2484     exit (0);
2485 
2486   /* Re-setup logging: it might have been reset in the config file */
2487   diag_setup (log_to_stderr_only ? DIAG_TO_STDERR : 0);
2488 
2489   if (!control.url)
2490     {
2491       char const *str = default_control_url[init_process];
2492       if (pies_url_create (&control.url, str))
2493 	{
2494 	  logmsg (LOG_CRIT, _("%s: cannot create control URL: %s"),
2495 		  str, strerror (errno));
2496 	  if (!init_process)
2497 	    exit (EX_OSERR);
2498 	}
2499     }
2500 
2501   switch (command)
2502     {
2503     case COM_RESTART_COMPONENT:
2504       pies_priv_setup (&pies_privs);
2505       if (pies_umask)
2506 	umask (pies_umask);
2507       exit (request_restart_components (argc, argv));
2508 
2509     case COM_RELOAD:
2510       exit (request_reload ());
2511 
2512     case COM_STATUS:
2513       exit (request_status ());
2514 
2515     case COM_STOP:
2516       exit (request_stop ());
2517 
2518     case COM_DUMP_DEPMAP:
2519       components_dump_depmap ();
2520       exit (0);
2521 
2522     case COM_TRACE_DEPEND:
2523       components_trace (argv, depmap_row);
2524       exit (0);
2525 
2526     case COM_TRACE_PREREQ:
2527       components_trace (argv, depmap_col);
2528       exit (0);
2529 
2530     default:
2531       pies_priv_setup (&pies_privs);
2532       if (pies_umask)
2533 	umask (pies_umask);
2534     }
2535 
2536   if (SYSVINIT_ACTIVE)
2537     {
2538       foreground = 1;
2539       sysvinit_begin ();
2540     }
2541   else
2542     switch (pies_check_status (&pid))
2543       {
2544       case pies_status_ctr:
2545 	break;
2546       case pies_status_stale:
2547       case pies_status_noresp:
2548 	if (!force_option)
2549 	  {
2550 	    logmsg (LOG_ERR,
2551 		    _("another pies instance may be running (pid %lu), "
2552 		      "use --force to override"), (unsigned long) pid);
2553 	    exit (EX_USAGE);
2554 	  }
2555 	break;
2556 
2557       case pies_status_running:
2558 	logmsg (LOG_ERR, _("another pies instance already running (pid %lu)"),
2559 		(unsigned long) pid);
2560 	exit (EX_USAGE);
2561       }
2562 
2563   if (!foreground)
2564     {
2565       check_pidfile (pidfile);
2566       if (daemon (0, 0) == -1)
2567 	{
2568 	  logfuncall ("daemon", NULL, errno);
2569 	  exit (EX_SOFTWARE);
2570 	}
2571       diag_setup (DIAG_TO_SYSLOG);
2572     }
2573 
2574   logmsg (LOG_INFO, _("%s %s starting"), proginfo.package, proginfo.version);
2575 
2576   if (!SYSVINIT_ACTIVE)
2577     {
2578       if (ctl_open ())
2579 	exit (EX_UNAVAILABLE);
2580       create_pidfile (pidfile);
2581     }
2582 
2583   if (pies_master_argv[0][0] != '/')
2584     logmsg (LOG_NOTICE,
2585 	    _("not started as an absolute pathname; "
2586 	      "restart will not work"));
2587 
2588   signal_setup (sig_handler);
2589 
2590   progman_create_sockets ();
2591   program_init_startup ();
2592   progman_start ();
2593 
2594   do
2595     {
2596       if (children_op == PIES_CHLD_NONE)
2597 	pies_pause ();
2598       switch (action)
2599 	{
2600 	case ACTION_RESTART:
2601 	  if (pies_master_argv[0][0] != '/' || SYSVINIT_ACTIVE)
2602 	    {
2603 	      logmsg (LOG_INFO, _("restart command ignored"));
2604 	      action = ACTION_CONT;
2605 	    }
2606 	  break;
2607 
2608 	case ACTION_RELOAD:
2609 	  if (pies_reread_config ())
2610 	    {
2611 	      action = ACTION_CONT;
2612 	      break;
2613 	    }
2614 	  /* fall through */
2615 	case ACTION_COMMIT:
2616 	  component_config_commit ();
2617 	  if (SYSVINIT_ACTIVE)
2618 	    sysvinit_runlevel_setup (PIES_COMP_DEFAULT);
2619 	  progman_create_sockets ();
2620 	  progman_start ();
2621 
2622 	  pies_schedule_children (PIES_CHLD_WAKEUP);
2623 	  action = ACTION_CONT;
2624 	  break;
2625 
2626 	case ACTION_STOP:
2627 	  if (SYSVINIT_ACTIVE)
2628 	    {
2629 	      debug (1, ("ignoring stop/restart"));
2630 	      action = ACTION_CONT;
2631 	    }
2632 	  break;
2633 
2634 	case ACTION_CTRLALTDEL:
2635 	  debug (1, ("ctrl-alt-del"));
2636 	  if (SYSVINIT_ACTIVE)
2637 	    sysvinit_runlevel_setup (PIES_COMP_MASK (pies_comp_ctrlaltdel));
2638 	  pies_schedule_children (PIES_CHLD_WAKEUP);
2639 	  action = ACTION_CONT;
2640 	  break;
2641 
2642 	case ACTION_KBREQUEST:
2643 	  debug (1, ("kbrequest"));
2644 	  if (SYSVINIT_ACTIVE)
2645 	    sysvinit_runlevel_setup (PIES_COMP_MASK (pies_comp_kbrequest));
2646 	  pies_schedule_children (PIES_CHLD_WAKEUP);
2647 	  action = ACTION_CONT;
2648 	  break;
2649 
2650 	case ACTION_POWER:
2651 	  debug (1, ("SIGPWR"));
2652 	  if (SYSVINIT_ACTIVE)
2653 	    sysvinit_power ();
2654 	  pies_schedule_children (PIES_CHLD_WAKEUP);
2655 	  action = ACTION_CONT;
2656 	}
2657       if (action == ACTION_CONT)
2658 	{
2659 	  if (children_op & PIES_CHLD_RESCHEDULE_ALARM)
2660 	    progman_recompute_alarm ();
2661 	  if (children_op & PIES_CHLD_GC)
2662 	    progman_gc ();
2663 	  if (children_op & PIES_CHLD_CLEANUP)
2664 	    progman_cleanup (0);
2665 	  if (children_op & PIES_CHLD_WAKEUP)
2666 	    progman_wake_sleeping (1);
2667 	  children_op = PIES_CHLD_NONE;
2668 	}
2669     }
2670   while (SYSVINIT_ACTIVE || action == ACTION_CONT);
2671 
2672   progman_stop ();
2673   remove_pidfile (pidfile);
2674 
2675   if (action == ACTION_RESTART)
2676     {
2677       pies_close_fds (3);
2678       signal_setup (SIG_DFL);
2679       execv (pies_master_argv[0], pies_master_argv);
2680     }
2681 
2682   logmsg (LOG_INFO, _("%s %s terminated"), proginfo.package, proginfo.version);
2683   exit (EX_OK);
2684 }
2685 
2686 void
xalloc_die(void)2687 xalloc_die (void)
2688 {
2689   logmsg (LOG_CRIT, _("not enough memory"));
2690   abort ();
2691 }
2692 /* EOF */
2693