1 /* $NetBSD: postconf_edit.c,v 1.1.1.2 2013/09/25 19:06:33 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* postconf_edit 3 6 /* SUMMARY 7 /* edit main.cf 8 /* SYNOPSIS 9 /* #include <postconf.h> 10 /* 11 /* void edit_parameters(mode, argc, argv) 12 /* int mode; 13 /* int argc; 14 /* char **argv; 15 /* DESCRIPTION 16 /* Edit the \fBmain.cf\fR configuration file, and update 17 /* parameter settings with the "\fIname\fR=\fIvalue\fR" pairs 18 /* given on the command line. The file is copied to a temporary 19 /* file then renamed into place. 20 /* DIAGNOSTICS 21 /* Problems are reported to the standard error stream. 22 /* FILES 23 /* /etc/postfix/main.cf, Postfix configuration parameters 24 /* /etc/postfix/main.cf.tmp, temporary name 25 /* LICENSE 26 /* .ad 27 /* .fi 28 /* The Secure Mailer license must be distributed with this software. 29 /* AUTHOR(S) 30 /* Wietse Venema 31 /* IBM T.J. Watson Research 32 /* P.O. Box 704 33 /* Yorktown Heights, NY 10598, USA 34 /*--*/ 35 36 /* System library. */ 37 38 #include <sys_defs.h> 39 #include <string.h> 40 #include <ctype.h> 41 42 /* Utility library. */ 43 44 #include <msg.h> 45 #include <mymalloc.h> 46 #include <htable.h> 47 #include <vstring.h> 48 #include <vstring_vstream.h> 49 #include <edit_file.h> 50 #include <readlline.h> 51 #include <stringops.h> 52 53 /* Global library. */ 54 55 #include <mail_params.h> 56 57 /* Application-specific. */ 58 59 #include <postconf.h> 60 61 #define STR(x) vstring_str(x) 62 63 /* edit_parameters - edit parameter file */ 64 65 void edit_parameters(int mode, int argc, char **argv) 66 { 67 char *path; 68 EDIT_FILE *ep; 69 VSTREAM *src; 70 VSTREAM *dst; 71 VSTRING *buf = vstring_alloc(100); 72 VSTRING *key = vstring_alloc(10); 73 char *cp; 74 char *edit_key; 75 char *edit_val; 76 HTABLE *table; 77 struct cvalue { 78 char *value; 79 int found; 80 }; 81 struct cvalue *cvalue; 82 HTABLE_INFO **ht_info; 83 HTABLE_INFO **ht; 84 int interesting; 85 const char *err; 86 87 /* 88 * Store command-line parameters for quick lookup. 89 */ 90 table = htable_create(argc); 91 while ((cp = *argv++) != 0) { 92 if (strchr(cp, '\n') != 0) 93 msg_fatal("-e, -X, or -# accepts no multi-line input"); 94 while (ISSPACE(*cp)) 95 cp++; 96 if (*cp == '#') 97 msg_fatal("-e, -X, or -# accepts no comment input"); 98 if (mode & EDIT_MAIN) { 99 if ((err = split_nameval(cp, &edit_key, &edit_val)) != 0) 100 msg_fatal("%s: \"%s\"", err, cp); 101 } else if (mode & (COMMENT_OUT | EDIT_EXCL)) { 102 if (*cp == 0) 103 msg_fatal("-X or -# requires non-blank parameter names"); 104 if (strchr(cp, '=') != 0) 105 msg_fatal("-X or -# requires parameter names only"); 106 edit_key = cp; 107 trimblanks(edit_key, 0); 108 edit_val = 0; 109 } else { 110 msg_panic("edit_parameters: unknown mode %d", mode); 111 } 112 cvalue = (struct cvalue *) mymalloc(sizeof(*cvalue)); 113 cvalue->value = edit_val; 114 cvalue->found = 0; 115 htable_enter(table, edit_key, (char *) cvalue); 116 } 117 118 /* 119 * Open a temp file for the result. This uses a deterministic name so we 120 * don't leave behind thrash with random names. 121 */ 122 set_config_dir(); 123 path = concatenate(var_config_dir, "/", MAIN_CONF_FILE, (char *) 0); 124 if ((ep = edit_file_open(path, O_CREAT | O_WRONLY, 0644)) == 0) 125 msg_fatal("open %s%s: %m", path, EDIT_FILE_SUFFIX); 126 dst = ep->tmp_fp; 127 128 /* 129 * Open the original file for input. 130 */ 131 if ((src = vstream_fopen(path, O_RDONLY, 0)) == 0) { 132 /* OK to delete, since we control the temp file name exclusively. */ 133 (void) unlink(ep->tmp_path); 134 msg_fatal("open %s for reading: %m", path); 135 } 136 137 /* 138 * Copy original file to temp file, while replacing parameters on the 139 * fly. Issue warnings for names found multiple times. 140 */ 141 #define STR(x) vstring_str(x) 142 143 interesting = 0; 144 while (vstring_get(buf, src) != VSTREAM_EOF) { 145 for (cp = STR(buf); ISSPACE(*cp) /* including newline */ ; cp++) 146 /* void */ ; 147 /* Copy comment, all-whitespace, or empty line. */ 148 if (*cp == '#' || *cp == 0) { 149 vstream_fputs(STR(buf), dst); 150 } 151 /* Copy, skip or replace continued text. */ 152 else if (cp > STR(buf)) { 153 if (interesting == 0) 154 vstream_fputs(STR(buf), dst); 155 else if (mode & COMMENT_OUT) 156 vstream_fprintf(dst, "#%s", STR(buf)); 157 } 158 /* Copy or replace start of logical line. */ 159 else { 160 vstring_strncpy(key, cp, strcspn(cp, " \t\r\n=")); 161 cvalue = (struct cvalue *) htable_find(table, STR(key)); 162 if ((interesting = !!cvalue) != 0) { 163 if (cvalue->found++ == 1) 164 msg_warn("%s: multiple entries for \"%s\"", path, STR(key)); 165 if (mode & EDIT_MAIN) 166 vstream_fprintf(dst, "%s = %s\n", STR(key), cvalue->value); 167 else if (mode & COMMENT_OUT) 168 vstream_fprintf(dst, "#%s", cp); 169 } else { 170 vstream_fputs(STR(buf), dst); 171 } 172 } 173 } 174 175 /* 176 * Generate new entries for parameters that were not found. 177 */ 178 if (mode & EDIT_MAIN) { 179 for (ht_info = ht = htable_list(table); *ht; ht++) { 180 cvalue = (struct cvalue *) ht[0]->value; 181 if (cvalue->found == 0) 182 vstream_fprintf(dst, "%s = %s\n", ht[0]->key, cvalue->value); 183 } 184 myfree((char *) ht_info); 185 } 186 187 /* 188 * When all is well, rename the temp file to the original one. 189 */ 190 if (vstream_fclose(src)) 191 msg_fatal("read %s: %m", path); 192 if (edit_file_close(ep) != 0) 193 msg_fatal("close %s%s: %m", path, EDIT_FILE_SUFFIX); 194 195 /* 196 * Cleanup. 197 */ 198 myfree(path); 199 vstring_free(buf); 200 vstring_free(key); 201 htable_free(table, myfree); 202 } 203