diff options
author | Dave Wallace <dwallacelf@gmail.com> | 2017-10-24 04:12:18 -0400 |
---|---|---|
committer | Keith Burns <alagalah@gmail.com> | 2017-10-24 18:25:31 +0000 |
commit | 5c7cf1cc5358d137160be1619981e7eea9a7402f (patch) | |
tree | e981f6fc831e9c4e240e765d506ac8d58be43123 /src/vcl/vcom.c | |
parent | 4c151b5fa13312639b0a5ef0d0efb1866bf4ed5e (diff) |
VCL-LDPRELOAD: statically link vppcom into libvcl-ldpreload.so
- Move VCL & VCL-LDPRELOAD source into src/vcl
- Statically link vppcom into libvcl-ldpreload.so
Change-Id: I778300b37e8b06640d9dbc01caf297edf7a6edb7
Signed-off-by: Dave Wallace <dwallacelf@gmail.com>
Diffstat (limited to 'src/vcl/vcom.c')
-rw-r--r-- | src/vcl/vcom.c | 3334 |
1 files changed, 3334 insertions, 0 deletions
diff --git a/src/vcl/vcom.c b/src/vcl/vcom.c new file mode 100644 index 00000000000..6d98fdb75c0 --- /dev/null +++ b/src/vcl/vcom.c @@ -0,0 +1,3334 @@ +/* + * 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) + vcom_socket_main_show (); + rv = vcom_close (__fd); + if (VCOM_DEBUG > 0) + fprintf (stderr, "[%d] close: " "'%04d'='%04d'\n", pid, rv, __fd); + if (VCOM_DEBUG > 0) + vcom_socket_main_show (); + 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 */ + __readfds || __writefds || __exceptfds ? + &new_nfds : NULL, + __readfds, + __writefds, + __exceptfds, + __readfds || __writefds || __exceptfds ? + &new_nfd : NULL, + /* 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); +} + +#ifdef __USE_GNU +/* + * 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; + pid_t pid = getpid (); + + 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: " + "'%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; + } + return libc_accept4 (__fd, __addr, __addr_len, __flags); +} + +#endif + +/* + * 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 (); + + if (is_vcom_epfd (__epfd)) + { + /* TBD: currently limiting epoll to support only vcom fds */ + if (is_vcom_socket_fd (__fd)) + { + 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; + } + else + { + /* + * TBD: currently epoll does not support kernel fds + * or epoll fds */ + errno = EBADF; + return -1; + } + } + else + { + /* epfd is not an epoll file descriptor */ + errno = EINVAL; + return -1; + } + return 0; +} + +int +vcom_epoll_wait (int __epfd, struct epoll_event *__events, + int __maxevents, int __timeout) +{ + if (vcom_init () != 0) + { + return -1; + } + + return vcom_epoll_pwait (__epfd, __events, __maxevents, __timeout, NULL); +} + +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) + { + errno = EINVAL; + return -1; + } + + if (is_vcom_epfd (__epfd)) + { + rv = vcom_epoll_wait (__epfd, __events, __maxevents, __timeout); + if (VCOM_DEBUG > 0) + 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; + } + else + { + errno = EINVAL; + return -1; + } + return 0; +} + + +int +vcom_epoll_pwait (int __epfd, struct epoll_event *__events, + int __maxevents, int __timeout, const __sigset_t * __ss) +{ + if (vcom_init () != 0) + { + return -1; + } + + /* implementation */ + return vcom_socket_epoll_pwait (__epfd, __events, + __maxevents, __timeout, __ss); +} + +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_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: + */ |