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