xref: /dflybsd-src/sbin/rconfig/server.c (revision 063db479f3174a9a9add20a81493257ff0e0d565)
186ed60a6SMatthew Dillon /*
286ed60a6SMatthew Dillon  * RCONFIG/SERVER.C
386ed60a6SMatthew Dillon  *
47a25b4e0SMatthew Dillon  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
57a25b4e0SMatthew Dillon  *
67a25b4e0SMatthew Dillon  * This code is derived from software contributed to The DragonFly Project
77a25b4e0SMatthew Dillon  * by Matthew Dillon <dillon@backplane.com>
87a25b4e0SMatthew Dillon  *
97a25b4e0SMatthew Dillon  * Redistribution and use in source and binary forms, with or without
107a25b4e0SMatthew Dillon  * modification, are permitted provided that the following conditions
117a25b4e0SMatthew Dillon  * are met:
127a25b4e0SMatthew Dillon  *
137a25b4e0SMatthew Dillon  * 1. Redistributions of source code must retain the above copyright
147a25b4e0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer.
157a25b4e0SMatthew Dillon  * 2. Redistributions in binary form must reproduce the above copyright
167a25b4e0SMatthew Dillon  *    notice, this list of conditions and the following disclaimer in
177a25b4e0SMatthew Dillon  *    the documentation and/or other materials provided with the
187a25b4e0SMatthew Dillon  *    distribution.
197a25b4e0SMatthew Dillon  * 3. Neither the name of The DragonFly Project nor the names of its
207a25b4e0SMatthew Dillon  *    contributors may be used to endorse or promote products derived
217a25b4e0SMatthew Dillon  *    from this software without specific, prior written permission.
227a25b4e0SMatthew Dillon  *
237a25b4e0SMatthew Dillon  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
247a25b4e0SMatthew Dillon  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
257a25b4e0SMatthew Dillon  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
267a25b4e0SMatthew Dillon  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
277a25b4e0SMatthew Dillon  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
287a25b4e0SMatthew Dillon  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
297a25b4e0SMatthew Dillon  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
307a25b4e0SMatthew Dillon  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
317a25b4e0SMatthew Dillon  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
327a25b4e0SMatthew Dillon  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
337a25b4e0SMatthew Dillon  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
347a25b4e0SMatthew Dillon  * SUCH DAMAGE.
3586ed60a6SMatthew Dillon  */
3686ed60a6SMatthew Dillon 
3786ed60a6SMatthew Dillon #include "defs.h"
3886ed60a6SMatthew Dillon 
3986ed60a6SMatthew Dillon static void server_connection(int fd);
4086ed60a6SMatthew Dillon static void service_packet_loop(int fd);
4186ed60a6SMatthew Dillon static void server_chld_exit(int signo);
4286ed60a6SMatthew Dillon static int nconnects;
4386ed60a6SMatthew Dillon 
4486ed60a6SMatthew Dillon void
doServer(void)4586ed60a6SMatthew Dillon doServer(void)
4686ed60a6SMatthew Dillon {
4786ed60a6SMatthew Dillon     tag_t tag;
4886ed60a6SMatthew Dillon 
4986ed60a6SMatthew Dillon     /*
5086ed60a6SMatthew Dillon      * Listen on one or more UDP and TCP addresses, fork for each one.
5186ed60a6SMatthew Dillon      */
5286ed60a6SMatthew Dillon     signal(SIGCHLD, SIG_IGN);
5386ed60a6SMatthew Dillon     for (tag = AddrBase; tag; tag = tag->next) {
5486ed60a6SMatthew Dillon 	struct sockaddr_in sain;
551213fdc4SMatthew Dillon 	const char *host;
5686ed60a6SMatthew Dillon 	int lfd;
5786ed60a6SMatthew Dillon 	int fd;
5886ed60a6SMatthew Dillon 	int on = 1;
5986ed60a6SMatthew Dillon 
6086ed60a6SMatthew Dillon 	bzero(&sain, sizeof(sain));
6186ed60a6SMatthew Dillon 	if (tag->name == NULL) {
6286ed60a6SMatthew Dillon 	    sain.sin_addr.s_addr = INADDR_ANY;
6386ed60a6SMatthew Dillon 	    host = "<any>";
6486ed60a6SMatthew Dillon 	} else {
651213fdc4SMatthew Dillon 	    if (inet_aton(tag->name, &sain.sin_addr) == 0) {
6686ed60a6SMatthew Dillon 		struct hostent *hp;
671213fdc4SMatthew Dillon 		if ((hp = gethostbyname2(tag->name, AF_INET)) == NULL) {
681213fdc4SMatthew Dillon 		    fprintf(stderr, "Unable to resolve %s\n", tag->name);
6986ed60a6SMatthew Dillon 		    exit(1);
7086ed60a6SMatthew Dillon 		}
7186ed60a6SMatthew Dillon 		bcopy(hp->h_addr_list[0], &sain.sin_addr, hp->h_length);
7286ed60a6SMatthew Dillon 		host = strdup(hp->h_name);
7386ed60a6SMatthew Dillon 		endhostent();
741213fdc4SMatthew Dillon 	    } else {
751213fdc4SMatthew Dillon 		host = strdup(tag->name);
7686ed60a6SMatthew Dillon 	    }
7786ed60a6SMatthew Dillon 	}
7886ed60a6SMatthew Dillon 	sain.sin_port = htons(257);
7986ed60a6SMatthew Dillon 	sain.sin_len = sizeof(sain);
8086ed60a6SMatthew Dillon 	sain.sin_family = AF_INET;
8186ed60a6SMatthew Dillon 	fflush(stdout);
8286ed60a6SMatthew Dillon 	if (fork() == 0) {
8386ed60a6SMatthew Dillon 	    if ((lfd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
8486ed60a6SMatthew Dillon 		fprintf(stderr, "%s: socket: %s\n", host, strerror(errno));
8586ed60a6SMatthew Dillon 		exit(1);
8686ed60a6SMatthew Dillon 	    }
8786ed60a6SMatthew Dillon 	    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
8886ed60a6SMatthew Dillon 	    if (bind(lfd, (void *)&sain, sizeof(sain)) < 0) {
8986ed60a6SMatthew Dillon 		fprintf(stderr, "%s: bind: %s\n", host, strerror(errno));
9086ed60a6SMatthew Dillon 		exit(1);
9186ed60a6SMatthew Dillon 	    }
9286ed60a6SMatthew Dillon 	    if (listen(lfd, 20) < 0) {
9386ed60a6SMatthew Dillon 		fprintf(stderr, "%s: listen: %s\n", host, strerror(errno));
9486ed60a6SMatthew Dillon 		exit(1);
9586ed60a6SMatthew Dillon 	    }
9686ed60a6SMatthew Dillon 	    signal(SIGCHLD, server_chld_exit);
9786ed60a6SMatthew Dillon 	    for (;;) {
98d70f9514SSascha Wildner 		socklen_t slen = sizeof(sain);
9986ed60a6SMatthew Dillon 		fd = accept(lfd, (void *)&sain, &slen);
10086ed60a6SMatthew Dillon 		if (fd < 0) {
10186ed60a6SMatthew Dillon 		    if (errno != EINTR)
10286ed60a6SMatthew Dillon 			break;
10386ed60a6SMatthew Dillon 		    continue;
10486ed60a6SMatthew Dillon 		}
10586ed60a6SMatthew Dillon 		++nconnects; /* XXX sigblock/sigsetmask */
10686ed60a6SMatthew Dillon 		if (fork() == 0) {
10786ed60a6SMatthew Dillon 		    close(lfd);
10886ed60a6SMatthew Dillon 		    server_connection(fd);
10986ed60a6SMatthew Dillon 		    exit(0);
11086ed60a6SMatthew Dillon 		}
11186ed60a6SMatthew Dillon 		close(fd);
11286ed60a6SMatthew Dillon 	    }
11386ed60a6SMatthew Dillon 	    exit(0);
11486ed60a6SMatthew Dillon 	}
11586ed60a6SMatthew Dillon 	if (fork() == 0) {
11686ed60a6SMatthew Dillon 	    if ((lfd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC)) < 0) {
11786ed60a6SMatthew Dillon 		fprintf(stderr, "%s: socket: %s\n", host, strerror(errno));
11886ed60a6SMatthew Dillon 		exit(1);
11986ed60a6SMatthew Dillon 	    }
12086ed60a6SMatthew Dillon 	    if (bind(lfd, (void *)&sain, sizeof(sain)) < 0) {
12186ed60a6SMatthew Dillon 		fprintf(stderr, "%s: bind: %s\n", host, strerror(errno));
12286ed60a6SMatthew Dillon 		exit(1);
12386ed60a6SMatthew Dillon 	    }
12486ed60a6SMatthew Dillon 	    service_packet_loop(lfd);
12586ed60a6SMatthew Dillon 	    exit(1);
12686ed60a6SMatthew Dillon 	}
12786ed60a6SMatthew Dillon     }
12886ed60a6SMatthew Dillon     while (wait(NULL) > 0 || errno != EINTR)
12986ed60a6SMatthew Dillon 	;
13086ed60a6SMatthew Dillon }
13186ed60a6SMatthew Dillon 
13286ed60a6SMatthew Dillon static
13386ed60a6SMatthew Dillon void
server_chld_exit(int signo __unused)1341213fdc4SMatthew Dillon server_chld_exit(int signo __unused)
13586ed60a6SMatthew Dillon {
13686ed60a6SMatthew Dillon     while (wait3(NULL, WNOHANG, NULL) > 0)
13786ed60a6SMatthew Dillon 	--nconnects;
13886ed60a6SMatthew Dillon }
13986ed60a6SMatthew Dillon 
14086ed60a6SMatthew Dillon static
14186ed60a6SMatthew Dillon void
server_connection(int fd)14286ed60a6SMatthew Dillon server_connection(int fd)
14386ed60a6SMatthew Dillon {
14486ed60a6SMatthew Dillon     FILE *fi;
14586ed60a6SMatthew Dillon     FILE *fo;
14686ed60a6SMatthew Dillon     char buf[256];
14786ed60a6SMatthew Dillon     char *scan;
14886ed60a6SMatthew Dillon     const char *cmd;
14986ed60a6SMatthew Dillon     const char *name;
15086ed60a6SMatthew Dillon 
15186ed60a6SMatthew Dillon     fi = fdopen(fd, "r");
15286ed60a6SMatthew Dillon     fo = fdopen(dup(fd), "w");
15386ed60a6SMatthew Dillon 
15486ed60a6SMatthew Dillon     if (gethostname(buf, sizeof(buf)) == 0) {
15586ed60a6SMatthew Dillon 	fprintf(fo, "108 HELLO SERVER=%s\r\n", buf);
15686ed60a6SMatthew Dillon     } else {
157a8b658efSPeter Avalos 	fprintf(fo, "108 HELLO\r\n");
15886ed60a6SMatthew Dillon     }
15986ed60a6SMatthew Dillon     fflush(fo);
16086ed60a6SMatthew Dillon 
16186ed60a6SMatthew Dillon     while (fgets(buf, sizeof(buf), fi) != NULL) {
16286ed60a6SMatthew Dillon 	scan = buf;
16386ed60a6SMatthew Dillon 	cmd = parse_str(&scan, PAS_ALPHA);
16486ed60a6SMatthew Dillon 	if (cmd == NULL) {
16586ed60a6SMatthew Dillon 	    fprintf(fo, "502 Illegal Command String\r\n");
16686ed60a6SMatthew Dillon 	} else if (strcasecmp(cmd, "VAR") == 0) {
16786ed60a6SMatthew Dillon 	    fprintf(fo, "100 OK\r\n");
16886ed60a6SMatthew Dillon 	} else if (strcasecmp(cmd, "TAG") == 0) {
169*063db479SAaron LI 	    if ((name = parse_str(&scan, PAS_ALPHA|PAS_SYMBOL|PAS_NUMERIC))
170*063db479SAaron LI 		    == NULL) {
17186ed60a6SMatthew Dillon 		fprintf(fo, "401 Illegal Tag\r\n");
17286ed60a6SMatthew Dillon 	    } else {
17386ed60a6SMatthew Dillon 		char *path = NULL;
17486ed60a6SMatthew Dillon 		FILE *fp;
17586ed60a6SMatthew Dillon 		asprintf(&path, "%s/%s.sh", TagDir, name);
17686ed60a6SMatthew Dillon 		if ((fp = fopen(path, "r")) == NULL) {
17786ed60a6SMatthew Dillon 		    fprintf(fo, "402 '%s' Not Found\r\n", name);
17886ed60a6SMatthew Dillon 		} else {
1791213fdc4SMatthew Dillon 		    size_t bytes;
1801213fdc4SMatthew Dillon 		    size_t n;
18186ed60a6SMatthew Dillon 		    int error = 0;
18286ed60a6SMatthew Dillon 
18386ed60a6SMatthew Dillon 		    fseek(fp, 0L, 2);
1841213fdc4SMatthew Dillon 		    bytes = (size_t)ftell(fp);
18586ed60a6SMatthew Dillon 		    fseek(fp, 0L, 0);
1861213fdc4SMatthew Dillon 		    fprintf(fo, "201 SIZE=%d\r\n", (int)bytes);
18786ed60a6SMatthew Dillon 		    while (bytes > 0) {
18886ed60a6SMatthew Dillon 			n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
18986ed60a6SMatthew Dillon 			n = fread(buf, 1, n, fp);
19086ed60a6SMatthew Dillon 			if (n <= 0) {
19186ed60a6SMatthew Dillon 			    error = 1;
19286ed60a6SMatthew Dillon 			    break;
19386ed60a6SMatthew Dillon 			}
19486ed60a6SMatthew Dillon 			if (fwrite(buf, 1, n, fo) != n) {
19586ed60a6SMatthew Dillon 			    error = 1;
19686ed60a6SMatthew Dillon 			    break;
19786ed60a6SMatthew Dillon 			}
19886ed60a6SMatthew Dillon 			bytes -= n;
19986ed60a6SMatthew Dillon 		    }
20086ed60a6SMatthew Dillon 		    fclose(fp);
20186ed60a6SMatthew Dillon 		    if (bytes > 0 && ferror(fo) == 0) {
20286ed60a6SMatthew Dillon 			bzero(buf, sizeof(buf));
20386ed60a6SMatthew Dillon 			while (bytes > 0) {
20486ed60a6SMatthew Dillon 			    n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
20586ed60a6SMatthew Dillon 			    if (fwrite(buf, 1, n, fo) != n)
20686ed60a6SMatthew Dillon 				break;
20786ed60a6SMatthew Dillon 			    bytes -= n;
20886ed60a6SMatthew Dillon 			}
20986ed60a6SMatthew Dillon 		    }
21086ed60a6SMatthew Dillon 		    fprintf(fo, "202 ERROR=%d\r\n", error);
21186ed60a6SMatthew Dillon 		}
21286ed60a6SMatthew Dillon 		free(path);
21386ed60a6SMatthew Dillon 	    }
21486ed60a6SMatthew Dillon 	} else if (strcasecmp(cmd, "IDLE") == 0) {
21586ed60a6SMatthew Dillon 	    if ((name = parse_str(&scan, PAS_ANY)) == NULL) {
21686ed60a6SMatthew Dillon 		fprintf(fo, "401 Illegal String\r\n");
21786ed60a6SMatthew Dillon 	    } else {
21886ed60a6SMatthew Dillon 		fprintf(fo, "109 %s\r\n", name);
21986ed60a6SMatthew Dillon 	    }
22086ed60a6SMatthew Dillon 	} else if (strcasecmp(cmd, "QUIT") == 0) {
22186ed60a6SMatthew Dillon 	    fprintf(fo, "409 Bye!\r\n");
22286ed60a6SMatthew Dillon 	    break;
22386ed60a6SMatthew Dillon 	} else {
22486ed60a6SMatthew Dillon 	    fprintf(fo, "501 Unknown Command\r\n");
22586ed60a6SMatthew Dillon 	}
22686ed60a6SMatthew Dillon 	fflush(fo);
22786ed60a6SMatthew Dillon     }
22886ed60a6SMatthew Dillon     fclose(fi);
22986ed60a6SMatthew Dillon     fclose(fo);
23086ed60a6SMatthew Dillon }
23186ed60a6SMatthew Dillon 
23286ed60a6SMatthew Dillon /*
23386ed60a6SMatthew Dillon  * UDP packet loop.  For now just handle one request per packet.  Note that
23486ed60a6SMatthew Dillon  * since the protocol is designed to be used in a broadcast environment,
23586ed60a6SMatthew Dillon  * we only respond when we have something to contribute.
23686ed60a6SMatthew Dillon  */
23786ed60a6SMatthew Dillon static
23886ed60a6SMatthew Dillon void
service_packet_loop(int fd)23986ed60a6SMatthew Dillon service_packet_loop(int fd)
24086ed60a6SMatthew Dillon {
24186ed60a6SMatthew Dillon     struct sockaddr_in sain;
24286ed60a6SMatthew Dillon     char ibuf[256+1];
24386ed60a6SMatthew Dillon     char obuf[256+1];
244d70f9514SSascha Wildner     socklen_t sain_len;
24586ed60a6SMatthew Dillon     int n;
24686ed60a6SMatthew Dillon     char *scan;
24786ed60a6SMatthew Dillon     const char *cmd;
24886ed60a6SMatthew Dillon     const char *name;
24986ed60a6SMatthew Dillon 
25086ed60a6SMatthew Dillon     for (;;) {
25186ed60a6SMatthew Dillon 	sain_len = sizeof(sain);
25286ed60a6SMatthew Dillon 	n = recvfrom(fd, ibuf, sizeof(ibuf) - 1, 0, (void *)&sain, &sain_len);
25386ed60a6SMatthew Dillon 	if (n < 0) {
25486ed60a6SMatthew Dillon 	    if (errno == EINTR)
25586ed60a6SMatthew Dillon 		continue;
25686ed60a6SMatthew Dillon 	    break;
25786ed60a6SMatthew Dillon 	}
25886ed60a6SMatthew Dillon 	ibuf[n] = 0;
25986ed60a6SMatthew Dillon 	n = 0;
26086ed60a6SMatthew Dillon 	scan = ibuf;
26186ed60a6SMatthew Dillon 	cmd = parse_str(&scan, PAS_ALPHA);
26286ed60a6SMatthew Dillon 	if (cmd == NULL) {
26386ed60a6SMatthew Dillon 	    ;
26486ed60a6SMatthew Dillon 	} else if (strcasecmp(cmd, "TAG") == 0) {
265*063db479SAaron LI 	    if ((name = parse_str(&scan, PAS_ALPHA|PAS_SYMBOL|PAS_NUMERIC))
266*063db479SAaron LI 		    != NULL) {
26786ed60a6SMatthew Dillon 		char *path = NULL;
26886ed60a6SMatthew Dillon 		struct stat st;
26986ed60a6SMatthew Dillon 		asprintf(&path, "%s/%s.sh", TagDir, name);
27086ed60a6SMatthew Dillon 		if (stat(path, &st) == 0) {
27186ed60a6SMatthew Dillon 		    snprintf(obuf, sizeof(obuf), "101 TAG=%s\r\n", name);
27286ed60a6SMatthew Dillon 		    n = strlen(obuf);
27386ed60a6SMatthew Dillon 		}
27486ed60a6SMatthew Dillon 		free(path);
27586ed60a6SMatthew Dillon 	    }
27686ed60a6SMatthew Dillon 	}
27786ed60a6SMatthew Dillon 	if (n)
27886ed60a6SMatthew Dillon 	    sendto(fd, obuf, n, 0, (void *)&sain, sain_len);
27986ed60a6SMatthew Dillon     }
28086ed60a6SMatthew Dillon }
28186ed60a6SMatthew Dillon 
282