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