xref: /dragonfly/sbin/rconfig/server.c (revision ed5d5720)
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  * $DragonFly: src/sbin/rconfig/server.c,v 1.4 2006/10/20 14:50:42 pavalos Exp $
37  */
38 
39 #include "defs.h"
40 
41 static void server_connection(int fd);
42 static void service_packet_loop(int fd);
43 static void server_chld_exit(int signo);
44 static int nconnects;
45 
46 void
47 doServer(void)
48 {
49     tag_t tag;
50 
51     /*
52      * Listen on one or more UDP and TCP addresses, fork for each one.
53      */
54     signal(SIGCHLD, SIG_IGN);
55     for (tag = AddrBase; tag; tag = tag->next) {
56 	struct sockaddr_in sain;
57 	const char *host;
58 	int lfd;
59 	int fd;
60 	int on = 1;
61 
62 	bzero(&sain, sizeof(sain));
63 	if (tag->name == NULL) {
64 	    sain.sin_addr.s_addr = INADDR_ANY;
65 	    host = "<any>";
66 	} else {
67 	    if (inet_aton(tag->name, &sain.sin_addr) == 0) {
68 		struct hostent *hp;
69 		if ((hp = gethostbyname2(tag->name, AF_INET)) == NULL) {
70 		    fprintf(stderr, "Unable to resolve %s\n", tag->name);
71 		    exit(1);
72 		}
73 		bcopy(hp->h_addr_list[0], &sain.sin_addr, hp->h_length);
74 		host = strdup(hp->h_name);
75 		endhostent();
76 	    } else {
77 		host = strdup(tag->name);
78 	    }
79 	}
80 	sain.sin_port = htons(257);
81 	sain.sin_len = sizeof(sain);
82 	sain.sin_family = AF_INET;
83 	fflush(stdout);
84 	if (fork() == 0) {
85 	    if ((lfd = socket(AF_INET, SOCK_STREAM, PF_UNSPEC)) < 0) {
86 		fprintf(stderr, "%s: socket: %s\n", host, strerror(errno));
87 		exit(1);
88 	    }
89 	    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
90 	    if (bind(lfd, (void *)&sain, sizeof(sain)) < 0) {
91 		fprintf(stderr, "%s: bind: %s\n", host, strerror(errno));
92 		exit(1);
93 	    }
94 	    if (listen(lfd, 20) < 0) {
95 		fprintf(stderr, "%s: listen: %s\n", host, strerror(errno));
96 		exit(1);
97 	    }
98 	    signal(SIGCHLD, server_chld_exit);
99 	    for (;;) {
100 		int slen = sizeof(sain);
101 		fd = accept(lfd, (void *)&sain, &slen);
102 		if (fd < 0) {
103 		    if (errno != EINTR)
104 			break;
105 		    continue;
106 		}
107 		++nconnects; /* XXX sigblock/sigsetmask */
108 		if (fork() == 0) {
109 		    close(lfd);
110 		    server_connection(fd);
111 		    exit(0);
112 		}
113 		close(fd);
114 	    }
115 	    exit(0);
116 	}
117 	if (fork() == 0) {
118 	    if ((lfd = socket(AF_INET, SOCK_DGRAM, PF_UNSPEC)) < 0) {
119 		fprintf(stderr, "%s: socket: %s\n", host, strerror(errno));
120 		exit(1);
121 	    }
122 	    if (bind(lfd, (void *)&sain, sizeof(sain)) < 0) {
123 		fprintf(stderr, "%s: bind: %s\n", host, strerror(errno));
124 		exit(1);
125 	    }
126 	    service_packet_loop(lfd);
127 	    exit(1);
128 	}
129     }
130     while (wait(NULL) > 0 || errno != EINTR)
131 	;
132 }
133 
134 static
135 void
136 server_chld_exit(int signo __unused)
137 {
138     while (wait3(NULL, WNOHANG, NULL) > 0)
139 	--nconnects;
140 }
141 
142 static
143 void
144 server_connection(int fd)
145 {
146     FILE *fi;
147     FILE *fo;
148     char buf[256];
149     char *scan;
150     const char *cmd;
151     const char *name;
152 
153     fi = fdopen(fd, "r");
154     fo = fdopen(dup(fd), "w");
155 
156     if (gethostname(buf, sizeof(buf)) == 0) {
157 	fprintf(fo, "108 HELLO SERVER=%s\r\n", buf);
158     } else {
159 	fprintf(fo, "108 HELLO\r\n");
160     }
161     fflush(fo);
162 
163     while (fgets(buf, sizeof(buf), fi) != NULL) {
164 	scan = buf;
165 	cmd = parse_str(&scan, PAS_ALPHA);
166 	if (cmd == NULL) {
167 	    fprintf(fo, "502 Illegal Command String\r\n");
168 	} else if (strcasecmp(cmd, "VAR") == 0) {
169 	    fprintf(fo, "100 OK\r\n");
170 	} else if (strcasecmp(cmd, "TAG") == 0) {
171 	    if ((name = parse_str(&scan, PAS_ALPHA|PAS_NUMERIC)) == NULL) {
172 		fprintf(fo, "401 Illegal Tag\r\n");
173 	    } else {
174 		char *path = NULL;
175 		FILE *fp;
176 		asprintf(&path, "%s/%s.sh", TagDir, name);
177 		if ((fp = fopen(path, "r")) == NULL) {
178 		    fprintf(fo, "402 '%s' Not Found\r\n", name);
179 		} else {
180 		    size_t bytes;
181 		    size_t n;
182 		    int error = 0;
183 
184 		    fseek(fp, 0L, 2);
185 		    bytes = (size_t)ftell(fp);
186 		    fseek(fp, 0L, 0);
187 		    fprintf(fo, "201 SIZE=%d\r\n", (int)bytes);
188 		    while (bytes > 0) {
189 			n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
190 			n = fread(buf, 1, n, fp);
191 			if (n <= 0) {
192 			    error = 1;
193 			    break;
194 			}
195 			if (fwrite(buf, 1, n, fo) != n) {
196 			    error = 1;
197 			    break;
198 			}
199 			bytes -= n;
200 		    }
201 		    fclose(fp);
202 		    if (bytes > 0 && ferror(fo) == 0) {
203 			bzero(buf, sizeof(buf));
204 			while (bytes > 0) {
205 			    n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
206 			    if (fwrite(buf, 1, n, fo) != n)
207 				break;
208 			    bytes -= n;
209 			}
210 		    }
211 		    fprintf(fo, "202 ERROR=%d\r\n", error);
212 		}
213 		free(path);
214 	    }
215 	} else if (strcasecmp(cmd, "IDLE") == 0) {
216 	    if ((name = parse_str(&scan, PAS_ANY)) == NULL) {
217 		fprintf(fo, "401 Illegal String\r\n");
218 	    } else {
219 		fprintf(fo, "109 %s\r\n", name);
220 	    }
221 	} else if (strcasecmp(cmd, "QUIT") == 0) {
222 	    fprintf(fo, "409 Bye!\r\n");
223 	    break;
224 	} else {
225 	    fprintf(fo, "501 Unknown Command\r\n");
226 	}
227 	fflush(fo);
228     }
229     fclose(fi);
230     fclose(fo);
231 }
232 
233 /*
234  * UDP packet loop.  For now just handle one request per packet.  Note that
235  * since the protocol is designed to be used in a broadcast environment,
236  * we only respond when we have something to contribute.
237  */
238 static
239 void
240 service_packet_loop(int fd)
241 {
242     struct sockaddr_in sain;
243     char ibuf[256+1];
244     char obuf[256+1];
245     int sain_len;
246     int n;
247     char *scan;
248     const char *cmd;
249     const char *name;
250 
251     for (;;) {
252 	sain_len = sizeof(sain);
253 	n = recvfrom(fd, ibuf, sizeof(ibuf) - 1, 0, (void *)&sain, &sain_len);
254 	if (n < 0) {
255 	    if (errno == EINTR)
256 		continue;
257 	    break;
258 	}
259 	ibuf[n] = 0;
260 	n = 0;
261 	scan = ibuf;
262 	cmd = parse_str(&scan, PAS_ALPHA);
263 	if (cmd == NULL) {
264 	    ;
265 	} else if (strcasecmp(cmd, "TAG") == 0) {
266 	    if ((name = parse_str(&scan, PAS_ALPHA|PAS_NUMERIC)) != 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