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 13SM_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 26struct cmd 27{ 28 char *cmd_name; /* command name */ 29 int cmd_code; /* internal code, see below */ 30}; 31 32static 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 45static void controltimeout __P((int)); 46int 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 61int 62opencontrolsocket() 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 171void 172closecontrolsocket(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 219void 220clrcontrol() 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 242static jmp_buf CtxControlTimeout; 243 244/* ARGSUSED0 */ 245static void 246controltimeout(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 259void 260control_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