diff --git a/platforms/common/include/px4_platform_common/atomic.h b/platforms/common/include/px4_platform_common/atomic.h index 47b3fb6de8..3d8329dd16 100644 --- a/platforms/common/include/px4_platform_common/atomic.h +++ b/platforms/common/include/px4_platform_common/atomic.h @@ -58,6 +58,10 @@ #include #include +#if defined(_MSC_VER) && !defined(__clang__) +# include +#endif + #if defined(__PX4_NUTTX) # include #endif // __PX4_NUTTX @@ -73,7 +77,9 @@ public: #if defined(__PX4_POSIX) // Ensure that all operations are lock-free, so that 'atomic' can be used from // IRQ handlers. This might not be required everywhere though. +# if !defined(_MSC_VER) || defined(__clang__) static_assert(__atomic_always_lock_free(sizeof(T), 0), "atomic is not lock-free for the given type T"); +# endif #endif // __PX4_POSIX atomic() = default; @@ -95,7 +101,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.load(std::memory_order_seq_cst); +#else return __atomic_load_n(&_value, __ATOMIC_SEQ_CST); +#endif } } @@ -114,7 +124,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + _value.store(value, std::memory_order_seq_cst); +#else __atomic_store(&_value, &value, __ATOMIC_SEQ_CST); +#endif } } @@ -136,7 +150,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.fetch_add(num, std::memory_order_seq_cst); +#else return __atomic_fetch_add(&_value, num, __ATOMIC_SEQ_CST); +#endif } } @@ -158,7 +176,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.fetch_sub(num, std::memory_order_seq_cst); +#else return __atomic_fetch_sub(&_value, num, __ATOMIC_SEQ_CST); +#endif } } @@ -180,7 +202,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.fetch_and(num, std::memory_order_seq_cst); +#else return __atomic_fetch_and(&_value, num, __ATOMIC_SEQ_CST); +#endif } } @@ -202,7 +228,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.fetch_xor(num, std::memory_order_seq_cst); +#else return __atomic_fetch_xor(&_value, num, __ATOMIC_SEQ_CST); +#endif } } @@ -224,7 +254,11 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.fetch_or(num, std::memory_order_seq_cst); +#else return __atomic_fetch_or(&_value, num, __ATOMIC_SEQ_CST); +#endif } } @@ -246,7 +280,18 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + T expected = _value.load(std::memory_order_seq_cst); + T desired; + + do { + desired = static_cast(~(expected & num)); + } while (!_value.compare_exchange_weak(expected, desired, std::memory_order_seq_cst, std::memory_order_seq_cst)); + + return expected; +#else return __atomic_fetch_nand(&_value, num, __ATOMIC_SEQ_CST); +#endif } } @@ -279,12 +324,20 @@ public: } else #endif // __PX4_NUTTX { +#if defined(_MSC_VER) && !defined(__clang__) + return _value.compare_exchange_strong(*expected, desired, std::memory_order_seq_cst, std::memory_order_seq_cst); +#else return __atomic_compare_exchange(&_value, expected, &desired, false, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST); +#endif } } private: +#if defined(_MSC_VER) && !defined(__clang__) + std::atomic _value {}; +#else T _value {}; +#endif }; using atomic_int = atomic; diff --git a/platforms/common/include/px4_platform_common/defines.h b/platforms/common/include/px4_platform_common/defines.h index 134c04c8fc..340be3f7da 100644 --- a/platforms/common/include/px4_platform_common/defines.h +++ b/platforms/common/include/px4_platform_common/defines.h @@ -52,10 +52,21 @@ /* Define PX4_ISFINITE */ #ifdef __cplusplus +#if defined(_MSC_VER) && !defined(__clang__) +#include +static inline bool PX4_ISFINITE(float x) { return std::isfinite(x); } +static inline bool PX4_ISFINITE(double x) { return std::isfinite(x); } +#else static inline constexpr bool PX4_ISFINITE(float x) { return __builtin_isfinite(x); } static inline constexpr bool PX4_ISFINITE(double x) { return __builtin_isfinite(x); } +#endif +#else +#if defined(_MSC_VER) && !defined(__clang__) +#include +#define PX4_ISFINITE(x) isfinite(x) #else #define PX4_ISFINITE(x) __builtin_isfinite(x) +#endif #endif /* __cplusplus */ #if defined(__PX4_NUTTX) @@ -82,6 +93,12 @@ static inline constexpr bool PX4_ISFINITE(double x) { return __builtin_isfinite( #define O_BINARY 0 #endif +// S_IRUSR / S_IWUSR / S_IRGRP / ... live in . Most POSIX +// consumers of this header pull it in transitively via , but +// MinGW's fcntl.h doesn't — include it explicitly so the PX4_O_MODE_* +// macros below always resolve. +#include + // mode for open with O_CREAT #define PX4_O_MODE_777 (S_IRWXU | S_IRWXG | S_IRWXO) #define PX4_O_MODE_666 (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH ) @@ -109,6 +126,19 @@ __END_DECLS ****************************************************************************/ #define OK 0 +// defines ERROR as 0 for its GDI error codes and pulls it in +// transitively from . Undefine before redefining so we match +// the historical PX4 POSIX semantics. +#ifdef ERROR +#undef ERROR +#endif +// Windows also leaks IGNORE (value 0, used by the +// NEEDED_ACCESS / ServiceControl surface). PX4 uses IGNORE as an enum +// member in ControlAllocator::FailureMode — drop the Windows macro +// since no PX4 code consumes the Win32 meaning. +#ifdef IGNORE +#undef IGNORE +#endif #define ERROR -1 #define MAX_RAND 32767 diff --git a/platforms/common/include/px4_platform_common/exit.h b/platforms/common/include/px4_platform_common/exit.h new file mode 100644 index 0000000000..d2a6b34991 --- /dev/null +++ b/platforms/common/include/px4_platform_common/exit.h @@ -0,0 +1,69 @@ +/**************************************************************************** + * + * Copyright (c) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file exit.h + * + * Platform-level process exit hook. + */ + +#pragma once + +#include + +#if defined(__PX4_WINDOWS) +#include +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +static inline void px4_platform_exit(int status) noreturn_function; +static inline void px4_platform_exit(int status) +{ +#if defined(__PX4_WINDOWS) + px4_windows_exit(status); +#else + system_exit(status); +#endif +#if defined(_MSC_VER) && !defined(__clang__) + __assume(0); +#else + __builtin_unreachable(); +#endif +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/common/include/px4_platform_common/posix.h b/platforms/common/include/px4_platform_common/posix.h index 3c3ff8ce3c..e8664c13fe 100644 --- a/platforms/common/include/px4_platform_common/posix.h +++ b/platforms/common/include/px4_platform_common/posix.h @@ -117,7 +117,7 @@ __EXPORT int px4_open(const char *path, int flags, ...); __EXPORT int px4_close(int fd); __EXPORT ssize_t px4_read(int fd, void *buffer, size_t buflen); __EXPORT ssize_t px4_write(int fd, const void *buffer, size_t buflen); -__EXPORT int px4_ioctl(int fd, int cmd, unsigned long arg); +__EXPORT int px4_ioctl(int fd, int cmd, uintptr_t arg); __EXPORT int px4_poll(px4_pollfd_struct_t *fds, unsigned int nfds, int timeout); __EXPORT int px4_access(const char *pathname, int mode); __EXPORT px4_task_t px4_getpid(void); @@ -131,7 +131,7 @@ __END_DECLS // we often run out of stack space when pointers are larger than 4 bytes. // Double the stack size on posix when we're on a 64-bit architecture. // Most full-scale OS use 1-4K of memory from the stack themselves -#define PX4_STACK_ADJUSTED(_s) (_s * (__SIZEOF_POINTER__ >> 2) + PX4_STACK_OVERHEAD) +#define PX4_STACK_ADJUSTED(_s) (_s * (sizeof(void *) >> 2) + PX4_STACK_OVERHEAD) __BEGIN_DECLS diff --git a/platforms/common/include/px4_platform_common/tasks.h b/platforms/common/include/px4_platform_common/tasks.h index 6609b4cdb0..4f77b13587 100644 --- a/platforms/common/include/px4_platform_common/tasks.h +++ b/platforms/common/include/px4_platform_common/tasks.h @@ -73,6 +73,15 @@ typedef int px4_task_t; #define SCHED_PRIORITY_MIN sched_get_priority_min(SCHED_FIFO) #define SCHED_PRIORITY_DEFAULT (((sched_get_priority_max(SCHED_FIFO) - sched_get_priority_min(SCHED_FIFO)) / 2) + sched_get_priority_min(SCHED_FIFO)) +#elif defined(__PX4_WINDOWS) + +/* winpthreads maps SCHED_FIFO/SCHED_OTHER onto Win32 thread priorities. + * sched_get_priority_{max,min}(SCHED_FIFO) works but returns a narrow + * range (15..-15). Use fixed values consistent with the rest of PX4. */ +#define SCHED_PRIORITY_MAX 15 +#define SCHED_PRIORITY_MIN (-15) +#define SCHED_PRIORITY_DEFAULT 0 + #elif defined(__PX4_QURT) #define SCHED_PRIORITY_MAX 255 diff --git a/platforms/common/include/px4_platform_common/time.h b/platforms/common/include/px4_platform_common/time.h index d61e23c75c..e1edc1467a 100644 --- a/platforms/common/include/px4_platform_common/time.h +++ b/platforms/common/include/px4_platform_common/time.h @@ -25,20 +25,25 @@ __END_DECLS #endif -#if defined(ENABLE_LOCKSTEP_SCHEDULER) +#if defined(ENABLE_LOCKSTEP_SCHEDULER) || defined(__PX4_WINDOWS) __BEGIN_DECLS __EXPORT int px4_usleep(useconds_t usec); __EXPORT unsigned int px4_sleep(unsigned int seconds); +#if defined(ENABLE_LOCKSTEP_SCHEDULER) __EXPORT int px4_pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); +#endif __END_DECLS #else #define px4_usleep system_usleep #define px4_sleep system_sleep -#define px4_pthread_cond_timedwait system_pthread_cond_timedwait #endif + +#if !defined(ENABLE_LOCKSTEP_SCHEDULER) +#define px4_pthread_cond_timedwait system_pthread_cond_timedwait +#endif diff --git a/platforms/common/px4_work_queue/WorkQueueManager.cpp b/platforms/common/px4_work_queue/WorkQueueManager.cpp index 8d22e32cb2..d520921c15 100644 --- a/platforms/common/px4_work_queue/WorkQueueManager.cpp +++ b/platforms/common/px4_work_queue/WorkQueueManager.cpp @@ -49,6 +49,11 @@ #include #include +#if defined(__PX4_POSIX) +#include +#include +#endif + using namespace time_literals; namespace px4 @@ -274,8 +279,8 @@ WorkQueueManagerRun(int, char **) #elif defined(__PX4_POSIX) // On posix system , the desired stacksize round to the nearest multiplier of the system pagesize // It is a requirement of the pthread_attr_setstacksize* function - const unsigned int page_size = sysconf(_SC_PAGESIZE); - const size_t stacksize_adj = math::max((int)PTHREAD_STACK_MIN, PX4_STACK_ADJUSTED(wq->stacksize)); + const size_t page_size = static_cast(sysconf(_SC_PAGESIZE)); + const size_t stacksize_adj = math::max(static_cast(PTHREAD_STACK_MIN), static_cast(PX4_STACK_ADJUSTED(wq->stacksize))); const size_t stacksize = (stacksize_adj + page_size - (stacksize_adj % page_size)); #endif @@ -304,15 +309,33 @@ WorkQueueManagerRun(int, char **) PX4_ERR("getting sched param for %s failed (%i)", wq->name, ret_getschedparam); } - // schedule policy FIFO - int ret_setschedpolicy = pthread_attr_setschedpolicy(&attr, SCHED_FIFO); + // schedule policy FIFO — fall back to SCHED_OTHER on platforms + // where SCHED_FIFO is not available to unprivileged threads + // (winpthreads on MinGW is one such case). The work queues + // still work correctly at SCHED_OTHER priority. + int sched_policy = SCHED_FIFO; + int ret_setschedpolicy = pthread_attr_setschedpolicy(&attr, sched_policy); if (ret_setschedpolicy != 0) { - PX4_ERR("failed to set sched policy SCHED_FIFO (%i)", ret_setschedpolicy); + sched_policy = SCHED_OTHER; + ret_setschedpolicy = pthread_attr_setschedpolicy(&attr, sched_policy); } - // priority - param.sched_priority = sched_priority; + if (ret_setschedpolicy != 0) { + PX4_ERR("failed to set sched policy (%i)", ret_setschedpolicy); + } + + // priority — clamp to the policy's valid range, otherwise + // pthread_attr_setschedparam rejects the value and the + // thread ends up at whatever default the library picks. + const int max_prio = sched_get_priority_max(sched_policy); + const int min_prio = sched_get_priority_min(sched_policy); + int effective_prio = sched_priority; + + if (effective_prio > max_prio) { effective_prio = max_prio; } + if (effective_prio < min_prio) { effective_prio = min_prio; } + + param.sched_priority = effective_prio; int ret_setschedparam = pthread_attr_setschedparam(&attr, ¶m); if (ret_setschedparam != 0) { diff --git a/platforms/common/uORB/uORBDeviceNode.cpp b/platforms/common/uORB/uORBDeviceNode.cpp index fd4ef60e63..04f589bbbb 100644 --- a/platforms/common/uORB/uORBDeviceNode.cpp +++ b/platforms/common/uORB/uORBDeviceNode.cpp @@ -209,7 +209,7 @@ uORB::DeviceNode::write(cdev::file_t *filp, const char *buffer, size_t buflen) } int -uORB::DeviceNode::ioctl(cdev::file_t *filp, int cmd, unsigned long arg) +uORB::DeviceNode::ioctl(cdev::file_t *filp, int cmd, uintptr_t arg) { switch (cmd) { case ORBIOCUPDATED: { diff --git a/platforms/common/uORB/uORBDeviceNode.hpp b/platforms/common/uORB/uORBDeviceNode.hpp index 527c5ddf2b..9e296ab374 100644 --- a/platforms/common/uORB/uORBDeviceNode.hpp +++ b/platforms/common/uORB/uORBDeviceNode.hpp @@ -114,7 +114,7 @@ public: /** * IOCTL control for the subscriber. */ - int ioctl(cdev::file_t *filp, int cmd, unsigned long arg) override; + int ioctl(cdev::file_t *filp, int cmd, uintptr_t arg) override; /** * Method to publish a data to this node. diff --git a/platforms/common/uORB/uORBManager.cpp b/platforms/common/uORB/uORBManager.cpp index 3fa75a3454..cf8c02401e 100644 --- a/platforms/common/uORB/uORBManager.cpp +++ b/platforms/common/uORB/uORBManager.cpp @@ -302,7 +302,7 @@ orb_advert_t uORB::Manager::orb_advertise_multi(const struct orb_metadata *meta, /* get the advertiser handle and close the node */ orb_advert_t advertiser; - int result = px4_ioctl(fd, ORBIOCGADVERTISER, (unsigned long)&advertiser); + int result = px4_ioctl(fd, ORBIOCGADVERTISER, (uintptr_t)&advertiser); px4_close(fd); if (result == PX4_ERROR) { @@ -397,7 +397,7 @@ int uORB::Manager::orb_check(int handle, bool *updated) { /* Set to false here so that if `px4_ioctl` fails to false. */ *updated = false; - return px4_ioctl(handle, ORBIOCUPDATED, (unsigned long)(uintptr_t)updated); + return px4_ioctl(handle, ORBIOCUPDATED, (uintptr_t)updated); } int uORB::Manager::orb_set_interval(int handle, unsigned interval) @@ -407,7 +407,7 @@ int uORB::Manager::orb_set_interval(int handle, unsigned interval) int uORB::Manager::orb_get_interval(int handle, unsigned *interval) { - int ret = px4_ioctl(handle, ORBIOCGETINTERVAL, (unsigned long)interval); + int ret = px4_ioctl(handle, ORBIOCGETINTERVAL, (uintptr_t)interval); *interval /= 1000; return ret; } diff --git a/platforms/common/uORB/uORBManagerUsr.cpp b/platforms/common/uORB/uORBManagerUsr.cpp index e1a413e4c6..d2f03180fb 100644 --- a/platforms/common/uORB/uORBManagerUsr.cpp +++ b/platforms/common/uORB/uORBManagerUsr.cpp @@ -102,7 +102,7 @@ orb_advert_t uORB::Manager::orb_advertise_multi(const struct orb_metadata *meta, /* get the advertiser handle and close the node */ orb_advert_t advertiser; - int result = px4_ioctl(fd, ORBIOCGADVERTISER, (unsigned long)&advertiser); + int result = px4_ioctl(fd, ORBIOCGADVERTISER, (uintptr_t)&advertiser); px4_close(fd); if (result == PX4_ERROR) { @@ -177,7 +177,7 @@ int uORB::Manager::orb_check(int handle, bool *updated) { /* Set to false here so that if `px4_ioctl` fails to false. */ *updated = false; - return px4_ioctl(handle, ORBIOCUPDATED, (unsigned long)(uintptr_t)updated); + return px4_ioctl(handle, ORBIOCUPDATED, (uintptr_t)updated); } int uORB::Manager::orb_set_interval(int handle, unsigned interval) @@ -187,7 +187,7 @@ int uORB::Manager::orb_set_interval(int handle, unsigned interval) int uORB::Manager::orb_get_interval(int handle, unsigned *interval) { - int ret = px4_ioctl(handle, ORBIOCGETINTERVAL, (unsigned long)interval); + int ret = px4_ioctl(handle, ORBIOCGETINTERVAL, (uintptr_t)interval); *interval /= 1000; return ret; } diff --git a/platforms/posix/include/px4_windows/platform.h b/platforms/posix/include/px4_windows/platform.h new file mode 100644 index 0000000000..fbe9fd1c1a --- /dev/null +++ b/platforms/posix/include/px4_windows/platform.h @@ -0,0 +1,84 @@ +/**************************************************************************** + * + * Copyright (c) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/** + * @file platform.h + * + * Public Windows-host hooks for the POSIX platform implementation. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Restore the console modes captured during Windows platform startup. + * + * PX4 enables virtual-terminal output and may switch the input console into a + * raw-ish mode for the pxh shell. This must run before process teardown so a + * native console, a Wine-hosted terminal, or a parent shell does not inherit + * stale input flags. + */ +void px4_windows_restore_console_modes(void); + +/** + * @brief Drop console input that was queued while PX4 was shutting down. + * + * Wine can leave bytes for control sequences or buffered pxh input pending in + * the Linux terminal after Ctrl+C. The shutdown path calls this before + * returning control to the parent shell. + */ +void px4_windows_discard_pending_input(void); + +/** + * @brief Release Windows console resources owned by the PX4 process. + * + * Native Windows builds use this as the last console cleanup step; under Wine + * it complements the terminal-mode restore and input discard hooks above. + */ +void px4_windows_release_console(void); + +/** + * @brief Exit PX4 after running Windows-specific console cleanup. + * + * @param status Process exit code passed to the C runtime after cleanup. + */ +void px4_windows_exit(int status) noreturn_function; + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/Windows.h b/platforms/posix/include/windows_shim/Windows.h new file mode 100644 index 0000000000..59c7633cd7 --- /dev/null +++ b/platforms/posix/include/windows_shim/Windows.h @@ -0,0 +1,43 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file Windows.h + * + * MinGW-w64 only ships the lowercase , but a lot of + * Windows-targeted code (including Micro-XRCE-DDS-Client) writes + * #include . On case-sensitive Linux filesystems that fails + * when cross-compiling. This header is on the include search path + * ahead of the MinGW sysroot and just forwards to the real header. + */ +#pragma once +#include diff --git a/platforms/posix/include/windows_shim/afunix.h b/platforms/posix/include/windows_shim/afunix.h new file mode 100644 index 0000000000..e893b2a6e3 --- /dev/null +++ b/platforms/posix/include/windows_shim/afunix.h @@ -0,0 +1,116 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file afunix.h + * + * The Windows SDK's afunix.h is intentionally small: it defines the AF_UNIX + * socket address payload (`SOCKADDR_UN`, `PSOCKADDR_UN`, `UNIX_PATH_MAX`) and + * the AF_UNIX-specific WSAIoctl constants. Older MinGW sysroots do not ship + * that header at all, while newer ones may grow it over time. Mirror the + * native Windows SDK surface when present, provide a compatible fallback when + * absent, and layer the common POSIX convenience aliases (`sockaddr_un`, + * `SUN_LEN`) on top. + */ +#pragma once + +#ifndef _WIN32 +# error "afunix.h shim only valid on Windows" +#endif + +#include +#include +#include + +#if defined(__has_include_next) +# if __has_include_next() +# include_next +# endif +#endif + +#ifndef _AFUNIX_ +#define _AFUNIX_ + +#ifndef AF_UNIX +#define AF_UNIX 1 +#endif + +#ifndef PF_UNIX +#define PF_UNIX AF_UNIX +#endif + +#ifndef UNIX_PATH_MAX +#define UNIX_PATH_MAX 108 +#endif + +/** + * @brief AF_UNIX socket address compatible with the Windows SDK definition. + * + * The typedef aliases mirror the SDK names so code can use either POSIX + * `struct sockaddr_un` or Windows `SOCKADDR_UN`. + */ +typedef struct sockaddr_un { + ADDRESS_FAMILY sun_family; /* AF_UNIX */ + char sun_path[UNIX_PATH_MAX]; /* pathname */ +} SOCKADDR_UN, *PSOCKADDR_UN; + +#ifndef SIO_AF_UNIX_GETPEERPID +#define SIO_AF_UNIX_GETPEERPID _WSAIOR(IOC_VENDOR, 256) +#endif + +#ifndef SIO_AF_UNIX_SETBINDPARENTPATH +#define SIO_AF_UNIX_SETBINDPARENTPATH _WSAIOW(IOC_VENDOR, 257) +#endif + +#ifndef SIO_AF_UNIX_SETCONNPARENTPATH +#define SIO_AF_UNIX_SETCONNPARENTPATH _WSAIOW(IOC_VENDOR, 258) +#endif + +#endif /* _AFUNIX_ */ + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef _PX4_SOCKADDR_UN_ALIAS_DEFINED +#define _PX4_SOCKADDR_UN_ALIAS_DEFINED +/** @brief POSIX alias for struct sockaddr_un. */ +typedef struct sockaddr_un sockaddr_un; +#endif + +#ifndef SUN_LEN +#define SUN_LEN(ptr) ((socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path))) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/arpa/inet.h b/platforms/posix/include/windows_shim/arpa/inet.h new file mode 100644 index 0000000000..83261cf53e --- /dev/null +++ b/platforms/posix/include/windows_shim/arpa/inet.h @@ -0,0 +1,75 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file arpa/inet.h + * + * Re-export Winsock's address-conversion surface through the POSIX include + * path and provide the classic BSD helpers that Windows does not ship: + * `inet_aton`, `inet_ntoa_r`, `inet_network`, `inet_makeaddr`, + * `inet_lnaof`, and `inet_netof`. + */ +#pragma once + +#ifndef _WIN32 +# error "arpa/inet.h shim only valid on Windows" +#endif + +#include + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Parse an IPv4 address string into struct in_addr. */ +int inet_aton(const char *cp, struct in_addr *inp); + +/** @brief Thread-safe inet_ntoa() variant writing into caller storage. */ +char *inet_ntoa_r(struct in_addr in, char *buf, size_t buflen); + +/** @brief Parse a classful IPv4 network number. */ +in_addr_t inet_network(const char *cp); + +/** @brief Return the local-network portion of a classful IPv4 address. */ +in_addr_t inet_lnaof(struct in_addr in); + +/** @brief Return the classful network portion of an IPv4 address. */ +in_addr_t inet_netof(struct in_addr in); + +/** @brief Compose an IPv4 address from classful network and host parts. */ +struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/dirent.h b/platforms/posix/include/windows_shim/dirent.h new file mode 100644 index 0000000000..e2b0fa6b45 --- /dev/null +++ b/platforms/posix/include/windows_shim/dirent.h @@ -0,0 +1,314 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file dirent.h + * + * MinGW ships a wrapper over FindFirstFile, but its `struct + * dirent` is a subset of POSIX: no `d_type`, no DT_REG/DT_DIR. PX4 + * (mavlink_ftp, logger) relies on `d_type` to tell files apart from + * directories at iteration time without a second stat(). + * + * Replace MinGW's header entirely with a POSIX-shaped wrapper that + * populates d_type from the Win32 attributes already carried by + * WIN32_FIND_DATA. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifndef NAME_MAX +#define NAME_MAX 260 +#endif + +/* POSIX d_type values. We only populate DT_DIR / DT_REG; anything the + * Win32 attributes don't distinguish falls through to DT_UNKNOWN. */ +#define DT_UNKNOWN 0 +#define DT_FIFO 1 +#define DT_CHR 2 +#define DT_DIR 4 +#define DT_BLK 6 +#define DT_REG 8 +#define DT_LNK 10 +#define DT_SOCK 12 + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Directory entry returned by readdir(). + * + * The fields match the POSIX/BSD layout expected by PX4 callers. d_type is + * derived from WIN32_FIND_DATA attributes so code can distinguish regular + * files, directories, and reparse points without an extra stat() call. + */ +struct dirent { + unsigned long d_ino; + long d_off; + unsigned short d_reclen; + unsigned short d_namlen; + unsigned char d_type; + char d_name[NAME_MAX + 1]; +}; + +#define d_fileno d_ino + +/** + * @brief Opaque directory stream backed by FindFirstFileA/FindNextFileA. + */ +typedef struct px4_dir_shim { + HANDLE handle; + WIN32_FIND_DATAA find_data; + struct dirent entry; + int first; + long offset; + char pattern[MAX_PATH]; +} DIR; + +/** + * @brief Open a directory stream. + * + * @param name Directory path. + * @return Directory stream, or NULL with errno set. + */ +static inline DIR *opendir(const char *name) +{ + if (!name) { errno = EINVAL; return NULL; } + + DIR *d = (DIR *)calloc(1, sizeof(DIR)); + if (!d) { errno = ENOMEM; return NULL; } + + /* Translate "foo" -> "foo\*" so FindFirstFile enumerates the + * directory's contents rather than matching the directory itself. */ + int n = snprintf(d->pattern, sizeof(d->pattern), "%s\\*", name); + if (n <= 0 || (size_t)n >= sizeof(d->pattern)) { + free(d); + errno = ENAMETOOLONG; + return NULL; + } + + d->handle = FindFirstFileA(d->pattern, &d->find_data); + if (d->handle == INVALID_HANDLE_VALUE) { + free(d); + errno = ENOENT; + return NULL; + } + d->first = 1; + d->offset = 0; + return d; +} + +/** + * @brief Return the next directory entry. + * + * @return Pointer owned by @p d and overwritten by the next readdir() call, or + * NULL at end of directory or on error. + */ +static inline struct dirent *readdir(DIR *d) +{ + if (!d) { errno = EBADF; return NULL; } + + if (d->first) { + d->first = 0; + } else if (!FindNextFileA(d->handle, &d->find_data)) { + return NULL; /* end of directory */ + } + + strncpy(d->entry.d_name, d->find_data.cFileName, NAME_MAX); + d->entry.d_name[NAME_MAX] = '\0'; + d->entry.d_off = d->offset++; + d->entry.d_reclen = (unsigned short)strlen(d->entry.d_name); + d->entry.d_namlen = d->entry.d_reclen; + + if (d->find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + d->entry.d_type = DT_DIR; + } else if (d->find_data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + d->entry.d_type = DT_LNK; + } else { + d->entry.d_type = DT_REG; + } + + return &d->entry; +} + +/** @brief Close a directory stream and free its resources. */ +static inline int closedir(DIR *d) +{ + if (!d) { errno = EBADF; return -1; } + if (d->handle != INVALID_HANDLE_VALUE) { + FindClose(d->handle); + } + free(d); + return 0; +} + +/** @brief Rewind a directory stream to the first entry. */ +static inline void rewinddir(DIR *d) +{ + if (!d) { return; } + if (d->handle != INVALID_HANDLE_VALUE) { + FindClose(d->handle); + } + d->handle = FindFirstFileA(d->pattern, &d->find_data); + d->first = (d->handle != INVALID_HANDLE_VALUE) ? 1 : 0; + d->offset = 0; +} + +/** @brief Return the current logical directory offset. */ +static inline long telldir(DIR *d) +{ + if (!d) { + errno = EBADF; + return -1; + } + + return d->offset; +} + +/** @brief Seek to a logical offset previously returned by telldir(). */ +static inline void seekdir(DIR *d, long loc) +{ + if (!d || loc < 0) { + return; + } + + rewinddir(d); + + while (d->offset < loc) { + if (!readdir(d)) { + break; + } + } +} + +/** @brief Sort directory entries lexicographically by d_name. */ +static inline int alphasort(const struct dirent **a, const struct dirent **b) +{ + return strcoll((*a)->d_name, (*b)->d_name); +} + +/** @brief Version-aware sort placeholder; currently identical to alphasort(). */ +static inline int versionsort(const struct dirent **a, const struct dirent **b) +{ + return alphasort(a, b); +} + +/** + * @brief Read a directory into an allocated, optionally filtered/sorted list. + * + * The caller owns the returned array and each entry in it, matching POSIX + * scandir() ownership rules. + * + * @return Number of entries on success, or -1 with errno set. + */ +static inline int scandir(const char *dirp, struct dirent ***namelist, + int (*select)(const struct dirent *), + int (*compar)(const struct dirent **, const struct dirent **)) +{ + if (!namelist) { + errno = EINVAL; + return -1; + } + + *namelist = NULL; + + DIR *dir = opendir(dirp); + + if (!dir) { + return -1; + } + + size_t count = 0; + size_t capacity = 0; + struct dirent **list = NULL; + struct dirent *entry = NULL; + + while ((entry = readdir(dir)) != NULL) { + if (select && !select(entry)) { + continue; + } + + if (count == capacity) { + size_t next_capacity = capacity ? capacity * 2 : 16; + struct dirent **next = (struct dirent **)realloc(list, next_capacity * sizeof(*next)); + + if (!next) { + for (size_t i = 0; i < count; ++i) { + free(list[i]); + } + + free(list); + closedir(dir); + errno = ENOMEM; + return -1; + } + + list = next; + capacity = next_capacity; + } + + struct dirent *copy = (struct dirent *)malloc(sizeof(*copy)); + + if (!copy) { + for (size_t i = 0; i < count; ++i) { + free(list[i]); + } + + free(list); + closedir(dir); + errno = ENOMEM; + return -1; + } + + *copy = *entry; + list[count++] = copy; + } + + closedir(dir); + + if (compar && count > 1) { + qsort(list, count, sizeof(*list), (int (*)(const void *, const void *))compar); + } + + *namelist = list; + return (int)count; +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/dlfcn.h b/platforms/posix/include/windows_shim/dlfcn.h new file mode 100644 index 0000000000..7b1f07a76f --- /dev/null +++ b/platforms/posix/include/windows_shim/dlfcn.h @@ -0,0 +1,122 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file dlfcn.h + * + * The systemcmds/dyn module uses dlopen/dlsym to load .so plugins at + * runtime. On Windows we translate to LoadLibraryA / GetProcAddress / + * FreeLibrary. Modes are mostly ignored - Windows has no lazy/now, + * global/local distinction comparable to RTLD_* at the API level. + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define RTLD_LAZY 0x0001 +#define RTLD_NOW 0x0002 +#define RTLD_GLOBAL 0x0100 +#define RTLD_LOCAL 0x0000 +#define RTLD_NODELETE 0x1000 +#define RTLD_NOLOAD 0x0004 +#define RTLD_DEEPBIND 0x0008 +#define RTLD_DEFAULT ((void *)0) +#define RTLD_NEXT ((void *)-1) + +/** + * @brief Load a dynamic library using Windows LoadLibraryA semantics. + * + * @param filename Library path. A NULL filename is accepted for source + * compatibility but has no portable Windows equivalent. + * @param flag POSIX RTLD_* mode bits. The shim accepts them for API + * compatibility; Windows binding semantics are selected by the + * loader, not by these flags. + * @return Library handle on success, NULL on failure. Use dlerror() for text. + */ +void *dlopen(const char *filename, int flag); + +/** + * @brief Unload a library handle returned by dlopen(). + * + * @return 0 on success, -1 on failure with dlerror() text set. + */ +int dlclose(void *handle); + +/** + * @brief Look up an exported symbol in a library. + * + * @param handle Handle returned by dlopen(). + * @param symbol Export name to resolve. + * @return Symbol address on success, NULL on failure. + */ +void *dlsym(void *handle, const char *symbol); + +/** + * @brief Fetch and clear the thread-local dynamic-loader error string. + * + * @return Last loader error string, or NULL if no error is pending. + */ +char *dlerror(void); + +/** + * @brief POSIX dladdr information container. + * + * Windows does not expose all fields with POSIX fidelity, but keeping this + * type allows diagnostics and third-party code to compile. + */ +typedef struct { + const char *dli_fname; + void *dli_fbase; + const char *dli_sname; + void *dli_saddr; +} Dl_info; + +/** + * @brief Resolve best-effort module/symbol information for an address. + * + * @return Non-zero on success, 0 on failure. + */ +int dladdr(const void *addr, Dl_info *info); + +/** + * @brief Versioned symbol lookup compatibility entry point. + * + * Windows exports are not ELF-versioned, so the implementation ignores + * @p version and delegates to dlsym(). + */ +void *dlvsym(void *handle, const char *symbol, const char *version); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/fcntl.h b/platforms/posix/include/windows_shim/fcntl.h new file mode 100644 index 0000000000..cf201f330b --- /dev/null +++ b/platforms/posix/include/windows_shim/fcntl.h @@ -0,0 +1,332 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file fcntl.h + * + * MinGW-w64 ships a minimal (O_RDONLY / O_BINARY / O_CREAT / + * etc.) but none of the POSIX fcntl() operations - F_GETFL, F_SETFL, + * F_GETFD, F_SETFD, O_NONBLOCK - and no fcntl() function. PX4 uses + * these only to toggle non-blocking I/O, so we satisfy the interface + * and route the one semantically meaningful op (O_NONBLOCK) onto + * ioctlsocket(FIONBIO) / _setmode as appropriate. + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#if defined(__has_include) +# if __has_include(<../ucrt/fcntl.h>) +# include <../ucrt/fcntl.h> +# endif +#endif +#include +#include +#ifndef O_RDONLY +#define O_RDONLY 0x0000 +#endif +#ifndef O_WRONLY +#define O_WRONLY 0x0001 +#endif +#ifndef O_RDWR +#define O_RDWR 0x0002 +#endif +#ifndef O_APPEND +#define O_APPEND 0x0008 +#endif +#ifndef O_CREAT +#define O_CREAT 0x0100 +#endif +#ifndef O_TRUNC +#define O_TRUNC 0x0200 +#endif +#ifndef O_EXCL +#define O_EXCL 0x0400 +#endif +#ifndef O_TEXT +#define O_TEXT 0x4000 +#endif +#ifndef O_BINARY +#define O_BINARY 0x8000 +#endif +#else +#include_next +#endif + +#include +#include +#include +#include +#include +#include + +/* (pulled in transitively by ) leaks a handful + * of all-caps macros that collide with PX4 identifiers. Undefine them + * centrally here - this header sits at the top of posix.h's include + * chain, so every PX4 translation unit that touches winsock goes + * through these undefs before any C++ code uses the names. */ +/* wingdi.h defines ERROR as 0, winbase.h defines IGNORE as 0, winnt.h + * defines OPTIONAL as empty. All three clash with identifiers PX4 uses + * (ERROR=-1 from defines.h, FailureMode::IGNORE enumerator, and + * `OPTIONAL` function-argument annotations in third-party code). Strip + * the Windows leak, then - to cover the case where defines.h was + * already processed earlier in this TU - restore PX4's ERROR. */ +#ifdef ERROR +#undef ERROR +#endif +#if defined(__PX4_POSIX) +#define ERROR -1 +#endif +#ifdef IGNORE +#undef IGNORE +#endif +#ifdef OPTIONAL +#undef OPTIONAL +#endif + +#ifndef F_GETFD +#define F_GETFD 1 +#endif +#ifndef F_DUPFD +#define F_DUPFD 0 +#endif +#ifndef F_DUPFD_CLOEXEC +#define F_DUPFD_CLOEXEC 1030 +#endif +#ifndef F_SETFD +#define F_SETFD 2 +#endif +#ifndef F_GETFL +#define F_GETFL 3 +#endif +#ifndef F_SETFL +#define F_SETFL 4 +#endif +#ifndef F_GETLK +#define F_GETLK 5 +#endif +#ifndef F_SETLK +#define F_SETLK 6 +#endif +#ifndef F_SETLKW +#define F_SETLKW 7 +#endif +#ifndef F_RDLCK +#define F_RDLCK 0 +#endif +#ifndef F_WRLCK +#define F_WRLCK 1 +#endif +#ifndef F_UNLCK +#define F_UNLCK 2 +#endif +#ifndef FD_CLOEXEC +#define FD_CLOEXEC 1 +#endif +#ifndef AT_FDCWD +#define AT_FDCWD (-100) +#endif +#ifndef AT_SYMLINK_NOFOLLOW +#define AT_SYMLINK_NOFOLLOW 0x100 +#endif +#ifndef AT_REMOVEDIR +#define AT_REMOVEDIR 0x200 +#endif +#ifndef O_CLOEXEC +#define O_CLOEXEC 0 +#endif +#ifndef O_DIRECTORY +#define O_DIRECTORY 0 +#endif +#ifndef O_NOFOLLOW +#define O_NOFOLLOW 0 +#endif +#ifndef O_SYNC +#define O_SYNC 0 +#endif +#ifndef O_DSYNC +#define O_DSYNC O_SYNC +#endif + +/** + * @brief POSIX advisory byte-range lock descriptor. + * + * Windows has no fcntl-style locking; + * F_GETLK/F_SETLK are implemented below via LockFileEx/UnlockFileEx, + * which lock a byte range of a HANDLE. PX4 uses the "whole file" + * convention (l_start=0, l_len=0) for server singleton enforcement; + * that maps cleanly onto a max-range LockFileEx call. + */ +#ifndef _PX4_STRUCT_FLOCK_DEFINED +#define _PX4_STRUCT_FLOCK_DEFINED +struct flock { + short l_type; /* F_RDLCK / F_WRLCK / F_UNLCK */ + short l_whence; /* SEEK_SET / SEEK_CUR / SEEK_END */ + long l_start; + long l_len; /* 0 == lock through EOF */ + int l_pid; +}; +#endif + +/* O_NONBLOCK isn't defined by MinGW's fcntl.h. Pick a high bit that + * does not collide with the O_* flags MinGW does define. */ +#ifndef O_NONBLOCK +#if defined(_MSC_VER) && !defined(__clang__) +#define O_NONBLOCK 0x200000 +#else +#define O_NONBLOCK 0x4000 +#endif +#endif + +/* POSIX terminal-control flag; Windows has no controlling terminal + * concept, so we accept the flag and ignore it. */ +#ifndef O_NOCTTY +#define O_NOCTTY 0 +#endif + +#ifndef O_NDELAY +#define O_NDELAY O_NONBLOCK +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief POSIX fcntl() compatibility subset. + * + * Supported operations are the descriptor/status queries PX4 uses, + * FD_CLOEXEC acceptance, O_NONBLOCK enablement for Winsock sockets, and + * advisory file locking through LockFileEx. + * + * @return 0 or a positive query value on success, -1 with errno set on failure. + */ +static inline int fcntl(int fd, int cmd, ...) +{ + switch (cmd) { + case F_GETFL: + case F_GETFD: + /* No file-descriptor flag storage on Windows CRT fds. Pretend + * we got back 0 - PX4 only OR's O_NONBLOCK onto the result + * before handing it back via F_SETFL. */ + return 0; + + case F_SETFL: { + va_list ap; + va_start(ap, cmd); + int flags = va_arg(ap, int); + va_end(ap); + + if (flags & O_NONBLOCK) { + /* Socket fds live in a separate namespace on winsock - + * ioctlsocket works on SOCKET handles, which the CRT + * stores directly in its int fds via _open_osfhandle. */ + u_long mode = 1; + if (ioctlsocket((SOCKET)fd, FIONBIO, &mode) == 0) { + return 0; + } + errno = EBADF; + return -1; + } + return 0; + } + + case F_SETFD: + /* FD_CLOEXEC has no Win32 analog - close-on-exec is the + * default when bInheritHandle is false, which is the CRT + * default. Accept the call so code compiles. */ + return 0; + + case F_GETLK: + case F_SETLK: + case F_SETLKW: { + va_list ap; + va_start(ap, cmd); + struct flock *fl = va_arg(ap, struct flock *); + va_end(ap); + if (!fl) { errno = EINVAL; return -1; } + + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; } + + /* l_len == 0 means "lock through EOF" in POSIX. Use the max + * range LockFileEx accepts (2^63-1) so the byte-range lock + * effectively covers the whole file. */ + DWORD off_lo = (DWORD)fl->l_start; + DWORD off_hi = 0; + DWORD len_lo = fl->l_len ? (DWORD)fl->l_len : 0xFFFFFFFFu; + DWORD len_hi = fl->l_len ? 0 : 0x7FFFFFFFu; + OVERLAPPED ov; + memset(&ov, 0, sizeof(ov)); + ov.Offset = off_lo; + ov.OffsetHigh = off_hi; + + if (cmd == F_GETLK) { + /* Try to acquire non-blocking; if it succeeds no conflict + * exists, so release and report F_UNLCK. If it fails with + * ERROR_LOCK_VIOLATION something else holds a lock. */ + DWORD flags = LOCKFILE_EXCLUSIVE_LOCK | LOCKFILE_FAIL_IMMEDIATELY; + if (LockFileEx(h, flags, 0, len_lo, len_hi, &ov)) { + UnlockFileEx(h, 0, len_lo, len_hi, &ov); + fl->l_type = F_UNLCK; + } else if (GetLastError() == ERROR_LOCK_VIOLATION + || GetLastError() == ERROR_IO_PENDING) { + fl->l_type = F_WRLCK; + fl->l_pid = 0; + } else { + errno = EACCES; + return -1; + } + return 0; + } + + if (fl->l_type == F_UNLCK) { + if (UnlockFileEx(h, 0, len_lo, len_hi, &ov)) { return 0; } + errno = EACCES; + return -1; + } + + DWORD flags = LOCKFILE_EXCLUSIVE_LOCK; + if (cmd == F_SETLK) { flags |= LOCKFILE_FAIL_IMMEDIATELY; } + if (LockFileEx(h, flags, 0, len_lo, len_hi, &ov)) { return 0; } + errno = (GetLastError() == ERROR_LOCK_VIOLATION) ? EAGAIN : EACCES; + return -1; + } + + default: + errno = EINVAL; + return -1; + } +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/getopt.h b/platforms/posix/include/windows_shim/getopt.h new file mode 100644 index 0000000000..77d296f5c7 --- /dev/null +++ b/platforms/posix/include/windows_shim/getopt.h @@ -0,0 +1,184 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file getopt.h + * + * Minimal getopt/getopt_long shim for native Windows builds. + */ + +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define no_argument 0 +#define required_argument 1 +#define optional_argument 2 + +/** @brief getopt_long() option descriptor. */ +struct option { + const char *name; + int has_arg; + int *flag; + int val; +}; + +static char *optarg = NULL; +static int optind = 1; +static int opterr = 1; +static int optopt = 0; +static int px4_getopt_nextchar = 1; + +/** + * @brief Parse short options from argv. + * + * This is a compact implementation sufficient for PX4 command-line parsers in + * native MSVC builds. MinGW uses its system getopt implementation. + */ +static inline int getopt(int argc, char *const argv[], const char *optstring) +{ + optarg = NULL; + + if (optind >= argc || !argv[optind] || argv[optind][0] != '-' || argv[optind][1] == '\0') { + return -1; + } + + if (strcmp(argv[optind], "--") == 0) { + ++optind; + px4_getopt_nextchar = 1; + return -1; + } + + const char opt = argv[optind][px4_getopt_nextchar]; + const char *match = strchr(optstring, opt); + optopt = opt; + + if (!match) { + if (argv[optind][++px4_getopt_nextchar] == '\0') { + ++optind; + px4_getopt_nextchar = 1; + } + + return '?'; + } + + if (match[1] == ':') { + if (argv[optind][px4_getopt_nextchar + 1] != '\0') { + optarg = &argv[optind][px4_getopt_nextchar + 1]; + ++optind; + px4_getopt_nextchar = 1; + + } else if (optind + 1 < argc) { + optarg = argv[++optind]; + ++optind; + px4_getopt_nextchar = 1; + + } else { + ++optind; + px4_getopt_nextchar = 1; + return (optstring[0] == ':') ? ':' : '?'; + } + + } else { + if (argv[optind][++px4_getopt_nextchar] == '\0') { + ++optind; + px4_getopt_nextchar = 1; + } + } + + return opt; +} + +/** + * @brief Parse long and short options from argv. + * + * Supports exact long-option matches and required arguments. Optional long + * arguments are accepted as source-compatible descriptors but treated like + * no-argument options unless supplied with '='. + */ +static inline int getopt_long(int argc, char *const argv[], const char *optstring, + const struct option *longopts, int *longindex) +{ + if (optind < argc && argv[optind] && strncmp(argv[optind], "--", 2) == 0 && argv[optind][2] != '\0') { + const char *name = argv[optind] + 2; + const char *value = strchr(name, '='); + const size_t name_len = value ? (size_t)(value - name) : strlen(name); + + for (int i = 0; longopts && longopts[i].name; ++i) { + if (strncmp(name, longopts[i].name, name_len) == 0 && longopts[i].name[name_len] == '\0') { + if (longindex) { + *longindex = i; + } + + if (longopts[i].has_arg == required_argument) { + if (value) { + optarg = (char *)(value + 1); + + } else if (optind + 1 < argc) { + optarg = argv[++optind]; + + } else { + ++optind; + return '?'; + } + + } else { + optarg = NULL; + } + + ++optind; + + if (longopts[i].flag) { + *longopts[i].flag = longopts[i].val; + return 0; + } + + return longopts[i].val; + } + } + } + + return getopt(argc, argv, optstring); +} + +#ifdef __cplusplus +} +#endif + +#else +#include_next +#endif diff --git a/platforms/posix/include/windows_shim/grp.h b/platforms/posix/include/windows_shim/grp.h new file mode 100644 index 0000000000..3141d24d63 --- /dev/null +++ b/platforms/posix/include/windows_shim/grp.h @@ -0,0 +1,91 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file grp.h + * + * Windows has no /etc/group. Like pwd.h, this shim synthesizes a + * minimal current-user group entry so POSIX consumers can compile and + * basic lookups continue to work. + */ +#pragma once + +#include +#include + +#ifndef _PX4_UID_T_DEFINED_SHIM +#define _PX4_UID_T_DEFINED_SHIM +typedef unsigned int uid_t; +typedef unsigned int gid_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief POSIX group entry synthesized for the current Windows user. + */ +struct group { + char *gr_name; + char *gr_passwd; + gid_t gr_gid; + char **gr_mem; +}; + +/** @brief Return a static group entry for the requested GID when available. */ +struct group *getgrgid(gid_t gid); + +/** @brief Return a static group entry for the requested group name. */ +struct group *getgrnam(const char *name); + +/** + * @brief Reentrant GID lookup. + * + * @return 0 on success or not found; @p result is NULL when no entry matches. + * Returns an errno value when @p buf is too small or arguments are + * invalid. + */ +int getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result); + +/** + * @brief Reentrant group-name lookup. + * + * @return 0 on success or not found; @p result is NULL when no entry matches. + * Returns an errno value when @p buf is too small or arguments are + * invalid. + */ +int getgrnam_r(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/libgen.h b/platforms/posix/include/windows_shim/libgen.h new file mode 100644 index 0000000000..b4c9e985e9 --- /dev/null +++ b/platforms/posix/include/windows_shim/libgen.h @@ -0,0 +1,105 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file libgen.h + * + * basename()/dirname() helpers for Windows builds. + */ + +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Return the final path component in place. + * + * Accepts both POSIX '/' and Windows '\\' separators. The returned pointer + * aliases @p path or points to a static "." literal for empty paths. + */ +static inline char *basename(char *path) +{ + if (!path || !*path) { + return (char *)"."; + } + + char *last = path; + + for (char *p = path; *p; ++p) { + if (*p == '/' || *p == '\\') { + last = p + 1; + } + } + + return *last ? last : (char *)"."; +} + +/** + * @brief Replace the final path separator with NUL and return the directory. + * + * Accepts both POSIX '/' and Windows '\\' separators. The returned pointer + * aliases @p path or points to a static "." literal when no directory exists. + */ +static inline char *dirname(char *path) +{ + if (!path || !*path) { + return (char *)"."; + } + + char *last = NULL; + + for (char *p = path; *p; ++p) { + if (*p == '/' || *p == '\\') { + last = p; + } + } + + if (!last) { + return (char *)"."; + } + + if (last == path) { + last[1] = '\0'; + return path; + } + + *last = '\0'; + return path; +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/net/if.h b/platforms/posix/include/windows_shim/net/if.h new file mode 100644 index 0000000000..0f1c10d1af --- /dev/null +++ b/platforms/posix/include/windows_shim/net/if.h @@ -0,0 +1,243 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file net/if.h + * + * Windows exposes interface metadata through netioapi/iphlpapi rather than + * POSIX ioctls. This header keeps the public POSIX networking surface + * available: IF_* sizing, interface flags, ifreq/ifconf containers, + * if_nameindex, and the common SIOCGIF* request numbers used by callers as + * compile-time API. + */ +#pragma once + +#ifndef _WIN32 +# error "net/if.h shim only valid on Windows" +#endif + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include + +#ifndef IF_NAMESIZE +#define IF_NAMESIZE 256 +#endif + +#ifndef IFNAMSIZ +#define IFNAMSIZ IF_NAMESIZE +#endif +#ifndef IFHWADDRLEN +#define IFHWADDRLEN 6 +#endif +#ifndef MAX_IFINDEX +#define MAX_IFINDEX 256 +#endif + +/** @brief Hardware mapping payload carried by struct ifreq on POSIX. */ +struct ifmap { + unsigned long mem_start; + unsigned long mem_end; + unsigned short base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +/** + * @brief POSIX interface request container. + * + * POSIX exposes `struct ifreq` for SIOCGIFNETMASK / SIOCGIFADDR + * ioctls. Windows uses WSAIoctl with SIO_GET_INTERFACE_LIST instead. We + * still need the type to exist so MAVLink signatures parse - the UDP + * broadcast path (query_netmask_addr) is guarded with MAVLINK_UDP and + * stays wired on Windows even though the runtime implementation differs. + */ +struct ifreq { + char ifr_name[IF_NAMESIZE]; + union { + struct sockaddr ifr_addr; + struct sockaddr ifr_dstaddr; + struct sockaddr ifr_broadaddr; + struct sockaddr ifr_netmask; + struct sockaddr ifr_hwaddr; + short ifr_flags; + int ifr_ifindex; + int ifr_metric; + int ifr_mtu; + struct ifmap ifr_map; + char ifr_slave[IF_NAMESIZE]; + char ifr_newname[IF_NAMESIZE]; + char *ifr_data; + } ifr_ifru; +}; + +#define ifr_addr ifr_ifru.ifr_addr +#define ifr_dstaddr ifr_ifru.ifr_dstaddr +#define ifr_broadaddr ifr_ifru.ifr_broadaddr +#define ifr_netmask ifr_ifru.ifr_netmask +#define ifr_hwaddr ifr_ifru.ifr_hwaddr +#define ifr_flags ifr_ifru.ifr_flags +#define ifr_ifindex ifr_ifru.ifr_ifindex +#define ifr_metric ifr_ifru.ifr_metric +#define ifr_mtu ifr_ifru.ifr_mtu +#define ifr_map ifr_ifru.ifr_map +#define ifr_newname ifr_ifru.ifr_newname +#define ifr_data ifr_ifru.ifr_data + +#ifndef IFF_UP +#define IFF_UP 0x1 +#endif +#ifndef IFF_DEBUG +#define IFF_DEBUG 0x4 +#endif +#ifndef IFF_BROADCAST +#define IFF_BROADCAST 0x2 +#endif +#ifndef IFF_LOOPBACK +#define IFF_LOOPBACK 0x8 +#endif +#ifndef IFF_POINTOPOINT +#define IFF_POINTOPOINT 0x10 +#endif +#ifndef IFF_NOTRAILERS +#define IFF_NOTRAILERS 0x20 +#endif +#ifndef IFF_RUNNING +#define IFF_RUNNING 0x40 +#endif +#ifndef IFF_NOARP +#define IFF_NOARP 0x80 +#endif +#ifndef IFF_PROMISC +#define IFF_PROMISC 0x100 +#endif +#ifndef IFF_ALLMULTI +#define IFF_ALLMULTI 0x200 +#endif +#ifndef IFF_MASTER +#define IFF_MASTER 0x400 +#endif +#ifndef IFF_SLAVE +#define IFF_SLAVE 0x800 +#endif +#ifndef IFF_MULTICAST +#define IFF_MULTICAST 0x1000 +#endif +#ifndef IFF_PORTSEL +#define IFF_PORTSEL 0x2000 +#endif +#ifndef IFF_AUTOMEDIA +#define IFF_AUTOMEDIA 0x4000 +#endif +#ifndef IFF_DYNAMIC +#define IFF_DYNAMIC 0x8000 +#endif + +#define IFF_IS_UP(f) (((f) & IFF_UP) != 0) +#define IFF_IS_RUNNING(f) (((f) & IFF_RUNNING) != 0) +#define IFF_IS_LOOPBACK(f) (((f) & IFF_LOOPBACK) != 0) +#define IFF_IS_POINTOPOINT(f) (((f) & IFF_POINTOPOINT) != 0) +#define IFF_IS_NOARP(f) (((f) & IFF_NOARP) != 0) +#define IFF_IS_MULTICAST(f) (((f) & IFF_MULTICAST) != 0) +#define IFF_IS_BROADCAST(f) (((f) & IFF_BROADCAST) != 0) + +#ifndef SIOCGIFCONF +#define SIOCGIFCONF 0x8912 +#define SIOCGIFFLAGS 0x8913 +#define SIOCSIFFLAGS 0x8914 +#define SIOCGIFADDR 0x8915 +#define SIOCSIFADDR 0x8916 +#define SIOCGIFDSTADDR 0x8917 +#define SIOCSIFDSTADDR 0x8918 +#define SIOCGIFBRDADDR 0x8919 +#define SIOCSIFBRDADDR 0x891A +#define SIOCGIFNETMASK 0x891b +#define SIOCSIFNETMASK 0x891c +#define SIOCGIFMETRIC 0x891d +#define SIOCSIFMETRIC 0x891e +#define SIOCGIFMTU 0x8921 +#define SIOCSIFMTU 0x8922 +#define SIOCGIFHWADDR 0x8927 +#define SIOCGIFINDEX 0x8933 +#endif + +/** + * @brief POSIX variable-length interface request array for SIOCGIFCONF. + * + * Windows has no direct equivalent (interface enumeration goes through + * GetAdaptersAddresses instead). MAVLink uses ifconf only as a + * type container when walking broadcast addresses - the runtime + * iteration path is stubbed by the shim's ioctl/SIOCGIFCONF handler. + */ +struct ifconf { + int ifc_len; + union { + char *ifc_buf; + struct ifreq *ifc_req; + } ifc_ifcu; +}; +#define ifc_buf ifc_ifcu.ifc_buf +#define ifc_req ifc_ifcu.ifc_req + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Interface index/name pair returned by if_nameindex(). */ +struct if_nameindex { + unsigned int if_index; + char *if_name; +}; + +/* `if_nametoindex` and `if_indextoname` are already declared (with + * NET_IFINDEX / PCSTR / PCHAR) by MinGW's . Don't redeclare them here - the + * signatures don't match byte-for-byte (NET_IFINDEX vs. unsigned int) + * and GCC flags the conflict as an error. */ + +/** + * @brief Return a NULL-terminated array of interface index/name pairs. + * + * The implementation queries GetAdaptersAddresses and converts names into the + * POSIX ownership model. Free the returned array with if_freenameindex(). + */ +struct if_nameindex *if_nameindex(void); + +/** @brief Free an array returned by if_nameindex(). */ +void if_freenameindex(struct if_nameindex *ptr); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/netdb.h b/platforms/posix/include/windows_shim/netdb.h new file mode 100644 index 0000000000..4a1208a4e1 --- /dev/null +++ b/platforms/posix/include/windows_shim/netdb.h @@ -0,0 +1,166 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file netdb.h + * + * WinSock2 provides getaddrinfo/freeaddrinfo/gethostbyname and the + * struct addrinfo / hostent types, but it ships them through + * and . Forward to those so the POSIX + * includes work unchanged, and fill in the legacy BSD pieces + * that Windows omits (`netent`, `hstrerror`, `getnet*`, and the old + * set/end host/service/protocol database walkers). + */ +#pragma once + +#ifndef _WIN32 +# error "netdb.h shim only valid on Windows" +#endif + +#include + +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN +#endif +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef HOST_NOT_FOUND +#define HOST_NOT_FOUND 11001 +#endif +#ifndef TRY_AGAIN +#define TRY_AGAIN 11002 +#endif +#ifndef NO_RECOVERY +#define NO_RECOVERY 11003 +#endif +#ifndef NO_DATA +#define NO_DATA 11004 +#endif +#ifndef NO_ADDRESS +#define NO_ADDRESS NO_DATA +#endif + +#ifndef NI_MAXHOST +#define NI_MAXHOST 1025 +#endif +#ifndef NI_MAXSERV +#define NI_MAXSERV 32 +#endif + +#ifndef EAI_SYSTEM +#define EAI_SYSTEM 11 +#endif +#ifndef EAI_OVERFLOW +#define EAI_OVERFLOW WSAEFAULT +#endif + +#ifndef NI_NUMERICSCOPE +#define NI_NUMERICSCOPE 0x20 +#endif + +#ifndef IPPORT_RESERVED +#define IPPORT_RESERVED 1024 +#endif + +#ifndef h_errno +#define h_errno WSAGetLastError() +#endif + +/** + * @brief Return human-readable text for legacy resolver errors. + * + * @param err HOST_NOT_FOUND, TRY_AGAIN, NO_RECOVERY, NO_DATA, or a Winsock + * resolver code. + */ +const char *hstrerror(int err); + +/** @name Host database compatibility functions + * + * Windows does not expose the old sequential /etc/hosts database API. These + * functions are provided so POSIX-oriented code links; gethostent() returns + * NULL when no synthetic entry is available. + * + * @{ + */ +void sethostent(int stay_open); +void endhostent(void); +struct hostent *gethostent(void); +/** @} */ + +/** @name Network database compatibility functions + * + * The legacy netent API has no Windows equivalent. The implementation returns + * empty results while preserving source and link compatibility. + * + * @{ + */ +void setnetent(int stay_open); +void endnetent(void); +struct netent *getnetent(void); +struct netent *getnetbyname(const char *name); +struct netent *getnetbyaddr(uint32_t net, int type); +/** @} */ + +/** @name Protocol database compatibility functions + * + * Protocol lookup by explicit name/number is handled by Winsock where + * available. Sequential database walking is a no-op compatibility surface. + * + * @{ + */ +void setprotoent(int stay_open); +void endprotoent(void); +struct protoent *getprotoent(void); +/** @} */ + +/** @name Service database compatibility functions + * + * Sequential service database walking is kept as a stub for POSIX source + * compatibility. + * + * @{ + */ +void setservent(int stay_open); +void endservent(void); +struct servent *getservent(void); +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/netinet/in.h b/platforms/posix/include/windows_shim/netinet/in.h new file mode 100644 index 0000000000..f0e7631a2a --- /dev/null +++ b/platforms/posix/include/windows_shim/netinet/in.h @@ -0,0 +1,93 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file netinet/in.h + * + * Re-export Winsock's IPv4/IPv6 types and normalize the remaining POSIX + * networking surface: fixed-width in_addr_t / in_port_t, POSIX-sized + * INET_ADDRSTRLEN / INET6_ADDRSTRLEN, multicast constants, and a few + * helper aliases that Unix-oriented code expects. + */ +#pragma once + +#ifndef _WIN32 +# error "netinet/in.h shim only valid on Windows" +#endif + +#include + +#include +#include +#include + +#ifndef INADDR_NONE +#define INADDR_NONE ((in_addr_t)0xffffffffUL) +#endif +#ifndef IPPROTO_UDP +# define IPPROTO_UDP 17 +#endif +#ifndef IPPROTO_TCP +# define IPPROTO_TCP 6 +#endif + +#ifndef _PX4_IN_ADDR_T_DEFINED_SHIM +#define _PX4_IN_ADDR_T_DEFINED_SHIM +/** @brief IPv4 address integer type used by POSIX networking APIs. */ +typedef uint32_t in_addr_t; +#endif +#ifndef _PX4_IN_PORT_T_DEFINED_SHIM +#define _PX4_IN_PORT_T_DEFINED_SHIM +/** @brief TCP/UDP port integer type used by POSIX networking APIs. */ +typedef uint16_t in_port_t; +#endif + +#ifdef INET_ADDRSTRLEN +#undef INET_ADDRSTRLEN +#endif +#define INET_ADDRSTRLEN 16 + +#ifdef INET6_ADDRSTRLEN +#undef INET6_ADDRSTRLEN +#endif +#define INET6_ADDRSTRLEN 46 + +#ifndef MCAST_INCLUDE +#define MCAST_INCLUDE 1 +#endif +#ifndef MCAST_EXCLUDE +#define MCAST_EXCLUDE 0 +#endif + +#ifndef IN6_ARE_ADDR_EQUAL +#define IN6_ARE_ADDR_EQUAL(a, b) IN6_ADDR_EQUAL((a), (b)) +#endif diff --git a/platforms/posix/include/windows_shim/netinet/tcp.h b/platforms/posix/include/windows_shim/netinet/tcp.h new file mode 100644 index 0000000000..47d30c042c --- /dev/null +++ b/platforms/posix/include/windows_shim/netinet/tcp.h @@ -0,0 +1,72 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file netinet/tcp.h + * + * Export the standard TCP-level option names. Where Winsock has no symbolic + * equivalent, keep the POSIX macro available as a compile-time compatibility + * constant; runtime support still depends on the underlying Windows stack. + */ +#pragma once + +#ifndef _WIN32 +# error "netinet/tcp.h shim only valid on Windows" +#endif + +#include +#include +#include + +/* winsock already defines TCP_NODELAY. */ +#ifndef SOL_TCP +#define SOL_TCP IPPROTO_TCP +#endif +#ifndef TCP_KEEPIDLE +#ifdef TCP_KEEPALIVE +#define TCP_KEEPIDLE TCP_KEEPALIVE +#else +#define TCP_KEEPIDLE 3 +#endif +#endif +#ifndef TCP_KEEPINTVL +#define TCP_KEEPINTVL 17 +#endif +#ifndef TCP_KEEPCNT +#define TCP_KEEPCNT 16 +#endif +#ifndef TCP_MAXSEG +#define TCP_MAXSEG 4 +#endif +#ifndef TCP_USER_TIMEOUT +#define TCP_USER_TIMEOUT 18 +#endif diff --git a/platforms/posix/include/windows_shim/netinet/udp.h b/platforms/posix/include/windows_shim/netinet/udp.h new file mode 100644 index 0000000000..3d2c355bac --- /dev/null +++ b/platforms/posix/include/windows_shim/netinet/udp.h @@ -0,0 +1,49 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file netinet/udp.h + * + * POSIX mainly uses this header for the UDP protocol-level socket constant. + * Winsock exposes UDP through IPPROTO_UDP, so re-export that shape here. + */ +#pragma once + +#ifndef _WIN32 +# error "netinet/udp.h shim only valid on Windows" +#endif + +#include + +#ifndef SOL_UDP +#define SOL_UDP IPPROTO_UDP +#endif diff --git a/platforms/posix/include/windows_shim/poll.h b/platforms/posix/include/windows_shim/poll.h new file mode 100644 index 0000000000..1e0954648f --- /dev/null +++ b/platforms/posix/include/windows_shim/poll.h @@ -0,0 +1,87 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file poll.h + * + * Maps POSIX poll() onto Win32 WSAPoll(). WSAPoll has the same struct + * layout and event flags as POSIX poll; it has been available since + * Vista but only works on sockets, which is exactly how PX4 uses it + * (px4_daemon socket pair, muorb sockets, mavlink). + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* winsock2 already defines pollfd / WSAPOLLFD and the event bits when + * _WIN32_WINNT >= 0x0600 (Vista). Re-export as the POSIX-style names. */ + +#ifndef POLLRDNORM +#define POLLRDNORM POLLIN +#endif +#ifndef POLLRDBAND +#define POLLRDBAND 0x0080 +#endif +#ifndef POLLWRNORM +#define POLLWRNORM POLLOUT +#endif +#ifndef POLLWRBAND +#define POLLWRBAND 0x0100 +#endif +#ifndef POLLMSG +#define POLLMSG 0x0400 +#endif +#ifndef POLLREMOVE +#define POLLREMOVE 0x1000 +#endif + +typedef struct pollfd POLLFD; /* alias for readability */ + +typedef unsigned long nfds_t; + +/** + * @brief Poll socket descriptors using WSAPoll(). + * + * @return Number of ready descriptors, 0 on timeout, or SOCKET_ERROR. + */ +static inline int poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + return WSAPoll(fds, (ULONG)nfds, timeout); +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/pthread.h b/platforms/posix/include/windows_shim/pthread.h new file mode 100644 index 0000000000..5eb1a6e9ac --- /dev/null +++ b/platforms/posix/include/windows_shim/pthread.h @@ -0,0 +1,246 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file pthread.h + * + * MinGW-w64 links winpthreads which ships a full , but it + * omits a handful of POSIX macros that PX4 uses (PTHREAD_STACK_MIN). We + * forward to the real header via #include_next and add the missing + * pieces with values consistent with how Windows actually behaves + * (allocation granularity = 64 KiB, pthread_attr_setstacksize rounds + * up to it anyway). + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Thread handle stored as an integer-sized Windows HANDLE value. */ +typedef uintptr_t pthread_t; + +/** @brief Thread-local storage key backed by TlsAlloc/TlsFree. */ +typedef DWORD pthread_key_t; + +/** + * @brief Thread creation attributes understood by the MSVC pthread shim. + * + * Scheduling fields are stored for POSIX API round-tripping; Windows thread + * creation only consumes detach state and stack size. + */ +typedef struct pthread_attr_t { + void *stack_addr; + size_t stack_size; + int detach_state; + int sched_policy; + int inherit_sched; + int scope; + struct sched_param sched; +} pthread_attr_t; + +/** @brief Mutex attribute object; currently carries only the mutex type. */ +typedef struct pthread_mutexattr_t { + int type; +} pthread_mutexattr_t; + +/** @brief Condition-variable attribute object; stores the requested clock. */ +typedef struct pthread_condattr_t { + int clock_id; +} pthread_condattr_t; + +/** + * @brief Mutex object backed by a lazily initialized CRITICAL_SECTION. + * + * The INIT_ONCE field lets static PTHREAD_MUTEX_INITIALIZER instances be + * initialized on first lock without requiring a constructor. + */ +typedef struct pthread_mutex_t { + INIT_ONCE once; + CRITICAL_SECTION critical_section; + int type; +} pthread_mutex_t; + +/** @brief Condition variable object backed directly by CONDITION_VARIABLE. */ +typedef CONDITION_VARIABLE pthread_cond_t; + +/** @brief One-time initialization guard backed by INIT_ONCE. */ +typedef INIT_ONCE pthread_once_t; + +#define PTHREAD_MUTEX_INITIALIZER { INIT_ONCE_STATIC_INIT, { 0 }, 0 } +#define PTHREAD_COND_INITIALIZER CONDITION_VARIABLE_INIT +#define PTHREAD_ONCE_INIT INIT_ONCE_STATIC_INIT + +#define PTHREAD_MUTEX_NORMAL 0 +#define PTHREAD_MUTEX_RECURSIVE 1 +#define PTHREAD_PROCESS_PRIVATE 0 +#define PTHREAD_CREATE_JOINABLE 0 +#define PTHREAD_CREATE_DETACHED 1 +#define PTHREAD_INHERIT_SCHED 0 +#define PTHREAD_EXPLICIT_SCHED 1 +#define PTHREAD_SCOPE_SYSTEM 0 +#define PTHREAD_SCOPE_PROCESS 1 + +/** @name Thread attribute functions + * + * Store POSIX pthread attributes for later pthread_create() calls. Unsupported + * scheduling/scope attributes are retained for round-trip queries and ignored + * by Windows thread creation. + * + * @{ + */ +int pthread_attr_init(pthread_attr_t *attr); +int pthread_attr_destroy(pthread_attr_t *attr); +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size); +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stack_size); +int pthread_attr_setstack(pthread_attr_t *attr, void *stack_addr, size_t stack_size); +int pthread_attr_getstack(const pthread_attr_t *attr, void **stack_addr, size_t *stack_size); +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param); +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param); +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy); +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy); +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched); +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched); +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate); +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope); +int pthread_attr_setscope(pthread_attr_t *attr, int scope); +/** @} */ + +/** @name Mutex functions + * + * Implement POSIX mutex operations on top of CRITICAL_SECTION. Recursive + * mutexes are accepted through pthread_mutexattr_settype(). + * + * @{ + */ +int pthread_mutexattr_init(pthread_mutexattr_t *attr); +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr); +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type); +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); +int pthread_mutex_destroy(pthread_mutex_t *mutex); +int pthread_mutex_lock(pthread_mutex_t *mutex); +int pthread_mutex_trylock(pthread_mutex_t *mutex); +int pthread_mutex_unlock(pthread_mutex_t *mutex); +/** @} */ + +/** @name Condition-variable functions + * + * Implement pthread condition variables with SleepConditionVariableCS and + * WakeConditionVariable/WakeAllConditionVariable. + * + * @{ + */ +int pthread_condattr_init(pthread_condattr_t *attr); +int pthread_condattr_destroy(pthread_condattr_t *attr); +int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id); +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr); +int pthread_cond_destroy(pthread_cond_t *cond); +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex); +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime); +int pthread_cond_signal(pthread_cond_t *cond); +int pthread_cond_broadcast(pthread_cond_t *cond); +/** @} */ + +/** @name Thread lifecycle functions + * + * Wrap CreateThread/WaitForSingleObject/CloseHandle with pthread-compatible + * ownership and return codes. + * + * @{ + */ +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg); +int pthread_join(pthread_t thread, void **value_ptr); +int pthread_detach(pthread_t thread); +void pthread_exit(void *value_ptr); +pthread_t pthread_self(void); +int pthread_equal(pthread_t t1, pthread_t t2); +int pthread_cancel(pthread_t thread); +int pthread_kill(pthread_t thread, int sig); +/** @} */ + +/** @name Thread-local storage functions + * + * Back POSIX pthread keys with the Windows TLS API. + * + * @{ + */ +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)); +int pthread_key_delete(pthread_key_t key); +int pthread_setspecific(pthread_key_t key, const void *value); +void *pthread_getspecific(pthread_key_t key); +/** @} */ + +/** @brief Run an initialization routine exactly once. */ +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)); + +/** + * @brief Name another thread for debuggers. + * + * @param thread Thread returned by pthread_create() or pthread_self(). + * @param name UTF-8 thread name. + */ +int px4_pthread_setname_np(pthread_t thread, const char *name); + +/** + * @brief Name the current thread for debuggers. + * + * pthread_setname_np has one-argument and two-argument variants across POSIX + * platforms, so the macro below dispatches to this helper when only a name is + * supplied. + */ +int px4_pthread_setname_current_np(const char *name); + +/** @brief Read a thread name previously set through the shim when available. */ +int pthread_getname_np(pthread_t thread, char *name, size_t len); + +#define PX4_PTHREAD_SETNAME_SELECT(_1, _2, NAME, ...) NAME +#define pthread_setname_np(...) PX4_PTHREAD_SETNAME_SELECT(__VA_ARGS__, px4_pthread_setname_np, px4_pthread_setname_current_np)(__VA_ARGS__) + +#ifdef __cplusplus +} +#endif + +#else +#include_next +#endif + +#ifndef PTHREAD_STACK_MIN +#define PTHREAD_STACK_MIN 16384 +#endif diff --git a/platforms/posix/include/windows_shim/pwd.h b/platforms/posix/include/windows_shim/pwd.h new file mode 100644 index 0000000000..2a0129014c --- /dev/null +++ b/platforms/posix/include/windows_shim/pwd.h @@ -0,0 +1,95 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file pwd.h + * + * Windows has no /etc/passwd. SITL consumers only ever want the current + * user's home directory or name - we synthesise a passwd entry from + * %USERPROFILE% / GetUserName to keep them building and (mostly) working. + */ +#pragma once + +#include +#include + +/* MinGW's sys/types.h does not define uid_t/gid_t. */ +#ifndef _PX4_UID_T_DEFINED_SHIM +#define _PX4_UID_T_DEFINED_SHIM +typedef unsigned int uid_t; +typedef unsigned int gid_t; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief POSIX passwd entry synthesized from the current Windows user. + */ +struct passwd { + char *pw_name; + char *pw_passwd; + uid_t pw_uid; + gid_t pw_gid; + char *pw_gecos; + char *pw_dir; + char *pw_shell; +}; + +/** @brief Return a static passwd entry for the requested UID when available. */ +struct passwd *getpwuid(uid_t uid); + +/** @brief Return a static passwd entry for the requested user name. */ +struct passwd *getpwnam(const char *name); + +/** + * @brief Reentrant UID lookup. + * + * @return 0 on success or not found; @p result is NULL when no entry matches. + * Returns an errno value when @p buf is too small or arguments are + * invalid. + */ +int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); + +/** + * @brief Reentrant name lookup. + * + * @return 0 on success or not found; @p result is NULL when no entry matches. + * Returns an errno value when @p buf is too small or arguments are + * invalid. + */ +int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/sched.h b/platforms/posix/include/windows_shim/sched.h new file mode 100644 index 0000000000..cb0b745bb0 --- /dev/null +++ b/platforms/posix/include/windows_shim/sched.h @@ -0,0 +1,90 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sched.h + * + * POSIX scheduler surface for native Windows builds. + */ + +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) + +#include + +#ifndef SCHED_OTHER +#define SCHED_OTHER 0 +#endif +#ifndef SCHED_FIFO +#define SCHED_FIFO 1 +#endif +#ifndef SCHED_RR +#define SCHED_RR 2 +#endif + +struct sched_param { + int sched_priority; +}; + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Return the highest priority value accepted by the shim. */ +static inline int sched_get_priority_max(int policy) +{ + (void)policy; + return 15; +} + +/** @brief Return the lowest priority value accepted by the shim. */ +static inline int sched_get_priority_min(int policy) +{ + (void)policy; + return -15; +} + +/** @brief Yield the current Windows thread's remaining time slice. */ +static inline int sched_yield(void) +{ + Sleep(0); + return 0; +} + +#ifdef __cplusplus +} +#endif + +#else +#include_next +#endif diff --git a/platforms/posix/include/windows_shim/semaphore.h b/platforms/posix/include/windows_shim/semaphore.h new file mode 100644 index 0000000000..4a021247c2 --- /dev/null +++ b/platforms/posix/include/windows_shim/semaphore.h @@ -0,0 +1,50 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file semaphore.h + * + * Native Windows placeholder for POSIX semaphore headers. PX4 POSIX builds + * use px4_sem_t on Windows, so sem_t only needs to exist for code that + * includes without directly using POSIX sem_* calls. + */ + +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) + +/** @brief Placeholder POSIX semaphore type for code that only needs the name. */ +typedef void *sem_t; + +#else +#include_next +#endif diff --git a/platforms/posix/include/windows_shim/signal.h b/platforms/posix/include/windows_shim/signal.h new file mode 100644 index 0000000000..3844ede82e --- /dev/null +++ b/platforms/posix/include/windows_shim/signal.h @@ -0,0 +1,176 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file signal.h + * + * Re-exports the MinGW signal.h and adds the POSIX signal numbers that PX4 + * code references but MinGW does not define (SIGCONT, SIGUSR1, etc.). + * These values never reach the Win32 kernel - the PX4 runtime intercepts + * them in px4_task_kill() and treats them as thread wake-up hints + * (implemented via pthread_kill / no-op, since winpthreads can't signal + * a thread with an arbitrary value). + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#if defined(__has_include) +# if __has_include(<../ucrt/signal.h>) +# include <../ucrt/signal.h> +# endif +#endif +/** @brief Function-pointer type used by the MSVC signal() declaration. */ +typedef void (*__px4_signal_handler_t)(int); +#ifndef SIG_ERR +#define SIG_ERR ((__px4_signal_handler_t)-1) +#endif +#ifndef SIG_DFL +#define SIG_DFL ((__px4_signal_handler_t)0) +#endif +#ifndef SIG_IGN +#define SIG_IGN ((__px4_signal_handler_t)1) +#endif +#ifndef SIGINT +#define SIGINT 2 +#endif +#ifndef SIGILL +#define SIGILL 4 +#endif +#ifndef SIGABRT +#define SIGABRT 22 +#endif +#ifndef SIGFPE +#define SIGFPE 8 +#endif +#ifndef SIGSEGV +#define SIGSEGV 11 +#endif +#ifndef SIGTERM +#define SIGTERM 15 +#endif +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Register a C runtime signal handler. */ +__px4_signal_handler_t signal(int sig, __px4_signal_handler_t func); + +/** @brief Raise a C runtime signal in the current process. */ +int raise(int sig); +#ifdef __cplusplus +} +#endif +#else +#include_next +#endif + +#ifndef SIGHUP +#define SIGHUP 1 +#endif +#ifndef SIGQUIT +#define SIGQUIT 3 +#endif +#ifndef SIGKILL +#define SIGKILL 9 +#endif +#ifndef SIGUSR1 +#define SIGUSR1 10 +#endif +#ifndef SIGUSR2 +#define SIGUSR2 12 +#endif +#ifndef SIGPIPE +#define SIGPIPE 13 +#endif +#ifndef SIGBUS +#define SIGBUS 7 +#endif +#ifndef SIGTRAP +#define SIGTRAP 5 +#endif +#ifndef SIGALRM +#define SIGALRM 14 +#endif +#ifndef SIGCHLD +#define SIGCHLD 17 +#endif +#ifndef SIGCONT +#define SIGCONT 18 +#endif +#ifndef SIGSTOP +#define SIGSTOP 19 +#endif +#ifndef SIGTSTP +#define SIGTSTP 20 +#endif +#ifndef SIGTTIN +#define SIGTTIN 21 +#endif +#ifndef SIGTTOU +#define SIGTTOU 22 +#endif +#ifndef SIGWINCH +#define SIGWINCH 28 +#endif +#ifndef SIGPOLL +#define SIGPOLL 29 +#endif +#ifndef SIG_BLOCK +#define SIG_BLOCK 0 +#endif +#ifndef SIG_UNBLOCK +#define SIG_UNBLOCK 1 +#endif +#ifndef SIG_SETMASK +#define SIG_SETMASK 2 +#endif +#ifndef SA_RESTART +#define SA_RESTART 0x10000000 +#endif +#ifndef SA_NOCLDSTOP +#define SA_NOCLDSTOP 0x00000001 +#endif +#ifndef SA_NOCLDWAIT +#define SA_NOCLDWAIT 0x00000002 +#endif +#ifndef SA_NODEFER +#define SA_NODEFER 0x40000000 +#endif +#ifndef SA_RESETHAND +#define SA_RESETHAND 0x80000000 +#endif +#ifndef SA_SIGINFO +#define SA_SIGINFO 4 +#endif +#ifndef NSIG +#define NSIG 32 +#endif diff --git a/platforms/posix/include/windows_shim/string.h b/platforms/posix/include/windows_shim/string.h new file mode 100644 index 0000000000..693ac1e7c5 --- /dev/null +++ b/platforms/posix/include/windows_shim/string.h @@ -0,0 +1,67 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file string.h + * + * MSVC CRT names a few POSIX string helpers with leading underscores. + */ + +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#if defined(__has_include) +# if __has_include(<../ucrt/string.h>) +# include <../ucrt/string.h> +# else +# include +# endif +#else +#include +#endif +#include + +#ifndef strdup +#define strdup _strdup +#endif +#ifndef strcasecmp +#define strcasecmp _stricmp +#endif +#ifndef strncasecmp +#define strncasecmp _strnicmp +#endif +#ifndef bzero +#define bzero(ptr, len) memset((ptr), 0, (len)) +#endif +#else +#include_next +#endif diff --git a/platforms/posix/include/windows_shim/strings.h b/platforms/posix/include/windows_shim/strings.h new file mode 100644 index 0000000000..76e9dd997e --- /dev/null +++ b/platforms/posix/include/windows_shim/strings.h @@ -0,0 +1,41 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file strings.h + * + * BSD/POSIX string helper names for Windows builds. + */ + +#pragma once + +#include diff --git a/platforms/posix/include/windows_shim/sys/file.h b/platforms/posix/include/windows_shim/sys/file.h new file mode 100644 index 0000000000..db075c6815 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/file.h @@ -0,0 +1,62 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/file.h + * + * PX4's main.cpp uses flock(fd, LOCK_EX|LOCK_NB) on a lock file + * (/tmp/px4_lock) to detect and reject a second running instance. + * On Windows we translate this to LockFileEx on the file's HANDLE, + * backed by _get_osfhandle(fd). + */ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#define LOCK_SH 1 +#define LOCK_EX 2 +#define LOCK_NB 4 +#define LOCK_UN 8 + +/** + * @brief Apply or release an advisory lock on a CRT file descriptor. + * + * Used by PX4's singleton lock. The Windows implementation maps exclusive + * locks to LockFileEx on the descriptor's underlying HANDLE. + */ +int flock(int fd, int operation); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/sys/ioctl.h b/platforms/posix/include/windows_shim/sys/ioctl.h new file mode 100644 index 0000000000..3f6e314c88 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/ioctl.h @@ -0,0 +1,118 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/ioctl.h + * + * SITL does not actually drive ioctl'd character devices on Windows - + * there is no /dev/tty or /dev/mem. Callers include + * for FIONREAD/FIONBIO/TIOCM* constants. We expose those symbols, + * route FIONREAD/FIONBIO through winsock's ioctlsocket (which uses the + * same FIONREAD/FIONBIO values), and return -ENOSYS for anything else. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef FIONREAD +# define FIONREAD 0x4004667F +#endif +#ifndef FIONBIO +# define FIONBIO 0x8004667E +#endif +#ifndef TIOCINQ +# define TIOCINQ FIONREAD +#endif +#ifndef TIOCOUTQ +# define TIOCOUTQ 0x5411 +#endif +#ifndef FIOCLEX +# define FIOCLEX 0x5451 +#endif +#ifndef FIONCLEX +# define FIONCLEX 0x5450 +#endif +#ifndef TIOCEXCL +# define TIOCEXCL 0x540C +#endif +#ifndef TIOCNXCL +# define TIOCNXCL 0x540D +#endif +#ifndef TIOCSCTTY +# define TIOCSCTTY 0x540E +#endif +#ifndef TIOCMGET +# define TIOCMGET 0x5415 +#endif +#ifndef TIOCMSET +# define TIOCMSET 0x5418 +#endif +#ifndef TIOCM_DTR +# define TIOCM_DTR 0x002 +#endif +#ifndef TIOCM_RTS +# define TIOCM_RTS 0x004 +#endif +#ifndef TIOCSBRK +# define TIOCSBRK 0x5427 +#endif +#ifndef TIOCCBRK +# define TIOCCBRK 0x5428 +#endif +#ifndef TCGETS +# define TCGETS 0x5401 +#endif +#ifndef TCSETS +# define TCSETS 0x5402 +#endif +#ifndef TCSETSW +# define TCSETSW 0x5403 +#endif +#ifndef TCSETSF +# define TCSETSF 0x5404 +#endif + +/** + * @brief POSIX ioctl() compatibility subset for sockets and terminal constants. + * + * FIONREAD/FIONBIO are forwarded to ioctlsocket(). Unsupported requests fail + * with ENOSYS so callers can follow their existing POSIX fallback path. + */ +int ioctl(int fd, unsigned long request, ...); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/sys/mman.h b/platforms/posix/include/windows_shim/sys/mman.h new file mode 100644 index 0000000000..0ecdf42d80 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/mman.h @@ -0,0 +1,140 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/mman.h + * + * Covers the mmap surface SITL uses: anonymous memory for parameter + * backing stores / shared memory, and file-backed memory for logs and + * dataman. Backed by VirtualAlloc (anonymous) and CreateFileMapping + + * MapViewOfFile (file-backed). mlock/mlockall are no-ops - Windows + * has VirtualLock but requires the SeLockMemoryPrivilege which SITL + * does not need. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define PROT_NONE 0x0 +#define PROT_READ 0x1 +#define PROT_WRITE 0x2 +#define PROT_EXEC 0x4 + +#define MAP_SHARED 0x01 +#define MAP_PRIVATE 0x02 +#define MAP_FIXED 0x10 +#define MAP_ANON 0x20 +#define MAP_ANONYMOUS MAP_ANON +#define MAP_NORESERVE 0x4000 +#define MAP_POPULATE 0x8000 +#define MAP_LOCKED 0x2000 + +#define MAP_FAILED ((void *) -1) + +#define MS_ASYNC 1 +#define MS_SYNC 4 +#define MS_INVALIDATE 2 + +#define MCL_CURRENT 1 +#define MCL_FUTURE 2 + +#define MADV_NORMAL 0 +#define MADV_RANDOM 1 +#define MADV_SEQUENTIAL 2 +#define MADV_WILLNEED 3 +#define MADV_DONTNEED 4 + +#define POSIX_MADV_NORMAL MADV_NORMAL +#define POSIX_MADV_RANDOM MADV_RANDOM +#define POSIX_MADV_SEQUENTIAL MADV_SEQUENTIAL +#define POSIX_MADV_WILLNEED MADV_WILLNEED +#define POSIX_MADV_DONTNEED MADV_DONTNEED + +/** + * @brief Map anonymous or file-backed memory into the process. + * + * @param addr Requested address. MAP_FIXED is rejected because the Windows + * backend does not support safely replacing existing mappings. + * @param length Mapping length in bytes. + * @param prot POSIX PROT_* protection bits. + * @param flags POSIX MAP_* flags. MAP_ANONYMOUS uses VirtualAlloc; file-backed + * mappings use CreateFileMapping/MapViewOfFile. + * @param fd CRT file descriptor for file-backed mappings, or -1 for anonymous. + * @param offset File offset for file-backed mappings. + * @return Mapped address, or MAP_FAILED with errno set. + */ +void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); + +/** @brief Unmap a region returned by mmap(). */ +int munmap(void *addr, size_t length); + +/** @brief Flush a mapped file range when the mapping is file-backed. */ +int msync(void *addr, size_t length, int flags); + +/** @brief Change page protection for an existing mapping. */ +int mprotect(void *addr, size_t length, int prot); + +/** @brief Accept POSIX memory-lock calls; currently a no-op on Windows. */ +int mlock(const void *addr, size_t length); + +/** @brief Accept POSIX memory-unlock calls; currently a no-op on Windows. */ +int munlock(const void *addr, size_t length); + +/** @brief Accept process-wide memory-lock calls; currently a no-op. */ +int mlockall(int flags); + +/** @brief Accept process-wide memory-unlock calls; currently a no-op. */ +int munlockall(void); + +/** + * @brief Accept POSIX memory-advice calls. + * + * Windows has PrefetchVirtualMemory on newer releases, but PX4 does not depend + * on advisory paging behavior. Returning success matches the permissive POSIX + * interpretation for unsupported advice. + */ +static inline int madvise(void *addr, size_t length, int advice) +{ + (void)addr; + (void)length; + (void)advice; + return 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/sys/select.h b/platforms/posix/include/windows_shim/sys/select.h new file mode 100644 index 0000000000..830c6d5389 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/select.h @@ -0,0 +1,46 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/select.h + * + * WinSock provides fd_set/select/timeval; expose it at the POSIX path. + */ + +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#include +#else +#include_next +#endif diff --git a/platforms/posix/include/windows_shim/sys/socket.h b/platforms/posix/include/windows_shim/sys/socket.h new file mode 100644 index 0000000000..0f1ef57738 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/socket.h @@ -0,0 +1,266 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/socket.h + * + * MinGW ships winsock2.h; POSIX code expects . Pull in + * winsock2/ws2tcpip and expose the POSIX-style socket typedefs, + * ancillary-message structs, MSG_* flags, sendmsg/recvmsg, and a + * local socketpair() helper. + * A matching / sit next to this file and + * simply re-include us. + */ +#pragma once + +#include +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* POSIX names mapped onto winsock types. */ +typedef int socklen_t_compat_unused_; /* placeholder, ws2tcpip provides socklen_t */ +typedef SSIZE_T ssize_t_socket_unused_; + +#ifndef _PX4_SA_FAMILY_T_DEFINED +#define _PX4_SA_FAMILY_T_DEFINED +typedef ADDRESS_FAMILY sa_family_t; +#endif + +#ifndef AF_LOCAL +#define AF_LOCAL AF_UNIX +#endif + +#ifndef PF_LOCAL +#define PF_LOCAL PF_UNIX +#endif + +#ifndef _PX4_IOVEC_DEFINED +#define _PX4_IOVEC_DEFINED +/** @brief Scatter/gather buffer descriptor used by sendmsg()/recvmsg(). */ +struct iovec { + void *iov_base; + size_t iov_len; +}; +#endif + +#ifndef _PX4_MSGHDR_DEFINED +#define _PX4_MSGHDR_DEFINED +/** + * @brief POSIX message header for vectored socket I/O. + * + * The Windows backend supports payload iovecs and the ancillary fields needed + * for source compatibility. Unsupported control-message types are ignored by + * the implementation. + */ +struct msghdr { + void *msg_name; + socklen_t msg_namelen; + struct iovec *msg_iov; + int msg_iovlen; + void *msg_control; + size_t msg_controllen; + int msg_flags; +}; + +/** @brief Ancillary data header compatible with POSIX CMSG_* helpers. */ +struct cmsghdr { + size_t cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +#ifndef _PX4_UCRED_DEFINED +#define _PX4_UCRED_DEFINED +/** @brief Credential payload shape used by SCM_CREDENTIALS call sites. */ +struct ucred { + int pid; + unsigned int uid; + unsigned int gid; +}; +#endif +#endif + +#ifndef CMSG_ALIGN +#define CMSG_ALIGN(len) (((len) + sizeof(size_t) - 1) & ~(sizeof(size_t) - 1)) +#endif +#ifndef CMSG_SPACE +#define CMSG_SPACE(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + CMSG_ALIGN(len)) +#endif +#ifndef CMSG_LEN +#define CMSG_LEN(len) (CMSG_ALIGN(sizeof(struct cmsghdr)) + (len)) +#endif +#ifndef CMSG_DATA +#define CMSG_DATA(cmsg) ((unsigned char *)(cmsg) + CMSG_ALIGN(sizeof(struct cmsghdr))) +#endif +#ifndef CMSG_FIRSTHDR +#define CMSG_FIRSTHDR(msg) ((msg)->msg_controllen >= sizeof(struct cmsghdr) ? (struct cmsghdr *)(msg)->msg_control : (struct cmsghdr *)0) +#endif +#ifndef CMSG_NXTHDR +#define CMSG_NXTHDR(msg, cmsg) \ + (((uintptr_t)(CMSG_DATA(cmsg)) + CMSG_ALIGN((cmsg)->cmsg_len - CMSG_ALIGN(sizeof(struct cmsghdr))) + CMSG_ALIGN(sizeof(struct cmsghdr)) > \ + (uintptr_t)(msg)->msg_control + (msg)->msg_controllen) ? \ + (struct cmsghdr *)0 : \ + (struct cmsghdr *)((unsigned char *)(cmsg) + CMSG_ALIGN((cmsg)->cmsg_len))) +#endif + +#ifndef SCM_RIGHTS +#define SCM_RIGHTS 0x01 +#endif +#ifndef SCM_CREDENTIALS +#define SCM_CREDENTIALS 0x02 +#endif + +/* POSIX send/recv flags. winsock covers MSG_OOB/PEEK/DONTROUTE; the rest + * we map to 0 so callers still compile. */ +#ifndef MSG_TRUNC +# define MSG_TRUNC 0 +#endif +#ifndef MSG_EOR +# define MSG_EOR 0 +#endif +#ifndef MSG_CTRUNC +# define MSG_CTRUNC 0 +#endif +#ifndef MSG_CONFIRM +# define MSG_CONFIRM 0 +#endif +#ifndef MSG_ERRQUEUE +# define MSG_ERRQUEUE 0 +#endif +#ifndef MSG_WAITALL +# define MSG_WAITALL 0 +#endif +#ifndef MSG_WAITFORONE +# define MSG_WAITFORONE 0 +#endif +#ifndef MSG_DONTWAIT +# define MSG_DONTWAIT 0 +#endif +#ifndef MSG_MORE +# define MSG_MORE 0 +#endif +#ifndef MSG_NOSIGNAL +# define MSG_NOSIGNAL 0 +#endif +#ifndef MSG_CMSG_CLOEXEC +# define MSG_CMSG_CLOEXEC 0 +#endif +#ifndef SOCK_CLOEXEC +# define SOCK_CLOEXEC 0 +#endif +#ifndef SOCK_NONBLOCK +# define SOCK_NONBLOCK 0 +#endif + +/* Shutdown how values - winsock uses SD_RECEIVE/SD_SEND/SD_BOTH; + * POSIX code expects SHUT_*. */ +#ifndef SHUT_RD +# define SHUT_RD SD_RECEIVE +#endif +#ifndef SHUT_WR +# define SHUT_WR SD_SEND +#endif +#ifndef SHUT_RDWR +# define SHUT_RDWR SD_BOTH +#endif + +/** + * @brief Send a POSIX msghdr over a Winsock socket. + * + * @return Number of bytes sent, or -1 with errno set from WSAGetLastError(). + */ +ssize_t sendmsg(int socket, const struct msghdr *message, int flags); + +/** + * @brief Receive data into a POSIX msghdr from a Winsock socket. + * + * @return Number of bytes received, or -1 with errno set from WSAGetLastError(). + */ +ssize_t recvmsg(int socket, struct msghdr *message, int flags); + +/** + * @brief Create a connected socket pair for PX4 daemon/shell IPC. + * + * Windows AF_UNIX support is not present on every target we care about, so the + * implementation may fall back to a loopback TCP pair while preserving POSIX + * socketpair() behavior for the caller. + */ +int socketpair(int domain, int type, int protocol, int socket_vector[2]); + +#if defined(_MSC_VER) && !defined(__clang__) && !defined(PX4_WINDOWS_NO_SOCKET_MACROS) +/** + * @name MSVC socket wrappers + * + * MSVC does not expose POSIX-like int socket descriptors. These wrappers keep + * PX4 source code using socket(), bind(), send(), etc. while centralizing + * Winsock startup and errno translation in the Windows backend. + * + * @{ + */ +SOCKET WSAAPI px4_windows_socket(int af, int type, int protocol); +int WSAAPI px4_windows_bind(SOCKET s, const struct sockaddr *name, int namelen); +int WSAAPI px4_windows_listen(SOCKET s, int backlog); +SOCKET WSAAPI px4_windows_accept(SOCKET s, struct sockaddr *addr, int *addrlen); +int WSAAPI px4_windows_connect(SOCKET s, const struct sockaddr *name, int namelen); +int WSAAPI px4_windows_setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen); +int WSAAPI px4_windows_shutdown(SOCKET s, int how); +int WSAAPI px4_windows_recv(SOCKET s, char *buf, int len, int flags); +int WSAAPI px4_windows_send(SOCKET s, const char *buf, int len, int flags); +int WSAAPI px4_windows_recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); +int WSAAPI px4_windows_sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen); +char *px4_windows_strerror(int e); +/** @} */ + +#define socket(...) px4_windows_socket(__VA_ARGS__) +#define bind(...) px4_windows_bind(__VA_ARGS__) +#define listen(...) px4_windows_listen(__VA_ARGS__) +#define accept(...) px4_windows_accept(__VA_ARGS__) +#define connect(...) px4_windows_connect(__VA_ARGS__) +#define setsockopt(...) px4_windows_setsockopt(__VA_ARGS__) +#define shutdown(...) px4_windows_shutdown(__VA_ARGS__) +#define recv(...) px4_windows_recv(__VA_ARGS__) +#define send(...) px4_windows_send(__VA_ARGS__) +#define recvfrom(...) px4_windows_recvfrom(__VA_ARGS__) +#define sendto(...) px4_windows_sendto(__VA_ARGS__) +#define strerror(...) px4_windows_strerror(__VA_ARGS__) +#endif + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/sys/socketopt_compat.h b/platforms/posix/include/windows_shim/sys/socketopt_compat.h new file mode 100644 index 0000000000..a9fa2b2c22 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/socketopt_compat.h @@ -0,0 +1,53 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/socketopt_compat.h + * + * Small compatibility helpers for call sites that want POSIX-shaped + * setsockopt arguments. Kept here so it can be included on demand. + */ +#pragma once + +#include + +/** + * @brief Set SO_REUSEADDR with an int argument like POSIX call sites expect. + * + * Winsock's setsockopt() takes a char pointer. Keeping this helper avoids + * sprinkling casts through shared networking code. + */ +static inline int px4_set_socket_reuseaddr(int fd, int enable) +{ + return setsockopt((SOCKET)fd, SOL_SOCKET, SO_REUSEADDR, + (const char *)&enable, sizeof(enable)); +} diff --git a/platforms/posix/include/windows_shim/sys/stat.h b/platforms/posix/include/windows_shim/sys/stat.h new file mode 100644 index 0000000000..69732c7aa2 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/stat.h @@ -0,0 +1,193 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/stat.h + * + * MinGW's sys/stat.h provides POSIX's 1-argument `mkdir(path)` (forwarded + * to _mkdir). PX4 - along with almost every POSIX codebase - uses the + * 2-argument form `mkdir(path, mode)`. Wrap the real call in an inline + * that ignores the mode bits (Windows ACLs don't map to POSIX permission + * bits anyway). + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#if defined(__has_include) +# if __has_include(<../ucrt/sys/stat.h>) +# include <../ucrt/sys/stat.h> +# endif +#endif +#include +#else +#include_next +#endif +#include +#include + +#ifndef _PX4_MKDIR_SHIM_DEFINED +#define _PX4_MKDIR_SHIM_DEFINED + +/* Replace any existing mkdir macro/prototype with a 2-arg form that + * ignores the mode argument and falls back to _mkdir internally. */ +#ifdef mkdir +#undef mkdir +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Two-argument POSIX mkdir() wrapper around Windows _mkdir(). */ +static inline int px4_mkdir_shim(const char *path, mode_t mode) +{ + (void)mode; + return _mkdir(path); +} + +#ifdef __cplusplus +} +#endif + +#define mkdir(path, mode) px4_mkdir_shim((path), (mode)) + +#endif /* _PX4_MKDIR_SHIM_DEFINED */ + +/* POSIX permission bits that Windows's sys/stat.h partially ships. Fill + * in the rest with matching numeric values so code that OR's them + * compiles. */ +#ifndef S_IRWXU +#define S_IRWXU 0700 +#endif +#ifndef S_IFMT +#define S_IFMT 0170000 +#endif +#ifndef S_IFIFO +#define S_IFIFO 0010000 +#endif +#ifndef S_IFCHR +#define S_IFCHR 0020000 +#endif +#ifndef S_IFDIR +#define S_IFDIR 0040000 +#endif +#ifndef S_IFBLK +#define S_IFBLK 0060000 +#endif +#ifndef S_IFREG +#define S_IFREG 0100000 +#endif +#ifndef S_IFLNK +#define S_IFLNK 0120000 +#endif +#ifndef S_IFSOCK +#define S_IFSOCK 0140000 +#endif +#ifndef S_IRUSR +#define S_IRUSR 0400 +#endif +#ifndef S_IWUSR +#define S_IWUSR 0200 +#endif +#ifndef S_IXUSR +#define S_IXUSR 0100 +#endif +#ifndef S_IRWXG +#define S_IRWXG 0070 +#endif +#ifndef S_IRGRP +#define S_IRGRP 0040 +#endif +#ifndef S_IWGRP +#define S_IWGRP 0020 +#endif +#ifndef S_IXGRP +#define S_IXGRP 0010 +#endif +#ifndef S_IRWXO +#define S_IRWXO 0007 +#endif +#ifndef S_IROTH +#define S_IROTH 0004 +#endif +#ifndef S_IWOTH +#define S_IWOTH 0002 +#endif +#ifndef S_IXOTH +#define S_IXOTH 0001 +#endif + +/* MinGW's sys/stat.h has S_ISDIR / S_ISREG but not S_ISLNK. Windows + * tracks symlinks through FILE_ATTRIBUTE_REPARSE_POINT which isn't + * exposed in st_mode, so S_ISLNK always reports "not a symlink" - an + * acceptable simplification for PX4's symlink recreation path, which + * already handles the fall-through branch. */ +#ifndef S_ISLNK +#define S_ISLNK(m) (0) +#endif +#ifndef S_ISFIFO +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) +#endif +#ifndef S_ISCHR +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) +#endif +#ifndef S_ISBLK +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) +#endif +#ifndef S_ISSOCK +#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) +#endif + +#ifndef _PX4_LSTAT_SHIM_DEFINED +#define _PX4_LSTAT_SHIM_DEFINED +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief lstat() compatibility shim. + * + * Windows CRT stat() does not expose symlink metadata in st_mode, so lstat() + * intentionally follows the same path as stat(). S_ISLNK() above therefore + * remains false and callers take their normal non-symlink branch. + */ +static inline int lstat(const char *path, struct stat *buf) +{ + /* Windows has no per-link stat. Fall through to stat(); that's + * accurate enough because S_ISLNK above always returns 0, so + * callers fall into the non-symlink branch anyway. */ + return stat(path, buf); +} +#ifdef __cplusplus +} +#endif +#endif diff --git a/platforms/posix/include/windows_shim/sys/statfs.h b/platforms/posix/include/windows_shim/sys/statfs.h new file mode 100644 index 0000000000..23922828eb --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/statfs.h @@ -0,0 +1,78 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/statfs.h + * + * PX4 uses statfs() only to check whether SITL's synthetic storage + * directory looks like a working filesystem (f_blocks > 0). We route + * the query through GetDiskFreeSpaceExA on Windows - enough to make + * the arming check pass against %LOCALAPPDATA%\PX4. + */ +#pragma once + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef long fsblkcnt_t; +typedef long fsfilcnt_t; + +struct statfs { + unsigned long f_type; + unsigned long f_bsize; + unsigned long f_frsize; + fsblkcnt_t f_blocks; + fsblkcnt_t f_bfree; + fsblkcnt_t f_bavail; + fsfilcnt_t f_files; + fsfilcnt_t f_ffree; + unsigned long f_fsid; + unsigned long f_flags; + unsigned long f_namelen; + unsigned long f_spare[4]; +}; + +/** + * @brief Return filesystem capacity information for @p path. + * + * Backed by GetDiskFreeSpaceExA and shaped like POSIX statfs(). + */ +int statfs(const char *path, struct statfs *buf); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/sys/time.h b/platforms/posix/include/windows_shim/sys/time.h new file mode 100644 index 0000000000..68c5aec5f7 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/time.h @@ -0,0 +1,45 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/time.h + * + * POSIX sys/time.h forwarding shim for Windows builds. + */ + +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#else +#include_next +#endif diff --git a/platforms/posix/include/windows_shim/sys/types.h b/platforms/posix/include/windows_shim/sys/types.h new file mode 100644 index 0000000000..289a3cfaf7 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/types.h @@ -0,0 +1,101 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/types.h + * + * MinGW's omits the BSD-style unprefixed size aliases + * (uint, ushort, ulong) that glibc exports under _GNU_SOURCE. PX4 code + * uses `uint` directly in a couple of places - forward to the real + * header and add the aliases. + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#if defined(__has_include) +# if __has_include(<../ucrt/sys/types.h>) +# include <../ucrt/sys/types.h> +# endif +#endif +#include +#include +#include +#ifndef _PID_T_ +/** @brief Process id type for native MSVC builds. */ +typedef int pid_t; +#define _PID_T_ +#endif +#ifndef _MODE_T_ +/** @brief POSIX file mode bitmask type for native MSVC builds. */ +typedef int mode_t; +#define _MODE_T_ +#endif +#ifndef _OFF_T_DEFINED +/** @brief File offset type for native MSVC builds. */ +typedef long off_t; +#define _OFF_T_DEFINED +#endif +#ifndef _SSIZE_T_DEFINED +/** @brief Signed size type matching Windows SSIZE_T. */ +typedef SSIZE_T ssize_t; +#define _SSIZE_T_DEFINED +#endif +#ifndef _USECONDS_T_DEFINED +/** @brief Microsecond interval type used by usleep(). */ +typedef unsigned int useconds_t; +#define _USECONDS_T_DEFINED +#endif +#else +#include_next +#endif + +#ifndef _PX4_SYS_TYPES_ALIASES_DEFINED +#define _PX4_SYS_TYPES_ALIASES_DEFINED +/** @name BSD/GNU scalar aliases + * + * glibc exposes these names in PX4's POSIX build modes. Windows CRT headers do + * not, so the shim defines them once for shared code that uses the shorter + * spellings. + * + * @{ + */ +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +typedef unsigned int uint; +typedef unsigned short ushort; +typedef unsigned long ulong; +typedef long long quad_t; +typedef unsigned long long u_quad_t; +/** @} */ +#endif diff --git a/platforms/posix/include/windows_shim/sys/un.h b/platforms/posix/include/windows_shim/sys/un.h new file mode 100644 index 0000000000..0f2f870a4d --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/un.h @@ -0,0 +1,66 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/un.h + * + * AF_UNIX was added to Windows 10 in build 17063 (April 2018) and is + * exposed through afunix.h. MinGW-w64 headers do not yet forward a + * POSIX-style , so we supply one that re-exposes the struct + * under the expected name. Windows' AF_UNIX supports SOCK_STREAM over + * file-system paths but not abstract namespace sockets - SITL only + * uses file-system paths, so this is sufficient for the daemon IPC. + */ +#pragma once + +#ifndef _WIN32 +# error "sys/un.h shim only valid on Windows" +#endif + +#include +#include +#include +#include + +#ifndef UNIX_PATH_MAX +# define UNIX_PATH_MAX 108 +#endif + +typedef struct sockaddr_un sockaddr_un; + +#ifndef SUN_LEN +# define SUN_LEN(ptr) ((socklen_t)(offsetof(struct sockaddr_un, sun_path) + strlen((ptr)->sun_path))) +#endif + +/* afunix.h already provides struct sockaddr_un. Re-export sun_path/sun_family + * aliases if any translation unit expects them. afunix.h on Windows already + * names them that way, so no further work is needed. */ diff --git a/platforms/posix/include/windows_shim/sys/wait.h b/platforms/posix/include/windows_shim/sys/wait.h new file mode 100644 index 0000000000..806721e3a7 --- /dev/null +++ b/platforms/posix/include/windows_shim/sys/wait.h @@ -0,0 +1,96 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sys/wait.h + * + * MinGW-w64 does not ship a POSIX waitpid/wait status header. PX4's + * Windows target does not support fork/exec semantics, but a number of + * third-party libraries still expect the wait status macros and a + * waitpid() declaration to exist. The implementation in + * posix_shim.cpp waits on a Windows process id/handle and synthesizes + * POSIX-style exit status words. + */ +#pragma once + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#ifndef WNOHANG +#define WNOHANG 0x00000001 +#endif +#ifndef WUNTRACED +#define WUNTRACED 0x00000002 +#endif +#ifndef WCONTINUED +#define WCONTINUED 0x00000008 +#endif + +#ifndef WEXITSTATUS +#define WEXITSTATUS(status) (((status) >> 8) & 0xff) +#endif +#ifndef WTERMSIG +#define WTERMSIG(status) ((status) & 0x7f) +#endif +#ifndef WSTOPSIG +#define WSTOPSIG(status) WEXITSTATUS(status) +#endif +#ifndef WIFEXITED +#define WIFEXITED(status) (WTERMSIG(status) == 0) +#endif +#ifndef WIFSIGNALED +#define WIFSIGNALED(status) (WTERMSIG(status) != 0 && WTERMSIG(status) != 0x7f) +#endif +#ifndef WIFSTOPPED +#define WIFSTOPPED(status) (WTERMSIG(status) == 0x7f) +#endif +#ifndef WIFCONTINUED +#define WIFCONTINUED(status) ((status) == 0xffff) +#endif + +/** + * @brief Wait for a Windows process id and synthesize a POSIX wait status. + * + * @param pid Process id to wait for. + * @param status Optional POSIX wait status output. + * @param options Supports WNOHANG; other options are accepted for source + * compatibility. + * @return @p pid on completion, 0 for WNOHANG timeout, or -1 with errno set. + */ +pid_t waitpid(pid_t pid, int *status, int options); + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/syslog.h b/platforms/posix/include/windows_shim/syslog.h new file mode 100644 index 0000000000..885b1b45af --- /dev/null +++ b/platforms/posix/include/windows_shim/syslog.h @@ -0,0 +1,143 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file syslog.h + * + * MinGW has no . Most PX4 call sites only include the header + * (to satisfy printf-style macros through other logging layers) rather + * than actually calling syslog(), so we provide a header with the + * standard priority levels and a syslog() that forwards to stderr. + */ +#pragma once + +#include +#include + +#define LOG_KERN (0 << 3) +#define LOG_USER (1 << 3) +#define LOG_MAIL (2 << 3) +#define LOG_DAEMON (3 << 3) +#define LOG_AUTH (4 << 3) +#define LOG_SYSLOG (5 << 3) +#define LOG_LPR (6 << 3) +#define LOG_NEWS (7 << 3) +#define LOG_UUCP (8 << 3) +#define LOG_CRON (9 << 3) +#define LOG_AUTHPRIV (10 << 3) +#define LOG_FTP (11 << 3) +#define LOG_LOCAL0 (16 << 3) +#define LOG_LOCAL1 (17 << 3) +#define LOG_LOCAL2 (18 << 3) +#define LOG_LOCAL3 (19 << 3) +#define LOG_LOCAL4 (20 << 3) +#define LOG_LOCAL5 (21 << 3) +#define LOG_LOCAL6 (22 << 3) +#define LOG_LOCAL7 (23 << 3) + +#define LOG_EMERG 0 +#define LOG_ALERT 1 +#define LOG_CRIT 2 +#define LOG_ERR 3 +#define LOG_WARNING 4 +#define LOG_NOTICE 5 +#define LOG_INFO 6 +#define LOG_DEBUG 7 + +#define LOG_PID 0x01 +#define LOG_CONS 0x02 +#define LOG_NDELAY 0x08 +#define LOG_NOWAIT 0x10 +#define LOG_PERROR 0x20 + +#define LOG_PRIMASK 0x07 +#define LOG_FACMASK 0x03f8 +#define LOG_PRI(p) ((p) & LOG_PRIMASK) +#define LOG_FAC(p) (((p) & LOG_FACMASK) >> 3) +#define LOG_MAKEPRI(fac, pri) ((fac) | (pri)) +#define LOG_MASK(pri) (1 << (pri)) +#define LOG_UPTO(pri) ((1 << ((pri) + 1)) - 1) + +#ifdef __cplusplus +extern "C" { +#endif + +static int px4_syslog_mask = LOG_UPTO(LOG_DEBUG); + +/** @brief Accept syslog identity/options; no persistent Windows sink is opened. */ +static inline void openlog(const char *ident, int option, int facility) +{ + (void)ident; (void)option; (void)facility; +} + +/** @brief Close the syslog sink; no-op for the stderr-backed shim. */ +static inline void closelog(void) {} + +/** + * @brief Set the active syslog priority mask. + * + * @return Previous mask, matching POSIX setlogmask(). + */ +static inline int setlogmask(int maskpri) +{ + const int old_mask = px4_syslog_mask; + + if (maskpri != 0) { + px4_syslog_mask = maskpri; + } + + return old_mask; +} + +/** @brief Write a masked syslog message to stderr with a trailing newline. */ +static inline void vsyslog(int priority, const char *fmt, va_list ap) +{ + if ((LOG_MASK(LOG_PRI(priority)) & px4_syslog_mask) == 0) { + return; + } + + vfprintf(stderr, fmt, ap); + fputc('\n', stderr); +} + +/** @brief Variadic syslog wrapper around vsyslog(). */ +static inline void syslog(int priority, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsyslog(priority, fmt, ap); + va_end(ap); +} + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/termios.h b/platforms/posix/include/windows_shim/termios.h new file mode 100644 index 0000000000..cd05380832 --- /dev/null +++ b/platforms/posix/include/windows_shim/termios.h @@ -0,0 +1,256 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file termios.h + * + * Windows termios.h shim for PX4 SITL. + * + * MinGW-w64 does not ship a termios header. SITL drivers that touch + * serial UARTs (GPS, RC, telemetry radios, etc.) include + * unconditionally. We expose the full IEEE Std 1003.1-2017 structure + * and constants so the code compiles, and route the handful of termios + * functions to stubs that return -ENOSYS. Real serial I/O on Windows + * would go through CreateFileA("\\\\.\\COMx", ...) + SetCommState; + * that is out of scope for the base SITL port but this stub surface + * leaves room to add it later without touching any driver code. + */ +#pragma once + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +typedef unsigned int tcflag_t; +typedef unsigned int speed_t; +typedef unsigned char cc_t; + +#define NCCS 32 + +/** + * @brief POSIX terminal attribute container. + * + * Native Windows SITL currently exposes this structure for driver source + * compatibility only; tc* functions return -1/ENOSYS until a COM-port backed + * implementation is added. + */ +struct termios { + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; + cc_t c_line; + cc_t c_cc[NCCS]; + speed_t c_ispeed; + speed_t c_ospeed; +}; + +/* c_iflag - input modes */ +#define IGNBRK 0000001 +#define BRKINT 0000002 +#define IGNPAR 0000004 +#define PARMRK 0000010 +#define INPCK 0000020 +#define ISTRIP 0000040 +#define INLCR 0000100 +#define IGNCR 0000200 +#define ICRNL 0000400 +#define IUCLC 0001000 +#define IXON 0002000 +#define IXANY 0004000 +#define IXOFF 0010000 +#define IMAXBEL 0020000 +#define IUTF8 0040000 + +/* c_oflag - output modes */ +#define OPOST 0000001 +#define OLCUC 0000002 +#define ONLCR 0000004 +#define OCRNL 0000010 +#define ONOCR 0000020 +#define ONLRET 0000040 +#define OFILL 0000100 +#define OFDEL 0000200 +#define NLDLY 0000400 +#define NL0 0000000 +#define NL1 0000400 +#define CRDLY 0003000 +#define CR0 0000000 +#define CR1 0001000 +#define CR2 0002000 +#define CR3 0003000 +#define TABDLY 0014000 +#define TAB0 0000000 +#define TAB1 0004000 +#define TAB2 0010000 +#define TAB3 0014000 +#define BSDLY 0020000 +#define BS0 0000000 +#define BS1 0020000 +#define VTDLY 0040000 +#define VT0 0000000 +#define VT1 0040000 +#define FFDLY 0100000 +#define FF0 0000000 +#define FF1 0100000 + +/* c_cflag - control modes */ +#define CSIZE 0000060 +#define CS5 0000000 +#define CS6 0000020 +#define CS7 0000040 +#define CS8 0000060 +#define CSTOPB 0000100 +#define CREAD 0000200 +#define PARENB 0000400 +#define PARODD 0001000 +#define HUPCL 0002000 +#define CLOCAL 0004000 +#define CMSPAR 010000000000 +#define CRTSCTS 020000000000 + +/* c_lflag - local modes */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 +#define EXTPROC 0200000 + +/* c_cc indices */ +#define VINTR 0 +#define VQUIT 1 +#define VERASE 2 +#define VKILL 3 +#define VEOF 4 +#define VTIME 5 +#define VMIN 6 +#define VSWTC 7 +#define VSTART 8 +#define VSTOP 9 +#define VSUSP 10 +#define VEOL 11 +#define VREPRINT 12 +#define VDISCARD 13 +#define VWERASE 14 +#define VLNEXT 15 +#define VEOL2 16 + +/* optional_actions for tcsetattr */ +#define TCSANOW 0 +#define TCSADRAIN 1 +#define TCSAFLUSH 2 + +/* queue_selector for tcflush */ +#define TCIFLUSH 0 +#define TCOFLUSH 1 +#define TCIOFLUSH 2 + +/* action for tcflow */ +#define TCOOFF 0 +#define TCOON 1 +#define TCIOFF 2 +#define TCION 3 + +/* Baud rates - numeric values (Linux encodes as bitmask in c_cflag; + * PX4 never inspects these beyond round-tripping through the struct). */ +#define B0 0 +#define B50 50 +#define B75 75 +#define B110 110 +#define B134 134 +#define B150 150 +#define B200 200 +#define B300 300 +#define B600 600 +#define B1200 1200 +#define B1800 1800 +#define B2400 2400 +#define B4800 4800 +#define B9600 9600 +#define B19200 19200 +#define B38400 38400 +#define B57600 57600 +#define B115200 115200 +#define B230400 230400 +#define B460800 460800 +#define B500000 500000 +#define B576000 576000 +#define B921600 921600 +#define B1000000 1000000 +#define B1152000 1152000 +#define B1500000 1500000 +#define B2000000 2000000 +#define B2500000 2500000 +#define B3000000 3000000 +#define B3500000 3500000 +#define B4000000 4000000 + +/** @name Terminal attribute operations + * + * Declared for POSIX source compatibility. The Windows backend stubs these + * functions today because PX4 SITL does not talk to native serial devices + * through termios. + * + * @{ + */ +int tcgetattr(int fd, struct termios *termios_p); +int tcsetattr(int fd, int optional_actions, const struct termios *termios_p); +int tcflush(int fd, int queue_selector); +int tcdrain(int fd); +int tcflow(int fd, int action); +int tcsendbreak(int fd, int duration); +pid_t tcgetsid(int fd); +int cfsetispeed(struct termios *termios_p, speed_t speed); +int cfsetospeed(struct termios *termios_p, speed_t speed); +int cfsetspeed(struct termios *termios_p, speed_t speed); +speed_t cfgetispeed(const struct termios *termios_p); +speed_t cfgetospeed(const struct termios *termios_p); +void cfmakeraw(struct termios *termios_p); +/** @} */ + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/include/windows_shim/time.h b/platforms/posix/include/windows_shim/time.h new file mode 100644 index 0000000000..5f556c906b --- /dev/null +++ b/platforms/posix/include/windows_shim/time.h @@ -0,0 +1,167 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file time.h + * + * MinGW ships with the Windows _s variants but not POSIX's + * _r reentrant forms. The argument orders differ (_s takes buffer first, + * _r takes the input first) so we can't just #define. Provide thin + * inline wrappers. + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#if defined(__has_include) +# if __has_include(<../ucrt/time.h>) +# include <../ucrt/time.h> +# else +# include +# endif +#else +#include +#endif +#include +typedef int clockid_t; +#ifndef CLOCK_REALTIME +#define CLOCK_REALTIME 0 +#endif +#ifndef CLOCK_MONOTONIC +#define CLOCK_MONOTONIC 1 +#endif +#ifndef CLOCK_BOOTTIME +#define CLOCK_BOOTTIME CLOCK_MONOTONIC +#endif +#ifndef CLOCK_REALTIME_COARSE +#define CLOCK_REALTIME_COARSE CLOCK_REALTIME +#endif +#ifndef CLOCK_MONOTONIC_COARSE +#define CLOCK_MONOTONIC_COARSE CLOCK_MONOTONIC +#endif +#ifndef _TIMEVAL_DEFINED +#define _TIMEVAL_DEFINED +struct timeval { + long tv_sec; + long tv_usec; +}; +#endif +#else +#include_next +#endif + +#ifndef _PX4_TIME_R_SHIM_DEFINED +#define _PX4_TIME_R_SHIM_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +/** @brief POSIX clock_gettime() backed by Windows high-resolution timers. */ +int clock_gettime(clockid_t clk_id, struct timespec *tp); + +/** @brief POSIX clock_settime() compatibility entry point. */ +int clock_settime(clockid_t clk_id, const struct timespec *tp); + +/** @brief Fill a POSIX timeval with the current wall-clock time. */ +int gettimeofday(struct timeval *tv, void *tz); + +/** @brief Sleep for the requested interval, reporting no remaining time. */ +int nanosleep(const struct timespec *req, struct timespec *rem); +#endif + +/** @brief Thread-safe UTC conversion using the Windows gmtime_s() order. */ +static inline struct tm *gmtime_r(const time_t *timep, struct tm *result) +{ + if (!timep || !result) { return NULL; } + return (gmtime_s(result, timep) == 0) ? result : NULL; +} + +/** @brief Thread-safe local-time conversion using the Windows localtime_s(). */ +static inline struct tm *localtime_r(const time_t *timep, struct tm *result) +{ + if (!timep || !result) { return NULL; } + return (localtime_s(result, timep) == 0) ? result : NULL; +} + +/** @brief Thread-safe asctime() wrapper; @p buf must hold at least 26 bytes. */ +static inline char *asctime_r(const struct tm *tm, char *buf) +{ + if (!tm || !buf) { return NULL; } + /* POSIX requires a 26-byte buffer; match that to asctime_s. */ + return (asctime_s(buf, 26, tm) == 0) ? buf : NULL; +} + +/** @brief Thread-safe ctime() wrapper; @p buf must hold at least 26 bytes. */ +static inline char *ctime_r(const time_t *timep, char *buf) +{ + if (!timep || !buf) { return NULL; } + return (ctime_s(buf, 26, timep) == 0) ? buf : NULL; +} + +#ifndef timerisset +#define timerisset(tvp) ((tvp)->tv_sec || (tvp)->tv_usec) +#endif +#ifndef timerclear +#define timerclear(tvp) do { (tvp)->tv_sec = 0; (tvp)->tv_usec = 0; } while (0) +#endif +#ifndef timercmp +#define timercmp(a, b, CMP) (((a)->tv_sec == (b)->tv_sec) ? ((a)->tv_usec CMP (b)->tv_usec) : ((a)->tv_sec CMP (b)->tv_sec)) +#endif +#ifndef timeradd +#define timeradd(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec + (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec + (b)->tv_usec; \ + if ((result)->tv_usec >= 1000000) { \ + ++(result)->tv_sec; \ + (result)->tv_usec -= 1000000; \ + } \ + } while (0) +#endif +#ifndef timersub +#define timersub(a, b, result) \ + do { \ + (result)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ + (result)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ + if ((result)->tv_usec < 0) { \ + --(result)->tv_sec; \ + (result)->tv_usec += 1000000; \ + } \ + } while (0) +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* _PX4_TIME_R_SHIM_DEFINED */ diff --git a/platforms/posix/include/windows_shim/unistd.h b/platforms/posix/include/windows_shim/unistd.h new file mode 100644 index 0000000000..b5cf8c9b9c --- /dev/null +++ b/platforms/posix/include/windows_shim/unistd.h @@ -0,0 +1,319 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file unistd.h + * + * unistd.h extension shim for PX4 SITL on Windows. + * + * MinGW ships a minimal but omits POSIX helpers that PX4 and + * third-party code use directly (pipe, fsync, symlink, dprintf, sysconf, + * process/session helpers, environment setters, hard links). + * Forward to the real header via #include_next and layer the missing + * pieces on top using Win32 CRT equivalents (_pipe, _commit, _write) + * or Win32 APIs (CreateSymbolicLinkA, GetSystemInfo). + */ +#pragma once + +#if defined(_MSC_VER) && !defined(__clang__) +#include +#else +#include_next +#endif +#include +#include +#include +#include + +/* POSIX standard fd numbers - MinGW defines them via io.h but belt-and-suspenders. */ +#ifndef STDIN_FILENO +#define STDIN_FILENO 0 +#endif +#ifndef STDOUT_FILENO +#define STDOUT_FILENO 1 +#endif +#ifndef STDERR_FILENO +#define STDERR_FILENO 2 +#endif +#ifndef F_OK +#define F_OK 0 +#endif +#ifndef X_OK +#define X_OK 1 +#endif +#ifndef W_OK +#define W_OK 2 +#endif +#ifndef R_OK +#define R_OK 4 +#endif + +/* POSIX sysconf selectors that MinGW doesn't ship. Numerical values + * don't need to match Linux - only our own sysconf() implementation + * inspects them. */ +#ifndef _SC_PAGESIZE +#define _SC_PAGESIZE 30 +#endif +#ifndef _SC_PAGE_SIZE +#define _SC_PAGE_SIZE _SC_PAGESIZE +#endif +#ifndef _SC_CLK_TCK +#define _SC_CLK_TCK 2 +#endif +#ifndef _SC_NPROCESSORS_ONLN +#define _SC_NPROCESSORS_ONLN 84 +#endif +#ifndef _SC_NPROCESSORS_CONF +#define _SC_NPROCESSORS_CONF 83 +#endif +#ifndef _SC_OPEN_MAX +#define _SC_OPEN_MAX 4 +#endif +#ifndef _SC_HOST_NAME_MAX +#define _SC_HOST_NAME_MAX 180 +#endif +#ifndef _SC_LOGIN_NAME_MAX +#define _SC_LOGIN_NAME_MAX 71 +#endif +#ifndef _SC_PHYS_PAGES +#define _SC_PHYS_PAGES 85 +#endif +#ifndef _SC_AVPHYS_PAGES +#define _SC_AVPHYS_PAGES 86 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#if defined(_MSC_VER) && !defined(__clang__) +/** @brief Sleep for at least @p usec microseconds using Windows Sleep(). */ +static inline int usleep(useconds_t usec) +{ + Sleep((DWORD)((usec + 999U) / 1000U)); + return 0; +} + +/** @brief Sleep for at least @p seconds seconds using Windows Sleep(). */ +static inline unsigned int sleep(unsigned int seconds) +{ + Sleep(seconds * 1000U); + return 0; +} +#endif + +/* POSIX pipe(fd[2]) - default to 64 KiB buffer and binary mode. */ +#ifndef _PX4_PIPE_SHIM_DEFINED +#define _PX4_PIPE_SHIM_DEFINED +/** @brief Create a binary CRT pipe with a 64 KiB buffer. */ +static inline int pipe(int fds[2]) { return _pipe(fds, 65536, 0x8000 /* _O_BINARY */); } + +/** + * @brief pipe2() compatibility wrapper. + * + * Windows CRT pipes do not expose POSIX pipe2 flags; the shim accepts the + * argument and creates the same binary pipe as pipe(). + */ +static inline int pipe2(int fds[2], int flags) +{ + (void)flags; + return pipe(fds); +} +#endif + +/* POSIX fsync(fd) - forwards to _commit (flushes the CRT fd's buffers + * down to the underlying HANDLE and flushes the HANDLE to disk). */ +#ifndef _PX4_FSYNC_SHIM_DEFINED +#define _PX4_FSYNC_SHIM_DEFINED +static inline int fsync(int fd) { return _commit(fd); } +#endif + +/* fdatasync: Windows has no separate metadata/data flush. Treat it as + * equivalent to fsync (POSIX allows this; it's just stricter). */ +#ifndef _PX4_FDATASYNC_SHIM_DEFINED +#define _PX4_FDATASYNC_SHIM_DEFINED +static inline int fdatasync(int fd) { return _commit(fd); } +#endif + +/* POSIX sysconf - implemented in posix_shim.cpp so we can use + * 's GetSystemInfo / GlobalMemoryStatusEx without forcing + * every translation unit to drag in . */ +#ifndef _PX4_SYSCONF_SHIM_DEFINED +#define _PX4_SYSCONF_SHIM_DEFINED +/** + * @brief Query host limits for the POSIX selectors PX4 uses. + * + * Implemented in the Windows backend so page size, processor count, and memory + * values come from GetSystemInfo/GlobalMemoryStatusEx. + */ +long sysconf(int name); +#endif + +/* POSIX symlink(target, linkpath) - forwards to CreateSymbolicLinkA + * (requires the SE_CREATE_SYMBOLIC_LINK_NAME privilege or Windows 10 + * developer-mode). Returns -1 with errno = EPERM if the user lacks + * the privilege. */ +#ifndef _PX4_SYMLINK_SHIM_DEFINED +#define _PX4_SYMLINK_SHIM_DEFINED +/** + * @brief Create a filesystem symbolic link. + * + * @return 0 on success, -1 with errno set. EPERM indicates missing Windows + * symlink privilege or disabled developer mode. + */ +int symlink(const char *target, const char *linkpath); +#endif + +/* POSIX readlink - Windows has no O(1) path-to-reparse-target readout; + * fall through to DeviceIoControl on the reparse point. The base SITL + * build doesn't follow symlinks, so the shim lives in posix_shim.cpp. */ +#ifndef _PX4_READLINK_SHIM_DEFINED +#define _PX4_READLINK_SHIM_DEFINED +/** + * @brief Read the target of a filesystem reparse-point symlink. + * + * @return Number of bytes copied into @p buf, or -1 with errno set. + */ +ssize_t readlink(const char *path, char *buf, size_t bufsiz); +#endif + +/* POSIX truncate(path, length) - forwards to CreateFileA + SetEndOfFile + * in posix_shim.cpp. ftruncate on a CRT fd uses _chsize_s. */ +#ifndef _PX4_TRUNCATE_SHIM_DEFINED +#define _PX4_TRUNCATE_SHIM_DEFINED +/** @brief Truncate a file by path using CreateFileA/SetEndOfFile. */ +int truncate(const char *path, off_t length); +#endif + +/* POSIX link(oldpath, newpath) - maps onto CreateHardLinkA via + * posix_shim.cpp. */ +#ifndef _PX4_LINK_SHIM_DEFINED +#define _PX4_LINK_SHIM_DEFINED +/** @brief Create a hard link using CreateHardLinkA. */ +int link(const char *existing_path, const char *new_path); +#endif + +/* POSIX realpath fallback - CRT _fullpath returns the canonical path. */ +#ifndef _PX4_REALPATH_SHIM_DEFINED +#define _PX4_REALPATH_SHIM_DEFINED +#include +/** @brief Resolve a path to an absolute CRT path using _fullpath(). */ +static inline char *realpath(const char *path, char *resolved_path) +{ + return _fullpath(resolved_path, path, resolved_path ? 260 : 0); +} +#endif + +/* POSIX dprintf / vdprintf - forward to _write on the CRT fd. */ +#ifndef _PX4_DPRINTF_SHIM_DEFINED +#define _PX4_DPRINTF_SHIM_DEFINED +/** @brief Formatted write to a CRT file descriptor. */ +static inline int vdprintf(int fd, const char *fmt, va_list ap) +{ + char buf[1024]; + int n = vsnprintf(buf, sizeof(buf), fmt, ap); + if (n <= 0) { return n; } + if ((size_t)n >= sizeof(buf)) { n = (int)sizeof(buf) - 1; } + return (int)_write(fd, buf, (unsigned)n); +} + +/** @brief Variadic formatted write to a CRT file descriptor. */ +static inline int dprintf(int fd, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + int n = vdprintf(fd, fmt, ap); + va_end(ap); + return n; +} +#endif + +#ifndef TEMP_FAILURE_RETRY +#define TEMP_FAILURE_RETRY(expression) (expression) +#endif + +/* POSIX getpagesize - Windows has GetSystemInfo.dwPageSize (typically + * 4096 on x86/x64, 16384 on ARM64). */ +#ifndef _PX4_GETPAGESIZE_SHIM_DEFINED +#define _PX4_GETPAGESIZE_SHIM_DEFINED +/** @brief Return the host memory page size in bytes. */ +int getpagesize(void); +#endif + +/* POSIX/BSD process and environment helpers that MinGW does not + * declare. These are implemented in posix_shim.cpp with Windows-backed + * compatibility semantics suitable for PX4 and embedded host apps. */ +#ifndef _PX4_GETPPID_SHIM_DEFINED +#define _PX4_GETPPID_SHIM_DEFINED +/** @brief Return the parent process id when Windows can discover it. */ +pid_t getppid(void); +#endif + +#ifndef _PX4_SETSID_SHIM_DEFINED +#define _PX4_SETSID_SHIM_DEFINED +/** @brief Create a best-effort process session; returns the current pid. */ +pid_t setsid(void); +#endif + +#ifndef _PX4_GETSID_SHIM_DEFINED +#define _PX4_GETSID_SHIM_DEFINED +/** @brief Return the best-effort session id for @p pid. */ +pid_t getsid(pid_t pid); +#endif + +#ifndef _PX4_DAEMON_SHIM_DEFINED +#define _PX4_DAEMON_SHIM_DEFINED +/** + * @brief daemon() compatibility shim. + * + * Windows cannot fork and detach in the POSIX sense; the implementation applies + * the requested cwd/stdio behavior where possible and otherwise reports + * success for callers that only need a background-compatible code path. + */ +int daemon(int nochdir, int noclose); +#endif + +#ifndef _PX4_SETENV_SHIM_DEFINED +#define _PX4_SETENV_SHIM_DEFINED +/** @brief Set an environment variable using the Windows CRT environment. */ +int setenv(const char *name, const char *value, int overwrite); +#endif + +#ifndef _PX4_UNSETENV_SHIM_DEFINED +#define _PX4_UNSETENV_SHIM_DEFINED +/** @brief Remove an environment variable using the Windows CRT environment. */ +int unsetenv(const char *name); +#endif + +#ifdef __cplusplus +} +#endif diff --git a/platforms/posix/src/px4/common/drv_hrt.cpp b/platforms/posix/src/px4/common/drv_hrt.cpp index 1163f3fd8e..b2102bf93d 100644 --- a/platforms/posix/src/px4/common/drv_hrt.cpp +++ b/platforms/posix/src/px4/common/drv_hrt.cpp @@ -533,4 +533,21 @@ void px4_lockstep_wait_for_components() { lockstep_scheduler.components().wait_for_components(); } +#elif defined(__PX4_WINDOWS) +int px4_usleep(useconds_t usec) +{ + // MinGW's usleep/nanosleep round sub-millisecond waits down to an + // immediate return under Wine. HRT uses those tiny waits to yield between + // timer checks, so force at least one millisecond for non-zero sleeps. + if (usec > 0 && usec < 1000) { + usec = 1000; + } + + return system_usleep(usec); +} + +unsigned int px4_sleep(unsigned int seconds) +{ + return system_sleep(seconds); +} #endif diff --git a/platforms/posix/src/px4/common/px4_sem.cpp b/platforms/posix/src/px4/common/px4_sem.cpp index 492e019893..8f3a857fd4 100644 --- a/platforms/posix/src/px4/common/px4_sem.cpp +++ b/platforms/posix/src/px4/common/px4_sem.cpp @@ -55,16 +55,21 @@ int px4_sem_init(px4_sem_t *s, int pshared, unsigned value) // We do not used the process shared arg (void)pshared; s->value = value; - pthread_cond_init(&(s->wait), nullptr); pthread_mutex_init(&(s->lock), nullptr); -#if !defined(__PX4_DARWIN) - // We want to use CLOCK_MONOTONIC if possible but we can't on macOS - // because it's not available. +#if !defined(__PX4_DARWIN) && !defined(__PX4_WINDOWS) + // Use CLOCK_MONOTONIC so the abstime passed to px4_sem_timedwait is not + // affected by wall-clock jumps. Not available on macOS; on MinGW + // winpthreads (up to at least 8.0), pthread_condattr_setclock is a + // no-op — pthread_cond_timedwait always interprets abstime as + // CLOCK_REALTIME. For Windows we default-init the cond and rewrite + // the abstime at wait time (see px4_sem_timedwait below). pthread_condattr_t attr; pthread_condattr_init(&attr); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); pthread_cond_init(&(s->wait), &attr); +#else + pthread_cond_init(&(s->wait), nullptr); #endif return 0; @@ -133,6 +138,40 @@ int px4_sem_timedwait(px4_sem_t *s, const struct timespec *abstime) s->value--; errno = 0; +#if defined(__PX4_WINDOWS) && !defined(ENABLE_LOCKSTEP_SCHEDULER) + // Callers build `abstime` from CLOCK_MONOTONIC, but MinGW winpthreads' + // pthread_cond_timedwait interprets it as CLOCK_REALTIME. Rebase onto + // CLOCK_REALTIME by taking the remaining duration against MONOTONIC + // now and adding it to REALTIME now. + struct timespec abstime_realtime = *abstime; + + if (s->value < 0) { + struct timespec now_mono; + struct timespec now_real; + clock_gettime(CLOCK_MONOTONIC, &now_mono); + clock_gettime(CLOCK_REALTIME, &now_real); + + int64_t remaining_ns = + ((int64_t)abstime->tv_sec - (int64_t)now_mono.tv_sec) * 1000000000LL + + ((int64_t)abstime->tv_nsec - (int64_t)now_mono.tv_nsec); + + if (remaining_ns < 0) { + remaining_ns = 0; + } + + int64_t total_ns = (int64_t)now_real.tv_nsec + remaining_ns; + abstime_realtime.tv_sec = now_real.tv_sec + total_ns / 1000000000LL; + abstime_realtime.tv_nsec = total_ns % 1000000000LL; + } + + if (s->value < 0) { + ret = px4_pthread_cond_timedwait(&(s->wait), &(s->lock), &abstime_realtime); + + } else { + ret = 0; + } +#else + if (s->value < 0) { ret = px4_pthread_cond_timedwait(&(s->wait), &(s->lock), abstime); @@ -140,6 +179,15 @@ int px4_sem_timedwait(px4_sem_t *s, const struct timespec *abstime) ret = 0; } +#endif + + if (ret != 0) { + // The decrement above represents this waiter. If the timed wait + // expires or fails, the waiter is leaving and must not consume a + // future post. + s->value++; + } + errno = ret; if (ret != 0 && ret != ETIMEDOUT) { diff --git a/platforms/posix/src/px4/common/tasks.cpp b/platforms/posix/src/px4/common/tasks.cpp index b5df4d56f0..e543fd946c 100644 --- a/platforms/posix/src/px4/common/tasks.cpp +++ b/platforms/posix/src/px4/common/tasks.cpp @@ -195,7 +195,15 @@ px4_task_t px4_task_spawn_cmd(const char *name, int scheduler, int priority, int return (rv < 0) ? rv : -rv; } +#ifdef __PX4_WINDOWS + /* MinGW winpthreads only supports SCHED_OTHER for unprivileged threads; + * SCHED_FIFO/SCHED_RR require RT permissions that a SITL test run does + * not have. Ignore the requested policy and fall back to SCHED_OTHER. */ + (void)scheduler; + rv = pthread_attr_setschedpolicy(&attr, SCHED_OTHER); +#else rv = pthread_attr_setschedpolicy(&attr, scheduler); +#endif if (rv != 0) { PX4_ERR("px4_task_spawn_cmd: failed to set sched policy"); @@ -204,7 +212,7 @@ px4_task_t px4_task_spawn_cmd(const char *name, int scheduler, int priority, int return (rv < 0) ? rv : -rv; } -#ifdef __PX4_CYGWIN +#if defined(__PX4_CYGWIN) || defined(__PX4_WINDOWS) /* Priorities on Windows are defined a lot differently */ priority = SCHED_PRIORITY_DEFAULT; #endif @@ -329,7 +337,7 @@ void px4_task_exit(int ret) pthread_mutex_unlock(&task_mutex); - pthread_exit((void *)(unsigned long)ret); + pthread_exit(reinterpret_cast(static_cast(ret))); } int px4_task_kill(px4_task_t id, int sig) diff --git a/platforms/posix/src/px4/windows/include/px4_windows_internal.h b/platforms/posix/src/px4/windows/include/px4_windows_internal.h new file mode 100644 index 0000000000..d19686d95c --- /dev/null +++ b/platforms/posix/src/px4/windows/include/px4_windows_internal.h @@ -0,0 +1,112 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file px4_windows_internal.h + * + * Shared preamble for the Windows backend of the POSIX platform. + * Every Windows backend .cpp includes this header before anything else + * so the Win32 / winsock / POSIX-shim include order is identical + * everywhere, and so errno-mapping helpers are declared once. + * + * The layout under platforms/posix/src/px4/windows is: + * runtime/ - process init, console, VT, /dev/tty restore + * posix/fs/ - filesystem operations (mman, flock, ...) + * posix/sys/ - syscall-adjacent shims (termios, ioctl, sysconf, ...) + * posix/net/ - networking: resolvers, sockets, interface queries + * posix/proc/ - process / user identity / environment + * posix/lib/ - userland helpers (dlfcn, ...) + * shell/ - PX4 startup-script backend for Windows + */ +#pragma once + +#ifndef _WIN32 +# error "px4_windows_internal.h is Windows-only" +#endif + +#define _WIN32_WINNT 0x0A00 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * @brief Translate a Win32 error code into its nearest POSIX errno value. + * + * @param err Value returned by GetLastError(). + * @return POSIX errno value. Unknown Windows errors map conservatively to + * EINVAL so callers still fail through the normal POSIX path. + */ +int px4_win_error_to_errno(DWORD err); + +/** + * @brief Translate a Winsock error code into a POSIX errno value. + * + * @param err Value returned by WSAGetLastError(). + * @return POSIX errno value for socket/resolver callers. + */ +int px4_wsa_error_to_errno(int err); + +/** + * @brief Return a stable string for a legacy resolver h_errno code. + * + * @param err HOST_NOT_FOUND, TRY_AGAIN, NO_RECOVERY, NO_DATA, or a Winsock + * resolver error. + * @return Static, human-readable error string. + */ +const char *px4_hstrerror_text(int err); diff --git a/platforms/posix/src/px4/windows/posix/fs/flock.cpp b/platforms/posix/src/px4/windows/posix/fs/flock.cpp new file mode 100644 index 0000000000..bbaaf94d23 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/fs/flock.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file flock.cpp + * + * POSIX sys/file.h: flock(2) via LockFileEx / UnlockFileEx. + * Used by px4_daemon server-singleton enforcement. + * + * PX4 only needs advisory whole-file locks here. Windows exposes byte-range + * locks, so the implementation locks the maximum byte range from offset 0 and + * maps ERROR_LOCK_VIOLATION back to the POSIX non-blocking-lock error. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * sys/file: flock via LockFileEx. + * -------------------------------------------------------------------------- */ +extern "C" int flock(int fd, int operation) +{ + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { errno = EBADF; return -1; } + + if (operation & LOCK_UN) { + /* Match the whole-file range used for LOCK_SH/LOCK_EX below. */ + OVERLAPPED ov{}; ov.Offset = 0; ov.OffsetHigh = 0; + return UnlockFileEx(h, 0, MAXDWORD, MAXDWORD, &ov) ? 0 : -1; + } + + DWORD flags = 0; + if (operation & LOCK_EX) { flags |= LOCKFILE_EXCLUSIVE_LOCK; } + if (operation & LOCK_NB) { flags |= LOCKFILE_FAIL_IMMEDIATELY; } + + OVERLAPPED ov{}; ov.Offset = 0; ov.OffsetHigh = 0; + if (LockFileEx(h, flags, 0, MAXDWORD, MAXDWORD, &ov)) { return 0; } + + errno = (GetLastError() == ERROR_LOCK_VIOLATION) ? EWOULDBLOCK : EIO; + return -1; +} diff --git a/platforms/posix/src/px4/windows/posix/fs/mman.cpp b/platforms/posix/src/px4/windows/posix/fs/mman.cpp new file mode 100644 index 0000000000..cc2e062425 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/fs/mman.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file mman.cpp + * + * POSIX sys/mman.h: mmap / munmap backed by CreateFileMapping + + * MapViewOfFile. Used by the uORB shared ring buffers. + * + * There are two paths: anonymous mappings use VirtualAlloc, and file-backed + * mappings use CreateFileMapping/MapViewOfFile. munmap() tries both release + * mechanisms because the POSIX API does not carry enough state to tell us + * which Windows allocator created the address. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * sys/mman: MapViewOfFile-backed mmap. + * -------------------------------------------------------------------------- */ +static DWORD mman_page_protect(int prot) +{ + if ((prot & (PROT_READ | PROT_WRITE)) == (PROT_READ | PROT_WRITE)) { return PAGE_READWRITE; } + if (prot & PROT_WRITE) { return PAGE_READWRITE; } + if (prot & PROT_READ) { return PAGE_READONLY; } + return PAGE_NOACCESS; +} + +extern "C" void *mmap(void *, size_t length, int prot, int flags, int fd, off_t offset) +{ + const bool anon = (flags & (MAP_ANON | MAP_ANONYMOUS)) != 0; + + if (anon || fd < 0) { + void *p = VirtualAlloc(nullptr, length, MEM_COMMIT | MEM_RESERVE, + mman_page_protect(prot)); + return p ? p : MAP_FAILED; + } + + HANDLE h = (HANDLE)_get_osfhandle(fd); + if (h == INVALID_HANDLE_VALUE) { errno = EBADF; return MAP_FAILED; } + + DWORD map_access = (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ; + DWORD create_protect = (prot & PROT_WRITE) ? PAGE_READWRITE : PAGE_READONLY; + + HANDLE mapping = CreateFileMappingA(h, nullptr, create_protect, 0, 0, nullptr); + if (!mapping) { errno = EACCES; return MAP_FAILED; } + + /* The mapping handle can be closed after MapViewOfFile succeeds; the view + * itself holds the kernel reference until UnmapViewOfFile. */ + LARGE_INTEGER off; off.QuadPart = offset; + void *ptr = MapViewOfFile(mapping, map_access, off.HighPart, off.LowPart, length); + CloseHandle(mapping); /* view keeps a ref */ + + return ptr ? ptr : MAP_FAILED; +} + +extern "C" int munmap(void *addr, size_t length) +{ + if (!addr) { errno = EINVAL; return -1; } + + /* Try UnmapViewOfFile first (file-backed); if that fails, try + * VirtualFree for anonymous mappings. Both are idempotent enough + * that trying the wrong one is harmless. */ + if (UnmapViewOfFile(addr)) { return 0; } + if (VirtualFree(addr, 0, MEM_RELEASE)) { return 0; } + (void)length; + errno = EINVAL; + return -1; +} + +extern "C" int msync(void *addr, size_t length, int) { return FlushViewOfFile(addr, length) ? 0 : -1; } +extern "C" int mprotect(void *addr, size_t length, int prot) +{ + DWORD old; + return VirtualProtect(addr, length, mman_page_protect(prot), &old) ? 0 : -1; +} +extern "C" int mlock(const void *addr, size_t length) { return VirtualLock((LPVOID)addr, length) ? 0 : -1; } +extern "C" int munlock(const void *addr, size_t length) { return VirtualUnlock((LPVOID)addr, length) ? 0 : -1; } +extern "C" int mlockall(int) { return 0; } +extern "C" int munlockall(void) { return 0; } diff --git a/platforms/posix/src/px4/windows/posix/lib/dlfcn.cpp b/platforms/posix/src/px4/windows/posix/lib/dlfcn.cpp new file mode 100644 index 0000000000..c2db10bbb0 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/lib/dlfcn.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file dlfcn.cpp + * + * POSIX dlfcn(3): dlopen / dlsym / dlclose / dlerror backed by + * Win32 LoadLibraryA / GetProcAddress / FreeLibrary. + * + * Windows has no `RTLD_*` resolution modes that match ELF. The flag argument + * is intentionally ignored; PX4 uses this for optional runtime lookups, where + * loading by DLL name and resolving an exported symbol is enough. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * dlfcn: LoadLibrary-backed. + * -------------------------------------------------------------------------- */ +static thread_local char _dl_last_error[256] = {}; + +extern "C" void *dlopen(const char *filename, int /*flag*/) +{ + /* POSIX allows dlopen(NULL, ...) to reference the main program. On Windows + * the equivalent is the current process module. */ + HMODULE h = filename ? LoadLibraryA(filename) : GetModuleHandleA(nullptr); + if (!h) { snprintf(_dl_last_error, sizeof(_dl_last_error), "LoadLibrary(%s) failed: 0x%lx", filename ? filename : "(self)", (unsigned long)GetLastError()); } + return (void *)h; +} +extern "C" int dlclose(void *handle) +{ + if (!handle) { return 0; } + return FreeLibrary((HMODULE)handle) ? 0 : -1; +} +extern "C" void *dlsym(void *handle, const char *symbol) +{ + FARPROC p = GetProcAddress(handle ? (HMODULE)handle : GetModuleHandleA(nullptr), symbol); + if (!p) { snprintf(_dl_last_error, sizeof(_dl_last_error), "GetProcAddress(%s) failed: 0x%lx", symbol, (unsigned long)GetLastError()); } + return (void *)p; +} +extern "C" char *dlerror(void) +{ + if (_dl_last_error[0] == 0) { return nullptr; } + static thread_local char out[sizeof(_dl_last_error)]; + memcpy(out, _dl_last_error, sizeof(out)); + _dl_last_error[0] = 0; + return out; +} +extern "C" int dladdr(const void *addr, Dl_info *info) +{ + if (!addr || !info) { + errno = EINVAL; + return 0; + } + + MEMORY_BASIC_INFORMATION mbi {}; + + if (VirtualQuery(addr, &mbi, sizeof(mbi)) == 0 || !mbi.AllocationBase) { + return 0; + } + + HMODULE module = (HMODULE)mbi.AllocationBase; + static thread_local char module_name[MAX_PATH]; + + /* dladdr() on ELF can usually report the nearest symbol too. Windows does + * not expose that cheaply without walking debug data, and PX4 only needs + * the containing module, so dli_sname/dli_saddr stay null. */ + DWORD len = GetModuleFileNameA(module, module_name, sizeof(module_name)); + + if (len == 0 || len >= sizeof(module_name)) { + module_name[0] = '\0'; + } + + info->dli_fname = module_name[0] ? module_name : nullptr; + info->dli_fbase = mbi.AllocationBase; + info->dli_sname = nullptr; + info->dli_saddr = nullptr; + return 1; +} + +extern "C" void *dlvsym(void *handle, const char *symbol, const char *version) +{ + (void)version; + return dlsym(handle, symbol); +} diff --git a/platforms/posix/src/px4/windows/posix/net/if_query.cpp b/platforms/posix/src/px4/windows/posix/net/if_query.cpp new file mode 100644 index 0000000000..f1f105773a --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/net/if_query.cpp @@ -0,0 +1,101 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file if_query.cpp + * + * net/if.h surface: if_nameindex / if_freenameindex backed by + * Win32 GetAdaptersAddresses. MAVLink uses this to build its + * default network-discovery list. + * + * The returned POSIX array owns duplicated UTF-8 adapter names. Keep that + * allocation shape in sync with if_freenameindex(); callers expect one malloc + * family allocation per name plus one array allocation. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * net/if.h surface. PX4's MAVLink module enumerates interfaces via + * if_nameindex() to build its default network discovery list. Windows + * provides the same information through GetAdaptersAddresses(). We + * stuff the adapter's friendly name into POSIX-style entries and + * return them through a heap-allocated array that if_freenameindex() + * releases. + * -------------------------------------------------------------------------- */ +/* `if_nametoindex` and `if_indextoname` are provided by MinGW's + * / iphlpapi; no need to reimplement. We only fill in + * the POSIX `if_nameindex` / `if_freenameindex` helpers that MinGW + * omits. */ +extern "C" struct if_nameindex *if_nameindex(void) +{ + ULONG flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | + GAA_FLAG_SKIP_DNS_SERVER; + ULONG size = 0; + GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, nullptr, &size); + if (size == 0) { errno = ENODEV; return nullptr; } + + IP_ADAPTER_ADDRESSES *adapters = (IP_ADAPTER_ADDRESSES *)malloc(size); + if (!adapters) { errno = ENOMEM; return nullptr; } + if (GetAdaptersAddresses(AF_UNSPEC, flags, nullptr, adapters, &size) != NO_ERROR) { + free(adapters); + errno = ENODEV; + return nullptr; + } + + unsigned int count = 0; + for (IP_ADAPTER_ADDRESSES *a = adapters; a; a = a->Next) { count++; } + + struct if_nameindex *result = (struct if_nameindex *)calloc(count + 1, sizeof(*result)); + if (!result) { free(adapters); errno = ENOMEM; return nullptr; } + + unsigned int i = 0; + for (IP_ADAPTER_ADDRESSES *a = adapters; a; a = a->Next, i++) { + char name[IF_NAMESIZE] = {0}; + /* FriendlyName is UTF-16. POSIX callers get an IF_NAMESIZE-bounded + * UTF-8 string, which is what MAVLink logs and compares. */ + WideCharToMultiByte(CP_UTF8, 0, a->FriendlyName, -1, name, IF_NAMESIZE, nullptr, nullptr); + result[i].if_index = a->IfIndex; + result[i].if_name = _strdup(name); + } + free(adapters); + return result; +} + +extern "C" void if_freenameindex(struct if_nameindex *ptr) +{ + if (!ptr) { return; } + for (struct if_nameindex *p = ptr; p->if_name; p++) { + free(p->if_name); + } + free(ptr); +} diff --git a/platforms/posix/src/px4/windows/posix/net/resolver.cpp b/platforms/posix/src/px4/windows/posix/net/resolver.cpp new file mode 100644 index 0000000000..0a762f3573 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/net/resolver.cpp @@ -0,0 +1,685 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file resolver.cpp + * + * Name- and address-lookup surface (inet_aton / inet_netof / + * gethostbyname / getprotobyname / getservbyname / ...). MinGW + * ships the BSD socket types but not the libc resolver DB + * frontends, so we parse /etc/hosts / /etc/networks / + * /etc/protocols / /etc/services ourselves, caching the parsed + * entries in process-local tables. + * + * The exported `hostent`, `netent`, `protoent`, and `servent` APIs are old C + * interfaces that return pointers into static storage. The structs below own + * strings in C++ containers and then finalize pointer arrays that remain valid + * until the database is cleared. + */ + +#include "px4_windows_internal.h" + +#include +#include +#include +#include +#include + + +static std::string px4_windows_etc_path(const char *filename) +{ + char system_dir[MAX_PATH] {}; + const UINT len = GetSystemDirectoryA(system_dir, MAX_PATH); + + if (len > 0 && len < MAX_PATH) { + std::string path(system_dir, len); + path += "\\drivers\\etc\\"; + path += filename; + return path; + } + + return std::string("C:\\Windows\\System32\\drivers\\etc\\") + filename; +} + +static std::vector px4_tokenize_db_line(const std::string &line) +{ + /* Windows uses the same whitespace-and-comment syntax as the BSD resolver + * files, so a tiny lexer is enough for PX4's needs. */ + const std::string body = line.substr(0, line.find('#')); + std::istringstream stream(body); + std::vector tokens; + std::string token; + + while (stream >> token) { + tokens.push_back(token); + } + + return tokens; +} + +struct PX4HostDbEntry { + std::string name; + std::vector aliases_storage; + std::vector aliases; + int addrtype = AF_UNSPEC; + int addrlen = 0; + std::array address {}; + std::vector address_list; + struct hostent host {}; + + void finalize() + { + /* hostent wants mutable `char *` arrays. Store strings first, then point + * into them after vector growth is complete. */ + aliases.clear(); + + for (std::string &alias : aliases_storage) { + aliases.push_back(alias.data()); + } + + aliases.push_back(nullptr); + address_list = {reinterpret_cast(address.data()), nullptr}; + + host.h_name = name.empty() ? nullptr : name.data(); + host.h_aliases = aliases.data(); + host.h_addrtype = (short)addrtype; + host.h_length = (short)addrlen; + host.h_addr_list = address_list.data(); + } + + bool matches_name(const char *query) const + { + if (!query) { + return false; + } + + if (_stricmp(name.c_str(), query) == 0) { + return true; + } + + for (const std::string &alias : aliases_storage) { + if (_stricmp(alias.c_str(), query) == 0) { + return true; + } + } + + return false; + } +}; + +struct PX4NetDbEntry { + std::string name; + std::vector aliases_storage; + std::vector aliases; + struct netent net {}; + + void finalize() + { + aliases.clear(); + + for (std::string &alias : aliases_storage) { + aliases.push_back(alias.data()); + } + + aliases.push_back(nullptr); + + net.n_name = name.empty() ? nullptr : name.data(); + net.n_aliases = aliases.data(); + } + + bool matches_name(const char *query) const + { + if (!query) { + return false; + } + + if (_stricmp(name.c_str(), query) == 0) { + return true; + } + + for (const std::string &alias : aliases_storage) { + if (_stricmp(alias.c_str(), query) == 0) { + return true; + } + } + + return false; + } +}; + +struct PX4ProtoDbEntry { + std::string name; + std::vector aliases_storage; + std::vector aliases; + struct protoent proto {}; + + void finalize() + { + aliases.clear(); + + for (std::string &alias : aliases_storage) { + aliases.push_back(alias.data()); + } + + aliases.push_back(nullptr); + + proto.p_name = name.empty() ? nullptr : name.data(); + proto.p_aliases = aliases.data(); + } +}; + +struct PX4ServiceDbEntry { + std::string name; + std::vector aliases_storage; + std::vector aliases; + std::string protocol; + struct servent service {}; + + void finalize() + { + aliases.clear(); + + for (std::string &alias : aliases_storage) { + aliases.push_back(alias.data()); + } + + aliases.push_back(nullptr); + + service.s_name = name.empty() ? nullptr : name.data(); + service.s_aliases = aliases.data(); + service.s_proto = protocol.empty() ? nullptr : protocol.data(); + } +}; + +template +struct PX4ResolverDb { + std::vector entries; + size_t next = 0; + bool loaded = false; + bool stay_open = false; + + void reset() + { + next = 0; + } + + void close() + { + next = 0; + + /* POSIX set*ent(stay_open=1) asks libc to keep the database cached + * across end*ent(). Honor that so repeated PX4 lookups avoid reparsing + * the Windows etc files. */ + if (!stay_open) { + entries.clear(); + loaded = false; + } + } +}; + +static PX4ResolverDb g_hosts_db; +static PX4ResolverDb g_networks_db; +static PX4ResolverDb g_protocols_db; +static PX4ResolverDb g_services_db; + +static void px4_finalize_hosts_db(PX4ResolverDb &db) +{ + for (PX4HostDbEntry &entry : db.entries) { + entry.finalize(); + } +} + +static void px4_finalize_networks_db(PX4ResolverDb &db) +{ + for (PX4NetDbEntry &entry : db.entries) { + entry.finalize(); + } +} + +static void px4_finalize_protocols_db(PX4ResolverDb &db) +{ + for (PX4ProtoDbEntry &entry : db.entries) { + entry.finalize(); + } +} + +static void px4_finalize_services_db(PX4ResolverDb &db) +{ + for (PX4ServiceDbEntry &entry : db.entries) { + entry.finalize(); + } +} + +static void px4_load_hosts_db(PX4ResolverDb &db) +{ + if (db.loaded) { + return; + } + + db.entries.clear(); + db.next = 0; + + std::ifstream file(px4_windows_etc_path("hosts")); + std::string line; + + /* hosts lines are: address canonical-name [aliases...]. */ + while (std::getline(file, line)) { + const std::vector tokens = px4_tokenize_db_line(line); + + if (tokens.size() < 2) { + continue; + } + + PX4HostDbEntry entry {}; + + if (InetPtonA(AF_INET, tokens[0].c_str(), entry.address.data()) == 1) { + entry.addrtype = AF_INET; + entry.addrlen = sizeof(struct in_addr); + + } else if (InetPtonA(AF_INET6, tokens[0].c_str(), entry.address.data()) == 1) { + entry.addrtype = AF_INET6; + entry.addrlen = sizeof(struct in6_addr); + + } else { + continue; + } + + entry.name = tokens[1]; + + for (size_t i = 2; i < tokens.size(); ++i) { + entry.aliases_storage.push_back(tokens[i]); + } + + db.entries.push_back(std::move(entry)); + } + + px4_finalize_hosts_db(db); + db.loaded = true; +} + +static void px4_load_networks_db(PX4ResolverDb &db) +{ + if (db.loaded) { + return; + } + + db.entries.clear(); + db.next = 0; + + std::ifstream file(px4_windows_etc_path("networks")); + std::string line; + + while (std::getline(file, line)) { + const std::vector tokens = px4_tokenize_db_line(line); + + if (tokens.size() < 2) { + continue; + } + + PX4NetDbEntry entry {}; + entry.name = tokens[0]; + entry.net.n_addrtype = AF_INET; + entry.net.n_net = (u_long)inet_network(tokens[1].c_str()); + + if (entry.net.n_net == INADDR_NONE) { + char *end = nullptr; + const unsigned long value = strtoul(tokens[1].c_str(), &end, 0); + + if (!end || *end != '\0') { + continue; + } + + entry.net.n_net = (u_long)value; + } + + for (size_t i = 2; i < tokens.size(); ++i) { + entry.aliases_storage.push_back(tokens[i]); + } + + db.entries.push_back(std::move(entry)); + } + + px4_finalize_networks_db(db); + db.loaded = true; +} + +static void px4_load_protocols_db(PX4ResolverDb &db) +{ + if (db.loaded) { + return; + } + + db.entries.clear(); + db.next = 0; + + std::ifstream file(px4_windows_etc_path("protocol")); + std::string line; + + while (std::getline(file, line)) { + const std::vector tokens = px4_tokenize_db_line(line); + + if (tokens.size() < 2) { + continue; + } + + char *end = nullptr; + const long number = strtol(tokens[1].c_str(), &end, 10); + + if (!end || *end != '\0') { + continue; + } + + PX4ProtoDbEntry entry {}; + entry.name = tokens[0]; + entry.proto.p_proto = (short)number; + + for (size_t i = 2; i < tokens.size(); ++i) { + entry.aliases_storage.push_back(tokens[i]); + } + + db.entries.push_back(std::move(entry)); + } + + px4_finalize_protocols_db(db); + db.loaded = true; +} + +static void px4_load_services_db(PX4ResolverDb &db) +{ + if (db.loaded) { + return; + } + + db.entries.clear(); + db.next = 0; + + std::ifstream file(px4_windows_etc_path("services")); + std::string line; + + /* services lines are: service-name port/protocol [aliases...]. */ + while (std::getline(file, line)) { + const std::vector tokens = px4_tokenize_db_line(line); + + if (tokens.size() < 2) { + continue; + } + + const size_t slash = tokens[1].find('/'); + + if (slash == std::string::npos) { + continue; + } + + char *end = nullptr; + const long port = strtol(tokens[1].substr(0, slash).c_str(), &end, 10); + + if (!end || *end != '\0') { + continue; + } + + PX4ServiceDbEntry entry {}; + entry.name = tokens[0]; + entry.protocol = tokens[1].substr(slash + 1); + entry.service.s_port = htons((u_short)port); + + for (size_t i = 2; i < tokens.size(); ++i) { + entry.aliases_storage.push_back(tokens[i]); + } + + db.entries.push_back(std::move(entry)); + } + + px4_finalize_services_db(db); + db.loaded = true; +} + +extern "C" int inet_aton(const char *cp, struct in_addr *inp) +{ + if (!cp || !inp) { + return 0; + } + + return InetPtonA(AF_INET, cp, inp) == 1 ? 1 : 0; +} + +extern "C" char *inet_ntoa_r(struct in_addr in, char *buf, size_t buflen) +{ + if (!buf || buflen < INET_ADDRSTRLEN) { + errno = ENOSPC; + return nullptr; + } + + if (InetNtopA(AF_INET, &in, buf, (DWORD)buflen) == nullptr) { + return nullptr; + } + + return buf; +} + +extern "C" in_addr_t inet_network(const char *cp) +{ + struct in_addr addr {}; + + if (!inet_aton(cp, &addr)) { + return INADDR_NONE; + } + + return ntohl(addr.s_addr); +} + +extern "C" in_addr_t inet_lnaof(struct in_addr in) +{ + const in_addr_t addr = ntohl(in.s_addr); + + if (IN_CLASSA(addr)) { + return addr & IN_CLASSA_HOST; + + } else if (IN_CLASSB(addr)) { + return addr & IN_CLASSB_HOST; + + } else if (IN_CLASSC(addr)) { + return addr & IN_CLASSC_HOST; + } + + return 0; +} + +extern "C" in_addr_t inet_netof(struct in_addr in) +{ + const in_addr_t addr = ntohl(in.s_addr); + + if (IN_CLASSA(addr)) { + return (addr & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT; + + } else if (IN_CLASSB(addr)) { + return (addr & IN_CLASSB_NET) >> IN_CLASSB_NSHIFT; + + } else if (IN_CLASSC(addr)) { + return (addr & IN_CLASSC_NET) >> IN_CLASSC_NSHIFT; + } + + return addr; +} + +extern "C" struct in_addr inet_makeaddr(in_addr_t net, in_addr_t host) +{ + in_addr_t addr = 0; + + if (net < IN_CLASSA_MAX) { + addr = (net << IN_CLASSA_NSHIFT) | (host & IN_CLASSA_HOST); + + } else if (net < IN_CLASSB_MAX) { + addr = (net << IN_CLASSB_NSHIFT) | (host & IN_CLASSB_HOST); + + } else { + addr = (net << IN_CLASSC_NSHIFT) | (host & IN_CLASSC_HOST); + } + + struct in_addr out {}; + out.s_addr = htonl(addr); + return out; +} + +extern "C" const char *hstrerror(int err) +{ + return px4_hstrerror_text(err); +} + +extern "C" void sethostent(int stay_open) +{ + g_hosts_db.stay_open = stay_open != 0; + px4_load_hosts_db(g_hosts_db); + g_hosts_db.reset(); +} + +extern "C" void endhostent(void) +{ + g_hosts_db.close(); +} + +extern "C" struct hostent *gethostent(void) +{ + px4_load_hosts_db(g_hosts_db); + + if (g_hosts_db.next >= g_hosts_db.entries.size()) { + WSASetLastError(WSANO_DATA); + errno = ENOENT; + return nullptr; + } + + return &g_hosts_db.entries[g_hosts_db.next++].host; +} + +extern "C" void setnetent(int stay_open) +{ + g_networks_db.stay_open = stay_open != 0; + px4_load_networks_db(g_networks_db); + g_networks_db.reset(); +} + +extern "C" void endnetent(void) +{ + g_networks_db.close(); +} + +extern "C" struct netent *getnetent(void) +{ + px4_load_networks_db(g_networks_db); + + if (g_networks_db.next >= g_networks_db.entries.size()) { + WSASetLastError(WSANO_DATA); + errno = ENOENT; + return nullptr; + } + + return &g_networks_db.entries[g_networks_db.next++].net; +} + +extern "C" struct netent *getnetbyname(const char *name) +{ + px4_load_networks_db(g_networks_db); + + for (PX4NetDbEntry &entry : g_networks_db.entries) { + if (entry.matches_name(name)) { + return &entry.net; + } + } + + WSASetLastError(WSANO_DATA); + errno = ENOENT; + return nullptr; +} + +extern "C" struct netent *getnetbyaddr(uint32_t net, int type) +{ + px4_load_networks_db(g_networks_db); + + for (PX4NetDbEntry &entry : g_networks_db.entries) { + if ((type == AF_UNSPEC || entry.net.n_addrtype == type) && entry.net.n_net == (u_long)net) { + return &entry.net; + } + } + + WSASetLastError(WSANO_DATA); + errno = ENOENT; + return nullptr; +} + +extern "C" void setprotoent(int stay_open) +{ + g_protocols_db.stay_open = stay_open != 0; + px4_load_protocols_db(g_protocols_db); + g_protocols_db.reset(); +} + +extern "C" void endprotoent(void) +{ + g_protocols_db.close(); +} + +extern "C" struct protoent *getprotoent(void) +{ + px4_load_protocols_db(g_protocols_db); + + if (g_protocols_db.next >= g_protocols_db.entries.size()) { + WSASetLastError(WSANO_DATA); + errno = ENOENT; + return nullptr; + } + + return &g_protocols_db.entries[g_protocols_db.next++].proto; +} + +extern "C" void setservent(int stay_open) +{ + g_services_db.stay_open = stay_open != 0; + px4_load_services_db(g_services_db); + g_services_db.reset(); +} + +extern "C" void endservent(void) +{ + g_services_db.close(); +} + +extern "C" struct servent *getservent(void) +{ + px4_load_services_db(g_services_db); + + if (g_services_db.next >= g_services_db.entries.size()) { + WSASetLastError(WSANO_DATA); + errno = ENOENT; + return nullptr; + } + + return &g_services_db.entries[g_services_db.next++].service; +} diff --git a/platforms/posix/src/px4/windows/posix/net/socket.cpp b/platforms/posix/src/px4/windows/posix/net/socket.cpp new file mode 100644 index 0000000000..d35687942d --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/net/socket.cpp @@ -0,0 +1,208 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file socket.cpp + * + * POSIX socket extras MinGW does not ship: sendmsg / recvmsg + * (scatter/gather over winsock WSAMsg) and socketpair(2) + * implemented on AF_INET loopback. + * + * Ancillary data is deliberately unsupported. PX4's SITL paths use iovecs for + * payload scatter/gather, not SCM_RIGHTS or control messages, and there is no + * direct Winsock equivalent for the Unix control-message model. + */ + +#include "px4_windows_internal.h" + +#include + +extern "C" ssize_t sendmsg(int fd, const struct msghdr *msg, int flags) +{ + if (!msg || !msg->msg_iov || msg->msg_iovlen < 0) { + errno = EINVAL; + return -1; + } + + if (msg->msg_control && msg->msg_controllen > 0) { + errno = ENOTSUP; + return -1; + } + + std::vector bufs((size_t)msg->msg_iovlen); + + for (int i = 0; i < msg->msg_iovlen; ++i) { + /* WSABUF is the Winsock spelling of POSIX iovec. The buffer lifetime is + * owned by the caller, just like sendmsg(). */ + bufs[(size_t)i].buf = static_cast(msg->msg_iov[i].iov_base); + bufs[(size_t)i].len = (ULONG)msg->msg_iov[i].iov_len; + } + + DWORD sent = 0; + const int rc = (msg->msg_name && msg->msg_namelen > 0) + ? WSASendTo((SOCKET)fd, bufs.data(), (DWORD)bufs.size(), &sent, (DWORD)flags, + (const sockaddr *)msg->msg_name, msg->msg_namelen, nullptr, nullptr) + : WSASend((SOCKET)fd, bufs.data(), (DWORD)bufs.size(), &sent, (DWORD)flags, nullptr, nullptr); + + if (rc == SOCKET_ERROR) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + return -1; + } + + return (ssize_t)sent; +} + +extern "C" ssize_t recvmsg(int fd, struct msghdr *msg, int flags) +{ + if (!msg || !msg->msg_iov || msg->msg_iovlen < 0) { + errno = EINVAL; + return -1; + } + + if (msg->msg_control && msg->msg_controllen > 0) { + errno = ENOTSUP; + return -1; + } + + std::vector bufs((size_t)msg->msg_iovlen); + + for (int i = 0; i < msg->msg_iovlen; ++i) { + bufs[(size_t)i].buf = static_cast(msg->msg_iov[i].iov_base); + bufs[(size_t)i].len = (ULONG)msg->msg_iov[i].iov_len; + } + + DWORD received = 0; + DWORD recv_flags = 0; + int namelen = (int)msg->msg_namelen; + const int rc = (msg->msg_name && msg->msg_namelen > 0) + ? WSARecvFrom((SOCKET)fd, bufs.data(), (DWORD)bufs.size(), &received, &recv_flags, + (sockaddr *)msg->msg_name, &namelen, nullptr, nullptr) + : WSARecv((SOCKET)fd, bufs.data(), (DWORD)bufs.size(), &received, &recv_flags, nullptr, nullptr); + + if (rc == SOCKET_ERROR) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + return -1; + } + + msg->msg_flags = (int)recv_flags; + msg->msg_namelen = (socklen_t)namelen; + msg->msg_controllen = 0; + return (ssize_t)received; +} + +extern "C" int socketpair(int domain, int type, int protocol, int sv[2]) +{ + if (!sv) { + errno = EINVAL; + return -1; + } + + if (type != SOCK_STREAM) { + errno = EOPNOTSUPP; + return -1; + } + + if (domain != AF_UNIX && domain != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + + sv[0] = -1; + sv[1] = -1; + + /* Windows AF_UNIX support is version-dependent and does not map cleanly to + * CRT file descriptors. A loopback TCP pair gives PX4 a bidirectional + * byte-stream pair with the same blocking/read/write behavior it uses. */ + SOCKET listener = INVALID_SOCKET; + SOCKET client = INVALID_SOCKET; + SOCKET server = INVALID_SOCKET; + + listener = socket(AF_INET, type, protocol); + + if (listener == INVALID_SOCKET) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + return -1; + } + + sockaddr_in addr {}; + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + + if (bind(listener, reinterpret_cast(&addr), sizeof(addr)) == SOCKET_ERROR) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + closesocket(listener); + return -1; + } + + if (listen(listener, 1) == SOCKET_ERROR) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + closesocket(listener); + return -1; + } + + int addr_len = sizeof(addr); + + if (getsockname(listener, reinterpret_cast(&addr), &addr_len) == SOCKET_ERROR) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + closesocket(listener); + return -1; + } + + client = socket(AF_INET, type, protocol); + + if (client == INVALID_SOCKET) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + closesocket(listener); + return -1; + } + + if (connect(client, reinterpret_cast(&addr), addr_len) == SOCKET_ERROR) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + closesocket(client); + closesocket(listener); + return -1; + } + + server = accept(listener, nullptr, nullptr); + closesocket(listener); + + if (server == INVALID_SOCKET) { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + closesocket(client); + return -1; + } + + sv[0] = (int)client; + sv[1] = (int)server; + return 0; +} diff --git a/platforms/posix/src/px4/windows/posix/net/socket_msvc.cpp b/platforms/posix/src/px4/windows/posix/net/socket_msvc.cpp new file mode 100644 index 0000000000..fd2e5b1044 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/net/socket_msvc.cpp @@ -0,0 +1,215 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file socket_msvc.cpp + * + * MSVC-compatible WinSock wrappers. GNU/MinGW builds use ld --wrap in + * socket_wrap.cpp; MSVC routes call sites through macros in sys/socket.h. + * + * This file intentionally mirrors socket_wrap.cpp's behavior without relying + * on GNU linker features. `PX4_WINDOWS_NO_SOCKET_MACROS` is defined before + * including the shim headers so the wrapper bodies can call the real Winsock + * functions instead of recursively calling themselves. + */ + +#define PX4_WINDOWS_NO_SOCKET_MACROS +#include "px4_windows_internal.h" + +#if defined(_MSC_VER) && !defined(__clang__) + +namespace +{ +static inline void set_wsa_errno() +{ + /* Winsock keeps its error code separate from POSIX errno. This is the one + * translation point for every MSVC-routed socket call below. */ + errno = px4_wsa_error_to_errno(WSAGetLastError()); +} +} + +extern "C" { + +SOCKET WSAAPI px4_windows_socket(int af, int type, int protocol) +{ + const SOCKET s = socket(af, type, protocol); + + if (s == INVALID_SOCKET) { + set_wsa_errno(); + } + + return s; +} + +int WSAAPI px4_windows_bind(SOCKET s, const struct sockaddr *name, int namelen) +{ + const int rc = bind(s, name, namelen); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_listen(SOCKET s, int backlog) +{ + const int rc = listen(s, backlog); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +SOCKET WSAAPI px4_windows_accept(SOCKET s, struct sockaddr *addr, int *addrlen) +{ + const SOCKET r = accept(s, addr, addrlen); + + if (r == INVALID_SOCKET) { + set_wsa_errno(); + } + + return r; +} + +int WSAAPI px4_windows_connect(SOCKET s, const struct sockaddr *name, int namelen) +{ + const int rc = connect(s, name, namelen); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen) +{ + const int rc = setsockopt(s, level, optname, optval, optlen); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_shutdown(SOCKET s, int how) +{ + const int rc = shutdown(s, how); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_recv(SOCKET s, char *buf, int len, int flags) +{ + const int rc = recv(s, buf, len, flags); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_send(SOCKET s, const char *buf, int len, int flags) +{ + const int rc = send(s, buf, len, flags); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) +{ + const int rc = recvfrom(s, buf, len, flags, from, fromlen); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +int WSAAPI px4_windows_sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen) +{ + const int rc = sendto(s, buf, len, flags, to, tolen); + + if (rc == SOCKET_ERROR) { + set_wsa_errno(); + } + + return rc; +} + +char *px4_windows_strerror(int e) +{ + switch (e) { + case EADDRINUSE: return const_cast("Address already in use"); + case EADDRNOTAVAIL: return const_cast("Address not available"); + case EAFNOSUPPORT: return const_cast("Address family not supported"); + case ENOTSOCK: return const_cast("Not a socket"); + case EDESTADDRREQ: return const_cast("Destination address required"); + case EMSGSIZE: return const_cast("Message too long"); + case EPROTOTYPE: return const_cast("Protocol wrong type for socket"); + case ENOPROTOOPT: return const_cast("Protocol option not available"); + case EPROTONOSUPPORT: return const_cast("Protocol not supported"); + case EOPNOTSUPP: return const_cast("Operation not supported"); + case ENETDOWN: return const_cast("Network is down"); + case ENETUNREACH: return const_cast("Network unreachable"); + case ENETRESET: return const_cast("Connection aborted by network"); + case ECONNABORTED: return const_cast("Connection aborted"); + case ECONNRESET: return const_cast("Connection reset by peer"); + case ENOBUFS: return const_cast("No buffer space available"); + case EISCONN: return const_cast("Already connected"); + case ENOTCONN: return const_cast("Not connected"); + case ETIMEDOUT: return const_cast("Connection timed out"); + case ECONNREFUSED: return const_cast("Connection refused"); + case EALREADY: return const_cast("Operation already in progress"); + case EINPROGRESS: return const_cast("Operation in progress"); + case EWOULDBLOCK: return const_cast("Resource temporarily unavailable"); + default: return strerror(e); + } +} + +} // extern "C" + +#endif // defined(_MSC_VER) && !defined(__clang__) diff --git a/platforms/posix/src/px4/windows/posix/net/socket_wrap.cpp b/platforms/posix/src/px4/windows/posix/net/socket_wrap.cpp new file mode 100644 index 0000000000..782136fe05 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/net/socket_wrap.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file socket_wrap.cpp + * + * Linker `--wrap` shims that translate WSAGetLastError into POSIX errno + * for the bare WinSock entry points PX4 calls directly (socket, bind, + * connect, listen, accept, setsockopt, shutdown, recv/send, + * recvfrom/sendto). Without these wrappers, code such as + * if (bind(fd, ...) < 0) PX4_WARN("%s", strerror(errno)); + * prints whatever stale errno some unrelated prior call (often a file + * open returning ENOENT) happened to leave behind. WinSock keeps its + * own thread-local error retrievable via WSAGetLastError() and never + * touches POSIX errno. + * + * The --wrap= flags are configured on the px4 link in + * platforms/posix/cmake/windows.cmake. This only intercepts calls that + * resolve to the bare `` symbol; without the + * `-DWINSOCK_API_LINKAGE=` global compile def in px4_impl_os.cmake, + * winsock prototypes carry __declspec(dllimport) and PX4 callers + * resolve to `__imp_` (an indirect call through the import + * table) which --wrap cannot intercept. Stripping the dllimport makes + * MinGW emit a thunk named `` in the executable; --wrap rewrites + * references to that thunk to `__wrap_`, and `__real_` + * resolves back to the original thunk. The wrappers below are the + * only place the errno translation happens. + * + * Keep this file and socket_msvc.cpp behaviorally aligned. The dispatch + * mechanism is compiler-specific, but PX4 modules should observe the same + * return values, errno values, and strerror text on MinGW and MSVC. + */ + +#include "px4_windows_internal.h" + +extern "C" { + + SOCKET WSAAPI __real_socket(int af, int type, int protocol); + int WSAAPI __real_bind(SOCKET s, const struct sockaddr *name, int namelen); + int WSAAPI __real_listen(SOCKET s, int backlog); + SOCKET WSAAPI __real_accept(SOCKET s, struct sockaddr *addr, int *addrlen); + int WSAAPI __real_connect(SOCKET s, const struct sockaddr *name, int namelen); + int WSAAPI __real_setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen); + int WSAAPI __real_shutdown(SOCKET s, int how); + int WSAAPI __real_recv(SOCKET s, char *buf, int len, int flags); + int WSAAPI __real_send(SOCKET s, const char *buf, int len, int flags); + int WSAAPI __real_recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen); + int WSAAPI __real_sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen); + + static inline void wsa_set_errno() + { + errno = px4_wsa_error_to_errno(WSAGetLastError()); + } + + SOCKET WSAAPI __wrap_socket(int af, int type, int protocol) + { + const SOCKET s = __real_socket(af, type, protocol); + + if (s == INVALID_SOCKET) { + wsa_set_errno(); + } + + return s; + } + + int WSAAPI __wrap_bind(SOCKET s, const struct sockaddr *name, int namelen) + { + const int rc = __real_bind(s, name, namelen); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_listen(SOCKET s, int backlog) + { + const int rc = __real_listen(s, backlog); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + SOCKET WSAAPI __wrap_accept(SOCKET s, struct sockaddr *addr, int *addrlen) + { + const SOCKET r = __real_accept(s, addr, addrlen); + + if (r == INVALID_SOCKET) { + wsa_set_errno(); + } + + return r; + } + + int WSAAPI __wrap_connect(SOCKET s, const struct sockaddr *name, int namelen) + { + const int rc = __real_connect(s, name, namelen); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_setsockopt(SOCKET s, int level, int optname, const char *optval, int optlen) + { + const int rc = __real_setsockopt(s, level, optname, optval, optlen); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_shutdown(SOCKET s, int how) + { + const int rc = __real_shutdown(s, how); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_recv(SOCKET s, char *buf, int len, int flags) + { + const int rc = __real_recv(s, buf, len, flags); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_send(SOCKET s, const char *buf, int len, int flags) + { + const int rc = __real_send(s, buf, len, flags); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_recvfrom(SOCKET s, char *buf, int len, int flags, struct sockaddr *from, int *fromlen) + { + const int rc = __real_recvfrom(s, buf, len, flags, from, fromlen); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + int WSAAPI __wrap_sendto(SOCKET s, const char *buf, int len, int flags, const struct sockaddr *to, int tolen) + { + const int rc = __real_sendto(s, buf, len, flags, to, tolen); + + if (rc == SOCKET_ERROR) { + wsa_set_errno(); + } + + return rc; + } + + /* MSVCRT's strerror() table only covers C89 errnos; modern socket + * codes mapped from WSAGetLastError() (EADDRINUSE, EADDRNOTAVAIL, + * EAFNOSUPPORT, ENOTSOCK, ENETDOWN, ECONNREFUSED, ETIMEDOUT, ...) + * all return "Unknown error". Fill those in here; everything else + * defers to MSVCRT. */ + char *__real_strerror(int e); + + char *__wrap_strerror(int e) + { + switch (e) { + case EADDRINUSE: return const_cast("Address already in use"); + case EADDRNOTAVAIL: return const_cast("Address not available"); + case EAFNOSUPPORT: return const_cast("Address family not supported"); + case ENOTSOCK: return const_cast("Not a socket"); + case EDESTADDRREQ: return const_cast("Destination address required"); + case EMSGSIZE: return const_cast("Message too long"); + case EPROTOTYPE: return const_cast("Protocol wrong type for socket"); + case ENOPROTOOPT: return const_cast("Protocol option not available"); + case EPROTONOSUPPORT: return const_cast("Protocol not supported"); + case EOPNOTSUPP: return const_cast("Operation not supported"); + case ENETDOWN: return const_cast("Network is down"); + case ENETUNREACH: return const_cast("Network unreachable"); + case ENETRESET: return const_cast("Connection aborted by network"); + case ECONNABORTED: return const_cast("Connection aborted"); + case ECONNRESET: return const_cast("Connection reset by peer"); + case ENOBUFS: return const_cast("No buffer space available"); + case EISCONN: return const_cast("Already connected"); + case ENOTCONN: return const_cast("Not connected"); + case ETIMEDOUT: return const_cast("Connection timed out"); + case ECONNREFUSED: return const_cast("Connection refused"); + case EALREADY: return const_cast("Operation already in progress"); + case EINPROGRESS: return const_cast("Operation in progress"); + case EWOULDBLOCK: return const_cast("Resource temporarily unavailable"); + default: return __real_strerror(e); + } + } + +} // extern "C" diff --git a/platforms/posix/src/px4/windows/posix/proc/env.cpp b/platforms/posix/src/px4/windows/posix/proc/env.cpp new file mode 100644 index 0000000000..7697900cb3 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/proc/env.cpp @@ -0,0 +1,70 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file env.cpp + * + * POSIX environment manipulation: setenv(3) and unsetenv(3), + * routed through MSVCRT _putenv_s. + * + * _putenv_s updates the process environment visible to the CRT and child + * processes launched through the CRT. That matches PX4's usage: startup + * scripts and module code exchange small process-wide settings, not per-thread + * environment snapshots. + */ + +#include "px4_windows_internal.h" + +extern "C" int setenv(const char *name, const char *value, int overwrite) +{ + if (!name || name[0] == '\0' || strchr(name, '=') != nullptr || !value) { + errno = EINVAL; + return -1; + } + + if (!overwrite && getenv(name) != nullptr) { + /* POSIX says setenv(..., overwrite=0) leaves an existing value intact. */ + return 0; + } + + return _putenv_s(name, value) == 0 ? 0 : (errno = EINVAL, -1); +} + +extern "C" int unsetenv(const char *name) +{ + if (!name || name[0] == '\0' || strchr(name, '=') != nullptr) { + errno = EINVAL; + return -1; + } + + return _putenv_s(name, "") == 0 ? 0 : (errno = EINVAL, -1); +} diff --git a/platforms/posix/src/px4/windows/posix/proc/ids.cpp b/platforms/posix/src/px4/windows/posix/proc/ids.cpp new file mode 100644 index 0000000000..ed485a3448 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/proc/ids.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file ids.cpp + * + * POSIX process-identity helpers: getppid(2), setsid(2), + * getsid(2). Windows has no process-group identity; we return + * the session PID or the current process ID as a best-effort. + * + * PX4 uses this mostly for daemon/session bookkeeping. It does not rely on + * Unix job-control semantics, so a process-wide synthetic session id is enough + * and avoids pretending Windows has a real controlling terminal model. + */ + +#include "px4_windows_internal.h" + +/* Defined in runtime/init.cpp: the session id claimed by the first + * setsid() caller, shared process-wide via InterlockedCompareExchange. */ +extern volatile LONG g_px4_session_id; + +extern "C" pid_t getppid(void) +{ + const DWORD current_pid = GetCurrentProcessId(); + DWORD parent_pid = 1; + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + + if (snapshot == INVALID_HANDLE_VALUE) { + /* POSIX getppid() never fails. Fall back to PID 1 if Windows refuses + * the process snapshot. */ + return (pid_t)parent_pid; + } + + PROCESSENTRY32 entry {}; + entry.dwSize = sizeof(entry); + + if (Process32First(snapshot, &entry)) { + do { + if (entry.th32ProcessID == current_pid) { + parent_pid = entry.th32ParentProcessID ? entry.th32ParentProcessID : 1; + break; + } + + } while (Process32Next(snapshot, &entry)); + } + + CloseHandle(snapshot); + return (pid_t)parent_pid; +} + +extern "C" pid_t setsid(void) +{ + const LONG current_pid = (LONG)GetCurrentProcessId(); + const LONG session_id = InterlockedCompareExchange(&g_px4_session_id, current_pid, 0); + return (pid_t)((session_id == 0) ? current_pid : session_id); +} + +extern "C" pid_t getsid(pid_t pid) +{ + const DWORD requested_pid = (pid == 0) ? GetCurrentProcessId() : (DWORD)pid; + HANDLE process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, FALSE, requested_pid); + + if (!process) { + errno = ESRCH; + return -1; + } + + CloseHandle(process); + + LONG session_id = InterlockedCompareExchange(&g_px4_session_id, 0, 0); + + if (session_id == 0) { + session_id = (LONG)GetCurrentProcessId(); + } + + return (pid_t)session_id; +} diff --git a/platforms/posix/src/px4/windows/posix/proc/pthread.cpp b/platforms/posix/src/px4/windows/posix/proc/pthread.cpp new file mode 100644 index 0000000000..ee31f9d2f4 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/proc/pthread.cpp @@ -0,0 +1,687 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file pthread.cpp + * + * Small pthread compatibility layer for native MSVC builds. + * + * MinGW uses winpthreads and does not compile this file. MSVC has no pthread + * library, so this implements the subset used by PX4 SITL with Win32 threads, + * critical sections, condition variables, TLS, and init-once. It is not a + * general-purpose pthreads replacement; add POSIX surface here only when PX4 + * actually needs it. + */ + +#include "px4_windows_internal.h" + +#if defined(_MSC_VER) && !defined(__clang__) + +#include + +namespace +{ + +thread_local pthread_t tls_self = 0; + +struct PX4ThreadStart { + void *(*start_routine)(void *); + void *arg; + pthread_t self; +}; + +BOOL CALLBACK init_mutex_once(PINIT_ONCE, PVOID parameter, PVOID *) +{ + /* Static pthread mutex initializers cannot run a constructor. INIT_ONCE lets + * the first lock/trylock/destroy lazily initialize the CRITICAL_SECTION. */ + pthread_mutex_t *mutex = static_cast(parameter); + InitializeCriticalSection(&mutex->critical_section); + return TRUE; +} + +BOOL CALLBACK pthread_once_callback(PINIT_ONCE, PVOID parameter, PVOID *) +{ + reinterpret_cast(parameter)(); + return TRUE; +} + +static int ensure_mutex(pthread_mutex_t *mutex) +{ + if (!mutex) { + return EINVAL; + } + + return InitOnceExecuteOnce(&mutex->once, init_mutex_once, mutex, nullptr) ? 0 : EINVAL; +} + +static HANDLE handle_for_pthread(pthread_t thread) +{ + /* pthread_self() for externally-created threads falls back to the Win32 + * thread id. Thread-description APIs require a pseudo handle for the current + * thread in that case. */ + if (thread == 0 || thread == static_cast(GetCurrentThreadId())) { + return GetCurrentThread(); + } + + return reinterpret_cast(thread); +} + +static DWORD abstime_to_timeout_ms(const timespec *abstime) +{ + if (!abstime) { + return INFINITE; + } + + timespec now {}; + clock_gettime(CLOCK_REALTIME, &now); + + /* POSIX timed waits use an absolute timestamp; Win32 condition variables + * take a relative millisecond timeout. */ + const int64_t now_ms = (static_cast(now.tv_sec) * 1000) + (now.tv_nsec / 1000000); + const int64_t target_ms = (static_cast(abstime->tv_sec) * 1000) + (abstime->tv_nsec / 1000000); + + if (target_ms <= now_ms) { + return 0; + } + + const int64_t delta = target_ms - now_ms; + return (delta > static_cast(MAXDWORD)) ? INFINITE : static_cast(delta); +} + +unsigned __stdcall thread_trampoline(void *arg) +{ + PX4ThreadStart *start = static_cast(arg); + /* Store the handle-valued pthread id before entering PX4 code so + * pthread_self() returns something joinable/comparable inside the thread. */ + tls_self = start->self; + void *(*entry)(void *) = start->start_routine; + void *entry_arg = start->arg; + delete start; + + const uintptr_t result = reinterpret_cast(entry(entry_arg)); + _endthreadex(static_cast(result)); + return static_cast(result); +} + +} + +extern "C" { + +int pthread_attr_init(pthread_attr_t *attr) +{ + if (!attr) { + return EINVAL; + } + + attr->stack_size = 0; + attr->stack_addr = nullptr; + attr->detach_state = PTHREAD_CREATE_JOINABLE; + attr->sched_policy = SCHED_OTHER; + attr->inherit_sched = PTHREAD_INHERIT_SCHED; + attr->scope = PTHREAD_SCOPE_SYSTEM; + attr->sched.sched_priority = 0; + return 0; +} + +int pthread_attr_destroy(pthread_attr_t *attr) +{ + (void)attr; + return 0; +} + +int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stack_size) +{ + if (!attr) { + return EINVAL; + } + + attr->stack_size = stack_size; + return 0; +} + +int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stack_size) +{ + if (!attr || !stack_size) { + return EINVAL; + } + + *stack_size = attr->stack_size; + return 0; +} + +int pthread_attr_setstack(pthread_attr_t *attr, void *stack_addr, size_t stack_size) +{ + if (!attr) { + return EINVAL; + } + + attr->stack_addr = stack_addr; + attr->stack_size = stack_size; + return 0; +} + +int pthread_attr_getstack(const pthread_attr_t *attr, void **stack_addr, size_t *stack_size) +{ + if (!attr || !stack_addr || !stack_size) { + return EINVAL; + } + + *stack_addr = attr->stack_addr; + *stack_size = attr->stack_size; + return 0; +} + +int pthread_attr_getschedparam(const pthread_attr_t *attr, struct sched_param *param) +{ + if (!attr || !param) { + return EINVAL; + } + + *param = attr->sched; + return 0; +} + +int pthread_attr_setschedparam(pthread_attr_t *attr, const struct sched_param *param) +{ + if (!attr || !param) { + return EINVAL; + } + + attr->sched = *param; + return 0; +} + +int pthread_attr_getschedpolicy(const pthread_attr_t *attr, int *policy) +{ + if (!attr || !policy) { + return EINVAL; + } + + *policy = attr->sched_policy; + return 0; +} + +int pthread_attr_setschedpolicy(pthread_attr_t *attr, int policy) +{ + if (!attr) { + return EINVAL; + } + + attr->sched_policy = policy; + return 0; +} + +int pthread_attr_getinheritsched(const pthread_attr_t *attr, int *inheritsched) +{ + if (!attr || !inheritsched) { + return EINVAL; + } + + *inheritsched = attr->inherit_sched; + return 0; +} + +int pthread_attr_setinheritsched(pthread_attr_t *attr, int inheritsched) +{ + if (!attr) { + return EINVAL; + } + + attr->inherit_sched = inheritsched; + return 0; +} + +int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate) +{ + if (!attr || !detachstate) { + return EINVAL; + } + + *detachstate = attr->detach_state; + return 0; +} + +int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate) +{ + if (!attr) { + return EINVAL; + } + + attr->detach_state = detachstate; + return 0; +} + +int pthread_attr_getscope(const pthread_attr_t *attr, int *scope) +{ + if (!attr || !scope) { + return EINVAL; + } + + *scope = attr->scope; + return 0; +} + +int pthread_attr_setscope(pthread_attr_t *attr, int scope) +{ + if (!attr) { + return EINVAL; + } + + attr->scope = scope; + return 0; +} + +int pthread_mutexattr_init(pthread_mutexattr_t *attr) +{ + if (!attr) { + return EINVAL; + } + + attr->type = PTHREAD_MUTEX_NORMAL; + return 0; +} + +int pthread_mutexattr_destroy(pthread_mutexattr_t *attr) +{ + (void)attr; + return 0; +} + +int pthread_mutexattr_gettype(const pthread_mutexattr_t *attr, int *type) +{ + if (!attr || !type) { + return EINVAL; + } + + *type = attr->type; + return 0; +} + +int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type) +{ + if (!attr) { + return EINVAL; + } + + attr->type = type; + return 0; +} + +int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr) +{ + if (!mutex) { + return EINVAL; + } + + mutex->once = INIT_ONCE_STATIC_INIT; + mutex->type = attr ? attr->type : PTHREAD_MUTEX_NORMAL; + return ensure_mutex(mutex); +} + +int pthread_mutex_destroy(pthread_mutex_t *mutex) +{ + if (ensure_mutex(mutex) != 0) { + return EINVAL; + } + + DeleteCriticalSection(&mutex->critical_section); + mutex->once = INIT_ONCE_STATIC_INIT; + return 0; +} + +int pthread_mutex_lock(pthread_mutex_t *mutex) +{ + if (ensure_mutex(mutex) != 0) { + return EINVAL; + } + + EnterCriticalSection(&mutex->critical_section); + return 0; +} + +int pthread_mutex_trylock(pthread_mutex_t *mutex) +{ + if (ensure_mutex(mutex) != 0) { + return EINVAL; + } + + return TryEnterCriticalSection(&mutex->critical_section) ? 0 : EBUSY; +} + +int pthread_mutex_unlock(pthread_mutex_t *mutex) +{ + if (ensure_mutex(mutex) != 0) { + return EINVAL; + } + + LeaveCriticalSection(&mutex->critical_section); + return 0; +} + +int pthread_condattr_init(pthread_condattr_t *attr) +{ + if (!attr) { + return EINVAL; + } + + attr->clock_id = CLOCK_REALTIME; + return 0; +} + +int pthread_condattr_destroy(pthread_condattr_t *attr) +{ + (void)attr; + return 0; +} + +int pthread_condattr_setclock(pthread_condattr_t *attr, clockid_t clock_id) +{ + if (!attr) { + return EINVAL; + } + + attr->clock_id = clock_id; + return 0; +} + +int pthread_cond_init(pthread_cond_t *cond, const pthread_condattr_t *attr) +{ + (void)attr; + + if (!cond) { + return EINVAL; + } + + InitializeConditionVariable(cond); + return 0; +} + +int pthread_cond_destroy(pthread_cond_t *cond) +{ + (void)cond; + return 0; +} + +int pthread_cond_wait(pthread_cond_t *cond, pthread_mutex_t *mutex) +{ + if (!cond || ensure_mutex(mutex) != 0) { + return EINVAL; + } + + return SleepConditionVariableCS(cond, &mutex->critical_section, INFINITE) ? 0 : EINVAL; +} + +int pthread_cond_timedwait(pthread_cond_t *cond, pthread_mutex_t *mutex, const struct timespec *abstime) +{ + if (!cond || ensure_mutex(mutex) != 0) { + return EINVAL; + } + + if (SleepConditionVariableCS(cond, &mutex->critical_section, abstime_to_timeout_ms(abstime))) { + return 0; + } + + return (GetLastError() == ERROR_TIMEOUT) ? ETIMEDOUT : EINVAL; +} + +int pthread_cond_signal(pthread_cond_t *cond) +{ + if (!cond) { + return EINVAL; + } + + WakeConditionVariable(cond); + return 0; +} + +int pthread_cond_broadcast(pthread_cond_t *cond) +{ + if (!cond) { + return EINVAL; + } + + WakeAllConditionVariable(cond); + return 0; +} + +int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) +{ + if (!thread || !start_routine) { + return EINVAL; + } + + PX4ThreadStart *start = new PX4ThreadStart{start_routine, arg, 0}; + unsigned thread_id = 0; + /* Create suspended so the parent can publish the final HANDLE-valued + * pthread_t before the child calls pthread_self(). */ + const uintptr_t handle = _beginthreadex(nullptr, + attr ? static_cast(attr->stack_size) : 0, + thread_trampoline, + start, + CREATE_SUSPENDED, + &thread_id); + + if (handle == 0) { + delete start; + return EAGAIN; + } + + start->self = static_cast(handle); + *thread = static_cast(handle); + + if (attr && attr->sched.sched_priority != 0) { + SetThreadPriority(reinterpret_cast(handle), attr->sched.sched_priority); + } + + ResumeThread(reinterpret_cast(handle)); + + if (attr && attr->detach_state == PTHREAD_CREATE_DETACHED) { + /* Closing the handle detaches ownership from the creator. The Windows + * thread itself keeps running until its entry function returns. */ + CloseHandle(reinterpret_cast(handle)); + } + + return 0; +} + +int pthread_join(pthread_t thread, void **value_ptr) +{ + if (thread == 0) { + return ESRCH; + } + + HANDLE handle = reinterpret_cast(thread); + + if (WaitForSingleObject(handle, INFINITE) == WAIT_FAILED) { + return ESRCH; + } + + if (value_ptr) { + DWORD exit_code = 0; + GetExitCodeThread(handle, &exit_code); + *value_ptr = reinterpret_cast(static_cast(exit_code)); + } + + CloseHandle(handle); + return 0; +} + +int pthread_detach(pthread_t thread) +{ + if (thread == 0) { + return ESRCH; + } + + CloseHandle(reinterpret_cast(thread)); + return 0; +} + +void pthread_exit(void *value_ptr) +{ + _endthreadex(static_cast(reinterpret_cast(value_ptr))); +} + +pthread_t pthread_self(void) +{ + if (tls_self != 0) { + return tls_self; + } + + return static_cast(GetCurrentThreadId()); +} + +int pthread_equal(pthread_t t1, pthread_t t2) +{ + return t1 == t2; +} + +int pthread_cancel(pthread_t thread) +{ + if (thread == 0) { + return ESRCH; + } + + /* POSIX cancellation is cooperative; TerminateThread is not. PX4 does not + * depend on cancellation cleanup handlers in SITL, so this is a last-resort + * compatibility hook for code that expects pthread_cancel to exist. */ + return TerminateThread(reinterpret_cast(thread), 0) ? 0 : ESRCH; +} + +int pthread_kill(pthread_t thread, int sig) +{ + (void)sig; + return thread == 0 ? ESRCH : 0; +} + +int pthread_key_create(pthread_key_t *key, void (*destructor)(void *)) +{ + (void)destructor; + + if (!key) { + return EINVAL; + } + + const DWORD index = TlsAlloc(); + + if (index == TLS_OUT_OF_INDEXES) { + return EAGAIN; + } + + *key = index; + return 0; +} + +int pthread_key_delete(pthread_key_t key) +{ + return TlsFree(key) ? 0 : EINVAL; +} + +int pthread_setspecific(pthread_key_t key, const void *value) +{ + return TlsSetValue(key, const_cast(value)) ? 0 : EINVAL; +} + +void *pthread_getspecific(pthread_key_t key) +{ + return TlsGetValue(key); +} + +int pthread_once(pthread_once_t *once_control, void (*init_routine)(void)) +{ + if (!once_control || !init_routine) { + return EINVAL; + } + + return InitOnceExecuteOnce(once_control, pthread_once_callback, reinterpret_cast(init_routine), nullptr) ? 0 : EINVAL; +} + +int px4_pthread_setname_np(pthread_t thread, const char *name) +{ + if (!name) { + return EINVAL; + } + + HANDLE handle = handle_for_pthread(thread); + + wchar_t wide_name[64] {}; + MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_name, static_cast(sizeof(wide_name) / sizeof(wide_name[0]))); + + typedef HRESULT(WINAPI * SetThreadDescriptionFn)(HANDLE, PCWSTR); + /* Thread descriptions exist on modern Windows. Resolve dynamically so the + * binary still starts on older hosts or Wine versions lacking the export. */ + static SetThreadDescriptionFn set_thread_description = + reinterpret_cast(GetProcAddress(GetModuleHandleA("Kernel32.dll"), "SetThreadDescription")); + + if (set_thread_description) { + set_thread_description(handle, wide_name); + } + + return 0; +} + +int px4_pthread_setname_current_np(const char *name) +{ + return px4_pthread_setname_np(0, name); +} + +int pthread_getname_np(pthread_t thread, char *name, size_t len) +{ + if (!name || len == 0) { + return EINVAL; + } + + name[0] = '\0'; + + HANDLE handle = handle_for_pthread(thread); + + typedef HRESULT(WINAPI * GetThreadDescriptionFn)(HANDLE, PWSTR *); + static GetThreadDescriptionFn get_thread_description = + reinterpret_cast(GetProcAddress(GetModuleHandleA("Kernel32.dll"), "GetThreadDescription")); + + if (!get_thread_description) { + return 0; + } + + PWSTR wide_name = nullptr; + + if (FAILED(get_thread_description(handle, &wide_name)) || !wide_name) { + return 0; + } + + WideCharToMultiByte(CP_UTF8, 0, wide_name, -1, name, static_cast(len), nullptr, nullptr); + LocalFree(wide_name); + return 0; +} + +} // extern "C" + +#endif // defined(_MSC_VER) && !defined(__clang__) diff --git a/platforms/posix/src/px4/windows/posix/proc/sched.cpp b/platforms/posix/src/px4/windows/posix/proc/sched.cpp new file mode 100644 index 0000000000..82e3e0dd01 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/proc/sched.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sched.cpp + * + * POSIX process-scheduler edges: daemon(3), kill(2), waitpid(2). + * Windows has no unified process-group concept; we approximate + * what PX4 needs using CreateProcess / GenerateConsoleCtrlEvent / + * WaitForSingleObject. + * + * This is process control, not task scheduling. PX4 tasks are handled by + * platforms/common and pthreads; this file exists for POSIX APIs that scripts + * or daemon code may call while setting up or shutting down SITL. + */ + +#include "px4_windows_internal.h" + +#include +#include + +extern "C" int daemon(int nochdir, int noclose) +{ + if (!nochdir) { + char cwd[MAX_PATH] = {}; + char root[4] = "C:\\"; + + if (GetCurrentDirectoryA(sizeof(cwd), cwd) > 1 && cwd[1] == ':') { + root[0] = cwd[0]; + } + + if (!SetCurrentDirectoryA(root)) { + errno = px4_win_error_to_errno(GetLastError()); + return -1; + } + } + + if (!noclose) { + /* POSIX daemon() detaches stdio. Redirect to NUL rather than closing the + * CRT descriptors outright; many libraries assume fd 0/1/2 stay valid. */ + const int null_fd = _open("NUL", _O_RDWR | _O_BINARY); + + if (null_fd < 0) { + errno = EIO; + return -1; + } + + _dup2(null_fd, STDIN_FILENO); + _dup2(null_fd, STDOUT_FILENO); + _dup2(null_fd, STDERR_FILENO); + + if (null_fd > STDERR_FILENO) { + _close(null_fd); + } + } + + (void)setsid(); + return 0; +} + +extern "C" int kill(pid_t pid, int sig) +{ + if (pid <= 0 || sig < 0) { + errno = EINVAL; + return -1; + } + + if ((DWORD)pid == GetCurrentProcessId()) { + if (sig == 0) { + return 0; + } + + return raise(sig) == 0 ? 0 : (errno = EINVAL, -1); + } + + const DWORD access = PROCESS_QUERY_LIMITED_INFORMATION | SYNCHRONIZE | ((sig != 0) ? PROCESS_TERMINATE : 0); + HANDLE process = OpenProcess(access, FALSE, (DWORD)pid); + + if (!process) { + errno = ESRCH; + return -1; + } + + if (sig == 0) { + CloseHandle(process); + return 0; + } + + const BOOL terminated = TerminateProcess(process, (UINT)(128 + sig)); + CloseHandle(process); + + if (!terminated) { + errno = EPERM; + return -1; + } + + return 0; +} + +extern "C" pid_t waitpid(pid_t pid, int *status, int options) +{ + if (pid <= 0) { + errno = ENOTSUP; + return -1; + } + + const int supported_options = WNOHANG | WUNTRACED | WCONTINUED; + + if ((options & ~supported_options) != 0) { + errno = EINVAL; + return -1; + } + + HANDLE process = OpenProcess(SYNCHRONIZE | PROCESS_QUERY_LIMITED_INFORMATION, FALSE, (DWORD)pid); + + if (!process) { + errno = ESRCH; + return -1; + } + + const DWORD timeout_ms = (options & WNOHANG) ? 0 : INFINITE; + const DWORD wait_rc = WaitForSingleObject(process, timeout_ms); + + if (wait_rc == WAIT_TIMEOUT) { + CloseHandle(process); + return 0; + } + + if (wait_rc != WAIT_OBJECT_0) { + CloseHandle(process); + errno = EIO; + return -1; + } + + DWORD exit_code = STILL_ACTIVE; + + if (!GetExitCodeProcess(process, &exit_code)) { + CloseHandle(process); + errno = EIO; + return -1; + } + + CloseHandle(process); + + if (exit_code == STILL_ACTIVE) { + return 0; + } + + if (status) { + /* Store a wait status that WEXITSTATUS-style macros can decode. We do + * not model signal stops/continues on Windows. */ + *status = ((int)(exit_code & 0xff)) << 8; + } + + return pid; +} diff --git a/platforms/posix/src/px4/windows/posix/proc/user.cpp b/platforms/posix/src/px4/windows/posix/proc/user.cpp new file mode 100644 index 0000000000..3fb4a70065 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/proc/user.cpp @@ -0,0 +1,262 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file user.cpp + * + * POSIX user / group database shims: pwd.h and grp.h. Synthesised + * from %USERPROFILE% / GetUserName so code that calls + * getpwuid / getpwnam works without a real /etc/passwd. + * + * The UID/GID values are stable placeholders. PX4 only needs a home directory, + * user name, and successful lookups for logging/config paths; Windows account + * SIDs are not exposed through the POSIX structs. + */ + +#include "px4_windows_internal.h" + +#include + +/* -------------------------------------------------------------------------- + * pwd/grp: synthesise an entry from %USERPROFILE% / GetUserName. + * -------------------------------------------------------------------------- */ +static thread_local char _pw_name[256]; +static thread_local char _pw_dir[MAX_PATH]; +static thread_local char _pw_shell[] = "cmd.exe"; +static thread_local char _pw_passwd[] = "x"; +static thread_local struct passwd _pw_entry; +static thread_local char _gr_name[256]; +static thread_local char _gr_passwd[] = "x"; +static thread_local char *_gr_mem[2] = {nullptr, nullptr}; +static thread_local struct group _gr_entry; + +static int append_buf_string(char **cursor, size_t *remaining, const char *src, char **dst) +{ + if (!cursor || !remaining || !dst || !src) { + errno = EINVAL; + return ERANGE; + } + + const size_t len = strlen(src) + 1; + + if (*remaining < len) { + errno = ERANGE; + return ERANGE; + } + + memcpy(*cursor, src, len); + /* The *_r APIs require all pointed-to strings to live inside the caller + * buffer. Advance a simple bump pointer through that buffer. */ + *dst = *cursor; + *cursor += len; + *remaining -= len; + return 0; +} + +static int align_buf(char **cursor, size_t *remaining, size_t alignment) +{ + /* group::gr_mem is a char** stored inside the caller buffer. Keep it aligned + * even when the supplied buffer starts at an odd address. */ + uintptr_t ptr = reinterpret_cast(*cursor); + const size_t misalignment = ptr % alignment; + + if (misalignment == 0) { + return 0; + } + + const size_t padding = alignment - misalignment; + + if (*remaining < padding) { + errno = ERANGE; + return ERANGE; + } + + *cursor += padding; + *remaining -= padding; + return 0; +} + +static struct passwd *fill_passwd() +{ + DWORD n = sizeof(_pw_name); + if (!GetUserNameA(_pw_name, &n)) { strcpy(_pw_name, "px4"); } + const char *home = getenv("USERPROFILE"); + if (!home) { home = "C:\\"; } + strncpy(_pw_dir, home, sizeof(_pw_dir) - 1); + _pw_dir[sizeof(_pw_dir) - 1] = 0; + + _pw_entry.pw_name = _pw_name; + _pw_entry.pw_passwd = _pw_passwd; + _pw_entry.pw_uid = 1000; + _pw_entry.pw_gid = 1000; + _pw_entry.pw_gecos = _pw_name; + _pw_entry.pw_dir = _pw_dir; + _pw_entry.pw_shell = _pw_shell; + return &_pw_entry; +} + +static struct group *fill_group() +{ + struct passwd *pw = fill_passwd(); + strncpy(_gr_name, pw->pw_name, sizeof(_gr_name) - 1); + _gr_name[sizeof(_gr_name) - 1] = '\0'; + _gr_mem[0] = _gr_name; + _gr_mem[1] = nullptr; + + _gr_entry.gr_name = _gr_name; + _gr_entry.gr_passwd = _gr_passwd; + _gr_entry.gr_gid = 1000; + _gr_entry.gr_mem = _gr_mem; + return &_gr_entry; +} + +static int fill_passwd_r(struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) +{ + if (!pwd || !buf || !result) { + errno = EINVAL; + return EINVAL; + } + + struct passwd *src = fill_passwd(); + char *cursor = buf; + size_t remaining = buflen; + int rc = 0; + + rc = append_buf_string(&cursor, &remaining, src->pw_name, &pwd->pw_name); + if (rc != 0) { *result = nullptr; return rc; } + rc = append_buf_string(&cursor, &remaining, src->pw_passwd, &pwd->pw_passwd); + if (rc != 0) { *result = nullptr; return rc; } + rc = append_buf_string(&cursor, &remaining, src->pw_gecos, &pwd->pw_gecos); + if (rc != 0) { *result = nullptr; return rc; } + rc = append_buf_string(&cursor, &remaining, src->pw_dir, &pwd->pw_dir); + if (rc != 0) { *result = nullptr; return rc; } + rc = append_buf_string(&cursor, &remaining, src->pw_shell, &pwd->pw_shell); + if (rc != 0) { *result = nullptr; return rc; } + + pwd->pw_uid = src->pw_uid; + pwd->pw_gid = src->pw_gid; + *result = pwd; + return 0; +} + +static int fill_group_r(struct group *grp, char *buf, size_t buflen, struct group **result) +{ + if (!grp || !buf || !result) { + errno = EINVAL; + return EINVAL; + } + + struct group *src = fill_group(); + char *cursor = buf; + size_t remaining = buflen; + int rc = 0; + + rc = align_buf(&cursor, &remaining, alignof(char *)); + if (rc != 0) { *result = nullptr; return rc; } + + if (remaining < sizeof(char *) * 2) { + errno = ERANGE; + *result = nullptr; + return ERANGE; + } + + grp->gr_mem = reinterpret_cast(cursor); + cursor += sizeof(char *) * 2; + remaining -= sizeof(char *) * 2; + + rc = append_buf_string(&cursor, &remaining, src->gr_name, &grp->gr_name); + if (rc != 0) { *result = nullptr; return rc; } + rc = append_buf_string(&cursor, &remaining, src->gr_passwd, &grp->gr_passwd); + if (rc != 0) { *result = nullptr; return rc; } + rc = append_buf_string(&cursor, &remaining, src->gr_mem[0], &grp->gr_mem[0]); + if (rc != 0) { *result = nullptr; return rc; } + + grp->gr_mem[1] = nullptr; + grp->gr_gid = src->gr_gid; + *result = grp; + return 0; +} + +extern "C" struct passwd *getpwuid(uid_t) { return fill_passwd(); } +extern "C" struct passwd *getpwnam(const char *name) +{ + struct passwd *pw = fill_passwd(); + return (!name || strcmp(name, pw->pw_name) == 0) ? pw : nullptr; +} +extern "C" int getpwuid_r(uid_t uid, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) +{ + (void)uid; + return fill_passwd_r(pwd, buf, buflen, result); +} +extern "C" int getpwnam_r(const char *name, struct passwd *pwd, char *buf, size_t buflen, struct passwd **result) +{ + struct passwd *pw = fill_passwd(); + + if (name && strcmp(name, pw->pw_name) != 0) { + *result = nullptr; + return 0; + } + + return fill_passwd_r(pwd, buf, buflen, result); +} +extern "C" struct group *getgrgid(gid_t gid) +{ + struct group *grp = fill_group(); + return (gid == grp->gr_gid) ? grp : nullptr; +} +extern "C" struct group *getgrnam(const char *name) +{ + struct group *grp = fill_group(); + return (!name || strcmp(name, grp->gr_name) == 0) ? grp : nullptr; +} +extern "C" int getgrgid_r(gid_t gid, struct group *grp, char *buf, size_t buflen, struct group **result) +{ + struct group *src = fill_group(); + + if (gid != src->gr_gid) { + *result = nullptr; + return 0; + } + + return fill_group_r(grp, buf, buflen, result); +} +extern "C" int getgrnam_r(const char *name, struct group *grp, char *buf, size_t buflen, struct group **result) +{ + struct group *src = fill_group(); + + if (name && strcmp(name, src->gr_name) != 0) { + *result = nullptr; + return 0; + } + + return fill_group_r(grp, buf, buflen, result); +} diff --git a/platforms/posix/src/px4/windows/posix/sys/errno_map.cpp b/platforms/posix/src/px4/windows/posix/sys/errno_map.cpp new file mode 100644 index 0000000000..fe541252fb --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/sys/errno_map.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file errno_map.cpp + * + * Translation tables from Win32 / Winsock error codes into POSIX + * errno values. Shared by every shim that pipes through the + * Windows API boundary. + * + * Prefer a coarse but stable errno over leaking Windows-specific error values. + * Callers usually log strerror(errno) or branch on broad categories such as + * EAGAIN, ENOENT, EACCES, and ECONNREFUSED. + */ + +#include "px4_windows_internal.h" + +int px4_win_error_to_errno(DWORD err) +{ + switch (err) { + case ERROR_FILE_NOT_FOUND: + case ERROR_PATH_NOT_FOUND: + case ERROR_INVALID_DRIVE: + case ERROR_BAD_PATHNAME: + return ENOENT; + case ERROR_ACCESS_DENIED: + case ERROR_SHARING_VIOLATION: + case ERROR_LOCK_VIOLATION: + return EACCES; + case ERROR_ALREADY_EXISTS: + case ERROR_FILE_EXISTS: + return EEXIST; + case ERROR_INVALID_HANDLE: + return EBADF; + case ERROR_NOT_ENOUGH_MEMORY: + case ERROR_OUTOFMEMORY: + return ENOMEM; + case ERROR_DIRECTORY: + case ERROR_INVALID_NAME: + case ERROR_INVALID_PARAMETER: + return EINVAL; + case ERROR_DIR_NOT_EMPTY: + return ENOTEMPTY; + case ERROR_NOT_SUPPORTED: + case ERROR_CALL_NOT_IMPLEMENTED: + return ENOTSUP; + case ERROR_BUSY: + return EBUSY; + case ERROR_DISK_FULL: + return ENOSPC; + case ERROR_PROC_NOT_FOUND: + return ESRCH; + default: + /* EIO is the safest "something at the OS boundary failed" fallback. */ + return EIO; + } +} + +int px4_wsa_error_to_errno(int err) +{ + switch (err) { + case WSAEWOULDBLOCK: return EWOULDBLOCK; + case WSAEINPROGRESS: return EINPROGRESS; + case WSAEALREADY: return EALREADY; + case WSAENOTSOCK: return ENOTSOCK; + case WSAEDESTADDRREQ: return EDESTADDRREQ; + case WSAEMSGSIZE: return EMSGSIZE; + case WSAEPROTOTYPE: return EPROTOTYPE; + case WSAENOPROTOOPT: return ENOPROTOOPT; + case WSAEPROTONOSUPPORT: return EPROTONOSUPPORT; + case WSAEOPNOTSUPP: return EOPNOTSUPP; + case WSAEAFNOSUPPORT: return EAFNOSUPPORT; + case WSAEADDRINUSE: return EADDRINUSE; + case WSAEADDRNOTAVAIL: return EADDRNOTAVAIL; + case WSAENETDOWN: return ENETDOWN; + case WSAENETUNREACH: return ENETUNREACH; + case WSAENETRESET: return ENETRESET; + case WSAECONNABORTED: return ECONNABORTED; + case WSAECONNRESET: return ECONNRESET; + case WSAENOBUFS: return ENOBUFS; + case WSAEISCONN: return EISCONN; + case WSAENOTCONN: return ENOTCONN; + case WSAETIMEDOUT: return ETIMEDOUT; + case WSAECONNREFUSED: return ECONNREFUSED; + default: return EIO; + } +} + +const char *px4_hstrerror_text(int err) +{ + switch (err) { + case HOST_NOT_FOUND: return "Unknown host"; + case TRY_AGAIN: return "Temporary failure in name resolution"; + case NO_RECOVERY: return "Non-recoverable name server error"; + case NO_DATA: return "No address associated with name"; + default: return "Resolver error"; + } +} diff --git a/platforms/posix/src/px4/windows/posix/sys/ioctl.cpp b/platforms/posix/src/px4/windows/posix/sys/ioctl.cpp new file mode 100644 index 0000000000..200401909a --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/sys/ioctl.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file ioctl.cpp + * + * POSIX ioctl(2) dispatcher. Only FIONREAD/FIONBIO on winsock + * sockets are handled in-process; everything else returns + * -ENOSYS so the caller surfaces a clean error. + * + * Do not add driver-specific ioctls here unless there is a real Windows device + * implementation behind them. Returning success for unsupported device control + * would hide broken SITL plumbing. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * ioctl: cover FIONREAD/FIONBIO on sockets. Anything else is rejected + * with -ENOSYS. SITL drivers that ioctl a character device on Windows + * are not supported by this port. + * -------------------------------------------------------------------------- */ +extern "C" int ioctl(int fd, unsigned long request, ...) +{ + va_list ap; + va_start(ap, request); + void *arg = va_arg(ap, void *); + va_end(ap); + + if (request == FIONREAD || request == FIONBIO) { + unsigned long v = arg ? *(unsigned long *)arg : 0; + /* Winsock uses ioctlsocket for the socket readiness/non-blocking calls + * that POSIX code normally sends through ioctl(). */ + int rc = ioctlsocket((SOCKET)fd, (long)request, &v); + if (rc == 0 && arg) { *(unsigned long *)arg = v; } + return rc == 0 ? 0 : -1; + } + errno = ENOSYS; + return -1; +} diff --git a/platforms/posix/src/px4/windows/posix/sys/sysconf.cpp b/platforms/posix/src/px4/windows/posix/sys/sysconf.cpp new file mode 100644 index 0000000000..23582ee9eb --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/sys/sysconf.cpp @@ -0,0 +1,290 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file sysconf.cpp + * + * POSIX sysconf(3): backed by Win32 GetSystemInfo / + * GlobalMemoryStatusEx / GetCurrentProcess queries so reported + * numbers match what winpthreads / CreateFileMapping actually do. + * + * This file also holds a few filesystem helpers declared from unistd/statfs + * shims. They live here because they are syscall-style glue rather than + * module-specific PX4 behavior. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * sysconf: just enough of POSIX sysconf for PX4. All values come from + * Win32 directly so there is no drift between what the shim reports and + * what winpthreads/CreateFileMapping actually do. + * -------------------------------------------------------------------------- */ +extern "C" int statfs(const char *path, struct statfs *buf) +{ + if (!path || !buf) { errno = EINVAL; return -1; } + memset(buf, 0, sizeof(*buf)); + + ULARGE_INTEGER free_caller, total_bytes, free_total; + if (!GetDiskFreeSpaceExA(path, &free_caller, &total_bytes, &free_total)) { + errno = ENOENT; + return -1; + } + + /* Win32 doesn't give us a block size in that call; report 4 KiB + * which is the NTFS default and Windows' virtual cluster size. */ + buf->f_bsize = 4096; + buf->f_blocks = (long)(total_bytes.QuadPart / buf->f_bsize); + buf->f_bfree = (long)(free_total.QuadPart / buf->f_bsize); + buf->f_bavail = (long)(free_caller.QuadPart / buf->f_bsize); + buf->f_namelen = 255; + return 0; +} + +/* Recursive directory copy. Used as the symlink fallback on systems + * that can't create symlinks without elevation (stock Windows without + * Developer Mode) or that don't honour CreateSymbolicLinkA semantics + * correctly (wine 6 silently returns success but produces no entry). + * Overwrites existing targets; the caller checks for existence before + * invoking symlink(). */ +static BOOL copy_dir_recursive(const char *src, const char *dst) +{ + if (!CreateDirectoryA(dst, nullptr) && GetLastError() != ERROR_ALREADY_EXISTS) { + return FALSE; + } + + char pattern[MAX_PATH]; + snprintf(pattern, sizeof(pattern), "%s\\*", src); + + WIN32_FIND_DATAA fd; + HANDLE h = FindFirstFileA(pattern, &fd); + if (h == INVALID_HANDLE_VALUE) { return TRUE; /* empty dir is fine */ } + + BOOL ok = TRUE; + do { + if (strcmp(fd.cFileName, ".") == 0 || strcmp(fd.cFileName, "..") == 0) { continue; } + + char s[MAX_PATH], d[MAX_PATH]; + snprintf(s, sizeof(s), "%s\\%s", src, fd.cFileName); + snprintf(d, sizeof(d), "%s\\%s", dst, fd.cFileName); + + if (fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { + if (!copy_dir_recursive(s, d)) { ok = FALSE; break; } + } else { + if (!CopyFileA(s, d, FALSE)) { ok = FALSE; break; } + } + } while (FindNextFileA(h, &fd)); + + FindClose(h); + return ok; +} + +extern "C" int symlink(const char *target, const char *linkpath) +{ + if (!target || !linkpath) { errno = EINVAL; return -1; } + + /* Detect whether the target is a directory so we hand the right + * flag to CreateSymbolicLinkA. Windows distinguishes file and + * directory symlinks at creation time, unlike POSIX. */ + DWORD attrs = GetFileAttributesA(target); + const bool is_dir = (attrs != INVALID_FILE_ATTRIBUTES) && (attrs & FILE_ATTRIBUTE_DIRECTORY); + + DWORD flags = 0; + if (is_dir) { flags |= 0x1; /* SYMBOLIC_LINK_FLAG_DIRECTORY */ } + /* Allow creating symlinks without admin rights when Developer Mode + * is enabled (Win10 1703+). Harmless on older Windows. */ + flags |= 0x2; /* SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE */ + + BOOL sym_ok = CreateSymbolicLinkA(linkpath, target, flags); + /* Verify the link is actually accessible. Wine's CreateSymbolicLinkA + * can report success and leave nothing behind. */ + if (sym_ok && GetFileAttributesA(linkpath) != INVALID_FILE_ATTRIBUTES) { + return 0; + } + + /* Fall back to a hard link for files and a recursive copy for + * directories. This is slower and uses real disk, but it works + * without elevation and on hosts with broken symlink support. */ + if (is_dir) { + return copy_dir_recursive(target, linkpath) ? 0 : (errno = EPERM, -1); + } + if (CreateHardLinkA(linkpath, target, nullptr)) { return 0; } + if (CopyFileA(target, linkpath, FALSE)) { return 0; } + + errno = EPERM; + return -1; +} + +extern "C" int link(const char *existing_path, const char *new_path) +{ + if (!existing_path || !new_path) { + errno = EINVAL; + return -1; + } + + if (CreateHardLinkA(new_path, existing_path, nullptr)) { + return 0; + } + + errno = px4_win_error_to_errno(GetLastError()); + return -1; +} + +extern "C" ssize_t readlink(const char *path, char *buf, size_t bufsiz) +{ + if (!path || !buf || bufsiz == 0) { + errno = EINVAL; + return -1; + } + + HANDLE h = CreateFileA(path, 0, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, + FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, + nullptr); + + if (h == INVALID_HANDLE_VALUE) { + errno = ENOENT; + return -1; + } + + char target[MAX_PATH * 4]; + DWORD len = GetFinalPathNameByHandleA(h, target, sizeof(target), FILE_NAME_NORMALIZED); + CloseHandle(h); + + if (len == 0 || len >= sizeof(target)) { + errno = EIO; + return -1; + } + + const char *normalized = target; + + /* GetFinalPathNameByHandleA returns Win32 namespace prefixes. Strip them + * before handing the path back through the POSIX readlink API. */ + if (strncmp(normalized, "\\\\?\\UNC\\", 8) == 0) { + normalized += 6; // keep leading backslash for UNC + + } else if (strncmp(normalized, "\\\\?\\", 4) == 0) { + normalized += 4; + } + + const size_t out_len = strlen(normalized); + const size_t copy_len = (out_len < bufsiz) ? out_len : bufsiz; + memcpy(buf, normalized, copy_len); + return (ssize_t)copy_len; +} + +extern "C" int truncate(const char *path, off_t length) +{ + if (!path || length < 0) { + errno = EINVAL; + return -1; + } + + HANDLE h = CreateFileA(path, GENERIC_WRITE, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, + nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, nullptr); + + if (h == INVALID_HANDLE_VALUE) { + errno = ENOENT; + return -1; + } + + LARGE_INTEGER li {}; + li.QuadPart = length; + const BOOL ok = SetFilePointerEx(h, li, nullptr, FILE_BEGIN) && SetEndOfFile(h); + CloseHandle(h); + + if (!ok) { + errno = EIO; + return -1; + } + + return 0; +} + +extern "C" int getpagesize(void) +{ + SYSTEM_INFO si {}; + GetSystemInfo(&si); + return (int)si.dwPageSize; +} + +extern "C" long sysconf(int name) +{ + /* Numeric constants match the values declared in windows_shim/unistd.h. + * Keep this switch synchronized with that header. */ + switch (name) { + case 30: /* _SC_PAGESIZE */ { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (long)si.dwPageSize; + } + case 2: /* _SC_CLK_TCK: Win32 QueryPerformanceFrequency is Hz */ + /* Use 100Hz, matching what Windows' multimedia timers resolve + * to by default. Drivers that need higher resolution use + * hrt_absolute_time() which goes through QPC directly. */ + return 100; + case 84: /* _SC_NPROCESSORS_ONLN */ { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (long)si.dwNumberOfProcessors; + } + case 83: /* _SC_NPROCESSORS_CONF */ { + SYSTEM_INFO si; + GetSystemInfo(&si); + return (long)si.dwNumberOfProcessors; + } + case 4: /* _SC_OPEN_MAX */ + return (long)_getmaxstdio(); + case 180: /* _SC_HOST_NAME_MAX */ + return (long)(MAX_COMPUTERNAME_LENGTH + 1); + case 71: /* _SC_LOGIN_NAME_MAX */ + return 256; + case 85: /* _SC_PHYS_PAGES */ + case 86: { /* _SC_AVPHYS_PAGES */ + MEMORYSTATUSEX mem {}; + mem.dwLength = sizeof(mem); + + if (!GlobalMemoryStatusEx(&mem)) { + errno = EIO; + return -1; + } + + const ULONGLONG page_size = (ULONGLONG)getpagesize(); + const ULONGLONG bytes = (name == 85) ? mem.ullTotalPhys : mem.ullAvailPhys; + return (long)(bytes / page_size); + } + } + errno = EINVAL; + return -1; +} diff --git a/platforms/posix/src/px4/windows/posix/sys/termios.cpp b/platforms/posix/src/px4/windows/posix/sys/termios.cpp new file mode 100644 index 0000000000..2a139a0ea4 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/sys/termios.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file termios.cpp + * + * POSIX termios(3) surface: stubbed. SITL drivers that reference a + * serial UART compile against this, but no Windows serial I/O is + * wired up in the base build; calls that should move bytes over a + * COM port succeed with safe defaults rather than crashing. + * + * This is intentionally not a fake serial driver. If a Windows SITL target + * needs real COM-port traffic, add a separate implementation that owns a Win32 + * HANDLE and uses SetCommState/ReadFile/WriteFile; do not grow silent behavior + * into these no-op configuration helpers. + */ + +#include "px4_windows_internal.h" + +/* -------------------------------------------------------------------------- + * termios: present a sane default attribute set but reject real I/O. No + * driver in the default SITL image is expected to move bytes over a + * COM port in this port; real serial would be layered on top of + * CreateFileA("\\\\.\\COMx", ...) + SetCommState. + * -------------------------------------------------------------------------- */ +extern "C" int tcgetattr(int, struct termios *t) +{ + if (!t) { errno = EINVAL; return -1; } + memset(t, 0, sizeof(*t)); + t->c_cflag = CS8 | CREAD | CLOCAL; + t->c_ispeed = t->c_ospeed = B115200; + return 0; +} +extern "C" int tcsetattr(int, int, const struct termios *) { return 0; } +extern "C" int tcflush(int, int) { return 0; } +extern "C" int tcdrain(int) { return 0; } +extern "C" int tcflow(int, int) { return 0; } +extern "C" int tcsendbreak(int, int) { return 0; } +extern "C" pid_t tcgetsid(int) { return (pid_t)GetCurrentProcessId(); } +extern "C" int cfsetispeed(struct termios *t, speed_t s) { if (t) { t->c_ispeed = s; } return 0; } +extern "C" int cfsetospeed(struct termios *t, speed_t s) { if (t) { t->c_ospeed = s; } return 0; } +extern "C" int cfsetspeed(struct termios *t, speed_t s) { if (t) { t->c_ispeed = t->c_ospeed = s; } return 0; } +extern "C" speed_t cfgetispeed(const struct termios *t) { return t ? t->c_ispeed : 0; } +extern "C" speed_t cfgetospeed(const struct termios *t) { return t ? t->c_ospeed : 0; } +extern "C" void cfmakeraw(struct termios *t) +{ + if (!t) { return; } + /* Match the traditional POSIX cfmakeraw bit clearing so code inspecting the + * termios struct sees the same shape it would on Linux. */ + t->c_iflag &= ~(IGNBRK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL | IXON); + t->c_oflag &= ~OPOST; + t->c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + t->c_cflag &= ~(CSIZE | PARENB); + t->c_cflag |= CS8; +} diff --git a/platforms/posix/src/px4/windows/posix/sys/time.cpp b/platforms/posix/src/px4/windows/posix/sys/time.cpp new file mode 100644 index 0000000000..feeab625c4 --- /dev/null +++ b/platforms/posix/src/px4/windows/posix/sys/time.cpp @@ -0,0 +1,139 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file time.cpp + * + * POSIX time functions missing from MSVC's CRT. + * + * MinGW already supplies these through its POSIX-ish runtime. This file is + * MSVC-only and maps POSIX clock ids onto Windows high-resolution timers: + * CLOCK_MONOTONIC uses QueryPerformanceCounter, while CLOCK_REALTIME uses + * FILETIME converted from the Windows epoch to Unix time. + */ + +#include "px4_windows_internal.h" + +#if defined(_MSC_VER) && !defined(__clang__) + +namespace +{ +static constexpr uint64_t filetime_unix_epoch_100ns = 116444736000000000ULL; +} + +extern "C" { + +int clock_gettime(clockid_t clk_id, struct timespec *tp) +{ + if (!tp) { + errno = EINVAL; + return -1; + } + + if (clk_id == CLOCK_MONOTONIC) { + /* QPC is monotonic and high resolution, but relative to an arbitrary + * boot-time counter. That is exactly what CLOCK_MONOTONIC promises. */ + LARGE_INTEGER frequency {}; + LARGE_INTEGER counter {}; + QueryPerformanceFrequency(&frequency); + QueryPerformanceCounter(&counter); + + const uint64_t seconds = static_cast(counter.QuadPart / frequency.QuadPart); + const uint64_t remainder = static_cast(counter.QuadPart % frequency.QuadPart); + tp->tv_sec = static_cast(seconds); + tp->tv_nsec = static_cast((remainder * 1000000000ULL) / static_cast(frequency.QuadPart)); + return 0; + } + + if (clk_id == CLOCK_REALTIME) { + /* FILETIME counts 100 ns ticks since 1601-01-01 UTC. Convert to the + * Unix epoch expected by timespec. */ + FILETIME ft {}; + GetSystemTimePreciseAsFileTime(&ft); + const uint64_t ticks = (static_cast(ft.dwHighDateTime) << 32) | ft.dwLowDateTime; + const uint64_t unix_100ns = ticks - filetime_unix_epoch_100ns; + tp->tv_sec = static_cast(unix_100ns / 10000000ULL); + tp->tv_nsec = static_cast((unix_100ns % 10000000ULL) * 100ULL); + return 0; + } + + errno = EINVAL; + return -1; +} + +int clock_settime(clockid_t clk_id, const struct timespec *tp) +{ + (void)clk_id; + (void)tp; + errno = ENOTSUP; + return -1; +} + +int gettimeofday(struct timeval *tv, void *tz) +{ + (void)tz; + + if (!tv) { + errno = EINVAL; + return -1; + } + + timespec ts {}; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { + return -1; + } + + tv->tv_sec = static_cast(ts.tv_sec); + tv->tv_usec = ts.tv_nsec / 1000; + return 0; +} + +int nanosleep(const struct timespec *req, struct timespec *rem) +{ + (void)rem; + + if (!req || req->tv_nsec < 0 || req->tv_nsec >= 1000000000L) { + errno = EINVAL; + return -1; + } + + const uint64_t ms = (static_cast(req->tv_sec) * 1000ULL) + + ((static_cast(req->tv_nsec) + 999999ULL) / 1000000ULL); + /* Sleep() is millisecond-granular and cannot report remaining time if it is + * interrupted, so rem is intentionally unsupported for now. */ + Sleep(ms > MAXDWORD ? MAXDWORD : static_cast(ms)); + return 0; +} + +} // extern "C" + +#endif // defined(_MSC_VER) && !defined(__clang__) diff --git a/platforms/posix/src/px4/windows/runtime/init.cpp b/platforms/posix/src/px4/windows/runtime/init.cpp new file mode 100644 index 0000000000..12790d39b4 --- /dev/null +++ b/platforms/posix/src/px4/windows/runtime/init.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** + * + * Copyright (C) 2026 PX4 Development Team. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name PX4 nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ +/** + * @file init.cpp + * + * One-time process init for the Windows POSIX-shim subsystem. + * + * Owns WSAStartup/WSACleanup, binary-mode stdio, Console Output + * Code Page selection, native Windows ANSI virtual-terminal processing, + * and the Wine /dev/tty termios restore path invoked by Ctrl+C handling + * in main.cpp. + */ + +#include "px4_windows_internal.h" + +#include + +/* -------------------------------------------------------------------------- + * One-time process-wide initialisation. + * + * WinSock requires WSAStartup before any socket call, and we want a + * UTF-8 console so PX4_INFO banner output isn't mangled. Install a + * global constructor that runs before main(). + * -------------------------------------------------------------------------- */ +/* Session id claimed by the first setsid() caller - shared process-wide + * via InterlockedCompareExchange from proc/ids.cpp. External linkage so + * the extern declaration in proc/ids.cpp resolves. */ +volatile LONG g_px4_session_id = 0; + +namespace +{ + +bool px4_is_running_under_wine() +{ + if (HMODULE ntdll = GetModuleHandleA("ntdll.dll")) { + return GetProcAddress(ntdll, "wine_get_version") != nullptr; + } + + return false; +} + +struct PX4WindowsGlobalInit { + struct SavedConsoleMode { + HANDLE handle = nullptr; + DWORD mode = 0; + bool valid = false; + }; + + struct LinuxTermios { + unsigned int c_iflag; + unsigned int c_oflag; + unsigned int c_cflag; + unsigned int c_lflag; + unsigned char c_line; + unsigned char c_cc[19]; + }; + + SavedConsoleMode saved_stdin; + SavedConsoleMode saved_stdout; + SavedConsoleMode saved_stderr; + LinuxTermios saved_host_tty {}; + bool saved_host_tty_valid = false; + bool saved_host_tty_was_cooked = false; + const bool running_under_wine = px4_is_running_under_wine(); + + static constexpr long long LX_SYS_open = 2; + static constexpr long long LX_SYS_close = 3; + static constexpr long long LX_SYS_ioctl = 16; + static constexpr long long LX_O_RDWR = 2; + static constexpr long long LX_O_NOCTTY = 0x100; + static constexpr long long LX_TCGETS = 0x5401; + static constexpr long long LX_TCSETS = 0x5402; + // Input flags + static constexpr unsigned int LX_BRKINT = 0x0002, LX_IGNPAR = 0x0004, + LX_ICRNL = 0x0100, LX_IXON = 0x0400; + // Output flags + static constexpr unsigned int LX_OPOST = 0x0001, LX_ONLCR = 0x0004; + // Control flags + static constexpr unsigned int LX_CSIZE = 0x0030, LX_CS8 = 0x0030, + LX_CREAD = 0x0080; + // Local flags + static constexpr unsigned int LX_ISIG = 0x0001, LX_ICANON = 0x0002, + LX_ECHO = 0x0008, LX_ECHOE = 0x0010, + LX_ECHOK = 0x0020, LX_ECHOCTL = 0x0200, + LX_ECHOKE = 0x0800, LX_IEXTEN = 0x8000; + + DWORD cooked_stdin_mode() const + { + DWORD mode = ENABLE_PROCESSED_INPUT | ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT; + +#ifdef ENABLE_EXTENDED_FLAGS + mode |= ENABLE_EXTENDED_FLAGS; +#endif +#ifdef ENABLE_INSERT_MODE + mode |= ENABLE_INSERT_MODE; +#endif +#ifdef ENABLE_QUICK_EDIT_MODE + mode |= ENABLE_QUICK_EDIT_MODE; +#endif + + return mode; + } + + // Inline Linux syscall helpers (x86_64 ABI). + static long long linux_syscall1(long long num, long long a) + { + long long ret; + __asm__ volatile ( + "syscall" + : "=a"(ret) + : "0"(num), "D"(a) + : "rcx", "r11", "memory" + ); + return ret; + } + + static long long linux_syscall3(long long num, long long a, long long b, long long c) + { + long long ret; + __asm__ volatile ( + "syscall" + : "=a"(ret) + : "0"(num), "D"(a), "S"(b), "d"(c) + : "rcx", "r11", "memory" + ); + return ret; + } + + static long long open_host_tty() + { + return linux_syscall3(LX_SYS_open, + reinterpret_cast("/dev/tty"), + LX_O_RDWR | LX_O_NOCTTY, 0); + } + + static bool tcget_host_tty(LinuxTermios &term) + { + long long fd = open_host_tty(); + + if (fd < 0) { + return false; + } + + const long long ret = linux_syscall3(LX_SYS_ioctl, fd, LX_TCGETS, reinterpret_cast(&term)); + (void)linux_syscall1(LX_SYS_close, fd); + return ret == 0; + } + + static bool tcset_host_tty(const LinuxTermios &term) + { + long long fd = open_host_tty(); + + if (fd < 0) { + return false; + } + + const long long ret = linux_syscall3(LX_SYS_ioctl, fd, LX_TCSETS, reinterpret_cast(&term)); + (void)linux_syscall1(LX_SYS_close, fd); + return ret == 0; + } + + static bool termios_is_interactive_cooked(const LinuxTermios &term) + { + const unsigned int required_lflag = LX_ISIG | LX_ICANON | LX_ECHO; + return (term.c_lflag & required_lflag) == required_lflag; + } + + static void make_cooked_termios(LinuxTermios &term) + { + term.c_iflag = LX_BRKINT | LX_IGNPAR | LX_ICRNL | LX_IXON; + term.c_oflag = LX_OPOST | LX_ONLCR; + term.c_cflag = (term.c_cflag & ~LX_CSIZE) | LX_CS8 | LX_CREAD; + term.c_lflag = LX_ISIG | LX_ICANON | LX_ECHO | LX_ECHOE | LX_ECHOK + | LX_ECHOCTL | LX_ECHOKE | LX_IEXTEN; + + term.c_cc[0] = 3; // VINTR Ctrl+C + term.c_cc[1] = 28; // VQUIT Ctrl-backslash + term.c_cc[2] = 127; // VERASE DEL + term.c_cc[3] = 21; // VKILL Ctrl+U + term.c_cc[4] = 4; // VEOF Ctrl+D + term.c_cc[5] = 0; // VTIME + term.c_cc[6] = 1; // VMIN + term.c_cc[8] = 17; // VSTART Ctrl+Q + term.c_cc[9] = 19; // VSTOP Ctrl+S + term.c_cc[10] = 26; // VSUSP Ctrl+Z + term.c_cc[12] = 18; // VREPRINT Ctrl+R + term.c_cc[13] = 15; // VDISCARD Ctrl+O + term.c_cc[14] = 23; // VWERASE Ctrl+W + term.c_cc[15] = 22; // VLNEXT Ctrl+V + term.c_cc[16] = 0; // VEOL2 + } + + // Restore the host Linux tty when PX4 exits under Wine. Wine 6.x does + // not reliably translate our SetConsoleMode writes back into tcsetattr() + // on the launching terminal, which can leave bash without working + // readline/history keys after Ctrl+C. + // + // Prefer the exact /dev/tty termios snapshot captured at process init. + // That preserves user-specific tty defaults instead of approximating + // them with `stty sane`. If Wine had already put the tty in raw mode + // before our constructor ran, fall back to a conservative cooked mode. + // + // MinGW ships no , so the x86_64 Linux syscall numbers and + // struct layout are spelled out above. + void restore_host_tty_via_wine_unix() + { + if (saved_host_tty_valid && saved_host_tty_was_cooked) { + (void)tcset_host_tty(saved_host_tty); + return; + } + + LinuxTermios term {}; + + if (!tcget_host_tty(term)) { + return; + } + + make_cooked_termios(term); + (void)tcset_host_tty(term); + } + + void restore_console_modes() + { + if (saved_stdin.valid) { SetConsoleMode(saved_stdin.handle, running_under_wine ? cooked_stdin_mode() : saved_stdin.mode); } + if (saved_stdout.valid) { SetConsoleMode(saved_stdout.handle, saved_stdout.mode); } + if (saved_stderr.valid) { SetConsoleMode(saved_stderr.handle, saved_stderr.mode); } + + if (running_under_wine) { + restore_host_tty_via_wine_unix(); + } + } + + PX4WindowsGlobalInit() + { + WSADATA wsaData; + if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) { + fprintf(stderr, "PX4: WSAStartup failed\n"); + } + SetConsoleOutputCP(CP_UTF8); + + // PX4 stores binary data (parameters.bson, dataman) and expects + // read/write to preserve bytes exactly. MSVCRT's default text + // mode maps CRLF<->LF, which corrupts arbitrary binary content. + // MSVCRT exposes the global default mode through the + // `__p__fmode()` accessor; setting it is equivalent to linking + // against binmode.o or compiling the whole image with -D_O_BINARY. + if (int *fmode_ptr = __p__fmode()) { *fmode_ptr = _O_BINARY; } + _setmode(_fileno(stdin), _O_BINARY); + _setmode(_fileno(stdout), _O_BINARY); + _setmode(_fileno(stderr), _O_BINARY); + + if (running_under_wine) { + saved_host_tty_valid = tcget_host_tty(saved_host_tty); + saved_host_tty_was_cooked = saved_host_tty_valid + && termios_is_interactive_cooked(saved_host_tty); + } + + // Snapshot console modes so we can restore them on exit. Under + // Wine, enabling VT on the output handles also flips the host + // Linux tty into raw mode; if we don't write the original mode + // back, the shell that launched wine is left with broken + // arrows / Ctrl+C. On real Windows the restore is a no-op but + // keeps us honest. + auto snapshot = [](DWORD which, SavedConsoleMode &slot) { + HANDLE h = GetStdHandle(which); + if (h == INVALID_HANDLE_VALUE || h == nullptr) { return; } + DWORD mode = 0; + if (!GetConsoleMode(h, &mode)) { return; } + slot.handle = h; + slot.mode = mode; + slot.valid = true; + }; + snapshot(STD_INPUT_HANDLE, saved_stdin); + snapshot(STD_OUTPUT_HANDLE, saved_stdout); + snapshot(STD_ERROR_HANDLE, saved_stderr); + + // Opt native Windows consoles into ANSI escape processing. Under Wine, + // px4_log writes ANSI-colored buffers directly to the host stdout fd + // so Wine's console renderer cannot split escape sequences. + // DISABLE_NEWLINE_AUTO_RETURN prevents the console from inserting an + // extra CR at column 80, which otherwise shows up as spurious line wraps. + const DWORD vt_flags = ENABLE_VIRTUAL_TERMINAL_PROCESSING + | DISABLE_NEWLINE_AUTO_RETURN; + + if (!running_under_wine) { + if (saved_stdout.valid) { + SetConsoleMode(saved_stdout.handle, saved_stdout.mode | vt_flags); + } + + if (saved_stderr.valid) { + SetConsoleMode(saved_stderr.handle, saved_stderr.mode | vt_flags); + } + } + } + ~PX4WindowsGlobalInit() + { + restore_console_modes(); + WSACleanup(); + } +}; +static PX4WindowsGlobalInit _px4_win_init; +} // namespace + +extern "C" void px4_windows_restore_console_modes() +{ + _px4_win_init.restore_console_modes(); +} + +extern "C" void px4_windows_discard_pending_input() +{ + HANDLE stdin_h = GetStdHandle(STD_INPUT_HANDLE); + + if (stdin_h == INVALID_HANDLE_VALUE || stdin_h == nullptr) { + return; + } + + CancelIoEx(stdin_h, nullptr); + + DWORD mode = 0; + + if (GetConsoleMode(stdin_h, &mode)) { + FlushConsoleInputBuffer(stdin_h); + return; + } + + if (GetFileType(stdin_h) == FILE_TYPE_PIPE) { + char buffer[256]; + DWORD available = 0; + + while (PeekNamedPipe(stdin_h, nullptr, 0, nullptr, &available, nullptr) && available > 0) { + DWORD read_count = 0; + const DWORD to_read = (available < sizeof(buffer)) ? available : sizeof(buffer); + + if (!ReadFile(stdin_h, buffer, to_read, &read_count, nullptr) || read_count == 0) { + break; + } + } + } +} + +extern "C" void px4_windows_release_console() +{ + _px4_win_init.restore_console_modes(); + + if (!_px4_win_init.running_under_wine) { + FreeConsole(); + } +} + +extern "C" void px4_windows_exit(int status) +{ + fflush(stdout); + fflush(stderr); + _px4_win_init.restore_console_modes(); + + if (!_px4_win_init.running_under_wine) { + FreeConsole(); + } + + if (_px4_win_init.running_under_wine) { + TerminateProcess(GetCurrentProcess(), static_cast(status)); + } + + ExitProcess(static_cast(status)); +} diff --git a/src/include/visibility.h b/src/include/visibility.h index bc38ffb8a1..6ac351709d 100644 --- a/src/include/visibility.h +++ b/src/include/visibility.h @@ -44,12 +44,24 @@ #ifdef __EXPORT # undef __EXPORT #endif -#define __EXPORT __attribute__ ((visibility ("default"))) +#if defined(_MSC_VER) && !defined(__clang__) +# define __EXPORT __declspec(dllexport) +#else +# define __EXPORT __attribute__ ((visibility ("default"))) +#endif #ifdef __PRIVATE # undef __PRIVATE #endif -#define __PRIVATE __attribute__ ((visibility ("hidden"))) +#if defined(_MSC_VER) && !defined(__clang__) +# define __PRIVATE +#else +# define __PRIVATE __attribute__ ((visibility ("hidden"))) +#endif + +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__attribute__) +# define __attribute__(x) +#endif #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { diff --git a/src/lib/cdev/CDev.hpp b/src/lib/cdev/CDev.hpp index 071ae618fe..0e8b1a05a2 100644 --- a/src/lib/cdev/CDev.hpp +++ b/src/lib/cdev/CDev.hpp @@ -146,7 +146,7 @@ public: * @param arg The ioctl argument value. * @return OK on success, or -errno otherwise. */ - virtual int ioctl(file_t *filep, int cmd, unsigned long arg) { return -ENOTTY; }; + virtual int ioctl(file_t *filep, int cmd, uintptr_t arg) { return -ENOTTY; }; /** * Perform a poll setup/teardown operation. diff --git a/src/lib/cdev/posix/cdev_platform.cpp b/src/lib/cdev/posix/cdev_platform.cpp index 0a977a1bba..e18f6dcf17 100644 --- a/src/lib/cdev/posix/cdev_platform.cpp +++ b/src/lib/cdev/posix/cdev_platform.cpp @@ -312,7 +312,7 @@ extern "C" { return ret; } - int px4_ioctl(int fd, int cmd, unsigned long arg) + int px4_ioctl(int fd, int cmd, uintptr_t arg) { PX4_DEBUG("px4_ioctl fd = %d", fd); int ret = 0; diff --git a/src/lib/drivers/led/led.cpp b/src/lib/drivers/led/led.cpp index e472e859e3..afbdc11565 100644 --- a/src/lib/drivers/led/led.cpp +++ b/src/lib/drivers/led/led.cpp @@ -64,7 +64,7 @@ public: ~LED() override = default; int init() override; - int ioctl(cdev::file_t *filp, int cmd, unsigned long arg) override; + int ioctl(cdev::file_t *filp, int cmd, uintptr_t arg) override; }; LED::LED() : CDev(LED0_DEVICE_PATH) @@ -86,7 +86,7 @@ LED::init() } int -LED::ioctl(cdev::file_t *filp, int cmd, unsigned long arg) +LED::ioctl(cdev::file_t *filp, int cmd, uintptr_t arg) { int result = OK; diff --git a/src/lib/version/version.c b/src/lib/version/version.c index 6144ca46c1..b3d9a51dc5 100644 --- a/src/lib/version/version.c +++ b/src/lib/version/version.c @@ -300,8 +300,8 @@ uint32_t px4_board_version(void) uint32_t px4_os_version(void) { -#if defined(__PX4_DARWIN) || defined(__PX4_CYGWIN) || defined(__PX4_QURT) - return 0; //TODO: implement version for Darwin, Cygwin, QuRT +#if defined(__PX4_DARWIN) || defined(__PX4_CYGWIN) || defined(__PX4_QURT) || defined(__PX4_WINDOWS) + return 0; //TODO: implement version for Darwin, Cygwin, QuRT, Windows #elif defined(__PX4_LINUX) struct utsname name; @@ -348,6 +348,8 @@ const char *px4_os_name(void) return "NuttX"; #elif defined(__PX4_CYGWIN) return "Cygwin"; +#elif defined(__PX4_WINDOWS) + return "Windows"; #else # error "px4_os_name not implemented for current OS" #endif