/*--------------------------------------------------------------------*/ /*--- Intercepts for various libc functions we want to capture ---*/ /*--- (mostly for threading purposes). vg_intercept.c ---*/ /*--------------------------------------------------------------------*/ /* This file is part of Valgrind, an extensible x86 protected-mode emulator for monitoring program execution on x86-Unixes. Copyright (C) 2000-2003 Julian Seward jseward@acm.org This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. The GNU General Public License is contained in the file COPYING. */ /* --------------------------------------------------------------------- ALL THE CODE IN THIS FILE RUNS ON THE SIMULATED CPU. It is intended for various reasons as drop-in replacements for libc functions. These functions have global visibility (obviously) and have no prototypes in vg_include.h, since they are not intended to be called from within Valgrind. ------------------------------------------------------------------ */ /* General idea (2003-Apr-26) is that master implementations of selected functions are done as VGR_(fnname). Then we route all calls to the master, both here and in vg_libpthread.c. This means we no longer have to rely on the semantics of weak symbols, which seems to have changed in glibc >= 2.3.2 in such a way as to make the previous interception scheme stop working. */ /* Sidestep the normal check which disallows using valgrind.h directly. */ #define __VALGRIND_SOMESKIN_H #include "valgrind.h" #include "vg_include.h" #include "vg_kerneliface.h" /* This has some nasty duplication of stuff from vg_libpthread.c */ #include #include #include #include #include #include /* for ipc_kludge */ #include #include #include #ifdef GLIBC_2_1 #include #endif /* --------------------------------------------------------------- */ # define strong_alias(name, aliasname) \ extern __typeof (name) aliasname __attribute__ ((alias (#name))); #define WEAK __attribute__((weak)) /* --------------------------------------------------------------- */ static __inline__ int my_do_syscall1 ( int syscallno, int arg1 ) { int __res; __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx" : "=a" (__res) : "0" (syscallno), "d" (arg1) ); return __res; } static __inline__ int my_do_syscall2 ( int syscallno, int arg1, int arg2 ) { int __res; __asm__ volatile ("pushl %%ebx; movl %%edx,%%ebx ; int $0x80 ; popl %%ebx" : "=a" (__res) : "0" (syscallno), "d" (arg1), "c" (arg2) ); return __res; } static __inline__ int my_do_syscall3 ( int syscallno, int arg1, int arg2, int arg3 ) { int __res; __asm__ volatile ("pushl %%ebx; movl %%esi,%%ebx ; int $0x80 ; popl %%ebx" : "=a" (__res) : "0" (syscallno), "S" (arg1), "c" (arg2), "d" (arg3) ); return __res; } static __inline__ int my_do_syscall5 ( int syscallno, int arg1, int arg2, int arg3, int arg4, int arg5 ) { int __res; __asm__ volatile ("int $0x80" : "=a" (__res) : "0" (syscallno), "b" (arg1), "c" (arg2), "d" (arg3), "S" (arg4), "D" (arg5)); return __res; } static __inline__ int do_syscall_select( int n, vki_fd_set* readfds, vki_fd_set* writefds, vki_fd_set* exceptfds, struct vki_timeval * timeout ) { int res; int args[5]; args[0] = n; args[1] = (int)readfds; args[2] = (int)writefds; args[3] = (int)exceptfds; args[4] = (int)timeout; res = my_do_syscall1(__NR_select, (int)(&(args[0])) ); return res; } static __inline__ int do_syscall_ipc( unsigned call, int first, int second, int third, void *ptr) { return my_do_syscall5(__NR_ipc, call, first, second, third, (int)ptr); } /* --------------------------------------------------------------- */ /* Just start up Valgrind if it's not already going. VG_(startup)() detects and ignores second and subsequent calls. */ /* We need this guy -- it's in valgrind.so. */ extern void VG_(startup) ( void ); static __inline__ void ensure_valgrind ( char* caller ) { VG_(startup)(); } static __inline__ int is_kerror ( int res ) { if (res >= -4095 && res <= -1) return 1; else return 0; } /* --------------------------------------------------------------- */ /* Extract from Valgrind the value of VG_(clo_trace_pthread_level). Returns 0 (none) if not running on Valgrind. */ static int get_pt_trace_level ( void ) { int res; VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, VG_USERREQ__GET_PTHREAD_TRACE_LEVEL, 0, 0, 0, 0); return res; } static void cat_n_send ( char* pre, char* msg ) { char buf[1000]; if (get_pt_trace_level() >= 0) { snprintf(buf, sizeof(buf), "%s%s", pre, msg ); buf[sizeof(buf)-1] = '\0'; VALGRIND_NON_SIMD_CALL2(VG_(message), Vg_UserMsg, buf); } } static void my_exit ( int arg ) { my_do_syscall1(__NR_exit, arg); /*NOTREACHED*/ } static void my_assert_fail ( const Char* expr, const Char* file, Int line, const Char* fn ) { char buf[1000]; static Bool entered = False; if (entered) my_exit(2); entered = True; sprintf(buf, "\n%s: %s:%d (%s): Assertion `%s' failed.\n", "valgrind", file, line, fn, expr ); cat_n_send ( "", buf ); sprintf(buf, "Please report this bug to me at: %s\n\n", VG_EMAIL_ADDR); cat_n_send ( "", buf ); my_exit(1); } #define MY__STRING(__str) #__str #define my_assert(expr) \ ((void) ((expr) ? 0 : \ (my_assert_fail (MY__STRING(expr), \ __FILE__, __LINE__, \ __PRETTY_FUNCTION__), 0))) /* --------------------------------------------------------------- */ static __inline__ void __my_pthread_testcancel(void) { int res; ensure_valgrind("__my_pthread_testcancel"); VALGRIND_MAGIC_SEQUENCE(res, (-1) /* default */, VG_USERREQ__TESTCANCEL, 0, 0, 0, 0); my_assert(res == 0); } /* ================================ poll ================================ */ /* This is the master implementation of poll(). It blocks only the calling thread. All roads lead here. */ #ifndef HAVE_NFDS_T typedef unsigned long int nfds_t; #endif int VGR_(poll) (struct pollfd *__fds, nfds_t __nfds, int __timeout) { unsigned int ms_now, ms_end, i; int res; struct vki_timespec nanosleep_interval; __my_pthread_testcancel(); ensure_valgrind("poll"); /* Detect the current time and simultaneously find out if we are running on Valgrind. */ VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, VG_USERREQ__READ_MILLISECOND_TIMER, 0, 0, 0, 0); /* CHECK SIZES FOR struct pollfd */ my_assert(sizeof(struct timeval) == sizeof(struct vki_timeval)); /* dummy initialisation to keep gcc -Wall happy */ ms_end = 0; /* If a zero timeout specified, this call is harmless. Also do this if not running on Valgrind. */ if (__timeout == 0 || ms_now == 0xFFFFFFFF) { res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, __timeout); if (is_kerror(res)) { * (__errno_location()) = -res; return -1; } else { return res; } } /* If a timeout was specified, set ms_end to be the end wallclock time. Easy considering that __timeout is in milliseconds. */ if (__timeout > 0) { ms_end = ms_now + (unsigned int)__timeout; } /* fprintf(stderr, "MY_POLL: before loop\n"); */ /* Either timeout < 0, meaning wait indefinitely, or timeout > 0, in which case t_end holds the end time. */ my_assert(__timeout != 0); while (1) { /* Do a return-immediately poll. */ res = my_do_syscall3(__NR_poll, (int)__fds, __nfds, 0 ); if (is_kerror(res)) { /* Some kind of error. Set errno and return. */ * (__errno_location()) = -res; return -1; } if (res > 0) { /* One or more fds is ready. Return now. */ return res; } /* Nothing interesting happened, so we go to sleep for a while. */ /* fprintf(stderr, "MY_POLL: nanosleep\n"); */ /* nanosleep and go round again */ nanosleep_interval.tv_sec = 0; nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ /* It's critical here that valgrind's nanosleep implementation is nonblocking. */ res = my_do_syscall2(__NR_nanosleep, (int)(&nanosleep_interval), (int)NULL); if (res == -VKI_EINTR) { /* The nanosleep was interrupted by a signal. So we do the same. */ * (__errno_location()) = EINTR; return -1; } /* Sleeping finished. If a finite timeout, check to see if it has expired yet. */ if (__timeout > 0) { VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, VG_USERREQ__READ_MILLISECOND_TIMER, 0, 0, 0, 0); my_assert(ms_now != 0xFFFFFFFF); if (ms_now >= ms_end) { /* timeout; nothing interesting happened. */ for (i = 0; i < __nfds; i++) __fds[i].revents = 0; return 0; } } } } int poll(struct pollfd *__fds, nfds_t __nfds, int __timeout) { return VGR_(poll)(__fds, __nfds, __timeout); } strong_alias(poll, __poll) /* ================================ msgsnd ================================ */ /* Turn a blocking msgsnd() into a polling non-blocking one, so that other threads make progress */ int VGR_(msgsnd)(int msgid, const void *msgp, /*size_t*/ unsigned int msgsz, int msgflg) { struct vki_timespec nanosleep_interval; int err; ensure_valgrind("msgsnd"); nanosleep_interval.tv_sec = 0; nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ if (msgflg & IPC_NOWAIT) { /* If we aren't blocking anyway, just do it */ err = do_syscall_ipc(11, msgid, msgsz, msgflg, (void *)msgp); } else { /* Otherwise poll on the queue to let other things run */ for(;;) { err = do_syscall_ipc(11, msgid, msgsz, msgflg | IPC_NOWAIT, (void *)msgp); if (err != -EAGAIN) break; (void)my_do_syscall2(__NR_nanosleep, (int)(&nanosleep_interval), (int)NULL); } } if (is_kerror(err)) { *(__errno_location()) = -err; return -1; } return 0; } #ifdef GLIBC_2_1 int msgsnd(int msgid, void *msgp, size_t msgsz, int msgflg) #else int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg) #endif { return VGR_(msgsnd)(msgid, msgp, msgsz, msgflg); } /* ================================ msgrcv ================================ */ /* Turn a blocking msgrcv() into a polling non-blocking one, so that other threads make progress */ int VGR_(msgrcv)( int msqid, void* msgp, /*size_t*/ unsigned int msgsz, long msgtyp, int msgflg ) { struct vki_timespec nanosleep_interval; int err; struct ipc_kludge tmp; ensure_valgrind("msgrcv"); nanosleep_interval.tv_sec = 0; nanosleep_interval.tv_nsec = 13 * 1000 * 1000; /* 13 milliseconds */ tmp.msgp = msgp; tmp.msgtyp = msgtyp; if (msgflg & IPC_NOWAIT) { /* If we aren't blocking anyway, just do it */ err = do_syscall_ipc(12, msqid, msgsz, msgflg, &tmp ); } else { /* Otherwise poll on the queue to let other things run */ for(;;) { err = do_syscall_ipc(12, msqid, msgsz, msgflg | IPC_NOWAIT, &tmp ); if (err != -ENOMSG) break; (void)my_do_syscall2(__NR_nanosleep, (int)(&nanosleep_interval), (int)NULL); } } if (is_kerror(err)) { *(__errno_location()) = -err; return -1; } return err; } int msgrcv( int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg ) { return VGR_(msgrcv)( msqid, msgp, msgsz, msgtyp, msgflg ); } /* ================================ accept ================================ */ extern int __libc_accept(int s, struct sockaddr *addr, socklen_t *addrlen); int VGR_(accept)(int s, /*struct sockaddr*/ void *addr, /*socklen_t*/ void *addrlen) { __my_pthread_testcancel(); VGR_(wait_for_fd_to_be_readable_or_erring)(s); __my_pthread_testcancel(); return __libc_accept(s, addr, addrlen); } int accept(int s, struct sockaddr *addr, socklen_t *addrlen) { return VGR_(accept)(s, addr, addrlen); } /* ================================ recv ================================ */ extern int __libc_recv(int s, void *buf, size_t len, int flags); int VGR_(recv)(int s, void *buf, size_t len, int flags) { __my_pthread_testcancel(); VGR_(wait_for_fd_to_be_readable_or_erring)(s); __my_pthread_testcancel(); return __libc_recv(s, buf, len, flags); } int recv(int s, void *buf, size_t len, int flags) { return VGR_(recv)(s, buf, len, flags); } strong_alias(recv, __recv) /* ================================ select ================================ */ /* This is a wrapper round select(), which makes it thread-safe, meaning that only this thread will block, rather than the entire process. This wrapper in turn depends on nanosleep() not to block the entire process, but I think (hope? suspect?) that POSIX pthreads guarantees that to be the case. Basic idea is: modify the timeout parameter to select so that it returns immediately. Poll like this until select returns non-zero, indicating something interesting happened, or until our time is up. Space out the polls with nanosleeps of say 11 milliseconds, which is required to be nonblocking; this allows other threads to run. Assumes: * (checked via my_assert) types fd_set and vki_fd_set are identical. * (checked via my_assert) types timeval and vki_timeval are identical. * (unchecked) libc error numbers (EINTR etc) are the negation of the kernel's error numbers (VKI_EINTR etc). */ int VGR_(select) ( int n, /*fd_set*/ void *rfdsV, /*fd_set*/ void *wfdsV, /*fd_set*/ void *xfdsV, /*struct timeval*/ void *timeoutV ) { unsigned int ms_now, ms_end; int res; fd_set rfds_copy; fd_set wfds_copy; fd_set xfds_copy; struct vki_timeval t_now; struct vki_timeval zero_timeout; struct vki_timespec nanosleep_interval; struct timeval* timeout = (struct timeval*)timeoutV; fd_set* rfds = (fd_set*)rfdsV; fd_set* wfds = (fd_set*)wfdsV; fd_set* xfds = (fd_set*)xfdsV; __my_pthread_testcancel(); /* gcc's complains about ms_end being used uninitialised -- classic case it can't understand, where ms_end is both defined and used only if timeout != NULL. Hence ... */ ms_end = 0; /* We assume that the kernel and libc data layouts are identical for the following types. These asserts provide a crude check. */ my_assert(sizeof(struct timeval) == sizeof(struct vki_timeval)); /* Detect the current time and simultaneously find out if we are running on Valgrind. */ VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, VG_USERREQ__READ_MILLISECOND_TIMER, 0, 0, 0, 0); /* If a zero timeout specified, this call is harmless. Also go this route if we're not running on Valgrind, for whatever reason. */ if ( (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) || (ms_now == 0xFFFFFFFF) ) { res = do_syscall_select( n, (vki_fd_set*)rfds, (vki_fd_set*)wfds, (vki_fd_set*)xfds, (struct vki_timeval*)timeout); if (is_kerror(res)) { * (__errno_location()) = -res; return -1; } else { return res; } } /* If a timeout was specified, set ms_end to be the end millisecond counter [wallclock] time. */ if (timeout) { res = my_do_syscall2(__NR_gettimeofday, (int)&t_now, (int)NULL); my_assert(res == 0); ms_end = ms_now; ms_end += (timeout->tv_usec / 1000); ms_end += (timeout->tv_sec * 1000); /* Stay sane ... */ if (ms_end < ms_now) ms_end = ms_now; } /* fprintf(stderr, "MY_SELECT: before loop\n"); */ /* Either timeout == NULL, meaning wait indefinitely, or timeout != NULL, in which case ms_end holds the end time. */ while (1) { /* First, do a return-immediately select(). */ /* These could be trashed each time round the loop, so restore them each time. */ if (rfds) rfds_copy = *rfds; if (wfds) wfds_copy = *wfds; if (xfds) xfds_copy = *xfds; zero_timeout.tv_sec = zero_timeout.tv_usec = 0; res = do_syscall_select( n, rfds ? (vki_fd_set*)(&rfds_copy) : NULL, wfds ? (vki_fd_set*)(&wfds_copy) : NULL, xfds ? (vki_fd_set*)(&xfds_copy) : NULL, & zero_timeout ); if (is_kerror(res)) { /* Some kind of error (including EINTR). Set errno and return. The sets are unspecified in this case. */ * (__errno_location()) = -res; return -1; } if (res > 0) { /* one or more fds is ready. Copy out resulting sets and return. */ if (rfds) *rfds = rfds_copy; if (wfds) *wfds = wfds_copy; if (xfds) *xfds = xfds_copy; return res; } /* Nothing interesting happened, so we go to sleep for a while. */ /* fprintf(stderr, "MY_SELECT: nanosleep\n"); */ /* nanosleep and go round again */ nanosleep_interval.tv_sec = 0; nanosleep_interval.tv_nsec = 11 * 1000 * 1000; /* 11 milliseconds */ /* It's critical here that valgrind's nanosleep implementation is nonblocking. */ res = my_do_syscall2(__NR_nanosleep, (int)(&nanosleep_interval), (int)NULL); if (res == -VKI_EINTR) { /* The nanosleep was interrupted by a signal. So we do the same. */ * (__errno_location()) = EINTR; return -1; } /* Sleeping finished. If a finite timeout, check to see if it has expired yet. */ if (timeout) { VALGRIND_MAGIC_SEQUENCE(ms_now, 0xFFFFFFFF /* default */, VG_USERREQ__READ_MILLISECOND_TIMER, 0, 0, 0, 0); my_assert(ms_now != 0xFFFFFFFF); if (ms_now >= ms_end) { /* timeout; nothing interesting happened. */ if (rfds) FD_ZERO(rfds); if (wfds) FD_ZERO(wfds); if (xfds) FD_ZERO(xfds); return 0; } } } } int select ( int n, fd_set *rfds, fd_set *wfds, fd_set *xfds, struct timeval *timeout ) { return VGR_(select)(n, rfds, wfds, xfds, timeout); } strong_alias(select, __select) /* ================================ readv ================================ */ int VGR_(readv)(int fd, const /*struct iovec*/ void *iovV, int count) { int res; const struct iovec* iov = (const struct iovec*)iovV; __my_pthread_testcancel(); VGR_(wait_for_fd_to_be_readable_or_erring)(fd); __my_pthread_testcancel(); res = my_do_syscall3(__NR_readv, fd, (unsigned)iov, count); if (is_kerror(res)) { *(__errno_location()) = -res; return -1; } return res; } int readv (int fd, const struct iovec *iov, int count) { return VGR_(readv)(fd, iov, count); } strong_alias(readv, __readv) /* ================================ writev ================================ */ int VGR_(writev)(int fd, const /*struct iovec*/ void *iovV, int count) { int res; struct iovec* iov = (struct iovec*)iovV; __my_pthread_testcancel(); VGR_(wait_for_fd_to_be_writable_or_erring)(fd); __my_pthread_testcancel(); res = my_do_syscall3(__NR_writev, fd, (unsigned)iov, count); if (is_kerror(res)) { *(__errno_location()) = -res; return -1; } return res; } int writev (int fd, const struct iovec *iov, int count) { return VGR_(writev)(fd, iov, count); } strong_alias(writev, __writev) /* ================================ sigsuspend ============================ */ /* --------------------------------------------------------------------- Horrible hack to make sigsuspend() sort-of work OK. Same trick as for pause() in vg_libpthread.so. ------------------------------------------------------------------ */ /* Horrible because -- uses VG_(ksigprocmask), VG_(nanosleep) and vg_assert, which are valgrind-native (not intended for client use). -- This is here so single-threaded progs (not linking libpthread.so) can see it. But pause() should also be here. ??? */ /* Either libc supplies this (weak) or our libpthread.so supplies it (strong) in a threaded setting. */ extern int* __errno_location ( void ); int sigsuspend ( /* const sigset_t * */ void* mask) { unsigned int n_orig, n_now; struct vki_timespec nanosleep_interval; VALGRIND_MAGIC_SEQUENCE(n_orig, 0xFFFFFFFF /* default */, VG_USERREQ__GET_N_SIGS_RETURNED, 0, 0, 0, 0); vg_assert(n_orig != 0xFFFFFFFF); VG_(ksigprocmask)(VKI_SIG_SETMASK, mask, NULL); while (1) { VALGRIND_MAGIC_SEQUENCE(n_now, 0xFFFFFFFF /* default */, VG_USERREQ__GET_N_SIGS_RETURNED, 0, 0, 0, 0); vg_assert(n_now != 0xFFFFFFFF); vg_assert(n_now >= n_orig); if (n_now != n_orig) break; nanosleep_interval.tv_sec = 0; nanosleep_interval.tv_nsec = 53 * 1000 * 1000; /* 53 milliseconds */ /* It's critical here that valgrind's nanosleep implementation is nonblocking. */ VG_(nanosleep)( &nanosleep_interval, NULL); } /* Maybe this is OK both in single and multithreaded setting. */ * (__errno_location()) = -VKI_EINTR; /* == EINTR; */ return -1; } /* ================================ waitpid ============================ */ #undef WNOHANG #define WNOHANG 0x00000001 extern pid_t __libc_waitpid(pid_t pid, int *status, int options); pid_t waitpid(pid_t pid, int *status, int options) { pid_t res; struct vki_timespec nanosleep_interval; if (options & WNOHANG) return __libc_waitpid(pid,status,options); options |= WNOHANG; while (1) { res = __libc_waitpid(pid,status,options); if (res != 0) return res; nanosleep_interval.tv_sec = 0; nanosleep_interval.tv_nsec = 54 * 1000 * 1000; /* 54 milliseconds */ /* It's critical here that valgrind's nanosleep implementation is nonblocking. */ VG_(nanosleep)( &nanosleep_interval, NULL); } } #undef WNOHANG /* --------------------------------------------------------------------- Hook for running __libc_freeres once the program exits. ------------------------------------------------------------------ */ void VG_(__libc_freeres_wrapper)( void ) { int res; extern void __libc_freeres(void); __libc_freeres(); VALGRIND_MAGIC_SEQUENCE(res, 0 /* default */, VG_USERREQ__LIBC_FREERES_DONE, 0, 0, 0, 0); /*NOTREACHED*/ vg_assert(12345+54321 == 999999); } /* --------------------------------------------------------------------- Useful for skins that want to replace certain functions ------------------------------------------------------------------ */ Bool VG_(is_running_on_simd_CPU)(void) { return VG_(running_on_simd_CPU); } /* --------------------------------------------------------------------- Helpers for safely (nonblockingly) detecting when a file descriptor is safe to use. ------------------------------------------------------------------ */ /* Helper function used to make accept() non-blocking. Idea is to use the above nonblocking poll() to make this thread ONLY wait for the specified fd to become ready, and then return. */ /* Sigh -- a hack. We're not supposed to include this file directly; should do it via /usr/include/fcntl.h, but that introduces a varargs prototype for fcntl itself, which we can't mimic. */ #define _FCNTL_H #include extern int __libc_fcntl(int fd, int cmd, long arg); void VGR_(wait_for_fd_to_be_readable_or_erring) ( int fd ) { struct pollfd pfd; int res; /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ /* First check to see if the fd is nonblocking, and/or invalid. In either case return immediately. */ res = __libc_fcntl(fd, F_GETFL, 0); if (res == -1) return; /* fd is invalid somehow */ if (res & O_NONBLOCK) return; /* fd is nonblocking */ /* Ok, we'd better wait with poll. */ pfd.fd = fd; pfd.events = POLLIN | POLLPRI | POLLERR | POLLHUP | POLLNVAL; /* ... but not POLLOUT, you may notice. */ pfd.revents = 0; (void)poll(&pfd, 1, -1 /* forever */); } void VGR_(wait_for_fd_to_be_writable_or_erring) ( int fd ) { struct pollfd pfd; int res; /* fprintf(stderr, "wait_for_fd_to_be_readable_or_erring %d\n", fd); */ /* First check to see if the fd is nonblocking, and/or invalid. In either case return immediately. */ res = __libc_fcntl(fd, F_GETFL, 0); if (res == -1) return; /* fd is invalid somehow */ if (res & O_NONBLOCK) return; /* fd is nonblocking */ /* Ok, we'd better wait with poll. */ pfd.fd = fd; pfd.events = POLLOUT | POLLERR | POLLHUP | POLLNVAL; pfd.revents = 0; (void)poll(&pfd, 1, -1 /* forever */); } /*--------------------------------------------------------------------*/ /*--- end vg_intercept.c ---*/ /*--------------------------------------------------------------------*/