5
/* edit main.cf or master.cf
7
7
/* #include <postconf.h>
9
/* void edit_parameters(mode, argc, argv)
9
/* void pcf_edit_main(mode, argc, argv)
14
/* void pcf_edit_master(mode, argc, argv)
14
/* Edit the \fBmain.cf\fR configuration file, and update
15
/* parameter settings with the "\fIname\fR=\fIvalue\fR" pairs
16
/* given on the command line. The file is copied to a temporary
17
/* file then renamed into place.
19
/* pcf_edit_main() edits the \fBmain.cf\fR configuration file.
20
/* It replaces or adds parameter settings given as "\fIname=value\fR"
21
/* pairs given on the command line, or removes parameter
22
/* settings given as "\fIname\fR" on the command line. The
23
/* file is copied to a temporary file then renamed into place.
25
/* pcf_edit_master() edits the \fBmaster.cf\fR configuration
26
/* file. The file is copied to a temporary file then renamed
27
/* into place. Depending on the flags in \fBmode\fR:
28
/* .IP PCF_MASTER_ENTRY
29
/* With PCF_EDIT_CONF, pcf_edit_master() replaces or adds
30
/* entire master.cf entries, specified on the command line as
31
/* "\fIname/type = name type private unprivileged chroot wakeup
32
/* process_limit command...\fR".
34
/* With PCF_EDIT_EXCL or PCF_COMMENT_OUT, pcf_edit_master()
35
/* removes or comments out entries specified on the command
36
/* line as "\fIname/type\fR.
38
/* With PCF_EDIT_CONF, pcf_edit_master() replaces the value
39
/* of specific service attributes, specified on the command
40
/* line as "\fIname/type/attribute = value\fR".
41
/* .IP PCF_MASTER_PARAM
42
/* With PCF_EDIT_CONF, pcf_edit_master() replaces or adds the
43
/* value of service parameters, specified on the command line
44
/* as "\fIname/type/parameter = value\fR".
46
/* With PCF_EDIT_EXCL, pcf_edit_master() removes service
47
/* parameters specified on the command line as "\fIparametername\fR".
19
49
/* Problems are reported to the standard error stream.
21
51
/* /etc/postfix/main.cf, Postfix configuration parameters
22
52
/* /etc/postfix/main.cf.tmp, temporary name
53
/* /etc/postfix/master.cf, Postfix configuration parameters
54
/* /etc/postfix/master.cf.tmp, temporary name
198
277
vstring_free(key);
199
278
htable_free(table, myfree);
282
* Data structure to hold a master.cf edit request.
285
int match_count; /* hit count */
286
const char *raw_text; /* unparsed command-line argument */
287
char *parsed_text; /* destructive parse */
288
ARGV *service_pattern; /* service name, type, ... */
289
int field_number; /* attribute field number */
290
const char *param_pattern; /* parameter name */
291
char *edit_value; /* value substring */
292
} PCF_MASTER_EDIT_REQ;
294
/* pcf_edit_master - edit master.cf file */
296
void pcf_edit_master(int mode, int argc, char **argv)
298
const char *myname = "pcf_edit_master";
303
VSTRING *line_buf = vstring_alloc(100);
304
VSTRING *parse_buf = vstring_alloc(100);
306
PCF_MASTER_ENT *new_entry;
307
VSTRING *full_entry_buf = vstring_alloc(100);
310
int service_name_type_matched;
312
PCF_MASTER_EDIT_REQ *edit_reqs;
313
PCF_MASTER_EDIT_REQ *req;
315
const char *edit_opts = "-Me, -Fe, -Pe, -X, or -#";
323
msg_panic("%s: empty argument list", myname);
326
* Preprocessing: split pattern=value, then split the pattern components.
328
edit_reqs = (PCF_MASTER_EDIT_REQ *) mymalloc(sizeof(*edit_reqs) * num_reqs);
329
for (req = edit_reqs; *argv != 0; req++, argv++) {
330
req->match_count = 0;
331
req->raw_text = *argv;
332
cp = req->parsed_text = mystrdup(req->raw_text);
333
if (strchr(cp, '\n') != 0)
334
msg_fatal("%s accept no multi-line input", edit_opts);
338
msg_fatal("%s accept no comment input", edit_opts);
339
/* Separate the pattern from the value. */
340
if (mode & PCF_EDIT_CONF) {
341
if ((err = split_nameval(cp, &pattern, &req->edit_value)) != 0)
342
msg_fatal("%s: \"%s\"", err, req->raw_text);
343
if ((mode & PCF_MASTER_PARAM)
344
&& req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
345
msg_fatal("whitespace in parameter value: \"%s\"",
347
} else if (mode & (PCF_COMMENT_OUT | PCF_EDIT_EXCL)) {
348
if (strchr(cp, '=') != 0)
349
msg_fatal("-X or -# requires names without value");
351
trimblanks(pattern, 0);
354
msg_panic("%s: unknown mode %d", myname, mode);
357
#define PCF_MASTER_MASK (PCF_MASTER_ENTRY | PCF_MASTER_FLD | PCF_MASTER_PARAM)
360
* Split name/type or name/type/whatever pattern into components.
362
switch (mode & PCF_MASTER_MASK) {
363
case PCF_MASTER_ENTRY:
364
if ((req->service_pattern =
365
pcf_parse_service_pattern(pattern, 2, 2)) == 0)
366
msg_fatal("-Me, -MX or -M# requires service_name/type");
369
if ((req->service_pattern =
370
pcf_parse_service_pattern(pattern, 3, 3)) == 0)
371
msg_fatal("-Fe or -FX requires service_name/type/field_name");
373
pcf_parse_field_pattern(req->service_pattern->argv[2]);
374
if (pcf_is_magic_field_pattern(req->field_number))
375
msg_fatal("-Fe does not accept wild-card field name");
376
if ((mode & PCF_EDIT_CONF)
377
&& req->field_number < PCF_MASTER_FLD_CMD
378
&& req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
379
msg_fatal("-Fe does not accept whitespace in non-command field");
381
case PCF_MASTER_PARAM:
382
if ((req->service_pattern =
383
pcf_parse_service_pattern(pattern, 3, 3)) == 0)
384
msg_fatal("-Pe or -PX requires service_name/type/parameter");
385
req->param_pattern = req->service_pattern->argv[2];
386
if (PCF_IS_MAGIC_PARAM_PATTERN(req->param_pattern))
387
msg_fatal("-Pe does not accept wild-card parameter name");
388
if ((mode & PCF_EDIT_CONF)
389
&& req->edit_value[strcspn(req->edit_value, PCF_MASTER_BLANKS)])
390
msg_fatal("-Pe does not accept whitespace in parameter value");
393
msg_panic("%s: unknown edit mode %d", myname, mode);
398
* Open a temp file for the result. This uses a deterministic name so we
399
* don't leave behind thrash with random names.
401
pcf_set_config_dir();
402
path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0);
403
if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0)
404
msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX);
408
* Open the original file for input.
410
if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) {
411
/* OK to delete, since we control the temp file name exclusively. */
412
(void) unlink(ep->tmp_path);
413
msg_fatal("open %s for reading: %m", path);
417
* Copy original file to temp file, while replacing service entries on
420
service_name_type_matched = 0;
423
while ((cp = pcf_next_cf_line(parse_buf, src, dst, &lineno)) != 0) {
424
vstring_strcpy(line_buf, STR(parse_buf));
427
* Copy, skip or replace continued text.
429
if (cp > STR(parse_buf)) {
430
if (service_name_type_matched == 0)
431
vstream_fputs(STR(line_buf), dst);
432
else if (mode & PCF_COMMENT_OUT)
433
vstream_fprintf(dst, "#%s", STR(line_buf));
437
* Copy or replace (start of) logical line.
440
service_name_type_matched = 0;
443
* Parse out the service name and type.
445
if ((service_name = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0
446
|| (service_type = mystrtok(&cp, PCF_MASTER_BLANKS)) == 0)
447
msg_fatal("file %s: line %d: specify service name and type "
448
"on the same line", path, lineno);
449
if (strchr(service_name, '='))
450
msg_fatal("file %s: line %d: service name syntax \"%s\" is "
451
"unsupported with %s", path, lineno, service_name,
453
if (service_type[strcspn(service_type, "=/")] != 0)
454
msg_fatal("file %s: line %d: "
455
"service type syntax \"%s\" is unsupported with %s",
456
path, lineno, service_type, edit_opts);
459
* Match each service pattern.
461
for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
462
if (PCF_MATCH_SERVICE_PATTERN(req->service_pattern,
465
service_name_type_matched = 1; /* Sticky flag */
466
req->match_count += 1;
469
* Generate replacement master.cf entries.
471
if ((mode & PCF_EDIT_CONF)
472
|| ((mode & PCF_MASTER_PARAM) && (mode & PCF_EDIT_EXCL))) {
473
switch (mode & PCF_MASTER_MASK) {
476
* Replace master.cf entry field or parameter
480
case PCF_MASTER_PARAM:
481
if (new_entry == 0) {
482
/* Gobble up any continuation lines. */
483
pcf_gobble_cf_line(full_entry_buf, line_buf,
485
new_entry = (PCF_MASTER_ENT *)
486
mymalloc(sizeof(*new_entry));
487
if ((err = pcf_parse_master_entry(new_entry,
488
STR(full_entry_buf))) != 0)
489
msg_fatal("file %s: line %d: %s",
492
if (mode & PCF_MASTER_FLD) {
493
pcf_edit_master_field(new_entry,
497
pcf_edit_master_param(new_entry, mode,
504
* Replace entire master.cf entry.
506
case PCF_MASTER_ENTRY:
508
pcf_free_master_entry(new_entry);
509
new_entry = (PCF_MASTER_ENT *)
510
mymalloc(sizeof(*new_entry));
511
if ((err = pcf_parse_master_entry(new_entry,
512
req->edit_value)) != 0)
513
msg_fatal("%s: \"%s\"", err, req->raw_text);
516
msg_panic("%s: unknown edit mode %d", myname, mode);
523
* Pass through or replace the current input line.
526
pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry);
527
pcf_free_master_entry(new_entry);
529
} else if (service_name_type_matched == 0) {
530
vstream_fputs(STR(line_buf), dst);
531
} else if (mode & PCF_COMMENT_OUT) {
532
vstream_fprintf(dst, "#%s", STR(line_buf));
538
* Postprocessing: when editing entire service entries, generate new
539
* entries for services not found. Otherwise (editing fields or
540
* parameters), "service not found" is a fatal error.
542
for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
543
if (req->match_count == 0) {
544
if ((mode & PCF_MASTER_ENTRY) && (mode & PCF_EDIT_CONF)) {
545
new_entry = (PCF_MASTER_ENT *) mymalloc(sizeof(*new_entry));
546
if ((err = pcf_parse_master_entry(new_entry, req->edit_value)) != 0)
547
msg_fatal("%s: \"%s\"", err, req->raw_text);
548
pcf_print_master_entry(dst, PCF_FOLD_LINE, new_entry);
549
pcf_free_master_entry(new_entry);
550
} else if ((mode & PCF_MASTER_ENTRY) == 0) {
551
msg_warn("unmatched service_name/type: \"%s\"", req->raw_text);
557
* When all is well, rename the temp file to the original one.
559
if (vstream_fclose(src))
560
msg_fatal("read %s: %m", path);
561
if (edit_file_close(ep) != 0)
562
msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX);
568
vstring_free(line_buf);
569
vstring_free(parse_buf);
570
vstring_free(full_entry_buf);
571
for (req = edit_reqs; req < edit_reqs + num_reqs; req++) {
572
argv_free(req->service_pattern);
573
myfree(req->parsed_text);
575
myfree((char *) edit_reqs);