1 /* $NetBSD: postconf_master.c,v 1.1.1.2 2013/08/21 20:09:54 tron Exp $ */ 2 3 /*++ 4 /* NAME 5 /* postconf_master 3 6 /* SUMMARY 7 /* support for master.cf 8 /* SYNOPSIS 9 /* #include <postconf.h> 10 /* 11 /* void read_master(fail_on_open) 12 /* int fail_on_open; 13 /* 14 /* void show_master(mode, filters) 15 /* int mode; 16 /* char **filters; 17 /* DESCRIPTION 18 /* read_master() reads entries from master.cf into memory. 19 /* 20 /* show_master() writes the entries in the master.cf file 21 /* to standard output. 22 /* 23 /* Arguments 24 /* .IP fail_on_open 25 /* Specify FAIL_ON_OPEN if open failure is a fatal error, 26 /* WARN_ON_OPEN if a warning should be logged instead. 27 /* .IP mode 28 /* If the FOLD_LINE flag is set, show_master() wraps long 29 /* output lines. 30 /* .IP filters 31 /* A list of zero or more expressions in master_service(3) 32 /* format. If no list is specified, show_master() outputs 33 /* all master.cf entries in the specified order. 34 /* DIAGNOSTICS 35 /* Problems are reported to the standard error stream. 36 /* LICENSE 37 /* .ad 38 /* .fi 39 /* The Secure Mailer license must be distributed with this software. 40 /* AUTHOR(S) 41 /* Wietse Venema 42 /* IBM T.J. Watson Research 43 /* P.O. Box 704 44 /* Yorktown Heights, NY 10598, USA 45 /*--*/ 46 47 /* System library. */ 48 49 #include <sys_defs.h> 50 #include <string.h> 51 52 /* Utility library. */ 53 54 #include <msg.h> 55 #include <mymalloc.h> 56 #include <vstring.h> 57 #include <argv.h> 58 #include <vstream.h> 59 #include <readlline.h> 60 #include <stringops.h> 61 62 /* Global library. */ 63 64 #include <mail_params.h> 65 #include <match_service.h> 66 67 /* Application-specific. */ 68 69 #include <postconf.h> 70 71 #define STR(x) vstring_str(x) 72 73 /* normalize_options - bring options into canonical form */ 74 75 static void normalize_options(ARGV *argv) 76 { 77 int field; 78 char *arg; 79 char *cp; 80 char *junk; 81 82 /* 83 * Normalize options to simplify later processing. 84 */ 85 for (field = PC_MASTER_MIN_FIELDS; argv->argv[field] != 0; field++) { 86 arg = argv->argv[field]; 87 if (arg[0] != '-' || strcmp(arg, "--") == 0) 88 break; 89 for (cp = arg + 1; *cp; cp++) { 90 if (*cp == 'o' && cp > arg + 1) { 91 /* Split "-stuffo" into "-stuff" and "-o". */ 92 junk = concatenate("-", cp, (char *) 0); 93 argv_insert_one(argv, field + 1, junk); 94 myfree(junk); 95 *cp = 0; 96 break; 97 } 98 } 99 if (strncmp(arg, "-o", 2) == 0) { 100 if (arg[2] != 0) { 101 /* Split "-oname=value" into "-o" "name=value". */ 102 argv_insert_one(argv, field + 1, arg + 2); 103 argv_replace_one(argv, field, "-o"); 104 /* arg is now a dangling pointer. */ 105 field += 1; 106 } else if (argv->argv[field + 1] != 0) { 107 /* Already in "-o" "name=value" form. */ 108 field += 1; 109 } 110 } 111 } 112 } 113 114 /* read_master - read and digest the master.cf file */ 115 116 void read_master(int fail_on_open_error) 117 { 118 const char *myname = "read_master"; 119 char *path; 120 VSTRING *buf; 121 ARGV *argv; 122 VSTREAM *fp; 123 int entry_count = 0; 124 int line_count = 0; 125 126 /* 127 * Sanity check. 128 */ 129 if (master_table != 0) 130 msg_panic("%s: master table is already initialized", myname); 131 132 /* 133 * Get the location of master.cf. 134 */ 135 if (var_config_dir == 0) 136 set_config_dir(); 137 path = concatenate(var_config_dir, "/", MASTER_CONF_FILE, (char *) 0); 138 139 /* 140 * We can't use the master daemon's master_ent routines in their current 141 * form. They convert everything to internal form, and they skip disabled 142 * services. 143 * 144 * The postconf command needs to show default fields as "-", and needs to 145 * know about all service names so that it can generate service-dependent 146 * parameter names (transport-dependent etc.). 147 */ 148 #define MASTER_BLANKS " \t\r\n" /* XXX */ 149 150 /* 151 * Initialize the in-memory master table. 152 */ 153 master_table = (PC_MASTER_ENT *) mymalloc(sizeof(*master_table)); 154 155 /* 156 * Skip blank lines and comment lines. Degrade gracefully if master.cf is 157 * not available, and master.cf is not the primary target. 158 */ 159 if ((fp = vstream_fopen(path, O_RDONLY, 0)) == 0) { 160 if (fail_on_open_error) 161 msg_fatal("open %s: %m", path); 162 msg_warn("open %s: %m", path); 163 } else { 164 buf = vstring_alloc(100); 165 while (readlline(buf, fp, &line_count) != 0) { 166 master_table = (PC_MASTER_ENT *) myrealloc((char *) master_table, 167 (entry_count + 2) * sizeof(*master_table)); 168 argv = argv_split(STR(buf), MASTER_BLANKS); 169 if (argv->argc < PC_MASTER_MIN_FIELDS) 170 msg_fatal("file %s: line %d: bad field count", 171 path, line_count); 172 normalize_options(argv); 173 master_table[entry_count].name_space = 174 concatenate(argv->argv[0], ".", argv->argv[1], (char *) 0); 175 master_table[entry_count].argv = argv; 176 master_table[entry_count].valid_names = 0; 177 master_table[entry_count].all_params = 0; 178 entry_count += 1; 179 } 180 vstream_fclose(fp); 181 vstring_free(buf); 182 } 183 184 /* 185 * Null-terminate the master table and clean up. 186 */ 187 master_table[entry_count].argv = 0; 188 myfree(path); 189 } 190 191 /* print_master_line - print one master line */ 192 193 static void print_master_line(int mode, ARGV *argv) 194 { 195 char *arg; 196 char *aval; 197 int line_len; 198 int field; 199 int in_daemon_options; 200 static int column_goal[] = { 201 0, /* service */ 202 11, /* type */ 203 17, /* private */ 204 25, /* unpriv */ 205 33, /* chroot */ 206 41, /* wakeup */ 207 49, /* maxproc */ 208 57, /* command */ 209 }; 210 211 #define ADD_TEXT(text, len) do { \ 212 vstream_fputs(text, VSTREAM_OUT); line_len += len; } \ 213 while (0) 214 #define ADD_SPACE ADD_TEXT(" ", 1) 215 216 /* 217 * Show the standard fields at their preferred column position. Use at 218 * least one-space column separation. 219 */ 220 for (line_len = 0, field = 0; field < PC_MASTER_MIN_FIELDS; field++) { 221 arg = argv->argv[field]; 222 if (line_len > 0) { 223 do { 224 ADD_SPACE; 225 } while (line_len < column_goal[field]); 226 } 227 ADD_TEXT(arg, strlen(arg)); 228 } 229 230 /* 231 * Format the daemon command-line options and non-option arguments. Here, 232 * we have no data-dependent preference for column positions, but we do 233 * have argument grouping preferences. 234 */ 235 in_daemon_options = 1; 236 for ( /* void */ ; argv->argv[field] != 0; field++) { 237 arg = argv->argv[field]; 238 if (in_daemon_options) { 239 240 /* 241 * Try to show the generic options (-v -D) on the first line, and 242 * non-options on a later line. 243 */ 244 if (arg[0] != '-' || strcmp(arg, "--") == 0) { 245 in_daemon_options = 0; 246 if ((mode & FOLD_LINE) 247 && line_len > column_goal[PC_MASTER_MIN_FIELDS - 1]) { 248 vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT); 249 line_len = INDENT_LEN; 250 } 251 } 252 253 /* 254 * Try to avoid breaking "-o name=value" over multiple lines if 255 * it would fit on one line. 256 */ 257 else if ((mode & FOLD_LINE) 258 && line_len > INDENT_LEN && strcmp(arg, "-o") == 0 259 && (aval = argv->argv[field + 1]) != 0 260 && INDENT_LEN + 3 + strlen(aval) < LINE_LIMIT) { 261 vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT); 262 line_len = INDENT_LEN; 263 ADD_TEXT(arg, strlen(arg)); 264 arg = aval; 265 field += 1; 266 } 267 } 268 269 /* 270 * Insert a line break when the next argument won't fit (unless, of 271 * course, we just inserted a line break). 272 */ 273 if (line_len > INDENT_LEN) { 274 if ((mode & FOLD_LINE) == 0 275 || line_len + 1 + strlen(arg) < LINE_LIMIT) { 276 ADD_SPACE; 277 } else { 278 vstream_fputs("\n" INDENT_TEXT, VSTREAM_OUT); 279 line_len = INDENT_LEN; 280 } 281 } 282 ADD_TEXT(arg, strlen(arg)); 283 } 284 vstream_fputs("\n", VSTREAM_OUT); 285 } 286 287 /* show_master - show master.cf entries */ 288 289 void show_master(int mode, char **filters) 290 { 291 PC_MASTER_ENT *masterp; 292 ARGV *argv; 293 ARGV *service_filter = 0; 294 295 /* 296 * Initialize the service filter. 297 */ 298 if (filters[0]) 299 service_filter = match_service_init_argv(filters); 300 301 /* 302 * Iterate over the master table. 303 */ 304 for (masterp = master_table; (argv = masterp->argv) != 0; masterp++) 305 if (service_filter == 0 306 || match_service_match(service_filter, masterp->name_space) != 0) 307 print_master_line(mode, argv); 308 309 /* 310 * Cleanup. 311 */ 312 if (service_filter != 0) 313 argv_free(service_filter); 314 } 315