Hotspot UI

This commit is contained in:
Robin Davies
2021-08-22 11:51:09 -04:00
parent f6aa331d77
commit 865e23b413
51 changed files with 918 additions and 146 deletions
+2 -1
View File
@@ -88,7 +88,8 @@ std::map<std::string, std::string> extensionsToMimeType = {
{".tif", "image/tiff"},
{".svg", "image/svg+xml"},
{".svgz", "image/svg+xml"},
{".woff", "font/woff2" },
{".woff2", "font/woff2"}
};
// Return a reasonable mime type based on the extension of a file.
std::string
+1
View File
@@ -3,6 +3,7 @@
#include <boost/beast/http.hpp>
#include <boost/asio/ip/network_v4.hpp>
#include <mutex>
#include <thread>
#include "Uri.hpp"
+13
View File
@@ -70,6 +70,7 @@ message (STATUS "Cxx flags: ${CMAKE_CXX_FLAGS}" )
add_definitions(-DBOOST_ERROR_CODE_HEADER_ONLY)
set (PIPEDAL_SOURCES
SysExec.cpp SysExec.hpp
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
WifiConfigSettings.hpp WifiConfigSettings.cpp
RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
@@ -137,6 +138,9 @@ target_link_libraries(pipedald PRIVATE pthread atomic stdc++fs
add_executable(pipedaltest ${PIPEDAL_SOURCES} testMain.cpp
jsonTest.cpp
WifiChannelsTest.cpp
SystemConfigFile.hpp SystemConfigFile.cpp
SystemConfigFileTest.cpp
)
configure_file(config.hpp.in config.hpp)
@@ -162,6 +166,10 @@ add_executable(pipedalconfig json.cpp json.hpp
WriteTemplateFile.hpp WriteTemplateFile.cpp
PiPedalException.hpp
ConfigMain.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp
SetWifiConfig.hpp SetWifiConfig.cpp
SystemConfigFile.hpp SystemConfigFile.cpp
SysExec.hpp SysExec.cpp
)
target_link_libraries(pipedalconfig PRIVATE pthread atomic stdc++fs
@@ -171,6 +179,11 @@ add_executable(pipedalshutdownd ShutdownMain.cpp CommandLineParser.hpp
JackServerSettings.hpp JackServerSettings.cpp
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp
SetWifiConfig.hpp SetWifiConfig.cpp
SystemConfigFile.hpp SystemConfigFile.cpp
SysExec.hpp SysExec.cpp
)
target_link_libraries(pipedalshutdownd PRIVATE pthread atomic stdc++fs systemd)
+178 -64
View File
@@ -5,6 +5,9 @@
#include <stdlib.h>
#include "WriteTemplateFile.hpp"
#include <fstream>
#include "SetWifiConfig.hpp"
#include "SysExec.hpp"
#include <sys/wait.h>
using namespace std;
using namespace pipedal;
@@ -12,15 +15,20 @@ using namespace pipedal;
#define SERVICE_ACCOUNT_NAME "pipedal_d"
#define SERVICE_GROUP_NAME "pipedal_d"
#define SYSTEMCTL_BIN "/usr/bin/systemctl"
#define GROUPADD_BIN "/usr/sbin/groupadd"
#define USERADD_BIN "/usr/sbin/useradd"
#define USERMOD_BIN "/usr/sbin/usermod"
#define CHGRP_BIN "/usr/bin/chgrp"
#define CHOWN_BIN "/usr/bin/chown"
#define CHMOD_BIN "/usr/bin/chmod"
#define SERVICE_PATH "/usr/lib/systemd/system"
#define NATIVE_SERVICE "pipedald"
#define SHUTDOWN_SERVICE "pipedalshutdownd"
// #define REACT_SERVICE "pipedal_react"
bool exec(std::stringstream &s)
{
return system(s.str().c_str()) == EXIT_SUCCESS;
}
std::filesystem::path GetServiceFileName(const std::string &serviceName)
{
@@ -54,22 +62,22 @@ std::filesystem::path findOnPath(const std::string &command)
void EnableService()
{
if (system("systemctl enable " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " enable " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
{
cout << "Error: Failed to enable the " NATIVE_SERVICE " service.";
}
if (system("systemctl enable " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " enable " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{
cout << "Error: Failed to enable the " SHUTDOWN_SERVICE " service.";
}
}
void DisableService()
{
if (system("systemctl disable " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " disable " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
{
cout << "Error: Failed to disable the " NATIVE_SERVICE " service.";
}
if (system("systemctl disable " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " disable " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{
cout << "Error: Failed to disable the " SHUTDOWN_SERVICE " service.";
}
@@ -77,11 +85,11 @@ void DisableService()
void StopService()
{
if (system("systemctl stop " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " stop " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
{
cout << "Error: Failed to stop the " NATIVE_SERVICE " service.";
}
if (system("systemctl stop " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " stop " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{
cout << "Error: Failed to stop the " SHUTDOWN_SERVICE " service.";
}
@@ -89,11 +97,11 @@ void StopService()
void StartService()
{
if (system("systemctl start " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " start " NATIVE_SERVICE ".service") != EXIT_SUCCESS)
{
throw PiPedalException("Failed to start the " NATIVE_SERVICE " service.");
}
if (system("systemctl start " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
if (SysExec(SYSTEMCTL_BIN " start " SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{
throw PiPedalException("Failed to start the " SHUTDOWN_SERVICE " service.");
}
@@ -135,17 +143,18 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
bool authBindRequired = port < 512;
// Create and configur service account.
// Create and configure service account.
if (system("groupadd -f " SERVICE_GROUP_NAME) != EXIT_SUCCESS)
if (SysExec(GROUPADD_BIN " -f " SERVICE_GROUP_NAME) != EXIT_SUCCESS)
{
throw PiPedalException("Failed to create service group.");
}
if (system("useradd " SERVICE_ACCOUNT_NAME " -g " SERVICE_GROUP_NAME " -M -N -r") != EXIT_SUCCESS)
if (SysExec(USERADD_BIN " " SERVICE_ACCOUNT_NAME " -g " SERVICE_GROUP_NAME " -M -N -r") != EXIT_SUCCESS)
{
// throw PiPedalException("Failed to create service account.");
}
system("usermod -a -G jack " SERVICE_ACCOUNT_NAME);
SysExec(USERMOD_BIN " -a -G jack " SERVICE_ACCOUNT_NAME);
SysExec(USERMOD_BIN " -a -G audio " SERVICE_ACCOUNT_NAME);
// create and configure /var directory.
@@ -154,24 +163,24 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
{
std::stringstream s;
s << "chgrp " SERVICE_GROUP_NAME " " << varDirectory;
system(s.str().c_str());
s << CHGRP_BIN " " SERVICE_GROUP_NAME " " << varDirectory.c_str();
SysExec(s.str().c_str());
}
{
std::stringstream s;
s << "chown " SERVICE_ACCOUNT_NAME << " " << varDirectory;
system(s.str().c_str());
s << CHOWN_BIN << " " << SERVICE_ACCOUNT_NAME << " " << varDirectory.c_str();
SysExec(s.str().c_str());
}
{
std::stringstream s;
s << "chmod 775 " << varDirectory;
system(s.str().c_str());
s << CHMOD_BIN << " 775 " << varDirectory.c_str();
SysExec(s.str().c_str());
}
{
std::stringstream s;
s << "chmod g+s " << varDirectory; // child files/directories inherit ownership.
system(s.str().c_str());
s << CHMOD_BIN << " g+s " << varDirectory.c_str(); // child files/directories inherit ownership.
SysExec(s.str().c_str());
}
// authbind port.
@@ -187,19 +196,19 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
{
// own it.
std::stringstream s;
s << "chown " SERVICE_ACCOUNT_NAME " " << portAuthFile;
exec(s);
s << CHOWN_BIN << " " SERVICE_ACCOUNT_NAME " " << portAuthFile.c_str();
SysExec(s.str().c_str());
}
{
// group own it.
std::stringstream s;
s << "chgrp " SERVICE_GROUP_NAME " " << portAuthFile;
exec(s);
s << CHGRP_BIN << " " SERVICE_GROUP_NAME " " << portAuthFile.c_str();
SysExec(s.str().c_str());
}
{
std::stringstream s;
s << "chmod 770 " << portAuthFile;
exec(s);
s << CHMOD_BIN << " 770 " << portAuthFile.c_str();
SysExec(s.str().c_str());
}
}
@@ -216,9 +225,9 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
{
s << findOnPath("authbind").string() << " --deep ";
}
s
<< (programPrefix / "bin/pipedald").string()
<< " /etc/pipedal/config /etc/pipedal/react -port " << endpointAddress << " -systemd -shutdownPort " << shutdownPort;
s
<< (programPrefix / "bin/pipedald").string()
<< " /etc/pipedal/config /etc/pipedal/react -port " << endpointAddress << " -systemd -shutdownPort " << shutdownPort;
map["COMMAND"] = s.str();
}
@@ -228,17 +237,15 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
{
std::stringstream s;
s
<< (programPrefix / "bin/pipedalshutdownd").string()
<< " -port " << shutdownPort;
s
<< (programPrefix / "bin/pipedalshutdownd").string()
<< " -port " << shutdownPort;
map["COMMAND"] = s.str();
}
WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/templateShutdown.service"), GetServiceFileName(SHUTDOWN_SERVICE));
system("systemctl daemon-reload");
SysExec(SYSTEMCTL_BIN " daemon-reload");
cout << "Starting service" << endl;
RestartService();
@@ -247,6 +254,24 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
cout << "Complete" << endl;
}
int SudoExec(char**argv) {
int pbPid;
int returnValue = 0;
if ((pbPid = fork()) == 0)
{
execv(argv[0], argv);
exit(-1);
}
else
{
waitpid(pbPid, &returnValue, 0);
int exitStatus = WEXITSTATUS(returnValue);
return exitStatus;
}
}
int main(int argc, char **argv)
{
CommandLineParser parser;
@@ -255,9 +280,12 @@ int main(int argc, char **argv)
bool help = false;
bool stop = false, start = false;
bool enable = false, disable = false, restart = false;
bool enable_ap = false, disable_ap = false;
bool nopkexec = false;
std::string prefixOption;
std::string portOption;
parser.AddOption("-nopkexec", &nopkexec); // strictly a debugging aid.
parser.AddOption("-install", &install);
parser.AddOption("-uninstall", &uninstall);
parser.AddOption("-stop", &stop);
@@ -269,11 +297,13 @@ int main(int argc, char **argv)
parser.AddOption("--help", &help);
parser.AddOption("-prefix", &prefixOption);
parser.AddOption("-port", &portOption);
parser.AddOption("-enable_ap", &enable_ap);
parser.AddOption("-disable_ap", &disable_ap);
try
{
parser.Parse(argc, (const char **)argv);
int actionCount = install + uninstall + stop + start + enable + disable;
int actionCount = install + uninstall + stop + start + enable + disable + enable_ap + disable_ap;
if (actionCount > 1)
{
throw PiPedalException("Please provide only one action.");
@@ -282,6 +312,23 @@ int main(int argc, char **argv)
{
help = true;
}
if (enable_ap)
{
if (parser.Arguments().size() != 4)
{
cout << "Error: "
<< "Incorrect number of arguments supplied for -enable_ap option." << endl;
return EXIT_FAILURE;
}
}
else
{
if (parser.Arguments().size() != 0)
{
cout << "Error: Unexpected argument : " << parser.Arguments()[0] << endl;
return EXIT_FAILURE;
}
}
}
catch (const std::exception &e)
{
@@ -290,7 +337,7 @@ int main(int argc, char **argv)
exit(EXIT_FAILURE);
}
if (help || parser.Arguments().size() != 0)
if (help)
{
cout << "pipedalconfig - Post-install configuration for pipdeal" << endl
<< "Copyright (c) 2021 Robin Davies. All rights reserved." << endl
@@ -298,20 +345,28 @@ int main(int argc, char **argv)
<< "Syntax:" << endl
<< " pipedalconfig [Options...]" << endl
<< "Options: " << endl
<< " -install Install services and service accounts." << endl
<< " -uninstall Remove installed services and service accounts." << endl
<< " -enable Start the pipedal at boot time." << endl
<< " -disable Do not start the pipedal at boot time." << endl
<< " -h --help Display this message." << endl
<< " -install Install services and service accounts." << endl
<< " -prefix Either /usr/local or /usr as appropriate for the install" << endl
<< " (only valid with the -install option). Usually determined " << endl
<< " automatically." << endl
<< " -uninstall Remove installed services and service accounts." << endl
<< " -enable Start the pipedal service at boot time." << endl
<< " -disable Do not start the pipedal service at boot time." << endl
<< " -stop Stop the pipedal services." << endl
<< " -start Start the pipedal services." << endl
<< " -restart Restart the pipedal services." << endl
<< " -prefix Either /usr/local or /usr as appropriate for the install" << endl
<< " (only valid with the -install option)." << endl
<< " -port Port for the web server (only with -install option)." << endl
<< " Either a port (e.g. '81'), or a full endpoint (e.g.: " << endl
<< " '127.0.0.1:8080'). If no address is specified, the " << endl
<< " web server will listen on 0.0.0.0 (any)." << endl
<< " -enable_ap <country_code> <ssid> <wep_password> <channel>" << endl
<< " Enable the Wi-Fi access point. <country_code> is the 2-letter " << endl
<< " ISO-3166 country code for the country in which you are currently" << endl
<< " located. The country code determines which channels may be legally" << endl
<< " used in (and which features need to be enabled) in order to use a " << endl
<< " Wi-Fi channel in your legislative regime." << endl
<< " -disable_ap Disabled the Wi-Fi access point." << endl
<< endl
<< "Description:" << endl
<< " pipedalconfig performs post-install configuration of pipedal." << endl
@@ -322,29 +377,67 @@ int main(int argc, char **argv)
{
cout << "Error: -port option can only be specified with the -install option." << endl;
exit(EXIT_FAILURE);
}
auto uid = getuid();
if (uid != 0 && !nopkexec)
{
// re-execute with PKEXEC in order to prompt form SUDO credentials.
std::vector<char*> args;
std::string pkexec = "/usr/bin/pkexec"; // staged because "ISO C++ forbids converting a string constant to std::vector<char*>::value_type"(!)
args.push_back((char*)(pkexec.c_str()));
std::filesystem::path path = std::filesystem::absolute(argv[0]);
std::string sPath = path;
args.push_back((char*)path.c_str());
for (int arg = 1; arg < argc; ++arg)
{
args.push_back((char*)argv[arg]);
}
std::string prefixArg; // lifetime management for the prefix arguments
std::string prefixOptionArg;
if (prefixOption.length() == 0)
{
std::filesystem::path prefix = std::filesystem::path(argv[0]).parent_path().parent_path();
prefixOptionArg = "-prefix";
args.push_back((char*)(prefixOptionArg.c_str()));
prefixArg = prefix;
args.push_back((char*)prefixArg.c_str());
}
args.push_back(nullptr);
char**rawArgv = &args[0];
return SudoExec(rawArgv);
}
try
{
std::filesystem::path prefix;
if (prefixOption.length() != 0)
{
prefix = std::filesystem::path(prefixOption);
}
else
{
prefix = std::filesystem::path(argv[0]).parent_path().parent_path();
std::filesystem::path pipedalPath = prefix / "bin/pipedald";
if (!std::filesystem::exists(pipedalPath))
{
std::stringstream s;
s << "Can't find pipedald executable at " << pipedalPath << ". Try again using the -prefix option.";
throw PiPedalException(s.str());
}
}
if (install)
{
std::filesystem::path prefix;
if (prefixOption.length() != 0)
{
prefix = std::filesystem::path(prefixOption);
}
else
{
prefix = std::filesystem::path(argv[0]).parent_path().parent_path();
std::filesystem::path pipedalPath = prefix / "bin/pipedald";
if (!std::filesystem::exists(pipedalPath))
{
std::stringstream s;
s << "Can't find pipedald executable at " << pipedalPath << ". Try again using the -prefix option.";
throw PiPedalException(s.str());
}
}
if (portOption == "")
{
portOption = "80";
@@ -378,6 +471,27 @@ int main(int argc, char **argv)
{
DisableService();
}
else if (enable_ap)
{
auto argv = parser.Arguments();
WifiConfigSettings settings;
settings.valid_ = true;
settings.enable_ = true;
settings.countryCode_ = argv[0];
settings.hotspotName_ = argv[1];
settings.password_ = argv[2];
settings.channel_ = argv[3];
settings.hasPassword_ = settings.password_.length() != 0;
SetWifiConfig(settings);
}
else if (disable_ap)
{
WifiConfigSettings settings;
settings.valid_ = true;
settings.enable_ = false;
SetWifiConfig(settings);
}
}
catch (const std::exception &e)
{
+2
View File
@@ -11,6 +11,8 @@
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/lv2plug.in/ns/extensions/units/units.h"
#include "lv2/atom/forge.h"
namespace pipedal
{
+3
View File
@@ -21,9 +21,12 @@
#pragma once
#include <stdint.h>
#include <cassert>
#include <lv2/core/lv2.h>
#include <lv2/atom/atom.h>
#include <lv2/midi/midi.h>
#include <lv2/urid/urid.h>
#include <cstring>
namespace pipedal
{
+15
View File
@@ -1,5 +1,20 @@
#include "pch.h"
#include "OptionsFeature.hpp"
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/core/lv2_util.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
#include "lv2/midi/midi.h"
#include "lv2/urid/urid.h"
#include "lv2/log/logger.h"
#include "lv2/uri-map/uri-map.h"
#include "lv2/atom/forge.h"
#include "lv2/worker/worker.h"
#include "lv2/patch/patch.h"
#include "lv2/parameters/parameters.h"
#include "lv2/units/units.h"
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
#include "lv2/lv2plug.in/ns/ext/buf-size/buf-size.h"
using namespace pipedal;
+3 -1
View File
@@ -459,7 +459,9 @@ bool PiPedalModel::renamePreset(int64_t clientId, int64_t instanceId, const std:
void PiPedalModel::setWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{
std::lock_guard lock(this->mutex);
// xxx Commit to local!
ShutdownClient::SetWifiConfig(wifiConfigSettings);
this->storage.SetWifiConfigSettings(wifiConfigSettings);
{
+2 -1
View File
@@ -13,6 +13,7 @@
#include "ShutdownClient.hpp"
#include "WifiConfigSettings.hpp"
#include "WifiChannels.hpp"
#include "SysExec.hpp"
using namespace std;
using namespace pipedal;
@@ -277,7 +278,7 @@ static void requestShutdown(bool restart)
}
s << " now";
if (system(s.str().c_str()) != EXIT_SUCCESS)
if (SysExec(s.str().c_str()) != EXIT_SUCCESS)
{
Lv2Log::error("shutdown failed.");
if (restart) {
+3 -1
View File
@@ -3,8 +3,10 @@
#include <fstream>
#include <filesystem>
#include <unordered_set>
#include <arpa/inet.h>
#include "PiPedalException.hpp"
#include <cstring>
#include <fcntl.h>
#define REGDB_MAGIC 0x52474442
#define REGDB_VERSION 19
+84
View File
@@ -0,0 +1,84 @@
#include "pch.h"
#include "SetWifiConfig.hpp"
#include "PiPedalException.hpp"
#include "SystemConfigFile.hpp"
#include "SysExec.hpp"
using namespace pipedal;
void pipedal::SetWifiConfig(const WifiConfigSettings&settings)
{
char band;
if (!settings.enable_)
{
SysExec("/usr/bin/systemctl stop hostapd");
SysExec("/usr/bin/systemctl disable hostapd");
SysExec("/usr/bin/systemctl enable wpa_supplicant");
SysExec("/usr/bin/systemctl start wpa_supplicant");
} else {
boost::filesystem::path path("/etc/hostapd/hostapd.conf");
SystemConfigFile apdConfig(path);
apdConfig.Set("ieee80211n","1",false);
apdConfig.Set("ieee80211d","1",false);
apdConfig.Set("ieee80211ac","1",false);
apdConfig.Set("ieee80211d","1",false);
apdConfig.Set("ht_capab","[HT40][SHORT-GI-20][DSSS_CCK-40]",false);
apdConfig.Set("rsn_pairwise","CCMP",false);
apdConfig.Set("wmm_enabled","1");
apdConfig.Set("auth_algs","1");
apdConfig.Set("wpa","2");
std::string hwMode = "g";
if (settings.channel_.length() > 0 && settings.channel_[0] == 'a') {
hwMode = "a"; // 5G
} else {
hwMode = "g"; // 2.4G
}
std::string channel;
if (settings.channel_[0] > '9' || settings.channel_[0] < '0')
{
channel = settings.channel_.substr(1);
} else {
channel = settings.channel_;
int iChannel = std::stoi(channel);
if (iChannel <= 14) {
hwMode = "g";
} else {
hwMode = "a";
}
}
if (settings.password_.length() != 0)
{
apdConfig.Set("wpa_passphrase",settings.password_);
}
apdConfig.Set("ssid",settings.hotspotName_);
apdConfig.Set("country_code",settings.countryCode_);
apdConfig.Set("hw_mode",hwMode);
apdConfig.Set("channel",channel);
apdConfig.Save();
SysExec("/usr/bin/systemctl daemon-reload");
SysExec("/usr/bin/systemctl mask wpa_supplicant");
SysExec("/usr/bin/systemctl stop wpa_supplicant");
if (SysExec("/usr/bin/systemctl restart hostapd") != 0)
{
throw PiPedalException("Unable to start the access point.");
}
if (SysExec("systemctl is-active --quiet hostapd") != 0)
{
throw PiPedalException("Unable to start the access point.");
}
SysExec("/usr/bin/systemctl enable hostapd");
}
}
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "WifiConfigSettings.hpp"
namespace pipedal {
void SetWifiConfig(const WifiConfigSettings&settings);
} // namespace.
+21 -3
View File
@@ -90,6 +90,10 @@ bool ShutdownClient::WriteMessage(const char*message) {
while (!eolFound)
{
ssize_t nRead = read(sock,pWrite,available);
if (nRead == 0) {
*pWrite = 0;
break;
}
if (nRead <= 0) {
Lv2Log::error("Failed to read shutdown server response.");
close(sock);
@@ -186,7 +190,21 @@ bool ShutdownClient::IsOnLocalSubnet(const std::string&fromAddress)
}
freeifaddrs(ifap);
return result;
}
void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings)
{
if (!CanUseShutdownClient())
{
throw PiPedalException("Can't use ShutdownClient when running interactively.");
}
std::stringstream cmd;
cmd << "WifiConfigSettings ";
json_writer writer(cmd,true);
writer.write(settings);
cmd << '\n';
bool result = WriteMessage(cmd.str().c_str());
if (!result) { // unexpected. Should throw exception on failure.
throw PiPedalException("Operation failed.");
}
}
+2
View File
@@ -2,6 +2,7 @@
#include <string>
#include "JackServerSettings.hpp"
#include "WifiConfigSettings.hpp"
namespace pipedal {
@@ -12,6 +13,7 @@ public:
static bool RequestShutdown(bool restart);
static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings);
static bool IsOnLocalSubnet(const std::string&fromAddress);
static void SetWifiConfig(const WifiConfigSettings & settings);
};
} // namespace
+22 -3
View File
@@ -16,6 +16,9 @@
#include <cstdlib>
#include "Lv2Log.hpp"
#include "Lv2SystemdLogger.hpp"
#include "WifiConfigSettings.hpp"
#include "SetWifiConfig.hpp"
#include "SysExec.hpp"
using namespace std;
@@ -57,6 +60,7 @@ static void ReleaseAccessPoint(const std::string gatewayAddress)
{
}
bool setJackConfiguration(uint32_t sampleRate,uint32_t bufferSize,uint32_t numberOfBuffers)
{
bool success = true;
@@ -174,7 +178,22 @@ private:
{
int result = -1;
try {
if (startsWith(s,"release_ap "))
if (startsWith(s,"WifiConfigSettings "))
{
std::string json = s.substr(strlen("WifiConfigSettings "));
std::stringstream ss(json);
WifiConfigSettings settings;
try {
json_reader reader(ss);
reader.read(&settings);
} catch (const std::exception &e)
{
throw PiPedalArgumentException("Invalid arguments.");
}
pipedal::SetWifiConfig(settings);
result = 0;
} else if (startsWith(s,"release_ap "))
{
std::vector<std::string> argv = tokenize(s);
if (argv.size() == 2) {
@@ -197,11 +216,11 @@ private:
} else if (s == "shutdown")
{
result = ::system("/usr/sbin/shutdown -P now");
result = SysExec("/usr/sbin/shutdown -P now");
}
else if (s == "restart")
{
result = ::system("/usr/sbin/shutdown -r now");
result = SysExec("/usr/sbin/shutdown -r now");
} else if (startsWith(s,"setJackConfiguration "))
{
auto remainder = s.substr(strlen("setJackConfiguration "));
+2 -1
View File
@@ -5,6 +5,7 @@
#include "json.hpp"
#include <fstream>
#include "Lv2Log.hpp"
#include <map>
using namespace pipedal;
@@ -716,7 +717,7 @@ void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSetting
copyToStore.hasPassword_ = false;
}
copyToStore.password_ = "";
copyToStore.rebootRequired_ = true;
copyToStore.rebootRequired_ = false;
if (copyToStore.enable_ && !copyToStore.hasPassword_)
{
copyToStore.hasPassword_ = this->wifiConfigSettings.hasPassword_;
+96
View File
@@ -0,0 +1,96 @@
#include "pch.h"
#include "SysExec.hpp"
#include <sys/wait.h>
#include <filesystem>
#include "PiPedalException.hpp"
#include <string.h>
#include <unistd.h>
using namespace pipedal;
using namespace std;
// find on path, but ONLY /usr/bin and /usr/sbin
static std::filesystem::path findOnSystemPath(const std::string &command)
{
if (command.length() != 0 && command[0] == '/')
{
return command;
}
std::string path = "/usr/bin:/usr/sbin";
std::vector<std::string> paths;
size_t t = 0;
while (t < path.length())
{
size_t pos = path.find(':', t);
if (pos == string::npos)
{
pos = path.length();
}
std::string thisPath = path.substr(t, pos - t);
std::filesystem::path path = std::filesystem::path(thisPath) / command;
if (std::filesystem::exists(path))
{
return path;
}
t = pos + 1;
}
std::stringstream s;
s << "'" << command << "' is not installed.";
throw PiPedalException(s.str());
}
int pipedal::SysExec(const char*szCommand) {
char*args = strdup(szCommand);
int argc;
std::vector<char*> argv;
char *p = args;
while (*p) {
argv.push_back(p);
while (*p && *p != ' ') {
++p;
}
if (*p) {
*p++ = '\0';
while (*p && *p == ' ')
{
++p;
}
}
}
argv.push_back(nullptr);
std::filesystem::path execPath = argv[0];
if (execPath.is_relative())
{
execPath = findOnSystemPath(execPath);
}
if (!std::filesystem::exists(execPath))
{
throw PiPedalException( SS("Path does not exist: " << execPath << "."));
}
argv[0] = (char*)(execPath.c_str());
char**rawArgv = &argv[0];
int pbPid;
int returnValue = 0;
if ((pbPid = fork()) == 0)
{
execv(argv[0], rawArgv);
exit(-1);
}
else
{
free((void*)args);
waitpid(pbPid, &returnValue, 0);
int exitStatus = WEXITSTATUS(returnValue);
return exitStatus;
}
}
+8
View File
@@ -0,0 +1,8 @@
#pragma once
namespace pipedal {
// exec a command, returning the actual exit code (unlike execXX() or system() )
int SysExec(const char*szCommand);
}
+213
View File
@@ -0,0 +1,213 @@
#include "pch.h"
#include "SystemConfigFile.hpp"
#include "PiPedalException.hpp"
#include <fstream>
#include <string>
#include <regex>
using namespace pipedal;
using namespace std;
void SystemConfigFile::Load(const boost::filesystem::path&path)
{
this->lines.clear();
ifstream f(path);
if (!f.is_open())
{
stringstream s;
s << "File not found: " << path;
throw PiPedalException(s.str());
}
while (true)
{
std::string line;
std::getline(f,line);
if (f.fail())
{
break;
}
lines.push_back(line);
}
this->currentPath = path;
}
int64_t SystemConfigFile::GetLine(const std::string &key) const
{
for (size_t i = 0; i < lines.size(); ++i)
{
if (LineMatches(lines[i],key))
{
return i;
}
}
return -1;
}
bool SystemConfigFile::HasValue(const std::string&key) const
{
return GetLine(key) != -1;
}
static inline std::string ValuePart(const std::string &line)
{
auto pos = line.find('=');
if (pos == std::string::npos) throw PiPedalException("Value not found.");
return line.substr(pos+1);
}
std::string SystemConfigFile::Get(const std::string&key) const
{
int64_t lineIndex = GetLine(key);
if (lineIndex == -1) throw PiPedalArgumentException("Not found.");
return ValuePart(lines[lineIndex]);
}
bool SystemConfigFile::Get(const std::string&key,std::string*pResult) const
{
int64_t lineIndex = GetLine(key);
if (lineIndex == -1) return false;
*pResult = ValuePart(lines[lineIndex]);
return true;
}
void SystemConfigFile::Set(const std::string&key,const std::string &value)
{
stringstream s;
s << key << "=" << value;
std::string line = s.str();
int lineIndex = GetLine(key);
if (lineIndex != -1) {
lines[lineIndex] = line;
} else {
lines.push_back(line);
}
}
bool SystemConfigFile::Erase(const std::string&key)
{
bool matched = false;
for (size_t i = 0; i < lines.size(); ++i)
{
if (LineMatches(lines[i],key))
{
matched = true;
lines.erase(lines.begin()+i);
--i;
}
}
return matched;
}
static std::string makeLine(const std::string&key, const std::string&value) {
stringstream s;
s << key << "=" << value;
return s.str();
}
int64_t SystemConfigFile::Insert(int64_t position, const std::string&key, const std::string&value)
{
if (position < 0 || position >= lines.size())
{
lines.push_back(makeLine(key,value));
return (int64_t)lines.size();
} else {
lines.insert(lines.begin()+position,makeLine(key,value));
return position+1;
}
}
int64_t SystemConfigFile::Insert(int64_t position, const std::string&line)
{
if (position < 0 || position >= lines.size())
{
lines.push_back(line);
return (int64_t)lines.size();
} else {
lines.insert(lines.begin()+position,line);
return position+1;
}
}
bool SystemConfigFile::LineMatches(const std::string &line, const std::string&key) const
{
// (very permissive interpretation)
int pos = 0;
while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t'))
{
++pos;
}
if (line.compare(pos,pos+key.length(),key) != 0) return false;
pos += key.length();\
while (pos < line.length() && (line[pos] == ' ' || line[pos] == '\t'))
{
++pos;
}
return pos < line.length() && line[pos] == '=';
}
void SystemConfigFile::SetLine(int64_t lineIndex,const std::string&key,const std::string &value )
{
stringstream s;
s << key << "=" << value;
if (lineIndex >= 0 && lineIndex < this->lines.size())
{
this->lines[lineIndex] = s.str();
} else {
this->lines.push_back(s.str());
}
}
void SystemConfigFile::Set(const std::string&key,const std::string &value, bool overwrite)
{
if (!overwrite)
{
if (HasValue(key)) return;
}
Set(key,value);
}
void SystemConfigFile::Set(const std::string&key,const std::string &value, const std::string&comment)
{
uint64_t lineIndex = GetLine(key);
if (lineIndex != -1)
{
SetLine(lineIndex, key, value);
} else {
lines.push_back("");
lines.push_back("# " + comment);
SetLine(-1,key,value);
}
}
void SystemConfigFile::Save(std::ostream &os)
{
for (int i = 0; i < lines.size(); ++i)
{
os << lines[i] << std::endl;
}
}
void SystemConfigFile::Save(const boost::filesystem::path&path)
{
ofstream f(path);
if (!f.is_open()) {
stringstream s;
s << "Unable to write to " << path;
throw PiPedalException(s.str());
}
Save(f);
if (f.fail())
{
stringstream s;
s << "Unable to write to " << path;
throw PiPedalException(s.str());
}
}
void SystemConfigFile::Save()
{
Save(this->currentPath);
}
+39
View File
@@ -0,0 +1,39 @@
#pragma once
#include <boost/filesystem.hpp>
namespace pipedal {
class SystemConfigFile {
boost::filesystem::path currentPath;
std::vector<std::string> lines;
void SetLine(int64_t lineIndex,const std::string&key,const std::string &value );
bool LineMatches(const std::string &line, const std::string&key) const;
public:
SystemConfigFile() {
}
SystemConfigFile(const boost::filesystem::path& path)
{
Load(path);
}
void Load(const boost::filesystem::path&path);
void Save(std::ostream &os);
void Save(const boost::filesystem::path&path);
void Save();
int64_t GetLine(const std::string &key) const;
bool HasValue(const std::string&key) const;
bool Get(const std::string&key,std::string*pResult) const;
std::string Get(const std::string&key) const;
void Set(const std::string&key,const std::string &value);
void Set(const std::string&key,const std::string &value, bool overwrite);
void Set(const std::string&key,const std::string &value, const std::string&comment);
bool Erase(const std::string&key);
int64_t Insert(int64_t position, const std::string&key, const std::string&value);
int64_t Insert(int64_t position, const std::string&line);
};
};
+35
View File
@@ -0,0 +1,35 @@
#include "pch.h"
#include "catch.hpp"
#include <sstream>
#include <cstdint>
#include <string>
#include "PiPedalException.hpp"
#include <iostream>
#include "SystemConfigFile.hpp"
using namespace pipedal;
TEST_CASE( "SystemConfigFile Test", "[system_config_file_test]" ) {
boost::filesystem::path path("/etc/hostapd/hostapd.conf");
SystemConfigFile file(path);
std::string driverName;
REQUIRE(file.Get("driver") == "nl80211");
file.Set("ssid","ssid","Name of the access point");
file.Set("xx","new_feature","A new feature.");
file.Set("ssid","newSsid");
file.Set("country_code","FR");
file.Save(std::cout);
}
+4 -1
View File
@@ -1,5 +1,7 @@
#include "pch.h"
#include <unistd.h>
#include <fcntl.h>
#include <string>
#include <net/if.h>
#include <netlink/netlink.h>
@@ -16,6 +18,7 @@
#include "Lv2Log.hpp"
#include "RegDb.hpp"
/*
Heavily based on code from iw (1) command.
https://kernel.googlesource.com/pub/scm/linux/kernel/git/jberg/iw/+/v0.9/COPYING
@@ -596,7 +599,7 @@ std::vector<WifiChannel> pipedal::getWifiChannels(const char*countryIso3661)
t << " (2.4GHz)";
if (channel.indoorOnly) {
t << " Indoor only";
t << " Indoors only";
}
ch.channelName_ = t.str();
result.push_back(ch);
+21
View File
@@ -10,9 +10,30 @@
using namespace pipedal;
static WifiChannel GetChannel(std::vector<WifiChannel> channels, const std::string&channelId)
{
for (auto &channel: channels)
{
if (channel.channelId_ == channelId)
{
return channel;
}
}
throw PiPedalException("Channel not found.");;
}
TEST_CASE( "Wifi Channel Test", "[wifi_channels_test]" ) {
std::vector<WifiChannel> result = pipedal::getWifiChannels("CA");
// channel 36 is indoors only (CA)
WifiChannel channel = GetChannel(result,"a36");
REQUIRE(channel.channelName_.find("Indoors") != std::string::npos);
// channel 36 is not indoors only (US)
result = pipedal::getWifiChannels("US");
channel = GetChannel(result,"a36");
REQUIRE(channel.channelName_.find("Indoors") == std::string::npos);
}
+7
View File
@@ -246,6 +246,13 @@ private:
public:
static std::string encode_string(const std::string &text)
{
std::stringstream s;
json_writer writer(s);
writer.write(text);
return s.str();
}
void write_raw(const char*text) {
os << text;
}
+1 -1
View File
@@ -486,5 +486,5 @@ int main(int argc, char *argv[])
Lv2Log::error(e.what());
}
Lv2Log::info("PiPedal terminating.");
return EXIT_FAILURE; // only exit in response to a signal.
return EXIT_SUCCESS; // only exit in response to a signal.
}
+6 -6
View File
@@ -11,16 +11,13 @@ template <typename T> class TypeDisplay;
#include <stdexcept>
#include <sstream>
#include <cstdlib>
#include <map>
#include "lv2/core/lv2.h"
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/http.hpp>
// #include <lilv/lilv.h>
/*
#include "lv2/atom/atom.h"
#include "lv2/atom/util.h"
#include "lv2/core/lv2.h"
#include "lv2/core/lv2_util.h"
#include "lv2/log/log.h"
#include "lv2/log/logger.h"
@@ -34,3 +31,6 @@ template <typename T> class TypeDisplay;
#include "lv2/parameters/parameters.h"
#include "lv2/units/units.h"
#include <lv2/lv2plug.in/ns/ext/worker/worker.h>
*/
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())