From 6398251aff527f0e45ddae4138249d8fdcc2ebbf Mon Sep 17 00:00:00 2001 From: Brad King Date: Thu, 6 Apr 2017 21:56:34 -0400 Subject: [PATCH] cygwin: implement support for cygwin and msys2 Cygwin and MSYS2 are POSIX layers implemented on top of Windows. Use our POSIX `poll(2)` implementation of our poll abstraction. For most other components we already have dedicated sources implementing them in terms of APIs available on Cygwin or providing non-implementations of components not supported. This leaves only three components that need Cygwin-specific implementations: * uv_uptime: implement using sysinfo * uv_resident_set_memory: add a placeholder returning UV_ENOSYS * uv_cpu_info: add a placeholder returning UV_ENOSYS Update our test suite to account for features not available due to Cygwin platform limitations or our placeholders. PR-URL: https://github.com/libuv/libuv/pull/1312 Reviewed-By: Ben Noordhuis Reviewed-By: Santiago Gimeno --- Makefile.am | 26 ++++++++++++++ configure.ac | 2 ++ include/uv-unix.h | 2 ++ src/unix/bsd-ifaddrs.c | 6 ++++ src/unix/core.c | 2 ++ src/unix/cygwin.c | 54 +++++++++++++++++++++++++++++ test/task.h | 2 +- test/test-fs.c | 29 ++++++++++++++-- test/test-ip6-addr.c | 4 +++ test/test-ping-pong.c | 4 +++ test/test-platform-output.c | 8 +++++ test/test-poll.c | 2 +- test/test-process-title.c | 2 +- test/test-signal-multiple-loops.c | 7 ++++ test/test-spawn.c | 19 ++++++++++ test/test-tcp-create-socket-early.c | 2 +- test/test-udp-create-socket-early.c | 2 +- test/test-udp-ipv6.c | 5 +++ test/test-watcher-cross-stop.c | 5 +++ 19 files changed, 176 insertions(+), 7 deletions(-) create mode 100644 src/unix/cygwin.c diff --git a/Makefile.am b/Makefile.am index 7db9c306..404674ba 100644 --- a/Makefile.am +++ b/Makefile.am @@ -337,6 +337,19 @@ libuv_la_SOURCES += src/unix/android-ifaddrs.c \ src/unix/pthread-barrier.c endif +if CYGWIN +libuv_la_CFLAGS += -D_GNU_SOURCE +libuv_la_SOURCES += src/unix/cygwin.c \ + src/unix/bsd-ifaddrs.c \ + src/unix/no-fsevents.c \ + src/unix/no-proctitle.c \ + src/unix/posix-hrtime.c \ + src/unix/posix-poll.c \ + src/unix/procfs-exepath.c \ + src/unix/sysinfo-loadavg.c \ + src/unix/sysinfo-memory.c +endif + if DARWIN include_HEADERS += include/uv-darwin.h \ include/pthread-barrier.h @@ -384,6 +397,19 @@ libuv_la_SOURCES += src/unix/linux-core.c \ test_run_tests_LDFLAGS += -lutil endif +if MSYS +libuv_la_CFLAGS += -D_GNU_SOURCE +libuv_la_SOURCES += src/unix/cygwin.c \ + src/unix/bsd-ifaddrs.c \ + src/unix/no-fsevents.c \ + src/unix/no-proctitle.c \ + src/unix/posix-hrtime.c \ + src/unix/posix-poll.c \ + src/unix/procfs-exepath.c \ + src/unix/sysinfo-loadavg.c \ + src/unix/sysinfo-memory.c +endif + if NETBSD include_HEADERS += include/uv-bsd.h libuv_la_SOURCES += src/unix/bsd-ifaddrs.c \ diff --git a/configure.ac b/configure.ac index 963b4f15..593a6196 100644 --- a/configure.ac +++ b/configure.ac @@ -52,10 +52,12 @@ AC_CHECK_LIB([socket], [socket]) AC_SYS_LARGEFILE AM_CONDITIONAL([AIX], [AS_CASE([$host_os],[aix*], [true], [false])]) AM_CONDITIONAL([ANDROID], [AS_CASE([$host_os],[linux-android*],[true], [false])]) +AM_CONDITIONAL([CYGWIN], [AS_CASE([$host_os],[cygwin*], [true], [false])]) AM_CONDITIONAL([DARWIN], [AS_CASE([$host_os],[darwin*], [true], [false])]) AM_CONDITIONAL([DRAGONFLY],[AS_CASE([$host_os],[dragonfly*], [true], [false])]) AM_CONDITIONAL([FREEBSD], [AS_CASE([$host_os],[*freebsd*], [true], [false])]) AM_CONDITIONAL([LINUX], [AS_CASE([$host_os],[linux*], [true], [false])]) +AM_CONDITIONAL([MSYS], [AS_CASE([$host_os],[msys*], [true], [false])]) AM_CONDITIONAL([NETBSD], [AS_CASE([$host_os],[netbsd*], [true], [false])]) AM_CONDITIONAL([OPENBSD], [AS_CASE([$host_os],[openbsd*], [true], [false])]) AM_CONDITIONAL([OS390], [AS_CASE([$host_os],[openedition*], [true], [false])]) diff --git a/include/uv-unix.h b/include/uv-unix.h index 54407295..d7754509 100644 --- a/include/uv-unix.h +++ b/include/uv-unix.h @@ -60,6 +60,8 @@ defined(__OpenBSD__) || \ defined(__NetBSD__) # include "uv-bsd.h" +#elif defined(__CYGWIN__) || defined(__MSYS__) +# include "uv-posix.h" #endif #ifndef PTHREAD_BARRIER_SERIAL_THREAD diff --git a/src/unix/bsd-ifaddrs.c b/src/unix/bsd-ifaddrs.c index 0b5653d0..41478945 100644 --- a/src/unix/bsd-ifaddrs.c +++ b/src/unix/bsd-ifaddrs.c @@ -27,7 +27,9 @@ #include #include +#if !defined(__CYGWIN__) && !defined(__MSYS__) #include +#endif static int uv__ifaddr_exclude(struct ifaddrs *ent) { if (!((ent->ifa_flags & IFF_UP) && (ent->ifa_flags & IFF_RUNNING))) @@ -107,9 +109,13 @@ int uv_interface_addresses(uv_interface_address_t** addresses, int* count) { for (i = 0; i < *count; i++) { if (strcmp(address->name, ent->ifa_name) == 0) { +#if defined(__CYGWIN__) || defined(__MSYS__) + memset(address->phys_addr, 0, sizeof(address->phys_addr)); +#else struct sockaddr_dl* sa_addr; sa_addr = (struct sockaddr_dl*)(ent->ifa_addr); memcpy(address->phys_addr, LLADDR(sa_addr), sizeof(address->phys_addr)); +#endif } address++; } diff --git a/src/unix/core.c b/src/unix/core.c index 9754403c..30cdaef7 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -545,6 +545,7 @@ int uv__nonblock_ioctl(int fd, int set) { } +#if !defined(__CYGWIN__) && !defined(__MSYS__) int uv__cloexec_ioctl(int fd, int set) { int r; @@ -557,6 +558,7 @@ int uv__cloexec_ioctl(int fd, int set) { return 0; } +#endif int uv__nonblock_fcntl(int fd, int set) { diff --git a/src/unix/cygwin.c b/src/unix/cygwin.c new file mode 100644 index 00000000..5a887dd4 --- /dev/null +++ b/src/unix/cygwin.c @@ -0,0 +1,54 @@ +/* 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 +#include + +int uv_uptime(double* uptime) { + struct sysinfo info; + + if (sysinfo(&info) < 0) + return -errno; + + *uptime = info.uptime; + return 0; +} + +int uv_resident_set_memory(size_t* rss) { + /* FIXME: read /proc/meminfo? */ + *rss = 0; + return UV_ENOSYS; +} + +int uv_cpu_info(uv_cpu_info_t** cpu_infos, int* count) { + /* FIXME: read /proc/stat? */ + *cpu_infos = NULL; + *count = 0; + return UV_ENOSYS; +} + +void uv_free_cpu_info(uv_cpu_info_t* cpu_infos, int count) { + (void)cpu_infos; + (void)count; +} diff --git a/test/task.h b/test/task.h index 9d59dbbb..b2d2418d 100644 --- a/test/task.h +++ b/test/task.h @@ -209,7 +209,7 @@ UNUSED static int can_ipv6(void) { return supported; } -#if defined(__MVS__) +#if defined(__MVS__) || defined(__CYGWIN__) || defined(__MSYS__) # define NO_FS_EVENTS "Filesystem watching not supported on this platform." #endif diff --git a/test/test-fs.c b/test/test-fs.c index a92feb91..ce39ebd4 100644 --- a/test/test-fs.c +++ b/test/test-fs.c @@ -125,7 +125,7 @@ static void check_permission(const char* filename, unsigned int mode) { ASSERT(req.result == 0); s = &req.statbuf; -#ifdef _WIN32 +#if defined(_WIN32) || defined(__CYGWIN__) || defined(__MSYS__) /* * On Windows, chmod can only modify S_IWUSR (_S_IWRITE) bit, * so only testing for the specified flags. @@ -240,7 +240,7 @@ static void chown_cb(uv_fs_t* req) { static void chown_root_cb(uv_fs_t* req) { ASSERT(req->fs_type == UV_FS_CHOWN); -#ifdef _WIN32 +#if defined(_WIN32) || defined(__MSYS__) /* On windows, chown is a no-op and always succeeds. */ ASSERT(req->result == 0); #else @@ -250,7 +250,12 @@ static void chown_root_cb(uv_fs_t* req) { if (geteuid() == 0) ASSERT(req->result == 0); else +# if defined(__CYGWIN__) + /* On Cygwin, uid 0 is invalid (no root). */ + ASSERT(req->result == UV_EINVAL); +# else ASSERT(req->result == UV_EPERM); +# endif #endif chown_cb_count++; uv_fs_req_cleanup(req); @@ -641,6 +646,11 @@ TEST_IMPL(fs_file_loop) { */ if (r == UV_ENOTSUP || r == UV_EPERM) return 0; +#elif defined(__MSYS__) + /* MSYS2's approximation of symlinks with copies does not work for broken + links. */ + if (r == UV_ENOENT) + return 0; #endif ASSERT(r == 0); uv_fs_req_cleanup(&req); @@ -1735,6 +1745,10 @@ TEST_IMPL(fs_symlink) { ASSERT(r == 0); uv_fs_req_cleanup(&req); +#if defined(__MSYS__) + RETURN_SKIP("symlink reading is not supported on MSYS2"); +#endif + r = uv_fs_readlink(NULL, &req, "test_file_symlink_symlink", NULL); ASSERT(r == 0); ASSERT(strcmp(req.ptr, "test_file_symlink") == 0); @@ -1878,6 +1892,9 @@ TEST_IMPL(fs_symlink_dir) { r = uv_fs_lstat(NULL, &req, "test_dir_symlink", NULL); ASSERT(r == 0); +#if defined(__MSYS__) + RETURN_SKIP("symlink reading is not supported on MSYS2"); +#endif ASSERT(((uv_stat_t*)req.ptr)->st_mode & S_IFLNK); #ifdef _WIN32 ASSERT(((uv_stat_t*)req.ptr)->st_size == strlen(test_dir + 4)); @@ -2103,8 +2120,13 @@ TEST_IMPL(fs_futime) { uv_fs_req_cleanup(&req); r = uv_fs_futime(NULL, &req, file, atime, mtime, NULL); +#if defined(__CYGWIN__) || defined(__MSYS__) + ASSERT(r == UV_ENOSYS); + RETURN_SKIP("futime not supported on Cygwin"); +#else ASSERT(r == 0); ASSERT(req.result == 0); +#endif uv_fs_req_cleanup(&req); r = uv_fs_stat(NULL, &req, path, NULL); @@ -2413,6 +2435,9 @@ TEST_IMPL(fs_rename_to_existing_file) { TEST_IMPL(fs_read_file_eof) { +#if defined(__CYGWIN__) || defined(__MSYS__) + RETURN_SKIP("Cygwin pread at EOF may (incorrectly) return data!"); +#endif int r; /* Setup. */ diff --git a/test/test-ip6-addr.c b/test/test-ip6-addr.c index 869b099e..156fccde 100644 --- a/test/test-ip6-addr.c +++ b/test/test-ip6-addr.c @@ -32,6 +32,10 @@ TEST_IMPL(ip6_addr_link_local) { +#if defined(__CYGWIN__) || defined(__MSYS__) + /* FIXME: Does Cygwin support this? */ + RETURN_SKIP("FIXME: This test needs more investigation on Cygwin"); +#endif char string_address[INET6_ADDRSTRLEN]; uv_interface_address_t* addresses; uv_interface_address_t* address; diff --git a/test/test-ping-pong.c b/test/test-ping-pong.c index c0741785..bdc96715 100644 --- a/test/test-ping-pong.c +++ b/test/test-ping-pong.c @@ -27,7 +27,11 @@ static int completed_pingers = 0; +#if defined(__CYGWIN__) || defined(__MSYS__) +#define NUM_PINGS 100 /* fewer pings to avoid timeout */ +#else #define NUM_PINGS 1000 +#endif /* 64 bytes is enough for a pinger */ #define BUFSIZE 10240 diff --git a/test/test-platform-output.c b/test/test-platform-output.c index b8955080..72c176ed 100644 --- a/test/test-platform-output.c +++ b/test/test-platform-output.c @@ -47,8 +47,12 @@ TEST_IMPL(platform_output) { printf("uv_cwd: %s\n", buffer); err = uv_resident_set_memory(&rss); +#if defined(__CYGWIN__) || defined(__MSYS__) + ASSERT(err == UV_ENOSYS); +#else ASSERT(err == 0); printf("uv_resident_set_memory: %llu\n", (unsigned long long) rss); +#endif err = uv_uptime(&uptime); ASSERT(err == 0); @@ -73,6 +77,9 @@ TEST_IMPL(platform_output) { (unsigned long long) rusage.ru_maxrss); err = uv_cpu_info(&cpus, &count); +#if defined(__CYGWIN__) || defined(__MSYS__) + ASSERT(err == UV_ENOSYS); +#else ASSERT(err == 0); printf("uv_cpu_info:\n"); @@ -88,6 +95,7 @@ TEST_IMPL(platform_output) { printf(" times.nice: %llu\n", (unsigned long long) cpus[i].cpu_times.nice); } +#endif uv_free_cpu_info(cpus, count); err = uv_interface_addresses(&interfaces, &count); diff --git a/test/test-poll.c b/test/test-poll.c index 71948dee..0cf6740f 100644 --- a/test/test-poll.c +++ b/test/test-poll.c @@ -595,7 +595,7 @@ TEST_IMPL(poll_unidirectional) { TEST_IMPL(poll_bad_fdtype) { #if !defined(__DragonFly__) && !defined(__FreeBSD__) && !defined(__sun) && \ !defined(_AIX) && !defined(__MVS__) && !defined(__FreeBSD_kernel__) && \ - !defined(__OpenBSD__) + !defined(__OpenBSD__) && !defined(__CYGWIN__) && !defined(__MSYS__) uv_poll_t poll_handle; int fd; diff --git a/test/test-process-title.c b/test/test-process-title.c index f7a0bf61..886f83a7 100644 --- a/test/test-process-title.c +++ b/test/test-process-title.c @@ -60,7 +60,7 @@ static void uv_get_process_title_edge_cases(void) { TEST_IMPL(process_title) { -#if defined(__sun) +#if defined(__sun) || defined(__CYGWIN__) || defined(__MSYS__) RETURN_SKIP("uv_(get|set)_process_title is not implemented."); #else /* Check for format string vulnerabilities. */ diff --git a/test/test-signal-multiple-loops.c b/test/test-signal-multiple-loops.c index 15812991..11193dcf 100644 --- a/test/test-signal-multiple-loops.c +++ b/test/test-signal-multiple-loops.c @@ -193,6 +193,13 @@ static void loop_creating_worker(void* context) { TEST_IMPL(signal_multiple_loops) { +#if defined(__CYGWIN__) || defined(__MSYS__) + /* FIXME: This test needs more investigation. Somehow the `read` in + uv__signal_lock fails spuriously with EACCES or even EAGAIN even + though it is supposed to be blocking. Also the test hangs during + thread setup occasionally. */ + RETURN_SKIP("FIXME: This test needs more investigation on Cygwin"); +#endif uv_thread_t loop_creating_threads[NUM_LOOP_CREATING_THREADS]; uv_thread_t signal_handling_threads[NUM_SIGNAL_HANDLING_THREADS]; enum signal_action action; diff --git a/test/test-spawn.c b/test/test-spawn.c index e6f1ceb7..52fc7f6c 100644 --- a/test/test-spawn.c +++ b/test/test-spawn.c @@ -1297,7 +1297,11 @@ TEST_IMPL(spawn_setuid_fails) { options.uid = 0; r = uv_spawn(uv_default_loop(), &process, &options); +#if defined(__CYGWIN__) + ASSERT(r == UV_EINVAL); +#else ASSERT(r == UV_EPERM); +#endif r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); @@ -1328,7 +1332,11 @@ TEST_IMPL(spawn_setgid_fails) { options.gid = 0; r = uv_spawn(uv_default_loop(), &process, &options); +#if defined(__CYGWIN__) + ASSERT(r == UV_EINVAL); +#else ASSERT(r == UV_EPERM); +#endif r = uv_run(uv_default_loop(), UV_RUN_DEFAULT); ASSERT(r == 0); @@ -1537,6 +1545,17 @@ TEST_IMPL(spawn_reads_child_path) { exepath[len] = 0; strcpy(path, "PATH="); strcpy(path + 5, exepath); +#if defined(__CYGWIN__) || defined(__MSYS__) + /* Carry over the dynamic linker path in case the test runner + is linked against cyguv-1.dll or msys-uv-1.dll, see above. */ + { + char* syspath = getenv("PATH"); + if (syspath != NULL) { + strcat(path, ":"); + strcat(path, syspath); + } + } +#endif env[0] = path; env[1] = getenv(dyld_path_var); diff --git a/test/test-tcp-create-socket-early.c b/test/test-tcp-create-socket-early.c index 1a508e47..b87e7324 100644 --- a/test/test-tcp-create-socket-early.c +++ b/test/test-tcp-create-socket-early.c @@ -164,7 +164,7 @@ TEST_IMPL(tcp_create_early_bad_bind) { #endif r = uv_tcp_bind(&client, (const struct sockaddr*) &addr, 0); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MSYS__) ASSERT(r == UV_EINVAL); #else ASSERT(r == UV_EFAULT); diff --git a/test/test-udp-create-socket-early.c b/test/test-udp-create-socket-early.c index 3f302740..f7e46abc 100644 --- a/test/test-udp-create-socket-early.c +++ b/test/test-udp-create-socket-early.c @@ -104,7 +104,7 @@ TEST_IMPL(udp_create_early_bad_bind) { #endif r = uv_udp_bind(&client, (const struct sockaddr*) &addr, 0); -#ifndef _WIN32 +#if !defined(_WIN32) && !defined(__CYGWIN__) && !defined(__MSYS__) ASSERT(r == UV_EINVAL); #else ASSERT(r == UV_EFAULT); diff --git a/test/test-udp-ipv6.c b/test/test-udp-ipv6.c index a65f09e0..54b364da 100644 --- a/test/test-udp-ipv6.c +++ b/test/test-udp-ipv6.c @@ -163,6 +163,11 @@ static void do_test(uv_udp_recv_cb recv_cb, int bind_flags) { TEST_IMPL(udp_dual_stack) { +#if defined(__CYGWIN__) || defined(__MSYS__) + /* FIXME: Does Cygwin support this? */ + RETURN_SKIP("FIXME: This test needs more investigation on Cygwin"); +#endif + if (!can_ipv6()) RETURN_SKIP("IPv6 not supported"); diff --git a/test/test-watcher-cross-stop.c b/test/test-watcher-cross-stop.c index 6ff48d44..29a82a5c 100644 --- a/test/test-watcher-cross-stop.c +++ b/test/test-watcher-cross-stop.c @@ -26,7 +26,12 @@ #include /* NOTE: Number should be big enough to trigger this problem */ +#if defined(__CYGWIN__) || defined(__MSYS__) +/* Cygwin crashes or hangs in socket() with too many AF_INET sockets. */ +static uv_udp_t sockets[1250]; +#else static uv_udp_t sockets[2500]; +#endif static uv_udp_send_t reqs[ARRAY_SIZE(sockets)]; static char slab[1]; static unsigned int recv_cb_called;