1 /* 2 * Copyright (c) 1998-2004, 2006 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.128 2006/08/15 23:24:56 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)) == NULL) 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; isascii(*p) && isspace(*p); p++) 318 continue; 319 cmd = cmdbuf; 320 while (*p != '\0' && 321 !(isascii(*p) && isspace(*p)) && 322 cmd < &cmdbuf[sizeof(cmdbuf) - 2]) 323 *cmd++ = *p++; 324 *cmd = '\0'; 325 326 /* throw away leading whitespace */ 327 while (isascii(*p) && isspace(*p)) 328 p++; 329 330 /* decode command */ 331 for (c = CmdTab; c->cmd_name != NULL; c++) 332 { 333 if (sm_strcasecmp(c->cmd_name, cmdbuf) == 0) 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