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
};
24 #if (WITH_UDP || WITH_TCP)
25 const struct optdesc opt_range
= { "range", NULL
, OPT_RANGE
, GROUP_RANGE
, PH_ACCEPT
, TYPE_STRING
, OFUNC_SPEC
};
30 applies and consumes the following option:
31 PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
32 PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
33 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
34 OPT_SOURCEPORT, OPT_LOWPORT, cloexec
37 xioopen_listen(struct single
*xfd
, int xioflags
,
38 struct sockaddr
*us
, socklen_t uslen
,
39 struct opt
*opts
, struct opt
*opts0
,
40 int pf
, int socktype
, int proto
) {
45 if (xfd
->forever
|| xfd
->retry
) {
48 #endif /* WITH_RETRY */
51 while (true) { /* loop over failed attempts */
53 /* tcp listen; this can fork() for us; it only returns on error or on
54 successful establishment of tcp connection */
55 result
= _xioopen_listen(xfd
, xioflags
,
56 (struct sockaddr
*)us
, uslen
,
57 opts
, pf
, socktype
, proto
, level
);
58 /*! not sure if we should try again on retry/forever */
64 if (xfd
->forever
|| xfd
->retry
) {
65 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
66 if (result
== STAT_RETRYLATER
) {
67 Nanosleep(&xfd
->intervall
, NULL
);
69 dropopts(opts
, PH_ALL
); opts
= copyopts(opts0
, GROUP_ALL
);
74 #endif /* WITH_RETRY */
80 } /* drop out on success */
86 /* creates the listening socket, bind, applies options; waits for incoming
87 connection, checks its source address and port. Depending on fork option, it
88 may fork a subprocess.
89 pf specifies the syntax expected for range option. In the case of generic
90 socket it is 0 (expecting raw binary data), and the real pf can be obtained
91 from us->af_family; for other socket types pf == us->af_family
92 Returns 0 if a connection was accepted; with fork option, this is always in
94 Other return values indicate a problem; this can happen in the master
95 process or in a subprocess.
96 This function does not retry. If you need retries, handle this in a
97 loop in the calling function (and always provide the options...)
98 After fork, we set the forever/retry of the child process to 0
99 applies and consumes the following option:
100 PH_INIT, PH_PASTSOCKET, PH_PREBIND, PH_BIND, PH_PASTBIND, PH_EARLY,
101 PH_PREOPEN, PH_FD, PH_CONNECTED, PH_LATE, PH_LATE2
102 OPT_FORK, OPT_SO_TYPE, OPT_SO_PROTOTYPE, OPT_BACKLOG, OPT_RANGE, tcpwrap,
103 OPT_SOURCEPORT, OPT_LOWPORT, cloexec
105 int _xioopen_listen(struct single
*xfd
, int xioflags
, struct sockaddr
*us
, socklen_t uslen
,
106 struct opt
*opts
, int pf
, int socktype
, int proto
, int level
) {
109 int backlog
= 5; /* why? 1 seems to cause problems under some load */
115 union sockaddr_union _peername
;
116 union sockaddr_union _sockname
;
117 union sockaddr_union
*pa
= &_peername
; /* peer address */
118 union sockaddr_union
*la
= &_sockname
; /* local address */
119 socklen_t pas
= sizeof(_peername
); /* peer address size */
120 socklen_t las
= sizeof(_sockname
); /* local address size */
123 retropt_bool(opts
, OPT_FORK
, &dofork
);
126 if (!(xioflags
& XIO_MAYFORK
)) {
127 Error("option fork not allowed here");
130 xfd
->flags
|= XIO_DOESFORK
;
133 retropt_int(opts
, OPT_MAX_CHILDREN
, &maxchildren
);
135 if (! dofork
&& maxchildren
) {
136 Error("option max-children not allowed without option fork");
140 if (applyopts_single(xfd
, opts
, PH_INIT
) < 0) return -1;
143 xiosetchilddied(); /* set SIGCHLD handler */
146 if ((xfd
->fd
= xiosocket(opts
, us
->sa_family
, socktype
, proto
, level
)) < 0) {
147 return STAT_RETRYLATER
;
150 applyopts_cloexec(xfd
->fd
, opts
);
152 applyopts(xfd
->fd
, opts
, PH_PREBIND
);
153 applyopts(xfd
->fd
, opts
, PH_BIND
);
154 if (Bind(xfd
->fd
, (struct sockaddr
*)us
, uslen
) < 0) {
155 Msg4(level
, "bind(%d, {%s}, "F_socklen
"): %s", xfd
->fd
,
156 sockaddr_info(us
, uslen
, infobuff
, sizeof(infobuff
)), uslen
,
159 return STAT_RETRYLATER
;
163 if (us
->sa_family
== AF_UNIX
) {
164 applyopts_named(((struct sockaddr_un
*)us
)->sun_path
, opts
, PH_FD
);
167 /* under some circumstances (e.g., TCP listen on port 0) bind() fills empty
168 fields that we want to know. */
170 if (Getsockname(xfd
->fd
, us
, &uslen
) < 0) {
171 Warn4("getsockname(%d, %p, {%d}): %s",
172 xfd
->fd
, &us
, uslen
, strerror(errno
));
175 applyopts(xfd
->fd
, opts
, PH_PASTBIND
);
177 if (us
->sa_family
== AF_UNIX
) {
178 /*applyopts_early(((struct sockaddr_un *)us)->sun_path, opts);*/
179 applyopts_named(((struct sockaddr_un
*)us
)->sun_path
, opts
, PH_EARLY
);
180 applyopts_named(((struct sockaddr_un
*)us
)->sun_path
, opts
, PH_PREOPEN
);
182 #endif /* WITH_UNIX */
184 #if WITH_IP4 /*|| WITH_IP6*/
185 if (retropt_string(opts
, OPT_RANGE
, &rangename
) >= 0) {
186 if (xioparserange(rangename
, pf
, &xfd
->para
.socket
.range
)
192 xfd
->para
.socket
.dorange
= true;
196 #if (WITH_TCP || WITH_UDP) && WITH_LIBWRAP
197 xio_retropt_tcpwrap(xfd
, opts
);
198 #endif /* && (WITH_TCP || WITH_UDP) && WITH_LIBWRAP */
200 #if WITH_TCP || WITH_UDP
201 if (retropt_ushort(opts
, OPT_SOURCEPORT
, &xfd
->para
.socket
.ip
.sourceport
) >= 0) {
202 xfd
->para
.socket
.ip
.dosourceport
= true;
204 retropt_bool(opts
, OPT_LOWPORT
, &xfd
->para
.socket
.ip
.lowport
);
205 #endif /* WITH_TCP || WITH_UDP */
207 applyopts(xfd
->fd
, opts
, PH_PRELISTEN
);
208 retropt_int(opts
, OPT_BACKLOG
, &backlog
);
209 if (Listen(xfd
->fd
, backlog
) < 0) {
210 Error3("listen(%d, %d): %s", xfd
->fd
, backlog
, strerror(errno
));
211 return STAT_RETRYLATER
;
214 if (xioopts
.logopt
== 'm') {
215 Info("starting accept loop, switching to syslog");
216 diag_set('y', xioopts
.syslogfac
); xioopts
.logopt
= 'y';
218 Info("starting accept loop");
220 while (true) { /* but we only loop if fork option is set */
223 int ps
; /* peer socket */
227 salen
= sizeof(struct sockaddr
);
229 /*? int level = E_ERROR;*/
230 Notice1("listening on %s", sockaddr_info(us
, uslen
, lisname
, sizeof(lisname
)));
231 ps
= Accept(xfd
->fd
, (struct sockaddr
*)&sa
, &salen
);
233 /*0 Info4("accept(%d, %p, {"F_Zu"}) -> %d", xfd->fd, &sa, salen, ps);*/
234 break; /* success, break out of loop */
236 if (errno
== EINTR
) {
239 if (errno
== ECONNABORTED
) {
240 Notice4("accept(%d, %p, {"F_socklen
"}): %s",
241 xfd
->fd
, &sa
, salen
, strerror(errno
));
244 Msg4(level
, "accept(%d, %p, {"F_socklen
"}): %s",
245 xfd
->fd
, &sa
, salen
, strerror(errno
));
247 return STAT_RETRYLATER
;
249 applyopts_cloexec(ps
, opts
);
250 if (Getpeername(ps
, &pa
->soa
, &pas
) < 0) {
251 Warn4("getpeername(%d, %p, {"F_socklen
"}): %s",
252 ps
, pa
, pas
, strerror(errno
));
255 if (Getsockname(ps
, &la
->soa
, &las
) < 0) {
256 Warn4("getsockname(%d, %p, {"F_socklen
"}): %s",
257 ps
, la
, las
, strerror(errno
));
260 Notice2("accepting connection from %s on %s",
262 sockaddr_info(&pa
->soa
, pas
, peername
, sizeof(peername
)):"NULL",
264 sockaddr_info(&la
->soa
, las
, sockname
, sizeof(sockname
)):"NULL");
266 if (pa
!= NULL
&& la
!= NULL
&& xiocheckpeer(xfd
, pa
, la
) < 0) {
267 if (Shutdown(ps
, 2) < 0) {
268 Info2("shutdown(%d, 2): %s", ps
, strerror(errno
));
275 Info1("permitting connection from %s",
276 sockaddr_info((struct sockaddr
*)pa
, pas
,
277 infobuff
, sizeof(infobuff
)));
280 pid_t pid
; /* mostly int; only used with fork */
281 sigset_t mask_sigchld
;
283 /* we must prevent that the current packet triggers another fork;
284 therefore we wait for a signal from the recent child: USR1
285 indicates that is has consumed the last packet; CHLD means it has
287 /* block SIGCHLD and SIGUSR1 until parent is ready to react */
288 sigemptyset(&mask_sigchld
);
289 sigaddset(&mask_sigchld
, SIGCHLD
);
290 Sigprocmask(SIG_BLOCK
, &mask_sigchld
, NULL
);
292 if ((pid
= xio_fork(false, level
==E_ERROR
?level
:E_WARN
)) < 0) {
294 Sigprocmask(SIG_UNBLOCK
, &mask_sigchld
, NULL
);
295 return STAT_RETRYLATER
;
297 if (pid
== 0) { /* child */
298 pid_t cpid
= Getpid();
299 Sigprocmask(SIG_UNBLOCK
, &mask_sigchld
, NULL
);
301 Info1("just born: child process "F_pid
, cpid
);
302 xiosetenvulong("PID", cpid
, 1);
304 if (Close(xfd
->fd
) < 0) {
305 Info2("close(%d): %s", xfd
->fd
, strerror(errno
));
311 xfd
->forever
= false; xfd
->retry
= 0;
313 #endif /* WITH_RETRY */
318 /* server: continue loop with listen */
319 /* shutdown() closes the socket even for the child process, but
320 close() does what we want */
322 Info2("close(%d): %s", ps
, strerror(errno
));
325 /* now we are ready to handle signals */
326 Sigprocmask(SIG_UNBLOCK
, &mask_sigchld
, NULL
);
328 while (maxchildren
) {
329 if (num_child
< maxchildren
) break;
330 Notice("maxchildren are active, waiting");
331 /* UINT_MAX would even be nicer, but Openindiana works only
333 while (!Sleep(INT_MAX
)) ; /* any signal lets us continue */
335 Info("still listening");
337 if (Close(xfd
->fd
) < 0) {
338 Info2("close(%d): %s", xfd
->fd
, strerror(errno
));
345 applyopts(xfd
->fd
, opts
, PH_FD
);
346 applyopts(xfd
->fd
, opts
, PH_PASTSOCKET
);
347 applyopts(xfd
->fd
, opts
, PH_CONNECTED
);
348 if ((result
= _xio_openlate(xfd
, opts
)) < 0)
351 /* set the env vars describing the local and remote sockets */
352 if (la
!= NULL
) xiosetsockaddrenv("SOCK", la
, las
, proto
);
353 if (pa
!= NULL
) xiosetsockaddrenv("PEER", pa
, pas
, proto
);
358 #endif /* WITH_LISTEN */