1 /* 2 * Copyright (c) 1998-2001 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * 5 * By using this file, you agree to the terms and conditions set 6 * forth in the LICENSE file which can be found at the top level of 7 * the sendmail distribution. 8 * 9 */ 10 11 #ifndef lint 12 static char id[] = "@(#)$Id: control.c,v 8.44.14.20 2001/05/03 17:24:03 gshapiro Exp $"; 13 #endif /* ! lint */ 14 15 #include <sendmail.h> 16 17 /* values for cmd_code */ 18 # define CMDERROR 0 /* bad command */ 19 # define CMDRESTART 1 /* restart daemon */ 20 # define CMDSHUTDOWN 2 /* end daemon */ 21 # define CMDHELP 3 /* help */ 22 # define CMDSTATUS 4 /* daemon status */ 23 24 struct cmd 25 { 26 char *cmd_name; /* command name */ 27 int cmd_code; /* internal code, see below */ 28 }; 29 30 static struct cmd CmdTab[] = 31 { 32 { "help", CMDHELP }, 33 { "restart", CMDRESTART }, 34 { "shutdown", CMDSHUTDOWN }, 35 { "status", CMDSTATUS }, 36 { NULL, CMDERROR } 37 }; 38 39 40 int ControlSocket = -1; 41 42 /* 43 ** OPENCONTROLSOCKET -- create/open the daemon control named socket 44 ** 45 ** Creates and opens a named socket for external control over 46 ** the sendmail daemon. 47 ** 48 ** Parameters: 49 ** none. 50 ** 51 ** Returns: 52 ** 0 if successful, -1 otherwise 53 */ 54 55 int 56 opencontrolsocket() 57 { 58 #if NETUNIX 59 int save_errno; 60 int rval; 61 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 62 struct sockaddr_un controladdr; 63 64 if (ControlSocketName == NULL) 65 return 0; 66 67 if (strlen(ControlSocketName) >= sizeof controladdr.sun_path) 68 { 69 errno = ENAMETOOLONG; 70 return -1; 71 } 72 73 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName, 74 sff, S_IRUSR|S_IWUSR, NULL); 75 76 /* if not safe, don't create */ 77 if (rval != 0) 78 { 79 errno = rval; 80 return -1; 81 } 82 83 ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0); 84 if (ControlSocket < 0) 85 return -1; 86 87 (void) unlink(ControlSocketName); 88 memset(&controladdr, '\0', sizeof controladdr); 89 controladdr.sun_family = AF_UNIX; 90 (void) strlcpy(controladdr.sun_path, ControlSocketName, 91 sizeof controladdr.sun_path); 92 93 if (bind(ControlSocket, (struct sockaddr *) &controladdr, 94 sizeof controladdr) < 0) 95 { 96 save_errno = errno; 97 clrcontrol(); 98 errno = save_errno; 99 return -1; 100 } 101 102 if (geteuid() == 0) 103 { 104 uid_t u = 0; 105 106 if (RunAsUid != 0) 107 u = RunAsUid; 108 else if (TrustedUid != 0) 109 u = TrustedUid; 110 111 if (u != 0 && 112 chown(ControlSocketName, u, -1) < 0) 113 { 114 save_errno = errno; 115 sm_syslog(LOG_ALERT, NOQID, 116 "ownership change on %s to uid %d failed: %s", 117 ControlSocketName, (int) u, 118 errstring(save_errno)); 119 message("050 ownership change on %s to uid %d failed: %s", 120 ControlSocketName, (int) u, 121 errstring(save_errno)); 122 closecontrolsocket(TRUE); 123 errno = save_errno; 124 return -1; 125 } 126 } 127 128 if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0) 129 { 130 save_errno = errno; 131 closecontrolsocket(TRUE); 132 errno = save_errno; 133 return -1; 134 } 135 136 if (listen(ControlSocket, 8) < 0) 137 { 138 save_errno = errno; 139 closecontrolsocket(TRUE); 140 errno = save_errno; 141 return -1; 142 } 143 #endif /* NETUNIX */ 144 return 0; 145 } 146 /* 147 ** CLOSECONTROLSOCKET -- close the daemon control named socket 148 ** 149 ** Close a named socket. 150 ** 151 ** Parameters: 152 ** fullclose -- if set, close the socket and remove it; 153 ** otherwise, just remove it 154 ** 155 ** Returns: 156 ** none. 157 */ 158 159 void 160 closecontrolsocket(fullclose) 161 bool fullclose; 162 { 163 #if NETUNIX 164 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 165 166 if (ControlSocket >= 0) 167 { 168 int rval; 169 170 if (fullclose) 171 { 172 (void) close(ControlSocket); 173 ControlSocket = -1; 174 } 175 176 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, 177 RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL); 178 179 /* if not safe, don't unlink */ 180 if (rval != 0) 181 return; 182 183 if (unlink(ControlSocketName) < 0) 184 { 185 sm_syslog(LOG_WARNING, NOQID, 186 "Could not remove control socket: %s", 187 errstring(errno)); 188 return; 189 } 190 } 191 #endif /* NETUNIX */ 192 return; 193 } 194 /* 195 ** CLRCONTROL -- reset the control connection 196 ** 197 ** Parameters: 198 ** none. 199 ** 200 ** Returns: 201 ** none. 202 ** 203 ** Side Effects: 204 ** releases any resources used by the control interface. 205 */ 206 207 void 208 clrcontrol() 209 { 210 #if NETUNIX 211 if (ControlSocket >= 0) 212 (void) close(ControlSocket); 213 ControlSocket = -1; 214 #endif /* NETUNIX */ 215 } 216 217 #ifndef NOT_SENDMAIL 218 219 /* 220 ** CONTROL_COMMAND -- read and process command from named socket 221 ** 222 ** Read and process the command from the opened socket. 223 ** Exits when done since it is running in a forked child. 224 ** 225 ** Parameters: 226 ** sock -- the opened socket from getrequests() 227 ** e -- the current envelope 228 ** 229 ** Returns: 230 ** none. 231 */ 232 233 static jmp_buf CtxControlTimeout; 234 235 static void 236 controltimeout(timeout) 237 time_t timeout; 238 { 239 /* 240 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 241 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 242 ** DOING. 243 */ 244 245 errno = ETIMEDOUT; 246 longjmp(CtxControlTimeout, 1); 247 } 248 249 void 250 control_command(sock, e) 251 int sock; 252 ENVELOPE *e; 253 { 254 volatile int exitstat = EX_OK; 255 FILE *s = NULL; 256 EVENT *ev = NULL; 257 FILE *traffic; 258 FILE *oldout; 259 char *cmd; 260 char *p; 261 struct cmd *c; 262 char cmdbuf[MAXLINE]; 263 char inp[MAXLINE]; 264 265 sm_setproctitle(FALSE, e, "control cmd read"); 266 267 if (TimeOuts.to_control > 0) 268 { 269 /* handle possible input timeout */ 270 if (setjmp(CtxControlTimeout) != 0) 271 { 272 if (LogLevel > 2) 273 sm_syslog(LOG_NOTICE, e->e_id, 274 "timeout waiting for input during control command"); 275 exit(EX_IOERR); 276 } 277 ev = setevent(TimeOuts.to_control, controltimeout, 278 TimeOuts.to_control); 279 } 280 281 s = fdopen(sock, "r+"); 282 if (s == NULL) 283 { 284 int save_errno = errno; 285 286 (void) close(sock); 287 errno = save_errno; 288 exit(EX_IOERR); 289 } 290 setbuf(s, NULL); 291 292 if (fgets(inp, sizeof inp, s) == NULL) 293 { 294 (void) fclose(s); 295 exit(EX_IOERR); 296 } 297 (void) fflush(s); 298 299 /* clean up end of line */ 300 fixcrlf(inp, TRUE); 301 302 sm_setproctitle(FALSE, e, "control: %s", inp); 303 304 /* break off command */ 305 for (p = inp; isascii(*p) && isspace(*p); p++) 306 continue; 307 cmd = cmdbuf; 308 while (*p != '\0' && 309 !(isascii(*p) && isspace(*p)) && 310 cmd < &cmdbuf[sizeof cmdbuf - 2]) 311 *cmd++ = *p++; 312 *cmd = '\0'; 313 314 /* throw away leading whitespace */ 315 while (isascii(*p) && isspace(*p)) 316 p++; 317 318 /* decode command */ 319 for (c = CmdTab; c->cmd_name != NULL; c++) 320 { 321 if (strcasecmp(c->cmd_name, cmdbuf) == 0) 322 break; 323 } 324 325 switch (c->cmd_code) 326 { 327 case CMDHELP: /* get help */ 328 traffic = TrafficLogFile; 329 TrafficLogFile = NULL; 330 oldout = OutChannel; 331 OutChannel = s; 332 help("control", e); 333 TrafficLogFile = traffic; 334 OutChannel = oldout; 335 break; 336 337 case CMDRESTART: /* restart the daemon */ 338 fprintf(s, "OK\r\n"); 339 exitstat = EX_RESTART; 340 break; 341 342 case CMDSHUTDOWN: /* kill the daemon */ 343 fprintf(s, "OK\r\n"); 344 exitstat = EX_SHUTDOWN; 345 break; 346 347 case CMDSTATUS: /* daemon status */ 348 proc_list_probe(); 349 { 350 long bsize; 351 long free; 352 353 free = freediskspace(QueueDir, &bsize); 354 355 /* 356 ** Prevent overflow and don't lose 357 ** precision (if bsize == 512) 358 */ 359 360 free = (long)((double)free * ((double)bsize / 1024)); 361 362 fprintf(s, "%d/%d/%ld/%d\r\n", 363 CurChildren, MaxChildren, 364 free, sm_getla(NULL)); 365 } 366 proc_list_display(s); 367 break; 368 369 case CMDERROR: /* unknown command */ 370 fprintf(s, "Bad command (%s)\r\n", cmdbuf); 371 break; 372 } 373 (void) fclose(s); 374 if (ev != NULL) 375 clrevent(ev); 376 exit(exitstat); 377 } 378 #endif /* ! NOT_SENDMAIL */ 379 380