Home | History | Annotate | Line # | Download | only in master
      1 /*	$NetBSD: master_ent.c,v 1.5 2025/02/25 19:15:46 christos Exp $	*/
      2 
      3 /*++
      4 /* NAME
      5 /*	master_ent 3
      6 /* SUMMARY
      7 /*	Postfix master - config file access
      8 /* SYNOPSIS
      9 /*	#include "master.h"
     10 /*
     11 /*	void	fset_master_ent(path)
     12 /*	char	*path;
     13 /*
     14 /*	void	set_master_ent()
     15 /*
     16 /*	MASTER_SERV *get_master_ent()
     17 /*
     18 /*	void	end_master_ent()
     19 /*
     20 /*	void	print_master_ent(entry)
     21 /*	MASTER_SERV *entry;
     22 /*
     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.
     28 /*
     29 /*	fset_master_ent() specifies the location of the master process
     30 /*	configuration file.  The pathname is copied.
     31 /*
     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().
     36 /*
     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.
     40 /*
     41 /*	print_master_ent() prints the specified service entry.
     42 /*
     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.
     45 /*
     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 /*
     63 /*	Wietse Venema
     64 /*	Google, Inc.
     65 /*	111 8th Avenue
     66 /*	New York, NY 10011, USA
     67 /*--*/
     68 
     69 /* System libraries. */
     70 
     71 #include <sys_defs.h>
     72 #include <netinet/in.h>
     73 #include <stdarg.h>
     74 #include <string.h>
     75 #include <stdlib.h>
     76 #include <unistd.h>
     77 #include <ctype.h>
     78 #include <fcntl.h>
     79 
     80 #ifdef STRCASECMP_IN_STRINGS_H
     81 #include <strings.h>
     82 #endif
     83 
     84 /* Utility libraries. */
     85 
     86 #include <msg.h>
     87 #include <mymalloc.h>
     88 #include <vstring.h>
     89 #include <vstream.h>
     90 #include <argv.h>
     91 #include <stringops.h>
     92 #include <readlline.h>
     93 #include <inet_addr_list.h>
     94 #include <host_port.h>
     95 #include <inet_addr_host.h>
     96 #include <sock_addr.h>
     97 #include <inet_proto.h>
     98 
     99 /* Global library. */
    100 
    101 #include <match_service.h>
    102 #include <mail_proto.h>
    103 #include <mail_params.h>
    104 #include <own_inet_addr.h>
    105 #include <wildcard_inet_addr.h>
    106 #include <mail_conf.h>
    107 #include <compat_level.h>
    108 
    109 /* Local stuff. */
    110 
    111 #include "master_proto.h"
    112 #include "master.h"
    113 
    114 static char *master_path;		/* config file name */
    115 static VSTREAM *master_fp;		/* config file pointer */
    116 static int master_line_last;		/* config file line number */
    117 static int master_line;			/* config file line number */
    118 static ARGV *master_disable;		/* disabled service patterns */
    119 
    120 static char master_blanks[] = CHARS_SPACE;	/* field delimiters */
    121 
    122 /* fset_master_ent - specify configuration file pathname */
    123 
    124 void    fset_master_ent(char *path)
    125 {
    126     if (master_path != 0)
    127 	myfree(master_path);
    128     master_path = mystrdup(path);
    129 }
    130 
    131 /* set_master_ent - open configuration file */
    132 
    133 void    set_master_ent()
    134 {
    135     const char *myname = "set_master_ent";
    136     char   *disable;
    137 
    138     if (master_fp != 0)
    139 	msg_panic("%s: configuration file still open", myname);
    140     if (master_path == 0)
    141 	msg_panic("%s: no configuration file specified", myname);
    142     if ((master_fp = vstream_fopen(master_path, O_RDONLY, 0)) == 0)
    143 	msg_fatal("open %s: %m", master_path);
    144     master_line_last = 0;
    145     if (master_disable != 0)
    146 	msg_panic("%s: service disable list still exists", myname);
    147     if (inet_proto_info()->ai_family_list[0] == 0) {
    148 	msg_warn("all network protocols are disabled (%s = %s)",
    149 		 VAR_INET_PROTOCOLS, var_inet_protocols);
    150 	msg_warn("disabling all type \"inet\" services in master.cf");
    151 	disable = concatenate(MASTER_XPORT_NAME_INET, ",",
    152 			      var_master_disable, (char *) 0);
    153 	master_disable = match_service_init(disable);
    154 	myfree(disable);
    155     } else
    156 	master_disable = match_service_init(var_master_disable);
    157 }
    158 
    159 /* end_master_ent - close configuration file */
    160 
    161 void    end_master_ent()
    162 {
    163     const char *myname = "end_master_ent";
    164 
    165     if (master_fp == 0)
    166 	msg_panic("%s: configuration file not open", myname);
    167     if (vstream_fclose(master_fp) != 0)
    168 	msg_fatal("%s: close configuration file: %m", myname);
    169     master_fp = 0;
    170     if (master_disable == 0)
    171 	msg_panic("%s: no service disable list", myname);
    172     match_service_free(master_disable);
    173     master_disable = 0;
    174 }
    175 
    176 /* master_conf_context - plot the target range */
    177 
    178 static const char *master_conf_context(void)
    179 {
    180     static VSTRING *context_buf = 0;
    181 
    182     if (context_buf == 0)
    183 	context_buf = vstring_alloc(100);
    184     vstring_sprintf(context_buf, "%s: line %d", master_path, master_line);
    185     return (vstring_str(context_buf));
    186 }
    187 
    188 /* fatal_with_context - print fatal error with file/line context */
    189 
    190 static NORETURN PRINTFLIKE(1, 2) fatal_with_context(char *format,...)
    191 {
    192     const char *myname = "fatal_with_context";
    193     VSTRING *vp = vstring_alloc(100);
    194     va_list ap;
    195 
    196     if (master_path == 0)
    197 	msg_panic("%s: no configuration file specified", myname);
    198 
    199     va_start(ap, format);
    200     vstring_vsprintf(vp, format, ap);
    201     va_end(ap);
    202     msg_fatal("%s: %s", master_conf_context(), vstring_str(vp));
    203 }
    204 
    205 /* fatal_invalid_field - report invalid field value */
    206 
    207 static NORETURN fatal_invalid_field(char *name, char *value)
    208 {
    209     fatal_with_context("field \"%s\": bad value: \"%s\"", name, value);
    210 }
    211 
    212 /* get_str_ent - extract string field */
    213 
    214 static char *get_str_ent(char **bufp, char *name, char *def_val)
    215 {
    216     char   *value;
    217 
    218     if ((value = mystrtok(bufp, master_blanks)) == 0)
    219 	fatal_with_context("missing \"%s\" field", name);
    220     if (strcmp(value, "-") == 0) {
    221 	if (def_val == 0)
    222 	    fatal_with_context("field \"%s\" has no default value", name);
    223 	if (warn_compat_break_chroot && strcmp(name, "chroot") == 0)
    224 	    msg_info("%s: using backwards-compatible default setting "
    225 		     "%s=%s", master_conf_context(), name, def_val);
    226 	return (def_val);
    227     } else {
    228 	return (value);
    229     }
    230 }
    231 
    232 /* get_bool_ent - extract boolean field */
    233 
    234 static int get_bool_ent(char **bufp, char *name, char *def_val)
    235 {
    236     char   *value;
    237 
    238     value = get_str_ent(bufp, name, def_val);
    239     if (strcmp("y", value) == 0) {
    240 	return (1);
    241     } else if (strcmp("n", value) == 0) {
    242 	return (0);
    243     } else {
    244 	fatal_invalid_field(name, value);
    245     }
    246     /* NOTREACHED */
    247 }
    248 
    249 /* get_int_ent - extract integer field */
    250 
    251 static int get_int_ent(char **bufp, char *name, char *def_val, int min_val)
    252 {
    253     char   *value;
    254     int     n;
    255 
    256     value = get_str_ent(bufp, name, def_val);
    257     if (!ISDIGIT(*value) || (n = atoi(value)) < min_val)
    258 	fatal_invalid_field(name, value);
    259     return (n);
    260 }
    261 
    262 /* get_master_ent - read entry from configuration file */
    263 
    264 MASTER_SERV *get_master_ent()
    265 {
    266     VSTRING *buf = vstring_alloc(100);
    267     VSTRING *junk = vstring_alloc(100);
    268     MASTER_SERV *serv;
    269     char   *cp;
    270     char   *name;
    271     char   *host = 0;
    272     char   *port = 0;
    273     char   *transport;
    274     int     private;
    275     int     unprivileged;		/* passed on to child */
    276     int     chroot;			/* passed on to child */
    277     char   *command;
    278     int     n;
    279     char   *bufp;
    280     char   *atmp;
    281     const char *parse_err;
    282     static char *saved_interfaces = 0;
    283     char   *err;
    284 
    285     if (master_fp == 0)
    286 	msg_panic("get_master_ent: config file not open");
    287     if (master_disable == 0)
    288 	msg_panic("get_master_ent: no service disable list");
    289 
    290     /*
    291      * XXX We cannot change the inet_interfaces setting for a running master
    292      * process. Listening sockets are inherited by child processes so that
    293      * closing and reopening those sockets in the master does not work.
    294      *
    295      * Another problem is that library routines still cache results that are
    296      * based on the old inet_interfaces setting. It is too much trouble to
    297      * recompute everything.
    298      *
    299      * In order to keep our data structures consistent we ignore changes in
    300      * inet_interfaces settings, and issue a warning instead.
    301      */
    302     if (saved_interfaces == 0)
    303 	saved_interfaces = mystrdup(var_inet_interfaces);
    304 
    305     /*
    306      * Skip blank lines and comment lines.
    307      */
    308     for (;;) {
    309 	if (readllines(buf, master_fp, &master_line_last, &master_line) == 0) {
    310 	    vstring_free(buf);
    311 	    vstring_free(junk);
    312 	    return (0);
    313 	}
    314 	bufp = vstring_str(buf);
    315 	if ((cp = mystrtok(&bufp, master_blanks)) == 0)
    316 	    continue;
    317 	name = cp;
    318 	transport = get_str_ent(&bufp, "transport type", (char *) 0);
    319 	vstring_sprintf(junk, "%s/%s", name, transport);
    320 	if (match_service_match(master_disable, vstring_str(junk)) == 0)
    321 	    break;
    322     }
    323 
    324     /*
    325      * Parse one logical line from the configuration file. Initialize service
    326      * structure members in order.
    327      */
    328     serv = (MASTER_SERV *) mymalloc(sizeof(MASTER_SERV));
    329     serv->next = 0;
    330 
    331     /*
    332      * Flags member.
    333      */
    334     serv->flags = 0;
    335 
    336     /*
    337      * All servers busy warning timer.
    338      */
    339     serv->busy_warn_time = 0;
    340 
    341     /*
    342      * Service name. Syntax is transport-specific.
    343      */
    344     serv->ext_name = mystrdup(name);
    345 
    346     /*
    347      * Transport type: inet (wild-card listen or virtual) or unix.
    348      */
    349 #define STR_SAME	!strcmp
    350 
    351     if (STR_SAME(transport, MASTER_XPORT_NAME_INET)) {
    352 	if (!STR_SAME(saved_interfaces, var_inet_interfaces)) {
    353 	    msg_warn("service %s: ignoring %s change",
    354 		     serv->ext_name, VAR_INET_INTERFACES);
    355 	    msg_warn("to change %s, stop and start Postfix",
    356 		     VAR_INET_INTERFACES);
    357 	}
    358 	serv->type = MASTER_SERV_TYPE_INET;
    359 	atmp = mystrdup(name);
    360 	if ((parse_err = host_port(atmp, &host, "", &port, (char *) 0)) != 0)
    361 	    fatal_with_context("%s in \"%s\"", parse_err, name);
    362 	if (*host) {
    363 	    serv->flags |= MASTER_FLAG_INETHOST;/* host:port */
    364 	    MASTER_INET_ADDRLIST(serv) = (INET_ADDR_LIST *)
    365 		mymalloc(sizeof(*MASTER_INET_ADDRLIST(serv)));
    366 	    inet_addr_list_init(MASTER_INET_ADDRLIST(serv));
    367 	    if (inet_addr_host(MASTER_INET_ADDRLIST(serv), host) == 0)
    368 		fatal_with_context("bad hostname or network address: %s", name);
    369 	    inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
    370 	    serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
    371 	} else {
    372 	    MASTER_INET_ADDRLIST(serv) =
    373 		strcasecmp(saved_interfaces, INET_INTERFACES_ALL) ?
    374 		own_inet_addr_list() :		/* result can be empty */
    375 		wildcard_inet_addr_list();	/* result can't be empty */
    376 	    if (MASTER_INET_ADDRLIST(serv)->used == 0)
    377 		fatal_with_context("service definition requires valid"
    378 				   " host name or address, or non-empty"
    379 				   " %s setting", VAR_INET_INTERFACES);
    380 	    inet_addr_list_uniq(MASTER_INET_ADDRLIST(serv));
    381 	    serv->listen_fd_count = MASTER_INET_ADDRLIST(serv)->used;
    382 	}
    383 	MASTER_INET_PORT(serv) = mystrdup(port);
    384 	for (n = 0; /* see below */ ; n++) {
    385 	    if (n >= MASTER_INET_ADDRLIST(serv)->used) {
    386 		serv->flags |= MASTER_FLAG_LOCAL_ONLY;
    387 		break;
    388 	    }
    389 	    if (!sock_addr_in_loopback(SOCK_ADDR_PTR(MASTER_INET_ADDRLIST(serv)->addrs + n)))
    390 		break;
    391 	}
    392     } else if (STR_SAME(transport, MASTER_XPORT_NAME_UNIX)) {
    393 	serv->type = MASTER_SERV_TYPE_UNIX;
    394 	serv->listen_fd_count = 1;
    395 	serv->flags |= MASTER_FLAG_LOCAL_ONLY;
    396     } else if (STR_SAME(transport, MASTER_XPORT_NAME_UXDG)) {
    397 	serv->type = MASTER_SERV_TYPE_UXDG;
    398 	serv->listen_fd_count = 1;
    399 	serv->flags |= MASTER_FLAG_LOCAL_ONLY;
    400     } else if (STR_SAME(transport, MASTER_XPORT_NAME_FIFO)) {
    401 	serv->type = MASTER_SERV_TYPE_FIFO;
    402 	serv->listen_fd_count = 1;
    403 	serv->flags |= MASTER_FLAG_LOCAL_ONLY;
    404 #ifdef MASTER_SERV_TYPE_PASS
    405     } else if (STR_SAME(transport, MASTER_XPORT_NAME_PASS)) {
    406 	serv->type = MASTER_SERV_TYPE_PASS;
    407 	serv->listen_fd_count = 1;
    408 	/* If this is a connection screener, remote clients are likely. */
    409 #endif
    410     } else {
    411 	fatal_with_context("bad transport type: %s", transport);
    412     }
    413 
    414     /*
    415      * Service class: public or private.
    416      */
    417     private = get_bool_ent(&bufp, "private", "y");
    418 
    419     /*
    420      * Derive an internal service name. The name may depend on service
    421      * attributes such as privacy.
    422      */
    423     if (serv->type == MASTER_SERV_TYPE_INET) {
    424 	MAI_HOSTADDR_STR host_addr;
    425 	MAI_SERVPORT_STR serv_port;
    426 	struct addrinfo *res0;
    427 
    428 	if (private)
    429 	    fatal_with_context("inet service cannot be private");
    430 
    431 	/*
    432 	 * Canonicalize endpoint names so that we correctly handle "reload"
    433 	 * requests after someone changes "25" into "smtp" or vice versa.
    434 	 */
    435 	if (*host == 0)
    436 	    host = 0;
    437 	/* Canonicalize numeric host and numeric or symbolic service. */
    438 	if (hostaddr_to_sockaddr(host, port, 0, &res0) == 0) {
    439 	    SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
    440 				 host ? &host_addr : (MAI_HOSTADDR_STR *) 0,
    441 				 &serv_port, 0);
    442 	    serv->name = (host ? concatenate("[", host_addr.buf, "]:",
    443 					     serv_port.buf, (char *) 0) :
    444 			  mystrdup(serv_port.buf));
    445 	    freeaddrinfo(res0);
    446 	}
    447 	/* Canonicalize numeric or symbolic service. */
    448 	else if (hostaddr_to_sockaddr((char *) 0, port, 0, &res0) == 0) {
    449 	    SOCKADDR_TO_HOSTADDR(res0->ai_addr, res0->ai_addrlen,
    450 				 (MAI_HOSTADDR_STR *) 0, &serv_port, 0);
    451 	    serv->name = (host ? concatenate("[", host, "]:",
    452 					     serv_port.buf, (char *) 0) :
    453 			  mystrdup(serv_port.buf));
    454 	    freeaddrinfo(res0);
    455 	}
    456 	/* Bad service name? */
    457 	else
    458 	    serv->name = mystrdup(name);
    459 	myfree(atmp);
    460     } else if (serv->type == MASTER_SERV_TYPE_UNIX) {
    461 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
    462 				   MAIL_CLASS_PUBLIC, name);
    463     } else if (serv->type == MASTER_SERV_TYPE_UXDG) {
    464 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
    465 				   MAIL_CLASS_PUBLIC, name);
    466     } else if (serv->type == MASTER_SERV_TYPE_FIFO) {
    467 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
    468 				   MAIL_CLASS_PUBLIC, name);
    469 #ifdef MASTER_SERV_TYPE_PASS
    470     } else if (serv->type == MASTER_SERV_TYPE_PASS) {
    471 	serv->name = mail_pathname(private ? MAIL_CLASS_PRIVATE :
    472 				   MAIL_CLASS_PUBLIC, name);
    473 #endif
    474     } else {
    475 	msg_panic("bad transport type: %d", serv->type);
    476     }
    477 
    478     /*
    479      * Listen socket(s). XXX We pre-allocate storage because the number of
    480      * sockets is frozen anyway once we build the command-line vector below.
    481      */
    482     if (serv->listen_fd_count == 0) {
    483 	fatal_with_context("no valid IP address found: %s", name);
    484     }
    485     serv->listen_fd = (int *) mymalloc(sizeof(int) * serv->listen_fd_count);
    486     for (n = 0; n < serv->listen_fd_count; n++)
    487 	serv->listen_fd[n] = -1;
    488 
    489     /*
    490      * Privilege level. Default is to restrict process privileges to those of
    491      * the mail owner.
    492      */
    493     unprivileged = get_bool_ent(&bufp, "unprivileged", "y");
    494 
    495     /*
    496      * Chroot. Default is to restrict file system access to the mail queue.
    497      * XXX Chroot cannot imply unprivileged service (for example, the pickup
    498      * service runs chrooted but needs privileges to open files as the user).
    499      */
    500     chroot = get_bool_ent(&bufp, "chroot", compat_level
    501 		     < compat_level_from_string(COMPAT_LEVEL_1, msg_panic) ?
    502 			  "y" : "n");
    503 
    504     /*
    505      * Wakeup timer. XXX should we require that var_proc_limit == 1? Right
    506      * now, the only services that have a wakeup timer also happen to be the
    507      * services that have at most one running instance: local pickup and
    508      * local delivery.
    509      */
    510     serv->wakeup_time = get_int_ent(&bufp, "wakeup_time", "0", 0);
    511 
    512     /*
    513      * Find out if the wakeup time is conditional, i.e., wakeup triggers
    514      * should not be sent until the service has actually been used.
    515      */
    516     if (serv->wakeup_time > 0 && bufp[*bufp ? -2 : -1] == '?')
    517 	serv->flags |= MASTER_FLAG_CONDWAKE;
    518 
    519     /*
    520      * Concurrency limit. Zero means no limit.
    521      */
    522     vstring_sprintf(junk, "%d", var_proc_limit);
    523     serv->max_proc = get_int_ent(&bufp, "max_proc", vstring_str(junk), 0);
    524 
    525     /*
    526      * Path to command,
    527      */
    528     command = get_str_ent(&bufp, "command", (char *) 0);
    529     serv->path = concatenate(var_daemon_dir, "/", command, (char *) 0);
    530 
    531     /*
    532      * Idle and total process count.
    533      */
    534     serv->avail_proc = 0;
    535     serv->total_proc = 0;
    536 
    537     /*
    538      * Backoff time in case a service is broken.
    539      */
    540     serv->throttle_delay = var_throttle_time;
    541 
    542     /*
    543      * Shared channel for child status updates.
    544      */
    545     serv->status_fd[0] = serv->status_fd[1] = -1;
    546 
    547     /*
    548      * Child process structures.
    549      */
    550     serv->children = 0;
    551 
    552     /*
    553      * Command-line vector. Add "-n service_name" when the process name
    554      * basename differs from the service name. Always add the transport.
    555      */
    556     serv->args = argv_alloc(0);
    557     argv_add(serv->args, command, (char *) 0);
    558     if (serv->max_proc == 1)
    559 	argv_add(serv->args, "-l", (char *) 0);
    560     if (serv->max_proc == 0)
    561 	argv_add(serv->args, "-z", (char *) 0);
    562     if (strcmp(basename(command), name) != 0)
    563 	argv_add(serv->args, "-n", name, (char *) 0);
    564     argv_add(serv->args, "-t", transport, (char *) 0);
    565     if (master_detach == 0)
    566 	argv_add(serv->args, "-d", (char *) 0);
    567     if (msg_verbose)
    568 	argv_add(serv->args, "-v", (char *) 0);
    569     if (unprivileged)
    570 	argv_add(serv->args, "-u", (char *) 0);
    571     if (chroot)
    572 	argv_add(serv->args, "-c", (char *) 0);
    573     if ((serv->flags & MASTER_FLAG_LOCAL_ONLY) == 0 && serv->max_proc > 1) {
    574 	argv_add(serv->args, "-o", "stress=" CONFIG_BOOL_YES, (char *) 0);
    575 	serv->stress_param_val =
    576 	    serv->args->argv[serv->args->argc - 1] + sizeof("stress=") - 1;
    577 	serv->stress_param_val[0] = 0;
    578     } else
    579 	serv->stress_param_val = 0;
    580     serv->stress_expire_time = 0;
    581     if (serv->listen_fd_count > 1)
    582 	argv_add(serv->args, "-s",
    583 	    vstring_str(vstring_sprintf(junk, "%d", serv->listen_fd_count)),
    584 		 (char *) 0);
    585     while ((cp = mystrtokq(&bufp, master_blanks, CHARS_BRACE)) != 0) {
    586 	if (*cp == CHARS_BRACE[0]
    587 	    && (err = extpar(&cp, CHARS_BRACE, EXTPAR_FLAG_STRIP)) != 0)
    588 	    fatal_with_context("%s", err);
    589 	argv_add(serv->args, cp, (char *) 0);
    590     }
    591     argv_terminate(serv->args);
    592 
    593     /*
    594      * Cleanup.
    595      */
    596     vstring_free(buf);
    597     vstring_free(junk);
    598     return (serv);
    599 }
    600 
    601 /* print_master_ent - show service entry contents */
    602 
    603 void    print_master_ent(MASTER_SERV *serv)
    604 {
    605     char  **cpp;
    606 
    607     msg_info("====start service entry");
    608     msg_info("flags: %d", serv->flags);
    609     msg_info("name: %s", serv->name);
    610     msg_info("type: %s",
    611 	     serv->type == MASTER_SERV_TYPE_UNIX ? MASTER_XPORT_NAME_UNIX :
    612 	     serv->type == MASTER_SERV_TYPE_FIFO ? MASTER_XPORT_NAME_FIFO :
    613 	     serv->type == MASTER_SERV_TYPE_INET ? MASTER_XPORT_NAME_INET :
    614 #ifdef MASTER_SERV_TYPE_PASS
    615 	     serv->type == MASTER_SERV_TYPE_PASS ? MASTER_XPORT_NAME_PASS :
    616 #endif
    617 	     serv->type == MASTER_SERV_TYPE_UXDG ? MASTER_XPORT_NAME_UXDG :
    618 	     "unknown transport type");
    619     msg_info("listen_fd_count: %d", serv->listen_fd_count);
    620     msg_info("wakeup: %d", serv->wakeup_time);
    621     msg_info("max_proc: %d", serv->max_proc);
    622     msg_info("path: %s", serv->path);
    623     for (cpp = serv->args->argv; *cpp; cpp++)
    624 	msg_info("arg[%d]: %s", (int) (cpp - serv->args->argv), *cpp);
    625     msg_info("avail_proc: %d", serv->avail_proc);
    626     msg_info("total_proc: %d", serv->total_proc);
    627     msg_info("throttle_delay: %d", serv->throttle_delay);
    628     msg_info("status_fd %d %d", serv->status_fd[0], serv->status_fd[1]);
    629     msg_info("children: 0x%lx", (long) serv->children);
    630     msg_info("next: 0x%lx", (long) serv->next);
    631     msg_info("====end service entry");
    632 }
    633 
    634 /* free_master_ent - destroy process entry */
    635 
    636 void    free_master_ent(MASTER_SERV *serv)
    637 {
    638 
    639     /*
    640      * Undo what get_master_ent() created.
    641      */
    642     if (serv->flags & MASTER_FLAG_INETHOST) {
    643 	inet_addr_list_free(MASTER_INET_ADDRLIST(serv));
    644 	myfree((void *) MASTER_INET_ADDRLIST(serv));
    645     }
    646     if (serv->type == MASTER_SERV_TYPE_INET)
    647 	myfree(MASTER_INET_PORT(serv));
    648     myfree(serv->ext_name);
    649     myfree(serv->name);
    650     myfree(serv->path);
    651     argv_free(serv->args);
    652     myfree((void *) serv->listen_fd);
    653     myfree((void *) serv);
    654 }
    655