diff options
Diffstat (limited to 'Utilities/cmlibuv/src/unix')
28 files changed, 1158 insertions, 223 deletions
diff --git a/Utilities/cmlibuv/src/unix/aix.c b/Utilities/cmlibuv/src/unix/aix.c index 337e58e..1f36926 100644 --- a/Utilities/cmlibuv/src/unix/aix.c +++ b/Utilities/cmlibuv/src/unix/aix.c @@ -344,6 +344,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { perfstat_cpu_total_t ps_total; int result = perfstat_cpu_total(NULL, &ps_total, sizeof(ps_total), 1); @@ -1041,6 +1046,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct poll_ctl pc; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct pollfd*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; diff --git a/Utilities/cmlibuv/src/unix/async.c b/Utilities/cmlibuv/src/unix/async.c index 0b450ae..a5c47bc 100644 --- a/Utilities/cmlibuv/src/unix/async.c +++ b/Utilities/cmlibuv/src/unix/async.c @@ -61,14 +61,43 @@ int uv_async_send(uv_async_t* handle) { if (ACCESS_ONCE(int, handle->pending) != 0) return 0; - if (cmpxchgi(&handle->pending, 0, 1) == 0) - uv__async_send(handle->loop); + /* Tell the other thread we're busy with the handle. */ + if (cmpxchgi(&handle->pending, 0, 1) != 0) + return 0; + + /* Wake up the other thread's event loop. */ + uv__async_send(handle->loop); + + /* Tell the other thread we're done. */ + if (cmpxchgi(&handle->pending, 1, 2) != 1) + abort(); return 0; } +/* Only call this from the event loop thread. */ +static int uv__async_spin(uv_async_t* handle) { + int rc; + + for (;;) { + /* rc=0 -- handle is not pending. + * rc=1 -- handle is pending, other thread is still working with it. + * rc=2 -- handle is pending, other thread is done. + */ + rc = cmpxchgi(&handle->pending, 2, 0); + + if (rc != 1) + return rc; + + /* Other thread is busy with this handle, spin until it's done. */ + cpu_relax(); + } +} + + void uv__async_close(uv_async_t* handle) { + uv__async_spin(handle); QUEUE_REMOVE(&handle->queue); uv__handle_stop(handle); } @@ -109,8 +138,8 @@ static void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) { QUEUE_REMOVE(q); QUEUE_INSERT_TAIL(&loop->async_handles, q); - if (cmpxchgi(&h->pending, 1, 0) == 0) - continue; + if (0 == uv__async_spin(h)) + continue; /* Not pending. */ if (h->async_cb == NULL) continue; diff --git a/Utilities/cmlibuv/src/unix/atomic-ops.h b/Utilities/cmlibuv/src/unix/atomic-ops.h index be741dc..995aca6 100644 --- a/Utilities/cmlibuv/src/unix/atomic-ops.h +++ b/Utilities/cmlibuv/src/unix/atomic-ops.h @@ -23,7 +23,6 @@ #endif UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)); -UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)); UV_UNUSED(static void cpu_relax(void)); /* Prefer hand-rolled assembly over the gcc builtins because the latter also @@ -49,43 +48,7 @@ UV_UNUSED(static int cmpxchgi(int* ptr, int oldval, int newval)) { else return op4; #elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) - return atomic_cas_uint(ptr, oldval, newval); -#else - return __sync_val_compare_and_swap(ptr, oldval, newval); -#endif -} - -UV_UNUSED(static long cmpxchgl(long* ptr, long oldval, long newval)) { -#if defined(__i386__) || defined(__x86_64__) - long out; - __asm__ __volatile__ ("lock; cmpxchg %2, %1;" - : "=a" (out), "+m" (*(volatile long*) ptr) - : "r" (newval), "0" (oldval) - : "memory"); - return out; -#elif defined(_AIX) && (defined(__xlC__) || defined(__ibmxl__)) - const long out = (*(volatile int*) ptr); -# if defined(__64BIT__) - __compare_and_swaplp(ptr, &oldval, newval); -# else - __compare_and_swap(ptr, &oldval, newval); -# endif /* if defined(__64BIT__) */ - return out; -#elif defined (__MVS__) -#ifdef _LP64 - unsigned long long op4; - if (__plo_CSSTGR(ptr, (unsigned long long*) &oldval, newval, - (unsigned long long*) ptr, *ptr, &op4)) -#else - unsigned long op4; - if (__plo_CSST(ptr, (unsigned int*) &oldval, newval, - (unsigned int*) ptr, *ptr, &op4)) -#endif - return oldval; - else - return op4; -#elif defined(__SUNPRO_C) || defined(__SUNPRO_CC) - return atomic_cas_ulong(ptr, oldval, newval); + return atomic_cas_uint((uint_t *)ptr, (uint_t)oldval, (uint_t)newval); #else return __sync_val_compare_and_swap(ptr, oldval, newval); #endif diff --git a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c index 3c2253f..0d7bbe6 100644 --- a/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c +++ b/Utilities/cmlibuv/src/unix/bsd-ifaddrs.c @@ -31,6 +31,10 @@ #include <net/if_dl.h> #endif +#if defined(__HAIKU__) +#define IFF_RUNNING IFF_LINK +#endif + static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) return 1; @@ -45,7 +49,8 @@ static int uv__ifaddr_exclude(struct ifaddrs *ent, int exclude_type) { if (exclude_type == UV__EXCLUDE_IFPHYS) return (ent->ifa_addr->sa_family != AF_LINK); #endif -#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__DragonFly__) || \ + defined(__HAIKU__) /* * On BSD getifaddrs returns information related to the raw underlying * devices. We're not interested in this information. @@ -84,7 +89,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { return 0; } - *addresses = uv__malloc(*count * sizeof(**addresses)); + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); if (*addresses == NULL) { freeifaddrs(addrs); @@ -116,6 +122,7 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address++; } +#if !(defined(__CYGWIN__) || defined(__MSYS__)) /* Fill in physical addresses for each interface */ for (ent = addrs; ent != NULL; ent = ent->ifa_next) { if (uv__ifaddr_exclude(ent, UV__EXCLUDE_IFPHYS)) @@ -124,20 +131,15 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address = *addresses; for (i = 0; i < *count; i++) { -#if defined(__CYGWIN__) || defined(__MSYS__) - memset(address->phys_addr, 0, sizeof(address->phys_addr)); -#else if (strcmp(address->name, ent->ifa_name) == 0) { struct sockaddr_dl* sa_addr; sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); - } else { - memset(address->phys_addr, 0, sizeof(address->phys_addr)); } -#endif address++; } } +#endif freeifaddrs(addrs); diff --git a/Utilities/cmlibuv/src/unix/core.c b/Utilities/cmlibuv/src/unix/core.c index a8d6adb..93df7af 100644 --- a/Utilities/cmlibuv/src/unix/core.c +++ b/Utilities/cmlibuv/src/unix/core.c @@ -42,9 +42,9 @@ #include <pwd.h> #include <sched.h> #include <sys/utsname.h> +#include <sys/time.h> #ifdef __sun -# include <netdb.h> /* MAXHOSTNAMELEN on Solaris */ # include <sys/filio.h> # include <sys/types.h> # include <sys/wait.h> @@ -91,13 +91,8 @@ #include <sys/ioctl.h> #endif -#if !defined(__MVS__) -#include <sys/param.h> /* MAXHOSTNAMELEN on Linux and the BSDs */ -#endif - -/* Fallback for the maximum hostname length */ -#ifndef MAXHOSTNAMELEN -# define MAXHOSTNAMELEN 256 +#if defined(__linux__) +#include <sys/syscall.h> #endif static int uv__run_pending(uv_loop_t* loop); @@ -174,7 +169,9 @@ void uv_close(uv_handle_t* handle, uv_close_cb close_cb) { case UV_FS_POLL: uv__fs_poll_close((uv_fs_poll_t*)handle); - break; + /* Poll handles use file system requests, and one of them may still be + * running. The poll code will call uv__make_close_pending() for us. */ + return; case UV_SIGNAL: uv__signal_close((uv_signal_t*) handle); @@ -520,6 +517,34 @@ skip: } +/* close() on macos has the "interesting" quirk that it fails with EINTR + * without closing the file descriptor when a thread is in the cancel state. + * That's why libuv calls close$NOCANCEL() instead. + * + * glibc on linux has a similar issue: close() is a cancellation point and + * will unwind the thread when it's in the cancel state. Work around that + * by making the system call directly. Musl libc is unaffected. + */ +int uv__close_nocancel(int fd) { +#if defined(__APPLE__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wdollar-in-identifier-extension" +#if defined(__LP64__) + extern int close$NOCANCEL(int); + return close$NOCANCEL(fd); +#else + extern int close$NOCANCEL$UNIX2003(int); + return close$NOCANCEL$UNIX2003(fd); +#endif +#pragma GCC diagnostic pop +#elif defined(__linux__) + return syscall(SYS_close, fd); +#else + return close(fd); +#endif +} + + int uv__close_nocheckstdio(int fd) { int saved_errno; int rc; @@ -527,7 +552,7 @@ int uv__close_nocheckstdio(int fd) { assert(fd > -1); /* Catch uninitialized io_watcher.fd bugs. */ saved_errno = errno; - rc = close(fd); + rc = uv__close_nocancel(fd); if (rc == -1) { rc = UV__ERR(errno); if (rc == UV_EINTR || rc == UV__ERR(EINPROGRESS)) @@ -562,7 +587,7 @@ int uv__nonblock_ioctl(int fd, int set) { } -#if !defined(__CYGWIN__) && !defined(__MSYS__) +#if !defined(__CYGWIN__) && !defined(__MSYS__) && !defined(__HAIKU__) int uv__cloexec_ioctl(int fd, int set) { int r; @@ -895,7 +920,8 @@ void uv__io_close(uv_loop_t* loop, uv__io_t* w) { QUEUE_REMOVE(&w->pending_queue); /* Remove stale events for this file descriptor */ - uv__platform_invalidate_fd(loop, w->fd); + if (w->fd != -1) + uv__platform_invalidate_fd(loop, w->fd); } @@ -929,7 +955,7 @@ int uv_getrusage(uv_rusage_t* rusage) { rusage->ru_stime.tv_sec = usage.ru_stime.tv_sec; rusage->ru_stime.tv_usec = usage.ru_stime.tv_usec; -#if !defined(__MVS__) +#if !defined(__MVS__) && !defined(__HAIKU__) rusage->ru_maxrss = usage.ru_maxrss; rusage->ru_ixrss = usage.ru_ixrss; rusage->ru_idrss = usage.ru_idrss; @@ -1294,7 +1320,7 @@ int uv_os_gethostname(char* buffer, size_t* size) { instead by creating a large enough buffer and comparing the hostname length to the size input. */ - char buf[MAXHOSTNAMELEN + 1]; + char buf[UV_MAXHOSTNAMESIZE]; size_t len; if (buffer == NULL || size == NULL || *size == 0) @@ -1426,3 +1452,39 @@ error: buffer->machine[0] = '\0'; return r; } + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen) { + socklen_t socklen; + uv_os_fd_t fd; + int r; + + r = uv_fileno(handle, &fd); + if (r < 0) + return r; + + /* sizeof(socklen_t) != sizeof(int) on some systems. */ + socklen = (socklen_t) *namelen; + + if (func(fd, name, &socklen)) + return UV__ERR(errno); + + *namelen = (int) socklen; + return 0; +} + +int uv_gettimeofday(uv_timeval64_t* tv) { + struct timeval time; + + if (tv == NULL) + return UV_EINVAL; + + if (gettimeofday(&time, NULL) != 0) + return UV__ERR(errno); + + tv->tv_sec = (int64_t) time.tv_sec; + tv->tv_usec = (int32_t) time.tv_usec; + return 0; +} diff --git a/Utilities/cmlibuv/src/unix/darwin.c b/Utilities/cmlibuv/src/unix/darwin.c index 31ad8a9..e4cd8ff 100644 --- a/Utilities/cmlibuv/src/unix/darwin.c +++ b/Utilities/cmlibuv/src/unix/darwin.c @@ -117,6 +117,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/Utilities/cmlibuv/src/unix/freebsd.c b/Utilities/cmlibuv/src/unix/freebsd.c index 0f729cf..7de88d6 100644 --- a/Utilities/cmlibuv/src/unix/freebsd.c +++ b/Utilities/cmlibuv/src/unix/freebsd.c @@ -137,6 +137,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { struct loadavg info; size_t size = sizeof(info); diff --git a/Utilities/cmlibuv/src/unix/fs.c b/Utilities/cmlibuv/src/unix/fs.c index bffc956..48500ff 100644 --- a/Utilities/cmlibuv/src/unix/fs.c +++ b/Utilities/cmlibuv/src/unix/fs.c @@ -47,7 +47,7 @@ #if defined(__DragonFly__) || \ defined(__FreeBSD__) || \ - defined(__FreeBSD_kernel_) || \ + defined(__FreeBSD_kernel__) || \ defined(__OpenBSD__) || \ defined(__NetBSD__) # define HAVE_PREADV 1 @@ -60,7 +60,6 @@ #endif #if defined(__APPLE__) -# include <copyfile.h> # include <sys/sysctl.h> #elif defined(__linux__) && !defined(FICLONE) # include <sys/ioctl.h> @@ -143,19 +142,34 @@ extern char *mkdtemp(char *template); /* See issue #740 on AIX < 7 */ while (0) +static int uv__fs_close(int fd) { + int rc; + + rc = uv__close_nocancel(fd); + if (rc == -1) + if (errno == EINTR || errno == EINPROGRESS) + rc = 0; /* The close is in progress, not an error. */ + + return rc; +} + + static ssize_t uv__fs_fsync(uv_fs_t* req) { #if defined(__APPLE__) /* Apple's fdatasync and fsync explicitly do NOT flush the drive write cache * to the drive platters. This is in contrast to Linux's fdatasync and fsync * which do, according to recent man pages. F_FULLFSYNC is Apple's equivalent * for flushing buffered data to permanent storage. If F_FULLFSYNC is not - * supported by the file system we should fall back to fsync(). This is the - * same approach taken by sqlite. + * supported by the file system we fall back to F_BARRIERFSYNC or fsync(). + * This is the same approach taken by sqlite, except sqlite does not issue + * an F_BARRIERFSYNC call. */ int r; r = fcntl(req->file, F_FULLFSYNC); if (r != 0) + r = fcntl(req->file, F_BARRIERFSYNC); /* fsync + barrier */ + if (r != 0) r = fsync(req->file); return r; #else @@ -178,7 +192,8 @@ static ssize_t uv__fs_fdatasync(uv_fs_t* req) { static ssize_t uv__fs_futime(uv_fs_t* req) { #if defined(__linux__) \ - || defined(_AIX71) + || defined(_AIX71) \ + || defined(__HAIKU__) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ @@ -327,6 +342,18 @@ done: req->bufs = NULL; req->nbufs = 0; +#ifdef __PASE__ + /* PASE returns EOPNOTSUPP when reading a directory, convert to EISDIR */ + if (result == -1 && errno == EOPNOTSUPP) { + struct stat buf; + ssize_t rc; + rc = fstat(req->file, &buf); + if (rc == 0 && S_ISDIR(buf.st_mode)) { + errno = EISDIR; + } + } +#endif + return result; } @@ -349,7 +376,7 @@ static int uv__fs_scandir_sort(UV_CONST_DIRENT** a, UV_CONST_DIRENT** b) { static ssize_t uv__fs_scandir(uv_fs_t* req) { - uv__dirent_t **dents; + uv__dirent_t** dents; int n; dents = NULL; @@ -373,6 +400,87 @@ static ssize_t uv__fs_scandir(uv_fs_t* req) { return n; } +static int uv__fs_opendir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = uv__malloc(sizeof(*dir)); + if (dir == NULL) + goto error; + + dir->dir = opendir(req->path); + if (dir->dir == NULL) + goto error; + + req->ptr = dir; + return 0; + +error: + uv__free(dir); + req->ptr = NULL; + return -1; +} + +static int uv__fs_readdir(uv_fs_t* req) { + uv_dir_t* dir; + uv_dirent_t* dirent; + struct dirent* res; + unsigned int dirent_idx; + unsigned int i; + + dir = req->ptr; + dirent_idx = 0; + + while (dirent_idx < dir->nentries) { + /* readdir() returns NULL on end of directory, as well as on error. errno + is used to differentiate between the two conditions. */ + errno = 0; + res = readdir(dir->dir); + + if (res == NULL) { + if (errno != 0) + goto error; + break; + } + + if (strcmp(res->d_name, ".") == 0 || strcmp(res->d_name, "..") == 0) + continue; + + dirent = &dir->dirents[dirent_idx]; + dirent->name = uv__strdup(res->d_name); + + if (dirent->name == NULL) + goto error; + + dirent->type = uv__fs_get_dirent_type(res); + ++dirent_idx; + } + + return dirent_idx; + +error: + for (i = 0; i < dirent_idx; ++i) { + uv__free((char*) dir->dirents[i].name); + dir->dirents[i].name = NULL; + } + + return -1; +} + +static int uv__fs_closedir(uv_fs_t* req) { + uv_dir_t* dir; + + dir = req->ptr; + + if (dir->dir != NULL) { + closedir(dir->dir); + dir->dir = NULL; + } + + uv__free(req->ptr); + req->ptr = NULL; + return 0; +} + #if defined(_POSIX_PATH_MAX) # define UV__FS_PATH_MAX _POSIX_PATH_MAX #elif defined(PATH_MAX) @@ -702,7 +810,8 @@ static ssize_t uv__fs_sendfile(uv_fs_t* req) { static ssize_t uv__fs_utime(uv_fs_t* req) { #if defined(__linux__) \ || defined(_AIX71) \ - || defined(__sun) + || defined(__sun) \ + || defined(__HAIKU__) /* utimesat() has nanosecond resolution but we stick to microseconds * for the sake of consistency with other platforms. */ @@ -806,45 +915,6 @@ done: } static ssize_t uv__fs_copyfile(uv_fs_t* req) { -#if defined(__APPLE__) && !TARGET_OS_IPHONE - /* On macOS, use the native copyfile(3). */ - static int can_clone; - copyfile_flags_t flags; - char buf[64]; - size_t len; - int major; - - flags = COPYFILE_ALL; - - if (req->flags & UV_FS_COPYFILE_EXCL) - flags |= COPYFILE_EXCL; - - /* Check OS version. Cloning is only supported on macOS >= 10.12. */ - if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) { - if (can_clone == 0) { - len = sizeof(buf); - if (sysctlbyname("kern.osrelease", buf, &len, NULL, 0)) - return UV__ERR(errno); - - if (1 != sscanf(buf, "%d", &major)) - abort(); - - can_clone = -1 + 2 * (major >= 16); /* macOS >= 10.12 */ - } - - if (can_clone < 0) - return UV_ENOSYS; - } - - /* copyfile() simply ignores COPYFILE_CLONE if it's not supported. */ - if (req->flags & UV_FS_COPYFILE_FICLONE) - flags |= 1 << 24; /* COPYFILE_CLONE */ - - if (req->flags & UV_FS_COPYFILE_FICLONE_FORCE) - flags |= 1 << 25; /* COPYFILE_CLONE_FORCE */ - - return copyfile(req->path, req->new_path, NULL, flags); -#else uv_fs_t fs_req; uv_file srcfd; uv_file dstfd; @@ -971,7 +1041,6 @@ out: errno = UV__ERR(result); return -1; -#endif } static void uv__to_stat(struct stat* src, uv_stat_t* dst) { @@ -1051,10 +1120,84 @@ static void uv__to_stat(struct stat* src, uv_stat_t* dst) { } +static int uv__fs_statx(int fd, + const char* path, + int is_fstat, + int is_lstat, + uv_stat_t* buf) { + STATIC_ASSERT(UV_ENOSYS != -1); +#ifdef __linux__ + static int no_statx; + struct uv__statx statxbuf; + int dirfd; + int flags; + int mode; + int rc; + + if (no_statx) + return UV_ENOSYS; + + dirfd = AT_FDCWD; + flags = 0; /* AT_STATX_SYNC_AS_STAT */ + mode = 0xFFF; /* STATX_BASIC_STATS + STATX_BTIME */ + + if (is_fstat) { + dirfd = fd; + flags |= 0x1000; /* AT_EMPTY_PATH */ + } + + if (is_lstat) + flags |= AT_SYMLINK_NOFOLLOW; + + rc = uv__statx(dirfd, path, flags, mode, &statxbuf); + + if (rc == -1) { + /* EPERM happens when a seccomp filter rejects the system call. + * Has been observed with libseccomp < 2.3.3 and docker < 18.04. + */ + if (errno != EINVAL && errno != EPERM && errno != ENOSYS) + return -1; + + no_statx = 1; + return UV_ENOSYS; + } + + buf->st_dev = 256 * statxbuf.stx_dev_major + statxbuf.stx_dev_minor; + buf->st_mode = statxbuf.stx_mode; + buf->st_nlink = statxbuf.stx_nlink; + buf->st_uid = statxbuf.stx_uid; + buf->st_gid = statxbuf.stx_gid; + buf->st_rdev = statxbuf.stx_rdev_major; + buf->st_ino = statxbuf.stx_ino; + buf->st_size = statxbuf.stx_size; + buf->st_blksize = statxbuf.stx_blksize; + buf->st_blocks = statxbuf.stx_blocks; + buf->st_atim.tv_sec = statxbuf.stx_atime.tv_sec; + buf->st_atim.tv_nsec = statxbuf.stx_atime.tv_nsec; + buf->st_mtim.tv_sec = statxbuf.stx_mtime.tv_sec; + buf->st_mtim.tv_nsec = statxbuf.stx_mtime.tv_nsec; + buf->st_ctim.tv_sec = statxbuf.stx_ctime.tv_sec; + buf->st_ctim.tv_nsec = statxbuf.stx_ctime.tv_nsec; + buf->st_birthtim.tv_sec = statxbuf.stx_btime.tv_sec; + buf->st_birthtim.tv_nsec = statxbuf.stx_btime.tv_nsec; + buf->st_flags = 0; + buf->st_gen = 0; + + return 0; +#else + return UV_ENOSYS; +#endif /* __linux__ */ +} + + static int uv__fs_stat(const char *path, uv_stat_t *buf) { struct stat pbuf; int ret; + ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 0, buf); + if (ret != UV_ENOSYS) + return ret; + ret = stat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1067,6 +1210,10 @@ static int uv__fs_lstat(const char *path, uv_stat_t *buf) { struct stat pbuf; int ret; + ret = uv__fs_statx(-1, path, /* is_fstat */ 0, /* is_lstat */ 1, buf); + if (ret != UV_ENOSYS) + return ret; + ret = lstat(path, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1079,6 +1226,10 @@ static int uv__fs_fstat(int fd, uv_stat_t *buf) { struct stat pbuf; int ret; + ret = uv__fs_statx(fd, "", /* is_fstat */ 1, /* is_lstat */ 0, buf); + if (ret != UV_ENOSYS) + return ret; + ret = fstat(fd, &pbuf); if (ret == 0) uv__to_stat(&pbuf, buf); @@ -1167,7 +1318,7 @@ static void uv__fs_work(struct uv__work* w) { X(ACCESS, access(req->path, req->flags)); X(CHMOD, chmod(req->path, req->mode)); X(CHOWN, chown(req->path, req->uid, req->gid)); - X(CLOSE, close(req->file)); + X(CLOSE, uv__fs_close(req->file)); X(COPYFILE, uv__fs_copyfile(req)); X(FCHMOD, fchmod(req->file, req->mode)); X(FCHOWN, fchown(req->file, req->uid, req->gid)); @@ -1184,6 +1335,9 @@ static void uv__fs_work(struct uv__work* w) { X(OPEN, uv__fs_open(req)); X(READ, uv__fs_read(req)); X(SCANDIR, uv__fs_scandir(req)); + X(OPENDIR, uv__fs_opendir(req)); + X(READDIR, uv__fs_readdir(req)); + X(CLOSEDIR, uv__fs_closedir(req)); X(READLINK, uv__fs_readlink(req)); X(REALPATH, uv__fs_realpath(req)); X(RENAME, rename(req->path, req->new_path)); @@ -1454,6 +1608,40 @@ int uv_fs_scandir(uv_loop_t* loop, POST; } +int uv_fs_opendir(uv_loop_t* loop, + uv_fs_t* req, + const char* path, + uv_fs_cb cb) { + INIT(OPENDIR); + PATH; + POST; +} + +int uv_fs_readdir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(READDIR); + + if (dir == NULL || dir->dir == NULL || dir->dirents == NULL) + return UV_EINVAL; + + req->ptr = dir; + POST; +} + +int uv_fs_closedir(uv_loop_t* loop, + uv_fs_t* req, + uv_dir_t* dir, + uv_fs_cb cb) { + INIT(CLOSEDIR); + + if (dir == NULL) + return UV_EINVAL; + + req->ptr = dir; + POST; +} int uv_fs_readlink(uv_loop_t* loop, uv_fs_t* req, @@ -1594,6 +1782,9 @@ void uv_fs_req_cleanup(uv_fs_t* req) { req->path = NULL; req->new_path = NULL; + if (req->fs_type == UV_FS_READDIR && req->ptr != NULL) + uv__fs_readdir_cleanup(req); + if (req->fs_type == UV_FS_SCANDIR && req->ptr != NULL) uv__fs_scandir_cleanup(req); @@ -1601,7 +1792,7 @@ void uv_fs_req_cleanup(uv_fs_t* req) { uv__free(req->bufs); req->bufs = NULL; - if (req->ptr != &req->statbuf) + if (req->fs_type != UV_FS_OPENDIR && req->ptr != &req->statbuf) uv__free(req->ptr); req->ptr = NULL; } diff --git a/Utilities/cmlibuv/src/unix/fsevents.c b/Utilities/cmlibuv/src/unix/fsevents.c index c430562..ddacda3 100644 --- a/Utilities/cmlibuv/src/unix/fsevents.c +++ b/Utilities/cmlibuv/src/unix/fsevents.c @@ -21,9 +21,10 @@ #include "uv.h" #include "internal.h" -#if TARGET_OS_IPHONE +#if TARGET_OS_IPHONE || MAC_OS_X_VERSION_MAX_ALLOWED < 1070 /* iOS (currently) doesn't provide the FSEvents-API (nor CoreServices) */ +/* macOS prior to 10.7 doesn't provide the full FSEvents API so use kqueue */ int uv__fsevents_init(uv_fs_event_t* handle) { return 0; diff --git a/Utilities/cmlibuv/src/unix/getaddrinfo.c b/Utilities/cmlibuv/src/unix/getaddrinfo.c index 6d23fbe..d7ca7d1 100644 --- a/Utilities/cmlibuv/src/unix/getaddrinfo.c +++ b/Utilities/cmlibuv/src/unix/getaddrinfo.c @@ -92,7 +92,9 @@ int uv__getaddrinfo_translate_error(int sys_err) { } assert(!"unknown EAI_* error code"); abort(); +#ifndef __SUNPRO_C return 0; /* Pacify compiler. */ +#endif } diff --git a/Utilities/cmlibuv/src/unix/haiku.c b/Utilities/cmlibuv/src/unix/haiku.c new file mode 100644 index 0000000..7708851 --- /dev/null +++ b/Utilities/cmlibuv/src/unix/haiku.c @@ -0,0 +1,176 @@ +/* Copyright libuv project contributors. All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <FindDirectory.h> /* find_path() */ +#include <OS.h> + + +void uv_loadavg(double avg[3]) { + avg[0] = 0; + avg[1] = 0; + avg[2] = 0; +} + + +int uv_exepath(char* buffer, size_t* size) { + char abspath[B_PATH_NAME_LENGTH]; + status_t status; + ssize_t abspath_len; + + if (buffer == NULL || size == NULL || *size == 0) + return UV_EINVAL; + + status = find_path(B_APP_IMAGE_SYMBOL, B_FIND_PATH_IMAGE_PATH, NULL, abspath, + sizeof(abspath)); + if (status != B_OK) + return UV__ERR(status); + + abspath_len = uv__strscpy(buffer, abspath, *size); + *size -= 1; + if (abspath_len >= 0 && *size > (size_t)abspath_len) + *size = (size_t)abspath_len; + + return 0; +} + + +uint64_t uv_get_free_memory(void) { + status_t status; + system_info sinfo; + + status = get_system_info(&sinfo); + if (status != B_OK) + return 0; + + return (sinfo.max_pages - sinfo.used_pages) * B_PAGE_SIZE; +} + + +uint64_t uv_get_total_memory(void) { + status_t status; + system_info sinfo; + + status = get_system_info(&sinfo); + if (status != B_OK) + return 0; + + return sinfo.max_pages * B_PAGE_SIZE; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + +int uv_resident_set_memory(size_t* rss) { + area_info area; + ssize_t cookie; + status_t status; + thread_info thread; + + status = get_thread_info(find_thread(NULL), &thread); + if (status != B_OK) + return UV__ERR(status); + + cookie = 0; + *rss = 0; + while (get_next_area_info(thread.team, &cookie, &area) == B_OK) + *rss += area.ram_size; + + return 0; +} + + +int uv_uptime(double* uptime) { + /* system_time() returns time since booting in microseconds */ + *uptime = (double)system_time() / 1000000; + return 0; +} + + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + cpu_topology_node_info* topology_infos; + int i; + status_t status; + system_info system; + uint32_t topology_count; + uint64_t cpuspeed; + uv_cpu_info_t* cpu_info; + + if (cpu_infos == NULL || count == NULL) + return UV_EINVAL; + + status = get_cpu_topology_info(NULL, &topology_count); + if (status != B_OK) + return UV__ERR(status); + + topology_infos = uv__malloc(topology_count * sizeof(*topology_infos)); + if (topology_infos == NULL) + return UV_ENOMEM; + + status = get_cpu_topology_info(topology_infos, &topology_count); + if (status != B_OK) { + uv__free(topology_infos); + return UV__ERR(status); + } + + cpuspeed = 0; + for (i = 0; i < (int)topology_count; i++) { + if (topology_infos[i].type == B_TOPOLOGY_CORE) { + cpuspeed = topology_infos[i].data.core.default_frequency; + break; + } + } + + uv__free(topology_infos); + + status = get_system_info(&system); + if (status != B_OK) + return UV__ERR(status); + + *cpu_infos = uv__calloc(system.cpu_count, sizeof(**cpu_infos)); + if (*cpu_infos == NULL) + return UV_ENOMEM; + + /* CPU time and model are not exposed by Haiku. */ + cpu_info = *cpu_infos; + for (i = 0; i < (int)system.cpu_count; i++) { + cpu_info->model = uv__strdup("unknown"); + cpu_info->speed = (int)(cpuspeed / 1000000); + cpu_info++; + } + *count = system.cpu_count; + + return 0; +} + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + int i; + + for (i = 0; i < count; i++) + uv__free(cpu_infos[i].model); + + uv__free(cpu_infos); +} diff --git a/Utilities/cmlibuv/src/unix/ibmi.c b/Utilities/cmlibuv/src/unix/ibmi.c index 13fed6c..c7e1051 100644 --- a/Utilities/cmlibuv/src/unix/ibmi.c +++ b/Utilities/cmlibuv/src/unix/ibmi.c @@ -55,19 +55,155 @@ #include <strings.h> #include <sys/vnode.h> +#include <as400_protos.h> + + +typedef struct { + int bytes_available; + int bytes_returned; + char current_date_and_time[8]; + char system_name[8]; + char elapsed_time[6]; + char restricted_state_flag; + char reserved; + int percent_processing_unit_used; + int jobs_in_system; + int percent_permanent_addresses; + int percent_temporary_addresses; + int system_asp; + int percent_system_asp_used; + int total_auxiliary_storage; + int current_unprotected_storage_used; + int maximum_unprotected_storage_used; + int percent_db_capability; + int main_storage_size; + int number_of_partitions; + int partition_identifier; + int reserved1; + int current_processing_capacity; + char processor_sharing_attribute; + char reserved2[3]; + int number_of_processors; + int active_jobs_in_system; + int active_threads_in_system; + int maximum_jobs_in_system; + int percent_temporary_256mb_segments_used; + int percent_temporary_4gb_segments_used; + int percent_permanent_256mb_segments_used; + int percent_permanent_4gb_segments_used; + int percent_current_interactive_performance; + int percent_uncapped_cpu_capacity_used; + int percent_shared_processor_pool_used; + long main_storage_size_long; +} SSTS0200; + + +static int get_ibmi_system_status(SSTS0200* rcvr) { + /* rcvrlen is input parameter 2 to QWCRSSTS */ + unsigned int rcvrlen = sizeof(*rcvr); + + /* format is input parameter 3 to QWCRSSTS ("SSTS0200" in EBCDIC) */ + unsigned char format[] = {0xE2, 0xE2, 0xE3, 0xE2, 0xF0, 0xF2, 0xF0, 0xF0}; + + /* reset_status is input parameter 4 to QWCRSSTS ("*NO " in EBCDIC) */ + unsigned char reset_status[] = { + 0x5C, 0xD5, 0xD6, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40, 0x40 + }; + + /* errcode is input parameter 5 to QWCRSSTS */ + struct _errcode { + int bytes_provided; + int bytes_available; + char msgid[7]; + } errcode; + + /* qwcrssts_pointer is the 16-byte tagged system pointer to QWCRSSTS */ + ILEpointer __attribute__((aligned(16))) qwcrssts_pointer; + + /* qwcrssts_argv is the array of argument pointers to QWCRSSTS */ + void* qwcrssts_argv[6]; + + /* Set the IBM i pointer to the QSYS/QWCRSSTS *PGM object */ + int rc = _RSLOBJ2(&qwcrssts_pointer, RSLOBJ_TS_PGM, "QWCRSSTS", "QSYS"); + + if (rc != 0) + return rc; + + /* initialize the QWCRSSTS returned info structure */ + memset(rcvr, 0, sizeof(*rcvr)); + + /* initialize the QWCRSSTS error code structure */ + memset(&errcode, 0, sizeof(errcode)); + errcode.bytes_provided = sizeof(errcode); + + /* initialize the array of argument pointers for the QWCRSSTS API */ + qwcrssts_argv[0] = rcvr; + qwcrssts_argv[1] = &rcvrlen; + qwcrssts_argv[2] = &format; + qwcrssts_argv[3] = &reset_status; + qwcrssts_argv[4] = &errcode; + qwcrssts_argv[5] = NULL; + + /* Call the IBM i QWCRSSTS API from PASE */ + rc = _PGMCALL(&qwcrssts_pointer, (void**)&qwcrssts_argv, 0); + + return rc; +} + + uint64_t uv_get_free_memory(void) { - return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_AVPHYS_PAGES); + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + /* The amount of main storage, in kilobytes, in the system. */ + uint64_t main_storage_size = rcvr.main_storage_size; + + /* The current amount of storage in use for temporary objects. + * in millions (M) of bytes. + */ + uint64_t current_unprotected_storage_used = + rcvr.current_unprotected_storage_used * 1024ULL; + + uint64_t free_storage_size = + (main_storage_size - current_unprotected_storage_used) * 1024ULL; + + return free_storage_size < 0 ? 0 : free_storage_size; } uint64_t uv_get_total_memory(void) { - return (uint64_t) sysconf(_SC_PAGESIZE) * sysconf(_SC_PHYS_PAGES); + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) + return 0; + + return (uint64_t)rcvr.main_storage_size * 1024ULL; +} + + +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ } void uv_loadavg(double avg[3]) { + SSTS0200 rcvr; + + if (get_ibmi_system_status(&rcvr)) { avg[0] = avg[1] = avg[2] = 0; return; + } + + /* The average (in tenths) of the elapsed time during which the processing + * units were in use. For example, a value of 411 in binary would be 41.1%. + * This percentage could be greater than 100% for an uncapped partition. + */ + double processing_unit_used_percent = + rcvr.percent_processing_unit_used / 1000.0; + + avg[0] = avg[1] = avg[2] = processing_unit_used_percent; } @@ -111,3 +247,4 @@ int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { return 0; } + diff --git a/Utilities/cmlibuv/src/unix/internal.h b/Utilities/cmlibuv/src/unix/internal.h index 48fe6e8..b43c0b1 100644 --- a/Utilities/cmlibuv/src/unix/internal.h +++ b/Utilities/cmlibuv/src/unix/internal.h @@ -105,8 +105,7 @@ int uv__pthread_sigmask(int how, const sigset_t* set, sigset_t* oset); */ #if defined(__clang__) || \ defined(__GNUC__) || \ - defined(__INTEL_COMPILER) || \ - defined(__SUNPRO_C) + defined(__INTEL_COMPILER) # define UV_DESTRUCTOR(declaration) __attribute__((destructor)) declaration # define UV_UNUSED(declaration) __attribute__((unused)) declaration #else @@ -194,6 +193,7 @@ int uv__nonblock_ioctl(int fd, int set); int uv__nonblock_fcntl(int fd, int set); int uv__close(int fd); /* preserves errno */ int uv__close_nocheckstdio(int fd); +int uv__close_nocancel(int fd); int uv__socket(int domain, int type, int protocol); ssize_t uv__recvmsg(int fd, struct msghdr *msg, int flags); void uv__make_close_pending(uv_handle_t* handle); @@ -316,4 +316,11 @@ UV_UNUSED(static char* uv__basename_r(const char* path)) { int uv__inotify_fork(uv_loop_t* loop, void* old_watchers); #endif +typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); + +int uv__getsockpeername(const uv_handle_t* handle, + uv__peersockfunc func, + struct sockaddr* name, + int* namelen); + #endif /* UV_UNIX_INTERNAL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/kqueue.c b/Utilities/cmlibuv/src/unix/kqueue.c index c24f96e..c04e7a4 100644 --- a/Utilities/cmlibuv/src/unix/kqueue.c +++ b/Utilities/cmlibuv/src/unix/kqueue.c @@ -59,7 +59,7 @@ int uv__kqueue_init(uv_loop_t* loop) { } -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 static int uv__has_forked_with_cfrunloop; #endif @@ -70,7 +70,7 @@ int uv__io_fork(uv_loop_t* loop) { if (err) return err; -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (loop->cf_state != NULL) { /* We cannot start another CFRunloop and/or thread in the child process; CF aborts if you try or if you try to touch the thread @@ -86,7 +86,7 @@ int uv__io_fork(uv_loop_t* loop) { uv__free(loop->cf_state); loop->cf_state = NULL; } -#endif +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ return err; } @@ -387,6 +387,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct kevent*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; @@ -457,7 +458,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, if (uv__is_active(handle)) return UV_EINVAL; -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 /* Nullify field to perform checks later */ handle->cf_cb = NULL; handle->realpath = NULL; @@ -481,7 +482,7 @@ int uv_fs_event_start(uv_fs_event_t* handle, } return r; } -#endif /* defined(__APPLE__) */ +#endif /* #if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 */ /* TODO open asynchronously - but how do we report back errors? */ fd = open(path, O_RDONLY); @@ -489,8 +490,11 @@ int uv_fs_event_start(uv_fs_event_t* handle, return UV__ERR(errno); handle->path = uv__strdup(path); - if (handle->path == NULL) + if (handle->path == NULL) { + uv__close_nocheckstdio(fd); return UV_ENOMEM; + } + handle->cb = cb; uv__handle_start(handle); uv__io_init(&handle->event_watcher, uv__fs_event, fd); @@ -509,7 +513,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__handle_stop(handle); -#if defined(__APPLE__) +#if defined(__APPLE__) && MAC_OS_X_VERSION_MAX_ALLOWED >= 1070 if (!uv__has_forked_with_cfrunloop) r = uv__fsevents_close(handle); #endif diff --git a/Utilities/cmlibuv/src/unix/linux-core.c b/Utilities/cmlibuv/src/unix/linux-core.c index 3341b94..b539beb 100644 --- a/Utilities/cmlibuv/src/unix/linux-core.c +++ b/Utilities/cmlibuv/src/unix/linux-core.c @@ -26,6 +26,7 @@ #include "uv.h" #include "internal.h" +#include <inttypes.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> @@ -79,16 +80,20 @@ static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci); static void read_speeds(unsigned int numcpus, uv_cpu_info_t* ci); -static unsigned long read_cpufreq(unsigned int cpunum); +static uint64_t read_cpufreq(unsigned int cpunum); int uv__platform_loop_init(uv_loop_t* loop) { int fd; - fd = epoll_create1(EPOLL_CLOEXEC); + /* It was reported that EPOLL_CLOEXEC is not defined on Android API < 21, + * a.k.a. Lollipop. Since EPOLL_CLOEXEC is an alias for O_CLOEXEC on all + * architectures, we just use that instead. + */ + fd = epoll_create1(O_CLOEXEC); /* epoll_create1() can fail either because it's not implemented (old kernel) - * or because it doesn't understand the EPOLL_CLOEXEC flag. + * or because it doesn't understand the O_CLOEXEC flag. */ if (fd == -1 && (errno == ENOSYS || errno == EINVAL)) { fd = epoll_create(256); @@ -141,6 +146,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct epoll_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; @@ -714,20 +720,20 @@ static int read_models(unsigned int numcpus, uv_cpu_info_t* ci) { static int read_times(FILE* statfile_fp, unsigned int numcpus, uv_cpu_info_t* ci) { - unsigned long clock_ticks; struct uv_cpu_times_s ts; - unsigned long user; - unsigned long nice; - unsigned long sys; - unsigned long idle; - unsigned long dummy; - unsigned long irq; - unsigned int num; - unsigned int len; + uint64_t clock_ticks; + uint64_t user; + uint64_t nice; + uint64_t sys; + uint64_t idle; + uint64_t dummy; + uint64_t irq; + uint64_t num; + uint64_t len; char buf[1024]; clock_ticks = sysconf(_SC_CLK_TCK); - assert(clock_ticks != (unsigned long) -1); + assert(clock_ticks != (uint64_t) -1); assert(clock_ticks != 0); rewind(statfile_fp); @@ -760,7 +766,8 @@ static int read_times(FILE* statfile_fp, * fields, they're not allowed in C89 mode. */ if (6 != sscanf(buf + len, - "%lu %lu %lu %lu %lu %lu", + "%" PRIu64 " %" PRIu64 " %" PRIu64 + "%" PRIu64 " %" PRIu64 " %" PRIu64, &user, &nice, &sys, @@ -782,8 +789,8 @@ static int read_times(FILE* statfile_fp, } -static unsigned long read_cpufreq(unsigned int cpunum) { - unsigned long val; +static uint64_t read_cpufreq(unsigned int cpunum) { + uint64_t val; char buf[1024]; FILE* fp; @@ -796,7 +803,7 @@ static unsigned long read_cpufreq(unsigned int cpunum) { if (fp == NULL) return 0; - if (fscanf(fp, "%lu", &val) != 1) + if (fscanf(fp, "%" PRIu64, &val) != 1) val = 0; fclose(fp); @@ -859,7 +866,8 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { return 0; } - *addresses = uv__malloc(*count * sizeof(**addresses)); + /* Make sure the memory is initiallized to zero using calloc() */ + *addresses = uv__calloc(*count, sizeof(**addresses)); if (!(*addresses)) { freeifaddrs(addrs); return UV_ENOMEM; @@ -898,11 +906,12 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { address = *addresses; for (i = 0; i < (*count); i++) { - if (strcmp(address->name, ent->ifa_name) == 0) { + size_t namelen = strlen(ent->ifa_name); + /* Alias interface share the same physical address */ + if (strncmp(address->name, ent->ifa_name, namelen) == 0 && + (address->name[namelen] == 0 || address->name[namelen] == ':')) { sll = (struct sockaddr_ll*)ent->ifa_addr; memcpy(address->phys_addr, sll->sll_addr, sizeof(address->phys_addr)); - } else { - memset(address->phys_addr, 0, sizeof(address->phys_addr)); } address++; } @@ -932,3 +941,114 @@ void uv__set_process_title(const char* title) { prctl(PR_SET_NAME, title); /* Only copies first 16 characters. */ #endif } + + +static uint64_t uv__read_proc_meminfo(const char* what) { + uint64_t rc; + ssize_t n; + char* p; + int fd; + char buf[4096]; /* Large enough to hold all of /proc/meminfo. */ + + rc = 0; + fd = uv__open_cloexec("/proc/meminfo", O_RDONLY); + + if (fd == -1) + return 0; + + n = read(fd, buf, sizeof(buf) - 1); + + if (n <= 0) + goto out; + + buf[n] = '\0'; + p = strstr(buf, what); + + if (p == NULL) + goto out; + + p += strlen(what); + + if (1 != sscanf(p, "%" PRIu64 " kB", &rc)) + goto out; + + rc *= 1024; + +out: + + if (uv__close_nocheckstdio(fd)) + abort(); + + return rc; +} + + +uint64_t uv_get_free_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemFree:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.freeram * info.mem_unit; + + return 0; +} + + +uint64_t uv_get_total_memory(void) { + struct sysinfo info; + uint64_t rc; + + rc = uv__read_proc_meminfo("MemTotal:"); + + if (rc != 0) + return rc; + + if (0 == sysinfo(&info)) + return (uint64_t) info.totalram * info.mem_unit; + + return 0; +} + + +static uint64_t uv__read_cgroups_uint64(const char* cgroup, const char* param) { + char filename[256]; + uint64_t rc; + int fd; + ssize_t n; + char buf[32]; /* Large enough to hold an encoded uint64_t. */ + + snprintf(filename, 256, "/sys/fs/cgroup/%s/%s", cgroup, param); + + rc = 0; + fd = uv__open_cloexec(filename, O_RDONLY); + + if (fd < 0) + return 0; + + n = read(fd, buf, sizeof(buf) - 1); + + if (n > 0) { + buf[n] = '\0'; + sscanf(buf, "%" PRIu64, &rc); + } + + if (uv__close_nocheckstdio(fd)) + abort(); + + return rc; +} + + +uint64_t uv_get_constrained_memory(void) { + /* + * This might return 0 if there was a problem getting the memory limit from + * cgroups. This is OK because a return value of 0 signifies that the memory + * limit is unknown. + */ + return uv__read_cgroups_uint64("memory", "memory.limit_in_bytes"); +} diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.c b/Utilities/cmlibuv/src/unix/linux-syscalls.c index bfd7544..5637cf9 100644 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.c +++ b/Utilities/cmlibuv/src/unix/linux-syscalls.c @@ -187,6 +187,21 @@ # endif #endif /* __NR_pwritev */ +#ifndef __NR_statx +# if defined(__x86_64__) +# define __NR_statx 332 +# elif defined(__i386__) +# define __NR_statx 383 +# elif defined(__aarch64__) +# define __NR_statx 397 +# elif defined(__arm__) +# define __NR_statx (UV_SYSCALL_BASE + 397) +# elif defined(__ppc__) +# define __NR_statx 383 +# elif defined(__s390__) +# define __NR_statx 379 +# endif +#endif /* __NR_statx */ int uv__accept4(int fd, struct sockaddr* addr, socklen_t* addrlen, int flags) { #if defined(__i386__) @@ -336,3 +351,19 @@ int uv__dup3(int oldfd, int newfd, int flags) { return errno = ENOSYS, -1; #endif } + + +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf) { + /* __NR_statx make Android box killed by SIGSYS. + * That looks like a seccomp2 sandbox filter rejecting the system call. + */ +#if defined(__NR_statx) && !defined(__ANDROID__) + return syscall(__NR_statx, dirfd, path, flags, mask, statxbuf); +#else + return errno = ENOSYS, -1; +#endif +} diff --git a/Utilities/cmlibuv/src/unix/linux-syscalls.h b/Utilities/cmlibuv/src/unix/linux-syscalls.h index 3dfd329..7e58bfa 100644 --- a/Utilities/cmlibuv/src/unix/linux-syscalls.h +++ b/Utilities/cmlibuv/src/unix/linux-syscalls.h @@ -80,6 +80,36 @@ #define UV__IN_DELETE_SELF 0x400 #define UV__IN_MOVE_SELF 0x800 +struct uv__statx_timestamp { + int64_t tv_sec; + uint32_t tv_nsec; + int32_t unused0; +}; + +struct uv__statx { + uint32_t stx_mask; + uint32_t stx_blksize; + uint64_t stx_attributes; + uint32_t stx_nlink; + uint32_t stx_uid; + uint32_t stx_gid; + uint16_t stx_mode; + uint16_t unused0; + uint64_t stx_ino; + uint64_t stx_size; + uint64_t stx_blocks; + uint64_t stx_attributes_mask; + struct uv__statx_timestamp stx_atime; + struct uv__statx_timestamp stx_btime; + struct uv__statx_timestamp stx_ctime; + struct uv__statx_timestamp stx_mtime; + uint32_t stx_rdev_major; + uint32_t stx_rdev_minor; + uint32_t stx_dev_major; + uint32_t stx_dev_minor; + uint64_t unused1[14]; +}; + struct uv__inotify_event { int32_t wd; uint32_t mask; @@ -113,5 +143,10 @@ int uv__sendmmsg(int fd, ssize_t uv__preadv(int fd, const struct iovec *iov, int iovcnt, int64_t offset); ssize_t uv__pwritev(int fd, const struct iovec *iov, int iovcnt, int64_t offset); int uv__dup3(int oldfd, int newfd, int flags); +int uv__statx(int dirfd, + const char* path, + int flags, + unsigned int mask, + struct uv__statx* statxbuf); #endif /* UV_LINUX_SYSCALL_H_ */ diff --git a/Utilities/cmlibuv/src/unix/netbsd.c b/Utilities/cmlibuv/src/unix/netbsd.c index a2a4e52..c649bb3 100644 --- a/Utilities/cmlibuv/src/unix/netbsd.c +++ b/Utilities/cmlibuv/src/unix/netbsd.c @@ -126,6 +126,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { kvm_t *kd = NULL; struct kinfo_proc2 *kinfo = NULL; diff --git a/Utilities/cmlibuv/src/unix/openbsd.c b/Utilities/cmlibuv/src/unix/openbsd.c index bffb58b..ffae768 100644 --- a/Utilities/cmlibuv/src/unix/openbsd.c +++ b/Utilities/cmlibuv/src/unix/openbsd.c @@ -136,6 +136,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { struct kinfo_proc kinfo; size_t page_size = getpagesize(); diff --git a/Utilities/cmlibuv/src/unix/os390.c b/Utilities/cmlibuv/src/unix/os390.c index dc146e3..273ded7 100644 --- a/Utilities/cmlibuv/src/unix/os390.c +++ b/Utilities/cmlibuv/src/unix/os390.c @@ -356,6 +356,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + int uv_resident_set_memory(size_t* rss) { char* ascb; char* rax; @@ -657,6 +662,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct epoll_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; diff --git a/Utilities/cmlibuv/src/unix/pipe.c b/Utilities/cmlibuv/src/unix/pipe.c index 9657bc9..7d97550 100644 --- a/Utilities/cmlibuv/src/unix/pipe.c +++ b/Utilities/cmlibuv/src/unix/pipe.c @@ -213,7 +213,7 @@ void uv_pipe_connect(uv_connect_t* req, } if (err == 0) - uv__io_start(handle->loop, &handle->io_watcher, POLLIN | POLLOUT); + uv__io_start(handle->loop, &handle->io_watcher, POLLOUT); out: handle->delayed_error = err; @@ -231,9 +231,6 @@ out: } -typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); - - static int uv__pipe_getsockpeername(const uv_pipe_t* handle, uv__peersockfunc func, char* buffer, @@ -244,10 +241,13 @@ static int uv__pipe_getsockpeername(const uv_pipe_t* handle, addrlen = sizeof(sa); memset(&sa, 0, addrlen); - err = func(uv__stream_fd(handle), (struct sockaddr*) &sa, &addrlen); + err = uv__getsockpeername((const uv_handle_t*) handle, + func, + (struct sockaddr*) &sa, + (int*) &addrlen); if (err < 0) { *size = 0; - return UV__ERR(errno); + return err; } #if defined(__linux__) diff --git a/Utilities/cmlibuv/src/unix/posix-poll.c b/Utilities/cmlibuv/src/unix/posix-poll.c index f3181f9..a3b9f21 100644 --- a/Utilities/cmlibuv/src/unix/posix-poll.c +++ b/Utilities/cmlibuv/src/unix/posix-poll.c @@ -298,6 +298,8 @@ update_timeout: void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { size_t i; + assert(fd >= 0); + if (loop->poll_fds_iterating) { /* uv__io_poll is currently iterating. Just invalidate fd. */ for (i = 0; i < loop->poll_fds_used; i++) diff --git a/Utilities/cmlibuv/src/unix/process.c b/Utilities/cmlibuv/src/unix/process.c index e9579f5..f4826bf 100644 --- a/Utilities/cmlibuv/src/unix/process.c +++ b/Utilities/cmlibuv/src/unix/process.c @@ -426,6 +426,11 @@ static void uv__process_child_init(const uv_process_options_t* options, if (n == SIGKILL || n == SIGSTOP) continue; /* Can't be changed. */ +#if defined(__HAIKU__) + if (n == SIGKILLTHR) + continue; /* Can't be changed. */ +#endif + if (SIG_ERR != signal(n, SIG_DFL)) continue; @@ -486,6 +491,8 @@ int uv_spawn(uv_loop_t* loop, UV_PROCESS_SETGID | UV_PROCESS_SETUID | UV_PROCESS_WINDOWS_HIDE | + UV_PROCESS_WINDOWS_HIDE_CONSOLE | + UV_PROCESS_WINDOWS_HIDE_GUI | UV_PROCESS_WINDOWS_VERBATIM_ARGUMENTS))); uv__handle_init(loop, (uv_handle_t*)process, UV_PROCESS); diff --git a/Utilities/cmlibuv/src/unix/stream.c b/Utilities/cmlibuv/src/unix/stream.c index 4b9123f..8121f64 100644 --- a/Utilities/cmlibuv/src/unix/stream.c +++ b/Utilities/cmlibuv/src/unix/stream.c @@ -745,13 +745,13 @@ static int uv__write_req_update(uv_stream_t* stream, buf = req->bufs + req->write_index; - while (n > 0) { + do { len = n < buf->len ? n : buf->len; buf->base += len; buf->len -= len; buf += (buf->len == 0); /* Advance to next buffer if this one is empty. */ n -= len; - } + } while (n > 0); req->write_index = buf - req->bufs; @@ -897,7 +897,7 @@ start: goto error; } - if (n > 0 && uv__write_req_update(stream, req, n)) { + if (n >= 0 && uv__write_req_update(stream, req, n)) { uv__write_req_finish(req); return; /* TODO(bnoordhuis) Start trying to write the next request. */ } @@ -1541,7 +1541,7 @@ int uv_try_write(uv_stream_t* stream, } if (written == 0 && req_size != 0) - return UV_EAGAIN; + return req.error < 0 ? req.error : UV_EAGAIN; else return written; } diff --git a/Utilities/cmlibuv/src/unix/sunos.c b/Utilities/cmlibuv/src/unix/sunos.c index aac6504..0cd25c1 100644 --- a/Utilities/cmlibuv/src/unix/sunos.c +++ b/Utilities/cmlibuv/src/unix/sunos.c @@ -121,6 +121,7 @@ void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { uintptr_t nfds; assert(loop->watchers != NULL); + assert(fd >= 0); events = (struct port_event*) loop->watchers[loop->nwatchers]; nfds = (uintptr_t) loop->watchers[loop->nwatchers + 1]; @@ -138,8 +139,10 @@ int uv__io_check_fd(uv_loop_t* loop, int fd) { if (port_associate(loop->backend_fd, PORT_SOURCE_FD, fd, POLLIN, 0)) return UV__ERR(errno); - if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) + if (port_dissociate(loop->backend_fd, PORT_SOURCE_FD, fd)) { + perror("(libuv) port_dissociate()"); abort(); + } return 0; } @@ -177,8 +180,14 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { w = QUEUE_DATA(q, uv__io_t, watcher_queue); assert(w->pevents != 0); - if (port_associate(loop->backend_fd, PORT_SOURCE_FD, w->fd, w->pevents, 0)) + if (port_associate(loop->backend_fd, + PORT_SOURCE_FD, + w->fd, + w->pevents, + 0)) { + perror("(libuv) port_associate()"); abort(); + } w->events = w->pevents; } @@ -222,10 +231,12 @@ void uv__io_poll(uv_loop_t* loop, int timeout) { /* Work around another kernel bug: port_getn() may return events even * on error. */ - if (errno == EINTR || errno == ETIME) + if (errno == EINTR || errno == ETIME) { saved_errno = errno; - else + } else { + perror("(libuv) port_getn()"); abort(); + } } /* Update loop->time unconditionally. It's tempting to skip the update when @@ -373,6 +384,11 @@ uint64_t uv_get_total_memory(void) { } +uint64_t uv_get_constrained_memory(void) { + return 0; /* Memory constraints are unknown. */ +} + + void uv_loadavg(double avg[3]) { (void) getloadavg(avg, 3); } diff --git a/Utilities/cmlibuv/src/unix/tcp.c b/Utilities/cmlibuv/src/unix/tcp.c index 2982851..8cedcd6 100644 --- a/Utilities/cmlibuv/src/unix/tcp.c +++ b/Utilities/cmlibuv/src/unix/tcp.c @@ -82,7 +82,7 @@ static int maybe_new_socket(uv_tcp_t* handle, int domain, unsigned long flags) { handle->flags |= flags; return 0; } - + /* Query to see if tcp socket is bound. */ slen = sizeof(saddr); memset(&saddr, 0, sizeof(saddr)); @@ -235,12 +235,16 @@ int uv__tcp_connect(uv_connect_t* req, if (r == -1 && errno != 0) { if (errno == EINPROGRESS) ; /* not an error */ - else if (errno == ECONNREFUSED) - /* If we get a ECONNREFUSED wait until the next tick to report the - * error. Solaris wants to report immediately--other unixes want to - * wait. + else if (errno == ECONNREFUSED +#if defined(__OpenBSD__) + || errno == EINVAL +#endif + ) + /* If we get ECONNREFUSED (Solaris) or EINVAL (OpenBSD) wait until the + * next tick to report the error. Solaris and OpenBSD wants to report + * immediately -- other unixes want to wait. */ - handle->delayed_error = UV__ERR(errno); + handle->delayed_error = UV__ERR(ECONNREFUSED); else return UV__ERR(errno); } @@ -279,44 +283,28 @@ int uv_tcp_open(uv_tcp_t* handle, uv_os_sock_t sock) { int uv_tcp_getsockname(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - socklen_t socklen; if (handle->delayed_error) return handle->delayed_error; - if (uv__stream_fd(handle) < 0) - return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ - - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; - - if (getsockname(uv__stream_fd(handle), name, &socklen)) - return UV__ERR(errno); - - *namelen = (int) socklen; - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen); } int uv_tcp_getpeername(const uv_tcp_t* handle, struct sockaddr* name, int* namelen) { - socklen_t socklen; if (handle->delayed_error) return handle->delayed_error; - if (uv__stream_fd(handle) < 0) - return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ - - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; - - if (getpeername(uv__stream_fd(handle), name, &socklen)) - return UV__ERR(errno); - - *namelen = (int) socklen; - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen); } diff --git a/Utilities/cmlibuv/src/unix/thread.c b/Utilities/cmlibuv/src/unix/thread.c index 2900470..cd0b7aa 100644 --- a/Utilities/cmlibuv/src/unix/thread.c +++ b/Utilities/cmlibuv/src/unix/thread.c @@ -48,8 +48,10 @@ STATIC_ASSERT(sizeof(uv_barrier_t) == sizeof(pthread_barrier_t)); #endif -/* Note: guard clauses should match uv_barrier_t's in include/uv/uv-unix.h. */ -#if defined(_AIX) || !defined(PTHREAD_BARRIER_SERIAL_THREAD) +/* Note: guard clauses should match uv_barrier_t's in include/uv/unix.h. */ +#if defined(_AIX) || \ + defined(__OpenBSD__) || \ + !defined(PTHREAD_BARRIER_SERIAL_THREAD) int uv_barrier_init(uv_barrier_t* barrier, unsigned int count) { struct _uv_barrier* b; int rc; @@ -176,8 +178,21 @@ static size_t thread_stack_size(void) { if (lim.rlim_cur != RLIM_INFINITY) { /* pthread_attr_setstacksize() expects page-aligned values. */ lim.rlim_cur -= lim.rlim_cur % (rlim_t) getpagesize(); - if (lim.rlim_cur >= PTHREAD_STACK_MIN) - return lim.rlim_cur; + + /* Musl's PTHREAD_STACK_MIN is 2 KB on all architectures, which is + * too small to safely receive signals on. + * + * Musl's PTHREAD_STACK_MIN + MINSIGSTKSZ == 8192 on arm64 (which has + * the largest MINSIGSTKSZ of the architectures that musl supports) so + * let's use that as a lower bound. + * + * We use a hardcoded value because PTHREAD_STACK_MIN + MINSIGSTKSZ + * is between 28 and 133 KB when compiling against glibc, depending + * on the architecture. + */ + if (lim.rlim_cur >= 8192) + if (lim.rlim_cur >= PTHREAD_STACK_MIN) + return lim.rlim_cur; } #endif @@ -192,13 +207,36 @@ static size_t thread_stack_size(void) { int uv_thread_create(uv_thread_t *tid, void (*entry)(void *arg), void *arg) { + uv_thread_options_t params; + params.flags = UV_THREAD_NO_FLAGS; + return uv_thread_create_ex(tid, ¶ms, entry, arg); +} + +int uv_thread_create_ex(uv_thread_t* tid, + const uv_thread_options_t* params, + void (*entry)(void *arg), + void *arg) { int err; - size_t stack_size; pthread_attr_t* attr; pthread_attr_t attr_storage; + size_t pagesize; + size_t stack_size; + + stack_size = + params->flags & UV_THREAD_HAS_STACK_SIZE ? params->stack_size : 0; attr = NULL; - stack_size = thread_stack_size(); + if (stack_size == 0) { + stack_size = thread_stack_size(); + } else { + pagesize = (size_t)getpagesize(); + /* Round up to the nearest page boundary. */ + stack_size = (stack_size + pagesize - 1) &~ (pagesize - 1); +#ifdef PTHREAD_STACK_MIN + if (stack_size < PTHREAD_STACK_MIN) + stack_size = PTHREAD_STACK_MIN; +#endif + } if (stack_size > 0) { attr = &attr_storage; @@ -778,7 +816,9 @@ int uv_cond_timedwait(uv_cond_t* cond, uv_mutex_t* mutex, uint64_t timeout) { return UV_ETIMEDOUT; abort(); +#ifndef __SUNPRO_C return UV_EINVAL; /* Satisfy the compiler. */ +#endif } diff --git a/Utilities/cmlibuv/src/unix/udp.c b/Utilities/cmlibuv/src/unix/udp.c index ec337ec..b578e7b 100644 --- a/Utilities/cmlibuv/src/unix/udp.c +++ b/Utilities/cmlibuv/src/unix/udp.c @@ -30,6 +30,7 @@ #if defined(__MVS__) #include <xti.h> #endif +#include <sys/un.h> #if defined(IPV6_JOIN_GROUP) && !defined(IPV6_ADD_MEMBERSHIP) # define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP @@ -227,9 +228,22 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { assert(req != NULL); memset(&h, 0, sizeof h); - h.msg_name = &req->addr; - h.msg_namelen = (req->addr.ss_family == AF_INET6 ? - sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in)); + if (req->addr.ss_family == AF_UNSPEC) { + h.msg_name = NULL; + h.msg_namelen = 0; + } else { + h.msg_name = &req->addr; + if (req->addr.ss_family == AF_INET6) + h.msg_namelen = sizeof(struct sockaddr_in6); + else if (req->addr.ss_family == AF_INET) + h.msg_namelen = sizeof(struct sockaddr_in); + else if (req->addr.ss_family == AF_UNIX) + h.msg_namelen = sizeof(struct sockaddr_un); + else { + assert(0 && "unsupported address family"); + abort(); + } + } h.msg_iov = (struct iovec*) req->bufs; h.msg_iovlen = req->nbufs; @@ -263,16 +277,30 @@ static void uv__udp_sendmsg(uv_udp_t* handle) { * are different from the BSDs: it _shares_ the port rather than steal it * from the current listener. While useful, it's not something we can emulate * on other platforms so we don't enable it. + * + * zOS does not support getsockname with SO_REUSEPORT option when using + * AF_UNIX. */ static int uv__set_reuse(int fd) { int yes; - -#if defined(SO_REUSEPORT) && !defined(__linux__) yes = 1; + +#if defined(SO_REUSEPORT) && defined(__MVS__) + struct sockaddr_in sockfd; + unsigned int sockfd_len = sizeof(sockfd); + if (getsockname(fd, (struct sockaddr*) &sockfd, &sockfd_len) == -1) + return UV__ERR(errno); + if (sockfd.sin_family == AF_UNIX) { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) + return UV__ERR(errno); + } else { + if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) + return UV__ERR(errno); + } +#elif defined(SO_REUSEPORT) && !defined(__linux__) if (setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &yes, sizeof(yes))) return UV__ERR(errno); #else - yes = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes))) return UV__ERR(errno); #endif @@ -383,6 +411,50 @@ static int uv__udp_maybe_deferred_bind(uv_udp_t* handle, } +int uv__udp_connect(uv_udp_t* handle, + const struct sockaddr* addr, + unsigned int addrlen) { + int err; + + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + + do { + errno = 0; + err = connect(handle->io_watcher.fd, addr, addrlen); + } while (err == -1 && errno == EINTR); + + if (err) + return UV__ERR(errno); + + handle->flags |= UV_HANDLE_UDP_CONNECTED; + + return 0; +} + + +int uv__udp_disconnect(uv_udp_t* handle) { + int r; + struct sockaddr addr; + + memset(&addr, 0, sizeof(addr)); + + addr.sa_family = AF_UNSPEC; + + do { + errno = 0; + r = connect(handle->io_watcher.fd, &addr, sizeof(addr)); + } while (r == -1 && errno == EINTR); + + if (r == -1 && errno != EAFNOSUPPORT) + return UV__ERR(errno); + + handle->flags &= ~UV_HANDLE_UDP_CONNECTED; + return 0; +} + + int uv__udp_send(uv_udp_send_t* req, uv_udp_t* handle, const uv_buf_t bufs[], @@ -395,9 +467,11 @@ int uv__udp_send(uv_udp_send_t* req, assert(nbufs > 0); - err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); - if (err) - return err; + if (addr) { + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + } /* It's legal for send_queue_count > 0 even when the write_queue is empty; * it means there are error-state requests in the write_completed_queue that @@ -407,7 +481,10 @@ int uv__udp_send(uv_udp_send_t* req, uv__req_init(handle->loop, req, UV_UDP_SEND); assert(addrlen <= sizeof(req->addr)); - memcpy(&req->addr, addr, addrlen); + if (addr == NULL) + req->addr.ss_family = AF_UNSPEC; + else + memcpy(&req->addr, addr, addrlen); req->send_cb = send_cb; req->handle = handle; req->nbufs = nbufs; @@ -459,9 +536,13 @@ int uv__udp_try_send(uv_udp_t* handle, if (handle->send_queue_count != 0) return UV_EAGAIN; - err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); - if (err) - return err; + if (addr) { + err = uv__udp_maybe_deferred_bind(handle, addr->sa_family, 0); + if (err) + return err; + } else { + assert(handle->flags & UV_HANDLE_UDP_CONNECTED); + } memset(&h, 0, sizeof h); h.msg_name = (struct sockaddr*) addr; @@ -608,6 +689,7 @@ int uv_udp_init_ex(uv_loop_t* loop, uv_udp_t* handle, unsigned int flags) { uv__io_init(&handle->io_watcher, uv__udp_io, fd); QUEUE_INIT(&handle->write_queue); QUEUE_INIT(&handle->write_completed_queue); + return 0; } @@ -636,6 +718,9 @@ int uv_udp_open(uv_udp_t* handle, uv_os_sock_t sock) { return err; handle->io_watcher.fd = sock; + if (uv__udp_is_connected(handle)) + handle->flags |= UV_HANDLE_UDP_CONNECTED; + return 0; } @@ -743,13 +828,17 @@ int uv_udp_set_ttl(uv_udp_t* handle, int ttl) { IPV6_UNICAST_HOPS, &ttl, sizeof(ttl)); -#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || - defined(__MVS__) */ + +#else /* !(defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || + defined(__MVS__)) */ return uv__setsockopt_maybe_char(handle, IP_TTL, IPV6_UNICAST_HOPS, ttl); + +#endif /* defined(__sun) || defined(_AIX) || defined (__OpenBSD__) || + defined(__MVS__) */ } @@ -851,23 +940,24 @@ int uv_udp_set_multicast_interface(uv_udp_t* handle, const char* interface_addr) return 0; } - -int uv_udp_getsockname(const uv_udp_t* handle, +int uv_udp_getpeername(const uv_udp_t* handle, struct sockaddr* name, int* namelen) { - socklen_t socklen; - - if (handle->io_watcher.fd == -1) - return UV_EINVAL; /* FIXME(bnoordhuis) UV_EBADF */ - /* sizeof(socklen_t) != sizeof(int) on some systems. */ - socklen = (socklen_t) *namelen; + return uv__getsockpeername((const uv_handle_t*) handle, + getpeername, + name, + namelen); +} - if (getsockname(handle->io_watcher.fd, name, &socklen)) - return UV__ERR(errno); +int uv_udp_getsockname(const uv_udp_t* handle, + struct sockaddr* name, + int* namelen) { - *namelen = (int) socklen; - return 0; + return uv__getsockpeername((const uv_handle_t*) handle, + getsockname, + name, + namelen); } |