Deleted Added
full compact
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