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
doServer(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
server_chld_exit(int signo __unused)134 server_chld_exit(int signo __unused)
135 {
136 while (wait3(NULL, WNOHANG, NULL) > 0)
137 --nconnects;
138 }
139
140 static
141 void
server_connection(int fd)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
service_packet_loop(int fd)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