Postfix3.3.1
postconf_master.c
[詳解]
1 /*++
2 /* NAME
3 /* postconf_master 3
4 /* SUMMARY
5 /* support for master.cf
6 /* SYNOPSIS
7 /* #include <postconf.h>
8 /*
9 /* const char pcf_daemon_options_expecting_value[];
10 /*
11 /* void pcf_read_master(fail_on_open)
12 /* int fail_on_open;
13 /*
14 /* void pcf_show_master_entries(fp, mode, service_filters)
15 /* VSTREAM *fp;
16 /* int mode;
17 /* char **service_filters;
18 /*
19 /* void pcf_show_master_fields(fp, mode, n_filters, field_filters)
20 /* VSTREAM *fp;
21 /* int mode;
22 /* int n_filters;
23 /* char **field_filters;
24 /*
25 /* void pcf_edit_master_field(masterp, field, new_value)
26 /* PCF_MASTER_ENT *masterp;
27 /* int field;
28 /* const char *new_value;
29 /*
30 /* void pcf_show_master_params(fp, mode, argc, **param_filters)
31 /* VSTREAM *fp;
32 /* int mode;
33 /* int argc;
34 /* char **param_filters;
35 /*
36 /* void pcf_edit_master_param(masterp, mode, param_name, param_value)
37 /* PCF_MASTER_ENT *masterp;
38 /* int mode;
39 /* const char *param_name;
40 /* const char *param_value;
41 /* AUXILIARY FUNCTIONS
42 /* const char *pcf_parse_master_entry(masterp, buf)
43 /* PCF_MASTER_ENT *masterp;
44 /* const char *buf;
45 /*
46 /* void pcf_print_master_entry(fp, mode, masterp)
47 /* VSTREAM *fp;
48 /* int mode;
49 /* PCF_MASTER_ENT *masterp;
50 /*
51 /* void pcf_free_master_entry(masterp)
52 /* PCF_MASTER_ENT *masterp;
53 /* DESCRIPTION
54 /* pcf_read_master() reads entries from master.cf into memory.
55 /*
56 /* pcf_show_master_entries() writes the entries in the master.cf
57 /* file to the specified stream.
58 /*
59 /* pcf_show_master_fields() writes name/type/field=value records
60 /* to the specified stream.
61 /*
62 /* pcf_edit_master_field() updates the value of a single-column
63 /* or multi-column attribute.
64 /*
65 /* pcf_show_master_params() writes name/type/parameter=value
66 /* records to the specified stream.
67 /*
68 /* pcf_edit_master_param() updates, removes or adds the named
69 /* parameter in a master.cf entry (the remove request ignores
70 /* the parameter value).
71 /*
72 /* pcf_daemon_options_expecting_value[] is an array of master.cf
73 /* daemon command-line options that expect an option value.
74 /*
75 /* pcf_parse_master_entry() parses a (perhaps multi-line)
76 /* string that contains a complete master.cf entry, and
77 /* normalizes daemon command-line options to simplify further
78 /* handling.
79 /*
80 /* pcf_print_master_entry() prints a parsed master.cf entry.
81 /*
82 /* pcf_free_master_entry() returns storage to the heap that
83 /* was allocated by pcf_parse_master_entry().
84 /*
85 /* Arguments
86 /* .IP fail_on_open
87 /* Specify FAIL_ON_OPEN if open failure is a fatal error,
88 /* WARN_ON_OPEN if a warning should be logged instead.
89 /* .IP fp
90 /* Output stream.
91 /* .IP mode
92 /* Bit-wise OR of flags. Flags other than the following are
93 /* ignored.
94 /* .RS
95 /* .IP PCF_FOLD_LINE
96 /* Wrap long output lines.
97 /* .IP PCF_SHOW_EVAL
98 /* Expand $name in parameter values.
99 /* .IP PCF_EDIT_EXCL
100 /* Request that pcf_edit_master_param() removes the parameter.
101 /* .RE
102 /* .IP n_filters
103 /* The number of command-line filters.
104 /* .IP field_filters
105 /* A list of zero or more service field patterns (name/type/field).
106 /* The output is formatted as "name/type/field = value". If
107 /* no filters are specified, pcf_show_master_fields() outputs
108 /* the fields of all master.cf entries in the specified order.
109 /* .IP param_filters
110 /* A list of zero or more service parameter patterns
111 /* (name/type/parameter). The output is formatted as
112 /* "name/type/parameter = value". If no filters are specified,
113 /* pcf_show_master_params() outputs the parameters of all
114 /* master.cf entries in sorted order.
115 /* .IP service_filters
116 /* A list of zero or more service patterns (name or name/type).
117 /* If no filters are specified, pcf_show_master_entries()
118 /* outputs all master.cf entries in the specified order.
119 /* .IP field
120 /* Index into parsed master.cf entry.
121 /* .IP new_value
122 /* Replacement value for the specified field. It is split in
123 /* whitespace in case of a multi-field attribute.
124 /* DIAGNOSTICS
125 /* Problems are reported to the standard error stream.
126 /* LICENSE
127 /* .ad
128 /* .fi
129 /* The Secure Mailer license must be distributed with this software.
130 /* AUTHOR(S)
131 /* Wietse Venema
132 /* IBM T.J. Watson Research
133 /* P.O. Box 704
134 /* Yorktown Heights, NY 10598, USA
135 /*
136 /* Wietse Venema
137 /* Google, Inc.
138 /* 111 8th Avenue
139 /* New York, NY 10011, USA
140 /*--*/
141 
142 /* System library. */
143 
144 #include <sys_defs.h>
145 #include <string.h>
146 #include <stdlib.h>
147 #include <stdarg.h>
148 
149 /* Utility library. */
150 
151 #include <msg.h>
152 #include <mymalloc.h>
153 #include <vstring.h>
154 #include <argv.h>
155 #include <vstream.h>
156 #include <readlline.h>
157 #include <stringops.h>
158 #include <split_at.h>
159 
160 /* Global library. */
161 
162 #include <mail_params.h>
163 
164 /* Master library. */
165 
166 #include <master_proto.h>
167 
168 /* Application-specific. */
169 
170 #include <postconf.h>
171 
173 
174  /*
175  * Data structure to capture a command-line service field filter.
176  */
177 typedef struct {
178  int match_count; /* hit count */
179  const char *raw_text; /* full pattern text */
180  ARGV *service_pattern; /* parsed service name, type, ... */
181  int field_pattern; /* parsed field pattern */
182  const char *param_pattern; /* parameter pattern */
184 
185  /*
186  * Valid inputs.
187  */
188 static const char *pcf_valid_master_types[] = {
193  0,
194 };
195 
196 static const char pcf_valid_bool_types[] = "yn-";
197 
198 #define STR(x) vstring_str(x)
199 
200 /* pcf_extract_field - extract text from {}, trim leading/trailing blanks */
201 
202 static void pcf_extract_field(ARGV *argv, int field, const char *parens)
203 {
204  char *arg = argv->argv[field];
205  char *err;
206 
207  if ((err = extpar(&arg, parens, EXTPAR_FLAG_STRIP)) != 0) {
208  msg_warn("%s: %s", MASTER_CONF_FILE, err);
209  myfree(err);
210  }
211  argv_replace_one(argv, field, arg);
212 }
213 
214 /* pcf_normalize_nameval - normalize name = value from inside {} */
215 
216 static void pcf_normalize_nameval(ARGV *argv, int field)
217 {
218  char *arg = argv->argv[field];
219  char *name;
220  char *value;
221  const char *err;
222  char *normalized;
223 
224  if ((err = split_nameval(arg, &name, &value)) != 0) {
225  msg_warn("%s: %s: \"%s\"", MASTER_CONF_FILE, err, arg);
226  } else {
227  normalized = concatenate(name, "=", value, (char *) 0);
228  argv_replace_one(argv, field, normalized);
229  myfree(normalized);
230  }
231 }
232 
233 /* pcf_normalize_daemon_args - bring daemon arguments into canonical form */
234 
235 static void pcf_normalize_daemon_args(ARGV *argv)
236 {
237  int field;
238  char *arg;
239  char *cp;
240  char *junk;
241  int extract_field;
242 
243  /*
244  * Normalize options to simplify later processing.
245  */
246  for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
247  arg = argv->argv[field];
248  if (arg[0] != '-' || strcmp(arg, "--") == 0)
249  break;
250  for (cp = arg + 1; *cp; cp++) {
251  if (strchr(pcf_daemon_options_expecting_value, *cp) != 0
252  && cp > arg + 1) {
253  /* Split "-stuffozz" into "-stuff" and "-ozz". */
254  junk = concatenate("-", cp, (char *) 0);
255  argv_insert_one(argv, field + 1, junk);
256  myfree(junk);
257  *cp = 0; /* XXX argv_replace_one() */
258  break;
259  }
260  }
261  if (strchr(pcf_daemon_options_expecting_value, arg[1]) == 0)
262  /* Option requires no value. */
263  continue;
264  if (arg[2] != 0) {
265  /* Split "-oname=value" into "-o" "name=value". */
266  argv_insert_one(argv, field + 1, arg + 2);
267  arg[2] = 0; /* XXX argv_replace_one() */
268  field += 1;
269  extract_field = (argv->argv[field][0] == CHARS_BRACE[0]);
270  } else if (argv->argv[field + 1] != 0) {
271  /* Already in "-o" "name=value" form. */
272  field += 1;
273  extract_field = (argv->argv[field][0] == CHARS_BRACE[0]);
274  } else
275  extract_field = 0;
276  /* Extract text inside {}, optionally convert to name=value. */
277  if (extract_field) {
278  pcf_extract_field(argv, field, CHARS_BRACE);
279  if (argv->argv[field - 1][1] == 'o')
280  pcf_normalize_nameval(argv, field);
281  }
282  }
283  /* Normalize non-option arguments. */
284  for ( /* void */ ; argv->argv[field] != 0; field++)
285  /* Extract text inside {}. */
286  if (argv->argv[field][0] == CHARS_BRACE[0])
287  pcf_extract_field(argv, field, CHARS_BRACE);
288 }
289 
290 /* pcf_fix_fatal - fix multiline text before release */
291 
292 static NORETURN PRINTFLIKE(1, 2) pcf_fix_fatal(const char *fmt,...)
293 {
294  VSTRING *buf = vstring_alloc(100);
295  va_list ap;
296 
297  /*
298  * Replace newline with whitespace.
299  */
300  va_start(ap, fmt);
301  vstring_vsprintf(buf, fmt, ap);
302  va_end(ap);
303  translit(STR(buf), "\n", " ");
304  msg_fatal("%s", STR(buf));
305  /* NOTREACHED */
306 }
307 
308 /* pcf_check_master_entry - sanity check master.cf entry */
309 
310 static void pcf_check_master_entry(ARGV *argv, const char *raw_text)
311 {
312  const char **cpp;
313  char *cp;
314  int len;
315  int field;
316 
317  cp = argv->argv[PCF_MASTER_FLD_TYPE];
318  for (cpp = pcf_valid_master_types; /* see below */ ; cpp++) {
319  if (*cpp == 0)
320  pcf_fix_fatal("invalid " PCF_MASTER_NAME_TYPE " field \"%s\" in \"%s\"",
321  cp, raw_text);
322  if (strcmp(*cpp, cp) == 0)
323  break;
324  }
325 
326  for (field = PCF_MASTER_FLD_PRIVATE; field <= PCF_MASTER_FLD_CHROOT; field++) {
327  cp = argv->argv[field];
328  if (cp[1] != 0 || strchr(pcf_valid_bool_types, *cp) == 0)
329  pcf_fix_fatal("invalid %s field \"%s\" in \"%s\"",
330  pcf_str_field_pattern(field), cp, raw_text);
331  }
332 
333  cp = argv->argv[PCF_MASTER_FLD_WAKEUP];
334  len = strlen(cp);
335  if (len > 0 && cp[len - 1] == '?')
336  len--;
337  if (!(cp[0] == '-' && len == 1) && strspn(cp, "0123456789") != len)
338  pcf_fix_fatal("invalid " PCF_MASTER_NAME_WAKEUP " field \"%s\" in \"%s\"",
339  cp, raw_text);
340 
341  cp = argv->argv[PCF_MASTER_FLD_MAXPROC];
342  if (strcmp("-", cp) != 0 && cp[strspn(cp, "0123456789")] != 0)
343  pcf_fix_fatal("invalid " PCF_MASTER_NAME_MAXPROC " field \"%s\" in \"%s\"",
344  cp, raw_text);
345 }
346 
347 /* pcf_free_master_entry - destroy parsed entry */
348 
350 {
351  /* XX Fixme: allocation/deallocation asymmetry. */
352  myfree(masterp->name_space);
353  argv_free(masterp->argv);
354  if (masterp->valid_names)
355  htable_free(masterp->valid_names, myfree);
356  if (masterp->ro_params)
357  dict_free(masterp->ro_params);
358  if (masterp->all_params)
359  dict_free(masterp->all_params);
360  myfree((void *) masterp);
361 }
362 
363 /* pcf_parse_master_entry - parse one master line */
364 
365 const char *pcf_parse_master_entry(PCF_MASTER_ENT *masterp, const char *buf)
366 {
367  ARGV *argv;
368  char *ro_name_space;
369  char *process_name;
370 
371  /*
372  * We can't use the master daemon's master_ent routines in their current
373  * form. They convert everything to internal form, and they skip disabled
374  * services.
375  *
376  * The postconf command needs to show default fields as "-", and needs to
377  * know about all service names so that it can generate service-dependent
378  * parameter names (transport-dependent etc.).
379  *
380  * XXX Do per-field sanity checks.
381  */
383  if (argv->argc < PCF_MASTER_MIN_FIELDS) {
384  argv_free(argv); /* Coverity 201311 */
385  return ("bad field count");
386  }
387  pcf_check_master_entry(argv, buf);
388  pcf_normalize_daemon_args(argv);
389  masterp->name_space =
390  concatenate(argv->argv[0], PCF_NAMESP_SEP_STR, argv->argv[1], (char *) 0);
391  ro_name_space =
392  concatenate("ro", PCF_NAMESP_SEP_STR, masterp->name_space, (char *) 0);
393  masterp->argv = argv;
394  masterp->valid_names = 0;
395  process_name = basename(argv->argv[PCF_MASTER_FLD_CMD]);
396  dict_update(ro_name_space, VAR_PROCNAME, process_name);
397  dict_update(ro_name_space, VAR_SERVNAME,
398  strcmp(process_name, argv->argv[0]) != 0 ?
399  argv->argv[0] : process_name);
400  masterp->ro_params = dict_handle(ro_name_space);
401  myfree(ro_name_space);
402  masterp->all_params = 0;
403  return (0);
404 }
405 
406 /* pcf_read_master - read and digest the master.cf file */
407 
408 void pcf_read_master(int fail_on_open_error)
409 {
410  const char *myname = "pcf_read_master";
411  char *path;
412  VSTRING *buf;
413  VSTREAM *fp;
414  const char *err;
415  int entry_count = 0;
416  int line_count;
417  int last_line = 0;
418 
419  /*
420  * Sanity check.
421  */
422  if (pcf_master_table != 0)
423  msg_panic("%s: master table is already initialized", myname);
424 
425  /*
426  * Get the location of master.cf.
427  */
428  if (var_config_dir == 0)
430  path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
431 
432  /*
433  * Initialize the in-memory master table.
434  */
436 
437  /*
438  * Skip blank lines and comment lines. Degrade gracefully if master.cf is
439  * not available, and master.cf is not the primary target.
440  */
441  if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) {
442  if (fail_on_open_error)
443  msg_fatal("open %s: %m", path);
444  msg_warn("open %s: %m", path);
445  } else {
446  buf = vstring_alloc(100);
447  while (readllines(buf, fp, &last_line, &line_count) != 0) {
449  (entry_count + 2) * sizeof(*pcf_master_table));
450  if ((err = pcf_parse_master_entry(pcf_master_table + entry_count,
451  STR(buf))) != 0)
452  msg_fatal("file %s: line %d: %s", path, line_count, err);
453  entry_count += 1;
454  }
455  vstream_fclose(fp);
456  vstring_free(buf);
457  }
458 
459  /*
460  * Null-terminate the master table and clean up.
461  */
462  pcf_master_table[entry_count].argv = 0;
463  myfree(path);
464 }
465 
466 /* pcf_print_master_entry - print one master line */
467 
468 void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp)
469 {
470  char **argv = masterp->argv->argv;
471  const char *arg;
472  const char *aval;
473  int arg_len;
474  int line_len;
475  int field;
476  int in_daemon_options;
477  int need_parens;
478  static int column_goal[] = {
479  0, /* service */
480  11, /* type */
481  17, /* private */
482  25, /* unpriv */
483  33, /* chroot */
484  41, /* wakeup */
485  49, /* maxproc */
486  57, /* command */
487  };
488 
489 #define ADD_TEXT(text, len) do { \
490  vstream_fputs(text, fp); line_len += len; } \
491  while (0)
492 #define ADD_SPACE ADD_TEXT(" ", 1)
493 
494  /*
495  * Show the standard fields at their preferred column position. Use at
496  * least one-space column separation.
497  */
498  for (line_len = 0, field = 0; field < PCF_MASTER_MIN_FIELDS; field++) {
499  arg = argv[field];
500  if (line_len > 0) {
501  do {
502  ADD_SPACE;
503  } while (line_len < column_goal[field]);
504  }
505  ADD_TEXT(arg, strlen(arg));
506  }
507 
508  /*
509  * Format the daemon command-line options and non-option arguments. Here,
510  * we have no data-dependent preference for column positions, but we do
511  * have argument grouping preferences.
512  */
513  in_daemon_options = 1;
514  for ( /* void */ ; (arg = argv[field]) != 0; field++) {
515  arg_len = strlen(arg);
516  aval = 0;
517  need_parens = 0;
518  if (in_daemon_options) {
519 
520  /*
521  * Try to show the generic options (-v -D) on the first line, and
522  * non-options on a later line.
523  */
524  if (arg[0] != '-' || strcmp(arg, "--") == 0) {
525  in_daemon_options = 0;
526 #if 0
527  if (mode & PCF_FOLD_LINE)
528  /* Force line wrap. */
529  line_len = PCF_LINE_LIMIT;
530 #endif
531  }
532 
533  /*
534  * Special processing for options that require a value.
535  */
536  else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
537  && (aval = argv[field + 1]) != 0) {
538 
539  /* Force line wrap before option with value. */
540  line_len = PCF_LINE_LIMIT;
541 
542  /*
543  * Optionally, expand $name in parameter value.
544  */
545  if (strcmp(arg, "-o") == 0
546  && (mode & PCF_SHOW_EVAL) != 0)
547  aval = pcf_expand_parameter_value((VSTRING *) 0, mode,
548  aval, masterp);
549 
550  /*
551  * Keep option and value on the same line.
552  */
553  arg_len += strlen(aval) + 3;
554  if ((need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)]) != 0)
555  arg_len += 2;
556  }
557  } else {
558  need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
559  }
560 
561  /*
562  * Insert a line break when the next item won't fit.
563  */
564  if (line_len > PCF_INDENT_LEN) {
565  if ((mode & PCF_FOLD_LINE) == 0
566  || line_len + 1 + arg_len < PCF_LINE_LIMIT) {
567  ADD_SPACE;
568  } else {
569  vstream_fputs("\n" PCF_INDENT_TEXT, fp);
570  line_len = PCF_INDENT_LEN;
571  }
572  }
573  if (in_daemon_options == 0 && need_parens)
574  ADD_TEXT("{", 1);
575  ADD_TEXT(arg, strlen(arg));
576  if (in_daemon_options == 0 && need_parens)
577  ADD_TEXT("}", 1);
578  if (aval) {
579  ADD_TEXT(" ", 1);
580  if (need_parens)
581  ADD_TEXT("{", 1);
582  ADD_TEXT(aval, strlen(aval));
583  if (need_parens)
584  ADD_TEXT("}", 1);
585  field += 1;
586 
587  /* Force line wrap after option with value. */
588  line_len = PCF_LINE_LIMIT;
589 
590  }
591  }
592  vstream_fputs("\n", fp);
593 
594  if (msg_verbose)
595  vstream_fflush(fp);
596 }
597 
598 /* pcf_show_master_entries - show master.cf entries */
599 
600 void pcf_show_master_entries(VSTREAM *fp, int mode, int argc, char **argv)
601 {
602  PCF_MASTER_ENT *masterp;
603  PCF_MASTER_FLD_REQ *field_reqs;
604  PCF_MASTER_FLD_REQ *req;
605 
606  /*
607  * Parse the filter expressions.
608  */
609  if (argc > 0) {
610  field_reqs = (PCF_MASTER_FLD_REQ *)
611  mymalloc(sizeof(*field_reqs) * argc);
612  for (req = field_reqs; req < field_reqs + argc; req++) {
613  req->match_count = 0;
614  req->raw_text = *argv++;
615  req->service_pattern =
617  if (req->service_pattern == 0)
618  msg_fatal("-M option requires service_name[/type]");
619  }
620  }
621 
622  /*
623  * Iterate over the master table.
624  */
625  for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
626  if (argc > 0) {
627  for (req = field_reqs; req < field_reqs + argc; req++) {
629  masterp->argv->argv[0],
630  masterp->argv->argv[1])) {
631  req->match_count++;
632  pcf_print_master_entry(fp, mode, masterp);
633  }
634  }
635  } else {
636  pcf_print_master_entry(fp, mode, masterp);
637  }
638  }
639 
640  /*
641  * Cleanup.
642  */
643  if (argc > 0) {
644  for (req = field_reqs; req < field_reqs + argc; req++) {
645  if (req->match_count == 0)
646  msg_warn("unmatched request: \"%s\"", req->raw_text);
648  }
649  myfree((void *) field_reqs);
650  }
651 }
652 
653 /* pcf_print_master_field - scaffolding */
654 
655 static void pcf_print_master_field(VSTREAM *fp, int mode,
656  PCF_MASTER_ENT *masterp,
657  int field)
658 {
659  char **argv = masterp->argv->argv;
660  const char *arg;
661  const char *aval;
662  int arg_len;
663  int line_len;
664  int in_daemon_options;
665  int need_parens;
666 
667  /*
668  * Show the field value, or the first value in the case of a multi-column
669  * field.
670  */
671 #define ADD_CHAR(ch) ADD_TEXT((ch), 1)
672 
673  line_len = 0;
674  if ((mode & PCF_HIDE_NAME) == 0) {
675  ADD_TEXT(argv[0], strlen(argv[0]));
677  ADD_TEXT(argv[1], strlen(argv[1]));
679  ADD_TEXT(pcf_str_field_pattern(field), strlen(pcf_str_field_pattern(field)));
680  }
681  if ((mode & (PCF_HIDE_NAME | PCF_HIDE_VALUE)) == 0) {
682  ADD_TEXT(" = ", 3);
683  }
684  if ((mode & PCF_HIDE_VALUE) == 0) {
685  if (line_len > 0 && line_len + strlen(argv[field]) > PCF_LINE_LIMIT) {
686  vstream_fputs("\n" PCF_INDENT_TEXT, fp);
687  line_len = PCF_INDENT_LEN;
688  }
689  ADD_TEXT(argv[field], strlen(argv[field]));
690  }
691 
692  /*
693  * Format the daemon command-line options and non-option arguments. Here,
694  * we have no data-dependent preference for column positions, but we do
695  * have argument grouping preferences.
696  */
697  if (field == PCF_MASTER_FLD_CMD && (mode & PCF_HIDE_VALUE) == 0) {
698  in_daemon_options = 1;
699  for (field += 1; (arg = argv[field]) != 0; field++) {
700  arg_len = strlen(arg);
701  aval = 0;
702  need_parens = 0;
703  if (in_daemon_options) {
704 
705  /*
706  * We make no special case for generic options (-v -D)
707  * options.
708  */
709  if (arg[0] != '-' || strcmp(arg, "--") == 0) {
710  in_daemon_options = 0;
711  } else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
712  && (aval = argv[field + 1]) != 0) {
713 
714  /* Force line break before option with value. */
715  line_len = PCF_LINE_LIMIT;
716 
717  /*
718  * Optionally, expand $name in parameter value.
719  */
720  if (strcmp(arg, "-o") == 0
721  && (mode & PCF_SHOW_EVAL) != 0)
722  aval = pcf_expand_parameter_value((VSTRING *) 0, mode,
723  aval, masterp);
724 
725  /*
726  * Keep option and value on the same line.
727  */
728  arg_len += strlen(aval) + 1;
729  if ((need_parens = aval[strcspn(aval, PCF_MASTER_BLANKS)]) != 0)
730  arg_len += 2;
731  }
732  } else {
733  need_parens = arg[strcspn(arg, PCF_MASTER_BLANKS)];
734  }
735 
736  /*
737  * Insert a line break when the next item won't fit.
738  */
739  if (line_len > PCF_INDENT_LEN) {
740  if ((mode & PCF_FOLD_LINE) == 0
741  || line_len + 1 + arg_len < PCF_LINE_LIMIT) {
742  ADD_SPACE;
743  } else {
744  vstream_fputs("\n" PCF_INDENT_TEXT, fp);
745  line_len = PCF_INDENT_LEN;
746  }
747  }
748  if (in_daemon_options == 0 && need_parens)
749  ADD_TEXT("{", 1);
750  ADD_TEXT(arg, strlen(arg));
751  if (in_daemon_options == 0 && need_parens)
752  ADD_TEXT("}", 1);
753  if (aval) {
754  ADD_SPACE;
755  if (need_parens)
756  ADD_TEXT("{", 1);
757  ADD_TEXT(aval, strlen(aval));
758  if (need_parens)
759  ADD_TEXT("}", 1);
760  field += 1;
761 
762  /* Force line break after option with value. */
763  line_len = PCF_LINE_LIMIT;
764  }
765  }
766  }
767  vstream_fputs("\n", fp);
768 
769  if (msg_verbose)
770  vstream_fflush(fp);
771 }
772 
773 /* pcf_show_master_fields - show master.cf fields */
774 
775 void pcf_show_master_fields(VSTREAM *fp, int mode, int argc, char **argv)
776 {
777  const char *myname = "pcf_show_master_fields";
778  PCF_MASTER_ENT *masterp;
779  PCF_MASTER_FLD_REQ *field_reqs;
780  PCF_MASTER_FLD_REQ *req;
781  int field;
782 
783  /*
784  * Parse the filter expressions.
785  */
786  if (argc > 0) {
787  field_reqs = (PCF_MASTER_FLD_REQ *)
788  mymalloc(sizeof(*field_reqs) * argc);
789  for (req = field_reqs; req < field_reqs + argc; req++) {
790  req->match_count = 0;
791  req->raw_text = *argv++;
792  req->service_pattern =
794  if (req->service_pattern == 0)
795  msg_fatal("-F option requires service_name[/type[/field]]");
796  field = req->field_pattern =
798  if (pcf_is_magic_field_pattern(field) == 0
799  && (field < 0 || field > PCF_MASTER_FLD_CMD))
800  msg_panic("%s: bad attribute field index: %d",
801  myname, field);
802  }
803  }
804 
805  /*
806  * Iterate over the master table.
807  */
808  for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
809  if (argc > 0) {
810  for (req = field_reqs; req < field_reqs + argc; req++) {
812  masterp->argv->argv[0],
813  masterp->argv->argv[1])) {
814  req->match_count++;
815  field = req->field_pattern;
816  if (pcf_is_magic_field_pattern(field)) {
817  for (field = 0; field <= PCF_MASTER_FLD_CMD; field++)
818  pcf_print_master_field(fp, mode, masterp, field);
819  } else {
820  pcf_print_master_field(fp, mode, masterp, field);
821  }
822  }
823  }
824  } else {
825  for (field = 0; field <= PCF_MASTER_FLD_CMD; field++)
826  pcf_print_master_field(fp, mode, masterp, field);
827  }
828  }
829 
830  /*
831  * Cleanup.
832  */
833  if (argc > 0) {
834  for (req = field_reqs; req < field_reqs + argc; req++) {
835  if (req->match_count == 0)
836  msg_warn("unmatched request: \"%s\"", req->raw_text);
838  }
839  myfree((void *) field_reqs);
840  }
841 }
842 
843 /* pcf_edit_master_field - replace master.cf field value. */
844 
845 void pcf_edit_master_field(PCF_MASTER_ENT *masterp, int field,
846  const char *new_value)
847 {
848 
849  /*
850  * Replace multi-column attribute.
851  */
852  if (field == PCF_MASTER_FLD_CMD) {
854  argv_splitq_append(masterp->argv, new_value, PCF_MASTER_BLANKS, CHARS_BRACE);
855  pcf_normalize_daemon_args(masterp->argv);
856  }
857 
858  /*
859  * Replace single-column attribute.
860  */
861  else {
862  argv_replace_one(masterp->argv, field, new_value);
863  }
864 
865  /*
866  * Do per-field sanity checks.
867  */
868  pcf_check_master_entry(masterp->argv, new_value);
869 }
870 
871 /* pcf_print_master_param - scaffolding */
872 
873 static void pcf_print_master_param(VSTREAM *fp, int mode,
874  PCF_MASTER_ENT *masterp,
875  const char *param_name,
876  const char *param_value)
877 {
878  if (mode & PCF_HIDE_VALUE) {
879  pcf_print_line(fp, mode, "%s%c%s\n",
880  masterp->name_space, PCF_NAMESP_SEP_CH,
881  param_name);
882  } else {
883  if ((mode & PCF_SHOW_EVAL) != 0)
884  param_value = pcf_expand_parameter_value((VSTRING *) 0, mode,
885  param_value, masterp);
886  if ((mode & PCF_HIDE_NAME) == 0) {
887  pcf_print_line(fp, mode, "%s%c%s = %s\n",
888  masterp->name_space, PCF_NAMESP_SEP_CH,
889  param_name, param_value);
890  } else {
891  pcf_print_line(fp, mode, "%s\n", param_value);
892  }
893  }
894  if (msg_verbose)
895  vstream_fflush(fp);
896 }
897 
898 /* pcf_sort_argv_cb - sort argv call-back */
899 
900 static int pcf_sort_argv_cb(const void *a, const void *b)
901 {
902  return (strcmp(*(char **) a, *(char **) b));
903 }
904 
905 /* pcf_show_master_any_param - show any parameter in master.cf service entry */
906 
907 static void pcf_show_master_any_param(VSTREAM *fp, int mode,
908  PCF_MASTER_ENT *masterp)
909 {
910  const char *myname = "pcf_show_master_any_param";
911  ARGV *argv = argv_alloc(10);
912  DICT *dict = masterp->all_params;
913  const char *param_name;
914  const char *param_value;
915  int param_count = 0;
916  int how;
917  char **cpp;
918 
919  /*
920  * Print parameters in sorted order. The number of parameters per
921  * master.cf entry is small, so we optmiize for code simplicity and don't
922  * worry about the cost of double lookup.
923  */
924 
925  /* Look up the parameter names and ignore the values. */
926 
927  for (how = DICT_SEQ_FUN_FIRST;
928  dict->sequence(dict, how, &param_name, &param_value) == 0;
929  how = DICT_SEQ_FUN_NEXT) {
930  argv_add(argv, param_name, ARGV_END);
931  param_count++;
932  }
933 
934  /* Print the parameters in sorted order. */
935 
936  qsort(argv->argv, param_count, sizeof(argv->argv[0]), pcf_sort_argv_cb);
937  for (cpp = argv->argv; (param_name = *cpp) != 0; cpp++) {
938  if ((param_value = dict_get(dict, param_name)) == 0)
939  msg_panic("%s: parameter name not found: %s", myname, param_name);
940  pcf_print_master_param(fp, mode, masterp, param_name, param_value);
941  }
942 
943  /*
944  * Clean up.
945  */
946  argv_free(argv);
947 }
948 
949 /* pcf_show_master_params - show master.cf params */
950 
951 void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv)
952 {
953  PCF_MASTER_ENT *masterp;
954  PCF_MASTER_FLD_REQ *field_reqs;
955  PCF_MASTER_FLD_REQ *req;
956  DICT *dict;
957  const char *param_value;
958 
959  /*
960  * Parse the filter expressions.
961  */
962  if (argc > 0) {
963  field_reqs = (PCF_MASTER_FLD_REQ *)
964  mymalloc(sizeof(*field_reqs) * argc);
965  for (req = field_reqs; req < field_reqs + argc; req++) {
966  req->match_count = 0;
967  req->raw_text = *argv++;
968  req->service_pattern =
970  if (req->service_pattern == 0)
971  msg_fatal("-P option requires service_name[/type[/parameter]]");
972  req->param_pattern = req->service_pattern->argv[2];
973  }
974  }
975 
976  /*
977  * Iterate over the master table.
978  */
979  for (masterp = pcf_master_table; masterp->argv != 0; masterp++) {
980  if ((dict = masterp->all_params) != 0) {
981  if (argc > 0) {
982  for (req = field_reqs; req < field_reqs + argc; req++) {
984  masterp->argv->argv[0],
985  masterp->argv->argv[1])) {
987  pcf_show_master_any_param(fp, mode, masterp);
988  req->match_count += 1;
989  } else if ((param_value = dict_get(dict,
990  req->param_pattern)) != 0) {
991  pcf_print_master_param(fp, mode, masterp,
992  req->param_pattern,
993  param_value);
994  req->match_count += 1;
995  }
996  }
997  }
998  } else {
999  pcf_show_master_any_param(fp, mode, masterp);
1000  }
1001  }
1002  }
1003 
1004  /*
1005  * Cleanup.
1006  */
1007  if (argc > 0) {
1008  for (req = field_reqs; req < field_reqs + argc; req++) {
1009  if (req->match_count == 0)
1010  msg_warn("unmatched request: \"%s\"", req->raw_text);
1011  argv_free(req->service_pattern);
1012  }
1013  myfree((void *) field_reqs);
1014  }
1015 }
1016 
1017 /* pcf_edit_master_param - update, add or remove -o parameter=value */
1018 
1019 void pcf_edit_master_param(PCF_MASTER_ENT *masterp, int mode,
1020  const char *param_name,
1021  const char *param_value)
1022 {
1023  const char *myname = "pcf_edit_master_param";
1024  ARGV *argv = masterp->argv;
1025  const char *arg;
1026  const char *aval;
1027  int param_match = 0;
1028  int name_len = strlen(param_name);
1029  int field;
1030 
1031  for (field = PCF_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) {
1032  arg = argv->argv[field];
1033 
1034  /*
1035  * Stop at the first non-option argument or end-of-list.
1036  */
1037  if (arg[0] != '-' || strcmp(arg, "--") == 0) {
1038  break;
1039  }
1040 
1041  /*
1042  * Zoom in on command-line options with a value.
1043  */
1044  else if (strchr(pcf_daemon_options_expecting_value, arg[1]) != 0
1045  && (aval = argv->argv[field + 1]) != 0) {
1046 
1047  /*
1048  * Zoom in on "-o parameter=value".
1049  */
1050  if (strcmp(arg, "-o") == 0) {
1051  if (strncmp(aval, param_name, name_len) == 0
1052  && aval[name_len] == '=') {
1053  param_match = 1;
1054  switch (mode & (PCF_EDIT_CONF | PCF_EDIT_EXCL)) {
1055 
1056  /*
1057  * Update parameter=value.
1058  */
1059  case PCF_EDIT_CONF:
1060  aval = concatenate(param_name, "=",
1061  param_value, (char *) 0);
1062  argv_replace_one(argv, field + 1, aval);
1063  myfree((void *) aval);
1064  if (masterp->all_params)
1065  dict_put(masterp->all_params, param_name, param_value);
1066  /* XXX Update parameter "used/defined" status. */
1067  break;
1068 
1069  /*
1070  * Delete parameter=value.
1071  */
1072  case PCF_EDIT_EXCL:
1073  argv_delete(argv, field, 2);
1074  if (masterp->all_params)
1075  dict_del(masterp->all_params, param_name);
1076  /* XXX Update parameter "used/defined" status. */
1077  field -= 2;
1078  break;
1079  default:
1080  msg_panic("%s: unexpected mode: %d", myname, mode);
1081  }
1082  }
1083  }
1084 
1085  /*
1086  * Skip over the command-line option value.
1087  */
1088  field += 1;
1089  }
1090  }
1091 
1092  /*
1093  * Add unmatched parameter.
1094  */
1095  if ((mode & PCF_EDIT_CONF) && param_match == 0) {
1096  /* XXX Generalize: argv_insert(argv, where, list...) */
1097  argv_insert_one(argv, field, "-o");
1098  aval = concatenate(param_name, "=",
1099  param_value, (char *) 0);
1100  argv_insert_one(argv, field + 1, aval);
1101  if (masterp->all_params)
1102  dict_put(masterp->all_params, param_name, param_value);
1103  /* XXX May affect parameter "used/defined" status. */
1104  myfree((void *) aval);
1105  param_match = 1;
1106  }
1107 }
int msg_verbose
Definition: msg.c:177
void htable_free(HTABLE *table, void(*free_fn)(void *))
Definition: htable.c:287
char * name_space
Definition: postconf.h:121
void myfree(void *ptr)
Definition: mymalloc.c:207
#define ADD_CHAR(ch)
DICT * all_params
Definition: postconf.h:123
#define ARGV_END
Definition: argv.h:52
#define CHARS_BRACE
Definition: sys_defs.h:1763
#define dict_put(dp, key, val)
Definition: dict.h:237
char * extpar(char **bp, const char *parens, int flags)
Definition: extpar.c:77
void pcf_show_master_entries(VSTREAM *fp, int mode, int argc, char **argv)
const char * param_pattern
#define VAR_SERVNAME
Definition: mail_params.h:2438
#define PCF_MASTER_FLD_TYPE
Definition: postconf.h:140
#define STR(x)
ARGV * argv_free(ARGV *argvp)
Definition: argv.c:136
#define DICT_SEQ_FUN_FIRST
Definition: dict.h:200
#define NORETURN
Definition: sys_defs.h:1583
PCF_MASTER_ENT * pcf_master_table
Definition: postconf.c:611
Definition: argv.h:17
NORETURN msg_panic(const char *fmt,...)
Definition: msg.c:295
ARGV * pcf_parse_service_pattern(const char *, int, int)
void argv_replace_one(ARGV *argvp, ssize_t where, const char *arg)
Definition: argv.c:293
void * myrealloc(void *ptr, ssize_t len)
Definition: mymalloc.c:175
#define DICT_SEQ_FUN_NEXT
Definition: dict.h:201
#define VAR_PROCNAME
Definition: mail_params.h:2435
void pcf_edit_master_field(PCF_MASTER_ENT *masterp, int field, const char *new_value)
char * pcf_expand_parameter_value(VSTRING *, int, const char *, PCF_MASTER_ENT *)
#define PCF_MASTER_FLD_PRIVATE
Definition: postconf.h:141
void pcf_show_master_fields(VSTREAM *fp, int mode, int argc, char **argv)
char ** argv
Definition: argv.h:20
#define EXTPAR_FLAG_STRIP
Definition: stringops.h:57
void argv_add(ARGV *argvp,...)
Definition: argv.c:197
const char * pcf_parse_master_entry(PCF_MASTER_ENT *masterp, const char *buf)
void pcf_edit_master_param(PCF_MASTER_ENT *masterp, int mode, const char *param_name, const char *param_value)
char * var_config_dir
Definition: mail_params.c:241
#define MASTER_XPORT_NAME_PASS
Definition: master_proto.h:18
#define PCF_FOLD_LINE
Definition: postconf.h:38
char * translit(char *, const char *, const char *)
Definition: translit.c:40
ARGV * argv_alloc(ssize_t len)
Definition: argv.c:149
#define PCF_NAMESP_SEP_CH
Definition: postconf.h:224
const char * split_nameval(char *buf, char **name, char **value)
Definition: split_nameval.c:61
#define PCF_INDENT_TEXT
Definition: postconf.h:163
ARGV * argv_splitq_append(ARGV *, const char *, const char *, const char *)
Definition: argv_splitq.c:106
#define pcf_str_field_pattern(pat)
Definition: postconf.h:246
void pcf_print_master_entry(VSTREAM *fp, int mode, PCF_MASTER_ENT *masterp)
#define PCF_MASTER_NAME_TYPE
Definition: postconf.h:131
#define PCF_MASTER_NAME_MAXPROC
Definition: postconf.h:136
VSTREAM * vstream_fopen(const char *path, int flags, mode_t mode)
Definition: vstream.c:1241
int const char * fmt
#define PCF_HIDE_VALUE
Definition: postconf.h:47
void pcf_read_master(int fail_on_open_error)
Definition: dict.h:78
const char pcf_daemon_options_expecting_value[]
VSTRING * vstring_vsprintf(VSTRING *vp, const char *format, va_list ap)
Definition: vstring.c:614
HTABLE * valid_names
Definition: postconf.h:125
#define PCF_MATCH_SERVICE_PATTERN(pat, name, type)
Definition: postconf.h:241
#define PCF_IS_MAGIC_PARAM_PATTERN(pat)
Definition: postconf.h:248
ARGV * argv_splitq(const char *, const char *, const char *)
Definition: argv_splitq.c:67
#define PCF_MASTER_FLD_CMD
Definition: postconf.h:146
#define dict_get(dp, key)
Definition: dict.h:236
int vstream_fclose(VSTREAM *stream)
Definition: vstream.c:1268
ARGV * argv
Definition: postconf.h:122
#define PCF_SHOW_EVAL
Definition: postconf.h:33
#define MASTER_CONF_FILE
Definition: mail_params.h:335
DICT * dict_handle(const char *dict_name)
Definition: dict.c:333
DICT * ro_params
Definition: postconf.h:124
void msg_warn(const char *fmt,...)
Definition: msg.c:215
VSTRING * vstring_alloc(ssize_t len)
Definition: vstring.c:353
#define PCF_MASTER_FLD_CHROOT
Definition: postconf.h:143
void argv_truncate(ARGV *argvp, ssize_t len)
Definition: argv.c:253
int pcf_parse_field_pattern(const char *)
#define PCF_NAMESP_SEP_STR
Definition: postconf.h:225
#define PCF_LINE_LIMIT
Definition: postconf.h:160
void argv_delete(ARGV *argvp, ssize_t first, ssize_t how_many)
Definition: argv.c:310
#define MASTER_XPORT_NAME_FIFO
Definition: master_proto.h:16
#define MASTER_XPORT_NAME_INET
Definition: master_proto.h:17
NORETURN msg_fatal(const char *fmt,...)
Definition: msg.c:249
int dict_update(const char *dict_name, const char *member, const char *value)
Definition: dict.c:369
#define MASTER_XPORT_NAME_UNIX
Definition: master_proto.h:15
VSTRING * readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
Definition: readlline.c:82
int vstream_fflush(VSTREAM *stream)
Definition: vstream.c:1257
void dict_free(DICT *)
Definition: dict_alloc.c:163
char * concatenate(const char *arg0,...)
Definition: concatenate.c:42
#define ADD_TEXT(text, len)
#define pcf_is_magic_field_pattern(pat)
Definition: postconf.h:245
void pcf_free_master_entry(PCF_MASTER_ENT *masterp)
VSTRING * vstring_free(VSTRING *vp)
Definition: vstring.c:380
#define PCF_MASTER_FLD_WAKEUP
Definition: postconf.h:144
#define PCF_MASTER_NAME_WAKEUP
Definition: postconf.h:135
void pcf_set_config_dir(void)
Definition: postconf_misc.c:48
const char * raw_text
#define PCF_HIDE_NAME
Definition: postconf.h:29
#define PRINTFLIKE(x, y)
Definition: sys_defs.h:1600
ssize_t argc
Definition: argv.h:19
int(* sequence)(struct DICT *, int, const char **, const char **)
Definition: dict.h:85
void pcf_show_master_params(VSTREAM *fp, int mode, int argc, char **argv)
#define dict_del(dp, key)
Definition: dict.h:238
#define PCF_INDENT_LEN
Definition: postconf.h:162
#define PCF_EDIT_EXCL
Definition: postconf.h:39
void pcf_print_line(VSTREAM *fp, int mode, const char *fmt,...)
void argv_insert_one(ARGV *argvp, ssize_t where, const char *arg)
Definition: argv.c:273
#define basename
Definition: stringops.h:36
#define ADD_SPACE
#define PCF_MASTER_BLANKS
Definition: postconf.h:205
#define PCF_MASTER_FLD_MAXPROC
Definition: postconf.h:145
#define PCF_MASTER_MIN_FIELDS
Definition: postconf.h:128
void * mymalloc(ssize_t len)
Definition: mymalloc.c:150
int vstream_fputs(const char *str, VSTREAM *stream)
Definition: vstream.c:1360
#define PCF_EDIT_CONF
Definition: postconf.h:31