Files
PX4-Autopilot/platforms/posix/src/main.cpp
T
Julian Oes 35074aaffd posix: remove segfault handler
I could not get a core dump without removing the segfault handler, hence
this change.
2019-06-05 08:16:19 +02:00

629 lines
16 KiB
C++

/****************************************************************************
*
* Copyright (C) 2015-2018 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 main.cpp
*
* This is the main() of PX4 for POSIX.
*
* The application is designed as a daemon/server app with multiple clients.
* Both, the server and the client is started using this main() function.
*
* If the executable is called with its usual name 'px4', it will start the
* server. However, if it is started with an executable name (symlink) starting
* with 'px4-' such as 'px4-navigator', it will start as a client and try to
* connect to the server.
*
* The symlinks for all modules are created using the build system.
*
* @author Mark Charlebois <charlebm@gmail.com>
* @author Roman Bapst <bapstroman@gmail.com>
* @author Julian Oes <julian@oes.ch>
* @author Beat Küng <beat-kueng@gmx.net>
*/
#include <string>
#include <algorithm>
#include <fstream>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <px4_time.h>
#include <px4_log.h>
#include <px4_getopt.h>
#include <px4_tasks.h>
#include <px4_posix.h>
#include "apps.h"
#include "px4_middleware.h"
#include "DriverFramework.hpp"
#include "px4_middleware.h"
#include "px4_daemon/client.h"
#include "px4_daemon/server.h"
#include "px4_daemon/pxh.h"
#define MODULE_NAME "px4"
static const char *LOCK_FILE_PATH = "/tmp/px4_lock";
#ifndef PATH_MAX
#define PATH_MAX 1024
#endif
static volatile bool _exit_requested = false;
namespace px4
{
void init_once();
}
static void sig_int_handler(int sig_num);
static void sig_fpe_handler(int sig_num);
static void register_sig_handler();
static void set_cpu_scaling();
static int create_symlinks_if_needed(std::string &data_path);
static int create_dirs();
static int run_startup_script(const std::string &commands_file, const std::string &absolute_binary_path, int instance);
static std::string get_absolute_binary_path(const std::string &argv0);
static void wait_to_exit();
static bool is_already_running(int instance);
static void print_usage();
static bool dir_exists(const std::string &path);
static bool file_exists(const std::string &name);
static std::string file_basename(std::string const &pathname);
static std::string pwd();
#ifdef __PX4_SITL_MAIN_OVERRIDE
int SITL_MAIN(int argc, char **argv);
int SITL_MAIN(int argc, char **argv)
#else
int main(int argc, char **argv)
#endif
{
bool is_client = false;
bool pxh_off = false;
/* Symlinks point to all commands that can be used as a client with a prefix. */
const char prefix[] = PX4_SHELL_COMMAND_PREFIX;
int path_length = 0;
std::string absolute_binary_path; // full path to the px4 binary being executed
if (argc > 0) {
/* The executed binary name could start with a path, so strip it away */
const std::string full_binary_name = argv[0];
const std::string binary_name = file_basename(full_binary_name);
if (binary_name.compare(0, strlen(prefix), prefix) == 0) {
is_client = true;
}
path_length = full_binary_name.length() - binary_name.length();
absolute_binary_path = get_absolute_binary_path(full_binary_name);
}
if (is_client) {
int instance = 0;
if (argc >= 3 && strcmp(argv[1], "--instance") == 0) {
instance = strtoul(argv[2], nullptr, 10);
/* update argv so that "--instance <instance>" is not visible anymore */
argc -= 2;
for (int i = 1; i < argc; ++i) {
argv[i] = argv[i + 2];
}
}
PX4_DEBUG("instance: %i", instance);
if (!is_already_running(instance)) {
if (errno) {
PX4_ERR("Failed to communicate with daemon: %s", strerror(errno));
} else {
PX4_ERR("PX4 daemon not running yet");
}
return -1;
}
/* Remove the path and prefix. */
argv[0] += path_length + strlen(prefix);
px4_daemon::Client client(instance);
return client.process_args(argc, (const char **)argv);
} else {
/* Server/daemon apps need to parse the command line arguments. */
std::string data_path;
std::string commands_file = "etc/init.d/rcS";
std::string test_data_path;
int instance = 0;
int myoptind = 1;
int ch;
const char *myoptarg = nullptr;
while ((ch = px4_getopt(argc, argv, "hdt:s:i:", &myoptind, &myoptarg)) != EOF) {
switch (ch) {
case 'h':
print_usage();
return 0;
case 'd':
pxh_off = true;
break;
case 't':
test_data_path = myoptarg;
break;
case 's':
commands_file = myoptarg;
break;
case 'i':
instance = strtoul(myoptarg, nullptr, 10);
break;
default:
PX4_ERR("unrecognized flag");
print_usage();
return -1;
}
}
PX4_DEBUG("instance: %i", instance);
if (myoptind < argc) {
std::string optional_arg = argv[myoptind];
if (optional_arg.compare(0, 2, "__") != 0 || optional_arg.find(":=") == std::string::npos) {
data_path = optional_arg;
} // else: ROS argument (in the form __<name>:=<value>)
}
if (is_already_running(instance)) {
// allow running multiple instances, but the server is only started for the first
PX4_INFO("PX4 daemon already running for instance %i (%s)", instance, strerror(errno));
return -1;
}
int ret = create_symlinks_if_needed(data_path);
if (ret != PX4_OK) {
return ret;
}
if (test_data_path != "") {
const std::string required_test_data_path = "./test_data";
if (!dir_exists(required_test_data_path)) {
ret = symlink(test_data_path.c_str(), required_test_data_path.c_str());
if (ret != PX4_OK) {
return ret;
}
}
}
if (!file_exists(commands_file)) {
PX4_ERR("Error opening startup file, does not exist: %s", commands_file.c_str());
return -1;
}
register_sig_handler();
set_cpu_scaling();
px4_daemon::Server server(instance);
server.start();
ret = create_dirs();
if (ret != PX4_OK) {
return ret;
}
DriverFramework::Framework::initialize();
px4::init_once();
px4::init(argc, argv, "px4");
ret = run_startup_script(commands_file, absolute_binary_path, instance);
// We now block here until we need to exit.
if (pxh_off) {
wait_to_exit();
} else {
px4_daemon::Pxh pxh;
pxh.run_pxh();
}
// When we exit, we need to stop muorb on Snapdragon.
#ifdef __PX4_POSIX_EAGLE
// Sending muorb stop is needed if it is running to exit cleanly.
// TODO: we should check with px4_task_is_running("muorb") before stopping it.
std::string muorb_stop_cmd("muorb stop");
px4_daemon::Pxh::process_line(muorb_stop_cmd, true);
#endif
std::string cmd("shutdown");
px4_daemon::Pxh::process_line(cmd, true);
}
return PX4_OK;
}
int create_symlinks_if_needed(std::string &data_path)
{
std::string current_path = pwd();
if (data_path.empty()) {
// No data path given, we'll just try to use the current working dir.
data_path = current_path;
PX4_INFO("assuming working directory is rootfs, no symlinks needed.");
return PX4_OK;
}
if (data_path == current_path) {
// We are already running in the data path, so no need to symlink
PX4_INFO("working directory seems to be rootfs, no symlinks needed");
return PX4_OK;
}
const std::string path_sym_link = "etc";
PX4_DEBUG("path sym link: %s", path_sym_link.c_str());
std::string src_path = data_path;
std::string dest_path = current_path + "/" + path_sym_link;
struct stat info;
if (lstat(dest_path.c_str(), &info) == 0) {
if (S_ISLNK(info.st_mode)) {
// recreate the symlink, as it might point to some other location than what we want now
unlink(dest_path.c_str());
} else if (S_ISDIR(info.st_mode)) {
return PX4_OK;
}
}
PX4_INFO("Creating symlink %s -> %s", src_path.c_str(), dest_path.c_str());
// create sym-link
int ret = symlink(src_path.c_str(), dest_path.c_str());
if (ret != 0) {
PX4_ERR("Error creating symlink %s -> %s", src_path.c_str(), dest_path.c_str());
return ret;
} else {
PX4_DEBUG("Successfully created symlink %s -> %s", src_path.c_str(), dest_path.c_str());
}
return PX4_OK;
}
int create_dirs()
{
std::string current_path = pwd();
std::vector<std::string> dirs{"log", "eeprom"};
for (const auto &dir : dirs) {
PX4_DEBUG("mkdir: %s", dir.c_str());;
std::string dir_path = current_path + "/" + dir;
if (dir_exists(dir_path)) {
continue;
}
// create dirs
int ret = mkdir(dir_path.c_str(), S_IRWXU | S_IRWXG | S_IRWXO);
if (ret != OK) {
PX4_WARN("failed creating new dir: %s", dir_path.c_str());
return ret;
} else {
PX4_DEBUG("Successfully created dir %s", dir_path.c_str());
}
}
return PX4_OK;
}
void register_sig_handler()
{
// SIGINT
struct sigaction sig_int {};
sig_int.sa_handler = sig_int_handler;
sig_int.sa_flags = 0;// not SA_RESTART!
// SIGFPE
struct sigaction sig_fpe {};
sig_fpe.sa_handler = sig_fpe_handler;
sig_fpe.sa_flags = 0;// not SA_RESTART!
// SIGPIPE
// We want to ignore if a PIPE has been closed.
struct sigaction sig_pipe {};
sig_pipe.sa_handler = SIG_IGN;
#ifdef __PX4_CYGWIN
// Do not catch SIGINT on Cygwin such that the process gets killed
// TODO: All threads should exit gracefully see https://github.com/PX4/Firmware/issues/11027
(void)sig_int; // this variable is unused
#else
sigaction(SIGINT, &sig_int, nullptr);
#endif
//sigaction(SIGTERM, &sig_int, nullptr);
sigaction(SIGFPE, &sig_fpe, nullptr);
sigaction(SIGPIPE, &sig_pipe, nullptr);
}
void sig_int_handler(int sig_num)
{
fflush(stdout);
printf("\nExiting...\n");
fflush(stdout);
px4_daemon::Pxh::stop();
_exit_requested = true;
}
void sig_fpe_handler(int sig_num)
{
fflush(stdout);
printf("\nfloating point exception\n");
fflush(stdout);
px4_daemon::Pxh::stop();
_exit_requested = true;
}
void set_cpu_scaling()
{
#ifdef __PX4_POSIX_EAGLE
// On Snapdragon we miss updates in sdlog2 unless all 4 CPUs are run
// at the maximum frequency all the time.
// Interestingely, cpu0 and cpu3 set the scaling for all 4 CPUs on Snapdragon.
system("echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
system("echo performance > /sys/devices/system/cpu/cpu3/cpufreq/scaling_governor");
// Alternatively we could also raise the minimum frequency to save some power,
// unfortunately this still lead to some drops.
//system("echo 1190400 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq");
#endif
}
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('/');
if (last_slash == std::string::npos) {
// either relative path or in PATH (PATH is ignored here)
return pwd();
}
std::string base = argv0.substr(0, last_slash);
if (base.length() > 0 && base[0] == '/') {
// absolute path
return base;
}
// relative path
return pwd() + "/" + base;
}
int run_startup_script(const std::string &commands_file, const std::string &absolute_binary_path,
int instance)
{
std::string shell_command("/bin/sh ");
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("Calling 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);
}
} else {
PX4_INFO("Startup script empty");
}
return ret;
}
void wait_to_exit()
{
while (!_exit_requested) {
px4_usleep(100000);
}
}
void print_usage()
{
printf("Usage for Server/daemon process: \n");
printf("\n");
printf(" px4 [-h|-d] [-s <startup_file>] [-t <test_data_directory>] [<rootfs_directory>] [-i <instance>]\n");
printf("\n");
printf(" -s <startup_file> shell script to be used as startup (default=etc/init.d/rcS)\n");
printf(" <rootfs_directory> directory where startup files and mixers are located,\n");
printf(" (if not given, CWD is used)\n");
printf(" -i <instance> px4 instance id to run multiple instances [0...N], default=0\n");
printf(" -h help/usage information\n");
printf(" -d daemon mode, don't start pxh shell\n");
printf("\n");
printf("Usage for client: \n");
printf("\n");
printf(" px4-MODULE [--instance <instance>] command using symlink.\n");
printf(" e.g.: px4-commander status\n");
}
bool is_already_running(int instance)
{
const std::string file_lock_path = std::string(LOCK_FILE_PATH) + '-' + std::to_string(instance);
struct flock fl;
int fd = open(file_lock_path.c_str(), O_RDWR | O_CREAT, 0666);
if (fd < 0) {
return false;
}
fl.l_type = F_WRLCK;
fl.l_whence = SEEK_SET;
fl.l_start = 0;
fl.l_len = 0;
fl.l_pid = getpid();
if (fcntl(fd, F_SETLK, &fl) == -1) {
// We failed to create a file lock, must be already locked.
if (errno == EACCES || errno == EAGAIN) {
return true;
}
return false;
}
errno = 0;
return false;
}
bool file_exists(const std::string &name)
{
struct stat buffer;
return (stat(name.c_str(), &buffer) == 0);
}
static std::string file_basename(std::string const &pathname)
{
struct MatchPathSeparator {
bool operator()(char ch) const
{
return ch == '/';
}
};
return std::string(std::find_if(pathname.rbegin(), pathname.rend(),
MatchPathSeparator()).base(), pathname.end());
}
bool dir_exists(const std::string &path)
{
struct stat info;
if (stat(path.c_str(), &info) != 0) {
return false;
} else if (info.st_mode & S_IFDIR) {
return true;
}
return false;
}
std::string pwd()
{
char temp[PATH_MAX];
return (getcwd(temp, PATH_MAX) ? std::string(temp) : std::string(""));
}