Deleted Added
full compact
control.c (73188) control.c (77349)
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