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