mirror of
https://repo.or.cz/socat.git
synced 2025-01-08 22:12:33 +00:00
SO_REUSEADDR per default on TCP LISTEN; so-reuseaddr=
This commit is contained in:
parent
b5640dd707
commit
2a9623d61c
9 changed files with 714 additions and 242 deletions
4
CHANGES
4
CHANGES
|
@ -66,6 +66,10 @@ Features:
|
||||||
With this option Socat restores the VLAN tag.
|
With this option Socat restores the VLAN tag.
|
||||||
Feature inspired by Zhao Dong.
|
Feature inspired by Zhao Dong.
|
||||||
|
|
||||||
|
Socket option SO_REUSEADDR is now automatically applied to TCP LISTEN
|
||||||
|
addresses. reuseaddr= restores the old behaviour.
|
||||||
|
Tests: TCP4_REUSEADDR OPENSSL_6_REUSEADDR REUSEADDR_NULL
|
||||||
|
|
||||||
Corrections:
|
Corrections:
|
||||||
When a sub process (EXEC, SYSTEM) terminated with exit code other than
|
When a sub process (EXEC, SYSTEM) terminated with exit code other than
|
||||||
0, its last sent data might have been lost depending on timing of read/
|
0, its last sent data might have been lost depending on timing of read/
|
||||||
|
|
29
doc/socat.yo
29
doc/socat.yo
|
@ -586,7 +586,7 @@ label(ADDRESS_OPENSSL_LISTEN)dit(bf(tt(OPENSSL-LISTEN:<port>)))
|
||||||
link(range)(OPTION_RANGE),
|
link(range)(OPTION_RANGE),
|
||||||
link(tcpwrap)(OPTION_TCPWRAPPERS),
|
link(tcpwrap)(OPTION_TCPWRAPPERS),
|
||||||
link(su)(OPTION_SUBSTUSER),
|
link(su)(OPTION_SUBSTUSER),
|
||||||
link(reuseaddr)(OPTION_REUSEADDR),
|
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||||
link(retry)(OPTION_RETRY)nl()
|
link(retry)(OPTION_RETRY)nl()
|
||||||
See also:
|
See also:
|
||||||
link(OPENSSL)(ADDRESS_OPENSSL_CONNECT),
|
link(OPENSSL)(ADDRESS_OPENSSL_CONNECT),
|
||||||
|
@ -652,7 +652,7 @@ label(ADDRESS_OPENSSL_DTLS_SERVER)dit(bf(tt(OPENSSL-DTLS-SERVER:<port>)))
|
||||||
link(range)(OPTION_RANGE),
|
link(range)(OPTION_RANGE),
|
||||||
link(tcpwrap)(OPTION_TCPWRAPPERS),
|
link(tcpwrap)(OPTION_TCPWRAPPERS),
|
||||||
link(su)(OPTION_SUBSTUSER),
|
link(su)(OPTION_SUBSTUSER),
|
||||||
link(reuseaddr)(OPTION_REUSEADDR),
|
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||||
link(retry)(OPTION_RETRY)nl()
|
link(retry)(OPTION_RETRY)nl()
|
||||||
link(rcvtimeo)(OPTION_SO_RCVTIMOE)nl()
|
link(rcvtimeo)(OPTION_SO_RCVTIMOE)nl()
|
||||||
See also:
|
See also:
|
||||||
|
@ -788,7 +788,7 @@ label(ADDRESS_SCTP_LISTEN)dit(bf(tt(SCTP-LISTEN:<port>)))
|
||||||
link(sctp-maxseg)(OPTION_SCTP_MAXSEG),
|
link(sctp-maxseg)(OPTION_SCTP_MAXSEG),
|
||||||
link(sctp-nodelay)(OPTION_SCTP_NODELAY),
|
link(sctp-nodelay)(OPTION_SCTP_NODELAY),
|
||||||
link(su)(OPTION_SUBSTUSER),
|
link(su)(OPTION_SUBSTUSER),
|
||||||
link(reuseaddr)(OPTION_REUSEADDR),
|
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||||
link(retry)(OPTION_RETRY)nl()
|
link(retry)(OPTION_RETRY)nl()
|
||||||
See also:
|
See also:
|
||||||
link(SCTP4-LISTEN)(ADDRESS_SCTP4_LISTEN),
|
link(SCTP4-LISTEN)(ADDRESS_SCTP4_LISTEN),
|
||||||
|
@ -1053,7 +1053,7 @@ label(ADDRESS_TCP_LISTEN)dit(bf(tt(TCP-LISTEN:<port>)))
|
||||||
link(accept-timeout)(OPTION_ACCEPT_TIMEOUT),
|
link(accept-timeout)(OPTION_ACCEPT_TIMEOUT),
|
||||||
link(mss)(OPTION_MSS),
|
link(mss)(OPTION_MSS),
|
||||||
link(su)(OPTION_SUBSTUSER),
|
link(su)(OPTION_SUBSTUSER),
|
||||||
link(reuseaddr)(OPTION_REUSEADDR),
|
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||||
link(retry)(OPTION_RETRY),
|
link(retry)(OPTION_RETRY),
|
||||||
link(cool-write)(OPTION_COOL_WRITE)nl()
|
link(cool-write)(OPTION_COOL_WRITE)nl()
|
||||||
See also:
|
See also:
|
||||||
|
@ -1413,7 +1413,7 @@ label(ADDRESS_VSOCK_LISTEN)dit(bf(tt(VSOCK-LISTEN:<port>)))
|
||||||
link(max-children)(OPTION_MAX_CHILDREN),
|
link(max-children)(OPTION_MAX_CHILDREN),
|
||||||
link(backlog)(OPTION_BACKLOG),
|
link(backlog)(OPTION_BACKLOG),
|
||||||
link(su)(OPTION_SUBSTUSER),
|
link(su)(OPTION_SUBSTUSER),
|
||||||
link(reuseaddr)(OPTION_REUSEADDR),
|
link(reuseaddr)(OPTION_SO_REUSEADDR),
|
||||||
link(retry)(OPTION_RETRY)nl()
|
link(retry)(OPTION_RETRY)nl()
|
||||||
See also:
|
See also:
|
||||||
link(VSOCK-CONNECT)(ADDRESS_VSOCK_CONNECT)
|
link(VSOCK-CONNECT)(ADDRESS_VSOCK_CONNECT)
|
||||||
|
@ -2004,9 +2004,16 @@ label(OPTION_SO_RCVTIMOE)dit(bf(tt(so-rcvtimeo=<time>, rcvtimeo=<time>)))
|
||||||
label(OPTION_SO_SNDTIMOE)dit(bf(tt(so-sndtimeo=<time>, sndtimeo=<time>)))
|
label(OPTION_SO_SNDTIMOE)dit(bf(tt(so-sndtimeo=<time>, sndtimeo=<time>)))
|
||||||
Like link(so-recvtimeo)(OPTION_SO_RCVTIMOE), but for code(send). Not usecase
|
Like link(so-recvtimeo)(OPTION_SO_RCVTIMOE), but for code(send). Not usecase
|
||||||
known.
|
known.
|
||||||
label(OPTION_REUSEADDR)dit(bf(tt(reuseaddr)))
|
label(OPTION_RCVLOWAT)dit(bf(tt(rcvlowat=<bytes>)))
|
||||||
|
Specifies the minimum number of received bytes [link(int)(TYPE_INT)] until
|
||||||
|
the socket layer will pass the buffered data to socat().
|
||||||
|
label(OPTION_SO_REUSEADDR)dit(bf(tt(reuseaddr[=[0|1]])))
|
||||||
Allows other sockets to bind to an address even if parts of it (e.g. the
|
Allows other sockets to bind to an address even if parts of it (e.g. the
|
||||||
local port) are already in use by socat() (link(example)(EXAMPLE_OPTION_REUSEADDR)).
|
local port) are already in use by socat().nl()
|
||||||
|
With version 1.7.5, this socket option is set automatically for TCP LISTEN
|
||||||
|
addresses. If you prefer the system default (no related
|
||||||
|
tt(setsockopt(...SO_REUSEADDR...)) call at all), use form tt(reuseaddr=).nl()
|
||||||
|
(link(example)(EXAMPLE_OPTION_SO_REUSEADDR)).
|
||||||
label(OPTION_SNDBUF)dit(bf(tt(sndbuf=<bytes>)))
|
label(OPTION_SNDBUF)dit(bf(tt(sndbuf=<bytes>)))
|
||||||
Sets the size of the send buffer after the code(socket()) call to
|
Sets the size of the send buffer after the code(socket()) call to
|
||||||
<bytes> [link(int)(TYPE_INT)].
|
<bytes> [link(int)(TYPE_INT)].
|
||||||
|
@ -3295,7 +3302,7 @@ connection comes in, accepts it, then connects to the remote host
|
||||||
a second connection.
|
a second connection.
|
||||||
|
|
||||||
label(EXAMPLE_OPTION_BIND_TCP4)
|
label(EXAMPLE_OPTION_BIND_TCP4)
|
||||||
label(EXAMPLE_OPTION_REUSEADDR)
|
label(EXAMPLE_OPTION_SO_REUSEADDR)
|
||||||
label(EXAMPLE_OPTION_FORK)
|
label(EXAMPLE_OPTION_FORK)
|
||||||
label(EXAMPLE_OPTION_SUBSTUSER)
|
label(EXAMPLE_OPTION_SUBSTUSER)
|
||||||
label(EXAMPLE_OPTION_RANGE)
|
label(EXAMPLE_OPTION_RANGE)
|
||||||
|
@ -3319,7 +3326,7 @@ link(fork)(OPTION_FORK)'ing a new
|
||||||
process after each code(accept()). It provides a little security by
|
process after each code(accept()). It provides a little security by
|
||||||
link(su)(OPTION_SUBSTUSER)'ing to user
|
link(su)(OPTION_SUBSTUSER)'ing to user
|
||||||
nobody after forking; it only permits connections from the private 10 network
|
nobody after forking; it only permits connections from the private 10 network
|
||||||
(link(range)(OPTION_RANGE)); due to link(reuseaddr)(OPTION_REUSEADDR), it
|
(link(range)(OPTION_RANGE)); due to link(reuseaddr)(OPTION_SO_REUSEADDR), it
|
||||||
allows immediate restart after master process's termination, even if some child
|
allows immediate restart after master process's termination, even if some child
|
||||||
sockets are not completely shut down.
|
sockets are not completely shut down.
|
||||||
With link(-lmlocal2)(option_lm), socat logs to stderr until successfully
|
With link(-lmlocal2)(option_lm), socat logs to stderr until successfully
|
||||||
|
@ -3512,7 +3519,7 @@ implements a simple network based message collector.
|
||||||
For each client connecting to port 3334, a new child process is generated (option link(fork)(OPTION_FORK)).
|
For each client connecting to port 3334, a new child process is generated (option link(fork)(OPTION_FORK)).
|
||||||
All data sent by the clients are link(append)(OPTION_APPEND)'ed to the file /tmp/in.log.
|
All data sent by the clients are link(append)(OPTION_APPEND)'ed to the file /tmp/in.log.
|
||||||
If the file does not exist, socat link(creat)(OPTION_O_CREAT)'s it.
|
If the file does not exist, socat link(creat)(OPTION_O_CREAT)'s it.
|
||||||
Option link(reuseaddr)(OPTION_REUSEADDR) allows immediate restart of the server
|
Option link(reuseaddr)(OPTION_SO_REUSEADDR) allows immediate restart of the server
|
||||||
process.
|
process.
|
||||||
|
|
||||||
COMMENT(
|
COMMENT(
|
||||||
|
@ -3523,7 +3530,7 @@ For each client connecting to port 3335, a new child process is generated
|
||||||
(option link(fork)(OPTION_FORK)).
|
(option link(fork)(OPTION_FORK)).
|
||||||
The contents of the file /tmp/motd is sent to each client.
|
The contents of the file /tmp/motd is sent to each client.
|
||||||
Messages sent by clients result in an error due to option link(rdonly)(OPTION_RDONLY).
|
Messages sent by clients result in an error due to option link(rdonly)(OPTION_RDONLY).
|
||||||
Option link(reuseaddr)(OPTION_REUSEADDR) allows immediate restart of the server
|
Option link(reuseaddr)(OPTION_SO_REUSEADDR) allows immediate restart of the server
|
||||||
process.
|
process.
|
||||||
)
|
)
|
||||||
COMMENT(
|
COMMENT(
|
||||||
|
|
|
@ -153,7 +153,10 @@ int _xioopen_listen(struct single *xfd, int xioflags, struct sockaddr *us, sockl
|
||||||
applyopts_offset(xfd, opts);
|
applyopts_offset(xfd, opts);
|
||||||
applyopts_cloexec(xfd->fd, opts);
|
applyopts_cloexec(xfd->fd, opts);
|
||||||
|
|
||||||
|
/* Phase prebind */
|
||||||
|
xiosock_reuseaddr(xfd->fd, proto, opts);
|
||||||
applyopts(xfd->fd, opts, PH_PREBIND);
|
applyopts(xfd->fd, opts, PH_PREBIND);
|
||||||
|
|
||||||
applyopts(xfd->fd, opts, PH_BIND);
|
applyopts(xfd->fd, opts, PH_BIND);
|
||||||
if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
|
if (Bind(xfd->fd, (struct sockaddr *)us, uslen) < 0) {
|
||||||
Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
|
Msg4(level, "bind(%d, {%s}, "F_socklen"): %s", xfd->fd,
|
||||||
|
|
36
xio-socket.c
36
xio-socket.c
|
@ -97,7 +97,7 @@ const struct optdesc opt_so_debug = { "so-debug", "debug", OPT_SO_DEBUG,
|
||||||
const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN};
|
const struct optdesc opt_so_acceptconn={ "so-acceptconn","acceptconn",OPT_SO_ACCEPTCONN,GROUP_SOCKET,PH_PASTSOCKET,TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_ACCEPTCONN};
|
||||||
#endif /* SO_ACCEPTCONN */
|
#endif /* SO_ACCEPTCONN */
|
||||||
const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST};
|
const struct optdesc opt_so_broadcast= { "so-broadcast", "broadcast", OPT_SO_BROADCAST,GROUP_SOCKET, PH_PASTSOCKET, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_BROADCAST};
|
||||||
const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR};
|
const struct optdesc opt_so_reuseaddr= { "so-reuseaddr", "reuseaddr", OPT_SO_REUSEADDR,GROUP_SOCKET, PH_PREBIND, TYPE_INT_NULL, OFUNC_SOCKOPT, SOL_SOCKET, SO_REUSEADDR};
|
||||||
const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE};
|
const struct optdesc opt_so_keepalive= { "so-keepalive", "keepalive", OPT_SO_KEEPALIVE,GROUP_SOCKET, PH_FD, TYPE_INT, OFUNC_SOCKOPT, SOL_SOCKET, SO_KEEPALIVE};
|
||||||
#if HAVE_STRUCT_LINGER
|
#if HAVE_STRUCT_LINGER
|
||||||
const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER };
|
const struct optdesc opt_so_linger = { "so-linger", "linger", OPT_SO_LINGER, GROUP_SOCKET, PH_PASTSOCKET, TYPE_LINGER,OFUNC_SOCKOPT,SOL_SOCKET, SO_LINGER };
|
||||||
|
@ -2153,3 +2153,37 @@ int xiobind(
|
||||||
applyopts(xfd->fd, opts, PH_PASTBIND);
|
applyopts(xfd->fd, opts, PH_PASTBIND);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Handles the SO_REUSEADDR socket option for TCP LISTEN addresses depending on
|
||||||
|
Socat option so-reuseaddr:
|
||||||
|
Option not applied: set it to 1
|
||||||
|
Option applied with a value: set it to the value
|
||||||
|
Option applied eith empty value "so-reuseaddr=": do not call setsockopt() for
|
||||||
|
SO_REUSEADDR
|
||||||
|
Return 0 on success, or -1 with errno when an error occurred.
|
||||||
|
*/
|
||||||
|
int xiosock_reuseaddr(int fd, int ipproto, struct opt *opts)
|
||||||
|
{
|
||||||
|
union integral val;
|
||||||
|
union integral notnull;
|
||||||
|
int _errno;
|
||||||
|
|
||||||
|
val.u_int = 0;
|
||||||
|
notnull.u_bool = false;
|
||||||
|
if (ipproto == IPPROTO_TCP) {
|
||||||
|
val.u_int = 1;
|
||||||
|
notnull.u_bool = true;
|
||||||
|
}
|
||||||
|
retropt_2integrals(opts, OPT_SO_REUSEADDR, &val, ¬null);
|
||||||
|
if (notnull.u_bool) {
|
||||||
|
if (Setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val.u_int, sizeof(int))
|
||||||
|
!= 0) {
|
||||||
|
_errno = errno;
|
||||||
|
Error4("setsockopt(%d, SOL_SOCKET, SO_REUSEADDR, { %d }, "F_Zu"): %s",
|
||||||
|
fd, val.u_int, sizeof(val.u_int), strerror(errno));
|
||||||
|
errno = _errno;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
|
@ -135,5 +135,6 @@ extern int
|
||||||
xiosocket(struct opt *opts, int pf, int socktype, int proto, int level);
|
xiosocket(struct opt *opts, int pf, int socktype, int proto, int level);
|
||||||
extern int
|
extern int
|
||||||
xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]);
|
xiosocketpair(struct opt *opts, int pf, int socktype, int proto, int sv[2]);
|
||||||
|
extern int xiosock_reuseaddr(int fd, int ipproto, struct opt *opts);
|
||||||
|
|
||||||
#endif /* !defined(__xio_socket_h_included) */
|
#endif /* !defined(__xio_socket_h_included) */
|
||||||
|
|
14
xio-udp.c
14
xio-udp.c
|
@ -133,24 +133,30 @@ int _xioopen_ipdgram_listen(struct single *sfd,
|
||||||
while (true) { /* we loop with fork or prohibited packets */
|
while (true) { /* we loop with fork or prohibited packets */
|
||||||
/* now wait for some packet on this datagram socket, get its sender
|
/* now wait for some packet on this datagram socket, get its sender
|
||||||
address, connect there, and return */
|
address, connect there, and return */
|
||||||
int reuseaddr = dofork;
|
union integral notnull;
|
||||||
|
union integral reuseaddr;
|
||||||
int doreuseaddr = (dofork != 0);
|
int doreuseaddr = (dofork != 0);
|
||||||
char infobuff[256];
|
char infobuff[256];
|
||||||
union sockaddr_union _sockname;
|
union sockaddr_union _sockname;
|
||||||
union sockaddr_union *la = &_sockname; /* local address */
|
union sockaddr_union *la = &_sockname; /* local address */
|
||||||
|
|
||||||
|
reuseaddr.u_int = dofork;
|
||||||
|
|
||||||
if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
|
if ((sfd->fd = xiosocket(opts, pf, socktype, ipproto, E_ERROR)) < 0) {
|
||||||
return STAT_RETRYLATER;
|
return STAT_RETRYLATER;
|
||||||
}
|
}
|
||||||
doreuseaddr |= (retropt_int(opts, OPT_SO_REUSEADDR, &reuseaddr) >= 0);
|
doreuseaddr |= (retropt_2integrals(opts, OPT_SO_REUSEADDR,
|
||||||
|
&reuseaddr, ¬null) >= 0);
|
||||||
applyopts(sfd->fd, opts, PH_PASTSOCKET);
|
applyopts(sfd->fd, opts, PH_PASTSOCKET);
|
||||||
|
|
||||||
|
/* SO_REUSEADDR handling of UDP sockets is helpful on Solaris */
|
||||||
if (doreuseaddr) {
|
if (doreuseaddr) {
|
||||||
if (Setsockopt(sfd->fd, opt_so_reuseaddr.major,
|
if (Setsockopt(sfd->fd, opt_so_reuseaddr.major,
|
||||||
opt_so_reuseaddr.minor, &reuseaddr, sizeof(reuseaddr))
|
opt_so_reuseaddr.minor, &reuseaddr.u_int, sizeof(reuseaddr.u_int))
|
||||||
< 0) {
|
< 0) {
|
||||||
Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
|
Warn6("setsockopt(%d, %d, %d, {%d}, "F_Zd"): %s",
|
||||||
sfd->fd, opt_so_reuseaddr.major,
|
sfd->fd, opt_so_reuseaddr.major,
|
||||||
opt_so_reuseaddr.minor, reuseaddr, sizeof(reuseaddr),
|
opt_so_reuseaddr.minor, reuseaddr.u_int, sizeof(reuseaddr.u_int),
|
||||||
strerror(errno));
|
strerror(errno));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
57
xioopts.c
57
xioopts.c
|
@ -2094,6 +2094,21 @@ int parseopts_table(const char **a, groups_t groups, struct opt **opts,
|
||||||
Info2("setting option \"%s\" to %d", ent->desc->defname,
|
Info2("setting option \"%s\" to %d", ent->desc->defname,
|
||||||
(*opts)[i].value.u_int);
|
(*opts)[i].value.u_int);
|
||||||
break;
|
break;
|
||||||
|
case TYPE_INT_NULL:
|
||||||
|
(*opts)[i].value2.u_bool = true;
|
||||||
|
if (assign && token[0] != '\0') {
|
||||||
|
char *rest;
|
||||||
|
(*opts)[i].value.u_int = Strtoul(token, &rest, 0, a0);
|
||||||
|
} else if (assign) {
|
||||||
|
(*opts)[i].value2.u_bool = false; /* NULL / no value */
|
||||||
|
Info1("setting option \"%s\" to NULL", ent->desc->defname);
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
(*opts)[i].value.u_int = 1;
|
||||||
|
}
|
||||||
|
Info2("setting option \"%s\" to %d", ent->desc->defname,
|
||||||
|
(*opts)[i].value.u_int);
|
||||||
|
break;
|
||||||
case TYPE_BOOL:
|
case TYPE_BOOL:
|
||||||
if (!assign) {
|
if (!assign) {
|
||||||
(*opts)[i].value.u_bool = 1;
|
(*opts)[i].value.u_bool = 1;
|
||||||
|
@ -2924,7 +2939,7 @@ int retropt_int(struct opt *opts, int optcode, int *result) {
|
||||||
case TYPE_INT: *result = opt->value.u_int; break;
|
case TYPE_INT: *result = opt->value.u_int; break;
|
||||||
case TYPE_STRING: *result = strtol(opt->value.u_string, &rest, 0);
|
case TYPE_STRING: *result = strtol(opt->value.u_string, &rest, 0);
|
||||||
if (*rest != '\0') {
|
if (*rest != '\0') {
|
||||||
Error1("retropts: trailing garbage in numerical arg of option \"%s\"",
|
Error1("retropts_int(): trailing garbage in numerical arg of option \"%s\"",
|
||||||
opt->desc->defname);
|
opt->desc->defname);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -2941,6 +2956,36 @@ int retropt_int(struct opt *opts, int optcode, int *result) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Looks for the first option of type <optcode>. If the option is found,
|
||||||
|
this function stores its int value in *result, "consumes" the
|
||||||
|
option, and returns 0.
|
||||||
|
If the option is not found, *result is not modified, and -1 is returned. */
|
||||||
|
int retropt_2integrals(struct opt *opts, int optcode,
|
||||||
|
union integral *value1, union integral *value2)
|
||||||
|
{
|
||||||
|
struct opt *opt = opts;
|
||||||
|
|
||||||
|
while (opt->desc != ODESC_END) {
|
||||||
|
if (opt->desc != ODESC_DONE && opt->desc->optcode == optcode) {
|
||||||
|
switch (opt->desc->type) {
|
||||||
|
case TYPE_INT_NULL:
|
||||||
|
/* ...and many more types */
|
||||||
|
*value1 = opt->value;
|
||||||
|
*value2 = opt->value2;
|
||||||
|
break;
|
||||||
|
default: Error2("cannot convert type %d of option %s to int/NULL",
|
||||||
|
opt->desc->type, opt->desc->defname);
|
||||||
|
opt->desc = ODESC_ERROR;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
opt->desc = ODESC_DONE;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
++opt;
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
/* Looks for the first option of type <optcode>. If the option is found,
|
/* Looks for the first option of type <optcode>. If the option is found,
|
||||||
this function stores its unsigned int value in *result, "consumes" the
|
this function stores its unsigned int value in *result, "consumes" the
|
||||||
option, and returns 0.
|
option, and returns 0.
|
||||||
|
@ -3335,6 +3380,16 @@ int applyopts(int fd, struct opt *opts, enum e_phase phase) {
|
||||||
opt->desc = ODESC_ERROR; ++opt; continue;
|
opt->desc = ODESC_ERROR; ++opt; continue;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TYPE_INT_NULL:
|
||||||
|
if (opt->value2.u_bool &&
|
||||||
|
Setsockopt(fd, opt->desc->major, opt->desc->minor,
|
||||||
|
&opt->value.u_int, sizeof(int)) < 0) {
|
||||||
|
Error6("setsockopt(%d, %d, %d, {%d}, "F_Zu"): %s",
|
||||||
|
fd, opt->desc->major, opt->desc->minor,
|
||||||
|
opt->value.u_int, sizeof(int), strerror(errno));
|
||||||
|
opt->desc = ODESC_ERROR; ++opt; continue;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TYPE_LONG:
|
case TYPE_LONG:
|
||||||
if (Setsockopt(fd, opt->desc->major, opt->desc->minor,
|
if (Setsockopt(fd, opt->desc->major, opt->desc->minor,
|
||||||
&opt->value.u_long, sizeof(long)) < 0) {
|
&opt->value.u_long, sizeof(long)) < 0) {
|
||||||
|
|
|
@ -27,6 +27,7 @@ enum e_types {
|
||||||
TYPE_BYTE, /* unsigned char */
|
TYPE_BYTE, /* unsigned char */
|
||||||
|
|
||||||
TYPE_INT, /* int */
|
TYPE_INT, /* int */
|
||||||
|
TYPE_INT_NULL, /* int, or opt= for no action (instead of default) */
|
||||||
TYPE_LONG, /* long */
|
TYPE_LONG, /* long */
|
||||||
TYPE_STRING, /* char * */
|
TYPE_STRING, /* char * */
|
||||||
TYPE_NAME = TYPE_STRING,
|
TYPE_NAME = TYPE_STRING,
|
||||||
|
@ -125,7 +126,7 @@ enum e_func {
|
||||||
groups. to keep the search for options simple, we allow each option to
|
groups. to keep the search for options simple, we allow each option to
|
||||||
belong to at most one group only. (we have a dummy GROUP_NONE for those
|
belong to at most one group only. (we have a dummy GROUP_NONE for those
|
||||||
that don't want to belong to any...)
|
that don't want to belong to any...)
|
||||||
The caller of parseopts() specifies per bitpatter a set of groups where it
|
The caller of parseopts() specifies per bit pattern a set of groups where it
|
||||||
accepts options from.
|
accepts options from.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -946,6 +947,7 @@ extern int retropt_bool(struct opt *opts, int optcode, bool *result);
|
||||||
extern int retropt_short(struct opt *opts, int optcode, short *result);
|
extern int retropt_short(struct opt *opts, int optcode, short *result);
|
||||||
extern int retropt_ushort(struct opt *opts, int optcode, unsigned short *result);
|
extern int retropt_ushort(struct opt *opts, int optcode, unsigned short *result);
|
||||||
extern int retropt_int(struct opt *opts, int optcode, int *result);
|
extern int retropt_int(struct opt *opts, int optcode, int *result);
|
||||||
|
extern int retropt_2integrals(struct opt *opts, int optcode, union integral *value1, union integral *value2);
|
||||||
extern int retropt_uint(struct opt *opts, int optcode, unsigned int *result);
|
extern int retropt_uint(struct opt *opts, int optcode, unsigned int *result);
|
||||||
extern int retropt_long(struct opt *opts, int optcode, long *result);
|
extern int retropt_long(struct opt *opts, int optcode, long *result);
|
||||||
extern int retropt_ulong(struct opt *opts, int optcode, unsigned long *result);
|
extern int retropt_ulong(struct opt *opts, int optcode, unsigned long *result);
|
||||||
|
|
Loading…
Reference in a new issue