X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=time.c;h=1f44dca714e0693f25509a59635b73fee7b31ca3;hb=90eee029a92746dc6d9d1cb1ee34ee9e268aecc9;hp=9864118eab55c8a8ddf103daafe73970c39a8c36;hpb=9f7027371cb40b5590064f9a9d74cfadd1c8c7d3;p=strace diff --git a/time.c b/time.c index 9864118e..1f44dca7 100644 --- a/time.c +++ b/time.c @@ -2,6 +2,7 @@ * Copyright (c) 1991, 1992 Paul Kranenburg * Copyright (c) 1993 Branko Lankester * Copyright (c) 1993, 1994, 1995, 1996 Rick Sladkey + * Copyright (c) 1996-2018 The strace developers. * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -29,147 +30,27 @@ #include "defs.h" #include -#include +#include #include -#ifndef UTIME_NOW -#define UTIME_NOW ((1l << 30) - 1l) -#endif -#ifndef UTIME_OMIT -#define UTIME_OMIT ((1l << 30) - 2l) -#endif - -#if SUPPORTED_PERSONALITIES > 1 -# if defined X86_64 || defined X32 -# define current_time_t_is_compat (current_personality == 1) -# else -# define current_time_t_is_compat (current_wordsize == 4) -# endif -#else -# define current_time_t_is_compat 0 -#endif - -struct timeval32 -{ - u_int32_t tv_sec, tv_usec; -}; - -static void -tprint_timeval32(struct tcb *tcp, const struct timeval32 *tv) -{ - tprintf("{%u, %u}", tv->tv_sec, tv->tv_usec); -} - static void -tprint_timeval(struct tcb *tcp, const struct timeval *tv) +print_timezone(struct tcb *const tcp, const kernel_ulong_t addr) { - tprintf("{%ju, %ju}", (uintmax_t) tv->tv_sec, (uintmax_t) tv->tv_usec); -} + struct timezone tz; -void -printtv_bitness(struct tcb *tcp, long addr, enum bitness_t bitness, int special) -{ - char buf[TIMEVAL_TEXT_BUFSIZE]; - sprinttv(buf, tcp, addr, bitness, special); - tprints(buf); -} - -static char * -do_sprinttv(char *buf, const uintmax_t sec, const uintmax_t usec, - const int special) -{ - if (special) { - switch (usec) { - case UTIME_NOW: - return stpcpy(buf, "UTIME_NOW"); - case UTIME_OMIT: - return stpcpy(buf, "UTIME_OMIT"); - } - } - return buf + sprintf(buf, "{%ju, %ju}", sec, usec); -} - -char * -sprinttv(char *buf, struct tcb *tcp, long addr, enum bitness_t bitness, int special) -{ - if (addr == 0) - return stpcpy(buf, "NULL"); - - if (!verbose(tcp) || (exiting(tcp) && syserror(tcp))) - return buf + sprintf(buf, "%#lx", addr); - - if (bitness == BITNESS_32 || current_time_t_is_compat) - { - struct timeval32 tv; - - if (umove(tcp, addr, &tv) >= 0) - return do_sprinttv(buf, tv.tv_sec, tv.tv_usec, special); - } else { - struct timeval tv; - - if (umove(tcp, addr, &tv) >= 0) - return do_sprinttv(buf, tv.tv_sec, tv.tv_usec, special); - } - - return buf + sprintf(buf, "%#lx", addr); -} - -void -print_timespec(struct tcb *tcp, long addr) -{ - char buf[TIMESPEC_TEXT_BUFSIZE]; - sprint_timespec(buf, tcp, addr); - tprints(buf); -} - -void -sprint_timespec(char *buf, struct tcb *tcp, long addr) -{ - if (addr == 0) - strcpy(buf, "NULL"); - else if (!verbose(tcp)) - sprintf(buf, "%#lx", addr); - else { - int rc; - -#if SUPPORTED_PERSONALITIES > 1 - if (current_time_t_is_compat) { - struct timeval32 tv; - - rc = umove(tcp, addr, &tv); - if (rc >= 0) - sprintf(buf, "{%u, %u}", - tv.tv_sec, tv.tv_usec); - } else -#endif - { - struct timespec ts; - - rc = umove(tcp, addr, &ts); - if (rc >= 0) - sprintf(buf, "{%ju, %ju}", - (uintmax_t) ts.tv_sec, - (uintmax_t) ts.tv_nsec); - } - if (rc < 0) - strcpy(buf, "{...}"); - } -} + if (umove_or_printaddr(tcp, addr, &tz)) + return; -SYS_FUNC(time) -{ - if (exiting(tcp)) { - printnum_long(tcp, tcp->u_arg[0], "%ld"); - } - return 0; + tprintf("{tz_minuteswest=%d, tz_dsttime=%d}", + tz.tz_minuteswest, tz.tz_dsttime); } SYS_FUNC(gettimeofday) { if (exiting(tcp)) { - printtv(tcp, tcp->u_arg[0]); + print_timeval(tcp, tcp->u_arg[0]); tprints(", "); - printtv(tcp, tcp->u_arg[1]); + print_timezone(tcp, tcp->u_arg[1]); } return 0; } @@ -178,9 +59,9 @@ SYS_FUNC(gettimeofday) SYS_FUNC(osf_gettimeofday) { if (exiting(tcp)) { - printtv_bitness(tcp, tcp->u_arg[0], BITNESS_32, 0); + print_timeval32(tcp, tcp->u_arg[0]); tprints(", "); - printtv_bitness(tcp, tcp->u_arg[1], BITNESS_32, 0); + print_timezone(tcp, tcp->u_arg[1]); } return 0; } @@ -188,36 +69,23 @@ SYS_FUNC(osf_gettimeofday) SYS_FUNC(settimeofday) { - if (entering(tcp)) { - printtv(tcp, tcp->u_arg[0]); - tprints(", "); - printtv(tcp, tcp->u_arg[1]); - } - return 0; + print_timeval(tcp, tcp->u_arg[0]); + tprints(", "); + print_timezone(tcp, tcp->u_arg[1]); + + return RVAL_DECODED; } #ifdef ALPHA SYS_FUNC(osf_settimeofday) { - if (entering(tcp)) { - printtv_bitness(tcp, tcp->u_arg[0], BITNESS_32, 0); - tprints(", "); - printtv_bitness(tcp, tcp->u_arg[1], BITNESS_32, 0); - } - return 0; -} -#endif + print_timeval32(tcp, tcp->u_arg[0]); + tprints(", "); + print_timezone(tcp, tcp->u_arg[1]); -SYS_FUNC(adjtime) -{ - if (entering(tcp)) { - printtv(tcp, tcp->u_arg[0]); - tprints(", "); - } else { - printtv(tcp, tcp->u_arg[1]); - } - return 0; + return RVAL_DECODED; } +#endif SYS_FUNC(nanosleep) { @@ -225,25 +93,19 @@ SYS_FUNC(nanosleep) print_timespec(tcp, tcp->u_arg[0]); tprints(", "); } else { - /* Second (returned) timespec is only significant - * if syscall was interrupted. On success, we print - * only its address, since kernel doesn't modify it, + + /* + * Second (returned) timespec is only significant if syscall + * was interrupted. On success and in case of other errors we + * print only its address, since kernel doesn't modify it, * and printing the value may show uninitialized data. */ - switch (tcp->u_error) { - default: - /* Not interrupted (slept entire interval) */ - if (tcp->u_arg[1]) { - tprintf("%#lx", tcp->u_arg[1]); - break; - } - /* Fall through: print_timespec(NULL) prints "NULL" */ - case ERESTARTSYS: - case ERESTARTNOINTR: - case ERESTARTNOHAND: - case ERESTART_RESTARTBLOCK: - /* Interrupted */ + if (is_erestart(tcp)) { + temporarily_clear_syserror(tcp); print_timespec(tcp, tcp->u_arg[1]); + restore_cleared_syserror(tcp); + } else { + printaddr(tcp->u_arg[1]); } } return 0; @@ -251,59 +113,14 @@ SYS_FUNC(nanosleep) #include "xlat/itimer_which.h" -static void -printitv_bitness(struct tcb *tcp, long addr, enum bitness_t bitness) -{ - if (addr == 0) - tprints("NULL"); - else if (!verbose(tcp)) - tprintf("%#lx", addr); - else { - int rc; - - if (bitness == BITNESS_32 || current_time_t_is_compat) { - struct { - struct timeval32 it_interval, it_value; - } itv; - - rc = umove(tcp, addr, &itv); - if (rc >= 0) { - tprints("{it_interval="); - tprint_timeval32(tcp, &itv.it_interval); - tprints(", it_value="); - tprint_timeval32(tcp, &itv.it_value); - tprints("}"); - } - } else { - struct itimerval itv; - - rc = umove(tcp, addr, &itv); - if (rc >= 0) { - tprints("{it_interval="); - tprint_timeval(tcp, &itv.it_interval); - tprints(", it_value="); - tprint_timeval(tcp, &itv.it_value); - tprints("}"); - } - } - if (rc < 0) - tprints("{...}"); - } -} - -#define printitv(tcp, addr) \ - printitv_bitness((tcp), (addr), BITNESS_CURRENT) - SYS_FUNC(getitimer) { if (entering(tcp)) { - printxval(itimer_which, tcp->u_arg[0], "ITIMER_???"); + printxval_index(itimer_which, (unsigned int) tcp->u_arg[0], + "ITIMER_???"); tprints(", "); } else { - if (syserror(tcp)) - tprintf("%#lx", tcp->u_arg[1]); - else - printitv(tcp, tcp->u_arg[1]); + print_itimerval(tcp, tcp->u_arg[1]); } return 0; } @@ -312,13 +129,11 @@ SYS_FUNC(getitimer) SYS_FUNC(osf_getitimer) { if (entering(tcp)) { - printxval(itimer_which, tcp->u_arg[0], "ITIMER_???"); + printxval_index(itimer_which, (unsigned int) tcp->u_arg[0], + "ITIMER_???"); tprints(", "); } else { - if (syserror(tcp)) - tprintf("%#lx", tcp->u_arg[1]); - else - printitv_bitness(tcp, tcp->u_arg[1], BITNESS_32); + print_itimerval32(tcp, tcp->u_arg[1]); } return 0; } @@ -327,15 +142,13 @@ SYS_FUNC(osf_getitimer) SYS_FUNC(setitimer) { if (entering(tcp)) { - printxval(itimer_which, tcp->u_arg[0], "ITIMER_???"); + printxval_index(itimer_which, (unsigned int) tcp->u_arg[0], + "ITIMER_???"); tprints(", "); - printitv(tcp, tcp->u_arg[1]); + print_itimerval(tcp, tcp->u_arg[1]); tprints(", "); } else { - if (syserror(tcp)) - tprintf("%#lx", tcp->u_arg[2]); - else - printitv(tcp, tcp->u_arg[2]); + print_itimerval(tcp, tcp->u_arg[2]); } return 0; } @@ -344,132 +157,28 @@ SYS_FUNC(setitimer) SYS_FUNC(osf_setitimer) { if (entering(tcp)) { - printxval(itimer_which, tcp->u_arg[0], "ITIMER_???"); + printxval_index(itimer_which, (unsigned int) tcp->u_arg[0], + "ITIMER_???"); tprints(", "); - printitv_bitness(tcp, tcp->u_arg[1], BITNESS_32); + print_itimerval32(tcp, tcp->u_arg[1]); tprints(", "); } else { - if (syserror(tcp)) - tprintf("%#lx", tcp->u_arg[2]); - else - printitv_bitness(tcp, tcp->u_arg[2], BITNESS_32); + print_itimerval32(tcp, tcp->u_arg[2]); } return 0; } #endif -#include "xlat/adjtimex_modes.h" -#include "xlat/adjtimex_status.h" #include "xlat/adjtimex_state.h" -#if SUPPORTED_PERSONALITIES > 1 -static int -tprint_timex32(struct tcb *tcp, long addr) -{ - struct { - unsigned int modes; - int offset; - int freq; - int maxerror; - int esterror; - int status; - int constant; - int precision; - int tolerance; - struct timeval32 time; - int tick; - int ppsfreq; - int jitter; - int shift; - int stabil; - int jitcnt; - int calcnt; - int errcnt; - int stbcnt; - } tx; - - if (umove(tcp, addr, &tx) < 0) - return -1; - - tprints("{modes="); - printflags(adjtimex_modes, tx.modes, "ADJ_???"); - tprintf(", offset=%d, freq=%d, maxerror=%d, ", - tx.offset, tx.freq, tx.maxerror); - tprintf("esterror=%u, status=", tx.esterror); - printflags(adjtimex_status, tx.status, "STA_???"); - tprintf(", constant=%d, precision=%u, ", - tx.constant, tx.precision); - tprintf("tolerance=%d, time=", tx.tolerance); - tprint_timeval32(tcp, &tx.time); - tprintf(", tick=%d, ppsfreq=%d, jitter=%d", - tx.tick, tx.ppsfreq, tx.jitter); - tprintf(", shift=%d, stabil=%d, jitcnt=%d", - tx.shift, tx.stabil, tx.jitcnt); - tprintf(", calcnt=%d, errcnt=%d, stbcnt=%d", - tx.calcnt, tx.errcnt, tx.stbcnt); - tprints("}"); - return 0; -} -#endif /* SUPPORTED_PERSONALITIES > 1 */ - -static int -tprint_timex(struct tcb *tcp, long addr) -{ - struct timex tx; - -#if SUPPORTED_PERSONALITIES > 1 - if (current_time_t_is_compat) - return tprint_timex32(tcp, addr); -#endif - if (umove(tcp, addr, &tx) < 0) - return -1; - -#if LINUX_VERSION_CODE < 66332 - tprintf("{mode=%d, offset=%ld, frequency=%ld, ", - tx.mode, tx.offset, tx.frequency); - tprintf("maxerror=%ld, esterror=%lu, status=%u, ", - tx.maxerror, tx.esterror, tx.status); - tprintf("time_constant=%ld, precision=%lu, ", - tx.time_constant, tx.precision); - tprintf("tolerance=%ld, time=", tx.tolerance); - tprint_timeval(tcp, &tx.time); -#else - tprints("{modes="); - printflags(adjtimex_modes, tx.modes, "ADJ_???"); - tprintf(", offset=%jd, freq=%jd, maxerror=%ju, esterror=%ju, status=", - (intmax_t) tx.offset, (intmax_t) tx.freq, - (uintmax_t) tx.maxerror, (uintmax_t) tx.esterror); - printflags(adjtimex_status, tx.status, "STA_???"); - tprintf(", constant=%jd, precision=%ju, tolerance=%jd, time=", - (intmax_t) tx.constant, (uintmax_t) tx.precision, - (intmax_t) tx.tolerance); - tprint_timeval(tcp, &tx.time); - tprintf(", tick=%jd, ppsfreq=%jd, jitter=%jd", - (intmax_t) tx.tick, (intmax_t) tx.ppsfreq, (intmax_t) tx.jitter); - tprintf(", shift=%d, stabil=%jd, jitcnt=%jd", - tx.shift, (intmax_t) tx.stabil, (intmax_t) tx.jitcnt); - tprintf(", calcnt=%jd, errcnt=%jd, stbcnt=%jd", - (intmax_t) tx.calcnt, (intmax_t) tx.errcnt, (intmax_t) tx.stbcnt); -#endif - tprints("}"); - return 0; -} - static int -do_adjtimex(struct tcb *tcp, long addr) +do_adjtimex(struct tcb *const tcp, const kernel_ulong_t addr) { - if (addr == 0) - tprints("NULL"); - else if (syserror(tcp) || !verbose(tcp)) - tprintf("%#lx", addr); - else if (tprint_timex(tcp, addr) < 0) - tprints("{...}"); - if (syserror(tcp)) + if (print_timex(tcp, addr)) return 0; - tcp->auxstr = xlookup(adjtimex_state, tcp->u_rval); - if (tcp->auxstr) - return RVAL_STR; - return 0; + tcp->auxstr = xlat_idx(adjtimex_state, ARRAY_SIZE(adjtimex_state) - 1, + (kernel_ulong_t) tcp->u_rval); + return RVAL_STR; } SYS_FUNC(adjtimex) @@ -492,27 +201,28 @@ printclockname(int clockid) if ((clockid & CLOCKFD_MASK) == CLOCKFD) tprintf("FD_TO_CLOCKID(%d)", CLOCKID_TO_FD(clockid)); else { - if(CPUCLOCK_PERTHREAD(clockid)) - tprintf("MAKE_THREAD_CPUCLOCK(%d,", CPUCLOCK_PID(clockid)); - else - tprintf("MAKE_PROCESS_CPUCLOCK(%d,", CPUCLOCK_PID(clockid)); - printxval(cpuclocknames, clockid & CLOCKFD_MASK, "CPUCLOCK_???"); + tprintf("%s(%d,", + CPUCLOCK_PERTHREAD(clockid) ? + "MAKE_THREAD_CPUCLOCK" : + "MAKE_PROCESS_CPUCLOCK", + CPUCLOCK_PID(clockid)); + printxval_index(cpuclocknames, + (unsigned int) clockid & CLOCKFD_MASK, + "CPUCLOCK_???"); tprints(")"); } - } - else + } else #endif - printxval(clocknames, clockid, "CLOCK_???"); + printxval_index(clocknames, clockid, "CLOCK_???"); } SYS_FUNC(clock_settime) { - if (entering(tcp)) { - printclockname(tcp->u_arg[0]); - tprints(", "); - printtv(tcp, tcp->u_arg[1]); - } - return 0; + printclockname(tcp->u_arg[0]); + tprints(", "); + print_timespec(tcp, tcp->u_arg[1]); + + return RVAL_DECODED; } SYS_FUNC(clock_gettime) @@ -521,7 +231,7 @@ SYS_FUNC(clock_gettime) printclockname(tcp->u_arg[0]); tprints(", "); } else { - printtv(tcp, tcp->u_arg[1]); + print_timespec(tcp, tcp->u_arg[1]); } return 0; } @@ -533,10 +243,20 @@ SYS_FUNC(clock_nanosleep) tprints(", "); printflags(clockflags, tcp->u_arg[1], "TIMER_???"); tprints(", "); - printtv(tcp, tcp->u_arg[2]); + print_timespec(tcp, tcp->u_arg[2]); tprints(", "); } else { - printtv(tcp, tcp->u_arg[3]); + /* + * Second (returned) timespec is only significant + * if syscall was interrupted and flags is not TIMER_ABSTIME. + */ + if (!tcp->u_arg[1] && is_erestart(tcp)) { + temporarily_clear_syserror(tcp); + print_timespec(tcp, tcp->u_arg[3]); + restore_cleared_syserror(tcp); + } else { + printaddr(tcp->u_arg[3]); + } } return 0; } @@ -550,107 +270,15 @@ SYS_FUNC(clock_adjtime) return 0; } -#ifndef SIGEV_THREAD_ID -# define SIGEV_THREAD_ID 4 -#endif -#include "xlat/sigev_value.h" - -#if SUPPORTED_PERSONALITIES > 1 -static void -printsigevent32(struct tcb *tcp, long arg) -{ - struct { - int sigev_value; - int sigev_signo; - int sigev_notify; - - union { - int tid; - struct { - int function, attribute; - } thread; - } un; - } sev; - - if (umove(tcp, arg, &sev) < 0) - tprints("{...}"); - else { - tprintf("{%#x, ", sev.sigev_value); - if (sev.sigev_notify == SIGEV_SIGNAL) - tprintf("%s, ", signame(sev.sigev_signo)); - else - tprintf("%u, ", sev.sigev_signo); - printxval(sigev_value, sev.sigev_notify, "SIGEV_???"); - tprints(", "); - if (sev.sigev_notify == SIGEV_THREAD_ID) - tprintf("{%d}", sev.un.tid); - else if (sev.sigev_notify == SIGEV_THREAD) - tprintf("{%#x, %#x}", - sev.un.thread.function, - sev.un.thread.attribute); - else - tprints("{...}"); - tprints("}"); - } -} -#endif - -void -printsigevent(struct tcb *tcp, long arg) -{ - struct sigevent sev; - -#if SUPPORTED_PERSONALITIES > 1 - if (current_wordsize == 4) { - printsigevent32(tcp, arg); - return; - } -#endif - if (umove(tcp, arg, &sev) < 0) - tprints("{...}"); - else { - tprintf("{%p, ", sev.sigev_value.sival_ptr); - if (sev.sigev_notify == SIGEV_SIGNAL) - tprintf("%s, ", signame(sev.sigev_signo)); - else - tprintf("%u, ", sev.sigev_signo); - printxval(sigev_value, sev.sigev_notify, "SIGEV_???"); - tprints(", "); - if (sev.sigev_notify == SIGEV_THREAD_ID) -#if defined(HAVE_STRUCT_SIGEVENT__SIGEV_UN__PAD) - /* _pad[0] is the _tid field which might not be - present in the userlevel definition of the - struct. */ - tprintf("{%d}", sev._sigev_un._pad[0]); -#elif defined(HAVE_STRUCT_SIGEVENT___PAD) - tprintf("{%d}", sev.__pad[0]); -#else -# warning unfamiliar struct sigevent => incomplete SIGEV_THREAD_ID decoding - tprints("{...}"); -#endif - else if (sev.sigev_notify == SIGEV_THREAD) - tprintf("{%p, %p}", sev.sigev_notify_function, - sev.sigev_notify_attributes); - else - tprints("{...}"); - tprints("}"); - } -} - SYS_FUNC(timer_create) { if (entering(tcp)) { printclockname(tcp->u_arg[0]); tprints(", "); - printsigevent(tcp, tcp->u_arg[1]); + print_sigevent(tcp, tcp->u_arg[1]); tprints(", "); } else { - int timer_id; - - if (syserror(tcp) || umove(tcp, tcp->u_arg[2], &timer_id) < 0) - tprintf("%#lx", tcp->u_arg[2]); - else - tprintf("{%d}", timer_id); + printnum_int(tcp, tcp->u_arg[2], "%d"); } return 0; } @@ -658,16 +286,13 @@ SYS_FUNC(timer_create) SYS_FUNC(timer_settime) { if (entering(tcp)) { - tprintf("%#lx, ", tcp->u_arg[0]); + tprintf("%d, ", (int) tcp->u_arg[0]); printflags(clockflags, tcp->u_arg[1], "TIMER_???"); tprints(", "); - printitv(tcp, tcp->u_arg[2]); + print_itimerspec(tcp, tcp->u_arg[2]); tprints(", "); } else { - if (syserror(tcp)) - tprintf("%#lx", tcp->u_arg[3]); - else - printitv(tcp, tcp->u_arg[3]); + print_itimerspec(tcp, tcp->u_arg[3]); } return 0; } @@ -675,40 +300,22 @@ SYS_FUNC(timer_settime) SYS_FUNC(timer_gettime) { if (entering(tcp)) { - tprintf("%#lx, ", tcp->u_arg[0]); + tprintf("%d, ", (int) tcp->u_arg[0]); } else { - if (syserror(tcp)) - tprintf("%#lx", tcp->u_arg[1]); - else - printitv(tcp, tcp->u_arg[1]); + print_itimerspec(tcp, tcp->u_arg[1]); } return 0; } #include "xlat/timerfdflags.h" -SYS_FUNC(timerfd) -{ - if (entering(tcp)) { - /* It does not matter that the kernel uses itimerspec. */ - tprintf("%ld, ", tcp->u_arg[0]); - printclockname(tcp->u_arg[0]); - tprints(", "); - printflags(timerfdflags, tcp->u_arg[2], "TFD_???"); - tprints(", "); - printitv(tcp, tcp->u_arg[3]); - } - return 0; -} - SYS_FUNC(timerfd_create) { - if (entering(tcp)) { - printclockname(tcp->u_arg[0]); - tprints(", "); - printflags(timerfdflags, tcp->u_arg[1], "TFD_???"); - } - return 0; + printclockname(tcp->u_arg[0]); + tprints(", "); + printflags(timerfdflags, tcp->u_arg[1], "TFD_???"); + + return RVAL_DECODED | RVAL_FD; } SYS_FUNC(timerfd_settime) @@ -718,9 +325,10 @@ SYS_FUNC(timerfd_settime) tprints(", "); printflags(timerfdflags, tcp->u_arg[1], "TFD_???"); tprints(", "); - printitv(tcp, tcp->u_arg[2]); + print_itimerspec(tcp, tcp->u_arg[2]); tprints(", "); - printitv(tcp, tcp->u_arg[3]); + } else { + print_itimerspec(tcp, tcp->u_arg[3]); } return 0; } @@ -730,7 +338,8 @@ SYS_FUNC(timerfd_gettime) if (entering(tcp)) { printfd(tcp, tcp->u_arg[0]); tprints(", "); - printitv(tcp, tcp->u_arg[1]); + } else { + print_itimerspec(tcp, tcp->u_arg[1]); } return 0; }