| /* |
| * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey <jrs@world.std.com> |
| * Copyright (c) 1996-1999 Wichert Akkerman <wichert@cistron.nl> |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote products |
| * derived from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
| * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
| * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
| * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * $Id$ |
| */ |
| |
| #include "defs.h" |
| #include <sys/syscall.h> |
| |
| #ifdef HAVE_POLL_H |
| #include <poll.h> |
| #endif |
| #ifdef HAVE_SYS_POLL_H |
| #include <sys/poll.h> |
| #endif |
| #ifdef HAVE_STROPTS_H |
| #include <stropts.h> |
| #endif |
| #ifdef HAVE_SYS_CONF_H |
| #include <sys/conf.h> |
| #endif |
| #ifdef HAVE_SYS_STREAM_H |
| #include <sys/stream.h> |
| #endif |
| #ifdef HAVE_SYS_TIHDR_H |
| #include <sys/tihdr.h> |
| #endif |
| |
| #if defined(HAVE_SYS_STREAM_H) || defined(LINUX) || defined(FREEBSD) |
| |
| #ifndef HAVE_STROPTS_H |
| #define RS_HIPRI 1 |
| struct strbuf { |
| int maxlen; /* no. of bytes in buffer */ |
| int len; /* no. of bytes returned */ |
| const char *buf; /* pointer to data */ |
| }; |
| #define MORECTL 1 |
| #define MOREDATA 2 |
| #endif /* !HAVE_STROPTS_H */ |
| |
| #ifdef HAVE_SYS_TIUSER_H |
| #include <sys/tiuser.h> |
| #include <sys/sockmod.h> |
| #include <sys/timod.h> |
| #endif /* HAVE_SYS_TIUSER_H */ |
| |
| #ifndef FREEBSD |
| static const struct xlat msgflags[] = { |
| { RS_HIPRI, "RS_HIPRI" }, |
| { 0, NULL }, |
| }; |
| |
| |
| static void |
| printstrbuf(struct tcb *tcp, struct strbuf *sbp, int getting) |
| { |
| if (sbp->maxlen == -1 && getting) |
| tprints("{maxlen=-1}"); |
| else { |
| tprints("{"); |
| if (getting) |
| tprintf("maxlen=%d, ", sbp->maxlen); |
| tprintf("len=%d, buf=", sbp->len); |
| printstr(tcp, (unsigned long) sbp->buf, sbp->len); |
| tprints("}"); |
| } |
| } |
| |
| static void |
| printstrbufarg(struct tcb *tcp, int arg, int getting) |
| { |
| struct strbuf buf; |
| |
| if (arg == 0) |
| tprints("NULL"); |
| else if (umove(tcp, arg, &buf) < 0) |
| tprints("{...}"); |
| else |
| printstrbuf(tcp, &buf, getting); |
| tprints(", "); |
| } |
| |
| int |
| sys_putmsg(struct tcb *tcp) |
| { |
| int i; |
| |
| if (entering(tcp)) { |
| /* fd */ |
| tprintf("%ld, ", tcp->u_arg[0]); |
| /* control and data */ |
| for (i = 1; i < 3; i++) |
| printstrbufarg(tcp, tcp->u_arg[i], 0); |
| /* flags */ |
| printflags(msgflags, tcp->u_arg[3], "RS_???"); |
| } |
| return 0; |
| } |
| |
| #if defined(SPARC) || defined(SPARC64) || defined(SUNOS4) || defined(SVR4) |
| int |
| sys_getmsg(struct tcb *tcp) |
| { |
| int i, flags; |
| |
| if (entering(tcp)) { |
| /* fd */ |
| tprintf("%lu, ", tcp->u_arg[0]); |
| } else { |
| if (syserror(tcp)) { |
| tprintf("%#lx, %#lx, %#lx", |
| tcp->u_arg[1], tcp->u_arg[2], tcp->u_arg[3]); |
| return 0; |
| } |
| /* control and data */ |
| for (i = 1; i < 3; i++) |
| printstrbufarg(tcp, tcp->u_arg[i], 1); |
| /* pointer to flags */ |
| if (tcp->u_arg[3] == 0) |
| tprints("NULL"); |
| else if (umove(tcp, tcp->u_arg[3], &flags) < 0) |
| tprints("[?]"); |
| else { |
| tprints("["); |
| printflags(msgflags, flags, "RS_???"); |
| tprints("]"); |
| } |
| /* decode return value */ |
| switch (tcp->u_rval) { |
| case MORECTL: |
| tcp->auxstr = "MORECTL"; |
| break; |
| case MORECTL|MOREDATA: |
| tcp->auxstr = "MORECTL|MOREDATA"; |
| break; |
| case MOREDATA: |
| tcp->auxstr = "MORECTL"; |
| break; |
| default: |
| tcp->auxstr = NULL; |
| break; |
| } |
| } |
| return RVAL_HEX | RVAL_STR; |
| } |
| #endif /* SPARC || SPARC64 || SUNOS4 || SVR4 */ |
| |
| #if defined SYS_putpmsg || defined SYS_getpmsg |
| static const struct xlat pmsgflags[] = { |
| #ifdef MSG_HIPRI |
| { MSG_HIPRI, "MSG_HIPRI" }, |
| #endif |
| #ifdef MSG_AND |
| { MSG_ANY, "MSG_ANY" }, |
| #endif |
| #ifdef MSG_BAND |
| { MSG_BAND, "MSG_BAND" }, |
| #endif |
| { 0, NULL }, |
| }; |
| #endif |
| |
| #ifdef SYS_putpmsg |
| int |
| sys_putpmsg(struct tcb *tcp) |
| { |
| int i; |
| |
| if (entering(tcp)) { |
| /* fd */ |
| tprintf("%ld, ", tcp->u_arg[0]); |
| /* control and data */ |
| for (i = 1; i < 3; i++) |
| printstrbufarg(tcp, tcp->u_arg[i], 0); |
| /* band */ |
| tprintf("%ld, ", tcp->u_arg[3]); |
| /* flags */ |
| printflags(pmsgflags, tcp->u_arg[4], "MSG_???"); |
| } |
| return 0; |
| } |
| #endif /* SYS_putpmsg */ |
| |
| #ifdef SYS_getpmsg |
| int |
| sys_getpmsg(struct tcb *tcp) |
| { |
| int i, flags; |
| |
| if (entering(tcp)) { |
| /* fd */ |
| tprintf("%lu, ", tcp->u_arg[0]); |
| } else { |
| if (syserror(tcp)) { |
| tprintf("%#lx, %#lx, %#lx, %#lx", tcp->u_arg[1], |
| tcp->u_arg[2], tcp->u_arg[3], tcp->u_arg[4]); |
| return 0; |
| } |
| /* control and data */ |
| for (i = 1; i < 3; i++) |
| printstrbufarg(tcp, tcp->u_arg[i], 1); |
| /* pointer to band */ |
| printnum(tcp, tcp->u_arg[3], "%d"); |
| tprints(", "); |
| /* pointer to flags */ |
| if (tcp->u_arg[4] == 0) |
| tprints("NULL"); |
| else if (umove(tcp, tcp->u_arg[4], &flags) < 0) |
| tprints("[?]"); |
| else { |
| tprints("["); |
| printflags(pmsgflags, flags, "MSG_???"); |
| tprints("]"); |
| } |
| /* decode return value */ |
| switch (tcp->u_rval) { |
| case MORECTL: |
| tcp->auxstr = "MORECTL"; |
| break; |
| case MORECTL|MOREDATA: |
| tcp->auxstr = "MORECTL|MOREDATA"; |
| break; |
| case MOREDATA: |
| tcp->auxstr = "MORECTL"; |
| break; |
| default: |
| tcp->auxstr = NULL; |
| break; |
| } |
| } |
| return RVAL_HEX | RVAL_STR; |
| } |
| #endif /* SYS_getpmsg */ |
| |
| #endif /* !FREEBSD */ |
| |
| |
| #ifdef HAVE_SYS_POLL_H |
| |
| static const struct xlat pollflags[] = { |
| #ifdef POLLIN |
| { POLLIN, "POLLIN" }, |
| { POLLPRI, "POLLPRI" }, |
| { POLLOUT, "POLLOUT" }, |
| #ifdef POLLRDNORM |
| { POLLRDNORM, "POLLRDNORM" }, |
| #endif |
| #ifdef POLLWRNORM |
| { POLLWRNORM, "POLLWRNORM" }, |
| #endif |
| #ifdef POLLRDBAND |
| { POLLRDBAND, "POLLRDBAND" }, |
| #endif |
| #ifdef POLLWRBAND |
| { POLLWRBAND, "POLLWRBAND" }, |
| #endif |
| { POLLERR, "POLLERR" }, |
| { POLLHUP, "POLLHUP" }, |
| { POLLNVAL, "POLLNVAL" }, |
| #endif |
| { 0, NULL }, |
| }; |
| |
| static int |
| decode_poll(struct tcb *tcp, long pts) |
| { |
| struct pollfd fds; |
| unsigned nfds; |
| unsigned long size, start, cur, end, abbrev_end; |
| int failed = 0; |
| |
| if (entering(tcp)) { |
| nfds = tcp->u_arg[1]; |
| size = sizeof(fds) * nfds; |
| start = tcp->u_arg[0]; |
| end = start + size; |
| if (nfds == 0 || size / sizeof(fds) != nfds || end < start) { |
| tprintf("%#lx, %d, ", |
| tcp->u_arg[0], nfds); |
| return 0; |
| } |
| if (abbrev(tcp)) { |
| abbrev_end = start + max_strlen * sizeof(fds); |
| if (abbrev_end < start) |
| abbrev_end = end; |
| } else { |
| abbrev_end = end; |
| } |
| tprints("["); |
| for (cur = start; cur < end; cur += sizeof(fds)) { |
| if (cur > start) |
| tprints(", "); |
| if (cur >= abbrev_end) { |
| tprints("..."); |
| break; |
| } |
| if (umoven(tcp, cur, sizeof fds, (char *) &fds) < 0) { |
| tprints("?"); |
| failed = 1; |
| break; |
| } |
| if (fds.fd < 0) { |
| tprintf("{fd=%d}", fds.fd); |
| continue; |
| } |
| tprints("{fd="); |
| printfd(tcp, fds.fd); |
| tprints(", events="); |
| printflags(pollflags, fds.events, "POLL???"); |
| tprints("}"); |
| } |
| tprints("]"); |
| if (failed) |
| tprintf(" %#lx", start); |
| tprintf(", %d, ", nfds); |
| return 0; |
| } else { |
| static char outstr[1024]; |
| char *outptr; |
| #define end_outstr (outstr + sizeof(outstr)) |
| const char *flagstr; |
| |
| if (syserror(tcp)) |
| return 0; |
| if (tcp->u_rval == 0) { |
| tcp->auxstr = "Timeout"; |
| return RVAL_STR; |
| } |
| |
| nfds = tcp->u_arg[1]; |
| size = sizeof(fds) * nfds; |
| start = tcp->u_arg[0]; |
| end = start + size; |
| if (nfds == 0 || size / sizeof(fds) != nfds || end < start) |
| return 0; |
| if (abbrev(tcp)) { |
| abbrev_end = start + max_strlen * sizeof(fds); |
| if (abbrev_end < start) |
| abbrev_end = end; |
| } else { |
| abbrev_end = end; |
| } |
| |
| outptr = outstr; |
| |
| for (cur = start; cur < end; cur += sizeof(fds)) { |
| if (umoven(tcp, cur, sizeof fds, (char *) &fds) < 0) { |
| if (outptr < end_outstr - 2) |
| *outptr++ = '?'; |
| failed = 1; |
| break; |
| } |
| if (!fds.revents) |
| continue; |
| if (outptr == outstr) { |
| *outptr++ = '['; |
| } else { |
| if (outptr < end_outstr - 3) |
| outptr = stpcpy(outptr, ", "); |
| } |
| if (cur >= abbrev_end) { |
| if (outptr < end_outstr - 4) |
| outptr = stpcpy(outptr, "..."); |
| break; |
| } |
| if (outptr < end_outstr - (sizeof("{fd=%d, revents=") + sizeof(int)*3) + 1) |
| outptr += sprintf(outptr, "{fd=%d, revents=", fds.fd); |
| flagstr = sprintflags("", pollflags, fds.revents); |
| if (outptr < end_outstr - (strlen(flagstr) + 2)) { |
| outptr = stpcpy(outptr, flagstr); |
| *outptr++ = '}'; |
| } |
| } |
| if (failed) |
| return 0; |
| |
| if (outptr != outstr /* && outptr < end_outstr - 1 (always true)*/) |
| *outptr++ = ']'; |
| |
| *outptr = '\0'; |
| if (pts) { |
| if (outptr < end_outstr - 128) { |
| outptr = stpcpy(outptr, outptr == outstr ? "left " : ", left "); |
| sprint_timespec(outptr, tcp, pts); |
| } |
| } |
| |
| if (outptr == outstr) |
| return 0; |
| |
| tcp->auxstr = outstr; |
| return RVAL_STR; |
| #undef end_outstr |
| } |
| } |
| |
| int |
| sys_poll(struct tcb *tcp) |
| { |
| int rc = decode_poll(tcp, 0); |
| if (entering(tcp)) { |
| #ifdef INFTIM |
| if (tcp->u_arg[2] == INFTIM) |
| tprints("INFTIM"); |
| else |
| #endif |
| tprintf("%ld", tcp->u_arg[2]); |
| } |
| return rc; |
| } |
| |
| #ifdef LINUX |
| int |
| sys_ppoll(struct tcb *tcp) |
| { |
| int rc = decode_poll(tcp, tcp->u_arg[2]); |
| if (entering(tcp)) { |
| print_timespec(tcp, tcp->u_arg[2]); |
| tprints(", "); |
| print_sigset(tcp, tcp->u_arg[3], 0); |
| tprintf(", %lu", tcp->u_arg[4]); |
| } |
| return rc; |
| } |
| #endif |
| |
| #else /* !HAVE_SYS_POLL_H */ |
| int |
| sys_poll(struct tcb *tcp) |
| { |
| return 0; |
| } |
| #endif |
| |
| #if !defined(LINUX) && !defined(FREEBSD) |
| |
| static const struct xlat stream_flush_options[] = { |
| { FLUSHR, "FLUSHR" }, |
| { FLUSHW, "FLUSHW" }, |
| { FLUSHRW, "FLUSHRW" }, |
| #ifdef FLUSHBAND |
| { FLUSHBAND, "FLUSHBAND" }, |
| #endif |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat stream_setsig_flags[] = { |
| { S_INPUT, "S_INPUT" }, |
| { S_HIPRI, "S_HIPRI" }, |
| { S_OUTPUT, "S_OUTPUT" }, |
| { S_MSG, "S_MSG" }, |
| #ifdef S_ERROR |
| { S_ERROR, "S_ERROR" }, |
| #endif |
| #ifdef S_HANGUP |
| { S_HANGUP, "S_HANGUP" }, |
| #endif |
| #ifdef S_RDNORM |
| { S_RDNORM, "S_RDNORM" }, |
| #endif |
| #ifdef S_WRNORM |
| { S_WRNORM, "S_WRNORM" }, |
| #endif |
| #ifdef S_RDBAND |
| { S_RDBAND, "S_RDBAND" }, |
| #endif |
| #ifdef S_WRBAND |
| { S_WRBAND, "S_WRBAND" }, |
| #endif |
| #ifdef S_BANDURG |
| { S_BANDURG, "S_BANDURG" }, |
| #endif |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat stream_read_options[] = { |
| { RNORM, "RNORM" }, |
| { RMSGD, "RMSGD" }, |
| { RMSGN, "RMSGN" }, |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat stream_read_flags[] = { |
| #ifdef RPROTDAT |
| { RPROTDAT, "RPROTDAT" }, |
| #endif |
| #ifdef RPROTDIS |
| { RPROTDIS, "RPROTDIS" }, |
| #endif |
| #ifdef RPROTNORM |
| { RPROTNORM, "RPROTNORM" }, |
| #endif |
| { 0, NULL }, |
| }; |
| |
| #ifndef RMODEMASK |
| #define RMODEMASK (~0) |
| #endif |
| |
| #ifdef I_SWROPT |
| static const struct xlat stream_write_flags[] = { |
| { SNDZERO, "SNDZERO" }, |
| { SNDPIPE, "SNDPIPE" }, |
| { 0, NULL }, |
| }; |
| #endif /* I_SWROPT */ |
| |
| #ifdef I_ATMARK |
| static const struct xlat stream_atmark_options[] = { |
| { ANYMARK, "ANYMARK" }, |
| { LASTMARK, "LASTMARK" }, |
| { 0, NULL }, |
| }; |
| #endif /* I_ATMARK */ |
| |
| #ifdef TI_BIND |
| static const struct xlat transport_user_options[] = { |
| { T_CONN_REQ, "T_CONN_REQ" }, |
| { T_CONN_RES, "T_CONN_RES" }, |
| { T_DISCON_REQ, "T_DISCON_REQ" }, |
| { T_DATA_REQ, "T_DATA_REQ" }, |
| { T_EXDATA_REQ, "T_EXDATA_REQ" }, |
| { T_INFO_REQ, "T_INFO_REQ" }, |
| { T_BIND_REQ, "T_BIND_REQ" }, |
| { T_UNBIND_REQ, "T_UNBIND_REQ" }, |
| { T_UNITDATA_REQ,"T_UNITDATA_REQ"}, |
| { T_OPTMGMT_REQ,"T_OPTMGMT_REQ" }, |
| { T_ORDREL_REQ, "T_ORDREL_REQ" }, |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat transport_user_flags[] = { |
| { 0, "0" }, |
| { T_MORE, "T_MORE" }, |
| { T_EXPEDITED, "T_EXPEDITED" }, |
| { T_NEGOTIATE, "T_NEGOTIATE" }, |
| { T_CHECK, "T_CHECK" }, |
| { T_DEFAULT, "T_DEFAULT" }, |
| { T_SUCCESS, "T_SUCCESS" }, |
| { T_FAILURE, "T_FAILURE" }, |
| { T_CURRENT, "T_CURRENT" }, |
| { T_PARTSUCCESS,"T_PARTSUCCESS" }, |
| { T_READONLY, "T_READONLY" }, |
| { T_NOTSUPPORT, "T_NOTSUPPORT" }, |
| { 0, NULL }, |
| }; |
| |
| |
| #ifdef HAVE_STRUCT_T_OPTHDR |
| |
| static const struct xlat xti_level[] = { |
| { XTI_GENERIC, "XTI_GENERIC" }, |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat xti_generic[] = { |
| { XTI_DEBUG, "XTI_DEBUG" }, |
| { XTI_LINGER, "XTI_LINGER" }, |
| { XTI_RCVBUF, "XTI_RCVBUF" }, |
| { XTI_RCVLOWAT, "XTI_RCVLOWAT" }, |
| { XTI_SNDBUF, "XTI_SNDBUF" }, |
| { XTI_SNDLOWAT, "XTI_SNDLOWAT" }, |
| { 0, NULL }, |
| }; |
| |
| |
| |
| void |
| print_xti_optmgmt(struct tcb *tcp, long addr, int len) |
| { |
| int c = 0; |
| struct t_opthdr hdr; |
| |
| while (len >= (int) sizeof hdr) { |
| if (umove(tcp, addr, &hdr) < 0) break; |
| if (c++) { |
| tprints(", "); |
| } |
| else if (len > hdr.len + sizeof hdr) { |
| tprints("["); |
| } |
| tprints("{level="); |
| printxval(xti_level, hdr.level, "???"); |
| tprints(", name="); |
| switch (hdr.level) { |
| case XTI_GENERIC: |
| printxval(xti_generic, hdr.name, "XTI_???"); |
| break; |
| default: |
| tprintf("%ld", hdr.name); |
| break; |
| } |
| tprints(", status="); |
| printxval(transport_user_flags, hdr.status, "T_???"); |
| addr += sizeof hdr; |
| len -= sizeof hdr; |
| if ((hdr.len -= sizeof hdr) > 0) { |
| if (hdr.len > len) break; |
| tprints(", val="); |
| if (len == sizeof(int)) |
| printnum(tcp, addr, "%d"); |
| else |
| printstr(tcp, addr, hdr.len); |
| addr += hdr.len; |
| len -= hdr.len; |
| } |
| tprints("}"); |
| } |
| if (len > 0) { |
| if (c++) tprints(", "); |
| printstr(tcp, addr, len); |
| } |
| if (c > 1) tprints("]"); |
| } |
| |
| #endif |
| |
| |
| static void |
| print_optmgmt(struct tcb *tcp, long addr, int len) |
| { |
| /* We don't know how to tell if TLI (socket) or XTI |
| optmgmt is being used yet, assume TLI. */ |
| #if defined (HAVE_STRUCT_OPTHDR) |
| print_sock_optmgmt(tcp, addr, len); |
| #elif defined (HAVE_STRUCT_T_OPTHDR) |
| print_xti_optmgmt(tcp, addr, len); |
| #else |
| printstr(tcp, addr, len); |
| #endif |
| } |
| |
| |
| |
| |
| static const struct xlat service_type[] = { |
| { T_COTS, "T_COTS" }, |
| { T_COTS_ORD, "T_COTS_ORD" }, |
| { T_CLTS, "T_CLTS" }, |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat ts_state[] = { |
| { TS_UNBND, "TS_UNBND" }, |
| { TS_WACK_BREQ, "TS_WACK_BREQ" }, |
| { TS_WACK_UREQ, "TS_WACK_UREQ" }, |
| { TS_IDLE, "TS_IDLE" }, |
| { TS_WACK_OPTREQ,"TS_WACK_OPTREQ"}, |
| { TS_WACK_CREQ, "TS_WACK_CREQ" }, |
| { TS_WCON_CREQ, "TS_WCON_CREQ" }, |
| { TS_WRES_CIND, "TS_WRES_CIND" }, |
| { TS_WACK_CRES, "TS_WACK_CRES" }, |
| { TS_DATA_XFER, "TS_DATA_XFER" }, |
| { TS_WIND_ORDREL,"TS_WIND_ORDREL"}, |
| { TS_WREQ_ORDREL,"TS_WREQ_ORDREL"}, |
| { TS_WACK_DREQ6,"TS_WACK_DREQ6" }, |
| { TS_WACK_DREQ7,"TS_WACK_DREQ7" }, |
| { TS_WACK_DREQ9,"TS_WACK_DREQ9" }, |
| { TS_WACK_DREQ10,"TS_WACK_DREQ10"}, |
| { TS_WACK_DREQ11,"TS_WACK_DREQ11"}, |
| { 0, NULL }, |
| }; |
| |
| static const struct xlat provider_flags[] = { |
| { 0, "0" }, |
| { SENDZERO, "SENDZERO" }, |
| { EXPINLINE, "EXPINLINE" }, |
| { XPG4_1, "XPG4_1" }, |
| { 0, NULL }, |
| }; |
| |
| |
| static const struct xlat tli_errors[] = { |
| { TBADADDR, "TBADADDR" }, |
| { TBADOPT, "TBADOPT" }, |
| { TACCES, "TACCES" }, |
| { TBADF, "TBADF" }, |
| { TNOADDR, "TNOADDR" }, |
| { TOUTSTATE, "TOUTSTATE" }, |
| { TBADSEQ, "TBADSEQ" }, |
| { TSYSERR, "TSYSERR" }, |
| { TLOOK, "TLOOK" }, |
| { TBADDATA, "TBADDATA" }, |
| { TBUFOVFLW, "TBUFOVFLW" }, |
| { TFLOW, "TFLOW" }, |
| { TNODATA, "TNODATA" }, |
| { TNODIS, "TNODIS" }, |
| { TNOUDERR, "TNOUDERR" }, |
| { TBADFLAG, "TBADFLAG" }, |
| { TNOREL, "TNOREL" }, |
| { TNOTSUPPORT, "TNOTSUPPORT" }, |
| { TSTATECHNG, "TSTATECHNG" }, |
| { TNOSTRUCTYPE, "TNOSTRUCTYPE" }, |
| { TBADNAME, "TBADNAME" }, |
| { TBADQLEN, "TBADQLEN" }, |
| { TADDRBUSY, "TADDRBUSY" }, |
| { TINDOUT, "TINDOUT" }, |
| { TPROVMISMATCH,"TPROVMISMATCH" }, |
| { TRESQLEN, "TRESQLEN" }, |
| { TRESADDR, "TRESADDR" }, |
| { TQFULL, "TQFULL" }, |
| { TPROTO, "TPROTO" }, |
| { 0, NULL }, |
| }; |
| |
| |
| static int |
| print_transport_message(struct tcb *tcp, int expect, long addr, int len) |
| { |
| union T_primitives m; |
| int c = 0; |
| |
| if (len < sizeof m.type) goto dump; |
| |
| if (umove(tcp, addr, &m.type) < 0) goto dump; |
| |
| #define GET(type, struct) \ |
| do { \ |
| if (len < sizeof m.struct) goto dump; \ |
| if (umove(tcp, addr, &m.struct) < 0) goto dump; \ |
| tprints("{"); \ |
| if (expect != type) { \ |
| ++c; \ |
| tprints(#type); \ |
| } \ |
| } \ |
| while (0) |
| |
| #define COMMA() \ |
| do { if (c++) tprints(", "); } while (0) |
| |
| |
| #define STRUCT(struct, elem, print) \ |
| do { \ |
| COMMA(); \ |
| if (m.struct.elem##_length < 0 || \ |
| m.struct.elem##_offset < sizeof m.struct || \ |
| m.struct.elem##_offset + m.struct.elem##_length > len) \ |
| { \ |
| tprintf(#elem "_length=%ld, " #elem "_offset=%ld",\ |
| m.struct.elem##_length, \ |
| m.struct.elem##_offset); \ |
| } \ |
| else { \ |
| tprints(#elem "="); \ |
| print(tcp, \ |
| addr + m.struct.elem##_offset, \ |
| m.struct.elem##_length); \ |
| } \ |
| } \ |
| while (0) |
| |
| #define ADDR(struct, elem) STRUCT(struct, elem, printstr) |
| |
| switch (m.type) { |
| #ifdef T_CONN_REQ |
| case T_CONN_REQ: /* connect request */ |
| GET(T_CONN_REQ, conn_req); |
| ADDR(conn_req, DEST); |
| ADDR(conn_req, OPT); |
| break; |
| #endif |
| #ifdef T_CONN_RES |
| case T_CONN_RES: /* connect response */ |
| GET(T_CONN_RES, conn_res); |
| #ifdef HAVE_STRUCT_T_CONN_RES_QUEUE_PTR |
| COMMA(); |
| tprintf("QUEUE=%p", m.conn_res.QUEUE_ptr); |
| #elif defined HAVE_STRUCT_T_CONN_RES_ACCEPTOR_ID |
| COMMA(); |
| tprintf("ACCEPTOR=%#lx", m.conn_res.ACCEPTOR_id); |
| #endif |
| ADDR(conn_res, OPT); |
| COMMA(); |
| tprintf("SEQ=%ld", m.conn_res.SEQ_number); |
| break; |
| #endif |
| #ifdef T_DISCON_REQ |
| case T_DISCON_REQ: /* disconnect request */ |
| GET(T_DISCON_REQ, discon_req); |
| COMMA(); |
| tprintf("SEQ=%ld", m.discon_req.SEQ_number); |
| break; |
| #endif |
| #ifdef T_DATA_REQ |
| case T_DATA_REQ: /* data request */ |
| GET(T_DATA_REQ, data_req); |
| COMMA(); |
| tprintf("MORE=%ld", m.data_req.MORE_flag); |
| break; |
| #endif |
| #ifdef T_EXDATA_REQ |
| case T_EXDATA_REQ: /* expedited data req */ |
| GET(T_EXDATA_REQ, exdata_req); |
| COMMA(); |
| tprintf("MORE=%ld", m.exdata_req.MORE_flag); |
| break; |
| #endif |
| #ifdef T_INFO_REQ |
| case T_INFO_REQ: /* information req */ |
| GET(T_INFO_REQ, info_req); |
| break; |
| #endif |
| #ifdef T_BIND_REQ |
| case T_BIND_REQ: /* bind request */ |
| #ifdef O_T_BIND_REQ |
| case O_T_BIND_REQ: /* Ugly xti/tli hack */ |
| #endif |
| GET(T_BIND_REQ, bind_req); |
| ADDR(bind_req, ADDR); |
| COMMA(); |
| tprintf("CONIND=%ld", m.bind_req.CONIND_number); |
| break; |
| #endif |
| #ifdef T_UNBIND_REQ |
| case T_UNBIND_REQ: /* unbind request */ |
| GET(T_UNBIND_REQ, unbind_req); |
| break; |
| #endif |
| #ifdef T_UNITDATA_REQ |
| case T_UNITDATA_REQ: /* unitdata requset */ |
| GET(T_UNITDATA_REQ, unitdata_req); |
| ADDR(unitdata_req, DEST); |
| ADDR(unitdata_req, OPT); |
| break; |
| #endif |
| #ifdef T_OPTMGMT_REQ |
| case T_OPTMGMT_REQ: /* manage opt req */ |
| GET(T_OPTMGMT_REQ, optmgmt_req); |
| COMMA(); |
| tprints("MGMT="); |
| printflags(transport_user_flags, m.optmgmt_req.MGMT_flags, |
| "T_???"); |
| STRUCT(optmgmt_req, OPT, print_optmgmt); |
| break; |
| #endif |
| #ifdef T_ORDREL_REQ |
| case T_ORDREL_REQ: /* orderly rel req */ |
| GET(T_ORDREL_REQ, ordrel_req); |
| break; |
| #endif |
| #ifdef T_CONN_IND |
| case T_CONN_IND: /* connect indication */ |
| GET(T_CONN_IND, conn_ind); |
| ADDR(conn_ind, SRC); |
| ADDR(conn_ind, OPT); |
| tprintf(", SEQ=%ld", m.conn_ind.SEQ_number); |
| break; |
| #endif |
| #ifdef T_CONN_CON |
| case T_CONN_CON: /* connect corfirm */ |
| GET(T_CONN_CON, conn_con); |
| ADDR(conn_con, RES); |
| ADDR(conn_con, OPT); |
| break; |
| #endif |
| #ifdef T_DISCON_IND |
| case T_DISCON_IND: /* discon indication */ |
| GET(T_DISCON_IND, discon_ind); |
| COMMA(); |
| tprintf("DISCON=%ld, SEQ=%ld", |
| m.discon_ind.DISCON_reason, m.discon_ind.SEQ_number); |
| break; |
| #endif |
| #ifdef T_DATA_IND |
| case T_DATA_IND: /* data indication */ |
| GET(T_DATA_IND, data_ind); |
| COMMA(); |
| tprintf("MORE=%ld", m.data_ind.MORE_flag); |
| break; |
| #endif |
| #ifdef T_EXDATA_IND |
| case T_EXDATA_IND: /* expedited data ind */ |
| GET(T_EXDATA_IND, exdata_ind); |
| COMMA(); |
| tprintf("MORE=%ld", m.exdata_ind.MORE_flag); |
| break; |
| #endif |
| #ifdef T_INFO_ACK |
| case T_INFO_ACK: /* info ack */ |
| GET(T_INFO_ACK, info_ack); |
| COMMA(); |
| tprintf("TSDU=%ld, ETSDU=%ld, CDATA=%ld, DDATA=%ld, " |
| "ADDR=%ld, OPT=%ld, TIDU=%ld, SERV=", |
| m.info_ack.TSDU_size, m.info_ack.ETSDU_size, |
| m.info_ack.CDATA_size, m.info_ack.DDATA_size, |
| m.info_ack.ADDR_size, m.info_ack.OPT_size, |
| m.info_ack.TIDU_size); |
| printxval(service_type, m.info_ack.SERV_type, "T_???"); |
| tprints(", CURRENT="); |
| printxval(ts_state, m.info_ack.CURRENT_state, "TS_???"); |
| tprints(", PROVIDER="); |
| printflags(provider_flags, m.info_ack.PROVIDER_flag, "???"); |
| break; |
| #endif |
| #ifdef T_BIND_ACK |
| case T_BIND_ACK: /* bind ack */ |
| GET(T_BIND_ACK, bind_ack); |
| ADDR(bind_ack, ADDR); |
| tprintf(", CONIND=%ld", m.bind_ack.CONIND_number); |
| break; |
| #endif |
| #ifdef T_ERROR_ACK |
| case T_ERROR_ACK: /* error ack */ |
| GET(T_ERROR_ACK, error_ack); |
| COMMA(); |
| tprints("ERROR="); |
| printxval(transport_user_options, |
| m.error_ack.ERROR_prim, "TI_???"); |
| tprints(", TLI="); |
| printxval(tli_errors, m.error_ack.TLI_error, "T???"); |
| tprintf("UNIX=%s", strerror(m.error_ack.UNIX_error)); |
| break; |
| #endif |
| #ifdef T_OK_ACK |
| case T_OK_ACK: /* ok ack */ |
| GET(T_OK_ACK, ok_ack); |
| COMMA(); |
| tprints("CORRECT="); |
| printxval(transport_user_options, |
| m.ok_ack.CORRECT_prim, "TI_???"); |
| break; |
| #endif |
| #ifdef T_UNITDATA_IND |
| case T_UNITDATA_IND: /* unitdata ind */ |
| GET(T_UNITDATA_IND, unitdata_ind); |
| ADDR(unitdata_ind, SRC); |
| ADDR(unitdata_ind, OPT); |
| break; |
| #endif |
| #ifdef T_UDERROR_IND |
| case T_UDERROR_IND: /* unitdata error ind */ |
| GET(T_UDERROR_IND, uderror_ind); |
| ADDR(uderror_ind, DEST); |
| ADDR(uderror_ind, OPT); |
| tprintf(", ERROR=%ld", m.uderror_ind.ERROR_type); |
| break; |
| #endif |
| #ifdef T_OPTMGMT_ACK |
| case T_OPTMGMT_ACK: /* manage opt ack */ |
| GET(T_OPTMGMT_ACK, optmgmt_ack); |
| COMMA(); |
| tprints("MGMT="); |
| printflags(transport_user_flags, m.optmgmt_ack.MGMT_flags, |
| "T_???"); |
| STRUCT(optmgmt_ack, OPT, print_optmgmt); |
| break; |
| #endif |
| #ifdef T_ORDREL_IND |
| case T_ORDREL_IND: /* orderly rel ind */ |
| GET(T_ORDREL_IND, ordrel_ind); |
| break; |
| #endif |
| #ifdef T_ADDR_REQ |
| case T_ADDR_REQ: /* address req */ |
| GET(T_ADDR_REQ, addr_req); |
| break; |
| #endif |
| #ifdef T_ADDR_ACK |
| case T_ADDR_ACK: /* address response */ |
| GET(T_ADDR_ACK, addr_ack); |
| ADDR(addr_ack, LOCADDR); |
| ADDR(addr_ack, REMADDR); |
| break; |
| #endif |
| default: |
| dump: |
| c = -1; |
| printstr(tcp, addr, len); |
| break; |
| } |
| |
| if (c >= 0) tprints("}"); |
| |
| #undef ADDR |
| #undef COMMA |
| #undef STRUCT |
| |
| return 0; |
| } |
| |
| |
| #endif /* TI_BIND */ |
| |
| |
| static int internal_stream_ioctl(struct tcb *tcp, int arg) |
| { |
| struct strioctl si; |
| struct ioctlent *iop; |
| int in_and_out; |
| int timod = 0; |
| #ifdef SI_GETUDATA |
| struct si_udata udata; |
| #endif /* SI_GETUDATA */ |
| |
| if (!arg) |
| return 0; |
| if (umove(tcp, arg, &si) < 0) { |
| if (entering(tcp)) |
| tprints(", {...}"); |
| return 1; |
| } |
| if (entering(tcp)) { |
| iop = ioctl_lookup(si.ic_cmd); |
| if (iop) { |
| tprintf(", {ic_cmd=%s", iop->symbol); |
| while ((iop = ioctl_next_match(iop))) |
| tprintf(" or %s", iop->symbol); |
| } else |
| tprintf(", {ic_cmd=%#x", si.ic_cmd); |
| if (si.ic_timout == INFTIM) |
| tprints(", ic_timout=INFTIM, "); |
| else |
| tprintf(" ic_timout=%d, ", si.ic_timout); |
| } |
| in_and_out = 1; |
| switch (si.ic_cmd) { |
| #ifdef SI_GETUDATA |
| case SI_GETUDATA: |
| in_and_out = 0; |
| break; |
| #endif /* SI_GETUDATA */ |
| } |
| if (in_and_out) { |
| if (entering(tcp)) |
| tprints("/* in */ "); |
| else |
| tprints(", /* out */ "); |
| } |
| if (in_and_out || entering(tcp)) |
| tprintf("ic_len=%d, ic_dp=", si.ic_len); |
| switch (si.ic_cmd) { |
| #ifdef TI_BIND |
| case TI_BIND: |
| /* in T_BIND_REQ, out T_BIND_ACK */ |
| ++timod; |
| if (entering(tcp)) { |
| print_transport_message(tcp, |
| T_BIND_REQ, |
| si.ic_dp, si.ic_len); |
| } |
| else { |
| print_transport_message(tcp, |
| T_BIND_ACK, |
| si.ic_dp, si.ic_len); |
| } |
| break; |
| #endif /* TI_BIND */ |
| #ifdef TI_UNBIND |
| case TI_UNBIND: |
| /* in T_UNBIND_REQ, out T_OK_ACK */ |
| ++timod; |
| if (entering(tcp)) { |
| print_transport_message(tcp, |
| T_UNBIND_REQ, |
| si.ic_dp, si.ic_len); |
| } |
| else { |
| print_transport_message(tcp, |
| T_OK_ACK, |
| si.ic_dp, si.ic_len); |
| } |
| break; |
| #endif /* TI_UNBIND */ |
| #ifdef TI_GETINFO |
| case TI_GETINFO: |
| /* in T_INFO_REQ, out T_INFO_ACK */ |
| ++timod; |
| if (entering(tcp)) { |
| print_transport_message(tcp, |
| T_INFO_REQ, |
| si.ic_dp, si.ic_len); |
| } |
| else { |
| print_transport_message(tcp, |
| T_INFO_ACK, |
| si.ic_dp, si.ic_len); |
| } |
| break; |
| #endif /* TI_GETINFO */ |
| #ifdef TI_OPTMGMT |
| case TI_OPTMGMT: |
| /* in T_OPTMGMT_REQ, out T_OPTMGMT_ACK */ |
| ++timod; |
| if (entering(tcp)) { |
| print_transport_message(tcp, |
| T_OPTMGMT_REQ, |
| si.ic_dp, si.ic_len); |
| } |
| else { |
| print_transport_message(tcp, |
| T_OPTMGMT_ACK, |
| si.ic_dp, si.ic_len); |
| } |
| break; |
| #endif /* TI_OPTMGMT */ |
| #ifdef SI_GETUDATA |
| case SI_GETUDATA: |
| if (entering(tcp)) |
| break; |
| if (umove(tcp, (int) si.ic_dp, &udata) < 0) |
| tprints("{...}"); |
| else { |
| tprintf("{tidusize=%d, addrsize=%d, ", |
| udata.tidusize, udata.addrsize); |
| tprintf("optsize=%d, etsdusize=%d, ", |
| udata.optsize, udata.etsdusize); |
| tprintf("servtype=%d, so_state=%d, ", |
| udata.servtype, udata.so_state); |
| tprintf("so_options=%d", udata.so_options); |
| tprints("}"); |
| } |
| break; |
| #endif /* SI_GETUDATA */ |
| default: |
| printstr(tcp, (long) si.ic_dp, si.ic_len); |
| break; |
| } |
| if (exiting(tcp)) { |
| tprints("}"); |
| if (timod && tcp->u_rval && !syserror(tcp)) { |
| tcp->auxstr = xlookup(tli_errors, tcp->u_rval); |
| return RVAL_STR + 1; |
| } |
| } |
| |
| return 1; |
| } |
| |
| int |
| stream_ioctl(struct tcb *tcp, int code, int arg) |
| { |
| #ifdef I_LIST |
| int i; |
| #endif |
| int val; |
| #ifdef I_FLUSHBAND |
| struct bandinfo bi; |
| #endif |
| struct strpeek sp; |
| struct strfdinsert sfi; |
| struct strrecvfd srf; |
| #ifdef I_LIST |
| struct str_list sl; |
| #endif |
| |
| /* I_STR is a special case because the data is read & written. */ |
| if (code == I_STR) |
| return internal_stream_ioctl(tcp, arg); |
| if (entering(tcp)) |
| return 0; |
| |
| switch (code) { |
| case I_PUSH: |
| case I_LOOK: |
| case I_FIND: |
| /* arg is a string */ |
| tprints(", "); |
| printpath(tcp, arg); |
| return 1; |
| case I_POP: |
| /* doesn't take an argument */ |
| return 1; |
| case I_FLUSH: |
| /* argument is an option */ |
| tprints(", "); |
| printxval(stream_flush_options, arg, "FLUSH???"); |
| return 1; |
| #ifdef I_FLUSHBAND |
| case I_FLUSHBAND: |
| /* argument is a pointer to a bandinfo struct */ |
| if (umove(tcp, arg, &bi) < 0) |
| tprints(", {...}"); |
| else { |
| tprintf(", {bi_pri=%d, bi_flag=", bi.bi_pri); |
| printflags(stream_flush_options, bi.bi_flag, "FLUSH???"); |
| tprints("}"); |
| } |
| return 1; |
| #endif /* I_FLUSHBAND */ |
| case I_SETSIG: |
| /* argument is a set of flags */ |
| tprints(", "); |
| printflags(stream_setsig_flags, arg, "S_???"); |
| return 1; |
| case I_GETSIG: |
| /* argument is a pointer to a set of flags */ |
| if (syserror(tcp)) |
| return 0; |
| tprints(", ["); |
| if (umove(tcp, arg, &val) < 0) |
| tprints("?"); |
| else |
| printflags(stream_setsig_flags, val, "S_???"); |
| tprints("]"); |
| return 1; |
| case I_PEEK: |
| /* argument is a pointer to a strpeek structure */ |
| if (syserror(tcp) || !arg) |
| return 0; |
| if (umove(tcp, arg, &sp) < 0) { |
| tprints(", {...}"); |
| return 1; |
| } |
| tprints(", {ctlbuf="); |
| printstrbuf(tcp, &sp.ctlbuf, 1); |
| tprints(", databuf="); |
| printstrbuf(tcp, &sp.databuf, 1); |
| tprints(", flags="); |
| printflags(msgflags, sp.flags, "RS_???"); |
| tprints("}"); |
| return 1; |
| case I_SRDOPT: |
| /* argument is an option with flags */ |
| tprints(", "); |
| printxval(stream_read_options, arg & RMODEMASK, "R???"); |
| addflags(stream_read_flags, arg & ~RMODEMASK); |
| return 1; |
| case I_GRDOPT: |
| /* argument is an pointer to an option with flags */ |
| if (syserror(tcp)) |
| return 0; |
| tprints(", ["); |
| if (umove(tcp, arg, &val) < 0) |
| tprints("?"); |
| else { |
| printxval(stream_read_options, |
| arg & RMODEMASK, "R???"); |
| addflags(stream_read_flags, arg & ~RMODEMASK); |
| } |
| tprints("]"); |
| return 1; |
| case I_NREAD: |
| #ifdef I_GETBAND |
| case I_GETBAND: |
| #endif |
| #ifdef I_SETCLTIME |
| case I_SETCLTIME: |
| #endif |
| #ifdef I_GETCLTIME |
| case I_GETCLTIME: |
| #endif |
| /* argument is a pointer to a decimal integer */ |
| if (syserror(tcp)) |
| return 0; |
| tprints(", "); |
| printnum(tcp, arg, "%d"); |
| return 1; |
| case I_FDINSERT: |
| /* argument is a pointer to a strfdinsert structure */ |
| if (syserror(tcp) || !arg) |
| return 0; |
| if (umove(tcp, arg, &sfi) < 0) { |
| tprints(", {...}"); |
| return 1; |
| } |
| tprints(", {ctlbuf="); |
| printstrbuf(tcp, &sfi.ctlbuf, 1); |
| tprints(", databuf="); |
| printstrbuf(tcp, &sfi.databuf, 1); |
| tprints(", flags="); |
| printflags(msgflags, sfi.flags, "RS_???"); |
| tprintf(", filedes=%d, offset=%d}", sfi.fildes, sfi.offset); |
| return 1; |
| #ifdef I_SWROPT |
| case I_SWROPT: |
| /* argument is a set of flags */ |
| tprints(", "); |
| printflags(stream_write_flags, arg, "SND???"); |
| return 1; |
| #endif /* I_SWROPT */ |
| #ifdef I_GWROPT |
| case I_GWROPT: |
| /* argument is an pointer to an option with flags */ |
| if (syserror(tcp)) |
| return 0; |
| tprints(", ["); |
| if (umove(tcp, arg, &val) < 0) |
| tprints("?"); |
| else |
| printflags(stream_write_flags, arg, "SND???"); |
| tprints("]"); |
| return 1; |
| #endif /* I_GWROPT */ |
| case I_SENDFD: |
| #ifdef I_CKBAND |
| case I_CKBAND: |
| #endif |
| #ifdef I_CANPUT |
| case I_CANPUT: |
| #endif |
| case I_LINK: |
| case I_UNLINK: |
| case I_PLINK: |
| case I_PUNLINK: |
| /* argument is a decimal integer */ |
| tprintf(", %d", arg); |
| return 1; |
| case I_RECVFD: |
| /* argument is a pointer to a strrecvfd structure */ |
| if (syserror(tcp) || !arg) |
| return 0; |
| if (umove(tcp, arg, &srf) < 0) { |
| tprints(", {...}"); |
| return 1; |
| } |
| tprintf(", {fd=%d, uid=%lu, gid=%lu}", srf.fd, |
| (unsigned long) srf.uid, (unsigned long) srf.gid); |
| return 1; |
| #ifdef I_LIST |
| case I_LIST: |
| if (syserror(tcp)) |
| return 0; |
| if (arg == 0) { |
| tprints(", NULL"); |
| return 1; |
| } |
| if (umove(tcp, arg, &sl) < 0) { |
| tprints(", {...}"); |
| return 1; |
| } |
| tprintf(", {sl_nmods=%d, sl_modlist=[", sl.sl_nmods); |
| for (i = 0; i < tcp->u_rval; i++) { |
| if (i) |
| tprints(", "); |
| printpath(tcp, (int) sl.sl_modlist[i].l_name); |
| } |
| tprints("]}"); |
| return 1; |
| #endif /* I_LIST */ |
| #ifdef I_ATMARK |
| case I_ATMARK: |
| tprints(", "); |
| printxval(stream_atmark_options, arg, "???MARK"); |
| return 1; |
| #endif /* I_ATMARK */ |
| default: |
| return 0; |
| } |
| } |
| |
| #endif /* !LINUX && !FREEBSD */ |
| |
| #endif /* HAVE_SYS_STREAM_H || LINUX || FREEBSD */ |