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
+1 -1
View File
@@ -15,7 +15,7 @@
"socketServerAddress": "0.0.0.0:8080",
/* Number of threads to use for servicing websockets */
"threads" : 5,
"threads" : 3,
"logHttpRequests": true,
/* { None=0,Error =1,Warning =2,Info = 3, Debug=4} */
+1 -1
View File
@@ -1 +1 @@
cmake --install /home/patch/src/pipedal/build --prefix /usr/local --config RelWithDebInfo
cmake --install /home/patch/src/pipedal/build --prefix /usr/local --config Release
+14
View File
@@ -82,6 +82,20 @@ add_custom_command(
src/NoChangePassword.tsx
src/XxxSnippet.tsx
public/css/roboto.css
public/fonts/Roboto-BlackItalic.woff2
public/fonts/Roboto-Black.woff2
public/fonts/Roboto-BoldItalic.woff2
public/fonts/Roboto-Bold.woff2
public/fonts/Roboto-Italic.woff2
public/fonts/Roboto-LightItalic.woff2
public/fonts/Roboto-Light.woff2
public/fonts/Roboto-MediumItalic.woff2
public/fonts/Roboto-Medium.woff2
public/fonts/Roboto-Regular.woff2
public/fonts/Roboto-ThinItalic.woff2
public/fonts/Roboto-Thin.woff2
public/iso_codes.json
public/logo512.png
public/index.html
+29
View File
@@ -0,0 +1,29 @@
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(/fonts/Roboto-Light.woff2) format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/fonts/Roboto-Regular.woff2) format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(/fonts/Roboto-Medium.woff2) format('woff2');
}
@font-face {
font-family: 'Roboto';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/fonts/Roboto-Bold.woff2) format('woff2');
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+4
View File
@@ -9,7 +9,11 @@
name="description"
content="PiPedal Guitar Pedals"
/>
<!--
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap" />
-->
<link rel="stylesheet" href="/css/roboto.css" />
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
+1 -1
View File
@@ -28,7 +28,7 @@ export type CloseEventHandler = () => void;
export type OkEventHandler = (pluginUri: string) => void;
const NARROW_DISPLAY_THRESHOLD = 600;
const FILTER_STORAGE_KEY = "com.twoplay.piddle.load_dlg.filter";
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
const pluginGridStyles = (theme: Theme) => createStyles({
+1 -1
View File
@@ -29,7 +29,7 @@ export type CloseEventHandler = () => void;
export type OkEventHandler = (pluginUri: string) => void;
const NARROW_DISPLAY_THRESHOLD = 600;
const FILTER_STORAGE_KEY = "com.twoplay.piddle.load_dlg.filter";
const FILTER_STORAGE_KEY = "com.twoplay.pipedal.load_dlg.filter";
const Transition = React.forwardRef(function Transition(
props: TransitionProps & { children?: React.ReactElement },
+2
View File
@@ -19,6 +19,7 @@ interface NoChangePasswordProps {
disabled?: boolean;
helperText?: string;
error?: boolean;
inputRef?: React.RefObject<any>
};
interface NoChangePasswordState {
@@ -88,6 +89,7 @@ class NoChangePassword extends React.Component<NoChangePasswordProps, NoChangePa
color="primary"
spellCheck="false"
autoComplete="off"
inputRef={this.props.inputRef}
readOnly
id="standard-adornment-password"
onChange={(e) => this.handleChange(e)}
+2 -2
View File
@@ -2,8 +2,8 @@ import { PiPedalArgumentError } from './PiPedalError';
import MidiBinding from './MidiBinding';
const SPLIT_PEDALBOARD_ITEM_URI = "uri://two-play/piddle/pedalboard#Split";
const EMPTY_PEDALBOARD_ITEM_URI = "uri://two-play/piddle/pedalboard#Empty";
const SPLIT_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Split";
const EMPTY_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Empty";
interface Deserializable<T> {
+2 -2
View File
@@ -16,8 +16,8 @@ import {
PedalBoard, PedalBoardItem, PedalBoardSplitItem, SplitType,
} from './PedalBoard';
const START_PEDALBOARD_ITEM_URI = "uri://two-play/piddle/pedalboard#Start";
const END_PEDALBOARD_ITEM_URI = "uri://two-play/piddle/pedalboard#End";
const START_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#Start";
const END_PEDALBOARD_ITEM_URI = "uri://two-play/pipedal/pedalboard#End";
const ENABLED_CONNECTOR_COLOR = "#666";
const DISABLED_CONNECTOR_COLOR = "#CCC";
+33 -38
View File
@@ -599,20 +599,18 @@ class PiPedalModelImpl implements PiPedalModel {
}
maxUploadSize: number = 512*1024;
maxUploadSize: number = 512 * 1024;
requestConfig(): Promise<boolean> {
const myRequest = new Request(this.varRequest('config.json'));
return fetch(myRequest)
.then(
(response) =>
{
(response) => {
return response.json();
}
)
.then(data => {
if (data.max_upload_size)
{
if (data.max_upload_size) {
this.maxUploadSize = data.max_upload_size;
}
let { socket_server_port, socket_server_address } = data;
@@ -622,7 +620,7 @@ class PiPedalModelImpl implements PiPedalModel {
if (!socket_server_port) socket_server_port = 8080;
let socket_server = this.makeSocketServerUrl(socket_server_address, socket_server_port);
let var_server_url = this.makeVarServerUrl("http", socket_server_address, socket_server_port);
this.socketServerUrl = socket_server;
this.varServerUrl = var_server_url;
@@ -684,8 +682,8 @@ class PiPedalModelImpl implements PiPedalModel {
})
.then(data => {
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
return this.getWebSocket().request<any>("getJackServerSettings");
})
.then(data => {
@@ -1421,8 +1419,7 @@ class PiPedalModelImpl implements PiPedalModel {
uploadPreset(file: File, uploadAfter: number): Promise<number> {
let result = new Promise<number>((resolve, reject) => {
try {
if (file.size > this.maxUploadSize)
{
if (file.size > this.maxUploadSize) {
reject("File is too large.");
}
let url = this.varServerUrl + "uploadPreset";
@@ -1436,7 +1433,7 @@ class PiPedalModelImpl implements PiPedalModel {
body: file,
headers: {
'Content-Type': 'application/json',
},
},
}
)
.then((response: Response) => {
@@ -1460,26 +1457,24 @@ class PiPedalModelImpl implements PiPedalModel {
});
return result;
}
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>
{
let result = new Promise<void>((resolve,reject) =>{
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void> {
let result = new Promise<void>((resolve, reject) => {
let oldSettings = this.wifiConfigSettings.get();
wifiConfigSettings = wifiConfigSettings.clone();
if ((!oldSettings.enable) && (!wifiConfigSettings.enable))
{
if ((!oldSettings.enable) && (!wifiConfigSettings.enable)) {
// no effective change.
resolve();
return;
}
let countryCodeChanged = wifiConfigSettings.countryCode !== oldSettings.countryCode;
if (!wifiConfigSettings.enable)
{
if (!wifiConfigSettings.enable) {
wifiConfigSettings.hasPassword = false;
wifiConfigSettings.hotspotName = oldSettings.hotspotName;
} else {
if (!wifiConfigSettings.hasPassword) {
if (wifiConfigSettings.hotspotName === oldSettings.hotspotName && !countryCodeChanged) {
if (wifiConfigSettings.countryCode === oldSettings.countryCode
&& wifiConfigSettings.channel === oldSettings.channel
&& wifiConfigSettings.hotspotName === oldSettings.hotspotName) {
if (!wifiConfigSettings.hasPassword) {
// no effective change.
resolve();
return;
@@ -1488,13 +1483,14 @@ class PiPedalModelImpl implements PiPedalModel {
}
// save a version for the server (potentially carrying a password)
let serverConfigSettings = wifiConfigSettings.clone();
wifiConfigSettings.password = "";
this.wifiConfigSettings.set(wifiConfigSettings);
// notify the server.
let ws = this.webSocket;
if (!ws)
{
if (!ws) {
reject("Not connected.");
return;
}
@@ -1502,29 +1498,28 @@ class PiPedalModelImpl implements PiPedalModel {
"setWifiConfigSettings",
serverConfigSettings
)
.then(()=> {
resolve();
})
.catch((err) =>{
reject(err);
});
.then(() => {
resolve();
})
.catch((err) => {
reject(err);
});
});
return result;
}
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>
{
let result = new Promise<WifiChannel[]>((resolve,reject) => {
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]> {
let result = new Promise<WifiChannel[]>((resolve, reject) => {
if (!this.webSocket) {
reject("Connection closed.");
return;
}
this.webSocket.request<WifiChannel[]>("getWifiChannels",countryIso3661)
.then((data) => {
resolve(WifiChannel.deserialize_array(data));
})
.catch((err)=> reject(err));
this.webSocket.request<WifiChannel[]>("getWifiChannels", countryIso3661)
.then((data) => {
resolve(WifiChannel.deserialize_array(data));
})
.catch((err) => reject(err));
});
return result;
}
+2 -2
View File
@@ -483,9 +483,9 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
<div style={{ width: "100%" }}>
{
this.state.restarting ? (
<Typography className={classes.primaryItem} display="block" variant="body2" color="textSecondary" noWrap>Restarting...</Typography>
<Typography className={classes.primaryItem} display="block" variant="body2" color="textSecondary" noWrap>Rebooting...</Typography>
) : (
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>Restart</Typography>
<Typography className={classes.primaryItem} display="block" variant="body2" noWrap>Reboot</Typography>
)
}
</div>
+37 -15
View File
@@ -52,11 +52,6 @@ export interface WifiConfigState {
};
let validNameRegex = /^[a-zA-Z][a-zA-Z0-9_-]*$/
function isValidName(text: string)
{
return validNameRegex.test(text);
}
const WifiConfigDialog = withStyles(styles, { withTheme: true })(
class extends ResizeResponsiveComponent<WifiConfigProps, WifiConfigState> {
@@ -105,7 +100,10 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })(
this.stateWasPopped = true;
let shouldClose = (!e.state || !e.state.WifiConfig);
if (shouldClose) {
this.props.onClose();
this.preventPasswordPrompt();
setTimeout(()=> {
this.props.onClose();
});
}
}
@@ -193,16 +191,23 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })(
if (name.length === 0) {
this.setState({nameError: true, nameErrorMessage: "* Required"});
hasError = true;
} else {
if (!isValidName(name))
{
this.setState({nameError: true, nameErrorMessage: "Invalid name"});
} else if (name.length > 31)
{
this.setState({nameError: true, nameErrorMessage: "> 31 characters"});
hasError = true;
}
let password = this.state.newPassword;
if (password.length > 0) {
if (password.length < 8) {
this.setState({passwordError: true, passwordErrorMessage: "Less than 8 characters"});
hasError = true;
} else if (name.length > 15)
{
this.setState({nameError: true, nameErrorMessage: "> 15 characters"});
} else if (password.length > 63) {
this.setState({passwordError: true, passwordErrorMessage: "> 63 characters"});
hasError = true;
}
}
}
if (this.state.enabled && (!this.props.wifiConfigSettings.hasPassword) && this.state.newPassword.length === 0)
@@ -229,11 +234,23 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })(
this.setState({showWifiWarningDialog: true});
} else {
wifiConfigSettings.wifiWarningGiven = true;
this.props.onOk(wifiConfigSettings);
this.preventPasswordPrompt();
setTimeout(()=> {
this.props.onOk(wifiConfigSettings);
});
}
}
}
preventPasswordPrompt()
{
let passwordInput = this.refPassword.current;
if (passwordInput)
{
passwordInput.value = "";
passwordInput.type = "text";
}
}
handleChannelChange(e: any)
{
let value = e.target.value as string;
@@ -257,7 +274,10 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })(
let { open, onClose} = props;
const handleClose = () => {
onClose();
this.preventPasswordPrompt();
setTimeout(()=> {
onClose();
});
};
return (
@@ -295,7 +315,9 @@ const WifiConfigDialog = withStyles(styles, { withTheme: true })(
disabled={!this.state.enabled}
/>
<div style={{ marginBottom: 16 }}>
<input type="password" style={{display:"none"}}/>
<NoChangePassword
inputRef={this.refPassword}
onPasswordChange={(text): void => { this.setState({ newPassword: text, passwordError: false, passwordErrorMessage: NBSP }); }}
hasPassword={this.props.wifiConfigSettings.hasPassword}
label="WEP Passphrase"
+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())