Merge branch 'vendor/OPENPAM'
[dragonfly.git] / sbin / rconfig / server.c
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_NUMERIC)) == NULL) {
170                 fprintf(fo, "401 Illegal Tag\r\n");
171             } else {
172                 char *path = NULL;
173                 FILE *fp;
174                 asprintf(&path, "%s/%s.sh", TagDir, name);
175                 if ((fp = fopen(path, "r")) == NULL) {
176                     fprintf(fo, "402 '%s' Not Found\r\n", name);
177                 } else {
178                     size_t bytes;
179                     size_t n;
180                     int error = 0;
181
182                     fseek(fp, 0L, 2);
183                     bytes = (size_t)ftell(fp);
184                     fseek(fp, 0L, 0);
185                     fprintf(fo, "201 SIZE=%d\r\n", (int)bytes);
186                     while (bytes > 0) {
187                         n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
188                         n = fread(buf, 1, n, fp);
189                         if (n <= 0) {
190                             error = 1;
191                             break;
192                         }
193                         if (fwrite(buf, 1, n, fo) != n) {
194                             error = 1;
195                             break;
196                         }
197                         bytes -= n;
198                     }
199                     fclose(fp);
200                     if (bytes > 0 && ferror(fo) == 0) {
201                         bzero(buf, sizeof(buf));
202                         while (bytes > 0) {
203                             n = (bytes > sizeof(buf)) ? sizeof(buf) : bytes;
204                             if (fwrite(buf, 1, n, fo) != n)
205                                 break;
206                             bytes -= n;
207                         }
208                     }
209                     fprintf(fo, "202 ERROR=%d\r\n", error); 
210                 }
211                 free(path);
212             }
213         } else if (strcasecmp(cmd, "IDLE") == 0) {
214             if ((name = parse_str(&scan, PAS_ANY)) == NULL) {
215                 fprintf(fo, "401 Illegal String\r\n");
216             } else {
217                 fprintf(fo, "109 %s\r\n", name);
218             }
219         } else if (strcasecmp(cmd, "QUIT") == 0) {
220             fprintf(fo, "409 Bye!\r\n");
221             break;
222         } else {
223             fprintf(fo, "501 Unknown Command\r\n");
224         }
225         fflush(fo);
226     }
227     fclose(fi);
228     fclose(fo);
229 }
230
231 /*
232  * UDP packet loop.  For now just handle one request per packet.  Note that
233  * since the protocol is designed to be used in a broadcast environment,
234  * we only respond when we have something to contribute.
235  */
236 static
237 void
238 service_packet_loop(int fd)
239 {
240     struct sockaddr_in sain;
241     char ibuf[256+1];
242     char obuf[256+1];
243     socklen_t sain_len;
244     int n;
245     char *scan;
246     const char *cmd;
247     const char *name;
248
249     for (;;) {
250         sain_len = sizeof(sain);
251         n = recvfrom(fd, ibuf, sizeof(ibuf) - 1, 0, (void *)&sain, &sain_len);
252         if (n < 0) {
253             if (errno == EINTR)
254                 continue;
255             break;
256         }
257         ibuf[n] = 0;
258         n = 0;
259         scan = ibuf;
260         cmd = parse_str(&scan, PAS_ALPHA);
261         if (cmd == NULL) {
262             ;
263         } else if (strcasecmp(cmd, "TAG") == 0) {
264             if ((name = parse_str(&scan, PAS_ALPHA|PAS_NUMERIC)) != NULL) {
265                 char *path = NULL;
266                 struct stat st;
267                 asprintf(&path, "%s/%s.sh", TagDir, name);
268                 if (stat(path, &st) == 0) {
269                     snprintf(obuf, sizeof(obuf), "101 TAG=%s\r\n", name);
270                     n = strlen(obuf);
271                 }
272                 free(path);
273             }
274         }
275         if (n)
276             sendto(fd, obuf, n, 0, (void *)&sain, sain_len);
277     }
278 }
279