xref: /dragonfly/sbin/rconfig/server.c (revision a4da4a90)
1 /*
2  * RCONFIG/SERVER.C
3  *
4  * Copyright (c) 2003,2004 The DragonFly Project.  All rights reserved.
5  *
6  * This code is derived from software contributed to The DragonFly Project
7  * by Matthew Dillon <dillon@backplane.com>
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  *
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in
17  *    the documentation and/or other materials provided with the
18  *    distribution.
19  * 3. Neither the name of The DragonFly Project nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific, prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
27  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
29  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
31  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
32  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
33  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include "defs.h"
38 
39 static void server_connection(int fd);
40 static void service_packet_loop(int fd);
41 static void server_chld_exit(int signo);
42 static int nconnects;
43 
44 void
45 doServer(void)
46 {
47     tag_t tag;
48 
49     /*
50      * Listen on one or more UDP and TCP addresses, fork for each one.
51      */
52     signal(SIGCHLD, SIG_IGN);
53     for (tag = AddrBase; tag; tag = tag->next) {
54 	struct sockaddr_in sain;
55 	const char *host;
56 	int lfd;
57 	int fd;
58 	int on = 1;
59 
60 	bzero(&sain, sizeof(sain));
61 	if (tag->name == NULL) {
62 	    sain.sin_addr.s_addr = INADDR_ANY;
63 	    host = "<any>";
64 	} else {
65 	    if (inet_aton(tag->name, &sain.sin_addr) == 0) {
66 		struct hostent *hp;
67 		if ((hp = gethostbyname2(tag->name, AF_INET)) == NULL) {
68 		    fprintf(stderr, "Unable to resolve %s\n", tag->name);
69 		    exit(1);
70 		}
71 		bcopy(hp->h_addr_list[0], &sain.sin_addr, hp->h_length);
72 		host = strdup(hp->h_name);
73 		endhostent();
74 	    } else {
75 		host = strdup(tag->name);
76 	    }
77 	}
78 	sain.sin_port = htons(257);
79 	sain.sin_len = sizeof(sain);
80 	sain.sin_family = AF_INET;
81 	fflush(stdout);
82 	if (fork() == 0) {
83 	    if ((lfd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
84 		fprintf(stderr, "%s: socket: %s\n", host, strerror(errno));
85 		exit(1);
86 	    }
87 	    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
88 	    if (bind(lfd, (void *)&sain, sizeof(sain)) < 0) {
89 		fprintf(stderr, "%s: bind: %s\n", host, strerror(errno));
90 		exit(1);
91 	    }
92 	    if (listen(lfd, 20) < 0) {
93 		fprintf(stderr, "%s: listen: %s\n", host, strerror(errno));
94 		exit(1);
95 	    }
96 	    signal(SIGCHLD, server_chld_exit);
97 	    for (;;) {
98 		socklen_t slen = sizeof(sain);
99 		fd = accept(lfd, (void *)&sain, &slen);
100 		if (fd < 0) {
101 		    if (errno != EINTR)
102 			break;
103 		    continue;
104 		}
105 		++nconnects; /* XXX sigblock/sigsetmask */
106 		if (fork() == 0) {
107 		    close(lfd);
108 		    server_connection(fd);
109 		    exit(0);
110 		}
111 		close(fd);
112 	    }
113 	    exit(0);
114 	}
115 	if (fork() == 0) {
116 	    if ((lfd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC)) < 0) {
117 		fprintf(stderr, "%s: socket: %s\n", host, strerror(errno));
118 		exit(1);
119 	    }
120 	    if (bind(lfd, (void *)&sain, sizeof(sain)) < 0) {
121 		fprintf(stderr, "%s: bind: %s\n", host, strerror(errno));
122 		exit(1);
123 	    }
124 	    service_packet_loop(lfd);
125 	    exit(1);
126 	}
127     }
128     while (wait(NULL) > 0 || errno != EINTR)
129 	;
130 }
131 
132 static
133 void
134 server_chld_exit(int signo __unused)
135 {
136     while (wait3(NULL, WNOHANG, NULL) > 0)
137 	--nconnects;
138 }
139 
140 static
141 void
142 server_connection(int fd)
143 {
144     FILE *fi;
145     FILE *fo;
146     char buf[256];
147     char *scan;
148     const char *cmd;
149     const char *name;
150 
151     fi = fdopen(fd, "r");
152     fo = fdopen(dup(fd), "w");
153 
154     if (gethostname(buf, sizeof(buf)) == 0) {
155 	fprintf(fo, "108 HELLO SERVER=%s\r\n", buf);
156     } else {
157 	fprintf(fo, "108 HELLO\r\n");
158     }
159     fflush(fo);
160 
161     while (fgets(buf, sizeof(buf), fi) != NULL) {
162 	scan = buf;
163 	cmd = parse_str(&scan, PAS_ALPHA);
164 	if (cmd == NULL) {
165 	    fprintf(fo, "502 Illegal Command String\r\n");
166 	} else if (strcasecmp(cmd, "VAR") == 0) {
167 	    fprintf(fo, "100 OK\r\n");
168 	} else if (strcasecmp(cmd, "TAG") == 0) {
169 	    if ((name = parse_str(&scan, PAS_ALPHA|PAS_SYMBOL|PAS_NUMERIC))
170 		    == NULL) {
171 		fprintf(fo, "401 Illegal Tag\r\n");
172 	    } else {
173 		char *path = NULL;
174 		FILE *fp;
175 		asprintf(&path, "%s/%s.sh", TagDir, name);
176 		if ((fp = fopen(path, "r")) == NULL) {
177 		    fprintf(fo, "402 '%s' Not Found\r\n", name);
178 		} else {
179 		    size_t bytes;
180 		    size_t n;
181 		    int error = 0;
182 
183 		    fseek(fp, 0L, 2);
184 		    bytes = (size_t)ftell(fp);
185 		    fseek(fp, 0L, 0);
186 		    fprintf(fo, "201 SIZE=%d\r\n", (int)bytes);
187 		    while (bytes > 0) {
188 			n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
189 			n = fread(buf, 1, n, fp);
190 			if (n <= 0) {
191 			    error = 1;
192 			    break;
193 			}
194 			if (fwrite(buf, 1, n, fo) != n) {
195 			    error = 1;
196 			    break;
197 			}
198 			bytes -= n;
199 		    }
200 		    fclose(fp);
201 		    if (bytes > 0 && ferror(fo) == 0) {
202 			bzero(buf, sizeof(buf));
203 			while (bytes > 0) {
204 			    n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
205 			    if (fwrite(buf, 1, n, fo) != n)
206 				break;
207 			    bytes -= n;
208 			}
209 		    }
210 		    fprintf(fo, "202 ERROR=%d\r\n", error);
211 		}
212 		free(path);
213 	    }
214 	} else if (strcasecmp(cmd, "IDLE") == 0) {
215 	    if ((name = parse_str(&scan, PAS_ANY)) == NULL) {
216 		fprintf(fo, "401 Illegal String\r\n");
217 	    } else {
218 		fprintf(fo, "109 %s\r\n", name);
219 	    }
220 	} else if (strcasecmp(cmd, "QUIT") == 0) {
221 	    fprintf(fo, "409 Bye!\r\n");
222 	    break;
223 	} else {
224 	    fprintf(fo, "501 Unknown Command\r\n");
225 	}
226 	fflush(fo);
227     }
228     fclose(fi);
229     fclose(fo);
230 }
231 
232 /*
233  * UDP packet loop.  For now just handle one request per packet.  Note that
234  * since the protocol is designed to be used in a broadcast environment,
235  * we only respond when we have something to contribute.
236  */
237 static
238 void
239 service_packet_loop(int fd)
240 {
241     struct sockaddr_in sain;
242     char ibuf[256+1];
243     char obuf[256+1];
244     socklen_t sain_len;
245     int n;
246     char *scan;
247     const char *cmd;
248     const char *name;
249 
250     for (;;) {
251 	sain_len = sizeof(sain);
252 	n = recvfrom(fd, ibuf, sizeof(ibuf) - 1, 0, (void *)&sain, &sain_len);
253 	if (n < 0) {
254 	    if (errno == EINTR)
255 		continue;
256 	    break;
257 	}
258 	ibuf[n] = 0;
259 	n = 0;
260 	scan = ibuf;
261 	cmd = parse_str(&scan, PAS_ALPHA);
262 	if (cmd == NULL) {
263 	    ;
264 	} else if (strcasecmp(cmd, "TAG") == 0) {
265 	    if ((name = parse_str(&scan, PAS_ALPHA|PAS_SYMBOL|PAS_NUMERIC))
266 		    != NULL) {
267 		char *path = NULL;
268 		struct stat st;
269 		asprintf(&path, "%s/%s.sh", TagDir, name);
270 		if (stat(path, &st) == 0) {
271 		    snprintf(obuf, sizeof(obuf), "101 TAG=%s\r\n", name);
272 		    n = strlen(obuf);
273 		}
274 		free(path);
275 	    }
276 	}
277 	if (n)
278 	    sendto(fd, obuf, n, 0, (void *)&sain, sain_len);
279     }
280 }
281 
282