func17()1 /* source: xio-listen.c */
2 /* Copyright Gerhard Rieger and contributors (see file CHANGES) */
3 /* Published under the GNU General Public License V.2, see file COPYING */
4
5 /* this file contains the source for listen socket options */
6
7 #include "xiosysincludes.h"
8
9 #if WITH_LISTEN
10
11 #include "xioopen.h"
12 #include "xio-named.h"
13 #include "xio-socket.h"
14 #include "xio-ip.h"
15 #include "xio-ip4.h"
16 #include "xio-listen.h"
17 #include "xio-tcpwrap.h"
18
19 /***** LISTEN options *****/
20 const struct optdesc opt_backlog = { "backlog", NULL, OPT_BACKLOG, GROUP_LISTEN, PH_LISTEN, TYPE_INT, OFUNC_SPEC };
21 const struct optdesc opt_fork = { "fork", NULL, OPT_FORK, GROUP_CHILD, PH_PASTACCEPT, TYPE_BOOL, OFUNC_SPEC };
22 const struct optdesc opt_max_children = { "max-children", NULL, OPT_MAX_CHILDREN, GROUP_CHILD, PH_PASTACCEPT, TYPE_INT, OFUNC_SPEC };
23 /**/
24 #if (WITH_UDP || WITH_TCP)
25 const struct optdesc opt_range = { "range", NULL, OPT_RANGE, GROUP_RANGE, PH_ACCEPT, TYPE_STRING, OFUNC_SPEC };
26 #endif
27 const struct optdesc opt_accept_timeout = { "accept-timeout", "listen-timeout", OPT_ACCEPT_TIMEOUT, GROUP_LISTEN, PH_LISTEN, TYPE_TIMEVAL, OFUNC_OFFSET, XIO_OFFSETOF(para.socket.accept_timeout) };
28
29
30 /*
31 applies and consumes the following option:
32 PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
33 PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
34 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
35 OPT_SOURCEPORT, OPT_LOWPORT, cloexec
36 */
37 int
38 xioopen_listen(struct single *xfd, int xioflags,
39 struct sockaddr *us, socklen_t uslen,
40 struct opt *opts, struct opt *opts0,
41 int pf, int socktype, int proto) {
42 int level;
43 int result;
44
45 #if WITH_RETRY
46 if (xfd->forever || xfd->retry) {
47 level = E_INFO;
48 } else
49 #endif /* WITH_RETRY */
50 level = E_ERROR;
51
52 while (true) { /* loop over failed attempts */
53
54 /* tcp listen; this can fork() for us; it only returns on error or on
55 successful establishment of tcp connection */
56 result = _xioopen_listen(xfd, xioflags,
57 (struct sockaddr *)us, uslen,
58 opts, pf, socktype, proto, level);
59 /*! not sure if we should try again on retry/forever */
60 switch (result) {
61 case STAT_OK: break;
62 #if WITH_RETRY
63 case STAT_RETRYLATER:
64 case STAT_RETRYNOW:
65 if (xfd->forever || xfd->retry) {
66 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
67 if (result == STAT_RETRYLATER) {
68 Nanosleep(&xfd->intervall, NULL);
69 }
70 dropopts(opts, PH_ALL); opts = copyopts(opts0, GROUP_ALL);
71 --xfd->retry;
72 continue;
73 }
74 return STAT_NORETRY;
75 #endif /* WITH_RETRY */
76 default:
77 return result;
78 }
79
80 break;
81 } /* drop out on success */
82
83 return result;
84 }
85
86
87 /* creates the listening socket, bind, applies options; waits for incoming
88 connection, checks its source address and port. Depending on fork option, it
89 may fork a subprocess.
90 pf specifies the syntax expected for range option. In the case of generic
91 socket it is 0 (expecting raw binary data), and the real pf can be obtained
92 from us->af_family; for other socket types pf == us->af_family
93 Returns 0 if a connection was accepted; with fork option, this is always in
94 a subprocess!
95 Other return values indicate a problem; this can happen in the master
96 process or in a subprocess.
97 This function does not retry. If you need retries, handle this in a
98 loop in the calling function (and always provide the options...)
99 After fork, we set the forever/retry of the child process to 0
100 applies and consumes the following option:
101 PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
102 PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
103 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
104 OPT_SOURCEPORT, OPT_LOWPORT, cloexec
105 */
106 int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, socklen_t uslen,
107 struct opt *opts, int pf, int socktype, int proto, int level) {
108 struct sockaddr sa;
109 socklen_t salen;
110 int backlog = 5; /* why? 1 seems to cause problems under some load */
111 char *rangename;
112 bool dofork = false;
113 int maxchildren = 0;
114 char infobuff[256];
115 char lisname[256];
116 union sockaddr_union _peername;
117 union sockaddr_union _sockname;
118 union sockaddr_union *pa = &_peername; /* peer address */
119 union sockaddr_union *la = &_sockname; /* local address */
120 socklen_t pas = sizeof(_peername); /* peer address size */
121 socklen_t las = sizeof(_sockname); /* local address size */
122 int result;
123
124 retropt_bool(opts, OPT_FORK, &dofork);
125
126 if (dofork) {
127 if (!(xioflags & XIO_MAYFORK)) {
128 Error("option fork not allowed here");
129 return STAT_NORETRY;
130 }
131 xfd->flags |= XIO_DOESFORK;
132 }
133
134 retropt_int(opts, OPT_MAX_CHILDREN, &maxchildren);
135
136 if (! dofork && maxchildren) {
137 Error("option max-children not allowed without option fork");
138 return STAT_NORETRY;
139 }
140
141 if (applyopts_single(xfd, opts, PH_INIT) < 0) return -1;
142
143 if (dofork) {
144 xiosetchilddied(); /* set SIGCHLD handler */
145 }
146
147 if ((xfd->fd = xiosocket(opts, us->sa_family, socktype, proto, level)) < 0) {
148 return STAT_RETRYLATER;
149 }
150 applyopts(xfd->fd, opts, PH_PASTSOCKET);
151
152 applyopts_offset(xfd, opts);
153 applyopts_cloexec(xfd->fd, opts);
154
155 applyopts(xfd->fd, opts, PH_PREBIND);
156 applyopts(xfd->fd, opts, PH_BIND);
157 if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
158 Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
159 sockaddr_info(us, uslen, infobuff, sizeof(infobuff)), uslen,
160 strerror(errno));
161 Close(xfd->fd);
162 return STAT_RETRYLATER;
163 }
164
165 #if WITH_UNIX
166 if (us->sa_family == AF_UNIX) {
167 if (((union sockaddr_union *)us)->un.sun_path[0] != '\0') {
168 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_FD);
169 } else {
170 applyopts(xfd->fd, opts, PH_FD);
171 }
172 }
173 #endif
174 /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
175 fields that we want to know. */
176 salen = sizeof(sa);
177 if (Getsockname(xfd->fd, us, &uslen) < 0) {
178 Warn4("getsockname(%d, %p, {%d}): %s",
179 xfd->fd, &us, uslen, strerror(errno));
180 }
181
182 applyopts(xfd->fd, opts, PH_PASTBIND);
183 #if WITH_UNIX
184 if (us->sa_family == AF_UNIX) {
185 if (((union sockaddr_union *)us)->un.sun_path[0] != '\0') {
186 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
187 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_EARLY);
188 applyopts_named(((struct sockaddr_un *)us)->sun_path, opts, PH_PREOPEN);
189 } else {
190 applyopts(xfd->fd, opts, PH_EARLY);
191 applyopts(xfd->fd, opts, PH_PREOPEN);
192 }
193 }
194 #endif /* WITH_UNIX */
195
196 #if WITH_IP4 /*|| WITH_IP6*/
197 if (retropt_string(opts, OPT_RANGE, &rangename) >= 0) {
198 if (xioparserange(rangename, pf, &xfd->para.socket.range)
199 < 0) {
200 free(rangename);
201 return STAT_NORETRY;
202 }
203 free(rangename);
204 xfd->para.socket.dorange = true;
205 }
206 #endif
207
208 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
209 xio_retropt_tcpwrap(xfd, opts);
210 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
211
212 #if WITH_TCP || WITH_UDP
213 if (retropt_ushort(opts, OPT_SOURCEPORT, &xfd->para.socket.ip.sourceport) >= 0) {
214 xfd->para.socket.ip.dosourceport = true;
215 }
216 retropt_bool(opts, OPT_LOWPORT, &xfd->para.socket.ip.lowport);
217 #endif /* WITH_TCP || WITH_UDP */
218
219 applyopts(xfd->fd, opts, PH_PRELISTEN);
220 retropt_int(opts, OPT_BACKLOG, &backlog);
221 applyopts(xfd->fd, opts, PH_LISTEN);
222 if (Listen(xfd->fd, backlog) < 0) {
223 Error3("listen(%d, %d): %s", xfd->fd, backlog, strerror(errno));
224 return STAT_RETRYLATER;
225 }
226
227 if (xioopts.logopt == 'm') {
228 Info("starting accept loop, switching to syslog");
229 diag_set('y', xioopts.syslogfac); xioopts.logopt = 'y';
230 } else {
231 Info("starting accept loop");
232 }
233 while (true) { /* but we only loop if fork option is set */
234 char peername[256];
235 char sockname[256];
236 int ps; /* peer socket */
237
238 pa = &_peername;
239 la = &_sockname;
240 salen = sizeof(struct sockaddr);
241 do {
242 /*? int level = E_ERROR;*/
243 Notice1("listening on %s", sockaddr_info(us, uslen, lisname, sizeof(lisname)));
244 if (xfd->para.socket.accept_timeout.tv_sec > 0 ||
245 xfd->para.socket.accept_timeout.tv_usec > 0) {
246 fd_set rfd;
247 struct timeval tmo;
248 FD_ZERO(&rfd);
249 FD_SET(xfd->fd, &rfd);
250 tmo.tv_sec = xfd->para.socket.accept_timeout.tv_sec;
251 tmo.tv_usec = xfd->para.socket.accept_timeout.tv_usec;
252 while (1) {
253 if (Select(xfd->fd+1, &rfd, NULL, NULL, &tmo) < 0) {
254 if (errno != EINTR) {
255 Error5("Select(%d, &0x%lx, NULL, NULL, {%ld.%06ld}): %s", xfd->fd+1, 1L<<(xfd->fd+1),
256 xfd->para.socket.accept_timeout.tv_sec, xfd->para.socket.accept_timeout.tv_usec,
257 strerror(errno));
258 }
259 } else {
260 break;
261 }
262 }
263 if (!FD_ISSET(xfd->fd, &rfd)) {
264 struct sigaction act;
265
266 Warn1("accept: %s", strerror(ETIMEDOUT));
267 Close(xfd->fd);
268 Notice("Waiting for child processes to terminate");
269 memset(&act, 0, sizeof(struct sigaction));
270 act.sa_flags = SA_NOCLDSTOP/*|SA_RESTART*/
271 #ifdef SA_SIGINFO /* not on Linux 2.0(.33) */
272 |SA_SIGINFO
273 #endif
274 #ifdef SA_NOMASK
275 |SA_NOMASK
276 #endif
277 ;
278 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
279 act.sa_sigaction = 0;
280 #else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */
281 act.sa_handler = 0;
282 #endif
283 sigemptyset(&act.sa_mask);
284 Sigaction(SIGCHLD, &act, NULL);
285 wait(NULL);
286 Exit(0);
287 }
288 }
289 ps = Accept(xfd->fd, (struct sockaddr *)&sa, &salen);
290 if (ps >= 0) {
291 /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
292 break; /* success, break out of loop */
293 }
294 if (errno == EINTR) {
295 continue;
296 }
297 if (errno == ECONNABORTED) {
298 Notice4("accept(%d, %p, {"F_socklen"}): %s",
299 xfd->fd, &sa, salen, strerror(errno));
300 continue;
301 }
302 Msg4(level, "accept(%d, %p, {"F_socklen"}): %s",
303 xfd->fd, &sa, salen, strerror(errno));
304 Close(xfd->fd);
305 return STAT_RETRYLATER;
306 } while (true);
307 applyopts_cloexec(ps, opts);
308 if (Getpeername(ps, &pa->soa, &pas) < 0) {
309 Warn4("getpeername(%d, %p, {"F_socklen"}): %s",
310 ps, pa, pas, strerror(errno));
311 pa = NULL;
312 }
313 if (Getsockname(ps, &la->soa, &las) < 0) {
314 Warn4("getsockname(%d, %p, {"F_socklen"}): %s",
315 ps, la, las, strerror(errno));
316 la = NULL;
317 }
318 Notice2("accepting connection from %s on %s",
319 pa?
320 sockaddr_info(&pa->soa, pas, peername, sizeof(peername)):"NULL",
321 la?
322 sockaddr_info(&la->soa, las, sockname, sizeof(sockname)):"NULL");
323
324 if (pa != NULL && la != NULL && xiocheckpeer(xfd, pa, la) < 0) {
325 if (Shutdown(ps, 2) < 0) {
326 Info2("shutdown(%d, 2): %s", ps, strerror(errno));
327 }
328 Close(ps);
329 continue;
330 }
331
332 if (pa != NULL)
333 Info1("permitting connection from %s",
334 sockaddr_info((struct sockaddr *)pa, pas,
335 infobuff, sizeof(infobuff)));
336
337 if (dofork) {
338 pid_t pid; /* mostly int; only used with fork */
339 sigset_t mask_sigchld;
340
341 /* we must prevent that the current packet triggers another fork;
342 therefore we wait for a signal from the recent child: USR1
343 indicates that is has consumed the last packet; CHLD means it has
344 terminated */
345 /* block SIGCHLD and SIGUSR1 until parent is ready to react */
346 sigemptyset(&mask_sigchld);
347 sigaddset(&mask_sigchld, SIGCHLD);
348 Sigprocmask(SIG_BLOCK, &mask_sigchld, NULL);
349
350 if ((pid = xio_fork(false, level==E_ERROR?level:E_WARN)) < 0) {
351 Close(xfd->fd);
352 Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
353 return STAT_RETRYLATER;
354 }
355 if (pid == 0) { /* child */
356 pid_t cpid = Getpid();
357 Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
358
359 Info1("just born: child process "F_pid, cpid);
360 xiosetenvulong("PID", cpid, 1);
361
362 if (Close(xfd->fd) < 0) {
363 Info2("close(%d): %s", xfd->fd, strerror(errno));
364 }
365 xfd->fd = ps;
366
367 #if WITH_RETRY
368 /* !? */
369 xfd->forever = false; xfd->retry = 0;
370 level = E_ERROR;
371 #endif /* WITH_RETRY */
372
373 break;
374 }
375
376 /* server: continue loop with listen */
377 /* shutdown() closes the socket even for the child process, but
378 close() does what we want */
379 if (Close(ps) < 0) {
380 Info2("close(%d): %s", ps, strerror(errno));
381 }
382
383 /* now we are ready to handle signals */
384 Sigprocmask(SIG_UNBLOCK, &mask_sigchld, NULL);
385
386 while (maxchildren) {
387 if (num_child < maxchildren) break;
388 Notice("maxchildren are active, waiting");
389 /* UINT_MAX would even be nicer, but Openindiana works only
390 with 31 bits */
391 while (!Sleep(INT_MAX)) ; /* any signal lets us continue */
392 }
393 Info("still listening");
394 } else {
395 if (Close(xfd->fd) < 0) {
396 Info2("close(%d): %s", xfd->fd, strerror(errno));
397 }
398 xfd->fd = ps;
399 break;
400 }
401 }
402
403 applyopts(xfd->fd, opts, PH_FD);
404 applyopts(xfd->fd, opts, PH_PASTSOCKET);
405 applyopts(xfd->fd, opts, PH_CONNECTED);
406 if ((result = _xio_openlate(xfd, opts)) < 0)
407 return result;
408
409 /* set the env vars describing the local and remote sockets */
410 if (la != NULL) xiosetsockaddrenv("SOCK", la, las, proto);
411 if (pa != NULL) xiosetsockaddrenv("PEER", pa, pas, proto);
412
413 return 0;
414 }
415
416 #endif /* WITH_LISTEN */
417