No empty .Rs/.Re
[netbsd-mini2440.git] / external / ibm-public / postfix / dist / src / master / master_ent.c
blob6c92cb5c7c827697787839e616cb42f8167eb271
1 /* $NetBSD$ */
3 /*++
4 /* NAME
5 /* master_ent 3
6 /* SUMMARY
7 /* Postfix master - config file access
8 /* SYNOPSIS
9 /* #include "master.h"
11 /* void fset_master_ent(path)
12 /* char *path;
14 /* void set_master_ent()
16 /* MASTER_SERV *get_master_ent()
18 /* void end_master_ent()
20 /* void print_master_ent(entry)
21 /* MASTER_SERV *entry;
23 /* void free_master_ent(entry)
24 /* MASTER_SERV *entry;
25 /* DESCRIPTION
26 /* This module implements a simple programmatic interface
27 /* for accessing Postfix master process configuration files.
29 /* fset_master_ent() specifies the location of the master process
30 /* configuration file. The pathname is copied.
32 /* set_master_ent() opens the configuration file. It is an error
33 /* to call this routine while the configuration file is still open.
34 /* It is an error to open a configuration file without specifying
35 /* its name to fset_master_ent().
37 /* get_master_ent() reads the next entry from an open configuration
38 /* file and returns the parsed result. A null result means the end
39 /* of file was reached.
41 /* print_master_ent() prints the specified service entry.
43 /* end_master_ent() closes an open configuration file. It is an error
44 /* to call this routine when the configuration file is not open.
46 /* free_master_ent() destroys the memory used for a parsed configuration
47 /* file entry.
48 /* DIAGNOSTICS
49 /* Panics: interface violations. Fatal errors: memory allocation
50 /* failure.
51 /* BUGS
52 /* SEE ALSO
53 /* LICENSE
54 /* .ad
55 /* .fi
56 /* The Secure Mailer license must be distributed with this software.
57 /* AUTHOR(S)
58 /* Wietse Venema
59 /* IBM T.J. Watson Research
60 /* P.O. Box 704
61 /* Yorktown Heights, NY 10598, USA
62 /*--*/
64 /* System libraries. */
66 #include <sys_defs.h>
67 #include <netinet/in.h>
68 #include <stdarg.h>
69 #include <string.h>
70 #include <stdlib.h>
71 #include <unistd.h>
72 #include <ctype.h>
73 #include <fcntl.h>
75 #ifdef STRCASECMP_IN_STRINGS_H
76 #include <strings.h>
77 #endif
79 /* Utility libraries. */
81 #include <msg.h>
82 #include <mymalloc.h>
83 #include <vstring.h>
84 #include <vstream.h>
85 #include <argv.h>
86 #include <stringops.h>
87 #include <readlline.h>
88 #include <inet_addr_list.h>
89 #include <host_port.h>
90 #include <inet_addr_host.h>
91 #include <sock_addr.h>
93 /* Global library. */
95 #include <match_service.h>
96 #include <mail_proto.h>
97 #include <mail_params.h>
98 #include <own_inet_addr.h>
99 #include <wildcard_inet_addr.h>
100 #include <mail_conf.h>
102 /* Local stuff. */
104 #include "master_proto.h"
105 #include "master.h"
107 static char *master_path; /* config file name */
108 static VSTREAM *master_fp; /* config file pointer */
109 static int master_line; /* config file line number */
110 static ARGV *master_disable; /* disabled service patterns */
112 static char master_blanks[] = " \t\r\n";/* field delimiters */
114 static NORETURN fatal_invalid_field(char *, char *);
115 static NORETURN fatal_with_context(char *,...);
117 /* fset_master_ent - specify configuration file pathname */
119 void fset_master_ent(char *path)
121 if (master_path != 0)
122 myfree(master_path);
123 master_path = mystrdup(path);
126 /* set_master_ent - open configuration file */
128 void set_master_ent()
130 const char *myname = "set_master_ent";
132 if (master_fp != 0)
133 msg_panic("%s: configuration file still open", myname);
134 if (master_path == 0)
135 msg_panic("%s: no configuration file specified", myname);
136 if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0)
137 msg_fatal("open %s: %m", master_path);
138 master_line = 0;
139 if (master_disable != 0)
140 msg_panic("%s: service disable list still exists", myname);
141 master_disable = match_service_init(var_master_disable);
144 /* end_master_ent - close configuration file */
146 void end_master_ent()
148 const char *myname = "end_master_ent";
150 if (master_fp == 0)
151 msg_panic("%s: configuration file not open", myname);
152 if (vstream_fclose(master_fp) != 0)
153 msg_fatal("%s: close configuration file: %m", myname);
154 master_fp = 0;
155 if (master_disable == 0)
156 msg_panic("%s: no service disable list", myname);
157 match_service_free(master_disable);
158 master_disable = 0;
161 /* fatal_with_context - print fatal error with file/line context */
163 static NORETURN fatal_with_context(char *format,...)
165 const char *myname = "fatal_with_context";
166 VSTRING *vp = vstring_alloc(100);
167 va_list ap;
169 if (master_path == 0)
170 msg_panic("%s: no configuration file specified", myname);
172 va_start(ap, format);
173 vstring_vsprintf(vp, format, ap);
174 va_end(ap);
175 msg_fatal("%s: line %d: %s", master_path, master_line, vstring_str(vp));
178 /* fatal_invalid_field - report invalid field value */
180 static NORETURN fatal_invalid_field(char *name, char *value)
182 fatal_with_context("field \"%s\": bad value: \"%s\"", name, value);
185 /* get_str_ent - extract string field */
187 static char *get_str_ent(char **bufp, char *name, char *def_val)
189 char *value;
191 if ((value = mystrtok(bufp, master_blanks)) == 0)
192 fatal_with_context("missing \"%s\" field", name);
193 if (strcmp(value, "-") == 0) {
194 if (def_val == 0)
195 fatal_with_context("field \"%s\" has no default value", name);
196 return (def_val);
197 } else {
198 return (value);
202 /* get_bool_ent - extract boolean field */
204 static int get_bool_ent(char **bufp, char *name, char *def_val)
206 char *value;
208 value = get_str_ent(bufp, name, def_val);
209 if (strcmp("y", value) == 0) {
210 return (1);
211 } else if (strcmp("n", value) == 0) {
212 return (0);
213 } else {
214 fatal_invalid_field(name, value);
216 /* NOTREACHED */
219 /* get_int_ent - extract integer field */
221 static int get_int_ent(char **bufp, char *name, char *def_val, int min_val)
223 char *value;
224 int n;
226 value = get_str_ent(bufp, name, def_val);
227 if (!ISDIGIT(*value) || (n = atoi(value)) < min_val)
228 fatal_invalid_field(name, value);
229 return (n);
232 /* get_master_ent - read entry from configuration file */
234 MASTER_SERV *get_master_ent()
236 VSTRING *buf = vstring_alloc(100);
237 VSTRING *junk = vstring_alloc(100);
238 MASTER_SERV *serv;
239 char *cp;
240 char *name;
241 char *host = 0;
242 char *port = 0;
243 char *transport;
244 int private;
245 int unprivileged; /* passed on to child */
246 int chroot; /* passed on to child */
247 char *command;
248 int n;
249 char *bufp;
250 char *atmp;
251 const char *parse_err;
252 static char *saved_interfaces = 0;
254 if (master_fp == 0)
255 msg_panic("get_master_ent: config file not open");
256 if (master_disable == 0)
257 msg_panic("get_master_ent: no service disable list");
260 * XXX We cannot change the inet_interfaces setting for a running master
261 * process. Listening sockets are inherited by child processes so that
262 * closing and reopening those sockets in the master does not work.
264 * Another problem is that library routines still cache results that are
265 * based on the old inet_interfaces setting. It is too much trouble to
266 * recompute everything.
268 * In order to keep our data structures consistent we ignore changes in
269 * inet_interfaces settings, and issue a warning instead.
271 if (saved_interfaces == 0)
272 saved_interfaces = mystrdup(var_inet_interfaces);
275 * Skip blank lines and comment lines.
277 do {
278 if (readlline(buf, master_fp, &master_line) == 0) {
279 vstring_free(buf);
280 vstring_free(junk);
281 return (0);
283 bufp = vstring_str(buf);
284 if ((cp = mystrtok(&bufp, master_blanks)) == 0)
285 continue;
286 name = cp;
287 transport = get_str_ent(&bufp, "transport type", (char *) 0);
288 vstring_sprintf(junk, "%s.%s", name, transport);
289 } while (match_service_match(master_disable, vstring_str(junk)) != 0);
292 * Parse one logical line from the configuration file. Initialize service
293 * structure members in order.
295 serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV));
296 serv->next = 0;
299 * Flags member.
301 serv->flags = 0;
304 * All servers busy warning timer.
306 serv->busy_warn_time = 0;
309 * Service name. Syntax is transport-specific.
311 serv->ext_name = mystrdup(name);
314 * Transport type: inet (wild-card listen or virtual) or unix.
316 #define STR_SAME !strcmp
318 if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) {
319 if (!STR_SAME(saved_interfaces, var_inet_interfaces)) {
320 msg_warn("service %s: ignoring %s change",
321 serv->ext_name, VAR_INET_INTERFACES);
322 msg_warn("to change %s, stop and start Postfix",
323 VAR_INET_INTERFACES);
325 serv->type = MASTER_SERV_TYPE_INET;
326 atmp = mystrdup(name);
327 if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0)
328 msg_fatal("%s: line %d: %s in \"%s\"",
329 VSTREAM_PATH(master_fp), master_line,
330 parse_err, name);
331 if (*host) {
332 serv->flags |= MASTER_FLAG_INETHOST;/* host:port */
333 MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *)
334 mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv)));
335 inet_addr_list_init(MASTER_INET_ADDRLIST(serv));
336 if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0)
337 msg_fatal("%s: line %d: bad hostname or network address: %s",
338 VSTREAM_PATH(master_fp), master_line, name);
339 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
340 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
341 } else {
342 MASTER_INET_ADDRLIST(serv) =
343 strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ?
344 own_inet_addr_list() : /* virtual */
345 wildcard_inet_addr_list(); /* wild-card */
346 inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
347 serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
349 MASTER_INET_PORT(serv) = mystrdup(port);
350 for (n = 0; /* see below */ ; n++) {
351 if (n >= MASTER_INET_ADDRLIST(serv)->used) {
352 serv->flags |= MASTER_FLAG_LOCAL_ONLY;
353 break;
355 if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n)))
356 break;
358 } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) {
359 serv->type = MASTER_SERV_TYPE_UNIX;
360 serv->listen_fd_count = 1;
361 serv->flags |= MASTER_FLAG_LOCAL_ONLY;
362 } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) {
363 serv->type = MASTER_SERV_TYPE_FIFO;
364 serv->listen_fd_count = 1;
365 serv->flags |= MASTER_FLAG_LOCAL_ONLY;
366 #ifdef MASTER_SERV_TYPE_PASS
367 } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) {
368 serv->type = MASTER_SERV_TYPE_PASS;
369 serv->listen_fd_count = 1;
370 /* If this is a connection screener, remote clients are likely. */
371 #endif
372 } else {
373 fatal_with_context("bad transport type: %s", transport);
377 * Service class: public or private.
379 private = get_bool_ent(&bufp, "private", "y");
382 * Derive an internal service name. The name may depend on service
383 * attributes such as privacy.
385 if (serv->type == MASTER_SERV_TYPE_INET) {
386 MAI_HOSTADDR_STR host_addr;
387 MAI_SERVPORT_STR serv_port;
388 struct addrinfo *res0;
390 if (private)
391 fatal_with_context("inet service cannot be private");
394 * Canonicalize endpoint names so that we correctly handle "reload"
395 * requests after someone changes "25" into "smtp" or vice versa.
397 if (*host == 0)
398 host = 0;
399 /* Canonicalize numeric host and numeric or symbolic service. */
400 if (hostaddr_to_sockaddr(host, port, 0, &res0) == 0) {
401 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
402 host ? &host_addr : (MAI_HOSTADDR_STR *) 0,
403 &serv_port, 0);
404 serv->name = (host ? concatenate("[", host_addr.buf, "]:",
405 serv_port.buf, (char *) 0) :
406 mystrdup(serv_port.buf));
407 freeaddrinfo(res0);
409 /* Canonicalize numeric or symbolic service. */
410 else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) {
411 SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
412 (MAI_HOSTADDR_STR *) 0, &serv_port, 0);
413 serv->name = (host ? concatenate("[", host, "]:",
414 serv_port.buf, (char *) 0) :
415 mystrdup(serv_port.buf));
416 freeaddrinfo(res0);
418 /* Bad service name? */
419 else
420 serv->name = mystrdup(name);
421 myfree(atmp);
422 } else if (serv->type == MASTER_SERV_TYPE_UNIX) {
423 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
424 MAIL_CLASS_PUBLIC, name);
425 } else if (serv->type == MASTER_SERV_TYPE_FIFO) {
426 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
427 MAIL_CLASS_PUBLIC, name);
428 #ifdef MASTER_SERV_TYPE_PASS
429 } else if (serv->type == MASTER_SERV_TYPE_PASS) {
430 serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
431 MAIL_CLASS_PUBLIC, name);
432 #endif
433 } else {
434 msg_panic("bad transport type: %d", serv->type);
438 * Listen socket(s). XXX We pre-allocate storage because the number of
439 * sockets is frozen anyway once we build the command-line vector below.
441 if (serv->listen_fd_count == 0) {
442 msg_fatal("%s: line %d: no valid IP address found: %s",
443 VSTREAM_PATH(master_fp), master_line, name);
445 serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count);
446 for (n = 0; n < serv->listen_fd_count; n++)
447 serv->listen_fd[n] = -1;
450 * Privilege level. Default is to restrict process privileges to those of
451 * the mail owner.
453 unprivileged = get_bool_ent(&bufp, "unprivileged", "y");
456 * Chroot. Default is to restrict file system access to the mail queue.
457 * XXX Chroot cannot imply unprivileged service (for example, the pickup
458 * service runs chrooted but needs privileges to open files as the user).
460 chroot = get_bool_ent(&bufp, "chroot", "y");
463 * Wakeup timer. XXX should we require that var_proc_limit == 1? Right
464 * now, the only services that have a wakeup timer also happen to be the
465 * services that have at most one running instance: local pickup and
466 * local delivery.
468 serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0);
471 * Find out if the wakeup time is conditional, i.e., wakeup triggers
472 * should not be sent until the service has actually been used.
474 if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1] == '?')
475 serv->flags |= MASTER_FLAG_CONDWAKE;
478 * Concurrency limit. Zero means no limit.
480 vstring_sprintf(junk, "%d", var_proc_limit);
481 serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0);
484 * Path to command,
486 command = get_str_ent(&bufp, "command", (char *) 0);
487 serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0);
490 * Idle and total process count.
492 serv->avail_proc = 0;
493 serv->total_proc = 0;
496 * Backoff time in case a service is broken.
498 serv->throttle_delay = var_throttle_time;
501 * Shared channel for child status updates.
503 serv->status_fd[0] = serv->status_fd[1] = -1;
506 * Child process structures.
508 serv->children = 0;
511 * Command-line vector. Add "-n service_name" when the process name
512 * basename differs from the service name. Always add the transport.
514 serv->args = argv_alloc(0);
515 argv_add(serv->args, command, (char *) 0);
516 if (serv->max_proc == 1)
517 argv_add(serv->args, "-l", (char *) 0);
518 if (serv->max_proc == 0)
519 argv_add(serv->args, "-z", (char *) 0);
520 if (strcmp(basename(command), name) != 0)
521 argv_add(serv->args, "-n", name, (char *) 0);
522 argv_add(serv->args, "-t", transport, (char *) 0);
523 if (master_detach == 0)
524 argv_add(serv->args, "-d", (char *) 0);
525 if (msg_verbose)
526 argv_add(serv->args, "-v", (char *) 0);
527 if (unprivileged)
528 argv_add(serv->args, "-u", (char *) 0);
529 if (chroot)
530 argv_add(serv->args, "-c", (char *) 0);
531 if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0) {
532 argv_add(serv->args, "-o", "stress=" CONFIG_BOOL_YES, (char *) 0);
533 serv->stress_param_val =
534 serv->args->argv[serv->args->argc - 1] + sizeof("stress=") - 1;
535 serv->stress_param_val[0] = 0;
536 } else
537 serv->stress_param_val = 0;
538 serv->stress_expire_time = 0;
539 if (serv->listen_fd_count > 1)
540 argv_add(serv->args, "-s",
541 vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)),
542 (char *) 0);
543 while ((cp = mystrtok(&bufp, master_blanks)) != 0)
544 argv_add(serv->args, cp, (char *) 0);
545 argv_terminate(serv->args);
548 * Cleanup.
550 vstring_free(buf);
551 vstring_free(junk);
552 return (serv);
555 /* print_master_ent - show service entry contents */
557 void print_master_ent(MASTER_SERV *serv)
559 char **cpp;
561 msg_info("====start service entry");
562 msg_info("flags: %d", serv->flags);
563 msg_info("name: %s", serv->name);
564 msg_info("type: %s",
565 serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX :
566 serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO :
567 serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET :
568 #ifdef MASTER_SERV_TYPE_PASS
569 serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS :
570 #endif
571 "unknown transport type");
572 msg_info("listen_fd_count: %d", serv->listen_fd_count);
573 msg_info("wakeup: %d", serv->wakeup_time);
574 msg_info("max_proc: %d", serv->max_proc);
575 msg_info("path: %s", serv->path);
576 for (cpp = serv->args->argv; *cpp; cpp++)
577 msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp);
578 msg_info("avail_proc: %d", serv->avail_proc);
579 msg_info("total_proc: %d", serv->total_proc);
580 msg_info("throttle_delay: %d", serv->throttle_delay);
581 msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]);
582 msg_info("children: 0x%lx", (long) serv->children);
583 msg_info("next: 0x%lx", (long) serv->next);
584 msg_info("====end service entry");
587 /* free_master_ent - destroy process entry */
589 void free_master_ent(MASTER_SERV *serv)
593 * Undo what get_master_ent() created.
595 if (serv->flags & MASTER_FLAG_INETHOST) {
596 inet_addr_list_free(MASTER_INET_ADDRLIST(serv));
597 myfree((char *) MASTER_INET_ADDRLIST(serv));
599 if (serv->type == MASTER_SERV_TYPE_INET)
600 myfree(MASTER_INET_PORT(serv));
601 myfree(serv->ext_name);
602 myfree(serv->name);
603 myfree(serv->path);
604 argv_free(serv->args);
605 myfree((char *) serv->listen_fd);
606 myfree((char *) serv);