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 
fset_master_ent(char * path)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 
set_master_ent()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 
end_master_ent()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 
master_conf_context(void)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 
fatal_with_context(char * format,...)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 
fatal_invalid_field(char * name,char * value)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 
get_str_ent(char ** bufp,char * name,char * def_val)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 
get_bool_ent(char ** bufp,char * name,char * def_val)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 
get_int_ent(char ** bufp,char * name,char * def_val,int min_val)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 
get_master_ent()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 
print_master_ent(MASTER_SERV * serv)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 
free_master_ent(MASTER_SERV * serv)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