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