/*++
/* NAME
/* master_ent 3
/* SUMMARY
/* Postfix master - config file access
/* SYNOPSIS
/* #include "master.h"
/*
/* void fset_master_ent(path)
/* char *path;
/*
/* void set_master_ent()
/*
/* MASTER_SERV *get_master_ent()
/*
/* void end_master_ent()
/*
/* void print_master_ent(entry)
/* MASTER_SERV *entry;
/*
/* void free_master_ent(entry)
/* MASTER_SERV *entry;
/* DESCRIPTION
/* This module implements a simple programmatic interface
/* for accessing Postfix master process configuration files.
/*
/* fset_master_ent() specifies the location of the master process
/* configuration file. The pathname is copied.
/*
/* set_master_ent() opens the configuration file. It is an error
/* to call this routine while the configuration file is still open.
/* It is an error to open a configuration file without specifying
/* its name to fset_master_ent().
/*
/* get_master_ent() reads the next entry from an open configuration
/* file and returns the parsed result. A null result means the end
/* of file was reached.
/*
/* print_master_ent() prints the specified service entry.
/*
/* end_master_ent() closes an open configuration file. It is an error
/* to call this routine when the configuration file is not open.
/*
/* free_master_ent() destroys the memory used for a parsed configuration
/* file entry.
/* DIAGNOSTICS
/* Panics: interface violations. Fatal errors: memory allocation
/* failure.
/* BUGS
/* SEE ALSO
/* LICENSE
/* .ad
/* .fi
/* The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/* Wietse Venema
/* IBM T.J. Watson Research
/* P.O. Box 704
/* Yorktown Heights, NY 10598, USA
/*
/* Wietse Venema
/* Google, Inc.
/* 111 8th Avenue
/* New York, NY 10011, USA
/*--*/
/* System libraries. */
#include <sys_defs.h>
#include <netinet/in.h>
#include <stdarg.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <fcntl.h>
#ifdef STRCASECMP_IN_STRINGS_H
#include <strings.h>
#endif
/* Utility libraries. */
#include <msg.h>
#include <mymalloc.h>
#include <vstring.h>
#include <vstream.h>
#include <argv.h>
#include <stringops.h>
#include <readlline.h>
#include <inet_addr_list.h>
#include <host_port.h>
#include <inet_addr_host.h>
#include <sock_addr.h>
#include <inet_proto.h>
/* Global library. */
#include <match_service.h>
#include <mail_proto.h>
#include <mail_params.h>
#include <own_inet_addr.h>
#include <wildcard_inet_addr.h>
#include <mail_conf.h>
#include <compat_level.h>
/* Local stuff. */
#include "master_proto.h"
#include "master.h"
static char *master_path; /* config file name */
static VSTREAM *master_fp; /* config file pointer */
static int master_line_last; /* config file line number */
static int master_line; /* config file line number */
static ARGV *master_disable; /* disabled service patterns */
static char master_blanks[] = CHARS_SPACE; /* field delimiters */
/* fset_master_ent - specify configuration file pathname */
void fset_master_ent(char *path)
{
if (master_path != 0 )
myfree(master_path);
master_path = mystrdup(path);
}
/* set_master_ent - open configuration file */
void set_master_ent()
{
const char *myname = "set_master_ent" ;
char *disable;
if (master_fp != 0 )
msg_panic("%s: configuration file still open" , myname);
if (master_path == 0 )
msg_panic("%s: no configuration file specified" , myname);
if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0 )) == 0 )
msg_fatal("open %s: %m" , master_path);
master_line_last = 0 ;
if (master_disable != 0 )
msg_panic("%s: service disable list still exists" , myname);
if (inet_proto_info()->ai_family_list[0 ] == 0 ) {
msg_warn("all network protocols are disabled (%s = %s)" ,
VAR_INET_PROTOCOLS, var_inet_protocols);
msg_warn("disabling all type \" inet\" services in master.cf" );
disable = concatenate(MASTER_XPORT_NAME_INET, "," ,
var_master_disable, (char *) 0 );
master_disable = match_service_init(disable);
myfree(disable);
} else
master_disable = match_service_init(var_master_disable);
}
/* end_master_ent - close configuration file */
void end_master_ent()
{
const char *myname = "end_master_ent" ;
if (master_fp == 0 )
msg_panic("%s: configuration file not open" , myname);
if (vstream_fclose(master_fp) != 0 )
msg_fatal("%s: close configuration file: %m" , myname);
master_fp = 0 ;
if (master_disable == 0 )
msg_panic("%s: no service disable list" , myname);
match_service_free(master_disable);
master_disable = 0 ;
}
/* master_conf_context - plot the target range */
static const char *master_conf_context(void )
{
static VSTRING *context_buf = 0 ;
if (context_buf == 0 )
context_buf = vstring_alloc(100 );
vstring_sprintf(context_buf, "%s: line %d" , master_path, master_line);
return (vstring_str(context_buf));
}
/* fatal_with_context - print fatal error with file/line context */
static NORETURN PRINTFLIKE(1 , 2 ) fatal_with_context(char *format,...)
{
const char *myname = "fatal_with_context" ;
VSTRING *vp = vstring_alloc(100 );
va_list ap;
if (master_path == 0 )
msg_panic("%s: no configuration file specified" , myname);
va_start(ap, format);
vstring_vsprintf(vp, format, ap);
va_end(ap);
msg_fatal("%s: %s" , master_conf_context(), vstring_str(vp));
}
/* fatal_invalid_field - report invalid field value */
static NORETURN fatal_invalid_field(char *name, char *value)
{
fatal_with_context("field \" %s\": bad value: \" %s\"" , name, value);
}
/* get_str_ent - extract string field */
static char *get_str_ent(char **bufp, char *name, char *def_val)
{
char *value;
if ((value = mystrtok(bufp, master_blanks)) == 0 )
fatal_with_context("missing \" %s\" field" , name);
if (strcmp(value, "-" ) == 0 ) {
if (def_val == 0 )
fatal_with_context("field \" %s\" has no default value" , name);
if (warn_compat_break_chroot && strcmp(name, "chroot" ) == 0 )
msg_info("%s: using backwards-compatible default setting "
"%s=%s" , master_conf_context(), name, def_val);
return (def_val);
} else {
return (value);
}
}
/* get_bool_ent - extract boolean field */
static int get_bool_ent(char **bufp, char *name, char *def_val)
{
char *value;
value = get_str_ent(bufp, name, def_val);
if (strcmp("y" , value) == 0 ) {
return (1 );
} else if (strcmp("n" , value) == 0 ) {
return (0 );
} else {
fatal_invalid_field(name, value);
}
/* NOTREACHED */
}
/* get_int_ent - extract integer field */
static int get_int_ent(char **bufp, char *name, char *def_val, int min_val)
{
char *value;
int n;
value = get_str_ent(bufp, name, def_val);
if (!ISDIGIT(*value) || (n = atoi(value)) < min_val)
fatal_invalid_field(name, value);
return (n);
}
/* get_master_ent - read entry from configuration file */
MASTER_SERV *get_master_ent()
{
VSTRING *buf = vstring_alloc(100 );
VSTRING *junk = vstring_alloc(100 );
MASTER_SERV *serv;
char *cp;
char *name;
char *host = 0 ;
char *port = 0 ;
char *transport;
int private ;
int unprivileged; /* passed on to child */
int chroot; /* passed on to child */
char *command;
int n;
char *bufp;
char *atmp;
const char *parse_err;
static char *saved_interfaces = 0 ;
char *err;
if (master_fp == 0 )
msg_panic("get_master_ent: config file not open" );
if (master_disable == 0 )
msg_panic("get_master_ent: no service disable list" );
/*
* XXX We cannot change the inet_interfaces setting for a running master
* process . Listening sockets are inherited by child processes so that
* closing and reopening those sockets in the master does not work .
*
* Another problem is that library routines still cache results that are
* based on the old inet_interfaces setting . It is too much trouble to
* recompute everything .
*
* In order to keep our data structures consistent we ignore changes in
* inet_interfaces settings , and issue a warning instead .
*/
if (saved_interfaces == 0 )
saved_interfaces = mystrdup(var_inet_interfaces);
/*
* Skip blank lines and comment lines .
*/
for (;;) {
if (readllines(buf, master_fp, &master_line_last, &master_line) == 0 ) {
vstring_free(buf);
vstring_free(junk);
return (0 );
}
bufp = vstring_str(buf);
if ((cp = mystrtok(&bufp, master_blanks)) == 0 )
continue ;
name = cp;
transport = get_str_ent(&bufp, "transport type" , (char *) 0 );
vstring_sprintf(junk, "%s/%s" , name, transport);
if (match_service_match(master_disable, vstring_str(junk)) == 0 )
break ;
}
/*
* Parse one logical line from the configuration file . Initialize service
* structure members in order .
*/
serv = (MASTER_SERV *) mymalloc(sizeof (MASTER_SERV));
serv->next = 0 ;
/*
* Flags member .
*/
serv->flags = 0 ;
/*
* All servers busy warning timer .
*/
serv->busy_warn_time = 0 ;
/*
* Service name . Syntax is transport - specific .
*/
serv->ext_name = mystrdup(name);
/*
* Transport type : inet ( wild - card listen or virtual ) or unix .
*/
#define STR_SAME !strcmp
if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) {
if (!STR_SAME(saved_interfaces, var_inet_interfaces)) {
msg_warn("service %s: ignoring %s change" ,
serv->ext_name, VAR_INET_INTERFACES);
msg_warn("to change %s, stop and start Postfix" ,
VAR_INET_INTERFACES);
}
serv->type = MASTER_SERV_TYPE_INET;
atmp = mystrdup(name);
if ((parse_err = host_port(atmp, &host, "" , &port, (char *) 0 )) != 0 )
fatal_with_context("%s in \" %s\"" , parse_err, name);
if (*host) {
serv->flags |= MASTER_FLAG_INETHOST;/* host:port */
MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *)
mymalloc(sizeof (*MASTER_INET_ADDRLIST(serv)));
inet_addr_list_init(MASTER_INET_ADDRLIST(serv));
if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0 )
fatal_with_context("bad hostname or network address: %s" , name);
inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
} else {
MASTER_INET_ADDRLIST(serv) =
strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ?
own_inet_addr_list() : /* result can be empty */
wildcard_inet_addr_list(); /* result can't be empty */
if (MASTER_INET_ADDRLIST(serv)->used == 0 )
fatal_with_context("service definition requires valid"
" host name or address, or non-empty"
" %s setting" , VAR_INET_INTERFACES);
inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
}
MASTER_INET_PORT(serv) = mystrdup(port);
for (n = 0 ; /* see below */ ; n++) {
if (n >= MASTER_INET_ADDRLIST(serv)->used) {
serv->flags |= MASTER_FLAG_LOCAL_ONLY;
break ;
}
if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n)))
break ;
}
} else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) {
serv->type = MASTER_SERV_TYPE_UNIX;
serv->listen_fd_count = 1 ;
serv->flags |= MASTER_FLAG_LOCAL_ONLY;
} else if (STR_SAME(transport, MASTER_XPORT_NAME_UXDG)) {
serv->type = MASTER_SERV_TYPE_UXDG;
serv->listen_fd_count = 1 ;
serv->flags |= MASTER_FLAG_LOCAL_ONLY;
} else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) {
serv->type = MASTER_SERV_TYPE_FIFO;
serv->listen_fd_count = 1 ;
serv->flags |= MASTER_FLAG_LOCAL_ONLY;
#ifdef MASTER_SERV_TYPE_PASS
} else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) {
serv->type = MASTER_SERV_TYPE_PASS;
serv->listen_fd_count = 1 ;
/* If this is a connection screener, remote clients are likely. */
#endif
} else {
fatal_with_context("bad transport type: %s" , transport);
}
/*
* Service class : public or private .
*/
private = get_bool_ent(&bufp, "private" , "y" );
/*
* Derive an internal service name . The name may depend on service
* attributes such as privacy .
*/
if (serv->type == MASTER_SERV_TYPE_INET) {
MAI_HOSTADDR_STR host_addr;
MAI_SERVPORT_STR serv_port;
struct addrinfo *res0;
if (private )
fatal_with_context("inet service cannot be private" );
/*
* Canonicalize endpoint names so that we correctly handle " reload "
* requests after someone changes " 25 " into " smtp " or vice versa .
*/
if (*host == 0 )
host = 0 ;
/* Canonicalize numeric host and numeric or symbolic service. */
if (hostaddr_to_sockaddr(host, port, 0 , &res0) == 0 ) {
SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
host ? &host_addr : (MAI_HOSTADDR_STR *) 0 ,
&serv_port, 0 );
serv->name = (host ? concatenate("[" , host_addr.buf, "]:" ,
serv_port.buf, (char *) 0 ) :
mystrdup(serv_port.buf));
freeaddrinfo(res0);
}
/* Canonicalize numeric or symbolic service. */
else if (hostaddr_to_sockaddr((char *) 0 , port, 0 , &res0) == 0 ) {
SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
(MAI_HOSTADDR_STR *) 0 , &serv_port, 0 );
serv->name = (host ? concatenate("[" , host, "]:" ,
serv_port.buf, (char *) 0 ) :
mystrdup(serv_port.buf));
freeaddrinfo(res0);
}
/* Bad service name? */
else
serv->name = mystrdup(name);
myfree(atmp);
} else if (serv->type == MASTER_SERV_TYPE_UNIX) {
serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
MAIL_CLASS_PUBLIC, name);
} else if (serv->type == MASTER_SERV_TYPE_UXDG) {
serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
MAIL_CLASS_PUBLIC, name);
} else if (serv->type == MASTER_SERV_TYPE_FIFO) {
serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
MAIL_CLASS_PUBLIC, name);
#ifdef MASTER_SERV_TYPE_PASS
} else if (serv->type == MASTER_SERV_TYPE_PASS) {
serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
MAIL_CLASS_PUBLIC, name);
#endif
} else {
msg_panic("bad transport type: %d" , serv->type);
}
/*
* Listen socket ( s ) . XXX We pre - allocate storage because the number of
* sockets is frozen anyway once we build the command - line vector below .
*/
if (serv->listen_fd_count == 0 ) {
fatal_with_context("no valid IP address found: %s" , name);
}
serv->listen_fd = (int *) mymalloc(sizeof (int ) * serv->listen_fd_count);
for (n = 0 ; n < serv->listen_fd_count; n++)
serv->listen_fd[n] = -1 ;
/*
* Privilege level . Default is to restrict process privileges to those of
* the mail owner .
*/
unprivileged = get_bool_ent(&bufp, "unprivileged" , "y" );
/*
* Chroot . Default is to restrict file system access to the mail queue .
* XXX Chroot cannot imply unprivileged service ( for example , the pickup
* service runs chrooted but needs privileges to open files as the user ) .
*/
chroot = get_bool_ent(&bufp, "chroot" , compat_level
< compat_level_from_string(COMPAT_LEVEL_1, msg_panic) ?
"y" : "n" );
/*
* Wakeup timer . XXX should we require that var_proc_limit = = 1 ? Right
* now , the only services that have a wakeup timer also happen to be the
* services that have at most one running instance : local pickup and
* local delivery .
*/
serv->wakeup_time = get_int_ent(&bufp, "wakeup_time" , "0" , 0 );
/*
* Find out if the wakeup time is conditional , i . e . , wakeup triggers
* should not be sent until the service has actually been used .
*/
if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1 ] == '?' )
serv->flags |= MASTER_FLAG_CONDWAKE;
/*
* Concurrency limit . Zero means no limit .
*/
vstring_sprintf(junk, "%d" , var_proc_limit);
serv->max_proc = get_int_ent(&bufp, "max_proc" , vstring_str(junk), 0 );
/*
* Path to command ,
*/
command = get_str_ent(&bufp, "command" , (char *) 0 );
serv->path = concatenate(var_daemon_dir, "/" , command, (char *) 0 );
/*
* Idle and total process count .
*/
serv->avail_proc = 0 ;
serv->total_proc = 0 ;
/*
* Backoff time in case a service is broken .
*/
serv->throttle_delay = var_throttle_time;
/*
* Shared channel for child status updates .
*/
serv->status_fd[0 ] = serv->status_fd[1 ] = -1 ;
/*
* Child process structures .
*/
serv->children = 0 ;
/*
* Command - line vector . Add " - n service_name " when the process name
* basename differs from the service name . Always add the transport .
*/
serv->args = argv_alloc(0 );
argv_add(serv->args, command, (char *) 0 );
if (serv->max_proc == 1 )
argv_add(serv->args, "-l" , (char *) 0 );
if (serv->max_proc == 0 )
argv_add(serv->args, "-z" , (char *) 0 );
if (strcmp(basename(command), name) != 0 )
argv_add(serv->args, "-n" , name, (char *) 0 );
argv_add(serv->args, "-t" , transport, (char *) 0 );
if (master_detach == 0 )
argv_add(serv->args, "-d" , (char *) 0 );
if (msg_verbose)
argv_add(serv->args, "-v" , (char *) 0 );
if (unprivileged)
argv_add(serv->args, "-u" , (char *) 0 );
if (chroot)
argv_add(serv->args, "-c" , (char *) 0 );
if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0 && serv->max_proc > 1 ) {
argv_add(serv->args, "-o" , "stress=" CONFIG_BOOL_YES, (char *) 0 );
serv->stress_param_val =
serv->args->argv[serv->args->argc - 1 ] + sizeof ("stress=" ) - 1 ;
serv->stress_param_val[0 ] = 0 ;
} else
serv->stress_param_val = 0 ;
serv->stress_expire_time = 0 ;
if (serv->listen_fd_count > 1 )
argv_add(serv->args, "-s" ,
vstring_str(vstring_sprintf(junk, "%d" , serv->listen_fd_count)),
(char *) 0 );
while ((cp = mystrtokq(&bufp, master_blanks, CHARS_BRACE)) != 0 ) {
if (*cp == CHARS_BRACE[0 ]
&& (err = extpar(&cp, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0 )
fatal_with_context("%s" , err);
argv_add(serv->args, cp, (char *) 0 );
}
argv_terminate(serv->args);
/*
* Cleanup .
*/
vstring_free(buf);
vstring_free(junk);
return (serv);
}
/* print_master_ent - show service entry contents */
void print_master_ent(MASTER_SERV *serv)
{
char **cpp;
msg_info("====start service entry" );
msg_info("flags: %d" , serv->flags);
msg_info("name: %s" , serv->name);
msg_info("type: %s" ,
serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX :
serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO :
serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET :
#ifdef MASTER_SERV_TYPE_PASS
serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS :
#endif
serv->type == MASTER_SERV_TYPE_UXDG ? MASTER_XPORT_NAME_UXDG :
"unknown transport type" );
msg_info("listen_fd_count: %d" , serv->listen_fd_count);
msg_info("wakeup: %d" , serv->wakeup_time);
msg_info("max_proc: %d" , serv->max_proc);
msg_info("path: %s" , serv->path);
for (cpp = serv->args->argv; *cpp; cpp++)
msg_info("arg[%d]: %s" , (int ) (cpp - serv->args->argv), *cpp);
msg_info("avail_proc: %d" , serv->avail_proc);
msg_info("total_proc: %d" , serv->total_proc);
msg_info("throttle_delay: %d" , serv->throttle_delay);
msg_info("status_fd %d %d" , serv->status_fd[0 ], serv->status_fd[1 ]);
msg_info("children: 0x%lx" , (long ) serv->children);
msg_info("next: 0x%lx" , (long ) serv->next);
msg_info("====end service entry" );
}
/* free_master_ent - destroy process entry */
void free_master_ent(MASTER_SERV *serv)
{
/*
* Undo what get_master_ent ( ) created .
*/
if (serv->flags & MASTER_FLAG_INETHOST) {
inet_addr_list_free(MASTER_INET_ADDRLIST(serv));
myfree((void *) MASTER_INET_ADDRLIST(serv));
}
if (serv->type == MASTER_SERV_TYPE_INET)
myfree(MASTER_INET_PORT(serv));
myfree(serv->ext_name);
myfree(serv->name);
myfree(serv->path);
argv_free(serv->args);
myfree((void *) serv->listen_fd);
myfree((void *) serv);
}
Messung V0.5 in Prozent C=82 H=96 G=89
¤ Dauer der Verarbeitung: 0.29 Sekunden
(vorverarbeitet am 2026-08-08)
¤
*© Formatika GbR, Deutschland