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