feat(px4): support Windows daemon and shell runtime

Make PX4 startup, daemon command handling, shell I/O, shutdown, logging, getopt, and instance state work across POSIX and native Windows runtime boundaries.

Keep command-client behavior explicit by routing shell output through shared logger paths and preserving socket protocol return markers during shutdown.
This commit is contained in:
Nuno Marques
2026-05-11 10:31:25 -07:00
parent be01eb648b
commit 3e37a74173
24 changed files with 2346 additions and 133 deletions
+14 -1
View File
@@ -2,6 +2,7 @@
#include <px4_platform_common/time.h> #include <px4_platform_common/time.h>
#include <px4_platform_common/posix.h> #include <px4_platform_common/posix.h>
#include <px4_platform_common/log.h> #include <px4_platform_common/log.h>
#include <px4_platform_common/exit.h>
#include <uORB/uORB.h> #include <uORB/uORB.h>
#include "apps.h" #include "apps.h"
@@ -43,9 +44,21 @@ void list_builtins(apps_map_type &apps)
int shutdown_main(int argc, char *argv[]) int shutdown_main(int argc, char *argv[])
{ {
// Reject any leftover positional arguments (e.g. "status", "start", "stop").
// shutdown takes no flags or subcommands -- it always tears the daemon
// down -- so silently ignoring extra args would terminate the daemon when
// the user almost certainly meant a different module (e.g.
// "<module> status"). Print usage and exit non-zero instead.
if (argc > 1) {
PX4_ERR("unrecognized argument: %s", argv[1]);
printf("Usage:\n shutdown\n");
return 1;
}
printf("Exiting NOW.\n"); printf("Exiting NOW.\n");
uorb_shutdown(); uorb_shutdown();
system_exit(0); px4_platform_exit(0);
return 0;
} }
int list_tasks_main(int argc, char *argv[]) int list_tasks_main(int argc, char *argv[])
@@ -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 <visibility.h>
#if defined(__PX4_WINDOWS)
#include <px4_windows/platform.h>
#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
@@ -123,14 +123,24 @@ __END_DECLS
#include <px4_platform_common/defines.h> #include <px4_platform_common/defines.h>
#include <drivers/drv_hrt.h> #include <drivers/drv_hrt.h>
#if defined(_MSC_VER) && !defined(__clang__)
# define __px4_log_format(_fmt, _args)
#elif defined(__PX4_WINDOWS)
# define __px4_log_format(_fmt, _args) __attribute__((format(gnu_printf, _fmt, _args)))
#else
# define __px4_log_format(_fmt, _args) __attribute__((format(printf, _fmt, _args)))
#endif
__BEGIN_DECLS __BEGIN_DECLS
__EXPORT extern const char *__px4_log_level_str[_PX4_LOG_LEVEL_PANIC + 1]; __EXPORT extern const char *__px4_log_level_str[_PX4_LOG_LEVEL_PANIC + 1];
__EXPORT void px4_log_modulename(int level, const char *moduleName, const char *fmt, ...) __EXPORT void px4_log_modulename(int level, const char *moduleName, const char *fmt, ...)
__attribute__((format(printf, 3, 4))); __px4_log_format(3, 4);
__EXPORT void px4_log_raw(int level, const char *fmt, ...) __EXPORT void px4_log_raw(int level, const char *fmt, ...)
__attribute__((format(printf, 2, 3))); __px4_log_format(2, 3);
__EXPORT void px4_log_history(FILE *out); __EXPORT void px4_log_history(FILE *out);
__EXPORT int px4_log_modulename_from_text(const char *line);
__EXPORT void px4_log_write_text(FILE *out, const char *data, size_t length);
#if __GNUC__ #if __GNUC__
// Allow empty format strings. // Allow empty format strings.
@@ -168,8 +178,9 @@ __END_DECLS
#define __px4__log_end_fmt "\n" #define __px4__log_end_fmt "\n"
#ifdef __PX4_POSIX #ifdef __PX4_POSIX
#define PX4_LOG_COLORIZED_OUTPUT //if defined and output is a tty, colorize the output according to the log level // If defined and output is a tty, colorize the output according to the log level.
#endif /* __PX4_POSIX */ #define PX4_LOG_COLORIZED_OUTPUT
#endif
/**************************************************************************** /****************************************************************************
@@ -0,0 +1,57 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#pragma once
#include <string>
namespace px4
{
// Run a POSIX shell init script (rcS, test_*_generated, etc.) to start a
// PX4 instance. The platform backend is responsible for actually executing
// the script. On POSIX this typically shells out to /bin/sh. On Windows the
// intended production path is a bundled in-process shell backend so px4.exe
// can be embedded and redistributed without an external shell dependency.
//
// script_path : absolute or cwd-relative path to the shell script
// binary_dir : directory containing px4.exe / px4-* commands; added
// to PATH so the script can invoke them unqualified
// instance : PX4 instance id, forwarded as $1 to the script
//
// Returns the script's exit status (0 on success).
int run_shell_script(const std::string &script_path,
const std::string &binary_dir,
int instance);
} // namespace px4
+16 -5
View File
@@ -38,6 +38,7 @@
#include <px4_platform_common/getopt.h> #include <px4_platform_common/getopt.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
// check if p is a valid option and if the option takes an arg // check if p is a valid option and if the option takes an arg
static char isvalidopt(char p, const char *options, int *takesarg) static char isvalidopt(char p, const char *options, int *takesarg)
@@ -63,11 +64,16 @@ static char isvalidopt(char p, const char *options, int *takesarg)
// reorder argv and put non-options at the end // reorder argv and put non-options at the end
static int reorder(int argc, char **argv, const char *options) static int reorder(int argc, char **argv, const char *options)
{ {
char *tmp_argv[argc]; char **tmp_argv = (char **)malloc(sizeof(*tmp_argv) * (size_t)argc);
char c; char c;
int idx = 1; int idx = 1;
int tmpidx = 1; int tmpidx = 1;
int takesarg; int takesarg;
int ret = 0;
if (tmp_argv == NULL) {
return 1;
}
// move the options to the front // move the options to the front
while (idx < argc && argv[idx] != 0) { while (idx < argc && argv[idx] != 0) {
@@ -75,7 +81,8 @@ static int reorder(int argc, char **argv, const char *options)
c = isvalidopt(argv[idx][1], options, &takesarg); c = isvalidopt(argv[idx][1], options, &takesarg);
if (c == '?') { if (c == '?') {
return 1; ret = 1;
goto cleanup;
} }
tmp_argv[tmpidx] = argv[idx]; tmp_argv[tmpidx] = argv[idx];
@@ -83,7 +90,8 @@ static int reorder(int argc, char **argv, const char *options)
if (takesarg) { if (takesarg) {
if (idx + 1 >= argc) { //Error: option takes an argument, but there is no more argument if (idx + 1 >= argc) { //Error: option takes an argument, but there is no more argument
return 1; ret = 1;
goto cleanup;
} }
tmp_argv[tmpidx] = argv[idx + 1]; tmp_argv[tmpidx] = argv[idx + 1];
@@ -104,7 +112,8 @@ static int reorder(int argc, char **argv, const char *options)
c = isvalidopt(argv[idx][1], options, &takesarg); c = isvalidopt(argv[idx][1], options, &takesarg);
if (c == '?') { if (c == '?') {
return c; ret = c;
goto cleanup;
} }
if (takesarg) { if (takesarg) {
@@ -124,7 +133,9 @@ static int reorder(int argc, char **argv, const char *options)
argv[idx] = tmp_argv[idx]; argv[idx] = tmp_argv[idx];
} }
return 0; cleanup:
free(tmp_argv);
return ret;
} }
// //
File diff suppressed because it is too large Load Diff
+20 -4
View File
@@ -40,6 +40,8 @@
#include <px4_platform_common/workqueue.h> #include <px4_platform_common/workqueue.h>
#include <px4_platform_common/shutdown.h> #include <px4_platform_common/shutdown.h>
#include <px4_platform_common/tasks.h> #include <px4_platform_common/tasks.h>
#include <px4_platform_common/time.h>
#include <px4_platform_common/exit.h>
#include <drivers/drv_hrt.h> #include <drivers/drv_hrt.h>
@@ -104,6 +106,20 @@ int px4_shutdown_unlock()
#if defined(CONFIG_SCHED_WORKQUEUE) || (!defined(CONFIG_BUILD_FLAT) && defined(CONFIG_LIBC_USRWORK)) #if defined(CONFIG_SCHED_WORKQUEUE) || (!defined(CONFIG_BUILD_FLAT) && defined(CONFIG_LIBC_USRWORK))
#if defined(ENABLE_LOCKSTEP_SCHEDULER) && defined(__PX4_WINDOWS)
static hrt_abstime shutdown_time_us_now()
{
timespec ts{};
system_clock_gettime(CLOCK_MONOTONIC, &ts);
return (static_cast<hrt_abstime>(ts.tv_sec) * 1000000ULL) + (static_cast<hrt_abstime>(ts.tv_nsec) / 1000ULL);
}
#else
static hrt_abstime shutdown_time_us_now()
{
return hrt_absolute_time();
}
#endif
static struct work_s shutdown_work = {}; static struct work_s shutdown_work = {};
static uint16_t shutdown_counter = 0; ///< count how many times the shutdown worker was executed static uint16_t shutdown_counter = 0; ///< count how many times the shutdown worker was executed
@@ -171,7 +187,7 @@ static void shutdown_worker(void *arg)
} }
} }
const hrt_abstime now = hrt_absolute_time(); const hrt_abstime now = shutdown_time_us_now();
const bool delay_elapsed = (now > shutdown_time_us); const bool delay_elapsed = (now > shutdown_time_us);
if (delay_elapsed && ((done && shutdown_lock_counter == 0) || (now > (shutdown_time_us + shutdown_timeout_us)))) { if (delay_elapsed && ((done && shutdown_lock_counter == 0) || (now > (shutdown_time_us + shutdown_timeout_us)))) {
@@ -206,7 +222,7 @@ static void shutdown_worker(void *arg)
#elif defined(__PX4_POSIX) #elif defined(__PX4_POSIX)
// simply exit on posix if real shutdown (poweroff) not available // simply exit on posix if real shutdown (poweroff) not available
PX4_INFO_RAW("Exiting NOW."); PX4_INFO_RAW("Exiting NOW.");
system_exit(0); px4_platform_exit(0);
#else #else
PX4_PANIC("board shutdown not available"); PX4_PANIC("board shutdown not available");
#endif #endif
@@ -239,7 +255,7 @@ int px4_reboot_request(reboot_request_t request, uint32_t delay_us)
shutdown_args |= SHUTDOWN_ARG_TO_ISP; shutdown_args |= SHUTDOWN_ARG_TO_ISP;
} }
shutdown_time_us = hrt_absolute_time(); shutdown_time_us = shutdown_time_us_now();
if (delay_us > 0) { if (delay_us > 0) {
shutdown_time_us += delay_us; shutdown_time_us += delay_us;
@@ -263,7 +279,7 @@ int px4_shutdown_request(uint32_t delay_us)
shutdown_args |= SHUTDOWN_ARG_IN_PROGRESS; shutdown_args |= SHUTDOWN_ARG_IN_PROGRESS;
shutdown_time_us = hrt_absolute_time(); shutdown_time_us = shutdown_time_us_now();
if (delay_us > 0) { if (delay_us > 0) {
shutdown_time_us += delay_us; shutdown_time_us += delay_us;
+1 -1
View File
@@ -209,7 +209,7 @@ uORB::DeviceNode::write(cdev::file_t *filp, const char *buffer, size_t buflen)
} }
int 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) { switch (cmd) {
case ORBIOCUPDATED: { case ORBIOCUPDATED: {
+1 -1
View File
@@ -114,7 +114,7 @@ public:
/** /**
* IOCTL control for the subscriber. * 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. * Method to publish a data to this node.
+3 -3
View File
@@ -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 */ /* get the advertiser handle and close the node */
orb_advert_t advertiser; 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); px4_close(fd);
if (result == PX4_ERROR) { 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. */ /* Set to false here so that if `px4_ioctl` fails to false. */
*updated = 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) 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 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; *interval /= 1000;
return ret; return ret;
} }
+3 -3
View File
@@ -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 */ /* get the advertiser handle and close the node */
orb_advert_t advertiser; 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); px4_close(fd);
if (result == PX4_ERROR) { 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. */ /* Set to false here so that if `px4_ioctl` fails to false. */
*updated = 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) 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 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; *interval /= 1000;
return ret; return ret;
} }
@@ -48,5 +48,7 @@ __BEGIN_DECLS
* @return The FILE* which represents the standard output of the current thread. * @return The FILE* which represents the standard output of the current thread.
*/ */
__EXPORT FILE *get_stdout(bool *isatty_); __EXPORT FILE *get_stdout(bool *isatty_);
__EXPORT void set_stdout_isatty_override(bool isatty_);
__EXPORT void clear_stdout_isatty_override();
__END_DECLS __END_DECLS
File diff suppressed because it is too large Load Diff
@@ -45,10 +45,17 @@
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
#include <sys/stat.h> #include <sys/stat.h>
#ifdef __PX4_WINDOWS
#include <netinet/in.h>
#include <windows.h>
#else
#include <sys/un.h> #include <sys/un.h>
#endif
#include <unistd.h> #include <unistd.h>
#include <string> #include <string>
#include <system_error>
#include <thread>
#include <px4_platform_common/log.h> #include <px4_platform_common/log.h>
#include "client.h" #include "client.h"
@@ -56,14 +63,133 @@
namespace px4_daemon namespace px4_daemon
{ {
namespace
{
#ifdef __PX4_WINDOWS
// SHUT_WR is the POSIX value of the half-close-send constant; on Winsock,
// the equivalent is SD_SEND. Use a portable alias so the relay code below is
// platform-neutral.
static constexpr int kShutWr = SD_SEND;
#else
static constexpr int kShutWr = SHUT_WR;
#endif
#ifdef __PX4_WINDOWS
bool ends_with_exe_suffix(const std::string &arg)
{
if (arg.size() < 4) {
return false;
}
const std::string suffix = arg.substr(arg.size() - 4);
return suffix == ".exe" || suffix == ".EXE"
|| suffix == ".Exe" || suffix == ".eXe"
|| suffix == ".exE" || suffix == ".EXe"
|| suffix == ".eXE" || suffix == ".ExE";
}
#endif
// Reads up to `n` bytes from this process's standard input. Returns 0 on EOF
// and -1 on error. Wraps the platform-specific stdin-read primitive so the
// forwarding loop below stays platform-neutral.
static ssize_t read_local_stdin(char *buf, size_t n)
{
#ifdef __PX4_WINDOWS
HANDLE stdin_h = GetStdHandle(STD_INPUT_HANDLE);
if (stdin_h == INVALID_HANDLE_VALUE || stdin_h == nullptr) {
return -1;
}
DWORD got = 0;
if (!ReadFile(stdin_h, buf, (DWORD)n, &got, nullptr)) {
return -1;
}
return (ssize_t)got;
#else
return read(STDIN_FILENO, buf, n);
#endif
}
// Worker that reads bytes from this process's stdin and forwards them over
// the socket so the daemon can hand them to the running module's stdin pipe.
// Stops on stdin EOF (half-closes the socket's send side, signalling EOF to
// the daemon) or on any send() error (typically the daemon already closed
// the connection because the command finished).
static void stdin_forward_loop(socket_handle_t fd)
{
char buffer[256];
while (true) {
const ssize_t bytes_read = read_local_stdin(buffer, sizeof(buffer));
if (bytes_read <= 0) {
break;
}
const char *buf = buffer;
ssize_t remaining = bytes_read;
while (remaining > 0) {
const int n_sent = send(fd, buf, (int)remaining, 0);
if (n_sent <= 0) {
// Daemon closed the connection (command finished). Done.
return;
}
buf += n_sent;
remaining -= n_sent;
}
}
// Local stdin reached EOF — half-close the send side so the daemon's
// stdin relay sees EOF on its recv() and drains the module's stdin pipe.
shutdown(fd, kShutWr);
}
} // namespace
Client::Client(int instance_id) : Client::Client(int instance_id) :
_fd(-1), _fd(invalid_socket_handle),
_instance_id(instance_id) _instance_id(instance_id)
{} {}
int int
Client::process_args(const int argc, const char **argv) Client::process_args(const int argc, const char **argv)
{ {
#ifdef __PX4_WINDOWS
const uint16_t port = get_socket_port(_instance_id);
_fd = socket(AF_INET, SOCK_STREAM, 0);
if (_fd == invalid_socket_handle) {
PX4_ERR("error creating socket");
return -1;
}
sockaddr_in addr = {};
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
addr.sin_port = htons(port);
if (connect(_fd, (sockaddr *)&addr, sizeof(addr)) < 0) {
PX4_ERR("error connecting to 127.0.0.1:%u: %s", port, strerror(errno));
return -1;
}
// No SO_RCVTIMEO: match POSIX/AF_UNIX behaviour where the client blocks
// in recv() until the daemon either replies or shuts the connection
// down. A short timeout here (the previous 5 s default) would fire
// before commands that legitimately take longer to return — most
// notably `commander takeoff`, whose lockstep wait_for_vehicle_command_reply
// can sit idle for the full 5 s acknowledgement window followed by a
// second 5 s wait for the arming reply. Letting the OS deliver an EOF
// or RST from the server is the correct termination signal here.
#else
std::string sock_path = get_socket_path(_instance_id); std::string sock_path = get_socket_path(_instance_id);
_fd = socket(AF_UNIX, SOCK_STREAM, 0); _fd = socket(AF_UNIX, SOCK_STREAM, 0);
@@ -82,6 +208,8 @@ Client::process_args(const int argc, const char **argv)
return -1; return -1;
} }
#endif
int ret = _send_cmds(argc, argv); int ret = _send_cmds(argc, argv);
if (ret != 0) { if (ret != 0) {
@@ -89,6 +217,21 @@ Client::process_args(const int argc, const char **argv)
return -3; return -3;
} }
// Forward this process's stdin to the daemon while we wait for the
// command's stdout. The thread is detached and will exit when the local
// stdin closes or the daemon hangs up the socket — process exit reaps it.
// Best-effort: commands that don't read stdin are unaffected; interactive
// ones simply won't see keystrokes if thread creation fails.
try {
std::thread(stdin_forward_loop, _fd).detach();
} catch (const std::system_error &) {
// intentionally swallowed: stdin relay is best-effort. If the OS cannot
// spawn the helper thread, interactive commands lose keystroke
// forwarding but the command itself still runs to completion.
(void)0;
}
return _listen(); return _listen();
} }
@@ -98,7 +241,20 @@ Client::_send_cmds(const int argc, const char **argv)
std::string cmd_buf; std::string cmd_buf;
for (int i = 0; i < argc; ++i) { for (int i = 0; i < argc; ++i) {
cmd_buf += argv[i]; std::string arg = argv[i];
#ifdef __PX4_WINDOWS
// Client executables are real .exe files on Windows, not POSIX
// symlinks. The pxh command namespace remains extensionless
// ("commander", "shutdown", ...), so strip only argv[0].
if (i == 0 && ends_with_exe_suffix(arg)) {
arg.resize(arg.size() - 4);
}
#endif
cmd_buf += arg;
if (i + 1 != argc) { if (i + 1 != argc) {
// TODO: Use '\0' as argument separator (and parse this server-side as well), // TODO: Use '\0' as argument separator (and parse this server-side as well),
@@ -114,10 +270,13 @@ Client::_send_cmds(const int argc, const char **argv)
const char *buf = cmd_buf.data(); const char *buf = cmd_buf.data();
while (n > 0) { while (n > 0) {
int n_sent = write(_fd, buf, n); // send() instead of write() so the same code path works with AF_UNIX
// on POSIX and AF_INET SOCKETs on Windows — write() does not operate
// on WinSock SOCKET handles.
int n_sent = send(_fd, buf, n, 0);
if (n_sent < 0) { if (n_sent < 0) {
PX4_ERR("write() failed: %s", strerror(errno)); PX4_ERR("send() failed: %s", strerror(errno));
return -1; return -1;
} }
@@ -138,10 +297,43 @@ Client::_listen()
// by another byte, we don't output it yet, until we know whether it was // by another byte, we don't output it yet, until we know whether it was
// the end of the stream or not. // the end of the stream or not.
while (true) { while (true) {
int n_read = read(_fd, buffer + n_buffer_used, sizeof buffer - n_buffer_used); int n_read = recv(_fd, buffer + n_buffer_used, sizeof buffer - n_buffer_used, 0);
if (n_read < 0) { if (n_read < 0) {
PX4_ERR("unable to read from socket"); #ifdef __PX4_WINDOWS
const int wsa_err = WSAGetLastError();
// WSAECONNRESET / WSAESHUTDOWN can fire instead of a clean
// recv() == 0 if the daemon's closesocket() races our recv()
// (the OS already delivered the bytes — TCP just abandoned the
// graceful FIN). Treat it as end-of-stream so we still pick up
// the {0, retval} sentinel that was already buffered.
if (wsa_err == WSAECONNRESET || wsa_err == WSAESHUTDOWN) {
if (n_buffer_used == 2) {
return buffer[1];
}
return -1;
}
PX4_ERR("unable to read from socket: WSA error = %d", wsa_err);
#else
// ECONNRESET / EPIPE can fire on AF_UNIX too if the daemon
// closes the socket between sending the {0, retval} sentinel
// and our recv() picking up the EOF. The bytes have already
// been delivered into our kernel buffer, so honour the
// sentinel rather than reporting a spurious socket error.
if (errno == ECONNRESET || errno == EPIPE) {
if (n_buffer_used == 2) {
return buffer[1];
}
return -1;
}
PX4_ERR("unable to read from socket: %s", strerror(errno));
#endif
return -1; return -1;
} else if (n_read == 0) { } else if (n_read == 0) {
@@ -179,8 +371,12 @@ Client::_listen()
Client::~Client() Client::~Client()
{ {
if (_fd >= 0) { if (_fd != invalid_socket_handle) {
#ifdef __PX4_WINDOWS
closesocket(_fd);
#else
close(_fd); close(_fd);
#endif
} }
} }
@@ -64,7 +64,7 @@ public:
Client(Client &&other) : _fd(other._fd), _instance_id(other._instance_id) Client(Client &&other) : _fd(other._fd), _instance_id(other._instance_id)
{ {
// Steal the fd from the moved-from client. // Steal the fd from the moved-from client.
other._fd = -1; other._fd = invalid_socket_handle;
} }
/** /**
@@ -80,7 +80,7 @@ private:
int _send_cmds(const int argc, const char **argv); int _send_cmds(const int argc, const char **argv);
int _listen(); int _listen();
int _fd; socket_handle_t _fd;
int _instance_id; ///< instance ID for running multiple instances of the px4 server int _instance_id; ///< instance ID for running multiple instances of the px4 server
}; };
@@ -49,12 +49,117 @@
#include <poll.h> #include <poll.h>
#include <fcntl.h> #include <fcntl.h>
#include <unistd.h> #include <unistd.h>
#ifdef __PX4_WINDOWS
#include <conio.h>
#include <windows.h>
#include <px4_windows/platform.h>
#endif
#include "pxh.h" #include "pxh.h"
namespace px4_daemon namespace px4_daemon
{ {
#ifdef __PX4_WINDOWS
namespace
{
bool read_windows_console_line(std::string &line)
{
HANDLE stdin_h = GetStdHandle(STD_INPUT_HANDLE);
if (stdin_h == INVALID_HANDLE_VALUE || stdin_h == nullptr) {
return false;
}
line.clear();
while (true) {
char buffer[128] {};
DWORD bytes_read = 0;
if (!ReadFile(stdin_h, buffer, sizeof(buffer), &bytes_read, nullptr)) {
return !line.empty();
}
if (bytes_read == 0) {
return !line.empty();
}
for (DWORD i = 0; i < bytes_read; ++i) {
const char c = buffer[i];
if (c == '\r') {
continue;
}
if (c == '\n') {
return true;
}
line.push_back(c);
}
}
}
void flush_pending_windows_stdin()
{
px4_windows_restore_console_modes();
px4_windows_discard_pending_input();
px4_windows_restore_console_modes();
}
bool stdin_is_windows_console()
{
HANDLE stdin_h = GetStdHandle(STD_INPUT_HANDLE);
if (stdin_h == INVALID_HANDLE_VALUE || stdin_h == nullptr) {
return false;
}
DWORD mode = 0;
return GetConsoleMode(stdin_h, &mode) != 0;
}
bool read_windows_redirected_line(std::string &line)
{
line.clear();
while (true) {
char c = '\0';
const ssize_t bytes_read = ::read(STDIN_FILENO, &c, 1);
if (bytes_read <= 0) {
return !line.empty();
}
if (c == '\r') {
continue;
}
if (c == '\n') {
return true;
}
line.push_back(c);
}
}
} // namespace
#endif
#ifdef __PX4_WINDOWS
static SOCKET poll_socket(int fd)
{
return static_cast<SOCKET>(fd);
}
#else
static int poll_socket(int fd)
{
return fd;
}
#endif
apps_map_type Pxh::_apps = {}; apps_map_type Pxh::_apps = {};
Pxh *Pxh::_instance = nullptr; Pxh *Pxh::_instance = nullptr;
@@ -126,6 +231,13 @@ int Pxh::process_line(const std::string &line, bool silently_fail)
list_builtins(_apps); list_builtins(_apps);
return 0; return 0;
} else if (command == "exit" || command == "quit") {
if (_instance) {
_instance->_should_exit = true;
}
return 0;
} else if (command.length() == 0 || command[0] == '#') { } else if (command.length() == 0 || command[0] == '#') {
// Do nothing // Do nothing
return 0; return 0;
@@ -199,7 +311,7 @@ void Pxh::run_remote_pxh(int remote_in_fd, int remote_out_fd)
// Any data from remote_in_fd will be process as shell commands when an '\n' is received // Any data from remote_in_fd will be process as shell commands when an '\n' is received
while (!_should_exit) { while (!_should_exit) {
struct pollfd fds[3] { {pipe_stderr, POLLIN}, {pipe_stdout, POLLIN}, {remote_in_fd, POLLIN}}; struct pollfd fds[3] { {poll_socket(pipe_stderr), POLLIN}, {poll_socket(pipe_stdout), POLLIN}, {poll_socket(remote_in_fd), POLLIN}};
if (poll(fds, 3, -1) == -1) { if (poll(fds, 3, -1) == -1) {
perror("Mavlink Shell Poll Error"); perror("Mavlink Shell Poll Error");
@@ -301,8 +413,61 @@ void Pxh::run_pxh()
// Only the local_terminal needed for static calls // Only the local_terminal needed for static calls
_instance = this; _instance = this;
_local_terminal = true; _local_terminal = true;
#ifndef __PX4_WINDOWS
_setup_term(); _setup_term();
#endif
#ifdef __PX4_WINDOWS
_print_prompt();
const bool stdin_is_tty = isatty(STDIN_FILENO) != 0;
const bool use_console_input = stdin_is_tty && stdin_is_windows_console();
while (!_should_exit) {
std::string line;
bool got_line = false;
if (use_console_input) {
got_line = read_windows_console_line(line);
} else {
got_line = read_windows_redirected_line(line);
}
if (!got_line) {
if (_should_exit) {
flush_pending_windows_stdin();
break;
}
clearerr(stdin);
usleep(10000);
continue;
}
if (_should_exit) {
flush_pending_windows_stdin();
break;
}
_history.try_to_add(line);
_history.reset_to_end();
if (!stdin_is_tty) {
printf("\n");
}
process_line(line, false);
if (_should_exit) {
flush_pending_windows_stdin();
break;
}
_print_prompt();
}
return;
#else
std::string mystr; std::string mystr;
int cursor_position = 0; // position of the cursor from right to left int cursor_position = 0; // position of the cursor from right to left
// (0: all the way to the right, mystr.length: all the way to the left) // (0: all the way to the right, mystr.length: all the way to the left)
@@ -310,7 +475,6 @@ void Pxh::run_pxh()
_print_prompt(); _print_prompt();
while (!_should_exit) { while (!_should_exit) {
int c = getchar(); int c = getchar();
std::string add_string; // string to add at current cursor position std::string add_string; // string to add at current cursor position
bool update_prompt = true; bool update_prompt = true;
@@ -330,6 +494,7 @@ void Pxh::run_pxh()
break; break;
case '\r': // Windows _getch() reports Enter as CR
case '\n': // user hit enter case '\n': // user hit enter
_history.try_to_add(mystr); _history.try_to_add(mystr);
_history.reset_to_end(); _history.reset_to_end();
@@ -409,6 +574,8 @@ void Pxh::run_pxh()
} }
} }
} }
#endif
} }
void Pxh::stop() void Pxh::stop()
File diff suppressed because it is too large Load Diff
@@ -115,7 +115,7 @@ private:
int _instance_id; ///< instance ID for running multiple instances of the px4 server int _instance_id; ///< instance ID for running multiple instances of the px4 server
int _fd; socket_handle_t _fd;
static void _pthread_key_destructor(void *arg); static void _pthread_key_destructor(void *arg);
@@ -57,12 +57,22 @@
using namespace px4_daemon; using namespace px4_daemon;
namespace
{
thread_local int stdout_isatty_override = -1;
}
FILE *get_stdout(bool *isatty_) FILE *get_stdout(bool *isatty_)
{ {
if (stdout_isatty_override >= 0) {
if (isatty_) {
*isatty_ = stdout_isatty_override != 0;
}
}
// If the server is not running, we are not in a thread that has been started // If the server is not running, we are not in a thread that has been started
if (!Server::is_running()) { if (!Server::is_running()) {
if (isatty_) { *isatty_ = isatty(1); } if (isatty_ && stdout_isatty_override < 0) { *isatty_ = isatty(1); }
return stdout; return stdout;
} }
@@ -74,12 +84,22 @@ FILE *get_stdout(bool *isatty_)
// have any thread specific data set and we won't have a pipe to write // have any thread specific data set and we won't have a pipe to write
// stdout to. // stdout to.
if (thread_data_ptr == nullptr || thread_data_ptr->thread_stdout == nullptr) { if (thread_data_ptr == nullptr || thread_data_ptr->thread_stdout == nullptr) {
if (isatty_) { *isatty_ = isatty(1); } if (isatty_ && stdout_isatty_override < 0) { *isatty_ = isatty(1); }
return stdout; return stdout;
} }
if (isatty_) { *isatty_ = thread_data_ptr->is_atty; } if (isatty_ && stdout_isatty_override < 0) { *isatty_ = thread_data_ptr->is_atty; }
return thread_data_ptr->thread_stdout; return thread_data_ptr->thread_stdout;
} }
void set_stdout_isatty_override(bool isatty_)
{
stdout_isatty_override = isatty_ ? 1 : 0;
}
void clear_stdout_isatty_override()
{
stdout_isatty_override = -1;
}
@@ -38,13 +38,32 @@
#include "sock_protocol.h" #include "sock_protocol.h"
#include <cstdlib>
namespace px4_daemon namespace px4_daemon
{ {
#ifdef __PX4_WINDOWS
uint16_t get_socket_port(int instance_id)
{
// Keep the local daemon control socket away from the default SITL MAVLink
// UDP ranges (for example 14580 + instance is used by px4-rc.mavlink).
// Override by setting PX4_DAEMON_PORT if the default collides with another
// app embedding PX4.
const char *override_port = std::getenv("PX4_DAEMON_PORT");
if (override_port) {
return static_cast<uint16_t>(std::atoi(override_port) + instance_id);
}
return static_cast<uint16_t>(14680 + instance_id);
}
#else
std::string get_socket_path(int instance_id) std::string get_socket_path(int instance_id)
{ {
// TODO: Use /var/run/px4/$instance/sock (or /var/run/user/$UID/... for non-root). // TODO: Use /var/run/px4/$instance/sock (or /var/run/user/$UID/... for non-root).
return "/tmp/px4-sock-" + std::to_string(instance_id); return "/tmp/px4-sock-" + std::to_string(instance_id);
} }
#endif
} // namespace px4_daemon } // namespace px4_daemon
@@ -37,11 +37,30 @@
*/ */
#pragma once #pragma once
#include <cstdint>
#include <string> #include <string>
#ifdef __PX4_WINDOWS
#include <winsock2.h>
#endif
namespace px4_daemon namespace px4_daemon
{ {
#ifdef __PX4_WINDOWS
using socket_handle_t = SOCKET;
static constexpr socket_handle_t invalid_socket_handle = INVALID_SOCKET;
// Windows: AF_INET TCP loopback. AF_UNIX was introduced in Windows 10 1803
// (WinSock2) and in principle would work, but Wine (used for SITL CI) did
// not support AF_UNIX until 7.x — 6.x still returns WSAEAFNOSUPPORT. TCP
// loopback sidesteps the portability gap without changing the rest of the
// daemon protocol.
uint16_t get_socket_port(int instance_id);
#else
using socket_handle_t = int;
static constexpr socket_handle_t invalid_socket_handle = -1;
std::string get_socket_path(int instance_id); std::string get_socket_path(int instance_id);
#endif
} // namespace px4_daemon } // namespace px4_daemon
+53 -4
View File
@@ -55,16 +55,21 @@ int px4_sem_init(px4_sem_t *s, int pshared, unsigned value)
// We do not used the process shared arg // We do not used the process shared arg
(void)pshared; (void)pshared;
s->value = value; s->value = value;
pthread_cond_init(&(s->wait), nullptr);
pthread_mutex_init(&(s->lock), nullptr); pthread_mutex_init(&(s->lock), nullptr);
#if !defined(__PX4_DARWIN) #if !defined(__PX4_DARWIN) && !defined(__PX4_WINDOWS)
// We want to use CLOCK_MONOTONIC if possible but we can't on macOS // Use CLOCK_MONOTONIC so the abstime passed to px4_sem_timedwait is not
// because it's not available. // 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_t attr;
pthread_condattr_init(&attr); pthread_condattr_init(&attr);
pthread_condattr_setclock(&attr, CLOCK_MONOTONIC); pthread_condattr_setclock(&attr, CLOCK_MONOTONIC);
pthread_cond_init(&(s->wait), &attr); pthread_cond_init(&(s->wait), &attr);
#else
pthread_cond_init(&(s->wait), nullptr);
#endif #endif
return 0; return 0;
@@ -133,6 +138,41 @@ int px4_sem_timedwait(px4_sem_t *s, const struct timespec *abstime)
s->value--; s->value--;
errno = 0; 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) { if (s->value < 0) {
ret = px4_pthread_cond_timedwait(&(s->wait), &(s->lock), abstime); ret = px4_pthread_cond_timedwait(&(s->wait), &(s->lock), abstime);
@@ -140,6 +180,15 @@ int px4_sem_timedwait(px4_sem_t *s, const struct timespec *abstime)
ret = 0; 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; errno = ret;
if (ret != 0 && ret != ETIMEDOUT) { if (ret != 0 && ret != ETIMEDOUT) {
@@ -0,0 +1,132 @@
/****************************************************************************
*
* 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.
*
****************************************************************************/
#include <px4_platform_common/shell.h>
#define MODULE_NAME "px4"
#include <px4_platform_common/log.h>
#include <cstdlib>
#include <cstring>
#include <string>
namespace px4
{
static std::string shell_quote(const std::string &value)
{
std::string quoted = "'";
for (char ch : value) {
if (ch == '\'') {
quoted += "'\\''";
} else {
quoted += ch;
}
}
quoted += "'";
return quoted;
}
static const char *startup_shell_wrapper = R"sh(
__px4_echo() {
__px4_line=$*
__px4_level=
case "$__px4_line" in
"DEBUG ["*) __px4_level=DEBUG; __px4_rest=${__px4_line#DEBUG } ;;
"DEBUG ["*) __px4_level=DEBUG; __px4_rest=${__px4_line#DEBUG } ;;
"INFO ["*) __px4_level=INFO; __px4_rest=${__px4_line#INFO } ;;
"INFO ["*) __px4_level=INFO; __px4_rest=${__px4_line#INFO } ;;
"WARN ["*) __px4_level=WARN; __px4_rest=${__px4_line#WARN } ;;
"WARN ["*) __px4_level=WARN; __px4_rest=${__px4_line#WARN } ;;
"ERROR ["*) __px4_level=ERROR; __px4_rest=${__px4_line#ERROR } ;;
"ERROR ["*) __px4_level=ERROR; __px4_rest=${__px4_line#ERROR } ;;
"PANIC ["*) __px4_level=PANIC; __px4_rest=${__px4_line#PANIC } ;;
"PANIC ["*) __px4_level=PANIC; __px4_rest=${__px4_line#PANIC } ;;
esac
if [ -z "$__px4_level" ]; then
command echo "$@"
return
fi
__px4_module=${__px4_rest#\[}
__px4_module=${__px4_module%%\]*}
__px4_message=${__px4_rest#*\]}
__px4_message=${__px4_message# }
if { [ -t 1 ] || [ "${PX4_FORCE_COLOR:-}" = 1 ]; } && [ -z "${NO_COLOR:-}" ]; then
case "$__px4_level" in
DEBUG) __px4_color='\033[32m' ;;
WARN) __px4_color='\033[33m' ;;
ERROR|PANIC) __px4_color='\033[31m' ;;
*) __px4_color='\033[0m' ;;
esac
printf '%b%-5s %b[%s] %b%s%b\n' "$__px4_color" "$__px4_level" '\033[37m' "$__px4_module" "$__px4_color" "$__px4_message" '\033[0m'
else
printf '%s\n' "$__px4_line"
fi
}
echo() {
__px4_echo "$@"
}
__px4_script=$1
set -- "$2"
. "$__px4_script"
)sh";
int run_shell_script(const std::string &script_path,
const std::string &binary_dir,
int instance)
{
// Prepend binary_dir to PATH so the script's `. px4-alias.sh` and
// px4-<module> invocations resolve regardless of the user's cwd.
const char *path = getenv("PATH");
if (path && strstr(path, binary_dir.c_str()) == nullptr) {
std::string updated = binary_dir + ':' + path;
setenv("PATH", updated.c_str(), 1);
}
PX4_INFO("startup script: /bin/sh %s %d", script_path.c_str(), instance);
const std::string cmd = "/bin/sh -c " + shell_quote(startup_shell_wrapper)
+ " px4-rc " + shell_quote(script_path) + ' ' + std::to_string(instance);
return system(cmd.c_str());
}
} // namespace px4
+10 -2
View File
@@ -195,7 +195,15 @@ px4_task_t px4_task_spawn_cmd(const char *name, int scheduler, int priority, int
return (rv < 0) ? rv : -rv; 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); rv = pthread_attr_setschedpolicy(&attr, scheduler);
#endif
if (rv != 0) { if (rv != 0) {
PX4_ERR("px4_task_spawn_cmd: failed to set sched policy"); 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; return (rv < 0) ? rv : -rv;
} }
#ifdef __PX4_CYGWIN #if defined(__PX4_CYGWIN) || defined(__PX4_WINDOWS)
/* Priorities on Windows are defined a lot differently */ /* Priorities on Windows are defined a lot differently */
priority = SCHED_PRIORITY_DEFAULT; priority = SCHED_PRIORITY_DEFAULT;
#endif #endif
@@ -329,7 +337,7 @@ void px4_task_exit(int ret)
pthread_mutex_unlock(&task_mutex); pthread_mutex_unlock(&task_mutex);
pthread_exit((void *)(unsigned long)ret); pthread_exit(reinterpret_cast<void *>(static_cast<intptr_t>(ret)));
} }
int px4_task_kill(px4_task_t id, int sig) int px4_task_kill(px4_task_t id, int sig)