1 /* $NetBSD: master_status.c,v 1.1.1.1 2009/06/23 10:08:49 tron 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 62 static void master_status_event(int event, char *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], (char *) &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 (char *) &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 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, (char *) serv); 175 } 176 177 /* master_status_cleanup - stop status event processing for this service */ 178 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