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