1/* 2 * Copyright (c) 1998-2001 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#ifndef lint
|
12static char id[] = "@(#)$Id: control.c,v 8.44.14.15 2001/01/22 19:00:22 gshapiro Exp $";
|
12static char id[] = "@(#)$Id: control.c,v 8.44.14.20 2001/05/03 17:24:03 gshapiro Exp $"; |
13#endif /* ! lint */ 14 15#include <sendmail.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
|
24struct cmd 25{ 26 char *cmd_name; /* command name */ 27 int cmd_code; /* internal code, see below */ 28}; 29 30static struct cmd CmdTab[] = 31{ 32 { "help", CMDHELP }, 33 { "restart", CMDRESTART }, 34 { "shutdown", CMDSHUTDOWN }, 35 { "status", CMDSTATUS }, 36 { NULL, CMDERROR } 37}; 38 39 |
40int ControlSocket = -1; 41 42/* 43** OPENCONTROLSOCKET -- create/open the daemon control named socket 44** 45** Creates and opens a named socket for external control over 46** the sendmail daemon. 47** 48** Parameters: 49** none. 50** 51** Returns: 52** 0 if successful, -1 otherwise 53*/ 54 55int 56opencontrolsocket() 57{ 58#if NETUNIX 59 int save_errno; 60 int rval; 61 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 62 struct sockaddr_un controladdr; 63 64 if (ControlSocketName == NULL) 65 return 0; 66 67 if (strlen(ControlSocketName) >= sizeof controladdr.sun_path) 68 { 69 errno = ENAMETOOLONG; 70 return -1; 71 } 72 73 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName, 74 sff, S_IRUSR|S_IWUSR, NULL); 75 76 /* if not safe, don't create */ 77 if (rval != 0) 78 { 79 errno = rval; 80 return -1; 81 } 82 83 ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0); 84 if (ControlSocket < 0) 85 return -1; 86 87 (void) unlink(ControlSocketName); 88 memset(&controladdr, '\0', sizeof controladdr); 89 controladdr.sun_family = AF_UNIX; 90 (void) strlcpy(controladdr.sun_path, ControlSocketName, 91 sizeof controladdr.sun_path); 92 93 if (bind(ControlSocket, (struct sockaddr *) &controladdr, 94 sizeof controladdr) < 0) 95 { 96 save_errno = errno; 97 clrcontrol(); 98 errno = save_errno; 99 return -1; 100 } 101 102 if (geteuid() == 0) 103 { 104 uid_t u = 0; 105 106 if (RunAsUid != 0) 107 u = RunAsUid; 108 else if (TrustedUid != 0) 109 u = TrustedUid; 110 111 if (u != 0 && 112 chown(ControlSocketName, u, -1) < 0) 113 { 114 save_errno = errno; 115 sm_syslog(LOG_ALERT, NOQID, 116 "ownership change on %s to uid %d failed: %s", 117 ControlSocketName, (int) u, 118 errstring(save_errno)); 119 message("050 ownership change on %s to uid %d failed: %s", 120 ControlSocketName, (int) u, 121 errstring(save_errno)); 122 closecontrolsocket(TRUE); 123 errno = save_errno; 124 return -1; 125 } 126 } 127 128 if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0) 129 { 130 save_errno = errno; 131 closecontrolsocket(TRUE); 132 errno = save_errno; 133 return -1; 134 } 135 136 if (listen(ControlSocket, 8) < 0) 137 { 138 save_errno = errno; 139 closecontrolsocket(TRUE); 140 errno = save_errno; 141 return -1; 142 } 143#endif /* NETUNIX */ 144 return 0; 145} 146/* 147** CLOSECONTROLSOCKET -- close the daemon control named socket 148** 149** Close a named socket. 150** 151** Parameters: 152** fullclose -- if set, close the socket and remove it; 153** otherwise, just remove it 154** 155** Returns: 156** none. 157*/ 158 159void 160closecontrolsocket(fullclose) 161 bool fullclose; 162{ 163#if NETUNIX 164 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; 165 166 if (ControlSocket >= 0) 167 { 168 int rval; 169 170 if (fullclose) 171 { 172 (void) close(ControlSocket); 173 ControlSocket = -1; 174 } 175 176 rval = safefile(ControlSocketName, RunAsUid, RunAsGid, 177 RunAsUserName, sff, S_IRUSR|S_IWUSR, NULL); 178 179 /* if not safe, don't unlink */ 180 if (rval != 0) 181 return; 182 183 if (unlink(ControlSocketName) < 0) 184 { 185 sm_syslog(LOG_WARNING, NOQID, 186 "Could not remove control socket: %s", 187 errstring(errno)); 188 return; 189 } 190 } 191#endif /* NETUNIX */ 192 return; 193} 194/* 195** CLRCONTROL -- reset the control connection 196** 197** Parameters: 198** none. 199** 200** Returns: 201** none. 202** 203** Side Effects: 204** releases any resources used by the control interface. 205*/ 206 207void 208clrcontrol() 209{ 210#if NETUNIX 211 if (ControlSocket >= 0) 212 (void) close(ControlSocket); 213 ControlSocket = -1; 214#endif /* NETUNIX */ 215} 216 217#ifndef NOT_SENDMAIL 218 219/* 220** CONTROL_COMMAND -- read and process command from named socket 221** 222** Read and process the command from the opened socket. 223** Exits when done since it is running in a forked child. 224** 225** Parameters: 226** sock -- the opened socket from getrequests() 227** e -- the current envelope 228** 229** Returns: 230** none. 231*/ 232
|
211struct cmd
212{
213 char *cmd_name; /* command name */
214 int cmd_code; /* internal code, see below */
215};
216
217/* values for cmd_code */
218# define CMDERROR 0 /* bad command */
219# define CMDRESTART 1 /* restart daemon */
220# define CMDSHUTDOWN 2 /* end daemon */
221# define CMDHELP 3 /* help */
222# define CMDSTATUS 4 /* daemon status */
223
224static struct cmd CmdTab[] =
225{
226 { "help", CMDHELP },
227 { "restart", CMDRESTART },
228 { "shutdown", CMDSHUTDOWN },
229 { "status", CMDSTATUS },
230 { NULL, CMDERROR }
231};
232
|
233static jmp_buf CtxControlTimeout; 234 235static void 236controltimeout(timeout) 237 time_t timeout; 238{
|
239 /* 240 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 241 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 242 ** DOING. 243 */ 244 245 errno = ETIMEDOUT; |
246 longjmp(CtxControlTimeout, 1); 247} 248 249void 250control_command(sock, e) 251 int sock; 252 ENVELOPE *e; 253{ 254 volatile int exitstat = EX_OK; 255 FILE *s = NULL; 256 EVENT *ev = NULL; 257 FILE *traffic; 258 FILE *oldout; 259 char *cmd; 260 char *p; 261 struct cmd *c; 262 char cmdbuf[MAXLINE]; 263 char inp[MAXLINE]; 264 265 sm_setproctitle(FALSE, e, "control cmd read"); 266 267 if (TimeOuts.to_control > 0) 268 { 269 /* handle possible input timeout */ 270 if (setjmp(CtxControlTimeout) != 0) 271 { 272 if (LogLevel > 2) 273 sm_syslog(LOG_NOTICE, e->e_id, 274 "timeout waiting for input during control command"); 275 exit(EX_IOERR); 276 } 277 ev = setevent(TimeOuts.to_control, controltimeout, 278 TimeOuts.to_control); 279 } 280 281 s = fdopen(sock, "r+"); 282 if (s == NULL) 283 { 284 int save_errno = errno; 285 286 (void) close(sock); 287 errno = save_errno; 288 exit(EX_IOERR); 289 } 290 setbuf(s, NULL); 291 292 if (fgets(inp, sizeof inp, s) == NULL) 293 { 294 (void) fclose(s); 295 exit(EX_IOERR); 296 } 297 (void) fflush(s); 298 299 /* clean up end of line */ 300 fixcrlf(inp, TRUE); 301 302 sm_setproctitle(FALSE, e, "control: %s", inp); 303 304 /* break off command */ 305 for (p = inp; isascii(*p) && isspace(*p); p++) 306 continue; 307 cmd = cmdbuf; 308 while (*p != '\0' && 309 !(isascii(*p) && isspace(*p)) && 310 cmd < &cmdbuf[sizeof cmdbuf - 2]) 311 *cmd++ = *p++; 312 *cmd = '\0'; 313 314 /* throw away leading whitespace */ 315 while (isascii(*p) && isspace(*p)) 316 p++; 317 318 /* decode command */ 319 for (c = CmdTab; c->cmd_name != NULL; c++) 320 { 321 if (strcasecmp(c->cmd_name, cmdbuf) == 0) 322 break; 323 } 324 325 switch (c->cmd_code) 326 { 327 case CMDHELP: /* get help */ 328 traffic = TrafficLogFile; 329 TrafficLogFile = NULL; 330 oldout = OutChannel; 331 OutChannel = s; 332 help("control", e); 333 TrafficLogFile = traffic; 334 OutChannel = oldout; 335 break; 336 337 case CMDRESTART: /* restart the daemon */ 338 fprintf(s, "OK\r\n"); 339 exitstat = EX_RESTART; 340 break; 341 342 case CMDSHUTDOWN: /* kill the daemon */ 343 fprintf(s, "OK\r\n"); 344 exitstat = EX_SHUTDOWN; 345 break; 346 347 case CMDSTATUS: /* daemon status */ 348 proc_list_probe(); 349 { 350 long bsize; 351 long free; 352 353 free = freediskspace(QueueDir, &bsize); 354 355 /* 356 ** Prevent overflow and don't lose 357 ** precision (if bsize == 512) 358 */ 359 360 free = (long)((double)free * ((double)bsize / 1024)); 361 362 fprintf(s, "%d/%d/%ld/%d\r\n", 363 CurChildren, MaxChildren, 364 free, sm_getla(NULL)); 365 } 366 proc_list_display(s); 367 break; 368 369 case CMDERROR: /* unknown command */ 370 fprintf(s, "Bad command (%s)\r\n", cmdbuf); 371 break; 372 } 373 (void) fclose(s); 374 if (ev != NULL) 375 clrevent(ev); 376 exit(exitstat); 377} 378#endif /* ! NOT_SENDMAIL */ 379
|