Write a remote configuration utility called 'rconfig'. This initial
[dragonfly.git] / sbin / rconfig / server.c
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