From 2c2508fc62d21a2bc8dd1cbbfd774c1c1cff30ce Mon Sep 17 00:00:00 2001 From: Gerhard Rieger Date: Fri, 2 May 2008 18:44:54 +0200 Subject: [PATCH] added address options: ioctl-void, ioctl-int, ioctl-intp, ioctl-bin, ioctl-string added option types: TYPE_INT_INT, TYPE_INT_BIN, TYPE_INT_STRING added syscall wrapper: Ioctl_int() added test: IOCTL_VOID --- CHANGES | 4 ++ EXAMPLES | 6 +++ VERSION | 2 +- sycls.c | 12 ++++- sycls.h | 4 +- test.sh | 58 +++++++++++++++++++++++ xio-fd.c | 9 +++- xio-fd.h | 7 ++- xio.h | 1 + xiohelp.c | 1 + xioopts.c | 137 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ xioopts.h | 11 ++++- 12 files changed, 246 insertions(+), 6 deletions(-) diff --git a/CHANGES b/CHANGES index e78e07a..d61235a 100644 --- a/CHANGES +++ b/CHANGES @@ -1,4 +1,8 @@ +new features: + added address options IOCTL-VOID, IOCTL-INT, IOCTL-INTP, IOCTL-STRING, + IOCTL-BIN for generic ioctl() calls. + corrections: some raw IP and UNIX datagram modes failed on BSD systems diff --git a/EXAMPLES b/EXAMPLES index 8dc0628..d528e4b 100644 --- a/EXAMPLES +++ b/EXAMPLES @@ -285,6 +285,12 @@ $ socat -d -d -d -d - udp-datagram:224.0.0.2:6666,bind=:6666,ip-add-membership=2 // packets leave via wrong interface (set route: ...) // socket bound to specific address +//============================================================================= +// IOCTL + +// open CD drive (given value valid on Linux) +$ socat /dev/cdrom,o-nonblock,ioctl-void=0x5309 - + =============================================================================== // not tested, just ideas, or have problems diff --git a/VERSION b/VERSION index 9a06c88..049994b 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock" +"1.6.0.1+ip4bind+recvfromfork+x64+execstderr+execspaces+cosmetics+poll+udplistencont+ignoreeofunblock+ioctl" diff --git a/sycls.c b/sycls.c index 347a7cb..6e4d37e 100644 --- a/sycls.c +++ b/sycls.c @@ -1,5 +1,5 @@ /* source: sycls.c */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* explicit system call and C library trace function, for those who miss strace @@ -593,6 +593,16 @@ int Ioctl(int d, int request, void *argp) { return retval; } +int Ioctl_int(int d, int request, int arg) { + int retval, _errno; + Debug3("ioctl(%d, 0x%x, %d)", d, request, arg); + retval = ioctl(d, request, arg); + _errno = errno; + Debug1("ioctl() -> %d", retval); + errno = _errno; + return retval; +} + int Close(int fd) { int retval, _errno; Info1("close(%d)", fd); diff --git a/sycls.h b/sycls.h index 8166d96..4f4d211 100644 --- a/sycls.h +++ b/sycls.h @@ -1,5 +1,5 @@ /* source: sycls.h */ -/* Copyright Gerhard Rieger 2001-2007 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __sycls_h_included @@ -64,6 +64,7 @@ int Ftruncate64(int fd, off64_t length); #endif /* HAVE_FTRUNCATE64 */ int Flock(int fd, int operation); int Ioctl(int d, int request, void *argp); +int Ioctl_int(int d, int request, int arg); int Close(int fd); int Fchown(int fd, uid_t owner, gid_t group); int Fchmod(int fd, mode_t mode); @@ -195,6 +196,7 @@ void Add_history(const char *string); #define Ftruncate64(f,l) ftruncate64(f,l) #define Flock(f,o) flock(f,o) #define Ioctl(d,r,a) ioctl(d,r,a) +#define Ioctl_int(d,r,a) ioctl(d,r,a) #define Close(f) close(f) #define Fchown(f,o,g) fchown(f,o,g) #define Fchmod(f,m) fchmod(f,m) diff --git a/test.sh b/test.sh index 5cbb3ec..7acd059 100755 --- a/test.sh +++ b/test.sh @@ -8447,6 +8447,64 @@ esac N=$((N+1)) +# test the generic ioctl-void option +NAME=IOCTL_VOID +case "$TESTS" in +*%functions%*|*%ip4%*|*%udp%*|*%dgram%*|*%$NAME%*) +TEST="$NAME: test the ioctl-void option" +# there are not many ioctls that apply to non global resources and do not +# require root. TIOCEXCL seems to fit: +# process 0 provides a pty; +# process 1 opens it with the TIOCEXCL ioctl; +# process 2 opens it too and fails with "device or resource busy" only when the +# previous ioctl was successful +if [ "$UNAME" != Linux ]; then + # we need access to more loopback addresses + $PRINTF "test $F_n $TEST... ${YELLOW}only on Linux${NORMAL}\n" $N + numCANT=$((numCANT+1)) +else +tp="$td/test$N.pty" +tf="$td/test$N.stdout" +te="$td/test$N.stderr" +tdiff="$td/test$N.diff" +da="$(date)" +CMD0="$SOCAT $opts PTY,LINK=$tp pipe" +CMD1="$SOCAT $opts - file:$tp,ioctl-void=0x540c,raw,echo=0" +CMD2="$SOCAT $opts - file:$tp,raw,echo=0" +printf "test $F_n $TEST... " $N +$CMD0 >/dev/null 2>"${te}0" & +pid0=$! +waitfile $tp 1 +(echo "$da"; sleep 2) |$CMD1 >"$tf" 2>"${te}1" & # this should always work +pid1=$! +usleep 1000000 +$CMD2 >/dev/null 2>"${te}2" /dev/null; wait +if ! echo "$da" |diff - "$tf"; then + $PRINTF "${YELLOW}phase 1 failed{NORMAL}\n" + echo "$CMD0 &" + echo "$CMD1" + numCANT=$((numCANT+1)) +elif [ $rc2 -eq 0 ]; then + $PRINTF "$FAILED: $SOCAT:\n" + echo "$CMD0 &" + echo "$CMD1" + echo "$CMD2" + cat "${te}0" "${te}1" "${te}2" + numFAIL=$((numFAIL+1)) +else + $PRINTF "$OK\n" + if [ -n "$debug" ]; then cat "${te}0" "${te}1" "${te}2"; fi + numOK=$((numOK+1)) +fi +fi # !Linux + ;; +esac +PORT=$((PORT+1)) +N=$((N+1)) + + echo "summary: $((N-1)) tests; $numOK ok, $numFAIL failed, $numCANT could not be performed" if [ "$numFAIL" -gt 0 ]; then diff --git a/xio-fd.c b/xio-fd.c index 142e5fe..a982005 100644 --- a/xio-fd.c +++ b/xio-fd.c @@ -1,5 +1,5 @@ /* source: xio-fd.c */ -/* Copyright Gerhard Rieger 2001-2006 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ /* this file contains common file descriptor related option definitions */ @@ -75,3 +75,10 @@ const struct optdesc opt_cool_write = { "cool-write", "coolwrite", OPT_COOL_WRIT /* control closing of connections */ const struct optdesc opt_end_close = { "end-close", "close", OPT_END_CLOSE, GROUP_FD, PH_INIT, TYPE_CONST, OFUNC_OFFSET, (bool)&((xiofile_t *)0)->stream.howtoend, END_CLOSE }; + +/****** generic ioctl() options ******/ +const struct optdesc opt_ioctl_void = { "ioctl-void", "ioctl", OPT_IOCTL_VOID, GROUP_FD, PH_FD, TYPE_INT, OFUNC_SPEC, 0, 0, 0 }; +const struct optdesc opt_ioctl_int = { "ioctl-int", NULL, OPT_IOCTL_INT, GROUP_FD, PH_FD, TYPE_INT_INT, OFUNC_SPEC, 0, 0, 0 }; +const struct optdesc opt_ioctl_intp = { "ioctl-intp", NULL, OPT_IOCTL_INTP, GROUP_FD, PH_FD, TYPE_INT_INT, OFUNC_SPEC, 0, 0, 0 }; +const struct optdesc opt_ioctl_bin = { "ioctl-bin", NULL, OPT_IOCTL_BIN, GROUP_FD, PH_FD, TYPE_INT_BIN, OFUNC_SPEC, 0, 0, 0 }; +const struct optdesc opt_ioctl_string = { "ioctl-string",NULL, OPT_IOCTL_STRING,GROUP_FD, PH_FD, TYPE_INT_STRING,OFUNC_SPEC, 0, 0, 0 }; diff --git a/xio-fd.h b/xio-fd.h index 25b25d0..a301b4e 100644 --- a/xio-fd.h +++ b/xio-fd.h @@ -1,10 +1,15 @@ /* source: xio-fd.h */ -/* Copyright Gerhard Rieger 2001-2006 */ +/* Copyright Gerhard Rieger 2001-2008 */ /* Published under the GNU General Public License V.2, see file COPYING */ #ifndef __xio_fd_h_included #define __xio_fd_h_included 1 +extern const struct optdesc opt_ioctl_void; +extern const struct optdesc opt_ioctl_int; +extern const struct optdesc opt_ioctl_intp; +extern const struct optdesc opt_ioctl_bin; +extern const struct optdesc opt_ioctl_string; extern const struct optdesc opt_append; extern const struct optdesc opt_nonblock; extern const struct optdesc opt_o_ndelay; diff --git a/xio.h b/xio.h index f5b1926..ddfdd6b 100644 --- a/xio.h +++ b/xio.h @@ -355,6 +355,7 @@ union integral { struct opt { const struct optdesc *desc; union integral value; + union integral value2; } ; extern const char *PIPESEP; diff --git a/xiohelp.c b/xiohelp.c index a994cdb..4ed9323 100644 --- a/xiohelp.c +++ b/xiohelp.c @@ -29,6 +29,7 @@ static const char *optiontypenames[] = { "STRUCT-IP_MREQ", #endif "IP4NAME", + "INT:INT", "INT:BIN", "INT:STRING", } ; diff --git a/xioopts.c b/xioopts.c index ad76434..3d7b53e 100644 --- a/xioopts.c +++ b/xioopts.c @@ -583,6 +583,12 @@ const struct optname optionnames[] = { IF_RETRY ("interval", &opt_intervall) IF_RETRY ("intervall", &opt_intervall) IF_TERMIOS("intr", &opt_vintr) + IF_ANY ("ioctl", &opt_ioctl_void) + IF_ANY ("ioctl-bin", &opt_ioctl_bin) + IF_ANY ("ioctl-int", &opt_ioctl_int) + IF_ANY ("ioctl-intp", &opt_ioctl_intp) + IF_ANY ("ioctl-string", &opt_ioctl_string) + IF_ANY ("ioctl-void", &opt_ioctl_void) #ifdef IP_ADD_MEMBERSHIP IF_IP ("ip-add-membership", &opt_ip_add_membership) #endif @@ -1967,6 +1973,71 @@ int parseopts_table(const char **a, unsigned int groups, struct opt **opts, (*opts)[i].value.u_linger.l_linger); break; #endif /* HAVE_STRUCT_LINGER */ + case TYPE_INT_INT: + if (!assign) { + Error1("option \"%s\": values required", a0); + continue; + } + { + char *rest; + (*opts)[i].value.u_int = strtoul(token, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 2 arguments required", + ent->desc->defname); + } + ++rest; + (*opts)[i].value2.u_int = strtoul(rest, &rest, 0); + } + Info3("setting option \"%s\" to %d:%d", ent->desc->defname, + (*opts)[i].value.u_int, (*opts)[i].value2.u_int); + break; + case TYPE_INT_BIN: + if (!assign) { + Error1("option \"%s\": values required", a0); + continue; + } + { + char *rest; + (*opts)[i].value.u_int = strtoul(token, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 2 arguments required", + ent->desc->defname); + } + ++rest; + optlen = 0; + if ((result = dalan(rest, optbuf, &optlen, sizeof(optbuf))) != 0) { + Error1("parseopts(): problem with \"%s\" data", rest); + continue; + } + if (((*opts)[i].value2.u_bin.b_data = memdup(optbuf, optlen)) == NULL) { + Error1("memdup(, "F_Zu"): out of memory", optlen); + return -1; + } + (*opts)[i].value2.u_bin.b_len = optlen; + } + Info2("setting option \"%s\" to %d:..."/*!!!*/, ent->desc->defname, + (*opts)[i].value.u_int); + break; + case TYPE_INT_STRING: + if (!assign) { + Error1("option \"%s\": values required", a0); + continue; + } + { + char *rest; + (*opts)[i].value.u_int = strtoul(token, &rest, 0); + if (*rest != ':') { + Error1("option \"%s\": 2 arguments required", + ent->desc->defname); + } + ++rest; + if (((*opts)[i].value2.u_string = strdup(token)) == NULL) { + Error("out of memory"); return -1; + } + } + Info3("setting option \"%s\" to %d:\"$s\"", ent->desc->defname, + (*opts)[i].value.u_int, (*opts)[i].value2.u_string); + break; #if defined(HAVE_STRUCT_IP_MREQ) || defined (HAVE_STRUCT_IP_MREQN) case TYPE_IP_MREQN: { @@ -2998,6 +3069,72 @@ int applyopts(int fd, struct opt *opts, unsigned int phase) { } } break; + case OPT_IOCTL_VOID: + switch (opt->desc->type) { + case TYPE_INT: + if (Ioctl(fd, opt->value.u_int, NULL) < 0) { + Error3("ioctl(%d, 0x%x, NULL): %s", + fd, opt->value.u_int, strerror(errno)); + opt->desc = ODESC_ERROR; ++opt; continue; + } + break; + default: + Error1("ioctl() data type %d not implemented", opt->desc->type); + } + break; + case OPT_IOCTL_INT: + switch (opt->desc->type) { + case TYPE_INT_INT: + if (Ioctl_int(fd, opt->value.u_int, opt->value2.u_int) < 0) { + Error4("ioctl(%d, %d, %p): %s", + fd, opt->value.u_int, opt->value2.u_int, strerror(errno)); + opt->desc = ODESC_ERROR; ++opt; continue; + } + break; + default: + Error1("ioctl() data type %d not implemented", opt->desc->type); + } + break; + case OPT_IOCTL_INTP: + switch (opt->desc->type) { + case TYPE_INT_INT: + if (Ioctl(fd, opt->value.u_int, (void *)&opt->value2.u_int) < 0) { + Error4("ioctl(%d, 0x%x, %p): %s", + fd, opt->value.u_int, (void *)&opt->value2.u_int, strerror(errno)); + opt->desc = ODESC_ERROR; ++opt; continue; + } + break; + default: + Error1("ioctl() data type %d not implemented", opt->desc->type); + } + break; + case OPT_IOCTL_BIN: + switch (opt->desc->type) { + case TYPE_INT_BIN: + if (Ioctl(fd, opt->value.u_int, (void *)opt->value2.u_bin.b_data) < 0) { + Error4("ioctl(%d, 0x%x, %p): %s", + fd, opt->value.u_int, (void *)opt->value2.u_bin.b_data, strerror(errno)); + opt->desc = ODESC_ERROR; ++opt; continue; + } + break; + default: + Error1("ioctl() data type %d not implemented", opt->desc->type); + } + break; + case OPT_IOCTL_STRING: + switch (opt->desc->type) { + case TYPE_INT_STRING: + if (Ioctl(fd, opt->value.u_int, (void *)opt->value2.u_string) < 0) { + Error4("ioctl(%d, 0x%x, %p): %s", + fd, opt->value.u_int, (void *)opt->value2.u_string, strerror(errno)); + opt->desc = ODESC_ERROR; ++opt; continue; + } + break; + default: + Error1("ioctl() data type %d not implemented", opt->desc->type); + } + break; + default: Error1("applyopts(): option \"%s\" not implemented", opt->desc->defname); opt->desc = ODESC_ERROR; ++opt; continue; diff --git a/xioopts.h b/xioopts.h index 849cb46..0981e45 100644 --- a/xioopts.h +++ b/xioopts.h @@ -53,6 +53,9 @@ enum e_types { TYPE_IP_MREQN, /* for struct ip_mreq or struct ip_mreqn */ #endif TYPE_IP4NAME, /* IPv4 hostname or address */ + TYPE_INT_INT, /* 2 parameters: first is int, second is int */ + TYPE_INT_BIN, /* 2 parameters: first is int, second is binary */ + TYPE_INT_STRING, /* 2 parameters: first is int, second is req string */ TYPE_2BYTE = TYPE_USHORT } ; @@ -64,7 +67,8 @@ enum e_func { OFUNC_SEEK32, /* lseek(): arg1 is whence (SEEK_SET etc.) */ OFUNC_SEEK64, /* lseek64(): arg1 is whence (SEEK_SET etc.) */ OFUNC_FCNTL, /* fcntl(, ): arg1 is cmd */ - OFUNC_IOCTL, /* ioctl(): arg1 is request */ + OFUNC_IOCTL, /* ioctl(): arg1 of option description is request, arg2 + is int setrequest */ OFUNC_IOCTL_MASK_LONG, /* arg1 is getrequest, arg2 is setrequest: ioctl(arg1, ); |= arg3; ioctl(arg2, ); */ OFUNC_SOCKOPT, /* setsockopt() */ @@ -329,6 +333,11 @@ enum e_optcode { #if 0 /* see Linux: man 7 netlink; probably not what we need yet */ OPT_IO_SIOCGIFNAME, #endif + OPT_IOCTL_BIN, /* generic ioctl with binary value (pointed to) */ + OPT_IOCTL_INT, /* generic ioctl with integer value */ + OPT_IOCTL_INTP, /* generic ioctl with integer value (pointed to) */ + OPT_IOCTL_STRING, /* generic ioctl with integer value (pointed to) */ + OPT_IOCTL_VOID, /* generic ioctl without value */ OPT_IP_ADD_MEMBERSHIP, #ifdef IP_HDRINCL OPT_IP_HDRINCL,