1 /* $Id: inndcomm.c 10283 2018-05-14 12:43:05Z iulius $
2 **
3 ** Library routines to let other programs control innd.
4 */
5
6 #include "config.h"
7 #include "clibrary.h"
8 #include "portable/socket.h"
9 #include <ctype.h>
10 #include <errno.h>
11 #include <fcntl.h>
12 #include <signal.h>
13 #include <sys/stat.h>
14
15 #ifdef HAVE_SYS_TIME_H
16 # include <sys/time.h>
17 #endif
18 #include <time.h>
19
20 /* Needed on AIX 4.1 to get fd_set and friends. */
21 #ifdef HAVE_SYS_SELECT_H
22 # include <sys/select.h>
23 #endif
24
25 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
26 # include "portable/socket-unix.h"
27 #endif
28
29 #include "inn/innconf.h"
30 #include "inn/inndcomm.h"
31 #include "inn/libinn.h"
32 #include "inn/paths.h"
33
34 static char *ICCsockname = NULL;
35 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
36 static struct sockaddr_un ICCserv;
37 static struct sockaddr_un ICCclient;
38 #endif
39 static int ICCfd;
40 static int ICCtimeout;
41 const char *ICCfailure;
42
43
44 /*
45 ** Set the timeout.
46 */
47 void
ICCsettimeout(int i)48 ICCsettimeout(int i)
49 {
50 ICCtimeout = i;
51 }
52
53
54 /*
55 ** Get ready to talk to the server.
56 */
57 int
ICCopen(void)58 ICCopen(void)
59 {
60 int mask, oerrno, fd;
61 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
62 int size = 65535;
63 #endif
64
65 if (innconf == NULL) {
66 if (!innconf_read(NULL)) {
67 ICCfailure = "innconf";
68 return -1;
69 }
70 }
71 /* Create a temporary name. mkstemp is complete overkill here and is used
72 only because it's convenient. We don't use it properly, since we
73 actually need to create a socket or named pipe, so there is a race
74 condition here. It doesn't matter, since pathrun isn't world-writable
75 (conceivably two processes could end up using the same temporary name
76 at the same time, but the worst that will happen is that one process
77 will delete the other's temporary socket). */
78 if (ICCsockname == NULL)
79 ICCsockname = concatpath(innconf->pathrun, INN_PATH_TEMPSOCK);
80 fd = mkstemp(ICCsockname);
81 if (fd < 0) {
82 ICCfailure = "mkstemp";
83 return -1;
84 }
85 close(fd);
86 if (unlink(ICCsockname) < 0 && errno != ENOENT) {
87 ICCfailure = "unlink";
88 return -1;
89 }
90
91 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
92
93 /* Make a socket and give it the name. */
94 if ((ICCfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
95 ICCfailure = "socket";
96 return -1;
97 }
98
99 /* Adjust the socket buffer size to accommodate large responses. Ignore
100 failure; the message may fit anyway, and if not, we'll fail below. */
101 setsockopt(ICCfd, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size));
102
103 memset(&ICCclient, 0, sizeof ICCclient);
104 ICCclient.sun_family = AF_UNIX;
105 strlcpy(ICCclient.sun_path, ICCsockname, sizeof(ICCclient.sun_path));
106 mask = umask(0);
107 if (bind(ICCfd, (struct sockaddr *) &ICCclient, SUN_LEN(&ICCclient)) < 0) {
108 oerrno = errno;
109 umask(mask);
110 errno = oerrno;
111 ICCfailure = "bind";
112 return -1;
113 }
114 umask(mask);
115
116 /* Name the server's socket. */
117 memset(&ICCserv, 0, sizeof ICCserv);
118 ICCserv.sun_family = AF_UNIX;
119 strlcpy(ICCserv.sun_path, innconf->pathrun, sizeof(ICCserv.sun_path));
120 strlcat(ICCserv.sun_path, "/", sizeof(ICCserv.sun_path));
121 strlcat(ICCserv.sun_path, INN_PATH_NEWSCONTROL, sizeof(ICCserv.sun_path));
122
123 #else /* !HAVE_UNIX_DOMAIN_SOCKETS */
124
125 /* Make a named pipe and open it. */
126 mask = umask(0);
127 if (mkfifo(ICCsockname, 0666) < 0) {
128 oerrno = errno;
129 umask(mask);
130 errno = oerrno;
131 ICCfailure = "mkfifo";
132 return -1;
133 }
134 umask(mask);
135 if ((ICCfd = open(ICCsockname, O_RDWR)) < 0) {
136 ICCfailure = "open";
137 return -1;
138 }
139 #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */
140
141 ICCfailure = NULL;
142 return 0;
143 }
144
145
146 /*
147 ** Close down.
148 */
149 int
ICCclose(void)150 ICCclose(void)
151 {
152 int i;
153
154 ICCfailure = NULL;
155 i = 0;
156 if (close(ICCfd) < 0) {
157 ICCfailure = "close";
158 i = -1;
159 }
160 if (unlink(ICCsockname) < 0 && errno != ENOENT) {
161 ICCfailure = "unlink";
162 i = -1;
163 }
164 return i;
165 }
166
167
168 /*
169 ** Get the server's pid.
170 */
171 static pid_t
ICCserverpid(void)172 ICCserverpid(void)
173 {
174 pid_t pid;
175 FILE *F;
176 char *path;
177 char buff[SMBUF];
178
179 pid = 1;
180 path = concatpath(innconf->pathrun, INN_PATH_SERVERPID);
181 F = fopen(path, "r");
182 free(path);
183 if (F != NULL) {
184 if (fgets(buff, sizeof buff, F) != NULL)
185 pid = atol(buff);
186 fclose(F);
187 }
188 return pid;
189 }
190
191
192 /*
193 ** See if the server is still there. When in doubt, assume yes. Cache the
194 ** PID since a rebooted server won't know about our pending message.
195 */
196 static bool
ICCserveralive(pid_t pid)197 ICCserveralive(pid_t pid)
198 {
199 if (kill(pid, 0) > 0 || errno != ESRCH)
200 return true;
201 return false;
202 }
203
204
205 /*
206 ** Send an arbitrary command to the server.
207 **
208 ** There is a protocol version (one-byte) on the front of the message,
209 ** followed by a two byte length count. The length includes the protocol
210 ** byte and the length itself. This differs from the protocol in much
211 ** earlier versions of INN.
212 */
213 int
ICCcommand(char cmd,const char * argv[],char ** replyp)214 ICCcommand(char cmd, const char *argv[], char **replyp)
215 {
216 char *buff;
217 char *p;
218 const char *q;
219 char save;
220 int i ;
221 #ifndef HAVE_UNIX_DOMAIN_SOCKETS
222 int fd;
223 char *path;
224 #endif
225 int len;
226 fd_set Rmask;
227 struct timeval T;
228 pid_t pid;
229 ICC_MSGLENTYPE rlen;
230 ICC_PROTOCOLTYPE protocol;
231 size_t bufsiz = 64 * 1024 - 1;
232
233 /* Is server there? */
234 pid = ICCserverpid();
235 if (!ICCserveralive(pid)) {
236 ICCfailure = "dead server";
237 return -1;
238 }
239
240 /* Get the length of the buffer. */
241 buff = xmalloc(bufsiz);
242 if (replyp)
243 *replyp = NULL;
244
245 /* Advance to leave space for length + protocol version info. */
246 buff += HEADER_SIZE;
247 bufsiz -= HEADER_SIZE;
248
249 /* Format the message. */
250 snprintf(buff, bufsiz, "%s%c%c", ICCsockname, SC_SEP, cmd);
251 for (p = buff + strlen(buff), i = 0; (q = argv[i]) != NULL; i++) {
252 *p++ = SC_SEP;
253 *p = '\0';
254 strlcat(buff, q, bufsiz);
255 p += strlen(q);
256 }
257
258 /* Send message. */
259 ICCfailure = NULL;
260 len = p - buff + HEADER_SIZE;
261 rlen = htons(len);
262
263 /* now stick in the protocol version and the length. */
264 buff -= HEADER_SIZE;
265 bufsiz += HEADER_SIZE;
266 protocol = ICC_PROTOCOL_1;
267 memcpy(buff, &protocol, sizeof(protocol));
268 memcpy(buff + sizeof(protocol), &rlen, sizeof(rlen));
269
270 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
271 if (sendto(ICCfd, buff, len, 0,(struct sockaddr *) &ICCserv,
272 SUN_LEN(&ICCserv)) < 0) {
273 free(buff);
274 ICCfailure = "sendto";
275 return -1;
276 }
277 #else /* !HAVE_UNIX_DOMAIN_SOCKETS */
278 path = concatpath(innconf->pathrun, INN_PATH_NEWSCONTROL);
279 fd = open(path, O_WRONLY);
280 free(path);
281 if (fd < 0) {
282 free(buff);
283 ICCfailure = "open";
284 return -1;
285 }
286 if (write(fd, buff, len) != len) {
287 i = errno;
288 free(buff);
289 close(fd);
290 errno = i;
291 ICCfailure = "write";
292 return -1;
293 }
294 close(fd);
295 #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */
296
297 /* Possibly get a reply. */
298 switch (cmd) {
299 default:
300 if (ICCtimeout >= 0)
301 break;
302 /* FALLTHROUGH */
303 case SC_SHUTDOWN:
304 case SC_XABORT:
305 case SC_XEXEC:
306 free(buff);
307 return 0;
308 }
309
310 /* Wait for the reply. */
311 for ( ; ; ) {
312 FD_ZERO(&Rmask);
313 FD_SET(ICCfd, &Rmask);
314 T.tv_sec = ICCtimeout ? ICCtimeout : 120;
315 T.tv_usec = 0;
316 i = select(ICCfd + 1, &Rmask, NULL, NULL, &T);
317 if (i < 0) {
318 free(buff);
319 ICCfailure = "select";
320 return -1;
321 }
322 if (i > 0 && FD_ISSET(ICCfd, &Rmask))
323 /* Server reply is there; go handle it. */
324 break;
325
326 /* No data -- if we timed out, return. */
327 if (ICCtimeout) {
328 free(buff);
329 errno = ETIMEDOUT;
330 ICCfailure = "timeout";
331 return -1;
332 }
333
334 if (!ICCserveralive(pid)) {
335 free(buff);
336 ICCfailure = "dead server";
337 return -1;
338 }
339 }
340
341 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
342
343 /* Read the reply. */
344 i = RECVorREAD(ICCfd, buff, bufsiz);
345 if ((unsigned int) i < HEADER_SIZE) {
346 free(buff);
347 ICCfailure = "read";
348 return -1;
349 }
350 memcpy(&protocol, buff, sizeof(protocol));
351 memcpy(&rlen, buff + sizeof(protocol), sizeof(rlen));
352 rlen = ntohs(rlen);
353
354 if (i != rlen) {
355 free(buff);
356 ICCfailure = "short read";
357 return -1;
358 }
359
360 if (protocol != ICC_PROTOCOL_1) {
361 free(buff);
362 ICCfailure = "protocol mismatch";
363 return -1;
364 }
365
366 memmove(buff, buff + HEADER_SIZE, rlen - HEADER_SIZE);
367 i -= HEADER_SIZE;
368
369 buff[i] = '\0';
370
371 #else /* !HAVE_UNIX_DOMAIN_SOCKETS */
372
373 i = RECVorREAD(ICCfd, buff, HEADER_SIZE);
374 if (i != HEADER_SIZE)
375 return -1;
376
377 memcpy(&protocol, buff, sizeof(protocol));
378 memcpy(&rlen, buff + sizeof(protocol), sizeof(rlen));
379 rlen = ntohs(rlen) - HEADER_SIZE;
380 if (rlen > bufsiz) {
381 ICCfailure = "bad length";
382 return -1;
383 }
384
385 i = RECVorREAD(ICCfd, buff, rlen);
386 if (i != rlen) {
387 ICCfailure = "short read";
388 return -1;
389 }
390
391 buff[i] = '\0';
392
393 if (protocol != ICC_PROTOCOL_1) {
394 ICCfailure = "protocol mismatch";
395 return -1;
396 }
397
398 #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */
399
400 /* Parse the rest of the reply; expected to be like
401 <exitcode><space><text>" */
402 i = 0;
403 if (isdigit((unsigned char) buff[0])) {
404 for (p = buff; *p && isdigit((unsigned char) *p); p++)
405 continue;
406 if (*p) {
407 save = *p;
408 *p = '\0';
409 i = atoi(buff);
410 *p = save;
411 }
412 }
413 if (replyp)
414 *replyp = buff;
415 else
416 free(buff);
417
418 return i;
419 }
420
421
422 /*
423 ** Send a "cancel" command.
424 */
425 int
ICCcancel(const char * msgid)426 ICCcancel(const char *msgid)
427 {
428 const char *args[2];
429
430 args[0] = msgid;
431 args[1] = NULL;
432 return ICCcommand(SC_CANCEL, args, NULL);
433 }
434
435
436 /*
437 ** Send a "go" command.
438 */
439 int
ICCgo(const char * why)440 ICCgo(const char *why)
441 {
442 const char *args[2];
443
444 args[0] = why;
445 args[1] = NULL;
446 return ICCcommand(SC_GO, args, NULL);
447 }
448
449
450 /*
451 ** Send a "pause" command.
452 */
453 int
ICCpause(const char * why)454 ICCpause(const char *why)
455 {
456 const char *args[2];
457
458 args[0] = why;
459 args[1] = NULL;
460 return ICCcommand(SC_PAUSE, args, NULL);
461 }
462
463
464 /*
465 ** Send a "reserve" command.
466 */
467 int
ICCreserve(const char * why)468 ICCreserve(const char *why)
469 {
470 const char *args[2];
471
472 args[0] = why;
473 args[1] = NULL;
474 return ICCcommand(SC_RESERVE, args, NULL);
475 }
476