xref: /netbsd-src/external/ibm-public/postfix/dist/src/master/master_status.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1 /*	$NetBSD: master_status.c,v 1.2 2017/02/14 01:16:45 christos Exp $	*/
2 
3 /*++
4 /* NAME
5 /*	master_status 3
6 /* SUMMARY
7 /*	Postfix master - process child status reports
8 /* SYNOPSIS
9 /*	#include "master.h"
10 /*
11 /*	void	master_status_init(serv)
12 /*	MASTER_SERV *serv;
13 /*
14 /*	void	master_status_cleanup(serv)
15 /*	MASTER_SERV *serv;
16 /* DESCRIPTION
17 /*	This module reads and processes status reports from child processes.
18 /*
19 /*	master_status_init() enables the processing of child status updates
20 /*	for the specified service. Child process status updates (process
21 /*	available, process taken) are passed on to the master_avail_XXX()
22 /*	routines.
23 /*
24 /*	master_status_cleanup() disables child status update processing
25 /*	for the specified service.
26 /* DIAGNOSTICS
27 /*	Panic: internal inconsistency. Warnings: a child process sends
28 /*	incomplete or incorrect information.
29 /* BUGS
30 /* SEE ALSO
31 /*	master_avail(3)
32 /* LICENSE
33 /* .ad
34 /* .fi
35 /*	The Secure Mailer license must be distributed with this software.
36 /* AUTHOR(S)
37 /*	Wietse Venema
38 /*	IBM T.J. Watson Research
39 /*	P.O. Box 704
40 /*	Yorktown Heights, NY 10598, USA
41 /*--*/
42 
43 /* System libraries. */
44 
45 #include <sys_defs.h>
46 #include <unistd.h>
47 
48 /* Utility library. */
49 
50 #include <msg.h>
51 #include <events.h>
52 #include <binhash.h>
53 #include <iostuff.h>
54 
55 /* Application-specific. */
56 
57 #include "master_proto.h"
58 #include "master.h"
59 
60 /* master_status_event - status read event handler */
61 
master_status_event(int event,void * context)62 static void master_status_event(int event, void *context)
63 {
64     const char *myname = "master_status_event";
65     MASTER_SERV *serv = (MASTER_SERV *) context;
66     MASTER_STATUS stat;
67     MASTER_PROC *proc;
68     MASTER_PID pid;
69     int     n;
70 
71     if (event == 0)				/* XXX Can this happen?  */
72 	return;
73 
74     /*
75      * We always keep the child end of the status pipe open, so an EOF read
76      * condition means that we're seriously confused. We use non-blocking
77      * reads so that we don't get stuck when someone sends a partial message.
78      * Messages are short, so a partial read means someone wrote less than a
79      * whole status message. Hopefully the next read will be in sync again...
80      * We use a global child process status table because when a child dies
81      * only its pid is known - we do not know what service it came from.
82      */
83     switch (n = read(serv->status_fd[0], (void *) &stat, sizeof(stat))) {
84 
85     case -1:
86 	msg_warn("%s: read: %m", myname);
87 	return;
88 
89     case 0:
90 	msg_panic("%s: read EOF status", myname);
91 	/* NOTREACHED */
92 
93     default:
94 	msg_warn("service %s(%s): child (pid %d) sent partial status update (%d bytes)",
95 		 serv->ext_name, serv->name, stat.pid, n);
96 	return;
97 
98     case sizeof(stat):
99 	pid = stat.pid;
100 	if (msg_verbose)
101 	    msg_info("%s: pid %d gen %u avail %d",
102 		     myname, stat.pid, stat.gen, stat.avail);
103     }
104 
105     /*
106      * Sanity checks. Do not freak out when the child sends garbage because
107      * it is confused or for other reasons. However, be sure to freak out
108      * when our own data structures are inconsistent. A process not found
109      * condition can happen when we reap a process before receiving its
110      * status update, so this is not an error.
111      */
112     if ((proc = (MASTER_PROC *) binhash_find(master_child_table,
113 					(void *) &pid, sizeof(pid))) == 0) {
114 	if (msg_verbose)
115 	    msg_info("%s: process id not found: %d", myname, stat.pid);
116 	return;
117     }
118     if (proc->gen != stat.gen) {
119 	msg_info("ignoring status update from child pid %d generation %u",
120 		 pid, stat.gen);
121 	return;
122     }
123     if (proc->serv != serv)
124 	msg_panic("%s: pointer corruption: %p != %p",
125 		  myname, (void *) proc->serv, (void *) serv);
126 
127     /*
128      * Update our idea of the child process status. Allow redundant status
129      * updates, because different types of events may be processed out of
130      * order. Otherwise, warn about weird status updates but do not take
131      * action. It's all gossip after all.
132      */
133     if (proc->avail == stat.avail)
134 	return;
135     switch (stat.avail) {
136     case MASTER_STAT_AVAIL:
137 	proc->use_count++;
138 	master_avail_more(serv, proc);
139 	break;
140     case MASTER_STAT_TAKEN:
141 	master_avail_less(serv, proc);
142 	break;
143     default:
144 	msg_warn("%s: ignoring unknown status: %d allegedly from pid: %d",
145 		 myname, stat.pid, stat.avail);
146 	break;
147     }
148 }
149 
150 /* master_status_init - start status event processing for this service */
151 
master_status_init(MASTER_SERV * serv)152 void    master_status_init(MASTER_SERV *serv)
153 {
154     const char *myname = "master_status_init";
155 
156     /*
157      * Sanity checks.
158      */
159     if (serv->status_fd[0] >= 0 || serv->status_fd[1] >= 0)
160 	msg_panic("%s: status events already enabled", myname);
161     if (msg_verbose)
162 	msg_info("%s: %s", myname, serv->name);
163 
164     /*
165      * Make the read end of this service's status pipe non-blocking so that
166      * we can detect partial writes on the child side. We use a duplex pipe
167      * so that the child side becomes readable when the master goes away.
168      */
169     if (duplex_pipe(serv->status_fd) < 0)
170 	msg_fatal("pipe: %m");
171     non_blocking(serv->status_fd[0], BLOCKING);
172     close_on_exec(serv->status_fd[0], CLOSE_ON_EXEC);
173     close_on_exec(serv->status_fd[1], CLOSE_ON_EXEC);
174     event_enable_read(serv->status_fd[0], master_status_event, (void *) serv);
175 }
176 
177 /* master_status_cleanup - stop status event processing for this service */
178 
master_status_cleanup(MASTER_SERV * serv)179 void    master_status_cleanup(MASTER_SERV *serv)
180 {
181     const char *myname = "master_status_cleanup";
182 
183     /*
184      * Sanity checks.
185      */
186     if (serv->status_fd[0] < 0 || serv->status_fd[1] < 0)
187 	msg_panic("%s: status events not enabled", myname);
188     if (msg_verbose)
189 	msg_info("%s: %s", myname, serv->name);
190 
191     /*
192      * Dispose of this service's status pipe after disabling read events.
193      */
194     event_disable_readwrite(serv->status_fd[0]);
195     if (close(serv->status_fd[0]) != 0)
196 	msg_warn("%s: close status descriptor (read side): %m", myname);
197     if (close(serv->status_fd[1]) != 0)
198 	msg_warn("%s: close status descriptor (write side): %m", myname);
199     serv->status_fd[0] = serv->status_fd[1] = -1;
200 }
201