blob: 624f0247e4752ff7ff14cab07745e2240ec92534 [file] [log] [blame]
/*
* Copyright (c) 2016 Cisco and/or its affiliates.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <dlfcn.h>
#include <pthread.h>
#include <time.h>
#include <stdarg.h>
#include <sys/resource.h>
#include <vcl/vcom_socket_wrapper.h>
#include <vcl/vcom.h>
#include <sys/time.h>
#include <vcl/vppcom.h>
#include <vcl/vcom_socket.h>
/* GCC have printf type attribute check. */
#ifdef HAVE_FUNCTION_ATTRIBUTE_FORMAT
#define PRINTF_ATTRIBUTE(a,b) \
__attribute__ ((__format__ (__printf__, a, b)))
#else
#define PRINTF_ATTRIBUTE(a,b)
#endif /* HAVE_FUNCTION_ATTRIBUTE_FORMAT */
#define HAVE_CONSTRUCTOR_ATTRIBUTE
#ifdef HAVE_CONSTRUCTOR_ATTRIBUTE
#define CONSTRUCTOR_ATTRIBUTE \
__attribute__ ((constructor))
#else
#define CONSTRUCTOR_ATTRIBUTE
#endif /* HAVE_CONSTRUCTOR_ATTRIBUTE */
#define HAVE_DESTRUCTOR_ATTRIBUTE
#ifdef HAVE_DESTRUCTOR_ATTRIBUTE
#define DESTRUCTOR_ATTRIBUTE \
__attribute__ ((destructor))
#else
#define DESTRUCTOR_ATTRIBUTE
#endif
#define HAVE_ADDRESS_SANITIZER_ATTRIBUTE
#ifdef HAVE_ADDRESS_SANITIZER_ATTRIBUTE
#define DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE \
__attribute__((no_sanitize_address))
#else
#define DO_NOT_SANITIZE_ADDRESS_ATTRIBUTE
#endif
#define VCOM_SOCKET_FD_MAX 0x10000
static char vcom_app_name[MAX_VCOM_APP_NAME];
/*
* RETURN: 0 on success or -1 on error.
* */
int
vcom_set_app_name (char *__app_name)
{
return snprintf (vcom_app_name, MAX_VCOM_APP_NAME, "vcom-%s-%d",
__app_name, getpid ()) < 0 ? -1 : 0;
}
static char *
vcom_get_app_name ()
{
if (vcom_app_name[0] == '\0')
{
snprintf (vcom_app_name, MAX_VCOM_APP_NAME, "vcom-app-%d", getpid ());
}
return vcom_app_name;
}
/*
* 1 if init, 0 otherwise
*/
static int is_vcom_init;
/*
* TBD: Make it thread safe
*/
/*
* constructor function called before main is called
* RETURN: 0 on success -1 on failure
* */
static inline int
vcom_init ()
{
pid_t pid = getpid ();
if (!is_vcom_init)
{
if (vppcom_app_create (vcom_get_app_name ()) != 0)
{
printf ("\n[%d] vcom_init...failed!\n", pid);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] vcom_init: vppcom_app_create failed!\n", pid);
return -1;
}
if (vcom_socket_main_init () != 0)
{
printf ("\n[%d] vcom_init...failed!\n", pid);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] vcom_init: vcom_socket_main_init failed!\n", pid);
return -1;
}
is_vcom_init = 1;
printf ("\n[%d] vcom_init...done!\n", pid);
}
return 0;
}
static inline void
vcom_destroy (void)
{
pid_t pid = getpid ();
if (is_vcom_init)
{
vcom_socket_main_destroy ();
vppcom_app_destroy ();
is_vcom_init = 0;
fprintf (stderr, "\n[%d] vcom_destroy...done!\n", pid);
}
}
static inline int
is_vcom_socket_fd (int fd)
{
return vcom_socket_is_vcom_fd (fd);
}
static inline int
is_vcom_epfd (int epfd)
{
return vcom_socket_is_vcom_epfd (epfd);
}
/*
*
* Generic glibc fd api
*
*/
/* Close the file descriptor FD.
This function is a cancellation point and therefore
not marked with __THROW. */
/*
* PRE: is_vcom_socket_fd(__fd) == 1
* RETURN: 0 on success and -1 for errors.
* */
int
vcom_close (int __fd)
{
if (vcom_init () != 0)
{
return -1;
}
if (vcom_socket_close (__fd) != 0)
{
return -1;
}
return 0;
}
/*
* RETURN: 0 on success, or -1 on error
*/
int
close (int __fd)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd) || is_vcom_epfd (__fd))
{
if (VCOM_DEBUG > 0)
fprintf (stderr, "[%d] close: fd %d\n", pid, __fd);
rv = vcom_close (__fd);
if (VCOM_DEBUG > 0)
fprintf (stderr, "[%d] close: vcom_close() returned %d\n", pid, rv);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_close (__fd);
}
/* Read NBYTES into BUF from FD. Return the
number read, -1 for errors or 0 for EOF.
This function is a cancellation point and therefore
not marked with __THROW. */
ssize_t
vcom_read (int __fd, void *__buf, size_t __nbytes)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_read (__fd, __buf, __nbytes);
}
ssize_t
read (int __fd, void *__buf, size_t __nbytes)
{
ssize_t size = 0;
pid_t pid = getpid ();
pthread_t tid = pthread_self ();
if (is_vcom_socket_fd (__fd))
{
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d][%lu (0x%lx)] read:1 "
"'%04d'='%04d', '%p', '%04d'\n",
pid, (unsigned long) tid, (unsigned long) tid,
(int) size, __fd, __buf, (int) __nbytes);
size = vcom_read (__fd, __buf, __nbytes);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d][%lu (0x%lx)] read:2 "
"'%04d'='%04d', '%p', '%04d'\n",
pid, (unsigned long) tid, (unsigned long) tid,
(int) size, __fd, __buf, (int) __nbytes);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_read (__fd, __buf, __nbytes);
}
ssize_t
vcom_readv (int __fd, const struct iovec * __iov, int __iovcnt)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_readv (__fd, __iov, __iovcnt);
}
ssize_t
readv (int __fd, const struct iovec * __iov, int __iovcnt)
{
ssize_t size = 0;
if (is_vcom_socket_fd (__fd))
{
size = vcom_readv (__fd, __iov, __iovcnt);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
else
return libc_readv (__fd, __iov, __iovcnt);
}
/* Write N bytes of BUF to FD. Return the number written, or -1.
This function is a cancellation point and therefore
not marked with __THROW. */
ssize_t
vcom_write (int __fd, const void *__buf, size_t __n)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_write (__fd, (void *) __buf, __n);
}
ssize_t
write (int __fd, const void *__buf, size_t __n)
{
ssize_t size = 0;
pid_t pid = getpid ();
pthread_t tid = pthread_self ();
if (is_vcom_socket_fd (__fd))
{
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d][%lu (0x%lx)] write:1 "
"'%04d'='%04d', '%p', '%04d'\n",
pid, (unsigned long) tid, (unsigned long) tid,
(int) size, __fd, __buf, (int) __n);
size = vcom_write (__fd, __buf, __n);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d][%lu (0x%lx)] write:2 "
"'%04d'='%04d', '%p', '%04d'\n",
pid, (unsigned long) tid, (unsigned long) tid,
(int) size, __fd, __buf, (int) __n);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_write (__fd, __buf, __n);
}
ssize_t
vcom_writev (int __fd, const struct iovec * __iov, int __iovcnt)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_writev (__fd, __iov, __iovcnt);
}
ssize_t
writev (int __fd, const struct iovec * __iov, int __iovcnt)
{
ssize_t size = 0;
if (is_vcom_socket_fd (__fd))
{
size = vcom_writev (__fd, __iov, __iovcnt);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
else
return libc_writev (__fd, __iov, __iovcnt);
}
/* Do the file control operation described by CMD on FD.
The remaining arguments are interpreted depending on CMD.
This function is a cancellation point and therefore
not marked with __THROW. */
int
vcom_fcntl_va (int __fd, int __cmd, va_list __ap)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_fcntl_va (__fd, __cmd, __ap);
}
int
vcom_fcntl (int __fd, int __cmd, ...)
{
int rv = -1;
va_list ap;
if (is_vcom_socket_fd (__fd))
{
va_start (ap, __cmd);
rv = vcom_fcntl_va (__fd, __cmd, ap);
va_end (ap);
}
return rv;
}
int
fcntl (int __fd, int __cmd, ...)
{
int rv;
va_list ap;
pid_t pid = getpid ();
va_start (ap, __cmd);
if (is_vcom_socket_fd (__fd))
{
rv = vcom_fcntl_va (__fd, __cmd, ap);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] fcntl: "
"'%04d'='%04d', '%04d'\n", pid, rv, __fd, __cmd);
if (rv < 0)
{
errno = -rv;
rv = -1;
}
goto out;
}
rv = libc_vfcntl (__fd, __cmd, ap);
out:
va_end (ap);
return rv;
}
int
vcom_ioctl_va (int __fd, unsigned long int __cmd, va_list __ap)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_ioctl_va (__fd, __cmd, __ap);
}
int
vcom_ioctl (int __fd, unsigned long int __cmd, ...)
{
int rv = -1;
va_list ap;
if (is_vcom_socket_fd (__fd))
{
va_start (ap, __cmd);
rv = vcom_ioctl_va (__fd, __cmd, ap);
va_end (ap);
}
return rv;
}
int
ioctl (int __fd, unsigned long int __cmd, ...)
{
int rv;
va_list ap;
pid_t pid = getpid ();
va_start (ap, __cmd);
if (is_vcom_socket_fd (__fd))
{
rv = vcom_ioctl_va (__fd, __cmd, ap);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] ioctl: "
"'%04d'='%04d', '%04ld'\n", pid, rv, __fd, __cmd);
if (rv < 0)
{
errno = -rv;
rv = -1;
}
goto out;
}
rv = libc_vioctl (__fd, __cmd, ap);
out:
va_end (ap);
return rv;
}
/*
* Check the first NFDS descriptors each in READFDS (if not NULL) for
* read readiness, in WRITEFDS (if not NULL) for write readiness,
* and in EXCEPTFDS (if not NULL) for exceptional conditions.
* If TIMEOUT is not NULL, time out after waiting the interval
* specified therein. Returns the number of ready descriptors,
* or -1 for errors.
*
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
/*
* clear all vcom FDs from fd_sets __readfds, __writefds and
* __exceptfds and update the new nfds
*
* new nfds is the highest-numbered file descriptor
* in any of the three sets, plus 1
*
* Return the number of file descriptors contained in the
* three descriptor sets. ie. the total number of the bits
* that are set in __readfds, __writefds and __exceptfds
*/
static inline int
vcom_fd_clear (int __nfds,
int *__new_nfds,
fd_set * __restrict __readfds,
fd_set * __restrict __writefds,
fd_set * __restrict __exceptfds)
{
int fd;
/* invalid max_fd is -1 */
int max_fd = -1;
int nfd = 0;
/* clear all vcom fd from the sets */
for (fd = 0; fd < __nfds; fd++)
{
/* clear vcom fd from set */
/*
* F fd set
*/
#define _(F) \
if ((F) && FD_ISSET (fd, (F))) \
{ \
if (is_vcom_socket_fd (fd)) \
{ \
FD_CLR (fd, (F)); \
} \
}
_(__readfds);
_(__writefds);
_(__exceptfds);
#undef _
}
/*
* compute nfd and __new_nfds
*/
for (fd = 0; fd < __nfds; fd++)
{
/*
* F fd set
*/
#define _(F) \
if ((F) && FD_ISSET (fd, (F))) \
{ \
if (fd > max_fd) \
{ \
max_fd = fd; \
} \
++nfd; \
}
_(__readfds);
_(__writefds);
_(__exceptfds);
#undef _
}
*__new_nfds = max_fd != -1 ? max_fd + 1 : 0;
return nfd;
}
/*
* Return the number of file descriptors contained in the
* three descriptor sets. ie. the total number of the bits
* that are set in __readfds, __writefds and __exceptfds
*/
static inline int
vcom_fd_set (int __nfds,
/* dest */
int *__new_nfds,
fd_set * __restrict __readfds,
fd_set * __restrict __writefds, fd_set * __restrict __exceptfds,
/* src */
fd_set * __restrict __saved_readfds,
fd_set * __restrict __saved_writefds,
fd_set * __restrict __saved_exceptfds)
{
int fd;
/* invalid max_fd is -1 */
int max_fd = -1;
int nfd = 0;
for (fd = 0; fd < __nfds; fd++)
{
/*
* F fd set
* S saved fd set
*/
#define _(S,F) \
if ((F) && (S) && FD_ISSET (fd, (S))) \
{ \
if (is_vcom_socket_fd (fd)) \
{ \
FD_SET (fd, (F)); \
} \
}
_(__saved_readfds, __readfds);
_(__saved_writefds, __writefds);
#undef _
}
/*
* compute nfd and __new_nfds
*/
for (fd = 0; fd < __nfds; fd++)
{
/*
* F fd set
*/
#define _(F) \
if ((F) && FD_ISSET (fd, (F))) \
{ \
if (fd > max_fd) \
{ \
max_fd = fd; \
} \
++nfd; \
}
_(__readfds);
_(__writefds);
_(__exceptfds);
#undef _
}
*__new_nfds = max_fd != -1 ? max_fd + 1 : 0;
return nfd;
}
/*
* split select sets(src) into
* vcom sets(dest1) and libc sets(dest2)
*/
static inline void
vcom_fd_set_split (
/* src, select sets */
int nfds,
fd_set * __restrict readfds,
fd_set * __restrict writefds,
fd_set * __restrict exceptfds,
/* dest1, vcom sets */
int *vcom_nfds,
fd_set * __restrict vcom_readfds,
fd_set * __restrict vcom_writefds,
fd_set * __restrict vcom_exceptfds, int *vcom_nfd,
/* dest2, libc sets */
int *libc_nfds,
fd_set * __restrict libc_readfds,
fd_set * __restrict libc_writefds,
fd_set * __restrict libc_exceptfds, int *libc_nfd)
{
int fd;
/* vcom */
/* invalid max_fd is -1 */
int vcom_max_fd = -1;
int vcom_nfd2 = 0;
/* libc */
/* invalid max_fd is -1 */
int libc_max_fd = -1;
int libc_nfd2 = 0;
for (fd = 0; fd < nfds; fd++)
{
/*
* S select fd set
* V vcom fd set
* L libc fd set
*/
#define _(S,V,L) \
if ((S) && FD_ISSET (fd, (S))) \
{ \
if (is_vcom_socket_fd (fd)) \
{ \
if ((V)) \
{ \
FD_SET(fd, (V)); \
if (fd > vcom_max_fd) \
{ \
vcom_max_fd = fd; \
} \
++vcom_nfd2; \
} \
} \
else \
{ \
if ((L)) \
{ \
FD_SET(fd, (L)); \
if (fd > libc_max_fd) \
{ \
libc_max_fd = fd; \
} \
++libc_nfd2; \
} \
} \
}
_(readfds, vcom_readfds, libc_readfds);
_(writefds, vcom_writefds, libc_writefds);
_(exceptfds, vcom_exceptfds, libc_exceptfds);
#undef _
}
if (vcom_nfds)
*vcom_nfds = vcom_max_fd != -1 ? vcom_max_fd + 1 : 0;
if (vcom_nfd)
*vcom_nfd = vcom_nfd2;
if (libc_nfds)
*libc_nfds = libc_max_fd != -1 ? libc_max_fd + 1 : 0;
if (libc_nfd)
*libc_nfd = libc_nfd2;
}
/*
* merge vcom sets(src1) and libc sets(src2)
* into select sets(dest)
*/
static inline void
vcom_fd_set_merge (
/* dest, select sets */
int *nfds,
fd_set * __restrict readfds,
fd_set * __restrict writefds,
fd_set * __restrict exceptfds, int *nfd,
/* src1, vcom sets */
int vcom_nfds,
fd_set * __restrict vcom_readfds,
fd_set * __restrict vcom_writefds,
fd_set * __restrict vcom_exceptfds, int vcom_nfd,
/* src2, libc sets */
int libc_nfds,
fd_set * __restrict libc_readfds,
fd_set * __restrict libc_writefds,
fd_set * __restrict libc_exceptfds, int libc_nfd)
{
int fd;
/* invalid max_fd is -1 */
int max_fd = -1;
int nfd2 = 0;
/* FD_BIT_OR
*
* dest |= src at current bit index
* update MAX and NFD of dest fd set
*
*
* FS source fd set
* FD dest fd set
* BI bit index
* MAX current max_fd of dest fd sets
* NFD current nfd of dest fd sets
* N nfds of source fd set
*/
#define FD_BIT_OR(FD,FS,BI, \
MAX,NFD) \
if ((FS) && (FD) && FD_ISSET ((BI), (FS))) \
{ \
FD_SET ((BI), (FD)); \
if ((BI) > (MAX)) \
{ \
(MAX) = (BI); \
} \
++(NFD); \
}
/* FD_RWE_SET_OR */
/*
* SR,SW,SE source RWE fd sets
* DR,DW,DE dest RWE fd sets
* BI bit index
* NFDS nfds of source fd sets
* MAX current max_fd of dest fd sets
* NFD current nfd of dest fd sets
*/
#define FD_RWE_SETS_OR(DR,DW,DE, \
SR,SW,SE, \
BI,NFDS, \
MAX,NFD) \
do \
{ \
for ((BI) = 0; (BI) < (NFDS); (BI)++) \
{ \
FD_BIT_OR((DR), (SR), (BI), (MAX), (NFD)); \
FD_BIT_OR((DW), (SW), (BI), (MAX), (NFD)); \
FD_BIT_OR((DE), (SE), (BI), (MAX), (NFD)); \
} \
} \
while (0);
/* source(vcom) to dest(select) rwe fd sets */
FD_RWE_SETS_OR (readfds, writefds, exceptfds,
vcom_readfds, vcom_writefds, vcom_exceptfds,
fd, vcom_nfds, max_fd, nfd2);
/* source(libc) to dest(select) rwe fd sets */
FD_RWE_SETS_OR (readfds, writefds, exceptfds,
libc_readfds, libc_writefds, libc_exceptfds,
fd, libc_nfds, max_fd, nfd2);
#undef FD_RWE_SETS_OR
#undef FD_BIT_OR
if (nfds)
*nfds = max_fd != -1 ? max_fd + 1 : 0;
if (nfd)
*nfd = nfd2;
}
/*
* RETURN 1 if fds is NULL or empty. 0 otherwise
*/
static inline int
fd_set_iszero (fd_set * __restrict fds)
{
int fd;
/* NULL fds */
if (!fds)
return 1;
for (fd = 0; fd < FD_SETSIZE; fd++)
{
if (FD_ISSET (fd, fds))
{
/* non-empty fds */
return 0;
}
}
/* empty fds */
return 1;
}
/*
* ################
* kernel time64.h
* ################
* */
typedef long int s64;
typedef unsigned long int u64;
typedef long long int __s64;
typedef unsigned long long int __u64;
typedef __s64 time64_t;
typedef __u64 timeu64_t;
/* Parameters used to convert the timespec values: */
#define MSEC_PER_SEC 1000L
#define USEC_PER_MSEC 1000L
#define NSEC_PER_USEC 1000L
#define NSEC_PER_MSEC 1000000L
#define USEC_PER_SEC 1000000L
#define NSEC_PER_SEC 1000000000L
#define FSEC_PER_SEC 1000000000000000LL
/*
* ################
* kernel time.h
* ################
* */
#define TIME_T_MAX (time_t)((1UL << ((sizeof(time_t) << 3) - 1)) - 1)
#ifdef VCOM_USE_TIMESPEC_EQUAL
static inline int
timespec_equal (const struct timespec *a, const struct timespec *b)
{
return (a->tv_sec == b->tv_sec) && (a->tv_nsec == b->tv_nsec);
}
#endif
/*
* lhs < rhs: return <0
* lhs == rhs: return 0
* lhs > rhs: return >0
*/
static inline int
timespec_compare (const struct timespec *lhs, const struct timespec *rhs)
{
if (lhs->tv_sec < rhs->tv_sec)
return -1;
if (lhs->tv_sec > rhs->tv_sec)
return 1;
return lhs->tv_nsec - rhs->tv_nsec;
}
#ifdef VCOM_USE_TIMEVAL_COMPARE
static inline int
timeval_compare (const struct timeval *lhs, const struct timeval *rhs)
{
if (lhs->tv_sec < rhs->tv_sec)
return -1;
if (lhs->tv_sec > rhs->tv_sec)
return 1;
return lhs->tv_usec - rhs->tv_usec;
}
#endif
extern void set_normalized_timespec (struct timespec *ts, time_t sec,
s64 nsec);
static inline struct timespec
timespec_add (struct timespec lhs, struct timespec rhs)
{
struct timespec ts_delta;
set_normalized_timespec (&ts_delta, lhs.tv_sec + rhs.tv_sec,
lhs.tv_nsec + rhs.tv_nsec);
return ts_delta;
}
/*
* sub = lhs - rhs, in normalized form
*/
static inline struct timespec
timespec_sub (struct timespec lhs, struct timespec rhs)
{
struct timespec ts_delta;
set_normalized_timespec (&ts_delta, lhs.tv_sec - rhs.tv_sec,
lhs.tv_nsec - rhs.tv_nsec);
return ts_delta;
}
/*
* ################
* kernel time.c
* ################
* */
/**
* set_normalized_timespec - set timespec sec and nsec parts and normalize
*
* @ts: pointer to timespec variable to be set
* @sec: seconds to set
* @nsec: nanoseconds to set
*
* Set seconds and nanoseconds field of a timespec variable and
* normalize to the timespec storage format
*
* Note: The tv_nsec part is always in the range of
* 0 <= tv_nsec < NSEC_PER_SEC
* For negative values only the tv_sec field is negative !
*/
void
set_normalized_timespec (struct timespec *ts, time_t sec, s64 nsec)
{
while (nsec >= NSEC_PER_SEC)
{
/*
* The following asm() prevents the compiler from
* optimising this loop into a modulo operation. See
* also __iter_div_u64_rem() in include/linux/time.h
*/
asm ("":"+rm" (nsec));
nsec -= NSEC_PER_SEC;
++sec;
}
while (nsec < 0)
{
asm ("":"+rm" (nsec));
nsec += NSEC_PER_SEC;
--sec;
}
ts->tv_sec = sec;
ts->tv_nsec = nsec;
}
#define vcom_timerisvalid(tvp) (!((tvp)->tv_sec < 0 || (tvp)->tv_usec < 0))
/* Macros for converting between `struct timeval' and `struct timespec'. */
#define VCOM_TIMEVAL_TO_TIMESPEC(tv, ts) { \
(ts)->tv_sec = (tv)->tv_sec; \
(ts)->tv_nsec = (tv)->tv_usec * 1000; \
}
#define VCOM_TIMESPEC_TO_TIMEVAL(tv, ts) { \
(tv)->tv_sec = (ts)->tv_sec; \
(tv)->tv_usec = (ts)->tv_nsec / 1000; \
}
static inline int
vcom_select_impl (int vcom_nfds, fd_set * __restrict vcom_readfds,
fd_set * __restrict vcom_writefds,
fd_set * __restrict vcom_exceptfds,
struct timeval *__restrict timeout)
{
return vcom_socket_select (vcom_nfds, vcom_readfds,
vcom_writefds, vcom_exceptfds, timeout);
}
int
vcom_select (int __nfds, fd_set * __restrict __readfds,
fd_set * __restrict __writefds,
fd_set * __restrict __exceptfds,
struct timeval *__restrict __timeout)
{
int rv;
int rv2 = 0;
pid_t pid = getpid ();
int timedout = 0;
/* block indefinitely */
int no_timeout = 0;
int first_clock_gettime_failed = 0;
/* timeout value in units of timespec */
struct timespec timeout_ts;
struct timespec start_time, now, end_time;
/* select sets attributes - after merge */
int new_nfds = 0;
int new_nfd = -1;
/* vcom */
int vcom_nfds = 0;
fd_set vcom_readfds;
fd_set vcom_writefds;
fd_set vcom_exceptfds;
int vcom_nfd = -1;
/* libc */
int libc_nfds = 0;
fd_set libc_readfds;
fd_set libc_writefds;
fd_set libc_exceptfds;
int libc_nfd = -1;
/* for polling */
struct timeval tv = {.tv_sec = 0,.tv_usec = 0 };
/* validate __timeout */
if (__timeout)
{
/* validate tv_sec */
/* bogus */
if (!vcom_timerisvalid (__timeout))
{
rv = -EINVAL;
goto select_done;
}
/* validate tv_usec */
/* TBD: */
/* init timeout_ts */
VCOM_TIMEVAL_TO_TIMESPEC (__timeout, &timeout_ts);
set_normalized_timespec (&timeout_ts,
timeout_ts.tv_sec, timeout_ts.tv_nsec);
}
rv = clock_gettime (CLOCK_MONOTONIC, &start_time);
if (rv == -1)
{
rv = -errno;
first_clock_gettime_failed = 1;
goto select_done;
}
/* init end_time */
if (__timeout)
{
if (timerisset (__timeout))
{
end_time = timespec_add (start_time, timeout_ts);
}
else
{
/*
* if both fields of the timeout structure are zero,
* then select returns immediately
* */
end_time = start_time;
}
}
else
{
/* block indefinitely */
no_timeout = 1;
}
if (vcom_init () != 0)
{
rv = -1;
goto select_done;
}
/* validate __nfds */
if (__nfds < 0 || __nfds > FD_SETSIZE)
{
rv = -EINVAL;
goto select_done;
}
/*
* usleep(3) emulation
* */
/* call libc_select() with a finite timeout and
* no file descriptors or empty fd sets and
* zero nfds */
if (__nfds == 0 &&
(!__readfds || fd_set_iszero (__readfds)) &&
(!__writefds || fd_set_iszero (__writefds)) &&
(!__exceptfds || fd_set_iszero (__exceptfds)))
{
if (__timeout)
{
rv = libc_select (__nfds,
__readfds, __writefds, __exceptfds, __timeout);
if (rv == -1)
rv = -errno;
}
else
{
/* TBD: block indefinitely or return -EINVAL */
rv = -EINVAL;
}
goto select_done;
}
/* init once before the polling loop */
/* zero vcom and libc fd sets */
/*
* S select fd set
* V vcom fd set
* L libc fd set
*/
#define _(S,V,L) \
if ((S)) \
{ \
FD_ZERO ((V)); \
FD_ZERO ((L)); \
}
_(__readfds, &vcom_readfds, &libc_readfds);
_(__writefds, &vcom_writefds, &libc_writefds);
_(__exceptfds, &vcom_exceptfds, &libc_exceptfds);
#undef _
new_nfds = 0;
new_nfd = -1;
vcom_nfds = 0;
vcom_nfd = -1;
libc_nfds = 0;
libc_nfd = -1;
vcom_fd_set_split (
/* src, select sets */
__nfds, __readfds, __writefds, __exceptfds,
/* dest1, vcom sets */
__readfds || __writefds || __exceptfds ?
&vcom_nfds : NULL,
__readfds ? &vcom_readfds : NULL,
__writefds ? &vcom_writefds : NULL,
__exceptfds ? &vcom_exceptfds : NULL,
__readfds || __writefds || __exceptfds ?
&vcom_nfd : NULL,
/* dest2, libc sets */
__readfds || __writefds || __exceptfds ?
&libc_nfds : NULL,
__readfds ? &libc_readfds : NULL,
__writefds ? &libc_writefds : NULL,
__exceptfds ? &libc_exceptfds : NULL,
__readfds || __writefds || __exceptfds ?
&libc_nfd : NULL);
/*
* polling loop
* */
do
{
new_nfd = -1;
vcom_nfd = -1;
libc_nfd = -1;
/*
* if both fields of timeval structure are zero,
* vcom_select_impl and libc_select returns immediately.
* useful for polling and ensure fairness among
* file descriptors watched.
*/
/* for polling */
tv.tv_sec = 0;
tv.tv_usec = 0;
/* select on vcom fds */
if (vcom_nfds)
{
vcom_nfd = vcom_select_impl (vcom_nfds,
__readfds ? &vcom_readfds : NULL,
__writefds ? &vcom_writefds : NULL,
__exceptfds ? &vcom_exceptfds : NULL,
&tv);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d] select vcom: "
"'%04d'='%04d'\n", pid, vcom_nfd, vcom_nfds);
if (vcom_nfd < 0)
{
rv = vcom_nfd;
goto select_done;
}
}
/* select on libc fds */
if (libc_nfds)
{
libc_nfd = libc_select (libc_nfds,
__readfds ? &libc_readfds : NULL,
__writefds ? &libc_writefds : NULL,
__exceptfds ? &libc_exceptfds : NULL, &tv);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d] select libc: "
"'%04d'='%04d'\n", pid, libc_nfd, libc_nfds);
if (libc_nfd < 0)
{
/* tv becomes undefined */
libc_nfd = errno;
rv = libc_nfd;
goto select_done;
}
}
/* check if any file descriptors changed status */
if ((vcom_nfds && vcom_nfd > 0) || (libc_nfds && libc_nfd > 0))
{
/* zero the sets before merge and exit */
/*
* F fd set
*/
#define _(F) \
if ((F)) \
{ \
FD_ZERO ((F)); \
}
_(__readfds);
_(__writefds);
_(__exceptfds);
#undef _
new_nfds = 0;
new_nfd = -1;
/*
* on exit, sets are modified in place to indicate which
* file descriptors actually changed status
* */
vcom_fd_set_merge (
/* dest, select sets */
&new_nfds,
__readfds, __writefds, __exceptfds, &new_nfd,
/* src1, vcom sets */
vcom_nfds,
__readfds ? &vcom_readfds : NULL,
__writefds ? &vcom_writefds : NULL,
__exceptfds ? &vcom_exceptfds : NULL, vcom_nfd,
/* src2, libc sets */
libc_nfds,
__readfds ? &libc_readfds : NULL,
__writefds ? &libc_writefds : NULL,
__exceptfds ? &libc_exceptfds : NULL, libc_nfd);
/*
* return the number of file descriptors contained in the
* three returned sets
* */
rv = 0;
/*
* for documentation
*
* if(vcom_nfd > 0)
* rv += vcom_nfd;
* if(libc_nfd > 0)
* rv += libc_nfd;
*/
rv = new_nfd == -1 ? 0 : new_nfd;
goto select_done;
}
rv = clock_gettime (CLOCK_MONOTONIC, &now);
if (rv == -1)
{
rv = -errno;
goto select_done;
}
}
while (no_timeout || timespec_compare (&now, &end_time) < 0);
/* timeout expired before anything interesting happened */
timedout = 1;
rv = 0;
select_done:
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] vselect1: " "'%04d'='%04d'\n", pid, rv, __nfds);
/*
* modify timeout parameter to reflect the amount of time not slept
* */
if (__timeout)
{
if (vcom_timerisvalid (__timeout))
{
/* timeout expired */
if (timedout)
{
timerclear (__timeout);
}
else if (!first_clock_gettime_failed)
{
rv2 = clock_gettime (CLOCK_MONOTONIC, &now);
if (rv2 == -1)
{
rv = -errno;
}
else
{
struct timespec ts_delta;
ts_delta = timespec_sub (end_time, now);
VCOM_TIMESPEC_TO_TIMEVAL (__timeout, &ts_delta);
}
}
}
}
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] vselect2: " "'%04d',='%04d'\n", pid, rv, __nfds);
return rv;
}
int
vcom_select_internal (int __nfds, fd_set * __restrict __readfds,
fd_set * __restrict __writefds,
fd_set * __restrict __exceptfds,
struct timeval *__restrict __timeout)
{
int rv;
int new_nfds = 0;
int nfd = 0;
pid_t pid = getpid ();
fd_set saved_readfds;
fd_set saved_writefds;
fd_set saved_exceptfds;
/* validate __nfds */
if (__nfds < 0)
{
errno = EINVAL;
return -1;
}
/* validate __timeout */
if (__timeout)
{
/* validate tv_sec */
/* bogus */
if (__timeout->tv_sec < 0 || __timeout->tv_usec < 0)
{
errno = EINVAL;
return -1;
}
/* validate tv_usec */
/* TBD: */
}
/* init saved_x fds */
if (__readfds)
{
saved_readfds = *__readfds;
/*
memcpy (&saved_readfds, __readfds, sizeof (*__readfds));
*/
}
else
{
FD_ZERO (&saved_readfds);
}
if (__writefds)
{
saved_writefds = *__writefds;
/*
memcpy (&saved_writefds, __writefds, sizeof (*__writefds));
*/
}
else
{
FD_ZERO (&saved_writefds);
}
if (__exceptfds)
{
saved_exceptfds = *__exceptfds;
/*
memcpy (&saved_exceptfds, __exceptfds, sizeof (*__exceptfds));
*/
}
else
{
FD_ZERO (&saved_exceptfds);
}
/* clear vcom fds */
nfd = vcom_fd_clear (__nfds, &new_nfds, __readfds, __writefds, __exceptfds);
/* set to an invalid value */
rv = -2;
/* have kernel fds */
if (new_nfds)
rv = libc_select (new_nfds, __readfds,
__writefds, __exceptfds, __timeout);
if (new_nfds && rv == -1)
{
/* on error, the file descriptor sets are unmodified */
if (__readfds)
*__readfds = saved_readfds;
if (__writefds)
*__writefds = saved_writefds;
if (__exceptfds)
*__exceptfds = saved_exceptfds;
return rv;
}
else if ((new_nfds && rv != -1) || (rv == -2))
{
/* restore vcom fds */
nfd = vcom_fd_set (__nfds,
&new_nfds,
__readfds,
__writefds,
__exceptfds,
&saved_readfds, &saved_writefds, &saved_exceptfds);
rv = nfd;
}
if (VCOM_DEBUG > 0)
fprintf (stderr, "[%d] select: " "'%04d'='%04d'\n", pid, rv, __nfds);
return rv;
}
int
select (int __nfds, fd_set * __restrict __readfds,
fd_set * __restrict __writefds,
fd_set * __restrict __exceptfds, struct timeval *__restrict __timeout)
{
int rv = 0;
pid_t pid = getpid ();
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] select1: " "'%04d'='%04d'\n", pid, rv, __nfds);
rv = vcom_select (__nfds, __readfds, __writefds, __exceptfds, __timeout);
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] select2: " "'%04d'='%04d'\n", pid, rv, __nfds);
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
#ifdef __USE_XOPEN2K
/*
* Same as above only that the TIMEOUT value is given with higher
* resolution and a sigmask which is been set temporarily. This
* version should be used.
*
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
int
vcom_pselect (int __nfds, fd_set * __restrict __readfds,
fd_set * __restrict __writefds,
fd_set * __restrict __exceptfds,
const struct timespec *__restrict __timeout,
const __sigset_t * __restrict __sigmask)
{
int fd;
int vcom_nfds = 0;
for (fd = 0; fd < __nfds; fd++)
{
if (__readfds && FD_ISSET (fd, __readfds))
{
if (is_vcom_socket_fd (fd))
{
vcom_nfds++;
}
}
if (__writefds && FD_ISSET (fd, __writefds))
{
if (is_vcom_socket_fd (fd))
{
vcom_nfds++;
}
}
if (__exceptfds && FD_ISSET (fd, __exceptfds))
{
if (is_vcom_socket_fd (fd))
{
FD_CLR (fd, __exceptfds);
}
}
}
return vcom_nfds;
}
int
pselect (int __nfds, fd_set * __restrict __readfds,
fd_set * __restrict __writefds,
fd_set * __restrict __exceptfds,
const struct timespec *__restrict __timeout,
const __sigset_t * __restrict __sigmask)
{
int rv;
int new_nfds = 0;
int nfd = 0;
pid_t pid = getpid ();
fd_set saved_readfds;
fd_set saved_writefds;
fd_set saved_exceptfds;
/* validate __nfds */
if (__nfds < 0)
{
errno = EINVAL;
return -1;
}
/* validate __timeout */
if (__timeout)
{
/* validate tv_sec */
/* bogus */
if (__timeout->tv_sec < 0 || __timeout->tv_nsec < 0)
{
errno = EINVAL;
return -1;
}
/* validate tv_usec */
/* TBD: */
}
/* init saved fds */
if (__readfds)
{
saved_readfds = *__readfds;
/*
memcpy (&saved_readfds, __readfds, sizeof (*__readfds));
*/
}
else
{
FD_ZERO (&saved_readfds);
}
if (__writefds)
{
saved_writefds = *__writefds;
/*
memcpy (&saved_writefds, __writefds, sizeof (*__writefds));
*/
}
else
{
FD_ZERO (&saved_writefds);
}
if (__exceptfds)
{
saved_exceptfds = *__exceptfds;
/*
memcpy (&saved_exceptfds, __exceptfds, sizeof (*__exceptfds));
*/
}
else
{
FD_ZERO (&saved_exceptfds);
}
/* clear vcom fds */
nfd = vcom_fd_clear (__nfds, &new_nfds, __readfds, __writefds, __exceptfds);
/* set to an invalid value */
rv = -2;
if (new_nfds)
rv = libc_pselect (new_nfds,
__readfds,
__writefds, __exceptfds, __timeout, __sigmask);
if (new_nfds && rv == -1)
{
/* on error, the file descriptor sets are unmodified */
if (__readfds)
*__readfds = saved_readfds;
if (__writefds)
*__writefds = saved_writefds;
if (__exceptfds)
*__exceptfds = saved_exceptfds;
return rv;
}
else if ((new_nfds && rv != -1) || (rv == -2))
{
/* restore vcom fds */
nfd = vcom_fd_set (__nfds,
&new_nfds,
__readfds,
__writefds,
__exceptfds,
&saved_readfds, &saved_writefds, &saved_exceptfds);
rv = nfd;
}
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] pselect: " "'%04d'='%04d'\n", pid, rv, __nfds);
return rv;
}
#endif
/*
*
* Socket specific glibc api
*
*/
/* Create a new socket of type TYPE in domain DOMAIN, using
* protocol PROTOCOL. If PROTOCOL is zero, one is chosen
* automatically. Returns a file descriptor for the new socket,
* or -1 for errors.
* RETURN: a valid file descriptor for the new socket,
* or -1 for errors.
* */
int
vcom_socket (int __domain, int __type, int __protocol)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_socket (__domain, __type, __protocol);
}
int
socket (int __domain, int __type, int __protocol)
{
int rv;
pid_t pid = getpid ();
pthread_t tid = pthread_self ();
/* handle domains implemented by vpp */
switch (__domain)
{
case AF_INET:
case AF_INET6:
/* handle types implemented by vpp */
switch (__type & ~(SOCK_CLOEXEC | SOCK_NONBLOCK))
{
case SOCK_STREAM:
case SOCK_DGRAM:
if (VCOM_DEBUG > 0)
vcom_socket_main_show ();
rv = vcom_socket (__domain, __type, __protocol);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d][%lu (0x%lx)] socket: "
"'%04d'= D='%04d', T='%04d', P='%04d'\n",
pid, (unsigned long) tid, (unsigned long) tid,
rv, __domain, __type, __protocol);
if (VCOM_DEBUG > 0)
vcom_socket_main_show ();
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
break;
default:
goto CALL_GLIBC_SOCKET_API;
break;
}
break;
default:
goto CALL_GLIBC_SOCKET_API;
break;
}
CALL_GLIBC_SOCKET_API:
return libc_socket (__domain, __type, __protocol);
}
/*
* Create two new sockets, of type TYPE in domain DOMAIN and using
* protocol PROTOCOL, which are connected to each other, and put file
* descriptors for them in FDS[0] and FDS[1]. If PROTOCOL is zero,
* one will be chosen automatically.
* Returns 0 on success, -1 for errors.
* */
int
vcom_socketpair (int __domain, int __type, int __protocol, int __fds[2])
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_socketpair (__domain, __type, __protocol, __fds);
}
int
socketpair (int __domain, int __type, int __protocol, int __fds[2])
{
int rv;
pid_t pid = getpid ();
/* handle domains implemented by vpp */
switch (__domain)
{
case AF_INET:
case AF_INET6:
/* handle types implemented by vpp */
switch (__type)
{
case SOCK_STREAM:
case SOCK_DGRAM:
rv = vcom_socketpair (__domain, __type, __protocol, __fds);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] socketpair: "
"'%04d'= D='%04d', T='%04d', P='%04d'\n",
pid, rv, __domain, __type, __protocol);
if (rv < 0)
{
errno = -rv;
return -1;
}
return 0;
break;
default:
goto CALL_GLIBC_SOCKET_API;
break;
}
break;
default:
goto CALL_GLIBC_SOCKET_API;
break;
}
CALL_GLIBC_SOCKET_API:
return libc_socketpair (__domain, __type, __protocol, __fds);
}
/*
* Give the socket FD the local address ADDR
* (which is LEN bytes long).
* */
int
vcom_bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
{
int rv;
if (vcom_init () != 0)
{
return -1;
}
/* validate __len */
switch (__addr->sa_family)
{
case AF_INET:
if (__len != sizeof (struct sockaddr_in))
return -EINVAL;
break;
case AF_INET6:
if (__len != sizeof (struct sockaddr_in6))
return -EINVAL;
break;
default:
return -1;
break;
}
/* handle domains implemented by vpp */
switch (__addr->sa_family)
{
case AF_INET:
case AF_INET6:
rv = vcom_socket_bind (__fd, __addr, __len);
return rv;
break;
default:
return -1;
break;
}
return -1;
}
int
bind (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_bind (__fd, __addr, __len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] bind: "
"'%04d'='%04d', '%p', '%04d'\n",
pid, rv, __fd, __addr, __len);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_bind (__fd, __addr, __len);
}
/*
* Put the local address of FD into *ADDR and its length in *LEN.
* */
int
vcom_getsockname (int __fd, __SOCKADDR_ARG __addr,
socklen_t * __restrict __len)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_getsockname (__fd, __addr, __len);
}
int
getsockname (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __len)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_getsockname (__fd, __addr, __len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] getsockname: "
"'%04d'='%04d', '%p', '%p'\n", pid, rv, __fd, __addr, __len);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_getsockname (__fd, __addr, __len);
}
/*
* Open a connection on socket FD to peer at ADDR
* (which LEN bytes long). For connectionless socket types, just set
* the default address to send to and the only address from which to
* accept transmissions. Return 0 on success, -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
int
vcom_connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
{
int rv = -1;
if (vcom_init () != 0)
{
return -1;
}
/* validate __len */
switch (__addr->sa_family)
{
case AF_INET:
if (__len != INET_ADDRSTRLEN)
return -1;
break;
case AF_INET6:
if (__len != INET6_ADDRSTRLEN)
return -1;
break;
default:
return -1;
break;
}
/* handle domains implemented by vpp */
switch (__addr->sa_family)
{
case AF_INET:
case AF_INET6:
rv = vcom_socket_connect (__fd, __addr, __len);
break;
default:
return -1;
break;
}
return rv;
}
int
connect (int __fd, __CONST_SOCKADDR_ARG __addr, socklen_t __len)
{
int rv;
pid_t pid = getpid ();
pthread_t tid = pthread_self ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_connect (__fd, __addr, __len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d][%lu (0x%lx)] connect: "
"'%04d'='%04d', '%p', '%04d'\n",
pid, (unsigned long) tid, (unsigned long) tid,
rv, __fd, __addr, __len);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_connect (__fd, __addr, __len);
}
/*
* Put the address of the peer connected to socket FD into *ADDR
* (which is *LEN bytes long), and its actual length into *LEN.
* */
int
vcom_getpeername (int __fd, __SOCKADDR_ARG __addr,
socklen_t * __restrict __len)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_getpeername (__fd, __addr, __len);
}
int
getpeername (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __len)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_getpeername (__fd, __addr, __len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] getpeername: "
"'%04d'='%04d', '%p', '%p'\n", pid, rv, __fd, __addr, __len);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_getpeername (__fd, __addr, __len);
}
/*
* Send N bytes of BUF to socket FD. Returns the number sent or -1.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
ssize_t
vcom_send (int __fd, const void *__buf, size_t __n, int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_send (__fd, (void *) __buf, (int) __n, __flags);
}
ssize_t
send (int __fd, const void *__buf, size_t __n, int __flags)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_send (__fd, __buf, __n, __flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] send: "
"'%04d'='%04d', '%p', '%04d', '%04x'\n",
pid, (int) size, __fd, __buf, (int) __n, __flags);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_send (__fd, __buf, __n, __flags);
}
/*
* Read N bytes into BUF from socket FD.
* Returns the number read or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
ssize_t
vcom_recv (int __fd, void *__buf, size_t __n, int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_recv (__fd, __buf, __n, __flags);
}
ssize_t
recv (int __fd, void *__buf, size_t __n, int __flags)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_recv (__fd, __buf, __n, __flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] recv: "
"'%04d'='%04d', '%p', '%04d', '%04x'\n",
pid, (int) size, __fd, __buf, (int) __n, __flags);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_recv (__fd, __buf, __n, __flags);
}
/*
* Send N bytes of BUF on socket FD to peer at address ADDR (which is
* ADDR_LEN bytes long). Returns the number sent, or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
ssize_t
vcom_sendto (int __fd, const void *__buf, size_t __n, int __flags,
__CONST_SOCKADDR_ARG __addr, socklen_t __addr_len)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_sendto (__fd, __buf, __n, __flags, __addr, __addr_len);
}
ssize_t
sendto (int __fd, const void *__buf, size_t __n, int __flags,
__CONST_SOCKADDR_ARG __addr, socklen_t __addr_len)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_sendto (__fd, __buf, __n, __flags, __addr, __addr_len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] sendto: "
"'%04d'='%04d', '%p', '%04d', '%04x', "
"'%p', '%04d'\n",
pid, (int) size, __fd, __buf, (int) __n, __flags,
__addr, __addr_len);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_sendto (__fd, __buf, __n, __flags, __addr, __addr_len);
}
/*
* Read N bytes into BUF through socket FD.
* If ADDR is not NULL, fill in *ADDR_LEN bytes of it with the
* address of the sender, and store the actual size of the address
* in *ADDR_LEN.
* Returns the number of bytes read or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
ssize_t
vcom_recvfrom (int __fd, void *__restrict __buf, size_t __n,
int __flags,
__SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_recvfrom (__fd, __buf, __n, __flags, __addr, __addr_len);
}
ssize_t
recvfrom (int __fd, void *__restrict __buf, size_t __n,
int __flags,
__SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_recvfrom (__fd, __buf, __n, __flags, __addr, __addr_len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] recvfrom: "
"'%04d'='%04d', '%p', '%04d', '%04x', "
"'%p', '%p'\n",
pid, (int) size, __fd, __buf, (int) __n, __flags,
__addr, __addr_len);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_recvfrom (__fd, __buf, __n, __flags, __addr, __addr_len);
}
/*
* Send a message described MESSAGE on socket FD.
* Returns the number of bytes sent, or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
ssize_t
vcom_sendmsg (int __fd, const struct msghdr * __message, int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_sendmsg (__fd, __message, __flags);
}
ssize_t
sendmsg (int __fd, const struct msghdr * __message, int __flags)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_sendmsg (__fd, __message, __flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] sendmsg: "
"'%04d'='%04d', '%p', '%04x'\n",
pid, (int) size, __fd, __message, __flags);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_sendmsg (__fd, __message, __flags);
}
#ifdef __USE_GNU
/*
* Send a VLEN messages as described by VMESSAGES to socket FD.
* Returns the number of datagrams successfully written
* or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
int
vcom_sendmmsg (int __fd, struct mmsghdr *__vmessages,
unsigned int __vlen, int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_sendmmsg (__fd, __message, __vlen, __flags);
}
int
sendmmsg (int __fd, struct mmsghdr *__vmessages,
unsigned int __vlen, int __flags)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_sendmmsg (__fd, __message, __vlen, __flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] sendmmsg: "
"'%04d'='%04d', '%p', '%04d', '%04x'\n",
pid, (int) size, __fd, __vmessages, __vlen, __flags);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_sendmmsg (__fd, __message, __vlen, __flags);
}
#endif
/*
* Receive a message as described by MESSAGE from socket FD.
* Returns the number of bytes read or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
ssize_t
vcom_recvmsg (int __fd, struct msghdr * __message, int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_recvmsg (__fd, __message, __flags);
}
ssize_t
recvmsg (int __fd, struct msghdr * __message, int __flags)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_recvmsg (__fd, __message, __flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] recvmsg: "
"'%04d'='%04d', '%p', '%04x'\n",
pid, (int) size, __fd, __message, __flags);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_recvmsg (__fd, __message, __flags);
}
#ifdef __USE_GNU
/*
* Receive up to VLEN messages as described by VMESSAGES from socket FD.
* Returns the number of messages received or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
int
vcom_recvmmsg (int __fd, struct mmsghdr *__vmessages,
unsigned int __vlen, int __flags, struct timespec *__tmo)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_recvmmsg (__fd, __message, __vlen, __flags, __tmo);
}
int
recvmmsg (int __fd, struct mmsghdr *__vmessages,
unsigned int __vlen, int __flags, struct timespec *__tmo)
{
ssize_t size;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
size = vcom_recvmmsg (__fd, __message, __vlen, __flags, __tmo);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] recvmmsg: "
"'%04d'='%04d', '%p', "
"'%04d', '%04x', '%p'\n",
pid, (int) size, __fd, __vmessages, __vlen, __flags, __tmo);
if (size < 0)
{
errno = -size;
return -1;
}
return size;
}
return libc_recvmmsg (__fd, __message, __vlen, __flags, __tmo);
}
#endif
/*
* Put the current value for socket FD's option OPTNAME
* at protocol level LEVEL into OPTVAL (which is *OPTLEN bytes long),
* and set *OPTLEN to the value's actual length.
* Returns 0 on success, -1 for errors.
* */
int
vcom_getsockopt (int __fd, int __level, int __optname,
void *__restrict __optval, socklen_t * __restrict __optlen)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_getsockopt (__fd, __level, __optname,
__optval, __optlen);
}
int
getsockopt (int __fd, int __level, int __optname,
void *__restrict __optval, socklen_t * __restrict __optlen)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_getsockopt (__fd, __level, __optname, __optval, __optlen);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d] getsockopt: "
"'%04d'='%04d', '%04d', '%04d', "
"'%p', '%p'\n",
pid, rv, __fd, __level, __optname, __optval, __optlen);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_getsockopt (__fd, __level, __optname, __optval, __optlen);
}
/*
* Set socket FD's option OPTNAME at protocol level LEVEL
* to *OPTVAL (which is OPTLEN bytes long).
* Returns 0 on success, -1 for errors.
* */
int
vcom_setsockopt (int __fd, int __level, int __optname,
const void *__optval, socklen_t __optlen)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_setsockopt (__fd, __level, __optname,
__optval, __optlen);
}
int
setsockopt (int __fd, int __level, int __optname,
const void *__optval, socklen_t __optlen)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_setsockopt (__fd, __level, __optname, __optval, __optlen);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] setsockopt: "
"'%04d'='%04d', '%04d', '%04d', "
"'%p', '%04d'\n",
pid, rv, __fd, __level, __optname, __optval, __optlen);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_setsockopt (__fd, __level, __optname, __optval, __optlen);
}
/*
* Prepare to accept connections on socket FD.
* N connection requests will be queued before further
* requests are refused.
* Returns 0 on success, -1 for errors.
* */
int
vcom_listen (int __fd, int __n)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_listen (__fd, __n);
}
int
listen (int __fd, int __n)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_listen (__fd, __n);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] listen: "
"'%04d'='%04d', '%04d'\n", pid, rv, __fd, __n);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_listen (__fd, __n);
}
/*
* Await a connection on socket FD.
* When a connection arrives, open a new socket to communicate
* with it, set *ADDR (which is *ADDR_LEN bytes long) to the address
* of the connecting peer and *ADDR_LEN to the address's actual
* length, and return the new socket's descriptor, or -1 for errors.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
int
vcom_accept (int __fd, __SOCKADDR_ARG __addr,
socklen_t * __restrict __addr_len)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_accept (__fd, __addr, __addr_len);
}
int
accept (int __fd, __SOCKADDR_ARG __addr, socklen_t * __restrict __addr_len)
{
int rv = -1;
pid_t pid = getpid ();
pthread_t tid = pthread_self ();
if (is_vcom_socket_fd (__fd))
{
if (VCOM_DEBUG > 0)
vcom_socket_main_show ();
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d][%lu (0x%lx)] accept1: "
"'%04d'='%04d', '%p', '%p'\n",
pid, (unsigned long) tid, (unsigned long) tid,
rv, __fd, __addr, __addr_len);
rv = vcom_accept (__fd, __addr, __addr_len);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d][%lu (0x%lx)] accept2: "
"'%04d'='%04d', '%p', '%p'\n",
pid, (unsigned long) tid, (unsigned long) tid,
rv, __fd, __addr, __addr_len);
if (VCOM_DEBUG > 0)
vcom_socket_main_show ();
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
return libc_accept (__fd, __addr, __addr_len);
}
/*
* Similar to 'accept' but takes an additional parameter to specify
* flags.
* This function is a cancellation point and therefore not marked
* with __THROW.
* */
int
vcom_accept4 (int __fd, __SOCKADDR_ARG __addr,
socklen_t * __restrict __addr_len, int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_accept4 (__fd, __addr, __addr_len, __flags);
}
int
accept4 (int __fd, __SOCKADDR_ARG __addr,
socklen_t * __restrict __addr_len, int __flags)
{
int rv = 0;
pid_t pid = getpid ();
fprintf (stderr,
"[%d] accept4: in the beginning... "
"'%04d'='%04d', '%p', '%p', '%04x'\n",
pid, rv, __fd, __addr, __addr_len, __flags);
if (is_vcom_socket_fd (__fd))
{
if (VCOM_DEBUG > 0)
vcom_socket_main_show ();
rv = vcom_accept4 (__fd, __addr, __addr_len, __flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] accept4: VCL "
"'%04d'='%04d', '%p', '%p', '%04x'\n",
pid, rv, __fd, __addr, __addr_len, __flags);
if (VCOM_DEBUG > 0)
vcom_socket_main_show ();
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
fprintf (stderr,
"[%d] accept4: libc "
"'%04d'='%04d', '%p', '%p', '%04x'\n",
pid, rv, __fd, __addr, __addr_len, __flags);
return libc_accept4 (__fd, __addr, __addr_len, __flags);
}
/*
* Shut down all or part of the connection open on socket FD.
* HOW determines what to shut down:
* SHUT_RD = No more receptions;
* SHUT_WR = No more transmissions;
* SHUT_RDWR = No more receptions or transmissions.
* Returns 0 on success, -1 for errors.
* */
int
vcom_shutdown (int __fd, int __how)
{
if (vcom_init () != 0)
{
return -1;
}
return vcom_socket_shutdown (__fd, __how);
}
int
shutdown (int __fd, int __how)
{
int rv;
pid_t pid = getpid ();
if (is_vcom_socket_fd (__fd))
{
rv = vcom_shutdown (__fd, __how);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] shutdown: "
"'%04d'='%04d', '%04d'\n", pid, rv, __fd, __how);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
return libc_shutdown (__fd, __how);
}
int
vcom_epoll_create (int __size)
{
if (vcom_init () != 0)
{
return -1;
}
if (__size <= 0)
{
return -EINVAL;
}
/* __size argument is ignored "thereafter" */
return vcom_epoll_create1 (0);
}
/*
* __size argument is ignored, but must be greater than zero
*/
int
epoll_create (int __size)
{
int rv = 0;
pid_t pid = getpid ();
rv = vcom_epoll_create (__size);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] epoll_create: " "'%04d'='%04d'\n", pid, rv, __size);
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
int
vcom_epoll_create1 (int __flags)
{
if (vcom_init () != 0)
{
return -1;
}
if (__flags < 0)
{
return -EINVAL;
}
if (__flags & ~EPOLL_CLOEXEC)
{
return -EINVAL;
}
/* __flags can be either zero or EPOLL_CLOEXEC */
/* implementation */
return vcom_socket_epoll_create1 (__flags);
}
/*
* __flags can be either zero or EPOLL_CLOEXEC
* */
int
epoll_create1 (int __flags)
{
int rv = 0;
pid_t pid = getpid ();
rv = vcom_epoll_create1 (__flags);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] epoll_create: " "'%04d'='%08x'\n", pid, rv, __flags);
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
static inline int
ep_op_has_event (int op)
{
return op != EPOLL_CTL_DEL;
}
int
vcom_epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event)
{
if (vcom_init () != 0)
{
return -1;
}
/*
* the requested operation __op is not supported
* by this interface */
if (!((__op == EPOLL_CTL_ADD) ||
(__op == EPOLL_CTL_MOD) || (__op == EPOLL_CTL_DEL)))
{
return -EINVAL;
}
/* op is ADD or MOD but event parameter is NULL */
if ((ep_op_has_event (__op) && !__event))
{
return -EFAULT;
}
/* fd is same as epfd */
/* do not permit adding an epoll file descriptor inside itself */
if (__epfd == __fd)
{
return -EINVAL;
}
/* implementation */
return vcom_socket_epoll_ctl (__epfd, __op, __fd, __event);
}
/*
* implement the controller interface for epoll
* that enables the insertion/removal/change of
* file descriptors inside the interest set.
*/
int
epoll_ctl (int __epfd, int __op, int __fd, struct epoll_event *__event)
{
int rv;
pid_t pid = getpid ();
rv = vcom_epoll_ctl (__epfd, __op, __fd, __event);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] epoll_ctl: "
"'%04d'='%04d', '%04d', '%04d'\n", pid, rv, __epfd, __op, __fd);
if (rv != 0)
{
errno = -rv;
return -1;
}
return 0;
}
int
epoll_wait (int __epfd, struct epoll_event *__events,
int __maxevents, int __timeout)
{
int rv;
pid_t pid = getpid ();
if (__maxevents <= 0 || __maxevents > EP_MAX_EVENTS)
{
fprintf (stderr, "[%d] ERROR: epoll_wait() invalid maxevents %d\n",
pid, __maxevents);
errno = EINVAL;
return -1;
}
rv =
vcom_socket_epoll_pwait (__epfd, __events, __maxevents, __timeout, NULL);
if (VCOM_DEBUG > 1)
fprintf (stderr,
"[%d] epoll_wait: "
"'%04d'='%04d', '%p', "
"'%04d', '%04d'\n",
pid, rv, __epfd, __events, __maxevents, __timeout);
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
int
epoll_pwait (int __epfd, struct epoll_event *__events,
int __maxevents, int __timeout, const __sigset_t * __ss)
{
int rv;
pid_t pid = getpid ();
if (__maxevents <= 0 || __maxevents > EP_MAX_EVENTS)
{
errno = EINVAL;
return -1;
}
if (is_vcom_epfd (__epfd))
{
rv =
vcom_socket_epoll_pwait (__epfd, __events, __maxevents, __timeout,
__ss);
if (VCOM_DEBUG > 0)
fprintf (stderr,
"[%d] epoll_pwait: "
"'%04d'='%04d', '%p', "
"'%04d', '%04d', "
"'%p'\n",
pid, rv, __epfd, __events, __maxevents, __timeout, __ss);
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
else
{
errno = EINVAL;
return -1;
}
return 0;
}
/* Poll the file descriptors described by the NFDS structures starting at
FDS. If TIMEOUT is nonzero and not -1, allow TIMEOUT milliseconds for
an event to occur; if TIMEOUT is -1, block until an event occurs.
Returns the number of file descriptors with events, zero if timed out,
or -1 for errors.
This function is a cancellation point and therefore not marked with
__THROW. */
int
vcom_poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
{
int rv = 0;
pid_t pid = getpid ();
struct rlimit nofile_limit;
struct pollfd vcom_fds[MAX_POLL_NFDS_DEFAULT];
nfds_t fds_idx = 0;
/* actual set of file descriptors to be monitored */
nfds_t libc_nfds = 0;
nfds_t vcom_nfds = 0;
/* ready file descriptors
*
* number of structures which have nonzero revents fields
* in other words, descriptors with events or errors reported.
* */
/* after call to libc_poll () */
int rlibc_nfds = 0;
/* after call to vcom_socket_poll () */
int rvcom_nfds = 0;
/* timeout value in units of timespec */
struct timespec timeout_ts;
struct timespec start_time, now, end_time;
/* get start_time */
rv = clock_gettime (CLOCK_MONOTONIC, &start_time);
if (rv == -1)
{
rv = -errno;
goto poll_done;
}
/* set timeout_ts & end_time */
if (__timeout >= 0)
{
/* set timeout_ts */
timeout_ts.tv_sec = __timeout / MSEC_PER_SEC;
timeout_ts.tv_nsec = (__timeout % MSEC_PER_SEC) * NSEC_PER_MSEC;
set_normalized_timespec (&timeout_ts,
timeout_ts.tv_sec, timeout_ts.tv_nsec);
/* set end_time */
if (__timeout)
{
end_time = timespec_add (start_time, timeout_ts);
}
else
{
end_time = start_time;
}
}
if (vcom_init () != 0)
{
rv = -1;
goto poll_done;
}
/* validate __fds */
if (!__fds)
{
rv = -EFAULT;
goto poll_done;
}
/* validate __nfds */
/*TBD: call getrlimit once when vcl-ldpreload library is init */
rv = getrlimit (RLIMIT_NOFILE, &nofile_limit);
if (rv != 0)
{
rv = -errno;
goto poll_done;
}
if (__nfds >= nofile_limit.rlim_cur)
{
rv = -EINVAL;
goto poll_done;
}
/*
* for the POC, it's fair to assume that nfds is less than 1024
* */
if (__nfds >= MAX_POLL_NFDS_DEFAULT)
{
rv = -EINVAL;
goto poll_done;
}
/* set revents field (output parameter)
* to zero
* */
for (fds_idx = 0; fds_idx < __nfds; fds_idx++)
{
__fds[fds_idx].revents = 0;
}
#if 0
/* set revents field (output parameter)
* to zero for user ignored fds
* */
for (fds_idx = 0; fds_idx < __nfds; fds_idx++)
{
/*
* if negative fd, ignore events field
* and set output parameter (revents field) to zero */
if (__fds[fds_idx].fd < 0)
{
__fds[fds_idx].revents = 0;
}
}
#endif
/*
* 00. prepare __fds and vcom_fds for polling
* copy __fds to vcom_fds
* 01. negate all except libc fds in __fds,
* ignore user negated fds
* 02. negate all except vcom_fds in vocm fds,
* ignore user negated fds
* ignore fd 0 by setting it to negative number
* */
memcpy (vcom_fds, __fds, sizeof (*__fds) * __nfds);
libc_nfds = 0;
vcom_nfds = 0;
for (fds_idx = 0; fds_idx < __nfds; fds_idx++)
{
/* ignore negative fds */
if (__fds[fds_idx].fd < 0)
{
continue;
}
/*
* 00. ignore vcom fds in __fds
* 01. ignore libc fds in vcom_fds,
* ignore fd 0 by setting it to negative number.
* as fd 0 cannot be ignored.
*/
if (is_vcom_socket_fd (__fds[fds_idx].fd) ||
is_vcom_epfd (__fds[fds_idx].fd))
{
__fds[fds_idx].fd = -__fds[fds_idx].fd;
vcom_nfds++;
}
else
{
libc_nfds++;
/* ignore fd 0 by setting it to negative number */
if (!vcom_fds[fds_idx].fd)
{
vcom_fds[fds_idx].fd = -1;
}
vcom_fds[fds_idx].fd = -vcom_fds[fds_idx].fd;
}
}
/*
* polling loop
*
* poll on libc fds and vcom fds
*
* specifying a timeout of zero causes libc_poll() and
* vcom_socket_poll() to return immediately, even if no
* file descriptors are ready
* */
do
{
rlibc_nfds = 0;
rvcom_nfds = 0;
/*
* timeout parameter for libc_poll () set to zero
* to poll on libc fds
* */
/* poll on libc fds */
if (libc_nfds)
{
/*
* a timeout of zero causes libc_poll()
* to return immediately
* */
rlibc_nfds = libc_poll (__fds, __nfds, 0);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d] poll libc: "
"'%04d'='%08lu'\n", pid, rlibc_nfds, __nfds);
if (rlibc_nfds < 0)
{
rv = -errno;
goto poll_done_update_nfds;
}
}
/*
* timeout parameter for vcom_socket_poll () set to zero
* to poll on vcom fds
* */
/* poll on vcom fds */
if (vcom_nfds)
{
/*
* a timeout of zero causes vcom_socket_poll()
* to return immediately
* */
rvcom_nfds = vcom_socket_poll (vcom_fds, __nfds, 0);
if (VCOM_DEBUG > 2)
fprintf (stderr,
"[%d] poll vcom: "
"'%04d'='%08lu'\n", pid, rvcom_nfds, __nfds);
if (rvcom_nfds < 0)
{
rv = rvcom_nfds;
goto poll_done_update_nfds;
}
}
/* check if any file descriptors changed status */
if ((libc_nfds && rlibc_nfds > 0) || (vcom_nfds && rvcom_nfds > 0))
{
/* something interesting happened */
rv = rlibc_nfds + rvcom_nfds;
goto poll_done_update_nfds;
}
rv = clock_gettime (CLOCK_MONOTONIC, &now);
if (rv == -1)
{
rv = -errno;
goto poll_done_update_nfds;
}
}
/* block indefinitely || timeout elapsed */
while ((__timeout < 0) || timespec_compare (&now, &end_time) < 0);
/* timeout expired before anything interesting happened */
rv = 0;
poll_done_update_nfds:
for (fds_idx = 0; fds_idx < __nfds; fds_idx++)
{
/* ignore negative fds in vcom_fds
* 00. user negated fds
* 01. libc fds
* */
if (vcom_fds[fds_idx].fd < 0)
{
continue;
}
/* from here on handle positive vcom fds */
/*
* restore vcom fds to positive number in __fds
* and update revents in __fds with the events
* that actually occurred in vcom fds
* */
__fds[fds_idx].fd = -__fds[fds_idx].fd;
if (rvcom_nfds)
{
__fds[fds_idx].revents = vcom_fds[fds_idx].revents;
}
}
poll_done:
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] vpoll: " "'%04d'='%08lu'\n", pid, rv, __nfds);
return rv;
}
/*
* 00. The field __fds[i].fd contains a file descriptor for an
* open file.
* If this field is negative, then the corresponding
* events field is ignored and the revents field returns zero.
* The field __fds[i].events is an input parameter.
* The field __fds[i].revents is an output parameter.
* 01. Specifying a negative value in timeout
* means an infinite timeout.
* Specifying a timeout of zero causes poll() to return
* immediately, even if no file descriptors are ready.
*
* NOTE: observed __nfds is less than 128 from kubecon strace files
*/
int
poll (struct pollfd *__fds, nfds_t __nfds, int __timeout)
{
int rv = 0;
pid_t pid = getpid ();
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] poll1: " "'%04d'='%08lu, %d, 0x%x'\n",
pid, rv, __nfds, __fds[0].fd, __fds[0].events);
rv = vcom_poll (__fds, __nfds, __timeout);
if (VCOM_DEBUG > 2)
fprintf (stderr, "[%d] poll2: " "'%04d'='%08lu, %d, 0x%x'\n",
pid, rv, __nfds, __fds[0].fd, __fds[0].revents);
if (rv < 0)
{
errno = -rv;
return -1;
}
return rv;
}
#ifdef __USE_GNU
/* Like poll, but before waiting the threads signal mask is replaced
with that specified in the fourth parameter. For better usability,
the timeout value is specified using a TIMESPEC object.
This function is a cancellation point and therefore not marked with
__THROW. */
int
vcom_ppoll (struct pollfd *__fds, nfds_t __nfds,
const struct timespec *__timeout, const __sigset_t * __ss)
{
if (vcom_init () != 0)
{
return -1;
}
return -EOPNOTSUPP;
}
int
ppoll (struct pollfd *__fds, nfds_t __nfds,
const struct timespec *__timeout, const __sigset_t * __ss)
{
int rv = 0;
errno = EOPNOTSUPP;
rv = -1;
return rv;
}
#endif
void CONSTRUCTOR_ATTRIBUTE vcom_constructor (void);
void DESTRUCTOR_ATTRIBUTE vcom_destructor (void);
void
vcom_constructor (void)
{
pid_t pid = getpid ();
swrap_constructor ();
if (vcom_init () != 0)
{
printf ("\n[%d] vcom_constructor...failed!\n", pid);
}
else
{
printf ("\n[%d] vcom_constructor...done!\n", pid);
}
}
/*
* This function is called when the library is unloaded
*/
void
vcom_destructor (void)
{
pid_t pid = getpid ();
vcom_destroy ();
swrap_destructor ();
printf ("\n[%d] vcom_destructor...done!\n", pid);
}
/*
* fd.io coding-style-patch-verification: ON
*
* Local Variables:
* eval: (c-set-style "gnu")
* End:
*/