1 | /* |
---|
2 | * Copyright (c) 1998 Sendmail, Inc. All rights reserved. |
---|
3 | * |
---|
4 | * By using this file, you agree to the terms and conditions set |
---|
5 | * forth in the LICENSE file which can be found at the top level of |
---|
6 | * the sendmail distribution. |
---|
7 | * |
---|
8 | */ |
---|
9 | |
---|
10 | #ifndef lint |
---|
11 | static char sccsid[] = "@(#)control.c 8.18 (Berkeley) 1/17/1999"; |
---|
12 | #endif /* not lint */ |
---|
13 | |
---|
14 | #include "sendmail.h" |
---|
15 | |
---|
16 | int ControlSocket = -1; |
---|
17 | |
---|
18 | /* |
---|
19 | ** OPENCONTROLSOCKET -- create/open the daemon control named socket |
---|
20 | ** |
---|
21 | ** Creates and opens a named socket for external control over |
---|
22 | ** the sendmail daemon. |
---|
23 | ** |
---|
24 | ** Parameters: |
---|
25 | ** none. |
---|
26 | ** |
---|
27 | ** Returns: |
---|
28 | ** 0 if successful, -1 otherwise |
---|
29 | */ |
---|
30 | |
---|
31 | int |
---|
32 | opencontrolsocket() |
---|
33 | { |
---|
34 | #ifdef NETUNIX |
---|
35 | # if _FFR_CONTROL_SOCKET |
---|
36 | int rval; |
---|
37 | int sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; |
---|
38 | struct sockaddr_un controladdr; |
---|
39 | |
---|
40 | if (ControlSocketName == NULL) |
---|
41 | return 0; |
---|
42 | |
---|
43 | if (strlen(ControlSocketName) >= sizeof controladdr.sun_path) |
---|
44 | { |
---|
45 | errno = ENAMETOOLONG; |
---|
46 | return -1; |
---|
47 | } |
---|
48 | |
---|
49 | rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName, |
---|
50 | sff, S_IRUSR|S_IWUSR, NULL); |
---|
51 | |
---|
52 | /* if not safe, don't create */ |
---|
53 | if (rval != 0) |
---|
54 | { |
---|
55 | errno = rval; |
---|
56 | return -1; |
---|
57 | } |
---|
58 | |
---|
59 | ControlSocket = socket(AF_UNIX, SOCK_STREAM, 0); |
---|
60 | if (ControlSocket < 0) |
---|
61 | return -1; |
---|
62 | |
---|
63 | unlink(ControlSocketName); |
---|
64 | bzero(&controladdr, sizeof controladdr); |
---|
65 | controladdr.sun_family = AF_UNIX; |
---|
66 | strcpy(controladdr.sun_path, ControlSocketName); |
---|
67 | |
---|
68 | if (bind(ControlSocket, (struct sockaddr *) &controladdr, |
---|
69 | sizeof controladdr) < 0) |
---|
70 | { |
---|
71 | int save_errno = errno; |
---|
72 | |
---|
73 | clrcontrol(); |
---|
74 | errno = save_errno; |
---|
75 | return -1; |
---|
76 | } |
---|
77 | |
---|
78 | # if _FFR_TRUSTED_USER |
---|
79 | if (geteuid() == 0 && TrustedUid != 0) |
---|
80 | { |
---|
81 | if (chown(ControlSocketName, TrustedUid, -1) < 0) |
---|
82 | { |
---|
83 | int save_errno = errno; |
---|
84 | |
---|
85 | sm_syslog(LOG_ALERT, NOQID, |
---|
86 | "ownership change on %s failed: %s", |
---|
87 | ControlSocketName, errstring(save_errno)); |
---|
88 | message("050 ownership change on %s failed: %s", |
---|
89 | ControlSocketName, errstring(save_errno)); |
---|
90 | closecontrolsocket(TRUE); |
---|
91 | errno = save_errno; |
---|
92 | return -1; |
---|
93 | } |
---|
94 | } |
---|
95 | # endif |
---|
96 | |
---|
97 | if (chmod(ControlSocketName, S_IRUSR|S_IWUSR) < 0) |
---|
98 | { |
---|
99 | int save_errno = errno; |
---|
100 | |
---|
101 | closecontrolsocket(TRUE); |
---|
102 | errno = save_errno; |
---|
103 | return -1; |
---|
104 | } |
---|
105 | |
---|
106 | if (listen(ControlSocket, 8) < 0) |
---|
107 | { |
---|
108 | int save_errno = errno; |
---|
109 | |
---|
110 | closecontrolsocket(TRUE); |
---|
111 | errno = save_errno; |
---|
112 | return -1; |
---|
113 | } |
---|
114 | # endif |
---|
115 | #endif |
---|
116 | return 0; |
---|
117 | } |
---|
118 | /* |
---|
119 | ** CLOSECONTROLSOCKET -- close the daemon control named socket |
---|
120 | ** |
---|
121 | ** Close a named socket. |
---|
122 | ** |
---|
123 | ** Parameters: |
---|
124 | ** fullclose -- if set, close the socket and remove it; |
---|
125 | ** otherwise, just remove it |
---|
126 | ** |
---|
127 | ** Returns: |
---|
128 | ** none. |
---|
129 | */ |
---|
130 | |
---|
131 | void |
---|
132 | closecontrolsocket(fullclose) |
---|
133 | bool fullclose; |
---|
134 | { |
---|
135 | #ifdef NETUNIX |
---|
136 | # if _FFR_CONTROL_SOCKET |
---|
137 | int sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_CREAT|SFF_MUSTOWN; |
---|
138 | |
---|
139 | if (ControlSocket >= 0) |
---|
140 | { |
---|
141 | int rval; |
---|
142 | |
---|
143 | if (fullclose) |
---|
144 | { |
---|
145 | (void) close(ControlSocket); |
---|
146 | ControlSocket = -1; |
---|
147 | } |
---|
148 | |
---|
149 | rval = safefile(ControlSocketName, RunAsUid, RunAsGid, RunAsUserName, |
---|
150 | sff, S_IRUSR|S_IWUSR, NULL); |
---|
151 | |
---|
152 | /* if not safe, don't unlink */ |
---|
153 | if (rval != 0) |
---|
154 | return; |
---|
155 | |
---|
156 | if (unlink(ControlSocketName) < 0) |
---|
157 | { |
---|
158 | sm_syslog(LOG_WARNING, NOQID, |
---|
159 | "Could not remove control socket: %s", |
---|
160 | errstring(errno)); |
---|
161 | return; |
---|
162 | } |
---|
163 | } |
---|
164 | # endif |
---|
165 | #endif |
---|
166 | return; |
---|
167 | } |
---|
168 | /* |
---|
169 | ** CLRCONTROL -- reset the control connection |
---|
170 | ** |
---|
171 | ** Parameters: |
---|
172 | ** none. |
---|
173 | ** |
---|
174 | ** Returns: |
---|
175 | ** none. |
---|
176 | ** |
---|
177 | ** Side Effects: |
---|
178 | ** releases any resources used by the control interface. |
---|
179 | */ |
---|
180 | |
---|
181 | void |
---|
182 | clrcontrol() |
---|
183 | { |
---|
184 | #ifdef NETUNIX |
---|
185 | # if _FFR_CONTROL_SOCKET |
---|
186 | if (ControlSocket >= 0) |
---|
187 | (void) close(ControlSocket); |
---|
188 | ControlSocket = -1; |
---|
189 | # endif |
---|
190 | #endif |
---|
191 | } |
---|
192 | |
---|
193 | #ifndef NOT_SENDMAIL |
---|
194 | |
---|
195 | /* |
---|
196 | ** CONTROL_COMMAND -- read and process command from named socket |
---|
197 | ** |
---|
198 | ** Read and process the command from the opened socket. |
---|
199 | ** Return the results down the same socket. |
---|
200 | ** |
---|
201 | ** Parameters: |
---|
202 | ** sock -- the opened socket from getrequests() |
---|
203 | ** e -- the current envelope |
---|
204 | ** |
---|
205 | ** Returns: |
---|
206 | ** none. |
---|
207 | */ |
---|
208 | |
---|
209 | struct cmd |
---|
210 | { |
---|
211 | char *cmdname; /* command name */ |
---|
212 | int cmdcode; /* internal code, see below */ |
---|
213 | }; |
---|
214 | |
---|
215 | /* values for cmdcode */ |
---|
216 | # define CMDERROR 0 /* bad command */ |
---|
217 | # define CMDRESTART 1 /* restart daemon */ |
---|
218 | # define CMDSHUTDOWN 2 /* end daemon */ |
---|
219 | # define CMDHELP 3 /* help */ |
---|
220 | # define CMDSTATUS 4 /* daemon status */ |
---|
221 | |
---|
222 | static struct cmd CmdTab[] = |
---|
223 | { |
---|
224 | { "help", CMDHELP }, |
---|
225 | { "restart", CMDRESTART }, |
---|
226 | { "shutdown", CMDSHUTDOWN }, |
---|
227 | { "status", CMDSTATUS }, |
---|
228 | { NULL, CMDERROR } |
---|
229 | }; |
---|
230 | |
---|
231 | void |
---|
232 | control_command(sock, e) |
---|
233 | int sock; |
---|
234 | ENVELOPE *e; |
---|
235 | { |
---|
236 | FILE *s; |
---|
237 | FILE *traffic; |
---|
238 | FILE *oldout; |
---|
239 | char *cmd; |
---|
240 | char *p; |
---|
241 | struct cmd *c; |
---|
242 | char cmdbuf[MAXLINE]; |
---|
243 | char inp[MAXLINE]; |
---|
244 | extern char **SaveArgv; |
---|
245 | extern void help __P((char *)); |
---|
246 | |
---|
247 | sm_setproctitle(FALSE, "control cmd read"); |
---|
248 | |
---|
249 | s = fdopen(sock, "r+"); |
---|
250 | if (s == NULL) |
---|
251 | { |
---|
252 | int save_errno = errno; |
---|
253 | |
---|
254 | close(sock); |
---|
255 | errno = save_errno; |
---|
256 | return; |
---|
257 | } |
---|
258 | setbuf(s, NULL); |
---|
259 | |
---|
260 | if (fgets(inp, sizeof inp, s) == NULL) |
---|
261 | { |
---|
262 | fclose(s); |
---|
263 | return; |
---|
264 | } |
---|
265 | (void) fflush(s); |
---|
266 | |
---|
267 | /* clean up end of line */ |
---|
268 | fixcrlf(inp, TRUE); |
---|
269 | |
---|
270 | sm_setproctitle(FALSE, "control: %s", inp); |
---|
271 | |
---|
272 | /* break off command */ |
---|
273 | for (p = inp; isascii(*p) && isspace(*p); p++) |
---|
274 | continue; |
---|
275 | cmd = cmdbuf; |
---|
276 | while (*p != '\0' && |
---|
277 | !(isascii(*p) && isspace(*p)) && |
---|
278 | cmd < &cmdbuf[sizeof cmdbuf - 2]) |
---|
279 | *cmd++ = *p++; |
---|
280 | *cmd = '\0'; |
---|
281 | |
---|
282 | /* throw away leading whitespace */ |
---|
283 | while (isascii(*p) && isspace(*p)) |
---|
284 | p++; |
---|
285 | |
---|
286 | /* decode command */ |
---|
287 | for (c = CmdTab; c->cmdname != NULL; c++) |
---|
288 | { |
---|
289 | if (!strcasecmp(c->cmdname, cmdbuf)) |
---|
290 | break; |
---|
291 | } |
---|
292 | |
---|
293 | switch (c->cmdcode) |
---|
294 | { |
---|
295 | case CMDHELP: /* get help */ |
---|
296 | traffic = TrafficLogFile; |
---|
297 | TrafficLogFile = NULL; |
---|
298 | oldout = OutChannel; |
---|
299 | OutChannel = s; |
---|
300 | help("control"); |
---|
301 | TrafficLogFile = traffic; |
---|
302 | OutChannel = oldout; |
---|
303 | break; |
---|
304 | |
---|
305 | case CMDRESTART: /* restart the daemon */ |
---|
306 | if (SaveArgv[0][0] != '/') |
---|
307 | { |
---|
308 | fprintf(s, "ERROR: could not restart: need full path\r\n"); |
---|
309 | break; |
---|
310 | } |
---|
311 | if (LogLevel > 3) |
---|
312 | sm_syslog(LOG_INFO, NOQID, |
---|
313 | "restarting %s on due to control command", |
---|
314 | SaveArgv[0]); |
---|
315 | closecontrolsocket(FALSE); |
---|
316 | if (drop_privileges(TRUE) != EX_OK) |
---|
317 | { |
---|
318 | if (LogLevel > 0) |
---|
319 | sm_syslog(LOG_ALERT, NOQID, |
---|
320 | "could not set[ug]id(%d, %d): %m", |
---|
321 | RunAsUid, RunAsGid); |
---|
322 | |
---|
323 | fprintf(s, "ERROR: could not set[ug]id(%d, %d): %s, exiting...\r\n", |
---|
324 | (int)RunAsUid, (int)RunAsGid, errstring(errno)); |
---|
325 | finis(FALSE, EX_OSERR); |
---|
326 | } |
---|
327 | fprintf(s, "OK\r\n"); |
---|
328 | clrcontrol(); |
---|
329 | (void) fcntl(sock, F_SETFD, 1); |
---|
330 | execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron); |
---|
331 | if (LogLevel > 0) |
---|
332 | sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", |
---|
333 | SaveArgv[0]); |
---|
334 | fprintf(s, "ERROR: could not exec %s: %s, exiting...\r\n", |
---|
335 | SaveArgv[0], errstring(errno)); |
---|
336 | finis(FALSE, EX_OSFILE); |
---|
337 | break; |
---|
338 | |
---|
339 | case CMDSHUTDOWN: /* kill the daemon */ |
---|
340 | fprintf(s, "OK\r\n"); |
---|
341 | finis(FALSE, EX_OK); |
---|
342 | break; |
---|
343 | |
---|
344 | case CMDSTATUS: /* daemon status */ |
---|
345 | proc_list_probe(); |
---|
346 | fprintf(s, "%d/%d\r\n", CurChildren, MaxChildren); |
---|
347 | proc_list_display(s); |
---|
348 | break; |
---|
349 | |
---|
350 | case CMDERROR: /* unknown command */ |
---|
351 | fprintf(s, "Bad command (%s)\r\n", cmdbuf); |
---|
352 | break; |
---|
353 | } |
---|
354 | fclose(s); |
---|
355 | } |
---|
356 | #endif |
---|