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 */
5 /* this file contains the source for listen socket options */
7 #include "xiosysincludes.h"
12 #include "xio-named.h"
13 #include "xio-socket.h"
16 #include "xio-listen.h"
17 #include "xio-tcpwrap.h"
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 const struct optdesc opt_children_shutup
= { "children-shutup", "child-shutup", OPT_CHILDREN_SHUTUP
, GROUP_CHILD
, PH_PASTACCEPT
, TYPE_INT
, OFUNC_OFFSET
, XIO_OFFSETOF(shutup
) };
25 #if (WITH_UDP || WITH_TCP)
26 const struct optdesc opt_range
= { "range", NULL
, OPT_RANGE
, GROUP_RANGE
, PH_ACCEPT
, TYPE_STRING
, OFUNC_SPEC
};
28 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
) };
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
38 xioopen_listen(struct single
*sfd
, int xioflags
,
39 struct sockaddr
*us
, socklen_t uslen
,
40 struct opt
*opts
, struct opt
*opts0
,
41 int pf
, int socktype
, int proto
) {
46 if (sfd
->forever
|| sfd
->retry
) {
49 #endif /* WITH_RETRY */
52 while (true) { /* loop over failed attempts */
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(sfd
, xioflags
,
57 (struct sockaddr
*)us
, uslen
,
58 opts
, pf
, socktype
, proto
, level
);
59 /*! not sure if we should try again on retry/forever */
65 if (sfd
->forever
|| sfd
->retry
) {
66 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
67 if (result
== STAT_RETRYLATER
) {
68 Nanosleep(&sfd
->intervall
, NULL
);
70 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
75 #endif /* WITH_RETRY */
81 } /* drop out on success */
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
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
106 int _xioopen_listen(struct single
*sfd
, int xioflags
, struct sockaddr
*us
, socklen_t uslen
,
107 struct opt
*opts
, int pf
, int socktype
, int proto
, int level
) {
108 int backlog
= 5; /* why? 1 seems to cause problems under some load */
111 if (applyopts_single(sfd
, opts
, PH_INIT
) < 0) return -1;
113 if ((sfd
->fd
= xiosocket(opts
, pf
?pf
:us
->sa_family
, socktype
, proto
, level
)) < 0) {
114 return STAT_RETRYLATER
;
116 applyopts(sfd
, -1, opts
, PH_PASTSOCKET
);
118 applyopts_offset(sfd
, opts
);
119 applyopts_cloexec(sfd
->fd
, opts
);
122 xiosock_reuseaddr(sfd
->fd
, proto
, opts
);
123 applyopts(sfd
, -1, opts
, PH_PREBIND
);
125 applyopts(sfd
, -1, opts
, PH_BIND
);
126 if (Bind(sfd
->fd
, (struct sockaddr
*)us
, uslen
) < 0) {
127 Msg4(level
, "bind(%d, {%s}, "F_socklen
"): %s", sfd
->fd
,
128 sockaddr_info(us
, uslen
, infobuff
, sizeof(infobuff
)), uslen
,
131 return STAT_RETRYLATER
;
135 if (us
->sa_family
== AF_UNIX
) {
136 if (((union sockaddr_union
*)us
)->un
.sun_path
[0] != '\0') {
137 applyopts_named(((struct sockaddr_un
*)us
)->sun_path
, opts
, PH_FD
);
139 applyopts(sfd
, -1, opts
, PH_FD
);
144 applyopts(sfd
, -1, opts
, PH_PASTBIND
);
146 if (us
->sa_family
== AF_UNIX
) {
147 if (((union sockaddr_union
*)us
)->un
.sun_path
[0] != '\0') {
148 applyopts_named(((struct sockaddr_un
*)us
)->sun_path
, opts
, PH_EARLY
);
149 applyopts_named(((struct sockaddr_un
*)us
)->sun_path
, opts
, PH_PREOPEN
);
151 applyopts(sfd
, -1, opts
, PH_EARLY
);
152 applyopts(sfd
, -1, opts
, PH_PREOPEN
);
155 #endif /* WITH_UNIX */
157 applyopts(sfd
, -1, opts
, PH_PRELISTEN
);
158 retropt_int(opts
, OPT_BACKLOG
, &backlog
);
159 applyopts(sfd
, -1, opts
, PH_LISTEN
);
160 if (Listen(sfd
->fd
, backlog
) < 0) {
161 Error3("listen(%d, %d): %s", sfd
->fd
, backlog
, strerror(errno
));
162 return STAT_RETRYLATER
;
164 return _xioopen_accept_fd(sfd
, xioflags
, us
, uslen
, opts
, pf
, proto
,level
);
167 int _xioopen_accept_fd(
184 union sockaddr_union _peername
;
185 union sockaddr_union _sockname
;
186 union sockaddr_union
*pa
= &_peername
; /* peer address */
187 union sockaddr_union
*la
= &_sockname
; /* local address */
188 socklen_t pas
= sizeof(_peername
); /* peer address size */
189 socklen_t las
= sizeof(_sockname
); /* local address size */
192 retropt_bool(opts
, OPT_FORK
, &dofork
);
194 if (!(xioflags
& XIO_MAYFORK
)) {
195 Error("option fork not allowed here");
198 sfd
->flags
|= XIO_DOESFORK
;
201 retropt_int(opts
, OPT_MAX_CHILDREN
, &maxchildren
);
203 if (! dofork
&& maxchildren
) {
204 Error("option max-children not allowed without option fork");
209 xiosetchilddied(); /* set SIGCHLD handler */
212 /* Under some circumstances (e.g., TCP listen on port 0) bind() fills empty
213 fields that we want to know. */
214 if (Getsockname(sfd
->fd
, us
, &uslen
) < 0) {
215 Warn4("getsockname(%d, %p, {%d}): %s",
216 sfd
->fd
, &us
, uslen
, strerror(errno
));
219 #if WITH_IP4 /*|| WITH_IP6*/
220 if (retropt_string(opts
, OPT_RANGE
, &rangename
) >= 0) {
221 if (xioparserange(rangename
, pf
, &sfd
->para
.socket
.range
,
222 sfd
->para
.socket
.ip
.ai_flags
)
228 sfd
->para
.socket
.dorange
= true;
232 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
233 xio_retropt_tcpwrap(sfd
, opts
);
234 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
236 #if WITH_TCP || WITH_UDP
237 if (retropt_ushort(opts
, OPT_SOURCEPORT
, &sfd
->para
.socket
.ip
.sourceport
) >= 0) {
238 sfd
->para
.socket
.ip
.dosourceport
= true;
240 retropt_bool(opts
, OPT_LOWPORT
, &sfd
->para
.socket
.ip
.lowport
);
241 #endif /* WITH_TCP || WITH_UDP */
243 if (xioparms
.logopt
== 'm') {
244 Info("starting accept loop, switching to syslog");
245 diag_set('y', xioparms
.syslogfac
); xioparms
.logopt
= 'y';
247 Info("starting accept loop");
249 while (true) { /* but we only loop if fork option is set */
252 int ps
; /* peer socket */
257 /*? int level = E_ERROR;*/
258 Notice1("listening on %s", sockaddr_info(us
, uslen
, lisname
, sizeof(lisname
)));
259 if (sfd
->para
.socket
.accept_timeout
.tv_sec
> 0 ||
260 sfd
->para
.socket
.accept_timeout
.tv_usec
> 0) {
264 FD_SET(sfd
->fd
, &rfd
);
265 tmo
.tv_sec
= sfd
->para
.socket
.accept_timeout
.tv_sec
;
266 tmo
.tv_usec
= sfd
->para
.socket
.accept_timeout
.tv_usec
;
268 if (Select(sfd
->fd
+1, &rfd
, NULL
, NULL
, &tmo
) < 0) {
269 if (errno
!= EINTR
) {
270 Error5("Select(%d, &0x%lx, NULL, NULL, {%ld.%06ld}): %s", sfd
->fd
+1, 1L<<(sfd
->fd
+1),
271 sfd
->para
.socket
.accept_timeout
.tv_sec
, sfd
->para
.socket
.accept_timeout
.tv_usec
,
278 if (!FD_ISSET(sfd
->fd
, &rfd
)) {
279 struct sigaction act
;
281 Warn1("accept: %s", strerror(ETIMEDOUT
));
283 Notice("Waiting for child processes to terminate");
284 memset(&act
, 0, sizeof(struct sigaction
));
285 act
.sa_flags
= SA_NOCLDSTOP
/*|SA_RESTART*/
286 #ifdef SA_SIGINFO /* not on Linux 2.0(.33) */
293 #if HAVE_STRUCT_SIGACTION_SA_SIGACTION && defined(SA_SIGINFO)
294 act
.sa_sigaction
= 0;
295 #else /* Linux 2.0(.33) does not have sigaction.sa_sigaction */
298 sigemptyset(&act
.sa_mask
);
299 Sigaction(SIGCHLD
, &act
, NULL
);
305 ps
= Accept(sfd
->fd
, (struct sockaddr
*)&sa
, &salen
);
307 /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", sfd->fd, &sa, salen, ps);*/
308 break; /* success, break out of loop */
310 if (errno
== EINTR
) {
313 if (errno
== ECONNABORTED
) {
314 Notice4("accept(%d, %p, {"F_socklen
"}): %s",
315 sfd
->fd
, &sa
, salen
, strerror(errno
));
318 Msg4(level
, "accept(%d, %p, {"F_socklen
"}): %s",
319 sfd
->fd
, &sa
, salen
, strerror(errno
));
321 return STAT_RETRYLATER
;
323 applyopts_cloexec(ps
, opts
);
324 if (Getpeername(ps
, &pa
->soa
, &pas
) < 0) {
325 Notice4("getpeername(%d, %p, {"F_socklen
"}): %s",
326 ps
, pa
, pas
, strerror(errno
));
329 if (Getsockname(ps
, &la
->soa
, &las
) < 0) {
330 Warn4("getsockname(%d, %p, {"F_socklen
"}): %s",
331 ps
, la
, las
, strerror(errno
));
334 Notice2("accepting connection from %s on %s",
336 sockaddr_info(&pa
->soa
, pas
, peername
, sizeof(peername
)):"NULL",
338 sockaddr_info(&la
->soa
, las
, sockname
, sizeof(sockname
)):"NULL");
340 if (pa
!= NULL
&& la
!= NULL
&& xiocheckpeer(sfd
, pa
, la
) < 0) {
341 if (Shutdown(ps
, 2) < 0) {
342 Info2("shutdown(%d, 2): %s", ps
, strerror(errno
));
349 Info1("permitting connection from %s",
350 sockaddr_info((struct sockaddr
*)pa
, pas
,
351 infobuff
, sizeof(infobuff
)));
354 pid_t pid
; /* mostly int; only used with fork */
355 sigset_t mask_sigchld
;
357 /* Block SIGCHLD until parent is ready to react */
358 sigemptyset(&mask_sigchld
);
359 sigaddset(&mask_sigchld
, SIGCHLD
);
360 Sigprocmask(SIG_BLOCK
, &mask_sigchld
, NULL
);
363 xio_fork(false, level
==E_ERROR
?level
:E_WARN
,
367 Sigprocmask(SIG_UNBLOCK
, &mask_sigchld
, NULL
);
368 return STAT_RETRYLATER
;
370 if (pid
== 0) { /* child */
371 pid_t cpid
= Getpid();
372 Sigprocmask(SIG_UNBLOCK
, &mask_sigchld
, NULL
);
374 Info1("just born: child process "F_pid
, cpid
);
375 xiosetenvulong("PID", cpid
, 1);
377 if (Close(sfd
->fd
) < 0) {
378 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
384 sfd
->forever
= false; sfd
->retry
= 0;
386 #endif /* WITH_RETRY */
391 /* server: continue loop with listen */
392 /* shutdown() closes the socket even for the child process, but
393 close() does what we want */
395 Info2("close(%d): %s", ps
, strerror(errno
));
398 /* now we are ready to handle signals */
399 Sigprocmask(SIG_UNBLOCK
, &mask_sigchld
, NULL
);
402 while (maxchildren
) {
403 if (num_child
< maxchildren
) break;
404 Notice("maxchildren are active, waiting");
405 /* UINT_MAX would even be nicer, but Openindiana works only
407 while (!Sleep(INT_MAX
)) ; /* any signal lets us continue */
409 Info("still listening");
411 if (Close(sfd
->fd
) < 0) {
412 Info2("close(%d): %s", sfd
->fd
, strerror(errno
));
419 applyopts(sfd
, -1, opts
, PH_FD
);
420 applyopts(sfd
, -1, opts
, PH_PASTSOCKET
);
421 applyopts(sfd
, -1, opts
, PH_CONNECTED
);
422 if ((result
= _xio_openlate(sfd
, opts
)) < 0)
425 /* set the env vars describing the local and remote sockets */
426 if (la
!= NULL
) xiosetsockaddrenv("SOCK", la
, las
, proto
);
427 if (pa
!= NULL
) xiosetsockaddrenv("PEER", pa
, pas
, proto
);
432 #endif /* WITH_LISTEN */