xref: /netbsd-src/external/ibm-public/postfix/dist/src/postconf/postconf_master.c (revision 975a152cfcdb39ae6e496af647af0c7275ca0b61)
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