1 /* $Id: inndcomm.c 7597 2007-01-16 23:11:05Z eagle $
3 ** Library routines to let other programs control innd.
8 #include "portable/time.h"
9 #include "portable/socket.h"
16 /* Needed on AIX 4.1 to get fd_set and friends. */
17 #ifdef HAVE_SYS_SELECT_H
18 # include <sys/select.h>
21 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
25 #include "inn/innconf.h"
30 static char *ICCsockname = NULL;
31 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
32 static struct sockaddr_un ICCserv;
33 static struct sockaddr_un ICCclient;
36 static int ICCtimeout;
37 const char *ICCfailure;
51 ** Get ready to talk to the server.
59 if (innconf == NULL) {
60 if (!innconf_read(NULL)) {
61 ICCfailure = "innconf";
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);
76 ICCfailure = "mkstemp";
80 if (unlink(ICCsockname) < 0 && errno != ENOENT) {
81 ICCfailure = "unlink";
85 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
87 /* Make a socket and give it the name. */
88 if ((ICCfd = socket(AF_UNIX, SOCK_DGRAM, 0)) < 0) {
89 ICCfailure = "socket";
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));
97 memset(&ICCclient, 0, sizeof ICCclient);
98 ICCclient.sun_family = AF_UNIX;
99 strlcpy(ICCclient.sun_path, ICCsockname, sizeof(ICCclient.sun_path));
101 if (bind(ICCfd, (struct sockaddr *) &ICCclient, SUN_LEN(&ICCclient)) < 0) {
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));
117 #else /* !HAVE_UNIX_DOMAIN_SOCKETS */
119 /* Make a named pipe and open it. */
121 if (mkfifo(ICCsockname, 0666) < 0) {
125 ICCfailure = "mkfifo";
129 if ((ICCfd = open(ICCsockname, O_RDWR)) < 0) {
133 #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */
150 if (close(ICCfd) < 0) {
151 ICCfailure = "close";
154 if (unlink(ICCsockname) < 0 && errno != ENOENT) {
155 ICCfailure = "unlink";
163 ** Get the server's pid.
174 path = concatpath(innconf->pathrun, _PATH_SERVERPID);
175 F = fopen(path, "r");
178 if (fgets(buff, sizeof buff, F) != NULL)
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.
191 ICCserveralive(pid_t pid)
193 if (kill(pid, 0) > 0 || errno != ESRCH)
200 ** Send an arbitrary command to the server.
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.
208 ICCcommand(char cmd, const char *argv[], char **replyp)
215 #ifndef HAVE_UNIX_DOMAIN_SOCKETS
224 ICC_PROTOCOLTYPE protocol;
225 size_t bufsiz = 64 * 1024 - 1;
227 /* Is server there? */
228 pid = ICCserverpid();
229 if (!ICCserveralive(pid)) {
230 ICCfailure = "dead server";
234 /* Get the length of the buffer. */
235 buff = xmalloc(bufsiz);
239 /* Advance to leave space for length + protocol version info. */
241 bufsiz -= HEADER_SIZE;
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++) {
248 strlcat(buff, q, bufsiz);
254 len = p - buff + HEADER_SIZE;
257 /* now stick in the protocol version and the length. */
259 bufsiz += HEADER_SIZE;
260 protocol = ICC_PROTOCOL_1;
261 memcpy(buff, &protocol, sizeof(protocol));
262 memcpy(buff + sizeof(protocol), &rlen, sizeof(rlen));
264 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
265 if (sendto(ICCfd, buff, len, 0,(struct sockaddr *) &ICCserv,
266 SUN_LEN(&ICCserv)) < 0) {
268 ICCfailure = "sendto";
271 #else /* !HAVE_UNIX_DOMAIN_SOCKETS */
272 path = concatpath(innconf->pathrun, _PATH_NEWSCONTROL);
273 fd = open(path, O_WRONLY);
280 if (write(fd, buff, len) != len) {
285 ICCfailure = "write";
289 #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */
291 /* Possibly get a reply. */
304 /* Wait for the reply. */
307 FD_SET(ICCfd, &Rmask);
308 T.tv_sec = ICCtimeout ? ICCtimeout : 120;
310 i = select(ICCfd + 1, &Rmask, NULL, NULL, &T);
313 ICCfailure = "select";
316 if (i > 0 && FD_ISSET(ICCfd, &Rmask))
317 /* Server reply is there; go handle it. */
320 /* No data -- if we timed out, return. */
324 ICCfailure = "timeout";
328 if (!ICCserveralive(pid)) {
330 ICCfailure = "dead server";
335 #ifdef HAVE_UNIX_DOMAIN_SOCKETS
337 /* Read the reply. */
338 i = RECVorREAD(ICCfd, buff, bufsiz);
339 if ((unsigned int) i < HEADER_SIZE) {
344 memcpy(&protocol, buff, sizeof(protocol));
345 memcpy(&rlen, buff + sizeof(protocol), sizeof(rlen));
350 ICCfailure = "short read";
354 if (protocol != ICC_PROTOCOL_1) {
356 ICCfailure = "protocol mismatch";
360 memmove(buff, buff + HEADER_SIZE, rlen - HEADER_SIZE);
365 #else /* !HAVE_UNIX_DOMAIN_SOCKETS */
367 i = RECVorREAD(ICCfd, buff, HEADER_SIZE);
368 if (i != HEADER_SIZE)
371 memcpy(&protocol, buff, sizeof(protocol));
372 memcpy(&rlen, buff + sizeof(protocol), sizeof(rlen));
373 rlen = ntohs(rlen) - HEADER_SIZE;
375 ICCfailure = "bad length";
379 i = RECVorREAD(ICCfd, buff, rlen);
381 ICCfailure = "short read";
387 if (protocol != ICC_PROTOCOL_1) {
388 ICCfailure = "protocol mismatch";
392 #endif /* !HAVE_UNIX_DOMAIN_SOCKETS */
394 /* Parse the rest of the reply; expected to be like
395 <exitcode><space><text>" */
397 if (CTYPE(isdigit, buff[0])) {
398 for (p = buff; *p && CTYPE(isdigit, *p); p++)
417 ** Send a "cancel" command.
420 ICCcancel(const char *msgid)
426 return ICCcommand(SC_CANCEL, args, NULL);
431 ** Send a "go" command.
434 ICCgo(const char *why)
440 return ICCcommand(SC_GO, args, NULL);
445 ** Send a "pause" command.
448 ICCpause(const char *why)
454 return ICCcommand(SC_PAUSE, args, NULL);
459 ** Send a "reserve" command.
462 ICCreserve(const char *why)
468 return ICCcommand(SC_RESERVE, args, NULL);