1 /* 2 * Copyright (c) 1998-2002 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 #include <sendmail.h> 12 13 SM_RCSID("@(#)$Id: control.c,v 8.118 2002/03/19 00:23:27 gshapiro Exp $") 14 15 /* values for cmd_code */ 16 #define CMDERROR 0 /* bad command */ 17 #define CMDRESTART 1 /* restart daemon */ 18 #define CMDSHUTDOWN 2 /* end daemon */ 19 #define CMDHELP 3 /* help */ 20 #define CMDSTATUS 4 /* daemon status */ 21 #define CMDMEMDUMP 5 /* dump memory, to find memory leaks */ 22 #if _FFR_CONTROL_MSTAT 23 # define CMDMSTAT 6 /* daemon status, more info, tagged data */ 24 #endif /* _FFR_CONTROL_MSTAT */ 25 26 struct cmd 27 { 28 char *cmd_name; /* command name */ 29 int cmd_code; /* internal code, see below */ 30 }; 31 32 static struct cmd CmdTab[] = 33 { 34 { "help", CMDHELP }, 35 { "restart", CMDRESTART }, 36 { "shutdown", CMDSHUTDOWN }, 37 { "status", CMDSTATUS }, 38 { "memdump", CMDMEMDUMP }, 39 #if _FFR_CONTROL_MSTAT 40 { "mstat", CMDMSTAT }, 41 #endif /* _FFR_CONTROL_MSTAT */ 42 { NULL, CMDERROR } 43 }; 44 45 46 47 int ControlSocket = -1; 48 49 /* 50 ** OPENCONTROLSOCKET -- create/open the daemon control named socket 51 ** 52 ** Creates and opens a named socket for external control over 53 ** the sendmail daemon. 54 ** 55 ** Parameters: 56 ** none. 57 ** 58 ** Returns: 59 ** 0 if successful, -1 otherwise 60 */ 61 62 int 63 opencontrolsocket() 64 { 65 # if NETUNIX 66 int save_errno; 67 int rval; 68 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 69 struct sockaddr_un controladdr; 70 71 if (ControlSocketName == NULL || *ControlSocketName == '\0') 72 return 0; 73 74 if (strlen(ControlSocketName) >= sizeof controladdr.sun_path) 75 { 76 errno = ENAMETOOLONG; 77 return -1; 78 } 79 80 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName, 81 sff, S_IRUSR|S_IWUSR, NULL); 82 83 /* if not safe, don't create */ 84 if (rval != 0) 85 { 86 errno = rval; 87 return -1; 88 } 89 90 ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0); 91 if (ControlSocket < 0) 92 return -1; 93 94 (void) unlink(ControlSocketName); 95 memset(&controladdr, '\0', sizeof controladdr); 96 controladdr.sun_family = AF_UNIX; 97 (void) sm_strlcpy(controladdr.sun_path, ControlSocketName, 98 sizeof controladdr.sun_path); 99 100 if (bind(ControlSocket, (struct sockaddr *) &controladdr, 101 sizeof controladdr) < 0) 102 { 103 save_errno = errno; 104 clrcontrol(); 105 errno = save_errno; 106 return -1; 107 } 108 109 if (geteuid() == 0) 110 { 111 uid_t u = 0; 112 113 if (RunAsUid != 0) 114 u = RunAsUid; 115 else if (TrustedUid != 0) 116 u = TrustedUid; 117 118 if (u != 0 && 119 chown(ControlSocketName, u, -1) < 0) 120 { 121 save_errno = errno; 122 sm_syslog(LOG_ALERT, NOQID, 123 "ownership change on %s to uid %d failed: %s", 124 ControlSocketName, (int) u, 125 sm_errstring(save_errno)); 126 message("050 ownership change on %s to uid %d failed: %s", 127 ControlSocketName, (int) u, 128 sm_errstring(save_errno)); 129 closecontrolsocket(true); 130 errno = save_errno; 131 return -1; 132 } 133 } 134 135 if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0) 136 { 137 save_errno = errno; 138 closecontrolsocket(true); 139 errno = save_errno; 140 return -1; 141 } 142 143 if (listen(ControlSocket, 8) < 0) 144 { 145 save_errno = errno; 146 closecontrolsocket(true); 147 errno = save_errno; 148 return -1; 149 } 150 # endif /* NETUNIX */ 151 return 0; 152 } 153 /* 154 ** CLOSECONTROLSOCKET -- close the daemon control named socket 155 ** 156 ** Close a named socket. 157 ** 158 ** Parameters: 159 ** fullclose -- if set, close the socket and remove it; 160 ** otherwise, just remove it 161 ** 162 ** Returns: 163 ** none. 164 */ 165 166 void 167 closecontrolsocket(fullclose) 168 bool fullclose; 169 { 170 # if NETUNIX 171 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 172 173 if (ControlSocket >= 0) 174 { 175 int rval; 176 177 if (fullclose) 178 { 179 (void) close(ControlSocket); 180 ControlSocket = -1; 181 } 182 183 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, 184 RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL); 185 186 /* if not safe, don't unlink */ 187 if (rval != 0) 188 return; 189 190 if (unlink(ControlSocketName) < 0) 191 { 192 sm_syslog(LOG_WARNING, NOQID, 193 "Could not remove control socket: %s", 194 sm_errstring(errno)); 195 return; 196 } 197 } 198 # endif /* NETUNIX */ 199 return; 200 } 201 /* 202 ** CLRCONTROL -- reset the control connection 203 ** 204 ** Parameters: 205 ** none. 206 ** 207 ** Returns: 208 ** none. 209 ** 210 ** Side Effects: 211 ** releases any resources used by the control interface. 212 */ 213 214 void 215 clrcontrol() 216 { 217 # if NETUNIX 218 if (ControlSocket >= 0) 219 (void) close(ControlSocket); 220 ControlSocket = -1; 221 # endif /* NETUNIX */ 222 } 223 /* 224 ** CONTROL_COMMAND -- read and process command from named socket 225 ** 226 ** Read and process the command from the opened socket. 227 ** Exits when done since it is running in a forked child. 228 ** 229 ** Parameters: 230 ** sock -- the opened socket from getrequests() 231 ** e -- the current envelope 232 ** 233 ** Returns: 234 ** none. 235 */ 236 237 static jmp_buf CtxControlTimeout; 238 239 /* ARGSUSED0 */ 240 static void 241 controltimeout(timeout) 242 time_t timeout; 243 { 244 /* 245 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 246 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 247 ** DOING. 248 */ 249 250 errno = ETIMEDOUT; 251 longjmp(CtxControlTimeout, 1); 252 } 253 254 void 255 control_command(sock, e) 256 int sock; 257 ENVELOPE *e; 258 { 259 volatile int exitstat = EX_OK; 260 SM_FILE_T *s = NULL; 261 SM_EVENT *ev = NULL; 262 SM_FILE_T *traffic; 263 SM_FILE_T *oldout; 264 char *cmd; 265 char *p; 266 struct cmd *c; 267 char cmdbuf[MAXLINE]; 268 char inp[MAXLINE]; 269 270 sm_setproctitle(false, e, "control cmd read"); 271 272 if (TimeOuts.to_control > 0) 273 { 274 /* handle possible input timeout */ 275 if (setjmp(CtxControlTimeout) != 0) 276 { 277 if (LogLevel > 2) 278 sm_syslog(LOG_NOTICE, e->e_id, 279 "timeout waiting for input during control command"); 280 exit(EX_IOERR); 281 } 282 ev = sm_setevent(TimeOuts.to_control, controltimeout, 283 TimeOuts.to_control); 284 } 285 286 s = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT, (void *) &sock, 287 SM_IO_RDWR, NULL); 288 if (s == NULL) 289 { 290 int save_errno = errno; 291 292 (void) close(sock); 293 errno = save_errno; 294 exit(EX_IOERR); 295 } 296 (void) sm_io_setvbuf(s, SM_TIME_DEFAULT, NULL, 297 SM_IO_NBF, SM_IO_BUFSIZ); 298 299 if (sm_io_fgets(s, SM_TIME_DEFAULT, inp, sizeof inp) == NULL) 300 { 301 (void) sm_io_close(s, SM_TIME_DEFAULT); 302 exit(EX_IOERR); 303 } 304 (void) sm_io_flush(s, SM_TIME_DEFAULT); 305 306 /* clean up end of line */ 307 fixcrlf(inp, true); 308 309 sm_setproctitle(false, e, "control: %s", inp); 310 311 /* break off command */ 312 for (p = inp; isascii(*p) && isspace(*p); p++) 313 continue; 314 cmd = cmdbuf; 315 while (*p != '\0' && 316 !(isascii(*p) && isspace(*p)) && 317 cmd < &cmdbuf[sizeof cmdbuf - 2]) 318 *cmd++ = *p++; 319 *cmd = '\0'; 320 321 /* throw away leading whitespace */ 322 while (isascii(*p) && isspace(*p)) 323 p++; 324 325 /* decode command */ 326 for (c = CmdTab; c->cmd_name != NULL; c++) 327 { 328 if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) 329 break; 330 } 331 332 switch (c->cmd_code) 333 { 334 case CMDHELP: /* get help */ 335 traffic = TrafficLogFile; 336 TrafficLogFile = NULL; 337 oldout = OutChannel; 338 OutChannel = s; 339 help("control", e); 340 TrafficLogFile = traffic; 341 OutChannel = oldout; 342 break; 343 344 case CMDRESTART: /* restart the daemon */ 345 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n"); 346 exitstat = EX_RESTART; 347 break; 348 349 case CMDSHUTDOWN: /* kill the daemon */ 350 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, "OK\r\n"); 351 exitstat = EX_SHUTDOWN; 352 break; 353 354 case CMDSTATUS: /* daemon status */ 355 proc_list_probe(); 356 { 357 int qgrp; 358 long bsize; 359 long free; 360 361 /* XXX need to deal with different partitions */ 362 qgrp = e->e_qgrp; 363 if (!ISVALIDQGRP(qgrp)) 364 qgrp = 0; 365 free = freediskspace(Queue[qgrp]->qg_qdir, &bsize); 366 367 /* 368 ** Prevent overflow and don't lose 369 ** precision (if bsize == 512) 370 */ 371 372 if (free > 0) 373 free = (long)((double) free * 374 ((double) bsize / 1024)); 375 376 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 377 "%d/%d/%ld/%d\r\n", 378 CurChildren, MaxChildren, 379 free, getla()); 380 } 381 proc_list_display(s, ""); 382 break; 383 384 # if _FFR_CONTROL_MSTAT 385 case CMDMSTAT: /* daemon status, extended, tagged format */ 386 proc_list_probe(); 387 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 388 "C:%d\r\nM:%d\r\nL:%d\r\n", 389 CurChildren, MaxChildren, 390 getla()); 391 printnqe(s, "Q:"); 392 disk_status(s, "D:"); 393 proc_list_display(s, "P:"); 394 break; 395 # endif /* _FFR_CONTROL_MSTAT */ 396 397 case CMDMEMDUMP: /* daemon memory dump, to find memory leaks */ 398 # if SM_HEAP_CHECK 399 /* dump the heap, if we are checking for memory leaks */ 400 if (sm_debug_active(&SmHeapCheck, 2)) 401 { 402 sm_heap_report(s, sm_debug_level(&SmHeapCheck) - 1); 403 } 404 else 405 { 406 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 407 "Memory dump unavailable.\r\n"); 408 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 409 "To fix, run sendmail with -dsm_check_heap.4\r\n"); 410 } 411 # else /* SM_HEAP_CHECK */ 412 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 413 "Memory dump unavailable.\r\n"); 414 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 415 "To fix, rebuild with -DSM_HEAP_CHECK\r\n"); 416 # endif /* SM_HEAP_CHECK */ 417 break; 418 419 case CMDERROR: /* unknown command */ 420 (void) sm_io_fprintf(s, SM_TIME_DEFAULT, 421 "Bad command (%s)\r\n", cmdbuf); 422 break; 423 } 424 (void) sm_io_close(s, SM_TIME_DEFAULT); 425 if (ev != NULL) 426 sm_clrevent(ev); 427 exit(exitstat); 428 } 429