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.2 2004/08/19 23:57:02 dillon 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 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 host = strdup(tag->name); 68 if (inet_aton(host, &sain.sin_addr) == 0) { 69 struct hostent *hp; 70 if ((hp = gethostbyname2(host, AF_INET)) == NULL) { 71 fprintf(stderr, "Unable to resolve %s\n", host); 72 exit(1); 73 } 74 bcopy(hp->h_addr_list[0], &sain.sin_addr, hp->h_length); 75 free(host); 76 host = strdup(hp->h_name); 77 endhostent(); 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) 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", buf); 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 long bytes; 181 int n; 182 int error = 0; 183 184 fseek(fp, 0L, 2); 185 bytes = ftell(fp); 186 fseek(fp, 0L, 0); 187 fprintf(fo, "201 SIZE=%ld\r\n", 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