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