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
|
||||
$(CXX) -o $@ $(LDFLAGS) $^
|
||||
|
||||
remotectl: remotectl.o puzzlefw.o
|
||||
remotectl: remotectl.o puzzlefw.o subproc.o
|
||||
$(CXX) -o $@ $(LDFLAGS) $^
|
||||
|
||||
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
|
||||
subproc.o: subproc.cpp subproc.hpp
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
|
|
|
@ -29,6 +29,7 @@
|
|||
#include "logging.hpp"
|
||||
#include "interrupt_manager.hpp"
|
||||
#include "data_server.hpp"
|
||||
#include "subproc.hpp"
|
||||
#include "version.hpp"
|
||||
|
||||
using namespace puzzlefw;
|
||||
|
@ -119,39 +120,53 @@ struct NetworkConfig {
|
|||
|
||||
|
||||
/** 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();
|
||||
|
||||
std::ifstream is(filename);
|
||||
if (!is) {
|
||||
log(LOG_ERROR, "Can not read %s", filename);
|
||||
log(LOG_ERROR, "Can not read %s", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string line;
|
||||
|
||||
while (std::getline(is, line)) {
|
||||
boost::system::error_code ec{};
|
||||
|
||||
size_t p = line.find_last_not_of(" \t\n\v\f\r");
|
||||
if (p != line.npos) {
|
||||
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;
|
||||
}
|
||||
if (line == "MODE=static") {
|
||||
if (value == "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) {
|
||||
ipcfg.netmask = asio::ip::make_address_v4(line.substr(8), ec);
|
||||
if (label == "IPADDR" && (! value.empty())) {
|
||||
ipcfg.ipaddr = asio::ip::make_address_v4(value, ec);
|
||||
}
|
||||
if (line.compare(0, 8, "GATEWAY=") == 0 && line.size() > 8) {
|
||||
ipcfg.gateway = asio::ip::make_address_v4(line.substr(8), ec);
|
||||
if (label == "NETMASK" && (! value.empty())) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
|
@ -240,7 +255,7 @@ struct Calibration {
|
|||
|
||||
|
||||
/** 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.
|
||||
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);
|
||||
if (!is) {
|
||||
log(LOG_ERROR, "Can not read calibration file");
|
||||
log(LOG_ERROR, "Can not read %s", filename.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -310,11 +325,11 @@ void read_calibration_file(const char *filename, Calibration& cal)
|
|||
|
||||
|
||||
/** 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);
|
||||
if (!os) {
|
||||
log(LOG_ERROR, "Can not write calibration file");
|
||||
log(LOG_ERROR, "Can not write %s", filename.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -342,7 +357,7 @@ bool write_calibration_file(const char *filename, const Calibration& cal)
|
|||
}
|
||||
|
||||
if (!os) {
|
||||
log(LOG_ERROR, "Can not write calibration file");
|
||||
log(LOG_ERROR, "Error while writing %s", filename.c_str());
|
||||
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 ******** */
|
||||
|
||||
|
@ -1200,13 +1252,16 @@ private:
|
|||
/** Handle command AIN:MINMAX:CLEAR */
|
||||
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";
|
||||
}
|
||||
|
||||
// TODO - invoke script to save to SD card
|
||||
if (! run_calibration_script()) {
|
||||
return "ERROR Saving calibration failed";
|
||||
}
|
||||
|
||||
return "ERROR";
|
||||
return "OK";
|
||||
}
|
||||
|
||||
/** Handle command IPCFG */
|
||||
|
@ -1218,8 +1273,30 @@ private:
|
|||
if (! parse_network_config(args, ipcfg, 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<
|
||||
|
|
|
@ -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