Configurable CPU Governor (fixed)

modified:   .vscode/launch.json
	modified:   react/CMakeLists.txt
	modified:   react/public/var/config.json
	new file:   react/src/GovernorSettings.tsx
	modified:   react/src/JackHostStatus.tsx
	new file:   react/src/ListSelectDialog.tsx
	modified:   react/src/PiPedalModel.tsx
	modified:   react/src/SettingsDialog.tsx
	modified:   react/tmp.txt
	modified:   src/BeastServer.cpp
	modified:   src/BeastServer.hpp
	modified:   src/CMakeLists.txt
	modified:   src/ConfigMain.cpp
	new file:   src/CpuGovernor.cpp
	new file:   src/CpuGovernor.hpp
	new file:   src/GovernorSettings.cpp
	new file:   src/GovernorSettings.hpp
	new file:   src/Ipv6Helpers.cpp
	new file:   src/Ipv6Helpers.hpp
	modified:   src/JackHost.cpp
	modified:   src/PiPedalModel.cpp
This commit is contained in:
Robin Davies
2022-03-09 21:35:01 -05:00
parent 5d42ec0753
commit 8ad2de8c72
31 changed files with 1642 additions and 377 deletions
+3 -1
View File
@@ -47,7 +47,9 @@
"/etc/pipedal/config", "/etc/pipedal/config",
"/etc/pipedal/react", "/etc/pipedal/react",
"-port", "-port",
"0.0.0.0:8080" "0.0.0.0:8080",
"-shutdownPort",
"3147"
], ],
"stopAtEntry": false, "stopAtEntry": false,
"cwd": "${workspaceFolder}", "cwd": "${workspaceFolder}",
+75 -76
View File
@@ -21,89 +21,88 @@ add_custom_command(
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/react
DEPENDS DEPENDS
src/NumericInput.tsx
src/RenameDialog.tsx
src/ToobInputStageView.tsx
src/ToobCabSimView.tsx
src/SearchFilter.tsx
src/Draggable.tsx
src/SelectHoverBackground.tsx
src/JackStatusView.tsx
src/SplitControlView.tsx
src/MidiBindingsDialog.tsx
src/WifiChannel.tsx
src/PluginControlView.tsx
src/TemporaryDrawer.tsx
src/logo.svg
src/AlsaDeviceInfo.tsx
src/JackServerSettingsDialog.tsx
src/ZoomedUiControl.tsx
src/PedalBoardView.tsx
src/MainPage.tsx
src/App.tsx
src/AppThemed.tsx
src/PresetDialog.tsx
src/PluginPreset.tsx
src/react-app-env.d.ts
src/ToobSpectrumResponseView.tsx
src/SettingsDialog.tsx
src/SelectChannelsDialog.tsx
src/StringBuilder.tsx
src/ToobPowerStage2View.tsx
src/SearchControl.tsx src/SearchControl.tsx
src/XxxSnippet.tsx src/SplitControlView.tsx
src/Units.tsx
src/Jack.tsx
src/PluginControl.tsx
src/ResizeResponsiveComponent.tsx
src/Lv2Plugin.tsx
src/index.tsx
src/NoChangePassword.tsx
src/VuMeter.tsx
src/ControlViewFactory.tsx
src/JackServerSettings.tsx
src/MidiBinding.tsx
src/UploadDialog.tsx
src/PiPedalSocket.tsx
src/PedalBoard.tsx
src/JackHostStatus.tsx
src/ZoomedDial.tsx
src/Rect.tsx
src/SvgPathBuilder.tsx
src/IControlViewFactory.tsx
src/PiPedalModel.tsx
src/PluginInfoDialog.tsx
src/reportWebVitals.ts
src/PiPedalError.tsx
src/BankDialog.tsx
src/PluginIcon.tsx
src/WifiConfigDialog.tsx
src/PresetSelector.tsx
src/LoadPluginDialog.tsx
src/ToobFrequencyResponseView.tsx
src/WifiConfigSettings.tsx src/WifiConfigSettings.tsx
src/setupTests.ts
src/AboutDialog.tsx
src/index.css
src/Banks.tsx
src/DialogEx.tsx
src/ToobWaveShapeView.tsx
src/FullScreenIME.tsx src/FullScreenIME.tsx
src/AppThemed.css src/JackHostStatus.tsx
src/SearchFilter.tsx
src/MainPage.tsx
src/PiPedalModel.tsx
src/PluginPresetSelector.tsx src/PluginPresetSelector.tsx
src/Utility.tsx
src/SelectMidiChannelsDialog.tsx
src/App.test.tsx
src/MidiBindingView.tsx
src/SplitUiControls.tsx
src/PluginClass.tsx
src/ObservableProperty.tsx
src/Pedal.tsx src/Pedal.tsx
src/GxTunerControl.tsx src/ToobMLView.tsx
src/DraggableGrid.tsx src/SettingsDialog.tsx
src/PiPedalSocket.tsx
src/ToobFrequencyResponseView.tsx
src/ControlViewFactory.tsx
src/Utility.tsx
src/ToobToneStackView.tsx src/ToobToneStackView.tsx
src/SelectHoverBackground.tsx
src/MidiBindingsDialog.tsx
src/TemporaryDrawer.tsx
src/GxTunerView.tsx
src/ToobInputStageView.tsx
src/DraggableGrid.tsx
src/JackStatusView.tsx
src/PluginControl.tsx
src/XxxSnippet.tsx
src/PluginPreset.tsx
src/Jack.tsx
src/AlsaDeviceInfo.tsx
src/MidiBinding.tsx
src/PiPedalError.tsx
src/IControlViewFactory.tsx
src/Lv2Plugin.tsx
src/PedalBoard.tsx
src/PedalBoardView.tsx
src/PresetDialog.tsx
src/AppThemed.tsx
src/ZoomedDial.tsx
src/RenameDialog.tsx
src/SplitUiControls.tsx
src/GovernorSettings.tsx
src/AboutDialog.tsx
src/ToobSpectrumResponseView.tsx
src/PluginControlView.tsx
src/NoChangePassword.tsx
src/ToobSpectrumAnalyzerView.tsx src/ToobSpectrumAnalyzerView.tsx
src/MidiBindingView.tsx
src/GxTunerControl.tsx
src/Banks.tsx
src/PluginPresetsDialog.tsx
src/BankDialog.tsx
src/UploadDialog.tsx
src/ToobPowerStage2View.tsx
src/index.tsx
src/Units.tsx
src/VuMeter.tsx
src/App.test.tsx
src/DialogEx.tsx
src/StringBuilder.tsx
src/SelectChannelsDialog.tsx
src/Draggable.tsx
src/ZoomedUiControl.tsx
src/Rect.tsx
src/PluginClass.tsx
src/PluginIcon.tsx
src/App.tsx
src/ResizeResponsiveComponent.tsx
src/SvgPathBuilder.tsx
src/NumericInput.tsx
src/JackServerSettings.tsx
src/ListSelectDialog.tsx
src/JackServerSettingsDialog.tsx
src/ObservableProperty.tsx
src/SelectMidiChannelsDialog.tsx
src/WifiChannel.tsx
src/LoadPluginDialog.tsx
src/WifiConfigDialog.tsx
src/PluginInfoDialog.tsx
src/ToobWaveShapeView.tsx
src/ToobCabSimView.tsx
src/PresetSelector.tsx
public/css/roboto.css public/css/roboto.css
public/fonts/Roboto-BlackItalic.woff2 public/fonts/Roboto-BlackItalic.woff2
public/fonts/Roboto-Black.woff2 public/fonts/Roboto-Black.woff2
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"socket_server_port": 8080, "socket_server_port": 8080,
"socket_server_address": "*", "socket_server_address": "[fe80::df30:41b9:1ede:c23c]",
"max_upload_size": 1048576, "max_upload_size": 1048576,
+35
View File
@@ -0,0 +1,35 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
export default class GovernorSettings {
deserialize(input: any) : GovernorSettings{
this.governors = input.governors;
this.governor = input.governor;
return this;
}
clone(): GovernorSettings
{
return new GovernorSettings().deserialize(this);
}
governors: string[] = ["ondemand","performance"];
governor: string = "performance";
}
+2 -2
View File
@@ -93,10 +93,10 @@ export default class JackHostStatus {
{ {
(status.cpuFreqMax === status.cpuFreqMin)? (status.cpuFreqMax === status.cpuFreqMin)?
( (
<span> {fmtCpuFreq(status.cpuFreqMax)} {status.governor} </span> <span> {status.governor} {fmtCpuFreq(status.cpuFreqMax)} </span>
) )
:( :(
<span> {fmtCpuFreq(status.cpuFreqMax)}-{fmtCpuFreq(status.cpuFreqMax)} {status.governor} </span> <span> {status.governor} {fmtCpuFreq(status.cpuFreqMax)}-{fmtCpuFreq(status.cpuFreqMax)} </span>
) )
} }
+116
View File
@@ -0,0 +1,116 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import React from 'react';
import Button from '@mui/material/Button';
import Radio from '@mui/material/Radio';
import List from '@mui/material/List';
import ListItem from '@mui/material/ListItem';
import ListItemButton from '@mui/material/ListItemButton';
import ListItemIcon from '@mui/material/ListItemIcon';
import ListItemText from '@mui/material/ListItemText';
import Dialog from '@mui/material/Dialog';
import DialogActions from '@mui/material/DialogActions';
import ResizeResponsiveComponent from './ResizeResponsiveComponent';
export interface ListSelectDialogProps {
open: boolean,
title: string,
width: number,
selectedItem: string,
items: string[],
onOk: (selectedItem: string) => void,
onClose: () => void
};
export interface ListSelectDialogState {
fullScreen: boolean;
};
export default class ListSelectDialog extends ResizeResponsiveComponent<ListSelectDialogProps, ListSelectDialogState> {
refText: React.RefObject<HTMLInputElement>;
constructor(props: ListSelectDialogProps) {
super(props);
this.state = {
fullScreen: false
};
this.refText = React.createRef<HTMLInputElement>();
}
mounted: boolean = false;
onWindowSizeChanged(width: number, height: number): void {
this.setState({ fullScreen: height < 200 })
}
componentDidMount() {
super.componentDidMount();
this.mounted = true;
}
componentWillUnmount() {
super.componentWillUnmount();
this.mounted = false;
}
componentDidUpdate() {
}
render() {
return (
<Dialog onClose={()=>this.props.onClose()} open={this.props.open}>
<List sx={{pt: 0}}>
{
this.props.items.map(
(value: string, index: number) => {
return (
<ListItem key={index} >
<ListItemButton role={undefined} onClick={()=> this.props.onOk(value)} dense>
<ListItemIcon>
<Radio
edge="start"
checked={value === this.props.selectedItem}
disableRipple
/>
</ListItemIcon>
<ListItemText primary={value} />
</ListItemButton>
</ListItem>
);
}
)
}
</List>
<DialogActions>
<Button onClick={()=> this.props.onClose()} color="primary">
Cancel
</Button>
</DialogActions>
</Dialog>
);
}
}
+64 -29
View File
@@ -31,8 +31,9 @@ import { BankIndex } from './Banks';
import JackHostStatus from './JackHostStatus'; import JackHostStatus from './JackHostStatus';
import JackServerSettings from './JackServerSettings'; import JackServerSettings from './JackServerSettings';
import MidiBinding from './MidiBinding'; import MidiBinding from './MidiBinding';
import {PluginUiPresets} from './PluginPreset'; import { PluginUiPresets } from './PluginPreset';
import WifiConfigSettings from './WifiConfigSettings'; import WifiConfigSettings from './WifiConfigSettings';
import GovernorSettings from './GovernorSettings';
import WifiChannel from './WifiChannel'; import WifiChannel from './WifiChannel';
import AlsaDeviceInfo from './AlsaDeviceInfo'; import AlsaDeviceInfo from './AlsaDeviceInfo';
@@ -316,6 +317,7 @@ export interface PiPedalModel {
banks: ObservableProperty<BankIndex>; banks: ObservableProperty<BankIndex>;
jackServerSettings: ObservableProperty<JackServerSettings>; jackServerSettings: ObservableProperty<JackServerSettings>;
wifiConfigSettings: ObservableProperty<WifiConfigSettings>; wifiConfigSettings: ObservableProperty<WifiConfigSettings>;
governorSettings: ObservableProperty<GovernorSettings>;
presets: ObservableProperty<PresetIndex>; presets: ObservableProperty<PresetIndex>;
@@ -350,7 +352,7 @@ export interface PiPedalModel {
saveCurrentPreset(): void; saveCurrentPreset(): void;
saveCurrentPresetAs(newName: string): void; saveCurrentPresetAs(newName: string): void;
saveCurrentPluginPresetAs(pluginInstanceId: number,newName: string): void; saveCurrentPluginPresetAs(pluginInstanceId: number, newName: string): void;
loadPreset(instanceId: number): void; loadPreset(instanceId: number): void;
updatePresets(presets: PresetIndex): Promise<void>; updatePresets(presets: PresetIndex): Promise<void>;
@@ -397,7 +399,7 @@ export interface PiPedalModel {
addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle addControlValueChangeListener(instanceId: number, onValueChanged: ControlValueChangedHandler): ControlValueChangedHandle
removeControlValueChangeListener(handle: ControlValueChangedHandle): void; removeControlValueChangeListener(handle: ControlValueChangedHandle): void;
addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler) : PluginPresetsChangedHandle; addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler): PluginPresetsChangedHandle;
removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void; removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void;
listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle; listenForAtomOutput(instanceId: number, onComplete: (instanceId: number, atomOutput: any) => void): ListenHandle;
@@ -405,12 +407,13 @@ export interface PiPedalModel {
download(targetType: string, isntanceId: number|string): void; download(targetType: string, isntanceId: number | string): void;
uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise<number>; uploadPreset(uploadPage: string, file: File, uploadAfter: number): Promise<number>;
uploadBank(file: File, uploadAfter: number): Promise<number>; uploadBank(file: File, uploadAfter: number): Promise<number>;
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>; setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void>;
setGovernorSettings(governor: string): Promise<void>;
getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>; getWifiChannels(countryIso3661: string): Promise<WifiChannel[]>;
@@ -447,6 +450,7 @@ class PiPedalModelImpl implements PiPedalModel {
= new ObservableProperty<JackServerSettings>(new JackServerSettings()); = new ObservableProperty<JackServerSettings>(new JackServerSettings());
wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings()); wifiConfigSettings: ObservableProperty<WifiConfigSettings> = new ObservableProperty<WifiConfigSettings>(new WifiConfigSettings());
governorSettings: ObservableProperty<GovernorSettings> = new ObservableProperty<GovernorSettings>(new GovernorSettings());
presets: ObservableProperty<PresetIndex> = new ObservableProperty<PresetIndex> presets: ObservableProperty<PresetIndex> = new ObservableProperty<PresetIndex>
@@ -575,6 +579,12 @@ class PiPedalModelImpl implements PiPedalModel {
} else if (message === "onWifiConfigSettingsChanged") { } else if (message === "onWifiConfigSettingsChanged") {
let wifiConfigSettings = new WifiConfigSettings().deserialize(body); let wifiConfigSettings = new WifiConfigSettings().deserialize(body);
this.wifiConfigSettings.set(wifiConfigSettings); this.wifiConfigSettings.set(wifiConfigSettings);
}
else if (message === "onGovernorSettingsChanged") {
let governor = body as string;
let newSettings = this.governorSettings.get().clone();
newSettings.governor = governor;
this.governorSettings.set(newSettings);
} else if (message === "onBanksChanged") { } else if (message === "onBanksChanged") {
let banks = new BankIndex().deserialize(body); let banks = new BankIndex().deserialize(body);
this.banks.set(banks); this.banks.set(banks);
@@ -660,6 +670,12 @@ class PiPedalModelImpl implements PiPedalModel {
.then(data => { .then(data => {
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data)); this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
return this.getWebSocket().request<any>("getGovernorSettings");
})
.then(data => {
this.governorSettings.set(new GovernorSettings().deserialize(data));
return this.getWebSocket().request<any>("getJackServerSettings"); return this.getWebSocket().request<any>("getJackServerSettings");
}) })
.then(data => { .then(data => {
@@ -792,6 +808,11 @@ class PiPedalModelImpl implements PiPedalModel {
.then(data => { .then(data => {
this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data)); this.wifiConfigSettings.set(new WifiConfigSettings().deserialize(data));
return this.getWebSocket().request<any>("getGovernorSettings");
})
.then(data => {
this.governorSettings.set(new GovernorSettings().deserialize(data));
return this.getWebSocket().request<any>("getJackServerSettings"); return this.getWebSocket().request<any>("getJackServerSettings");
}) })
@@ -994,7 +1015,7 @@ class PiPedalModelImpl implements PiPedalModel {
_pluginPresetsChangedHandles: PluginPresetsChangedHandle[] = []; _pluginPresetsChangedHandles: PluginPresetsChangedHandle[] = [];
addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler) : PluginPresetsChangedHandle { addPluginPresetsChangedListener(onPluginPresetsChanged: PluginPresetsChangedHandler): PluginPresetsChangedHandle {
let handle = ++this.nextListenHandle; let handle = ++this.nextListenHandle;
let t: PluginPresetsChangedHandle = { let t: PluginPresetsChangedHandle = {
_id: handle, _id: handle,
@@ -1003,28 +1024,23 @@ class PiPedalModelImpl implements PiPedalModel {
this._pluginPresetsChangedHandles.push(t); this._pluginPresetsChangedHandles.push(t);
return t; return t;
} }
removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void removePluginPresetsChangedListener(handle: PluginPresetsChangedHandle): void {
{
let pos = -1; let pos = -1;
for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i) for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i) {
{ if (this._pluginPresetsChangedHandles[i]._id === handle._id) {
if (this._pluginPresetsChangedHandles[i]._id === handle._id)
{
pos = i; pos = i;
break; break;
} }
} }
if (pos !== -1) if (pos !== -1) {
{ this._pluginPresetsChangedHandles.splice(pos, 1);
this._pluginPresetsChangedHandles.splice(pos,1);
} }
} }
firePluginPresetsChanged(pluginUri: string): void { firePluginPresetsChanged(pluginUri: string): void {
for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i) for (let i = 0; i < this._pluginPresetsChangedHandles.length; ++i) {
{
this._pluginPresetsChangedHandles[i]._handler(pluginUri); this._pluginPresetsChangedHandles[i]._handler(pluginUri);
} }
} }
@@ -1298,7 +1314,7 @@ class PiPedalModelImpl implements PiPedalModel {
return data; return data;
}); });
} }
saveCurrentPluginPresetAs(pluginInstanceId: number,newName: string): Promise<number> { saveCurrentPluginPresetAs(pluginInstanceId: number, newName: string): Promise<number> {
// default behaviour is to save after the currently selected preset. // default behaviour is to save after the currently selected preset.
let request: any = { let request: any = {
instanceId: pluginInstanceId, instanceId: pluginInstanceId,
@@ -1567,22 +1583,21 @@ class PiPedalModelImpl implements PiPedalModel {
this.webSocket?.send("loadPluginPreset", { pluginInstanceId: pluginInstanceId, presetInstanceId: presetInstanceId }); this.webSocket?.send("loadPluginPreset", { pluginInstanceId: pluginInstanceId, presetInstanceId: presetInstanceId });
} }
handlePluginPresetsChanged(pluginUri: string):void { handlePluginPresetsChanged(pluginUri: string): void {
this.uncachePluginPreset(pluginUri); this.uncachePluginPreset(pluginUri);
this.firePluginPresetsChanged(pluginUri); this.firePluginPresetsChanged(pluginUri);
} }
updatePluginPresets(pluginUri: string, presets: PluginUiPresets): Promise<void> updatePluginPresets(pluginUri: string, presets: PluginUiPresets): Promise<void> {
{
this.presetCache[pluginUri] = presets; this.presetCache[pluginUri] = presets;
this.firePluginPresetsChanged(pluginUri); this.firePluginPresetsChanged(pluginUri);
return nullCast(this.webSocket).request<void>("updatePluginPresets", presets); return nullCast(this.webSocket).request<void>("updatePluginPresets", presets);
} }
duplicatePluginPreset(pluginUri: string, instanceId: number): Promise<number> duplicatePluginPreset(pluginUri: string, instanceId: number): Promise<number> {
{ return nullCast(this.webSocket).request<number>("copyPluginPreset", {
return nullCast(this.webSocket).request<number>("copyPluginPreset", { pluginUri: pluginUri, instanceId: instanceId
pluginUri: pluginUri, instanceId: instanceId }); });
} }
@@ -1692,7 +1707,7 @@ class PiPedalModelImpl implements PiPedalModel {
window.open(url, "_blank"); window.open(url, "_blank");
} }
uploadPreset(uploadPage: string,file: File, uploadAfter: number): Promise<number> { uploadPreset(uploadPage: string, file: File, uploadAfter: number): Promise<number> {
let result = new Promise<number>((resolve, reject) => { let result = new Promise<number>((resolve, reject) => {
try { try {
if (file.size > this.maxUploadSize) { if (file.size > this.maxUploadSize) {
@@ -1775,6 +1790,27 @@ class PiPedalModelImpl implements PiPedalModel {
}); });
return result; return result;
} }
setGovernorSettings(governor: string): Promise<void> {
let newSettings = this.governorSettings.get().clone();
newSettings.governor = governor;
this.governorSettings.set(newSettings);
return new Promise<void>((resolve, reject) => {
let ws = this.webSocket;
if (!ws) return;
ws.request<void>(
"setGovernorSettings",
governor
)
.then(() => {
resolve();
})
.catch((err) => {
reject(err);
});
});
}
setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void> { setWifiConfigSettings(wifiConfigSettings: WifiConfigSettings): Promise<void> {
let result = new Promise<void>((resolve, reject) => { let result = new Promise<void>((resolve, reject) => {
let oldSettings = this.wifiConfigSettings.get(); let oldSettings = this.wifiConfigSettings.get();
@@ -1866,8 +1902,7 @@ class PiPedalModelImpl implements PiPedalModel {
preloadImages(imageList: string): void { preloadImages(imageList: string): void {
let imageNames = imageList.split(';'); let imageNames = imageList.split(';');
for (let i = 0; i < imageNames.length; ++i) for (let i = 0; i < imageNames.length; ++i) {
{
let imageName = imageNames[i]; let imageName = imageNames[i];
let img = new Image(); let img = new Image();
img.src = "/img/" + imageName; img.src = "/img/" + imageName;
+77 -4
View File
@@ -18,6 +18,7 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
import React, { SyntheticEvent, Component } from 'react'; import React, { SyntheticEvent, Component } from 'react';
import ListSelectDialog from './ListSelectDialog';
import IconButton from '@mui/material/IconButton'; import IconButton from '@mui/material/IconButton';
import Typography from '@mui/material/Typography'; import Typography from '@mui/material/Typography';
import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel'; import { PiPedalModel, PiPedalModelFactory, State } from './PiPedalModel';
@@ -36,6 +37,7 @@ import JackHostStatus from './JackHostStatus';
import WifiConfigSettings from './WifiConfigSettings'; import WifiConfigSettings from './WifiConfigSettings';
import WifiConfigDialog from './WifiConfigDialog'; import WifiConfigDialog from './WifiConfigDialog';
import DialogEx from './DialogEx' import DialogEx from './DialogEx'
import GovernorSettings from './GovernorSettings';
import Slide, {SlideProps} from '@mui/material/Slide'; import Slide, {SlideProps} from '@mui/material/Slide';
import { createStyles, Theme } from '@mui/material/styles'; import { createStyles, Theme } from '@mui/material/styles';
@@ -55,10 +57,12 @@ interface SettingsDialogState {
jackSettings: JackChannelSelection; jackSettings: JackChannelSelection;
jackServerSettings: JackServerSettings; jackServerSettings: JackServerSettings;
jackStatus?: JackHostStatus; jackStatus?: JackHostStatus;
governorSettings: GovernorSettings;
wifiConfigSettings: WifiConfigSettings; wifiConfigSettings: WifiConfigSettings;
showWifiConfigDialog: boolean; showWifiConfigDialog: boolean;
showGovernorSettingsDialog: boolean;
showInputSelectDialog: boolean; showInputSelectDialog: boolean;
showOutputSelectDialog: boolean; showOutputSelectDialog: boolean;
showMidiSelectDialog: boolean; showMidiSelectDialog: boolean;
@@ -146,7 +150,9 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
jackStatus: undefined, jackStatus: undefined,
jackSettings: this.model.jackSettings.get(), jackSettings: this.model.jackSettings.get(),
wifiConfigSettings: this.model.wifiConfigSettings.get(), wifiConfigSettings: this.model.wifiConfigSettings.get(),
governorSettings: this.model.governorSettings.get(),
showWifiConfigDialog: false, showWifiConfigDialog: false,
showGovernorSettingsDialog: false,
showInputSelectDialog: false, showInputSelectDialog: false,
showOutputSelectDialog: false, showOutputSelectDialog: false,
showMidiSelectDialog: false, showMidiSelectDialog: false,
@@ -160,6 +166,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this); this.handleJackSettingsChanged = this.handleJackSettingsChanged.bind(this);
this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this); this.handleJackServerSettingsChanged = this.handleJackServerSettingsChanged.bind(this);
this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this); this.handleWifiConfigSettingsChanged = this.handleWifiConfigSettingsChanged.bind(this);
this.handleGovernorSettingsChanged = this.handleGovernorSettingsChanged.bind(this);
this.handleConnectionStateChanged = this.handleConnectionStateChanged.bind(this); this.handleConnectionStateChanged = this.handleConnectionStateChanged.bind(this);
} }
@@ -182,6 +189,15 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.showAlert(err); this.model.showAlert(err);
}); });
} }
handleApplyGovernorSettings(governor: string): void {
this.model.setGovernorSettings(governor)
.then(() => {
})
.catch((err) => {
this.model.showAlert(err);
});
}
handleWifiConfigSettingsChanged(): void { handleWifiConfigSettingsChanged(): void {
this.setState( this.setState(
@@ -190,6 +206,14 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
} }
) )
} }
handleGovernorSettingsChanged(): void {
this.setState(
{
governorSettings: this.model.governorSettings.get()
}
)
}
handleJackSettingsChanged(): void { handleJackSettingsChanged(): void {
this.setState({ this.setState({
jackSettings: this.model.jackSettings.get() jackSettings: this.model.jackSettings.get()
@@ -233,6 +257,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged); this.model.jackConfiguration.addOnChangedHandler(this.handleJackConfigurationChanged);
this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged); this.model.jackServerSettings.addOnChangedHandler(this.handleJackServerSettingsChanged);
this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged); this.model.wifiConfigSettings.addOnChangedHandler(this.handleWifiConfigSettingsChanged);
this.model.governorSettings.addOnChangedHandler(this.handleGovernorSettingsChanged);
this.model.getJackStatus() this.model.getJackStatus()
.then((jackStatus) => .then((jackStatus) =>
this.setState( this.setState(
@@ -261,6 +286,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged); this.model.jackSettings.removeOnChangedHandler(this.handleJackSettingsChanged);
this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged); this.model.jackServerSettings.removeOnChangedHandler(this.handleJackServerSettingsChanged);
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged); this.model.wifiConfigSettings.removeOnChangedHandler(this.handleWifiConfigSettingsChanged);
this.model.wifiConfigSettings.removeOnChangedHandler(this.handleGovernorSettingsChanged);
} }
} }
@@ -314,6 +340,7 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
let newSelection = this.state.jackSettings.clone(); let newSelection = this.state.jackSettings.clone();
newSelection.inputMidiPorts = channels; newSelection.inputMidiPorts = channels;
this.model.setJackSettings(newSelection); this.model.setJackSettings(newSelection);
} }
this.setState({ this.setState({
showMidiSelectDialog: false, showMidiSelectDialog: false,
@@ -358,6 +385,11 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
showWifiConfigDialog: true showWifiConfigDialog: true
}); });
} }
handleShowGovernorSettingsDialogDialog() {
this.setState({
showGovernorSettingsDialog: true
});
}
handleRestart() { handleRestart() {
this.setState({ restarting: true }); this.setState({ restarting: true });
this.model.restart() this.model.restart()
@@ -417,19 +449,19 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
> >
<div > <div >
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary"> <Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">
AUDIO STATUS
</Typography> </Typography>
{(!isConfigValid) ? {(!isConfigValid) ?
( (
<div style={{ paddingLeft: 48, position: "relative", top: -12 }}> <div style={{ paddingLeft: 48, position: "relative", top: -12 }}>
<Typography display="block" variant="caption" color="textSecondary">Status: <span style={{ color: "#F00" }}>Not configured.</span></Typography> <Typography display="block" variant="caption" color="textSecondary">Status: <span style={{ color: "#F00" }}>Not configured.</span></Typography>
<Typography display="block" variant="caption" color="textSecondary">CPU: </Typography> <Typography display="block" variant="caption" color="textSecondary">Governor: </Typography>
</div> </div>
) : ) :
( (
<div style={{ paddingLeft: 48, position: "relative", top: -12 }}> <div style={{ paddingLeft: 48, position: "relative", top: -12 }}>
{JackHostStatus.getDisplayView("Status:\u00A0", this.state.jackStatus)} {JackHostStatus.getDisplayView("", this.state.jackStatus)}
{JackHostStatus.getCpuInfo("CPU:\u00A0", this.state.jackStatus)} { JackHostStatus.getCpuInfo("Governor:\u00A0", this.state.jackStatus)}
</div> </div>
) )
} }
@@ -442,6 +474,9 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
) )
} }
<Typography className={classes.sectionHead} display="block" variant="caption" color="secondary">
AUDIO
</Typography>
<ButtonBase className={classes.setting} onClick={() => this.handleJackServerSettings()} <ButtonBase className={classes.setting} onClick={() => this.handleJackServerSettings()}
> >
<SelectHoverBackground selected={false} showHover={true} /> <SelectHoverBackground selected={false} showHover={true} />
@@ -512,6 +547,18 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
</div> </div>
</ButtonBase> </ButtonBase>
<ButtonBase className={classes.setting} disabled={!this.state.wifiConfigSettings.valid}
onClick={() => this.handleShowGovernorSettingsDialogDialog()} >
<SelectHoverBackground selected={false} showHover={true} />
<div style={{ width: "100%" }}>
<Typography className={classes.primaryItem} display="block" variant="body2" color="textPrimary" noWrap>
CPU Governor</Typography>
<Typography display="block" variant="caption" noWrap color="textSecondary">
{this.state.governorSettings.governor }
</Typography>
</div>
</ButtonBase>
<ButtonBase className={classes.setting} disabled={!isConfigValid || disableShutdown} <ButtonBase className={classes.setting} disabled={!isConfigValid || disableShutdown}
onClick={() => this.handleRestart()} > onClick={() => this.handleRestart()} >
<SelectHoverBackground selected={false} showHover={true} /> <SelectHoverBackground selected={false} showHover={true} />
@@ -556,6 +603,32 @@ const SettingsDialog = withStyles(styles, { withTheme: true })(
) )
} }
{
(this.state.showGovernorSettingsDialog) &&
(
<ListSelectDialog
width={220}
open={this.state.showGovernorSettingsDialog}
items={this.state.governorSettings.governors}
selectedItem={this.state.governorSettings.governor}
title="Governor"
onClose={()=> this.setState({showGovernorSettingsDialog: false})}
onOk={(selectedValue) => {
this.model.setGovernorSettings(selectedValue)
.then(()=>{
})
.catch(error => {
this.model.showAlert(error);
});
this.setState({showGovernorSettingsDialog: false})
}}
>
</ListSelectDialog>
)
}
{ {
(this.state.showMidiSelectDialog) && (this.state.showMidiSelectDialog) &&
( (
+74 -76
View File
@@ -1,83 +1,81 @@
src
src/NumericInput.tsx
src/RenameDialog.tsx
src/ToobInputStageView.tsx
src/ToobCabSimView.tsx
src/SearchFilter.tsx
src/Draggable.tsx
src/SelectHoverBackground.tsx
src/JackStatusView.tsx
src/SplitControlView.tsx
src/MidiBindingsDialog.tsx
src/WifiChannel.tsx
src/PluginControlView.tsx
src/TemporaryDrawer.tsx
src/LoadPluginDialogBackup.tsx
src/logo.svg
src/AlsaDeviceInfo.tsx
src/JackServerSettingsDialog.tsx
src/ZoomedUiControl.tsx
src/PedalBoardView.tsx
src/MainPage.tsx
src/App.tsx
src/PresetDialog.tsx
src/PluginPreset.tsx
src/react-app-env.d.ts
src/ToobSpectrumResponseView.tsx
src/SettingsDialog.tsx
src/SelectChannelsDialog.tsx
src/StringBuilder.tsx
src/ToobPowerStage2View.tsx
src/SearchControl.tsx src/SearchControl.tsx
src/XxxSnippet.tsx src/SplitControlView.tsx
src/Units.tsx
src/Jack.tsx
src/PluginControl.tsx
src/ResizeResponsiveComponent.tsx
src/Lv2Plugin.tsx
src/index.tsx
src/NoChangePassword.tsx
src/VuMeter.tsx
src/ControlViewFactory.tsx
src/JackServerSettings.tsx
src/MidiBinding.tsx
src/UploadDialog.tsx
src/PiPedalSocket.tsx
src/PedalBoard.tsx
src/JackHostStatus.tsx
src/ZoomedDial.tsx
src/Rect.tsx
src/SvgPathBuilder.tsx
src/IControlViewFactory.tsx
src/PiPedalModel.tsx
src/PluginInfoDialog.tsx
src/reportWebVitals.ts
src/PiPedalError.tsx
src/BankDialog.tsx
src/PluginIcon.tsx
src/WifiConfigDialog.tsx
src/PresetSelector.tsx
src/LoadPluginDialog.tsx
src/ToobFrequencyResponseView.tsx
src/WifiConfigSettings.tsx src/WifiConfigSettings.tsx
src/setupTests.ts
src/AboutDialog.tsx
src/index.css
src/Banks.tsx
src/DialogEx.tsx
src/ToobWaveShapeView.tsx
src/FullScreenIME.tsx src/FullScreenIME.tsx
src/App.css src/JackHostStatus.tsx
src/SearchFilter.tsx
src/MainPage.tsx
src/PiPedalModel.tsx
src/PluginPresetSelector.tsx src/PluginPresetSelector.tsx
src/Utility.tsx
src/SelectMidiChannelsDialog.tsx
src/App.test.tsx
src/MidiBindingView.tsx
src/SplitUiControls.tsx
src/PluginClass.tsx
src/ObservableProperty.tsx
src/Pedal.tsx src/Pedal.tsx
src/GxTunerControl.tsx src/ToobMLView.tsx
src/DraggableGrid.tsx src/SettingsDialog.tsx
src/PiPedalSocket.tsx
src/ToobFrequencyResponseView.tsx
src/ControlViewFactory.tsx
src/Utility.tsx
src/ToobToneStackView.tsx src/ToobToneStackView.tsx
src/SelectHoverBackground.tsx
src/MidiBindingsDialog.tsx
src/TemporaryDrawer.tsx
src/GxTunerView.tsx
src/ToobInputStageView.tsx
src/DraggableGrid.tsx
src/JackStatusView.tsx
src/PluginControl.tsx
src/XxxSnippet.tsx
src/PluginPreset.tsx
src/Jack.tsx
src/AlsaDeviceInfo.tsx
src/MidiBinding.tsx
src/PiPedalError.tsx
src/IControlViewFactory.tsx
src/Lv2Plugin.tsx
src/PedalBoard.tsx
src/PedalBoardView.tsx
src/PresetDialog.tsx
src/AppThemed.tsx
src/ZoomedDial.tsx
src/RenameDialog.tsx
src/SplitUiControls.tsx
src/GovernorSettings.tsx
src/AboutDialog.tsx
src/ToobSpectrumResponseView.tsx
src/PluginControlView.tsx
src/NoChangePassword.tsx
src/ToobSpectrumAnalyzerView.tsx src/ToobSpectrumAnalyzerView.tsx
src/MidiBindingView.tsx
src/GxTunerControl.tsx
src/Banks.tsx
src/PluginPresetsDialog.tsx
src/BankDialog.tsx
src/UploadDialog.tsx
src/ToobPowerStage2View.tsx
src/index.tsx
src/Units.tsx
src/VuMeter.tsx
src/App.test.tsx
src/DialogEx.tsx
src/StringBuilder.tsx
src/SelectChannelsDialog.tsx
src/Draggable.tsx
src/ZoomedUiControl.tsx
src/Rect.tsx
src/PluginClass.tsx
src/PluginIcon.tsx
src/App.tsx
src/ResizeResponsiveComponent.tsx
src/SvgPathBuilder.tsx
src/NumericInput.tsx
src/JackServerSettings.tsx
src/ListSelectDialog.tsx
src/JackServerSettingsDialog.tsx
src/ObservableProperty.tsx
src/SelectMidiChannelsDialog.tsx
src/WifiChannel.tsx
src/LoadPluginDialog.tsx
src/WifiConfigDialog.tsx
src/PluginInfoDialog.tsx
src/ToobWaveShapeView.tsx
src/ToobCabSimView.tsx
src/PresetSelector.tsx
+57 -6
View File
@@ -49,6 +49,40 @@ pipedal::last_modified(const std::filesystem::path &path)
} }
} }
static std::string getHostName()
{
char buff[512];
if (gethostname(buff,sizeof(buff)) == 0)
{
buff[511] = '\0';
return buff;
}
return "";
}
static std::string getIpv4Address(const std::string interface)
{
int fd = -1;
struct ifreq ifr;
memset(&ifr,0,sizeof(ifr));
fd = socket(AF_INET, SOCK_DGRAM, 0);
/* I want to get an IPv4 IP address */
ifr.ifr_addr.sa_family = AF_INET;
/* I want an IP address attached to "eth0" */
strncpy(ifr.ifr_name, interface.c_str(), IFNAMSIZ-1);
int result = ioctl(fd, SIOCGIFADDR, &ifr);
if (result == -1) return "";
close(fd);
char *name = inet_ntoa(((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr);
if (name == nullptr) return "";
return name;
}
static std::map<std::string, std::string> extensionsToMimeType = { static std::map<std::string, std::string> extensionsToMimeType = {
{".htm", "text/html; charset=UTF-8"}, {".htm", "text/html; charset=UTF-8"},
{".html", "text/html; charset=UTF-8"}, {".html", "text/html; charset=UTF-8"},
@@ -192,7 +226,8 @@ namespace pipedal
webSocket(webSocket) webSocket(webSocket)
{ {
uri requestUri(webSocket->get_uri()->str().c_str()); uri requestUri(webSocket->get_uri()->str().c_str());
fromAddress = requestUri.authority(); fromAddress = SS(webSocket->get_socket().remote_endpoint());
auto pFactory = pServer->GetSocketFactory(requestUri); auto pFactory = pServer->GetSocketFactory(requestUri);
if (!pFactory) if (!pFactory)
@@ -364,11 +399,14 @@ namespace pipedal
con->set_status(websocketpp::http::status_code::ok); con->set_status(websocketpp::http::status_code::ok);
return; return;
} }
for (auto requestHandler : this->request_handlers) for (auto requestHandler : this->request_handlers)
{ {
if (requestHandler->wants(req.method(), requestUri)) if (requestHandler->wants(req.method(), requestUri))
{ {
std::string fromAddress = SS(con->get_remote_endpoint());
try try
{ {
if (req.method() == HttpVerb::head) if (req.method() == HttpVerb::head)
@@ -377,7 +415,7 @@ namespace pipedal
res.set(HttpField::date, HtmlHelper::timeToHttpDate(time(nullptr))); res.set(HttpField::date, HtmlHelper::timeToHttpDate(time(nullptr)));
res.set(HttpField::access_control_allow_origin, origin); res.set(HttpField::access_control_allow_origin, origin);
requestHandler->head_response(requestUri, req, res, ec); requestHandler->head_response(fromAddress,requestUri, req, res, ec);
res.keepAlive(req.keepAlive()); res.keepAlive(req.keepAlive());
if (ec == std::errc::no_such_file_or_directory) if (ec == std::errc::no_such_file_or_directory)
{ {
@@ -401,7 +439,7 @@ namespace pipedal
res.set(HttpField::cache_control, "max-age: 31104000"); // cache for a year. res.set(HttpField::cache_control, "max-age: 31104000"); // cache for a year.
requestHandler->get_response(requestUri, req, res, ec); requestHandler->get_response(fromAddress,requestUri, req, res, ec);
res.keepAlive(req.keepAlive()); res.keepAlive(req.keepAlive());
if (ec == std::errc::no_such_file_or_directory) if (ec == std::errc::no_such_file_or_directory)
@@ -425,7 +463,7 @@ namespace pipedal
res.set(HttpField::date, HtmlHelper::timeToHttpDate(time(nullptr))); res.set(HttpField::date, HtmlHelper::timeToHttpDate(time(nullptr)));
res.set(HttpField::access_control_allow_origin, origin); res.set(HttpField::access_control_allow_origin, origin);
requestHandler->post_response(requestUri, req, res, ec); requestHandler->post_response(fromAddress,requestUri, req, res, ec);
if (ec == std::errc::no_such_file_or_directory) if (ec == std::errc::no_such_file_or_directory)
{ {
@@ -547,15 +585,28 @@ namespace pipedal
m_endpoint.set_close_handler(bind(&BeastServerImpl::on_close, this, _1)); m_endpoint.set_close_handler(bind(&BeastServerImpl::on_close, this, _1));
m_endpoint.set_http_handler(bind(&BeastServerImpl::on_http, this, _1)); m_endpoint.set_http_handler(bind(&BeastServerImpl::on_http, this, _1));
std::string hostName = getHostName();
if (hostName.length() != 0)
{ {
std::stringstream ss; std::stringstream ss;
ss << "Listening on " << this->address << ":" << this->port; ss << "Listening on " << hostName << ".local:" << this->port;
Lv2Log::info(ss.str()); Lv2Log::info(ss.str());
} }
std::string ipv4Address = getIpv4Address("eth0");
if (ipv4Address.length() != 0)
{
Lv2Log::info(SS("Listening on " << ipv4Address << ":" << this->port));
}
std::string wifiAddress = getIpv4Address("wlan0");
if (wifiAddress.length() != 0)
{
Lv2Log::info(SS("Listening on Wi-Fi address " << wifiAddress << ":" << this->port));
}
std::stringstream ss; std::stringstream ss;
ss << port; ss << port;
m_endpoint.listen(this->address, ss.str()); // m_endpoint.listen(this->address, ss.str());
m_endpoint.listen(tcp::v6(),(uint16_t)port);
m_endpoint.start_accept(); m_endpoint.start_accept();
// Start IOC service threads. // Start IOC service threads.
+34 -1
View File
@@ -3,7 +3,7 @@
#include <string.h> #include <string.h>
#include <boost/asio/ip/tcp.hpp> #include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ip/network_v4.hpp> #include <boost/asio/ip/network_v6.hpp>
#include <cstring> #include <cstring>
@@ -164,12 +164,34 @@ public:
HttpResponse &res, HttpResponse &res,
std::error_code &ec) = 0; std::error_code &ec) = 0;
virtual void head_response(
const std::string&fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec) {
head_response(request_uri,req,res,ec);
}
virtual void get_response( virtual void get_response(
const uri&request_uri, const uri&request_uri,
const HttpRequest &req, const HttpRequest &req,
HttpResponse &res, HttpResponse &res,
std::error_code &ec) = 0; std::error_code &ec) = 0;
virtual void get_response(
const std::string&fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
get_response(request_uri,req,res,ec);
}
virtual void post_response( virtual void post_response(
const uri&request_uri, const uri&request_uri,
const HttpRequest &req, const HttpRequest &req,
@@ -178,6 +200,17 @@ public:
{ {
get_response(request_uri,req,res,ec); get_response(request_uri,req,res,ec);
} }
virtual void post_response(
const std::string&fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
post_response(
request_uri,req,res,ec);
}
}; };
class BeastServer { class BeastServer {
+5 -1
View File
@@ -102,7 +102,10 @@ add_custom_command(
set (PIPEDAL_SOURCES set (PIPEDAL_SOURCES
PluginPreset.cpp PluginPreset.hpp Ipv6Helpers.cpp Ipv6Helpers.hpp
PluginPreset.cpp PluginPreset.hpp
CpuGovernor.cpp CpuGovernor.hpp
GovernorSettings.cpp GovernorSettings.hpp
SysExec.cpp SysExec.hpp SysExec.cpp SysExec.hpp
BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp BeastServer.cpp BeastServer.hpp HtmlHelper.cpp HtmlHelper.hpp pch.h Uri.cpp Uri.hpp
WifiConfigSettings.hpp WifiConfigSettings.cpp WifiConfigSettings.hpp WifiConfigSettings.cpp
@@ -233,6 +236,7 @@ add_executable(pipedalshutdownd ShutdownMain.cpp CommandLineParser.hpp
SetWifiConfig.hpp SetWifiConfig.cpp SetWifiConfig.hpp SetWifiConfig.cpp
SystemConfigFile.hpp SystemConfigFile.cpp SystemConfigFile.hpp SystemConfigFile.cpp
SysExec.hpp SysExec.cpp SysExec.hpp SysExec.cpp
CpuGovernor.cpp CpuGovernor.hpp
asan_options.cpp asan_options.cpp
) )
target_link_libraries(pipedalshutdownd PRIVATE pthread atomic stdc++fs systemd) target_link_libraries(pipedalshutdownd PRIVATE pthread atomic stdc++fs systemd)
+6 -5
View File
@@ -650,7 +650,7 @@ int main(int argc, char **argv)
<< " (only valid with the -install option). Usually determined " << endl << " (only valid with the -install option). Usually determined " << endl
<< " automatically." << endl << " automatically." << endl
<< endl << endl
<< " --uninstall Remove installed services and service accounts." << endl << " --uninstall Remove installed services." << endl
<< endl << endl
<< " --enable Start the pipedal service at boot time." << endl << " --enable Start the pipedal service at boot time." << endl
<< endl << endl
@@ -668,10 +668,11 @@ int main(int argc, char **argv)
<< " web server will listen on 0.0.0.0 (any)." << endl << " web server will listen on 0.0.0.0 (any)." << endl
<< endl << endl
<< " --enable_ap <country_code> <ssid> <wep_password> <channel>" << endl << " --enable_ap <country_code> <ssid> <wep_password> <channel>" << endl
<< " Enable the Wi-Fi access point. <country_code> is the 2-letter " << endl << " Enable the Wi-Fi access point. <country_code> is the " << endl
<< " ISO-3166 country code for the country in which you are currently" << endl << " 2-letter ISO-3166 country code for the country in " << endl
<< " located. The country code determines which channels may be legally" << endl << " which you are currently located. The country code " << endl
<< " used in (and which features need to be enabled) in order to use a " << endl << " determines which channels may be legally used in (and " << endl
<< " which features need to be enabled) in order to use a " << endl
<< " Wi-Fi channel in your legislative regime." << endl << " Wi-Fi channel in your legislative regime." << endl
<< endl << endl
<< " --disable_ap Disabled the Wi-Fi access point." << endl << " --disable_ap Disabled the Wi-Fi access point." << endl
+141
View File
@@ -0,0 +1,141 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "CpuGovernor.hpp"
#include "ss.hpp"
#include <fstream>
#include <filesystem>
#include "PiPedalException.hpp"
#include <unistd.h>
using namespace pipedal;
#ifdef __WIN32__
// not a feature on Win32.
void pipedal::SetCpuGovernor(const std::string &governor) { }
std::vector<std::string> pipedal::GetAvailableGovernors() { return std::vector<std::string>(); }
#else
static const int SYSFS_RETRIES = 3;
std::string pipedal::GetCpuGovernor()
{
std::string result;
try {
std::ifstream f("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
f >> result;
} catch (const std::exception &)
{
}
return result;
}
static bool writeAndVerify(std::filesystem::path&sysfsPath, const std::string &value)
{
// return if the value has already been set.
{
std::ifstream f;
f.open(sysfsPath);
if (f.is_open())
{
std::string line;
std::getline(f,line);
if (!f.fail() && value == line)
{
return true;
}
}
}
for (int i = 0; i < SYSFS_RETRIES; ++i)
{
{
// write the value.
// write the value.
std::ofstream f;
f.open(sysfsPath);
if (f.is_open())
{
f << value;
} else {
std::cout << SS("Can't open " << sysfsPath << " for writing.") << std::endl;
sleep(1);
continue;
}
}
// verify that we wrote it
{
std::ifstream f;
f.open(sysfsPath);
if (f.is_open())
{
std::string line;
std::getline(f,line);
if (value == line)
{
return true;
}
std::cout << "Read value: " << line << "Expected value: " << value << std::endl;
}
std::cout << "Failed to update " << sysfsPath << std::endl;
}
sleep(1);
}
return false;
}
void pipedal::SetCpuGovernor(const std::string &governor) {
// using sysfs
int nCpu = 0;
while (true)
{
std::filesystem::path base = SS("/sys/devices/system/cpu/cpu" << nCpu);
if (!std::filesystem::is_directory(base))
break;
std::filesystem::path sysFsPath = SS("/sys/devices/system/cpu/cpu" << nCpu << "/cpufreq/scaling_governor");
if (!writeAndVerify(sysFsPath,governor))
{
throw PiPedalException(SS("Write to " << sysFsPath << " failed."));
break;
}
++nCpu;
}
}
std::vector<std::string> pipedal::GetAvailableGovernors() {
return std::vector<std::string> {
"performance",
"ondemand",
"powersave"
};
}
#endif
void SetCpuGovernor(const std::string &governor);
std::vector<std::string> GetAvailableGovernors();
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#include <string>
#include <vector>
namespace pipedal {
std::string GetCpuGovernor();
void SetCpuGovernor(const std::string &governor);
std::vector<std::string> GetAvailableGovernors();
};
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "GovernorSettings.hpp"
using namespace pipedal;
JSON_MAP_BEGIN(GovernorSettings)
JSON_MAP_REFERENCE(GovernorSettings, governor)
JSON_MAP_REFERENCE(GovernorSettings, governors)
JSON_MAP_END()
+36
View File
@@ -0,0 +1,36 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#include "json.hpp"
#include <string>
#include <vector>
namespace pipedal {
class GovernorSettings {
public:
std::string governor_;
std::vector<std::string> governors_;
DECLARE_JSON_MAP(GovernorSettings);
};
}
+360
View File
@@ -0,0 +1,360 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "Ipv6Helpers.hpp"
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "ss.hpp"
#include <sys/types.h>
#include <ifaddrs.h>
#include <memory.h>
#include <exception>
#include <netdb.h>
using namespace pipedal;
std::string pipedal::GetLinkLocalAddress(const std::string fromAddress);
static bool IsIpv4MappedAddress(const struct in6_addr &inetAddr6)
{
return IN6_IS_ADDR_V4MAPPED(&inetAddr6) != 0;
}
static uint32_t GetIpv4MappedAddress(const struct in6_addr &inetAddr6)
{
if (!IsIpv4MappedAddress(inetAddr6))
{
return 0;
}
uint8_t *p = (uint8_t *)&inetAddr6;
return htonl(*(uint32_t *)(p + 12));
}
static bool ipv6NetmaskCompare(const struct in6_addr &left, const struct in6_addr &right, const struct in6_addr &mask)
{
uint8_t *pLeft = (uint8_t *)&left;
uint8_t *pRight = (uint8_t *)&right;
uint8_t *pMask = (uint8_t *)&mask;
for (int i = 0; i < 16; ++i)
{
if ((pLeft[i] & pMask[i]) != (pRight[i] & pMask[i]))
{
return false;
}
}
return true;
}
static bool IsIpv4OnLocalSubnet(uint32_t ipv4Addres)
{
bool result = false;
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return false;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in *)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in *)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & ipv4Addres))
{
result = true;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
static std::string StripPortNumber(const std::string &fromAddress)
{
std::string address = fromAddress;
if (address.size() == 0)
return fromAddress;
char lastChar = address[address.size() - 1];
size_t pos = address.find_last_of(':');
// if ipv6, make sure we found an actual port address.
size_t posBracket = address.find_last_of(']');
if (posBracket != std::string::npos && pos != std::string::npos)
{
if (posBracket > pos)
{
pos = std::string::npos;
}
}
if (pos != std::string::npos)
{
address = address.substr(0, pos);
}
return address;
}
bool pipedal::IsOnLocalSubnet(const std::string &fromAddress)
{
std::string address = StripPortNumber(fromAddress);
if (address.length() == 0)
return false;
bool result = false;
if (address[0] != '[')
{
// ipv4
struct in_addr inetAddr;
memset(&inetAddr, 0, sizeof(inetAddr));
if (inet_pton(AF_INET, address.c_str(), &inetAddr) == 1)
{
uint32_t remoteAddress = htonl(inetAddr.s_addr);
result = IsIpv4OnLocalSubnet(remoteAddress);
}
}
else
{
// ipv6
if (address[0] != '[' || address[address.length() - 1] != ']')
return false;
address = address.substr(1, address.length() - 2);
// strip scope if neccessary.
auto pos = address.find_last_of('%');
if (pos != std::string::npos)
{
address = address.substr(0, pos);
}
struct in6_addr inetAddr6;
memset(&inetAddr6, 0, sizeof(inetAddr6));
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
{
int32_t remoteAddress = -1;
// cases:
// [::FFFF:ipv4 address]
// [FE80:: ] link local.
// [FEC0:: ] site local.
// others?
if (IsIpv4MappedAddress(inetAddr6))
{
uint32_t remoteAddress = GetIpv4MappedAddress(inetAddr6);
return IsIpv4OnLocalSubnet(remoteAddress);
}
if (IN6_IS_ADDR_LINKLOCAL(&inetAddr6))
{
return true;
}
if (IN6_IS_ADDR_SITELOCAL(&inetAddr6))
{
return true;
}
return false;
#if JUNK
// if the address is global, we have to reject it.
// no point in going further.
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return false;
const int SITE_LOCAL_SCOPE = 5;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
struct sockaddr_in6 *pNetMask = (struct sockaddr_in6 *)(p->ifa_netmask);
if (pAddr->sin6_scope_id <= SITE_LOCAL_SCOPE)
{
if (ipv6NetmaskCompare(inetAddr6, pAddr->sin6_addr, pNetMask->sin6_addr))
{
result = true;
break;
}
}
}
}
freeifaddrs(ifap);
#endif
}
}
return result;
}
static std::string GetInterfaceForIp4Address(uint32_t ipv4Address)
{
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return "";
std::string result;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in *)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in *)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & ipv4Address))
{
result = p->ifa_name;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
static std::string GetInterfaceForIp6Address(const in6_addr inetAddr6)
{
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return "";
std::string result;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
struct sockaddr_in6 *pNetMask = (struct sockaddr_in6 *)(p->ifa_netmask);
if (ipv6NetmaskCompare(inetAddr6, pAddr->sin6_addr, pNetMask->sin6_addr))
{
result = p->ifa_name;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
static std::string GetLinkLocalAddressForInterface(const std::string &name)
{
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0)
return "";
std::string result;
for (ifaddrs *p = ifap; p != nullptr; p = p->ifa_next)
{
if (p->ifa_addr->sa_family == AF_INET6 && p->ifa_addr != nullptr && p->ifa_netmask != nullptr)
{ // TODO: Add support for AF_INET6
struct sockaddr_in6 *pAddr = (struct sockaddr_in6 *)(p->ifa_addr);
if (IN6_IS_ADDR_LINKLOCAL(&(pAddr->sin6_addr)))
{
if (name.length() == 0 || name == p->ifa_name)
{
const int BUFSIZE = 128;
char host[BUFSIZE];
if (getnameinfo(p->ifa_addr, sizeof(struct sockaddr_in6), host, BUFSIZE, NULL, 0, NI_NUMERICHOST) == 0)
{
host[BUFSIZE - 1] = '\0';
// trim the interface spec if present
for (char *p = host; *p != 0; ++p)
{
if (*p == '%')
{
*p = '\0';
break;
}
}
result = SS('[' << host << ']');
break;
}
}
}
}
}
freeifaddrs(ifap);
return result;
}
std::string pipedal::GetLinkLocalAddress(const std::string fromAddress)
{
std::string address = StripPortNumber(fromAddress);
std::string result;
if (address[0] != '[')
{
// ipv4
struct in_addr inetAddr;
memset(&inetAddr, 0, sizeof(inetAddr));
if (inet_pton(AF_INET, address.c_str(), &inetAddr) == 1)
{
uint32_t remoteAddress = htonl(inetAddr.s_addr);
std::string interfaceName = GetInterfaceForIp4Address(remoteAddress);
result = GetLinkLocalAddressForInterface(interfaceName);
}
}
else
{
// ipv6
if (address[0] != '[' || address[address.length() - 1] != ']')
throw std::invalid_argument("Bad address.");
address = address.substr(1, address.length() - 2);
// strip scope if neccessary.
auto pos = address.find_last_of('%');
if (pos != std::string::npos)
{
address = address.substr(0, pos);
}
struct in6_addr inetAddr6;
memset(&inetAddr6, 0, sizeof(inetAddr6));
if (inet_pton(AF_INET6, address.c_str(), &inetAddr6) == 1)
{
int32_t remoteAddress = -1;
// cases:
// [::FFFF:ipv4 address]
// [FE80:: ] link local.
// [FEC0:: ] site local.
// others?
if (IN6_IS_ADDR_LINKLOCAL(&inetAddr6))
{
return address;
}
if (IN6_IS_ADDR_SITELOCAL(&inetAddr6))
{
return address;
}
std::string interfaceName;
if (IsIpv4MappedAddress(inetAddr6))
{
uint32_t remoteAddress = GetIpv4MappedAddress(inetAddr6);
interfaceName = GetInterfaceForIp4Address(remoteAddress);
}
else
{
interfaceName = GetInterfaceForIp6Address(inetAddr6);
}
result = GetLinkLocalAddressForInterface(interfaceName);
}
}
if (result == "")
{
result = GetLinkLocalAddressForInterface("");
}
return result;
}
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
namespace pipedal {
std::string GetLinkLocalAddress(const std::string fromAddress);
bool IsOnLocalSubnet(const std::string&fromAddress);
}
+2 -9
View File
@@ -33,6 +33,7 @@ using namespace pipedal;
#include <thread> #include <thread>
#include <semaphore.h> #include <semaphore.h>
#include "VuUpdate.hpp" #include "VuUpdate.hpp"
#include "CpuGovernor.hpp"
#include "RingBuffer.hpp" #include "RingBuffer.hpp"
#include "RingBufferReader.hpp" #include "RingBufferReader.hpp"
@@ -99,15 +100,7 @@ static void GetCpuFrequency(uint64_t*freqMin,uint64_t*freqMax)
} }
static std::string GetGovernor() static std::string GetGovernor()
{ {
std::string result; return pipedal::GetCpuGovernor();
try {
std::ifstream f("/sys/devices/system/cpu/cpu0/cpufreq/scaling_governor");
f >> result;
} catch (const std::exception &)
{
}
return result;
} }
+59 -8
View File
@@ -26,6 +26,7 @@
#include "PiPedalConfiguration.hpp" #include "PiPedalConfiguration.hpp"
#include "ShutdownClient.hpp" #include "ShutdownClient.hpp"
#include "SplitEffect.hpp" #include "SplitEffect.hpp"
#include "CpuGovernor.hpp"
#ifndef NO_MLOCK #ifndef NO_MLOCK
#include <sys/mman.h> #include <sys/mman.h>
@@ -76,14 +77,31 @@ void PiPedalModel::Close()
PiPedalModel::~PiPedalModel() PiPedalModel::~PiPedalModel()
{ {
CurrentPreset currentPreset; try {
currentPreset.modified_ = this->hasPresetChanged; ShutdownClient::UnmonitorGovernor();
currentPreset.preset_ = this->pedalBoard; } catch (...) // noexcept here!
storage.SaveCurrentPreset(currentPreset);
if (jackHost)
{ {
jackHost->Close();
}
try {
CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset);
} catch (...)
{
}
try {
if (jackHost)
{
jackHost->Close();
}
} catch (...)
{
} }
} }
@@ -134,7 +152,7 @@ void PiPedalModel::Load(const PiPedalConfiguration &configuration)
this->webRoot = configuration.GetWebRoot(); this->webRoot = configuration.GetWebRoot();
this->jackServerSettings.ReadJackConfiguration(); this->jackServerSettings.ReadJackConfiguration();
ShutdownClient::MonitorGovernor(storage.GetGovernorSettings());
// lv2Host.Load(configuration.GetLv2Path().c_str()); // lv2Host.Load(configuration.GetLv2Path().c_str());
@@ -594,6 +612,39 @@ bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std:
} }
} }
GovernorSettings PiPedalModel::GetGovernorSettings()
{
{
std::lock_guard<std::recursive_mutex> guard(mutex);
GovernorSettings result;
result.governor_ = storage.GetGovernorSettings();
result.governors_ = pipedal::GetAvailableGovernors();
return result;
}
}
void PiPedalModel::SetGovernorSettings(const std::string& governor)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
ShutdownClient::SetGovernorSettings(governor);
this->storage.SetGovernorSettings(governor);
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
{
for (size_t i = 0; i < subscribers.size(); ++i)
{
t[i] = this->subscribers[i];
}
size_t n = this->subscribers.size();
for (size_t i = 0; i < n; ++i)
{
t[i]->OnGovernorSettingsChanged(governor);
}
delete[] t;
}
}
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings) void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{ {
std::lock_guard<std::recursive_mutex> guard(mutex); std::lock_guard<std::recursive_mutex> guard(mutex);
+5
View File
@@ -20,6 +20,7 @@
#pragma once #pragma once
#include <mutex> #include <mutex>
#include "Lv2Host.hpp" #include "Lv2Host.hpp"
#include "GovernorSettings.hpp"
#include "PedalBoard.hpp" #include "PedalBoard.hpp"
#include "Storage.hpp" #include "Storage.hpp"
#include "Banks.hpp" #include "Banks.hpp"
@@ -57,6 +58,7 @@ public:
virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0; virtual void OnNotifyMidiListener(int64_t clientHandle, bool isNote, uint8_t noteOrControl) = 0;
virtual void OnNotifyAtomOutput(int64_t clientModel,uint64_t instanceId,const std::string&atomType, const std::string&atomJson) = 0; virtual void OnNotifyAtomOutput(int64_t clientModel,uint64_t instanceId,const std::string&atomType, const std::string&atomJson) = 0;
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0; virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) = 0;
virtual void OnGovernorSettingsChanged(const std::string &governor) = 0;
virtual void Close() = 0; virtual void Close() = 0;
}; };
@@ -193,6 +195,9 @@ public:
void SetWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings); void SetWifiConfigSettings(const WifiConfigSettings&wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings(); WifiConfigSettings GetWifiConfigSettings();
void SetGovernorSettings(const std::string& governor);
GovernorSettings GetGovernorSettings();
+20 -1
View File
@@ -28,6 +28,7 @@
#include "JackConfiguration.hpp" #include "JackConfiguration.hpp"
#include <future> #include <future>
#include <atomic> #include <atomic>
#include "Ipv6Helpers.hpp"
#include "ShutdownClient.hpp" #include "ShutdownClient.hpp"
#include "WifiConfigSettings.hpp" #include "WifiConfigSettings.hpp"
@@ -804,6 +805,17 @@ public:
this->Reply(replyTo,"setJackserverSettings"); this->Reply(replyTo,"setJackserverSettings");
} }
else if (message == "setGovernorSettings") {
std::string governor;
pReader->read(&governor);
std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
this->model.SetGovernorSettings(governor);
this->Reply(replyTo,"setGovernorSettings");
}
else if (message == "setWifiConfigSettings") { else if (message == "setWifiConfigSettings") {
WifiConfigSettings wifiConfigSettings; WifiConfigSettings wifiConfigSettings;
pReader->read(&wifiConfigSettings); pReader->read(&wifiConfigSettings);
@@ -812,7 +824,7 @@ public:
throw PiPedalException("Can't change server settings when running interactively."); throw PiPedalException("Can't change server settings when running interactively.");
} }
std::string fromAddress = this->getFromAddress(); std::string fromAddress = this->getFromAddress();
if (!ShutdownClient::IsOnLocalSubnet(fromAddress)) if (!IsOnLocalSubnet(fromAddress))
{ {
throw PiPedalException("Permission denied. Not on local subnet."); throw PiPedalException("Permission denied. Not on local subnet.");
} }
@@ -824,6 +836,10 @@ public:
else if (message == "getWifiConfigSettings") { else if (message == "getWifiConfigSettings") {
this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings()); this->Reply(replyTo, "getWifiConfigSettings", model.GetWifiConfigSettings());
}
else if (message == "getGovernorSettings") {
this->Reply(replyTo, "getGovernorSettings", model.GetGovernorSettings());
} }
else if (message == "getJackServerSettings") { else if (message == "getJackServerSettings") {
@@ -1461,6 +1477,9 @@ public:
virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) { virtual void OnWifiConfigSettingsChanged(const WifiConfigSettings&wifiConfigSettings) {
Send("onWifiConfigSettingsChanged",wifiConfigSettings); Send("onWifiConfigSettingsChanged",wifiConfigSettings);
} }
virtual void OnGovernorSettingsChanged(const std::string& governor) {
Send("onGovernorSettingsChanged",governor);
}
virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard) virtual void OnPedalBoardChanged(int64_t clientId, const PedalBoard &pedalBoard)
{ {
+52 -46
View File
@@ -27,6 +27,7 @@
#include <sstream> #include <sstream>
#include <sys/types.h> #include <sys/types.h>
#include <ifaddrs.h> #include <ifaddrs.h>
#include "Ipv6Helpers.hpp"
using namespace pipedal; using namespace pipedal;
@@ -165,57 +166,12 @@ bool ShutdownClient::SetJackServerConfiguration(const JackServerSettings & jackS
return WriteMessage(s.str().c_str()); return WriteMessage(s.str().c_str());
} }
bool ShutdownClient::IsOnLocalSubnet(const std::string&fromAddress)
{
std::string address = fromAddress;
if (address.size() == 0) return false;
char lastChar = address[address.size()-1];
if (address[0] != '[' || lastChar != ']') // i.e. not an ipv6 address;
{
size_t pos = address.find_last_of(':');
if (pos != std::string::npos)
{
address = address.substr(0,pos);
}
}
if (address[0] == '[') return false;
char buf[512];
struct in_addr inetAddr;
memset(&inetAddr,0,sizeof(inetAddr));
if (inet_pton(AF_INET,address.c_str(),&inetAddr) == 0) {
return false;
}
uint32_t remoteAddress = htonl(inetAddr.s_addr);
struct ifaddrs *ifap = nullptr;
if (getifaddrs(&ifap) != 0) return false;
bool result = false;
for (ifaddrs*p = ifap; p != nullptr; p = p->ifa_next) {
if (p->ifa_addr->sa_family == AF_INET && p->ifa_addr != nullptr && p->ifa_netmask != nullptr) { // TODO: Add support for AF_INET6
uint32_t netmask = htonl(((sockaddr_in*)(p->ifa_netmask))->sin_addr.s_addr);
uint32_t ifAddr = htonl(((sockaddr_in*)(p->ifa_addr))->sin_addr.s_addr);
if ((netmask & ifAddr) == (netmask & remoteAddress))
{
result = true;
break;
}
}
}
freeifaddrs(ifap);
return result;
}
void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings) void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings)
{ {
if (!CanUseShutdownClient()) if (!CanUseShutdownClient())
{ {
throw PiPedalException("Can't use ShutdownClient when running interactively."); throw PiPedalException("Can't perform this operation when debugging.");
} }
std::stringstream cmd; std::stringstream cmd;
cmd << "WifiConfigSettings "; cmd << "WifiConfigSettings ";
@@ -227,3 +183,53 @@ void ShutdownClient::SetWifiConfig(const WifiConfigSettings & settings)
throw PiPedalException("Operation failed."); throw PiPedalException("Operation failed.");
} }
} }
void ShutdownClient::SetGovernorSettings(const std::string & settings)
{
if (!CanUseShutdownClient())
{
throw PiPedalException("Can't use ShutdownClient when running interactively.");
}
std::stringstream cmd;
cmd << "GovernorSettings ";
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.");
}
}
void ShutdownClient::MonitorGovernor(const std::string &governor)
{
if (!CanUseShutdownClient())
{
return;
}
std::stringstream cmd;
cmd << "MonitorGovernor ";
json_writer writer(cmd,true);
writer.write(governor);
cmd << '\n';
bool result = WriteMessage(cmd.str().c_str());
if (!result) { // unexpected. Should throw exception on failure.
throw PiPedalException("Operation failed.");
}
}
void ShutdownClient::UnmonitorGovernor()
{
if (!CanUseShutdownClient())
{
return;
}
std::stringstream cmd;
cmd << "UnmonitorGovernor";
cmd << '\n';
bool result = WriteMessage(cmd.str().c_str());
if (!result) { // unexpected. Should throw exception on failure.
throw PiPedalException("Operation failed.");
}
}
+4 -1
View File
@@ -25,14 +25,17 @@
namespace pipedal { namespace pipedal {
class ShutdownClient { class ShutdownClient {
static bool WriteMessage(const char*message); static bool WriteMessage(const char*message);
public: public:
static bool CanUseShutdownClient(); static bool CanUseShutdownClient();
static bool RequestShutdown(bool restart); static bool RequestShutdown(bool restart);
static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings); static bool SetJackServerConfiguration(const JackServerSettings & jackServerSettings);
static bool IsOnLocalSubnet(const std::string&fromAddress);
static void SetWifiConfig(const WifiConfigSettings & settings); static void SetWifiConfig(const WifiConfigSettings & settings);
static void SetGovernorSettings(const std::string & governor);
static void MonitorGovernor(const std::string &governor);
static void UnmonitorGovernor();
}; };
} // namespace } // namespace
+212 -103
View File
@@ -23,6 +23,7 @@
#include "pch.h" #include "pch.h"
#include "CommandLineParser.hpp" #include "CommandLineParser.hpp"
#include "CpuGovernor.hpp"
#include <iostream> #include <iostream>
#include <cstdint> #include <cstdint>
#include <boost/asio.hpp> #include <boost/asio.hpp>
@@ -40,7 +41,10 @@
#include "SetWifiConfig.hpp" #include "SetWifiConfig.hpp"
#include "SysExec.hpp" #include "SysExec.hpp"
#include <filesystem> #include <filesystem>
#include <thread>
#include <mutex>
#include <condition_variable>
#include <chrono>
using namespace std; using namespace std;
using namespace pipedal; using namespace pipedal;
@@ -50,15 +54,122 @@ using ip::tcp;
const size_t MAX_LENGTH = 128; const size_t MAX_LENGTH = 128;
class GovernorMonitorThread
static bool startsWith(const std::string&s, const char*text)
{ {
if (s.length() < strlen(text)) return false; public:
const char*sp = s.c_str(); ~GovernorMonitorThread()
{
Stop();
}
void Start(std::string governor)
{
std::unique_lock<std::mutex> lock(mutex);
if (!pThread)
{
this->governor = governor;
cancelled = false;
wake = false;
pThread = std::make_unique<std::thread>([this]()
{ this->ServiceProc(); });
} else {
this->governor = governor;
wake = true;
lock.unlock();
cv.notify_one();
}
}
void Stop()
{
std::unique_lock<std::mutex> lock(mutex);
if (pThread)
{
wake = true;
cancelled = true;
lock.unlock();
cv.notify_one();
pThread->join();
pThread.reset();
}
}
void SetGovernor(const std::string &governor)
{
{
std::unique_lock<std::mutex> lock(mutex);
wake = true;
this->governor = governor;
}
cv.notify_one();
}
private:
bool wake = false;
bool cancelled = false;
std::string savedGovernor;
void ServiceProc()
{
savedGovernor = pipedal::GetCpuGovernor();
pipedal::SetCpuGovernor(this->governor);
while (true)
{
bool cancelled;
std::string governor;
{
std::unique_lock<std::mutex> lock(mutex);
auto timeToWaitFor = std::chrono::system_clock::now() + 5000ms;
cv.wait_until(
lock,
timeToWaitFor,
[this]()
{ return wake; });
wake = false;
governor = this->governor;
cancelled = this->cancelled;
}
if (cancelled) {
break;
}
std::string activeGovernor = pipedal::GetCpuGovernor();
if (activeGovernor != governor)
{
// somebody set it so they must have wanted it.
// save the value so that we can restore it when done.
savedGovernor = activeGovernor;
// but insist on using ours!!
pipedal::SetCpuGovernor(governor);
}
}
pipedal::SetCpuGovernor(savedGovernor);
}
std::unique_ptr<std::thread> pThread;
std::string governor;
std::mutex mutex;
bool canceled;
std::condition_variable cv;
};
GovernorMonitorThread governorMonitorThread;
void StopGovernorMonitorThread()
{
governorMonitorThread.Stop();
}
void StartGovernorMonitorThread(const std::string&governor)
{
governorMonitorThread.Start(governor);
}
static bool startsWith(const std::string &s, const char *text)
{
if (s.length() < strlen(text))
return false;
const char *sp = s.c_str();
while (*text) while (*text)
{ {
if (*text++ != *sp++) return false; if (*text++ != *sp++)
return false;
} }
return true; return true;
} }
@@ -67,7 +178,7 @@ static std::vector<std::string> tokenize(std::string value)
std::vector<std::string> result; std::vector<std::string> result;
std::stringstream s(value); std::stringstream s(value);
std::string item; std::string item;
while (std::getline(s,item,' ')) while (std::getline(s, item, ' '))
{ {
if (item.length() != 0) if (item.length() != 0)
result.push_back(item); result.push_back(item);
@@ -75,26 +186,17 @@ static std::vector<std::string> tokenize(std::string value)
return result; return result;
} }
static void CaptureAccessPoint(const std::string gatewayAddress) static void delayedRestartProc()
{
}
static void ReleaseAccessPoint(const std::string gatewayAddress)
{
}
void delayedRestartProc()
{ {
sleep(1); // give a chance for websocket messages to propagate. sleep(1); // give a chance for websocket messages to propagate.
Lv2Log::error("Delayed Restart"); Lv2Log::error("Delayed Restart");
std::string pipedalConfigPath = std::filesystem::path(getSelfExePath()).parent_path() / "pipedalconfig"; std::string pipedalConfigPath = std::filesystem::path(getSelfExePath()).parent_path() / "pipedalconfig";
std::stringstream s; std::stringstream s;
s << pipedalConfigPath.c_str() << " --restart --excludeShutdownService"; s << pipedalConfigPath.c_str() << " --restart --excludeShutdownService";
if (::system(s.str().c_str())!= EXIT_SUCCESS) if (::system(s.str().c_str()) != EXIT_SUCCESS)
{ {
Lv2Log::error("Delayed Restart failed."); Lv2Log::error("Delayed Restart failed.");
} }
} }
@@ -110,8 +212,6 @@ bool setJackConfiguration(JackServerSettings serverSettings)
std::thread delayedRestartThread(delayedRestartProc); std::thread delayedRestartThread(delayedRestartProc);
delayedRestartThread.detach(); delayedRestartThread.detach();
return true; return true;
} }
class Reader : public std::enable_shared_from_this<Reader> class Reader : public std::enable_shared_from_this<Reader>
@@ -162,7 +262,7 @@ private:
return; return;
} }
socket.async_write_some( socket.async_write_some(
boost::asio::buffer(response.c_str()+ writePosition, response.length()-writePosition), boost::asio::buffer(response.c_str() + writePosition, response.length() - writePosition),
boost::bind(&Reader::HandleWrite, boost::bind(&Reader::HandleWrite,
shared_from_this(), shared_from_this(),
boost::asio::placeholders::error, boost::asio::placeholders::error,
@@ -185,7 +285,7 @@ private:
if (c == '\n') if (c == '\n')
{ {
std::string command = message.str(); std::string command = message.str();
cout << "Received command: " << command << endl; cout << command << endl;
HandleCommand(command); HandleCommand(command);
socket.close(); socket.close();
return; return;
@@ -202,59 +302,88 @@ private:
socket.close(); socket.close();
} }
} }
void HandleCommand(const std::string &s) void HandleCommand(const std::string &text)
{ {
int result = -1; int result = -1;
try { std::string command;
if (startsWith(s,"WifiConfigSettings ")) std::string args;
auto pos = text.find_first_of(' ');
if (pos == std::string::npos)
{
command = text;
}
else
{
command = text.substr(0, pos);
args = text.substr(pos + 1);
}
try
{
if (command == "UnmonitorGovernor")
{ {
std::string json = s.substr(strlen("WifiConfigSettings ")); StopGovernorMonitorThread();
std::stringstream ss(json); result = 0;
}
else if (command == "MonitorGovernor")
{
std::stringstream ss(args);
std::string governor;
try
{
json_reader reader(ss);
reader.read(&governor);
}
catch (const std::exception &e)
{
throw PiPedalArgumentException("Invalid arguments.");
}
StartGovernorMonitorThread(governor);
result = 0;
}
else if (command == "GovernorSettings")
{
std::stringstream ss(args);
std::string governor;
try
{
json_reader reader(ss);
reader.read(&governor);
}
catch (const std::exception &e)
{
throw PiPedalArgumentException("Invalid arguments.");
}
governorMonitorThread.SetGovernor(governor);
result = 0;
}
else if (command == "WifiConfigSettings ")
{
std::stringstream ss(args);
WifiConfigSettings settings; WifiConfigSettings settings;
try { try
{
json_reader reader(ss); json_reader reader(ss);
reader.read(&settings); reader.read(&settings);
} catch (const std::exception &e) }
catch (const std::exception &e)
{ {
throw PiPedalArgumentException("Invalid arguments."); throw PiPedalArgumentException("Invalid arguments.");
} }
pipedal::SetWifiConfig(settings); pipedal::SetWifiConfig(settings);
result = 0; result = 0;
}
} else if (startsWith(s,"release_ap ")) else if (command == "shutdown")
{
std::vector<std::string> argv = tokenize(s);
if (argv.size() == 2) {
ReleaseAccessPoint(argv[1]);
result = 0;
} else {
throw PiPedalArgumentException("Invalid arguments.");
}
} else if (startsWith(s,"capture_ap "))
{
std::vector<std::string> argv = tokenize(s);
if (argv.size() == 2) {
CaptureAccessPoint(argv[1]);
result = 0;
} else {
throw PiPedalArgumentException("Invalid arguments.");
}
} else if (s == "shutdown")
{ {
result = sysExec("/usr/sbin/shutdown -P now"); result = sysExec("/usr/sbin/shutdown -P now");
} }
else if (s == "restart") else if (command == "restart")
{ {
result = sysExec("/usr/sbin/shutdown -r now"); result = sysExec("/usr/sbin/shutdown -r now");
} else if (startsWith(s,"setJackConfiguration ")) }
else if (command == "setJackConfiguration")
{ {
auto remainder = s.substr(strlen("setJackConfiguration "));
std::stringstream input(remainder); std::stringstream input(args);
JackServerSettings serverSettings; JackServerSettings serverSettings;
json_reader reader(input); json_reader reader(input);
@@ -263,12 +392,18 @@ private:
if (input.fail()) if (input.fail())
{ {
result = -1; result = -1;
} else {
result = setJackConfiguration(serverSettings) ? 0: -1;
} }
else
{
result = setJackConfiguration(serverSettings) ? 0 : -1;
}
} }
} catch (const std::exception &e) else
{
throw std::invalid_argument(SS("Unknown command:" << command));
}
}
catch (const std::exception &e)
{ {
std::stringstream t; std::stringstream t;
t << "-2 " << e.what() << "\n"; t << "-2 " << e.what() << "\n";
@@ -284,7 +419,6 @@ private:
this->writePosition = 0; this->writePosition = 0;
WriteSome(); WriteSome();
} }
}; };
class Server class Server
@@ -314,16 +448,16 @@ private:
} }
public: public:
//constructor for accepting connection from client // constructor for accepting connection from client
Server(boost::asio::io_service &io_service, const tcp::endpoint &endpoint) Server(boost::asio::io_service &io_service, const tcp::endpoint &endpoint)
: acceptor_(io_service, endpoint), : acceptor_(io_service, endpoint),
io_service(io_service) io_service(io_service)
{ {
StartAccept(); StartAccept();
} }
~Server() { ~Server()
{
} }
}; };
@@ -332,29 +466,26 @@ void runServer(uint16_t port)
asio::ip::tcp::endpoint endpoint(ip::make_address_v4("127.0.0.1"), port); asio::ip::tcp::endpoint endpoint(ip::make_address_v4("127.0.0.1"), port);
asio::io_service ios; asio::io_service ios;
try { try
Server server(ios,endpoint); {
Server server(ios, endpoint);
ios.run(); ios.run();
cout << "Processing terminated." << endl; cout << "Processing terminated." << endl;
} catch (const std::exception & e) }
catch (const std::exception &e)
{ {
cout << "Error: " << e.what() << endl; cout << "Error: " << e.what() << endl;
} }
} }
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
Lv2Log::set_logger(MakeLv2SystemdLogger()); Lv2Log::set_logger(MakeLv2SystemdLogger());
CommandLineParser parser; CommandLineParser parser;
uint16_t port = 0; uint16_t port = 0;
std::string captureAccessPoint;
std::string releaseAccessPoint;
parser.AddOption("-port", &port); parser.AddOption("-port", &port);
parser.AddOption("-captureAP",&captureAccessPoint);
parser.AddOption("-releaseAP",&releaseAccessPoint);
try try
{ {
@@ -369,34 +500,12 @@ int main(int argc, char **argv)
cout << "Error: " << e.what() << endl; cout << "Error: " << e.what() << endl;
return EXIT_FAILURE; return EXIT_FAILURE;
} }
if (captureAccessPoint.length() != 0) if (port == 0)
{ {
try{ std::cerr << "Error: port not specified." << std::endl;
CaptureAccessPoint(captureAccessPoint); return EXIT_FAILURE;
} catch (const std::exception &e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "Access point captured." << std::endl;
} else if (releaseAccessPoint.length() != 0)
{
try{
ReleaseAccessPoint(releaseAccessPoint);
} catch (const std::exception &e)
{
std::cerr << "Error: " << e.what() << std::endl;
return EXIT_FAILURE;
}
std::cout << "Access point released." << std::endl;
} else {
if (port == 0) {
std::cerr << "Error: port not specified." << std::endl;
return EXIT_FAILURE;
}
runServer(port);
} }
runServer(port);
governorMonitorThread.Stop();
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
+45
View File
@@ -32,6 +32,7 @@ const char *BANK_EXTENSION = ".bank";
const char *BANKS_FILENAME = "index.banks"; const char *BANKS_FILENAME = "index.banks";
#define WIFI_CONFIG_SETTINGS_FILENAME "wifiConfigSettings.json"; #define WIFI_CONFIG_SETTINGS_FILENAME "wifiConfigSettings.json";
#define GOVERNOR_SETTINGS_FILENAME "governorSettings.json";
Storage::Storage() Storage::Storage()
{ {
@@ -182,6 +183,7 @@ void Storage::Initialize()
{ {
} }
LoadWifiConfigSettings(); LoadWifiConfigSettings();
LoadGovernorSettings();
} }
@@ -802,6 +804,25 @@ int64_t Storage::UploadBank(BankFile&bankFile,int64_t uploadAfter)
return lastBank; return lastBank;
} }
void Storage::SetGovernorSettings(const std::string & governor)
{
std::filesystem::path path = this->dataRoot / GOVERNOR_SETTINGS_FILENAME;
{
std::ofstream f(path);
if (!f.is_open())
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_writer writer(f);
writer.write(governor);
this->governorSettings = governor;
}
}
std::string Storage::GetGovernorSettings() const
{
return this->governorSettings;
}
void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings) void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings)
{ {
@@ -839,7 +860,30 @@ void Storage::SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSetting
} }
this->wifiConfigSettings = copyToStore; this->wifiConfigSettings = copyToStore;
} }
void Storage::LoadGovernorSettings()
{
std::filesystem::path path = this->dataRoot / GOVERNOR_SETTINGS_FILENAME;
try {
if (std::filesystem::is_regular_file(path))
{
std::ifstream f(path);
if (!f.is_open())
{
throw PiPedalException("Unable to write to " + ((std::string)path));
}
json_reader reader(f);
std::string governorSettings;
reader.read(&governorSettings);
this->governorSettings = governorSettings;
}
} catch (const std::exception&)
{
this->governorSettings = "performance";
}
this->wifiConfigSettings.valid_ = true;
}
void Storage::LoadWifiConfigSettings() void Storage::LoadWifiConfigSettings()
{ {
std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME; std::filesystem::path path = this->dataRoot / WIFI_CONFIG_SETTINGS_FILENAME;
@@ -1137,6 +1181,7 @@ uint64_t Storage::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId
} }
JSON_MAP_BEGIN(CurrentPreset) JSON_MAP_BEGIN(CurrentPreset)
JSON_MAP_REFERENCE(CurrentPreset,modified) JSON_MAP_REFERENCE(CurrentPreset,modified)
JSON_MAP_REFERENCE(CurrentPreset,preset) JSON_MAP_REFERENCE(CurrentPreset,preset)
+6
View File
@@ -72,6 +72,7 @@ private:
bool isJackChannelSelectionValid = false; bool isJackChannelSelectionValid = false;
JackChannelSelection jackChannelSelection; JackChannelSelection jackChannelSelection;
WifiConfigSettings wifiConfigSettings; WifiConfigSettings wifiConfigSettings;
std::string governorSettings = "performance";
public: public:
Storage(); Storage();
void Initialize(); void Initialize();
@@ -85,6 +86,7 @@ public:
void LoadWifiConfigSettings(); void LoadWifiConfigSettings();
void LoadGovernorSettings();
void LoadBank(int64_t instanceId); void LoadBank(int64_t instanceId);
const PedalBoard& GetCurrentPreset(); const PedalBoard& GetCurrentPreset();
void SaveCurrentPreset(const PedalBoard&pedalBoard); void SaveCurrentPreset(const PedalBoard&pedalBoard);
@@ -116,6 +118,10 @@ public:
void SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings); void SetWifiConfigSettings(const WifiConfigSettings & wifiConfigSettings);
WifiConfigSettings GetWifiConfigSettings(); WifiConfigSettings GetWifiConfigSettings();
void SetGovernorSettings(const std::string & governor);
std::string GetGovernorSettings() const;
void SaveCurrentPreset(const CurrentPreset &currentPreset); void SaveCurrentPreset(const CurrentPreset &currentPreset);
bool RestoreCurrentPreset(CurrentPreset*pResult); bool RestoreCurrentPreset(CurrentPreset*pResult);
+35 -5
View File
@@ -27,12 +27,13 @@
#include <boost/system/error_code.hpp> #include <boost/system/error_code.hpp>
#include <filesystem> #include <filesystem>
#include "PiPedalConfiguration.hpp" #include "PiPedalConfiguration.hpp"
#include "Shutdown.hpp" #include "ShutdownClient.hpp"
#include "CommandLineParser.hpp" #include "CommandLineParser.hpp"
#include "Lv2SystemdLogger.hpp" #include "Lv2SystemdLogger.hpp"
#include <sys/stat.h> #include <sys/stat.h>
#include <boost/asio.hpp> #include <boost/asio.hpp>
#include "HtmlHelper.hpp" #include "HtmlHelper.hpp"
#include "Ipv6Helpers.hpp"
#include <signal.h> #include <signal.h>
#include <semaphore.h> #include <semaphore.h>
@@ -406,19 +407,26 @@ public:
class InterceptConfig : public RequestHandler class InterceptConfig : public RequestHandler
{ {
private: private:
std::string response;
uint64_t maxUploadSize; uint64_t maxUploadSize;
int portNumber;
public: public:
InterceptConfig(int portNumber, uint64_t maxUploadSize) InterceptConfig(int portNumber, uint64_t maxUploadSize)
: RequestHandler("/var/config.json"), : RequestHandler("/var/config.json"),
maxUploadSize(maxUploadSize) maxUploadSize(maxUploadSize),
portNumber(portNumber)
{ {
}
std::string GetConfig(const std::string&fromAddress)
{
std::string linkLocalAddress = GetLinkLocalAddress(fromAddress);
std::stringstream s; std::stringstream s;
s << "{ \"socket_server_port\": " << portNumber s << "{ \"socket_server_port\": " << portNumber
<< ", \"socket_server_address\": \"*\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }"; << ", \"socket_server_address\": \"" << linkLocalAddress << "\", \"ui_plugins\": [ ], \"max_upload_size\": " << maxUploadSize << " }";
response = s.str();
return s.str();
} }
virtual ~InterceptConfig() {} virtual ~InterceptConfig() {}
@@ -428,6 +436,17 @@ public:
HttpResponse &res, HttpResponse &res,
std::error_code &ec) std::error_code &ec)
{ {
// intercepted. See the other overload.
}
virtual void head_response(
const std::string &fromAddress,
const uri&request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{
std::string response = GetConfig(fromAddress);
res.set(HttpField::content_type, "application/json"); res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache"); res.set(HttpField::cache_control, "no-cache");
res.setContentLength(response.length()); res.setContentLength(response.length());
@@ -439,7 +458,18 @@ public:
const HttpRequest &req, const HttpRequest &req,
HttpResponse &res, HttpResponse &res,
std::error_code &ec) std::error_code &ec)
{
// intercepted. see the other overload.
}
virtual void get_response(
const std::string&fromAddress,
const uri &request_uri,
const HttpRequest &req,
HttpResponse &res,
std::error_code &ec)
{ {
std::string response = GetConfig(fromAddress);
res.set(HttpField::content_type, "application/json"); res.set(HttpField::content_type, "application/json");
res.set(HttpField::cache_control, "no-cache"); res.set(HttpField::cache_control, "no-cache");
res.setContentLength(response.length()); res.setContentLength(response.length());
+1 -2
View File
@@ -53,5 +53,4 @@ template <typename T> class TypeDisplay;
#include "lv2/parameters.lv2/parameters.h" #include "lv2/parameters.lv2/parameters.h"
#include "lv2/units.lv2/units.h" #include "lv2/units.lv2/units.h"
*/ */
#include "ss.hpp"
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())
+26
View File
@@ -0,0 +1,26 @@
// Copyright (c) 2022 Robin Davies
//
// Permission is hereby granted, free of charge, to any person obtaining a copy of
// this software and associated documentation files (the "Software"), to deal in
// the Software without restriction, including without limitation the rights to
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
// the Software, and to permit persons to whom the Software is furnished to do so,
// subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#pragma once
#include <sstream>
#include <iostream>
// usage: SS("xyz" << 123 << 45.6) returning a std::string rvalue.
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str())