tools/llvm: Do not build with symbols
[minix3.git] / minix / commands / tcpdp / tcpd.c
blob9d1d32d7fa1ade5aa1efce69663ce37634c425b8
1 /*
2 tcpd.c
3 */
5 #include <sys/types.h>
6 #include <errno.h>
7 #include <fcntl.h>
8 #include <limits.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11 #include <string.h>
12 #include <unistd.h>
13 #include <signal.h>
14 #include <minix/config.h>
15 #include <minix/paths.h>
16 #include <sys/ioctl.h>
17 #include <sys/wait.h>
18 #include <net/hton.h>
19 #include <net/netlib.h>
20 #include <net/gen/in.h>
21 #include <net/gen/inet.h>
22 #include <netdb.h>
23 #include <net/gen/tcp.h>
24 #include <net/gen/tcp_io.h>
26 /* This program can be compiled to be paranoid, i.e. check incoming connection
27 * according to an access file, or to trust anyone. The much smaller "trust
28 * 'em" binary will call the paranoid version if the access file exists.
31 static char *arg0, *service;
32 static unsigned nchildren;
34 static void report(const char *label)
36 int err= errno;
38 fprintf(stderr, "%s %s: %s: %s\n", arg0, service, label, strerror(err));
39 errno= err;
42 static void sigchld(int sig)
44 while (waitpid(0, NULL, WNOHANG) > 0) {
45 if (nchildren > 0) nchildren--;
49 static void release(int *fd)
51 if (*fd != -1) {
52 close(*fd);
53 *fd= -1;
57 static void usage(void)
59 fprintf(stderr,
60 "Usage: %s [-d] [-m maxclients] service program [arg ...]\n",
61 arg0);
62 exit(1);
65 int main(int argc, char **argv)
67 tcpport_t port;
68 int last_failed = 0;
69 struct nwio_tcpcl tcplistenopt;
70 struct nwio_tcpconf tcpconf;
71 struct nwio_tcpopt tcpopt;
72 char *tcp_device;
73 struct servent *servent;
74 int tcp_fd, client_fd, r;
75 int pfd[2];
76 unsigned stall= 0;
77 struct sigaction sa;
78 sigset_t chldmask, chldunmask, oldmask;
79 char **progv;
81 #if !PARANOID
82 # define debug 0
83 # define max_children ((unsigned) -1)
84 arg0= argv[0];
86 /* Switch to the paranoid version of me if there are flags, or if
87 * there is an access file.
89 if (argv[1][0] == '-' || access(_PATH_SERVACCES, F_OK) == 0) {
90 execv("/usr/bin/tcpdp", argv);
91 report("tcpdp");
92 exit(1);
94 if (argc < 3) usage();
95 service= argv[1];
96 progv= argv+2;
98 #else /* PARANOID */
99 int debug, i;
100 unsigned max_children;
102 arg0= argv[0];
103 debug= 0;
104 max_children= -1;
105 i= 1;
106 while (i < argc && argv[i][0] == '-') {
107 char *opt= argv[i++] + 1;
108 unsigned long m;
109 char *end;
111 if (*opt == '-' && opt[1] == 0) break; /* -- */
113 while (*opt != 0) switch (*opt++) {
114 case 'd':
115 debug= 1;
116 break;
117 case 'm':
118 if (*opt == 0) {
119 if (i == argc) usage();
120 opt= argv[i++];
122 m= strtoul(opt, &end, 10);
123 if (m <= 0 || m > UINT_MAX || *end != 0) usage();
124 max_children= m;
125 opt= "";
126 break;
127 default:
128 usage();
131 service= argv[i++];
132 progv= argv+i;
133 if (i >= argc) usage();
134 #endif
136 /* The interface to start the service on. */
137 if ((tcp_device= getenv("TCP_DEVICE")) == NULL) tcp_device= TCP_DEVICE;
139 /* Let SIGCHLD interrupt whatever I'm doing. */
140 sigemptyset(&chldmask);
141 sigaddset(&chldmask, SIGCHLD);
142 sigprocmask(SIG_BLOCK, &chldmask, &oldmask);
143 chldunmask= oldmask;
144 sigdelset(&chldunmask, SIGCHLD);
145 sigemptyset(&sa.sa_mask);
146 sa.sa_flags = 0;
147 sa.sa_handler = sigchld;
148 sigaction(SIGCHLD, &sa, NULL);
150 /* Open a socket to the service I'm to serve. */
151 if ((servent= getservbyname(service, "tcp")) == NULL) {
152 unsigned long p;
153 char *end;
155 p= strtoul(service, &end, 0);
156 if (p <= 0 || p > 0xFFFF || *end != 0) {
157 fprintf(stderr, "%s: %s: Unknown service\n",
158 arg0, service);
159 exit(1);
161 port= htons((tcpport_t) p);
162 } else {
163 port= servent->s_port;
165 if (debug)
167 fprintf(stderr, "%s %s: listening to port %u\n",
168 arg0, service, ntohs(port));
172 /* No client yet. */
173 client_fd= -1;
175 while (1) {
176 if ((tcp_fd= open(tcp_device, O_RDWR)) < 0) {
177 report(tcp_device);
178 #if 0
179 if (errno == ENOENT || errno == ENODEV
180 || errno == ENXIO) {
181 exit(1);
183 #endif
184 last_failed = 1;
185 goto bad;
187 if(last_failed)
188 fprintf(stderr, "%s %s: %s: Ok\n",
189 arg0, service, tcp_device);
190 last_failed = 0;
192 tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_UNSET_RA | NWTC_UNSET_RP;
193 tcpconf.nwtc_locport= port;
195 if (ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf) < 0) {
196 report("Can't configure TCP channel");
197 exit(1);
200 tcpopt.nwto_flags= NWTO_DEL_RST;
202 if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) < 0) {
203 report("Can't set TCP options");
204 exit(1);
207 if (client_fd != -1) {
208 /* We have a client, so start a server for it. */
210 tcpopt.nwto_flags= 0;
211 (void) ioctl(client_fd, NWIOSTCPOPT, &tcpopt);
213 fflush(NULL);
215 /* Create a pipe to serve as an error indicator. */
216 if (pipe(pfd) < 0) {
217 report("pipe");
218 goto bad;
220 (void) fcntl(pfd[1], F_SETFD,
221 fcntl(pfd[1], F_GETFD) | FD_CLOEXEC);
223 /* Fork and exec. */
224 switch (fork()) {
225 case -1:
226 report("fork");
227 close(pfd[0]);
228 close(pfd[1]);
229 goto bad;
230 case 0:
231 close(tcp_fd);
232 close(pfd[0]);
233 #if PARANOID
234 /* Check if access to this service allowed. */
235 if (ioctl(client_fd, NWIOGTCPCONF, &tcpconf) == 0
236 && tcpconf.nwtc_remaddr != tcpconf.nwtc_locaddr
237 && !servxcheck(tcpconf.nwtc_remaddr, service, NULL)
239 exit(1);
241 #endif
242 sigprocmask(SIG_SETMASK, &oldmask, NULL);
243 dup2(client_fd, 0);
244 dup2(client_fd, 1);
245 close(client_fd);
246 execvp(progv[0], progv);
247 report(progv[0]);
248 write(pfd[1], &errno, sizeof(errno));
249 exit(1);
250 default:
251 nchildren++;
252 release(&client_fd);
253 close(pfd[1]);
254 r= read(pfd[0], &errno, sizeof(errno));
255 close(pfd[0]);
256 if (r != 0) goto bad;
257 break;
261 while (nchildren >= max_children) {
262 /* Too many clients, wait for one to die off. */
263 sigsuspend(&chldunmask);
266 /* Wait for a new connection. */
267 sigprocmask(SIG_UNBLOCK, &chldmask, NULL);
269 tcplistenopt.nwtcl_flags= 0;
270 while (ioctl(tcp_fd, NWIOTCPLISTEN, &tcplistenopt) < 0) {
271 if (errno != EINTR) {
272 if (errno != EAGAIN || debug) {
273 report("Unable to listen");
275 goto bad;
278 sigprocmask(SIG_BLOCK, &chldmask, NULL);
280 /* We got a connection. */
281 client_fd= tcp_fd;
282 tcp_fd= -1;
284 if (debug && ioctl(client_fd, NWIOGTCPCONF, &tcpconf) == 0) {
285 fprintf(stderr, "%s %s: Connection from %s:%u\n",
286 arg0, service,
287 inet_ntoa(tcpconf.nwtc_remaddr),
288 ntohs(tcpconf.nwtc_remport));
290 /* All is well, no need to stall. */
291 stall= 0;
292 continue;
294 bad:
295 /* All is not well, release resources. */
296 release(&tcp_fd);
297 release(&client_fd);
299 /* Wait a bit if this happens more than once. */
300 if (stall != 0) {
301 if (debug) {
302 fprintf(stderr, "%s %s: stalling %u second%s\n",
303 arg0, service,
304 stall, stall == 1 ? "" : "s");
306 sleep(stall);
307 stall <<= 1;
308 } else {
309 stall= 1;