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