mirror of
https://github.com/PX4/PX4-Autopilot.git
synced 2026-05-23 06:36:45 +08:00
feat(posix): add portable startup script backend
Route POSIX startup scripts through a platform shell hook instead of hard-coding /bin/sh in main.cpp. Linux keeps the existing external-shell behavior through shell_posix.cpp, while the Windows backend can use an embedded shell implementation. The same path also restores console state and exits through the platform cleanup hook during shutdown. Signed-off-by: Nuno Marques <n.marques21@hotmail.com>
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
#include <px4_platform_common/time.h>
|
||||
#include <px4_platform_common/posix.h>
|
||||
#include <px4_platform_common/log.h>
|
||||
#include <px4_platform_common/exit.h>
|
||||
#include <uORB/uORB.h>
|
||||
|
||||
#include "apps.h"
|
||||
@@ -45,7 +46,7 @@ int shutdown_main(int argc, char *argv[])
|
||||
{
|
||||
printf("Exiting NOW.\n");
|
||||
uorb_shutdown();
|
||||
system_exit(0);
|
||||
px4_platform_exit(0);
|
||||
}
|
||||
|
||||
int list_tasks_main(int argc, char *argv[])
|
||||
|
||||
@@ -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
|
||||
@@ -40,6 +40,8 @@
|
||||
#include <px4_platform_common/workqueue.h>
|
||||
#include <px4_platform_common/shutdown.h>
|
||||
#include <px4_platform_common/tasks.h>
|
||||
#include <px4_platform_common/time.h>
|
||||
#include <px4_platform_common/exit.h>
|
||||
|
||||
#include <drivers/drv_hrt.h>
|
||||
|
||||
@@ -63,6 +65,20 @@
|
||||
|
||||
using namespace time_literals;
|
||||
|
||||
#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 pthread_mutex_t shutdown_mutex =
|
||||
PTHREAD_MUTEX_INITIALIZER; // protects access to shutdown_hooks & shutdown_lock_counter
|
||||
static uint8_t shutdown_lock_counter = 0;
|
||||
@@ -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);
|
||||
|
||||
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)
|
||||
// simply exit on posix if real shutdown (poweroff) not available
|
||||
PX4_INFO_RAW("Exiting NOW.");
|
||||
system_exit(0);
|
||||
px4_platform_exit(0);
|
||||
#else
|
||||
PX4_PANIC("board shutdown not available");
|
||||
#endif
|
||||
@@ -239,7 +255,7 @@ int px4_reboot_request(reboot_request_t request, uint32_t delay_us)
|
||||
shutdown_args |= SHUTDOWN_ARG_TO_ISP;
|
||||
}
|
||||
|
||||
shutdown_time_us = hrt_absolute_time();
|
||||
shutdown_time_us = shutdown_time_us_now();
|
||||
|
||||
if (delay_us > 0) {
|
||||
shutdown_time_us += delay_us;
|
||||
@@ -263,7 +279,7 @@ int px4_shutdown_request(uint32_t delay_us)
|
||||
|
||||
shutdown_args |= SHUTDOWN_ARG_IN_PROGRESS;
|
||||
|
||||
shutdown_time_us = hrt_absolute_time();
|
||||
shutdown_time_us = shutdown_time_us_now();
|
||||
|
||||
if (delay_us > 0) {
|
||||
shutdown_time_us += delay_us;
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
@@ -63,16 +62,27 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#if (_POSIX_MEMLOCK > 0)
|
||||
#if (_POSIX_MEMLOCK > 0) && !defined(__PX4_WINDOWS)
|
||||
#include <sys/mman.h>
|
||||
#endif
|
||||
|
||||
#ifdef __PX4_WINDOWS
|
||||
// MinGW ships no shell at /bin/sh, no geteuid, no sigaction.
|
||||
// mkdir is already a 2-arg POSIX wrapper via the windows_shim sys/stat.h.
|
||||
// Provide the remaining forwards inline so the stock POSIX main.cpp
|
||||
// compiles unchanged.
|
||||
#include <windows.h>
|
||||
#include <px4_windows/platform.h>
|
||||
static inline unsigned int geteuid(void) { return 1000; }
|
||||
#endif
|
||||
|
||||
#include <px4_platform_common/time.h>
|
||||
#include <px4_platform_common/log.h>
|
||||
#include <px4_platform_common/init.h>
|
||||
#include <px4_platform_common/getopt.h>
|
||||
#include <px4_platform_common/tasks.h>
|
||||
#include <px4_platform_common/posix.h>
|
||||
#include <px4_platform_common/shell.h>
|
||||
#include <uORB/uORB.h>
|
||||
|
||||
#include "apps.h"
|
||||
@@ -90,6 +100,7 @@ static const char *LOCK_FILE_PATH = "/tmp/px4_lock";
|
||||
|
||||
|
||||
static volatile bool _exit_requested = false;
|
||||
static volatile sig_atomic_t _shutdown_started = 0;
|
||||
|
||||
|
||||
namespace px4
|
||||
@@ -111,10 +122,70 @@ static int set_server_running(int instance);
|
||||
static void print_usage();
|
||||
static bool dir_exists(const std::string &path);
|
||||
static bool file_exists(const std::string &name);
|
||||
static bool is_absolute_path(const std::string &path);
|
||||
static bool is_path_separator(char ch);
|
||||
static std::string file_basename(std::string const &pathname);
|
||||
static std::string pwd();
|
||||
static int change_directory(const std::string &directory);
|
||||
|
||||
#ifdef __PX4_WINDOWS
|
||||
// Unblock the main thread's getchar() so the pxh loop can notice
|
||||
// _should_exit. Windows delivers Ctrl+C on a dedicated handler thread,
|
||||
// which means just flipping a flag leaves the main thread blocked in its
|
||||
// stdin ReadFile forever. Two nudges, tried in order:
|
||||
// 1) inject a synthetic '\n' keypress into the console input buffer so
|
||||
// getchar() returns cleanly; works when stdin is an attached console;
|
||||
// 2) CancelIoEx on the stdin handle; works when stdin has been
|
||||
// redirected to a pipe/file (e.g. `wine px4.exe < script`).
|
||||
static void kick_stdin_reader()
|
||||
{
|
||||
HANDLE stdin_h = GetStdHandle(STD_INPUT_HANDLE);
|
||||
|
||||
if (stdin_h == INVALID_HANDLE_VALUE || stdin_h == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
INPUT_RECORD rec[2] = {};
|
||||
rec[0].EventType = KEY_EVENT;
|
||||
rec[0].Event.KeyEvent.bKeyDown = TRUE;
|
||||
rec[0].Event.KeyEvent.wRepeatCount = 1;
|
||||
rec[0].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
|
||||
rec[0].Event.KeyEvent.uChar.UnicodeChar = L'\n';
|
||||
rec[1] = rec[0];
|
||||
rec[1].Event.KeyEvent.bKeyDown = FALSE;
|
||||
|
||||
DWORD written = 0;
|
||||
WriteConsoleInputW(stdin_h, rec, 2, &written);
|
||||
CancelIoEx(stdin_h, nullptr);
|
||||
}
|
||||
|
||||
static void prepare_console_for_host_shell()
|
||||
{
|
||||
px4_windows_restore_console_modes();
|
||||
px4_windows_discard_pending_input();
|
||||
px4_windows_restore_console_modes();
|
||||
}
|
||||
|
||||
static BOOL WINAPI px4_console_ctrl_handler(DWORD ctrl_type)
|
||||
{
|
||||
switch (ctrl_type) {
|
||||
case CTRL_C_EVENT:
|
||||
case CTRL_BREAK_EVENT:
|
||||
case CTRL_CLOSE_EVENT:
|
||||
case CTRL_LOGOFF_EVENT:
|
||||
case CTRL_SHUTDOWN_EVENT:
|
||||
sig_int_handler(SIGINT);
|
||||
kick_stdin_reader();
|
||||
prepare_console_for_host_shell();
|
||||
px4_windows_release_console();
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef __PX4_SITL_MAIN_OVERRIDE
|
||||
int SITL_MAIN(int argc, char **argv);
|
||||
@@ -416,6 +487,19 @@ int main(int argc, char **argv)
|
||||
|
||||
std::string cmd("shutdown");
|
||||
px4_daemon::Pxh::process_line(cmd, true);
|
||||
#ifdef __PX4_WINDOWS
|
||||
// The shutdown command runs asynchronously on the worker queue. While
|
||||
// waiting for the worker to terminate the process, keep restoring and
|
||||
// draining stdin so Enters typed during shutdown do not leak back into
|
||||
// the host Linux shell once Wine returns.
|
||||
for (int i = 0; i < 120; ++i) {
|
||||
prepare_console_for_host_shell();
|
||||
Sleep(50);
|
||||
}
|
||||
|
||||
px4_windows_release_console();
|
||||
px4_windows_exit(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
return PX4_OK;
|
||||
@@ -505,6 +589,14 @@ int create_dirs()
|
||||
|
||||
void register_sig_handler()
|
||||
{
|
||||
#ifdef __PX4_WINDOWS
|
||||
// MinGW's signal.h has no sigaction. SIGPIPE does not exist on
|
||||
// Windows (closed sockets return WSAECONNRESET instead). Fall back
|
||||
// to plain signal() for SIGINT/SIGTERM.
|
||||
signal(SIGINT, sig_int_handler);
|
||||
signal(SIGTERM, sig_int_handler);
|
||||
SetConsoleCtrlHandler(px4_console_ctrl_handler, TRUE);
|
||||
#else
|
||||
// SIGINT
|
||||
struct sigaction sig_int {};
|
||||
sig_int.sa_handler = sig_int_handler;
|
||||
@@ -525,13 +617,24 @@ void register_sig_handler()
|
||||
|
||||
sigaction(SIGTERM, &sig_int, nullptr);
|
||||
sigaction(SIGPIPE, &sig_pipe, nullptr);
|
||||
#endif // __PX4_WINDOWS
|
||||
}
|
||||
|
||||
void sig_int_handler(int sig_num)
|
||||
{
|
||||
(void)sig_num;
|
||||
|
||||
if (_shutdown_started) {
|
||||
return;
|
||||
}
|
||||
|
||||
_shutdown_started = 1;
|
||||
fflush(stdout);
|
||||
printf("\nPX4 Exiting...\n");
|
||||
fflush(stdout);
|
||||
#ifdef __PX4_WINDOWS
|
||||
prepare_console_for_host_shell();
|
||||
#endif
|
||||
uorb_shutdown();
|
||||
px4_daemon::Pxh::stop();
|
||||
_exit_requested = true;
|
||||
@@ -553,7 +656,7 @@ std::string get_absolute_binary_path(const std::string &argv0)
|
||||
{
|
||||
// On Linux we could also use readlink("/proc/self/exe", buf, bufsize) to get the absolute path
|
||||
|
||||
std::size_t last_slash = argv0.find_last_of('/');
|
||||
std::size_t last_slash = argv0.find_last_of("/\\");
|
||||
|
||||
if (last_slash == std::string::npos) {
|
||||
// either relative path or in PATH (PATH is ignored here)
|
||||
@@ -562,8 +665,9 @@ std::string get_absolute_binary_path(const std::string &argv0)
|
||||
|
||||
std::string base = argv0.substr(0, last_slash);
|
||||
|
||||
if (base.length() > 0 && base[0] == '/') {
|
||||
// absolute path
|
||||
if (is_absolute_path(base)) {
|
||||
// Absolute POSIX path, or an absolute Windows path when running the
|
||||
// Windows backend natively / under Wine.
|
||||
return base;
|
||||
}
|
||||
|
||||
@@ -574,62 +678,13 @@ std::string get_absolute_binary_path(const std::string &argv0)
|
||||
int run_startup_script(const std::string &commands_file, const std::string &absolute_binary_path,
|
||||
int instance)
|
||||
{
|
||||
std::string shell_command("/bin/sh ");
|
||||
int ret = px4::run_shell_script(commands_file, absolute_binary_path, instance);
|
||||
|
||||
shell_command += commands_file + ' ' + std::to_string(instance);
|
||||
|
||||
// Update the PATH variable to include the absolute_binary_path
|
||||
// (required for the px4-alias.sh script and px4-* commands).
|
||||
// They must be within the same directory as the px4 binary
|
||||
const char *path_variable = "PATH";
|
||||
std::string updated_path = absolute_binary_path;
|
||||
const char *path = getenv(path_variable);
|
||||
|
||||
if (path) {
|
||||
std::string spath = path;
|
||||
|
||||
// Check if absolute_binary_path already in PATH
|
||||
bool already_in_path = false;
|
||||
std::size_t current, previous = 0;
|
||||
current = spath.find(':');
|
||||
|
||||
while (current != std::string::npos) {
|
||||
if (spath.substr(previous, current - previous) == absolute_binary_path) {
|
||||
already_in_path = true;
|
||||
}
|
||||
|
||||
previous = current + 1;
|
||||
current = spath.find(':', previous);
|
||||
}
|
||||
|
||||
if (spath.substr(previous, current - previous) == absolute_binary_path) {
|
||||
already_in_path = true;
|
||||
}
|
||||
|
||||
if (!already_in_path) {
|
||||
// Prepend to path to prioritize PX4 commands over potentially already installed PX4 commands.
|
||||
updated_path = updated_path + ":" + path;
|
||||
setenv(path_variable, updated_path.c_str(), 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PX4_INFO("startup script: %s", shell_command.c_str());
|
||||
|
||||
int ret = 0;
|
||||
|
||||
if (!shell_command.empty()) {
|
||||
ret = system(shell_command.c_str());
|
||||
|
||||
if (ret == 0) {
|
||||
PX4_INFO("Startup script returned successfully");
|
||||
|
||||
} else {
|
||||
PX4_ERR("Startup script returned with return value: %d", ret);
|
||||
}
|
||||
if (ret == 0) {
|
||||
PX4_INFO("Startup script returned successfully");
|
||||
|
||||
} else {
|
||||
PX4_INFO("Startup script empty");
|
||||
PX4_ERR("Startup script returned with return value: %d", ret);
|
||||
}
|
||||
|
||||
return ret;
|
||||
@@ -743,13 +798,47 @@ static std::string file_basename(std::string const &pathname)
|
||||
struct MatchPathSeparator {
|
||||
bool operator()(char ch) const
|
||||
{
|
||||
return ch == '/';
|
||||
return is_path_separator(ch);
|
||||
}
|
||||
};
|
||||
return std::string(std::find_if(pathname.rbegin(), pathname.rend(),
|
||||
MatchPathSeparator()).base(), pathname.end());
|
||||
}
|
||||
|
||||
static bool is_path_separator(char ch)
|
||||
{
|
||||
return ch == '/' || ch == '\\';
|
||||
}
|
||||
|
||||
static bool is_absolute_path(const std::string &path)
|
||||
{
|
||||
if (path.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (path[0] == '/') {
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef __PX4_WINDOWS
|
||||
const bool drive_letter = path.length() >= 3
|
||||
&& ((path[0] >= 'A' && path[0] <= 'Z') || (path[0] >= 'a' && path[0] <= 'z'))
|
||||
&& path[1] == ':'
|
||||
&& is_path_separator(path[2]);
|
||||
|
||||
if (drive_letter) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// UNC paths begin with two separators, for example \\server\share.
|
||||
if (path.length() >= 2 && is_path_separator(path[0]) && is_path_separator(path[1])) {
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool dir_exists(const std::string &path)
|
||||
{
|
||||
struct stat info;
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,52 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* 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::embedded_shell
|
||||
{
|
||||
|
||||
// True when a real in-process shell backend is compiled into px4.exe.
|
||||
bool is_available();
|
||||
|
||||
// Human-readable backend name for logs and diagnostics.
|
||||
const char *backend_name();
|
||||
|
||||
// Execute the script in-process and return the shell exit status.
|
||||
int run_script(const std::string &script_path,
|
||||
const std::string &binary_dir,
|
||||
int instance);
|
||||
|
||||
} // namespace px4::embedded_shell
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,168 @@
|
||||
/****************************************************************************
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
// Windows needs a real in-process shell backend if px4.exe is to be
|
||||
// redistributed as a self-contained component. The minimal parser below is
|
||||
// only a stopgap for trivial generated scripts and should not be used for
|
||||
// full rcS semantics.
|
||||
|
||||
#include <px4_platform_common/shell.h>
|
||||
|
||||
#define MODULE_NAME "px4"
|
||||
#include <px4_platform_common/log.h>
|
||||
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
|
||||
#include "../../common/px4_daemon/pxh.h"
|
||||
#include "embedded_backend.h"
|
||||
|
||||
namespace px4
|
||||
{
|
||||
|
||||
static int set_env(const char *key, const char *value)
|
||||
{
|
||||
return _putenv_s(key, value);
|
||||
}
|
||||
|
||||
static bool allow_external_shell_fallback()
|
||||
{
|
||||
const char *value = getenv("PX4_ALLOW_EXTERNAL_SH");
|
||||
|
||||
return value != nullptr && strcmp(value, "1") == 0;
|
||||
}
|
||||
|
||||
static int run_shell_script_fallback(const std::string &script_path)
|
||||
{
|
||||
std::ifstream file(script_path);
|
||||
|
||||
if (!file.is_open()) {
|
||||
PX4_ERR("Could not open startup script %s", script_path.c_str());
|
||||
return -1;
|
||||
}
|
||||
|
||||
PX4_WARN("falling back to the minimal Windows shell parser for %s", script_path.c_str());
|
||||
|
||||
int last_ret = 0;
|
||||
std::string line;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
// strip CR (files written with CRLF line endings)
|
||||
while (!line.empty() && (line.back() == '\r' || line.back() == '\n')) {
|
||||
line.pop_back();
|
||||
}
|
||||
|
||||
std::size_t first = line.find_first_not_of(" \t");
|
||||
|
||||
if (first == std::string::npos) {
|
||||
continue; // blank line
|
||||
}
|
||||
|
||||
std::string trimmed = line.substr(first);
|
||||
|
||||
if (trimmed[0] == '#') {
|
||||
continue; // comment or shebang
|
||||
}
|
||||
|
||||
if (trimmed.rfind(". ", 0) == 0) {
|
||||
// Skip POSIX `source`; the only script init files do this
|
||||
// for is px4-alias.sh, whose aliases are redundant here
|
||||
// because Pxh dispatches bare command names via the apps map.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (trimmed.rfind("echo ", 0) == 0 || trimmed == "echo") {
|
||||
// Minimal `echo` so scripts can emit PASS/FAIL markers that
|
||||
// ctest PASS_REGULAR_EXPRESSION looks for. Strips a single
|
||||
// pair of surrounding quotes but otherwise prints verbatim.
|
||||
std::string arg = trimmed.size() > 4 ? trimmed.substr(5) : "";
|
||||
|
||||
if (arg.size() >= 2 &&
|
||||
((arg.front() == '"' && arg.back() == '"') ||
|
||||
(arg.front() == '\'' && arg.back() == '\''))) {
|
||||
arg = arg.substr(1, arg.size() - 2);
|
||||
}
|
||||
|
||||
const std::string output = arg + '\n';
|
||||
px4_log_write_text(stdout, output.data(), output.size());
|
||||
last_ret = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
last_ret = px4_daemon::Pxh::process_line(trimmed, true);
|
||||
}
|
||||
|
||||
return last_ret;
|
||||
}
|
||||
|
||||
int run_shell_script(const std::string &script_path,
|
||||
const std::string &binary_dir,
|
||||
int instance)
|
||||
{
|
||||
const char *path = getenv("PATH");
|
||||
|
||||
if (path && strstr(path, binary_dir.c_str()) == nullptr) {
|
||||
std::string updated = binary_dir + ':' + path;
|
||||
set_env("PATH", updated.c_str());
|
||||
|
||||
} else if (!path) {
|
||||
set_env("PATH", binary_dir.c_str());
|
||||
}
|
||||
|
||||
if (embedded_shell::is_available()) {
|
||||
PX4_INFO("startup script (%s): %s",
|
||||
embedded_shell::backend_name(),
|
||||
script_path.c_str());
|
||||
return embedded_shell::run_script(script_path, binary_dir, instance);
|
||||
}
|
||||
|
||||
if (allow_external_shell_fallback()) {
|
||||
std::string cmd = "sh \"" + script_path + "\" " + std::to_string(instance);
|
||||
PX4_WARN("no embedded shell backend compiled in, using external shell fallback: %s", cmd.c_str());
|
||||
|
||||
const int ret = system(cmd.c_str());
|
||||
|
||||
if (ret != -1) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
PX4_WARN("no embedded shell backend is available for %s", script_path.c_str());
|
||||
PX4_WARN("build a real backend into px4.exe for full rcS support");
|
||||
return run_shell_script_fallback(script_path);
|
||||
}
|
||||
|
||||
} // namespace px4
|
||||
Reference in New Issue
Block a user