Remote saving of IP config and calibration
This commit is contained in:
parent
2a2f97c006
commit
055c084c60
|
@ -13,12 +13,13 @@ all: puzzlecmd remotectl
|
||||||
puzzlecmd: puzzlecmd.o puzzlefw.o
|
puzzlecmd: puzzlecmd.o puzzlefw.o
|
||||||
$(CXX) -o $@ $(LDFLAGS) $^
|
$(CXX) -o $@ $(LDFLAGS) $^
|
||||||
|
|
||||||
remotectl: remotectl.o puzzlefw.o
|
remotectl: remotectl.o puzzlefw.o subproc.o
|
||||||
$(CXX) -o $@ $(LDFLAGS) $^
|
$(CXX) -o $@ $(LDFLAGS) $^
|
||||||
|
|
||||||
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
|
puzzlecmd.o: puzzlecmd.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp
|
||||||
remotectl.o: remotectl.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp version.hpp
|
remotectl.o: remotectl.cpp logging.hpp puzzlefw.hpp interrupt_manager.hpp data_server.hpp subproc.hpp version.hpp
|
||||||
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
|
puzzlefw.o: puzzlefw.cpp puzzlefw.hpp
|
||||||
|
subproc.o: subproc.cpp subproc.hpp
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
#include "logging.hpp"
|
#include "logging.hpp"
|
||||||
#include "interrupt_manager.hpp"
|
#include "interrupt_manager.hpp"
|
||||||
#include "data_server.hpp"
|
#include "data_server.hpp"
|
||||||
|
#include "subproc.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
|
|
||||||
using namespace puzzlefw;
|
using namespace puzzlefw;
|
||||||
|
@ -119,39 +120,53 @@ struct NetworkConfig {
|
||||||
|
|
||||||
|
|
||||||
/** Read network configuration from file. */
|
/** Read network configuration from file. */
|
||||||
bool read_network_config(const char *filename, NetworkConfig& ipcfg)
|
bool read_network_config(const std::string& filename, NetworkConfig& ipcfg)
|
||||||
{
|
{
|
||||||
ipcfg = NetworkConfig();
|
ipcfg = NetworkConfig();
|
||||||
|
|
||||||
std::ifstream is(filename);
|
std::ifstream is(filename);
|
||||||
if (!is) {
|
if (!is) {
|
||||||
log(LOG_ERROR, "Can not read %s", filename);
|
log(LOG_ERROR, "Can not read %s", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string line;
|
std::string line;
|
||||||
|
|
||||||
while (std::getline(is, line)) {
|
while (std::getline(is, line)) {
|
||||||
boost::system::error_code ec{};
|
|
||||||
size_t p = line.find_last_not_of(" \t\n\v\f\r");
|
size_t p = line.find_last_not_of(" \t\n\v\f\r");
|
||||||
if (p != line.npos) {
|
if (p != line.npos) {
|
||||||
line.erase(p + 1);
|
line.erase(p + 1);
|
||||||
}
|
}
|
||||||
if (line == "MODE=dhcp") {
|
|
||||||
|
p = line.find('=');
|
||||||
|
if (p == line.npos) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string label = line.substr(0, p);
|
||||||
|
std::string value = line.substr(p + 1);
|
||||||
|
|
||||||
|
boost::system::error_code ec{};
|
||||||
|
|
||||||
|
if (label == "MODE") {
|
||||||
|
if (value == "dhcp") {
|
||||||
ipcfg.mode = NETCFG_DHCP;
|
ipcfg.mode = NETCFG_DHCP;
|
||||||
}
|
}
|
||||||
if (line == "MODE=static") {
|
if (value == "static") {
|
||||||
ipcfg.mode = NETCFG_STATIC;
|
ipcfg.mode = NETCFG_STATIC;
|
||||||
}
|
}
|
||||||
if (line.compare(0, 7, "IPADDR=") == 0) {
|
|
||||||
ipcfg.ipaddr = asio::ip::make_address_v4(line.substr(7), ec);
|
|
||||||
}
|
}
|
||||||
if (line.compare(0, 8, "NETMASK=") == 0) {
|
if (label == "IPADDR" && (! value.empty())) {
|
||||||
ipcfg.netmask = asio::ip::make_address_v4(line.substr(8), ec);
|
ipcfg.ipaddr = asio::ip::make_address_v4(value, ec);
|
||||||
}
|
}
|
||||||
if (line.compare(0, 8, "GATEWAY=") == 0 && line.size() > 8) {
|
if (label == "NETMASK" && (! value.empty())) {
|
||||||
ipcfg.gateway = asio::ip::make_address_v4(line.substr(8), ec);
|
ipcfg.netmask = asio::ip::make_address_v4(value, ec);
|
||||||
}
|
}
|
||||||
|
if (label == "GATEWAY" && (! value.empty())) {
|
||||||
|
ipcfg.gateway = asio::ip::make_address_v4(value, ec);
|
||||||
|
}
|
||||||
|
|
||||||
if (ec) {
|
if (ec) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -240,7 +255,7 @@ struct Calibration {
|
||||||
|
|
||||||
|
|
||||||
/** Read calibration from file. */
|
/** Read calibration from file. */
|
||||||
void read_calibration_file(const char *filename, Calibration& cal)
|
void read_calibration_file(const std::string& filename, Calibration& cal)
|
||||||
{
|
{
|
||||||
// Set defaults in case calibration is missing or incomplete.
|
// Set defaults in case calibration is missing or incomplete.
|
||||||
for (unsigned int channel = 0; channel < 4; channel++) {
|
for (unsigned int channel = 0; channel < 4; channel++) {
|
||||||
|
@ -253,7 +268,7 @@ void read_calibration_file(const char *filename, Calibration& cal)
|
||||||
|
|
||||||
std::ifstream is(filename);
|
std::ifstream is(filename);
|
||||||
if (!is) {
|
if (!is) {
|
||||||
log(LOG_ERROR, "Can not read calibration file");
|
log(LOG_ERROR, "Can not read %s", filename.c_str());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -310,11 +325,11 @@ void read_calibration_file(const char *filename, Calibration& cal)
|
||||||
|
|
||||||
|
|
||||||
/** Write calibration to file. */
|
/** Write calibration to file. */
|
||||||
bool write_calibration_file(const char *filename, const Calibration& cal)
|
bool write_calibration_file(const std::string& filename, const Calibration& cal)
|
||||||
{
|
{
|
||||||
std::ofstream os(filename);
|
std::ofstream os(filename);
|
||||||
if (!os) {
|
if (!os) {
|
||||||
log(LOG_ERROR, "Can not write calibration file");
|
log(LOG_ERROR, "Can not write %s", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -342,7 +357,7 @@ bool write_calibration_file(const char *filename, const Calibration& cal)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!os) {
|
if (!os) {
|
||||||
log(LOG_ERROR, "Can not write calibration file");
|
log(LOG_ERROR, "Error while writing %s", filename.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -350,6 +365,43 @@ bool write_calibration_file(const char *filename, const Calibration& cal)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ******** Run subprograms ******** */
|
||||||
|
|
||||||
|
/** Run script to activate or save network configuration. */
|
||||||
|
bool run_ipcfg_script(const NetworkConfig& ipcfg, bool save)
|
||||||
|
{
|
||||||
|
std::vector<std::string> args;
|
||||||
|
args.push_back(save ? "save" : "config");
|
||||||
|
if (ipcfg.mode == NETCFG_STATIC) {
|
||||||
|
args.push_back("--mode");
|
||||||
|
args.push_back("static");
|
||||||
|
args.push_back("--ipaddr");
|
||||||
|
args.push_back(ipcfg.ipaddr.to_string());
|
||||||
|
args.push_back("--netmask");
|
||||||
|
args.push_back(ipcfg.netmask.to_string());
|
||||||
|
args.push_back("--gateway");
|
||||||
|
if (ipcfg.gateway.is_unspecified()) {
|
||||||
|
args.push_back("");
|
||||||
|
} else {
|
||||||
|
args.push_back(ipcfg.gateway.to_string());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
args.push_back("--mode");
|
||||||
|
args.push_back("dhcp");
|
||||||
|
}
|
||||||
|
int status = run_subprocess("/opt/puzzlefw/bin/puzzle-ipcfg", args);
|
||||||
|
return (status == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/** Run script to copy calibration file to SD card. */
|
||||||
|
bool run_calibration_script()
|
||||||
|
{
|
||||||
|
std::vector<std::string> args = { "save" };
|
||||||
|
int status = run_subprocess("/opt/puzzlefw/bin/puzzle-calibration", args);
|
||||||
|
return (status == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* ******** class CommandHandler ******** */
|
/* ******** class CommandHandler ******** */
|
||||||
|
|
||||||
|
@ -1200,13 +1252,16 @@ private:
|
||||||
/** Handle command AIN:MINMAX:CLEAR */
|
/** Handle command AIN:MINMAX:CLEAR */
|
||||||
std::string cmd_cal_save(CommandEnvironment env)
|
std::string cmd_cal_save(CommandEnvironment env)
|
||||||
{
|
{
|
||||||
if (! write_calibration_file(CFG_FILE_CALIBRATION, m_calibration)) {
|
std::string filename = std::string(CFG_FILE_CALIBRATION) + ".new";
|
||||||
|
if (! write_calibration_file(filename, m_calibration)) {
|
||||||
return "ERROR Can not write calibration";
|
return "ERROR Can not write calibration";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO - invoke script to save to SD card
|
if (! run_calibration_script()) {
|
||||||
|
return "ERROR Saving calibration failed";
|
||||||
|
}
|
||||||
|
|
||||||
return "ERROR";
|
return "OK";
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Handle command IPCFG */
|
/** Handle command IPCFG */
|
||||||
|
@ -1218,8 +1273,30 @@ private:
|
||||||
if (! parse_network_config(args, ipcfg, errmsg)) {
|
if (! parse_network_config(args, ipcfg, errmsg)) {
|
||||||
return "ERROR " + errmsg;
|
return "ERROR " + errmsg;
|
||||||
}
|
}
|
||||||
// TODO -- activate or save address, restart networking if needed
|
|
||||||
return "ERROR";
|
if (env.saved_flag) {
|
||||||
|
|
||||||
|
if (! run_ipcfg_script(ipcfg, true)) {
|
||||||
|
return "ERROR Saving network configuration failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "OK";
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// Shut down server sockets; then change IP address and resume.
|
||||||
|
m_shutting_down = true;
|
||||||
|
stop_server(
|
||||||
|
[this,ipcfg]() {
|
||||||
|
run_ipcfg_script(ipcfg, false);
|
||||||
|
m_shutting_down = false;
|
||||||
|
start_server();
|
||||||
|
});
|
||||||
|
|
||||||
|
// No response while shutting down.
|
||||||
|
// Response delivery would not be reliable while the socket is closing.
|
||||||
|
return std::string();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static const inline std::map<
|
static const inline std::map<
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
/*
|
||||||
|
* subproc.cpp
|
||||||
|
*
|
||||||
|
* Run program as a subprocess.
|
||||||
|
*
|
||||||
|
* Joris van Rantwijk 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/wait.h>
|
||||||
|
#include <system_error>
|
||||||
|
|
||||||
|
#include "subproc.hpp"
|
||||||
|
|
||||||
|
|
||||||
|
/** Run the specified program in a subprocess. */
|
||||||
|
int puzzlefw::run_subprocess(std::string path, std::vector<std::string> args)
|
||||||
|
{
|
||||||
|
// Prepare argv array.
|
||||||
|
std::vector<char*> argv_vec;
|
||||||
|
argv_vec.push_back(path.data());
|
||||||
|
for (std::string& arg : args) {
|
||||||
|
argv_vec.push_back(arg.data());
|
||||||
|
}
|
||||||
|
argv_vec.push_back(nullptr);
|
||||||
|
|
||||||
|
char** argv = argv_vec.data();
|
||||||
|
|
||||||
|
int max_file_descriptor = sysconf(_SC_OPEN_MAX);
|
||||||
|
|
||||||
|
// Block SIGCHLD.
|
||||||
|
sigset_t block_sigmask, saved_sigmask;
|
||||||
|
sigemptyset(&block_sigmask);
|
||||||
|
sigaddset(&block_sigmask, SIGCHLD);
|
||||||
|
pthread_sigmask(SIG_BLOCK, &block_sigmask, &saved_sigmask);
|
||||||
|
|
||||||
|
pid_t pid = fork();
|
||||||
|
if (pid == -1) {
|
||||||
|
pthread_sigmask(SIG_SETMASK, &saved_sigmask, nullptr);
|
||||||
|
throw std::system_error(
|
||||||
|
std::error_code(errno, std::system_category()),
|
||||||
|
std::string("Can not run subprocess"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pid == 0) {
|
||||||
|
// This code runs in the subprocess.
|
||||||
|
|
||||||
|
// Restore signal mask.
|
||||||
|
sigprocmask(SIG_SETMASK, &saved_sigmask, nullptr);
|
||||||
|
|
||||||
|
// Close non-standard file descriptors.
|
||||||
|
for (int i = 3; i < max_file_descriptor; i++) {
|
||||||
|
close(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute program.
|
||||||
|
execv(argv[0], argv);
|
||||||
|
_exit(127);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait until the subprocess ends.
|
||||||
|
int wstatus;
|
||||||
|
while (1) {
|
||||||
|
if (waitpid(pid, &wstatus, 0) < 0) {
|
||||||
|
if (errno == EINTR) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
pthread_sigmask(SIG_SETMASK, &saved_sigmask, nullptr);
|
||||||
|
throw std::system_error(
|
||||||
|
std::error_code(errno, std::system_category()),
|
||||||
|
std::string("Can not wait for subprocess"));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore signal mask.
|
||||||
|
pthread_sigmask(SIG_SETMASK, &saved_sigmask, nullptr);
|
||||||
|
|
||||||
|
return wstatus;
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* subproc.hpp
|
||||||
|
*
|
||||||
|
* Run program as a subprocess.
|
||||||
|
*
|
||||||
|
* Joris van Rantwijk 2024
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PUZZLEFW_SUBPROC_H_
|
||||||
|
#define PUZZLEFW_SUBPROC_H_
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
|
||||||
|
namespace puzzlefw {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run the specified program in a subprocess.
|
||||||
|
*
|
||||||
|
* The argument "path" specifies the full path of the target program.
|
||||||
|
* This string also becomes the value of "argv[0]".
|
||||||
|
*
|
||||||
|
* The argument "args" specifies a list of optional additional arguments
|
||||||
|
* ("argv[1]" and further).
|
||||||
|
*
|
||||||
|
* The subprocess closes non-standard file descriptors before executing
|
||||||
|
* the program.
|
||||||
|
*
|
||||||
|
* This function waits (blocks) until the subprocess ends.
|
||||||
|
* It returns the termination status of the subprocess.
|
||||||
|
*/
|
||||||
|
int run_subprocess(std::string path, std::vector<std::string> args);
|
||||||
|
|
||||||
|
} // namespace puzzlefw
|
||||||
|
|
||||||
|
#endif // PUZZLEFW_SUBPROC_H_
|
Loading…
Reference in New Issue