refactor service.conf

This commit is contained in:
Robin Davies
2022-07-12 21:10:49 -04:00
parent 2133403f7e
commit fa9888914d
38 changed files with 1181 additions and 1733 deletions
+5 -5
View File
@@ -210,13 +210,13 @@ bool setJackConfiguration(JackServerSettings serverSettings)
silentSysExec("/usr/bin/systemctl unmask jack");
silentSysExec("/usr/bin/systemctl enable jack");
#else
// otherwise,
// the config was written before invoking admin main. but we still need to restart the service.
#endif
std::thread delayedRestartThread(delayedRestartProc);
delayedRestartThread.detach();
#else
// otherwise,
// the config was written before invoking admin main and we don't need to to restart the service.
#endif
return true;
}
+30 -7
View File
@@ -268,6 +268,15 @@ namespace pipedal
snd_pcm_sw_params_free(playbackSwParams);
playbackSwParams = nullptr;
}
for (auto *midiState: this->midiStates)
{
if (midiState != nullptr)
{
midiState->Close();
delete midiState;
}
}
midiStates.resize(0);
}
std::string discover_alsa_using_apps()
@@ -573,9 +582,14 @@ namespace pipedal
this->channelSelection = channelSelection;
open = true;
OpenMidi(jackServerSettings, channelSelection);
OpenAudio(jackServerSettings, channelSelection);
try {
OpenMidi(jackServerSettings, channelSelection);
OpenAudio(jackServerSettings, channelSelection);
} catch (const std::exception&e)
{
Close();
throw;
}
}
void OpenAudio(const JackServerSettings &jackServerSettings, const JackChannelSelection &channelSelection)
@@ -1512,7 +1526,7 @@ namespace pipedal
try
{
int err;
for (int retry = 0; retry < 5; ++retry)
for (int retry = 0; retry < 15; ++retry)
{
err = snd_pcm_open(&playbackHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_PLAYBACK, SND_PCM_NONBLOCK);
if (err == -EBUSY)
@@ -1524,13 +1538,22 @@ namespace pipedal
}
if (err < 0)
{
throw PiPedalStateException(SS(alsaDeviceName << " not found. "
throw PiPedalStateException(SS(alsaDeviceName << " playback device not found. "
<< "(" << snd_strerror(err) << ")"));
}
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
for (int retry = 0; retry < 15; ++retry)
{
err = snd_pcm_open(&captureHandle, alsaDeviceName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
if (err == -EBUSY)
{
sleep(1);
continue;
}
break;
}
if (err < 0)
throw PiPedalStateException(SS(alsaDeviceName << " not found."));
throw PiPedalStateException(SS(alsaDeviceName << " capture device not found."));
if (snd_pcm_hw_params_malloc(&playbackHwParams) < 0)
{
File diff suppressed because it is too large Load Diff
-217
View File
@@ -1,217 +0,0 @@
#pragma once
// avoid breaking changes from 1.64 to.71
// Note that raspbian only prvides 1.64; ubuntu studio only provides 1.71
#define BOOST_BEAST_ALLOW_DEPRECATED
#include <boost/asio/ip/tcp.hpp>
#include <boost/beast/http.hpp>
#include <boost/asio/ip/network_v4.hpp>
#include <mutex>
#include <thread>
#include "Uri.hpp"
#include <string_view>
#include <filesystem>
// forward declaration.
class websocket_session;
namespace pipedal {
using HttpRequest =boost::beast::http::request<boost::beast::http::string_body>;
std::string last_modified(const std::filesystem::path& path);
class SocketHandler {
public:
class IWriteCallback {
public:
virtual void close() = 0;
virtual void writeCallback(const std::string& text) = 0;
virtual std::string getFromAddress() const = 0;
};
private:
friend class ::websocket_session;
IWriteCallback *writeCallback_;
void setWriteCallback(IWriteCallback *writeCallback) {
writeCallback_ = writeCallback;
}
protected:
virtual void onReceive(const std::string_view&text) = 0;
public:
std::string getFromAddress() const { return writeCallback_->getFromAddress(); }
void receive(const std::string_view&text) {
onReceive(text);
}
void send(const std::string &text) {
if (writeCallback_ != nullptr)
{
writeCallback_->writeCallback(text);
}
}
virtual void Close()
{
writeCallback_->close();
}
virtual ~SocketHandler() = default;
virtual void onAttach() { }
};
class ISocketFactory {
public:
virtual bool wants(const uri &request) = 0;
virtual std::shared_ptr<SocketHandler> CreateHandler(const uri& request) = 0;
};
class RequestHandler {
private:
uri target_url_;
public:
RequestHandler(const char*target_url)
: target_url_(target_url)
{
}
~RequestHandler() = default;
virtual bool wantsRedirect(const uri& requestUri)
{
return false;
}
uri GetRedirect(const uri& requestUri)
{
return requestUri;
}
virtual bool wants(boost::beast::http::verb verb,const uri &request_uri) const {
if (request_uri.segment_count() < target_url_.segment_count())
{
return false;
}
for (int i = 0; i < target_url_.segment_count(); ++i)
{
if (request_uri.segment(i) != target_url_.segment(i))
{
return false;
}
}
return true;
}
virtual void head_response(
const uri&request_uri,
const boost::beast::http::request<boost::beast::http::string_body> &req,
boost::beast::http::response<boost::beast::http::empty_body> &res,
boost::beast::error_code &ec) = 0;
virtual void get_response(
const uri&request_uri,
const boost::beast::http::request<boost::beast::http::string_body> &req,
boost::beast::http::response<boost::beast::http::string_body> &res,
boost::beast::error_code &ec) = 0;
virtual void post_response(
const uri&request_uri,
const boost::beast::http::request<boost::beast::http::string_body> &req,
boost::beast::http::response<boost::beast::http::string_body> &res,
boost::beast::error_code &ec)
{
get_response(request_uri,req,res,ec);
}
};
class BeastServer {
private:
boost::asio::ip::address address;
unsigned short port;
std::string doc_root;
int threads;
std::mutex log_mutex;
std::vector<std::shared_ptr<RequestHandler> > request_handlers;
std::vector<std::shared_ptr<ISocketFactory> > socket_factories;
std::mutex io_mutex;
boost::asio::io_context *pIoContext = nullptr;
void*pListener = nullptr;
std::thread*pBgThread = nullptr;
bool logHttpRequests = false;
bool hasAccessPointGateway = false;
boost::asio::ip::network_v4 accessPointGateway;
std::string accessPointServerAddress;
bool stopping = false;
public:
BeastServer(
const boost::asio::ip::address& address,
int port,
const char*rootPath,
int threads = 10);
void Run();
void RunInBackground();
bool Stopping() const { return this->stopping; }
void Stop();
void Join();
void SetLogHttpRequests(bool logRequests) { this->logHttpRequests = logRequests; }
bool LogHttpRequests() const { return this->logHttpRequests; }
boost::asio::io_context& IoContext() {
return *pIoContext;
}
void AddRequestHandler(std::shared_ptr<RequestHandler> requestHandler)
{
request_handlers.push_back(requestHandler);
}
void AddSocketFactory(const std::shared_ptr<ISocketFactory> &socketHandler)
{
socket_factories.push_back(socketHandler);
}
std::vector<std::shared_ptr<RequestHandler> > &RequestHandlers() { return request_handlers; }
const std::string&GetDocRoot() { return doc_root; }
virtual ~BeastServer();
std::shared_ptr<ISocketFactory> GetSocketFactory(const uri &request_uri);
void SetAccessPointGateway(const boost::asio::ip::network_v4 &gateway, const std::string&accessPointServerAddress)
{
this->accessPointGateway = gateway;
this->hasAccessPointGateway = true;
this->accessPointServerAddress = accessPointServerAddress;
}
bool HasAccessPointGateway() const { return hasAccessPointGateway; }
boost::asio::ip::network_v4 &GetAccessPointGateway() { return this->accessPointGateway; }
const std::string & GetAccessPointServerAddress() const { return this->accessPointServerAddress; }
public:
virtual void onError(boost::system::error_code ec, char const* what);
virtual void onWarning(boost::system::error_code ec, char const* what);
virtual void onInfo(char const* what);
virtual void onListenerClosed();
};
} // namespace pipedal;
+8 -4
View File
@@ -89,9 +89,10 @@ set (PIPEDAL_SOURCES
CpuUse.hpp CpuUse.cpp
P2pConfigFiles.hpp
AvahiService.cpp AvahiService.hpp
DeviceIdFile.cpp DeviceIdFile.hpp
ServiceConfiguration.cpp ServiceConfiguration.hpp
WriteTemplateFile.cpp WriteTemplateFile.hpp
ConfigUtil.hpp ConfigUtil.cpp
ConfigSerializer.h ConfigSerializer.cpp
StdErrorCapture.hpp StdErrorCapture.cpp
Ipv6Helpers.cpp Ipv6Helpers.hpp
PluginPreset.cpp PluginPreset.hpp
@@ -236,12 +237,13 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
add_executable(pipedalconfig
PrettyPrinter.hpp
DeviceIdFile.cpp DeviceIdFile.hpp
ServiceConfiguration.cpp ServiceConfiguration.hpp
json.cpp json.hpp
HtmlHelper.cpp HtmlHelper.hpp
CommandLineParser.hpp
WriteTemplateFile.hpp WriteTemplateFile.cpp
PiPedalException.hpp
ConfigSerializer.h ConfigSerializer.cpp
ConfigMain.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp
@@ -283,7 +285,9 @@ target_link_libraries(pipedal_latency_test PRIVATE pthread asound
add_executable(pipedaladmind AdminMain.cpp CommandLineParser.hpp
UnixSocket.cpp UnixSocket.hpp
DeviceIdFile.cpp DeviceIdFile.hpp
ServiceConfiguration.cpp ServiceConfiguration.hpp
ConfigSerializer.h ConfigSerializer.cpp
autoptr_vector.h
JackServerSettings.hpp JackServerSettings.cpp
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp
+59 -26
View File
@@ -33,7 +33,7 @@
#include "JackServerSettings.hpp"
#include "P2pConfigFiles.hpp"
#include "PrettyPrinter.hpp"
#include "DeviceIdFile.hpp"
#include "ServiceConfiguration.hpp"
#include <uuid/uuid.h>
#include <random>
#include "AudioConfig.hpp"
@@ -366,6 +366,7 @@ void InstallLimits()
}
#if JACK_HOST
void MaybeStartJackService()
{
std::filesystem::path drcFile = "/etc/jackdrc";
@@ -379,7 +380,9 @@ void MaybeStartJackService()
silentSysExec(SYSTEMCTL_BIN " mask jack");
}
}
void InstallJackService()
#endif
void InstallAudioService()
{
InstallLimits();
InstallPamEnv();
@@ -435,6 +438,28 @@ int SudoExec(char **argv)
}
}
static bool IsP2pServiceEnabled()
{
WifiDirectConfigSettings settings;
settings.Load();
return settings.enable_;
}
static void RestartP2pdService()
{
if (IsP2pServiceEnabled())
{
silentSysExec(SYSTEMCTL_BIN " restart " PIPEDAL_P2PD_SERVICE);
}
}
static void UninstallP2pdService()
{
silentSysExec("systemctl stop " PIPEDAL_P2PD_SERVICE);
silentSysExec("systemctl disable " PIPEDAL_P2PD_SERVICE);
}
void Uninstall()
{
try
@@ -444,9 +469,13 @@ void Uninstall()
StopService();
DisableService();
UninstallP2pdService();
#if UNINSTALL_JACK_SERVICE
silentSysExec(SYSTEMCTL_BIN " stop jack");
silentSysExec(SYSTEMCTL_BIN " disable jack");
OnWifiUninstall();
#endif
try
{
@@ -545,33 +574,33 @@ static std::string RandomChars(int nchars)
return s.str();
}
static void PrepareDeviceidFile()
static void PrepareServiceConfigurationFile(uint16_t portNumber)
{
if (!std::filesystem::exists(DeviceIdFile::DEVICEID_FILE_NAME))
ServiceConfiguration serviceConfiguration;
try {
serviceConfiguration.Load();
} catch (const std::exception &)
{
DeviceIdFile deviceIdFile;
deviceIdFile.deviceName = "PiPedal";
deviceIdFile.uuid = MakeUuid();
deviceIdFile.Save();
} else {
DeviceIdFile deviceIdFile;
if (deviceIdFile.deviceName == "" || deviceIdFile.uuid == "")
{
if (deviceIdFile.deviceName == "")
{
deviceIdFile.deviceName = "PiPedal";
}
if (deviceIdFile.uuid == "")
{
deviceIdFile.uuid = MakeUuid();
}
deviceIdFile.Save();
}
}
if (serviceConfiguration.deviceName == "" || serviceConfiguration.uuid == ""
|| portNumber != serviceConfiguration.server_port
)
{
if (serviceConfiguration.deviceName == "")
{
serviceConfiguration.deviceName = "PiPedal";
}
if (serviceConfiguration.uuid == "")
{
serviceConfiguration.uuid = MakeUuid();
}
serviceConfiguration.server_port = portNumber;
serviceConfiguration.Save();
}
}
void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
{
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
@@ -583,8 +612,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
throw PiPedalException("Failed to create pipedald service group.");
}
PrepareDeviceidFile();
InstallJackService();
InstallAudioService();
auto endpos = endpointAddress.find_last_of(':');
if (endpos == string::npos)
{
@@ -610,6 +638,9 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
s << "Invalid port number: " << strPort;
throw PiPedalException(s.str());
}
PrepareServiceConfigurationFile(port);
bool authBindRequired = port < 512;
@@ -744,6 +775,8 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
RestartService(false);
EnableService();
RestartP2pdService();
cout << "Complete"
<< "\n";
}
+175
View File
@@ -0,0 +1,175 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. 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 "ConfigSerializer.h"
using namespace config_serializer;
using namespace config_serializer::detail;
using namespace std;
std::string config_serializer::detail::trim(const std::string &v)
{
size_t start = v.find_first_not_of(' ');
size_t end = v.find_last_not_of(' ');
if (start >= end+1)
return "";
return v.substr(start, end+1 - start);
}
std::vector<std::string> config_serializer::detail::split(const std::string &value, char delimiter)
{
size_t start = 0;
std::vector<std::string> result;
while (start < value.length())
{
size_t pos = value.find_first_of(delimiter, start);
if (pos == std::string::npos)
{
result.push_back(value.substr(start));
break;
}
result.push_back(value.substr(start, pos - start));
start = pos + 1;
if (start == value.length())
{
// ends with delimieter? Then there's an empty-length value at the end.
result.push_back("");
}
}
return result;
}
std::string config_serializer::detail::EncodeString(const std::string &s)
{
bool requiresEncoding = false;
for (char c : s)
{
switch (c)
{
case '\0':
break;
case '\r':
case '\t':
case '\\':
case ' ':
case '\"':
requiresEncoding = true;
break;
default:
break;
}
}
if (!requiresEncoding)
return s;
std::stringstream ss;
ss << '"';
for (char c : s)
{
switch (c)
{
case '\0':
// discard.
break;
case '\r':
ss << "\\r";
break;
case '\t':
ss << "\\t";
break;
case '\\':
ss << "\\\\";
break;
case '\"':
ss << "\\\"";
break;
default:
ss << c;
}
}
ss << '"';
return ss.str();
}
std::string config_serializer::detail::DecodeString(const std::string &value)
{
std::stringstream s;
const char *p = value.c_str();
char quoteChar;
if (*p == '\'' || *p == '\"')
{
quoteChar = *p;
}
else
{
return value;
}
if (*p != quoteChar)
return value;
if (value.at(value.length()-1) != quoteChar)
{
throw std::invalid_argument("Invalid quoted string.");
}
++p;
while (*p != 0 && *p != quoteChar)
{
char c = *p++;
if (c == '\\')
{
c = *p++;
switch (c)
{
case 'r':
s << '\r';
break;
case 't':
s << '\t';
break;
case 'n':
s << '\n';
break;
case '\0':
throw std::invalid_argument("Invalid quoted string.");
break;
default:
s << c;
}
if (c != 0)
{
s << c;
}
}
else
{
s << c;
}
}
return s.str();
}
+369
View File
@@ -0,0 +1,369 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. 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 <sstream>
#include <exception>
#include "autoptr_vector.h"
#include <unordered_map>
#include <fstream>
namespace config_serializer
{
namespace detail
{
std::string trim(const std::string &v);
std::vector<std::string> split(const std::string &v, char seperator);
/**
* @brief Convert string to config file format.
*
* Adds quotes and escapes, but only if neccessary.
*
* Source text is assumed to be UTF-8. Escapes for the
* following values only: \r \n \t \" \\.
*
*
* @param s
* @return std::string Encoded string.
*/
std::string EncodeString(const std::string &s);
/**
* @brief Config file format to string.
*
* Decodes quotes and escapes, but only if neccessary.
*
* Source text is assumed to be UTF-8. Escapes for the
* following values only: \r \n \t \" \\.
*
* @param s
* @return std::string
*/
std::string DecodeString(const std::string &s);
/**
* @brief Convert a string to an integer.
*
* @tparam T A signed or unsigned integral type.
* @param value The string to parse.
* @return T The parsed result.
* @throws std::invalid_argument if the supplied string is not a valid integer.
*/
template <class T>
T ToInt(const std::string &value)
{
std::stringstream s(value);
T result;
s >> result;
if (s.fail())
{
throw std::invalid_argument("Invalid integer value.");
}
return result;
}
}
using namespace detail;
template <class OBJ>
class ConfigSerializerBase
{
protected:
ConfigSerializerBase(const std::string &name, const std::string &comment)
: name(name),
comment(comment)
{
}
public:
using Obj = OBJ;
virtual ~ConfigSerializerBase() {}
const std::string &GetName() const { return name; }
const std::string &GetComment() const { return comment; }
virtual void SetValue(Obj &configurable, const std::string &value) const = 0;
virtual const std::string GetValue(Obj &configurable) const = 0;
private:
std::string name;
std::string comment;
};
template <class T>
class ConfigConverter
{
public:
static T FromString(const std::string &value)
{
T result;
std::stringstream s(value);
s >> result;
return result;
}
static std::string ToString(const T &value)
{
std::stringstream s;
s << value;
return s.str();
}
};
// string specializations.
template <>
inline std::string ConfigConverter<std::string>::FromString(const std::string &value)
{
return DecodeString(value);
}
template <>
inline std::string ConfigConverter<std::string>::ToString(const std::string &config)
{
return EncodeString(config);
}
// bool specializations.
template <>
inline bool ConfigConverter<bool>::FromString(const std::string &value)
{
bool result = false;
if (value == "")
{
result = false;
}
else if (value == "true")
{
result = true;
}
else if (value == "false")
{
result = false;
}
else
{
try
{
int t = ToInt<int>(value);
result = (t != 0);
}
catch (const std::exception &)
{
throw std::invalid_argument("Expecting true or false.");
}
}
return result;
}
template <>
inline std::string ConfigConverter<bool>::ToString(const bool &value)
{
return value ? "true" : "false";
}
template <class OBJ, class T>
class ConfigSerializer : public ConfigSerializerBase<OBJ>
{
public:
using Obj = OBJ;
using pointer_type = T Obj::*;
using base = ConfigSerializerBase<Obj>;
using converter = ConfigConverter<T>;
ConfigSerializer(const std::string &name,
pointer_type pMember,
const std::string &comment)
: base(name, comment),
pMember(pMember)
{
}
virtual void SetValue(Obj &configurable, const std::string &value) const
{
configurable.*pMember = ConfigConverter<T>::FromString(value);
}
virtual const std::string GetValue(Obj &configurable) const
{
return ConfigConverter<T>::ToString(configurable.*pMember);
}
private:
pointer_type pMember;
};
template <class OBJ>
class ConfigSerializable
{
private:
using Obj = OBJ;
using serializer_t = ConfigSerializerBase<OBJ>;
using serializers_t = p2p::autoptr_vector<serializer_t>;
const serializers_t &serializers;
protected:
ConfigSerializable(const serializers_t &serializers)
: serializers(serializers)
{
}
public:
virtual void Save(std::ostream &f)
{
bool firstLine = true;
for (const serializer_t *serializer : serializers)
{
if (serializer->GetComment().length() != 0)
{
if (!firstLine)
{
f << std::endl;
}
firstLine = false;
std::vector<std::string> comments = split(serializer->GetComment(), '\n');
if (comments.size() != 0)
for (const std::string &comment : comments)
{
f << "# " << comment << std::endl;
}
}
f << serializer->GetName() << '=' << serializer->GetValue((Obj &)*this) << std::endl;
}
}
void Save(const std::string &path)
{
std::ofstream f;
f.open(path);
if (!f.is_open())
{
throw std::invalid_argument("Can't write to file " + path);
}
try
{
Save(f);
}
catch (const std::exception &e)
{
throw std::invalid_argument("Error writing to '" + path + "'. " + e.what());
}
}
virtual void Load(std::istream &f)
{
std::unordered_map<std::string, serializer_t *> index;
for (serializer_t *serializer : serializers)
{
index[serializer->GetName()] = serializer;
}
std::string line;
int nLine = 0;
while (true)
{
if (f.eof())
break;
getline(f, line);
++nLine;
// remove comments.
auto npos = line.find_first_of('#');
if (npos != std::string::npos)
{
line = line.substr(0, npos);
}
size_t start = 0;
while (start < line.size() && line[start] == ' ')
{
++start;
}
if (start == line.size())
{
continue;
}
auto pos = line.find_first_of('=', start);
if (pos == std::string::npos)
{
std::stringstream msg;
msg << "(" << nLine << "," << (start + 1) << "): Syntax error. Expecting '='.";
throw std::logic_error(msg.str());
}
std::string label = trim(line.substr(start, pos - start));
std::string value = trim(line.substr(pos + 1));
if (index.contains(label))
{
serializer_t *serializer = index[label];
try
{
serializer->SetValue((Obj &)*this, value);
}
catch (const std::exception &e)
{
std::stringstream msg;
msg << "(" << nLine << ',' << (pos + 1) << "): " << e.what();
throw std::logic_error(msg.str());
}
}
else
{
std::stringstream msg;
msg << "(" << nLine << ',' << (start) << "): "
<< "Invalid property: " + label;
throw std::logic_error(
msg.str());
}
}
}
void Load(const std::string &path, bool throwIfNotFound = false)
{
try
{
std::ifstream f;
f.open(path);
if (!f.is_open())
{
if (throwIfNotFound)
{
throw std::logic_error("Can't open file.");
}
return;
}
Load(f);
}
catch (const std::exception &e)
{
std::stringstream msg;
msg << "Error: " << path << " " << e.what();
throw std::logic_error(msg.str());
}
}
};
}
+8 -1
View File
@@ -161,6 +161,12 @@ private:
isOpen = false;
StopReaderThread();
if (realtimeMonitorPortSubscriptions != nullptr)
{
delete realtimeMonitorPortSubscriptions;
realtimeMonitorPortSubscriptions = nullptr;
}
if (active)
{
audioDriver->Deactivate();
@@ -933,8 +939,9 @@ public:
Lv2Log::info("Audio started.");
}
catch (PiPedalException &e)
catch (const std::exception &e)
{
Lv2Log::error(SS("Failed to start audio. " << e.what()));
Close();
active = false;
throw;
+5
View File
@@ -785,6 +785,9 @@ Lv2PortInfo::Lv2PortInfo(Lv2Host *host, const LilvPlugin *plugin, const LilvPort
NodeAutoFree unitsValueUri = lilv_port_get(plugin, pPort, host->lilvUris.unitsUri);
this->units_ = UriToUnits(nodeAsString(unitsValueUri));
NodeAutoFree commentNode = lilv_port_get(plugin,pPort,host->lilvUris.rdfsComment);
this->comment_ = nodeAsString(commentNode);
NodeAutoFree bufferType = lilv_port_get(plugin, pPort, host->lilvUris.bufferType_uri);
this->buffer_type_ = "";
@@ -1167,6 +1170,7 @@ json_map::storage_type<Lv2PortInfo> Lv2PortInfo::jmap{{
MAP_REF(Lv2PortInfo, buffer_type),
MAP_REF(Lv2PortInfo, port_group),
json_map::enum_reference("units", &Lv2PortInfo::units_, get_units_enum_converter()),
MAP_REF(Lv2PortInfo, comment)
}};
json_map::storage_type<Lv2PortGroup> Lv2PortGroup::jmap{{
@@ -1229,6 +1233,7 @@ json_map::storage_type<Lv2PluginUiControlPort> Lv2PluginUiControlPort::jmap{{
MAP_REF(Lv2PluginUiControlPort, port_group),
json_map::enum_reference("units", &Lv2PluginUiControlPort::units_, get_units_enum_converter()),
MAP_REF(Lv2PluginUiControlPort, comment),
}};
+5
View File
@@ -205,6 +205,7 @@ private:
std::string designation_;
Units units_ = Units::none;
std::string comment_;
public:
bool IsSwitch() const {
return min_value_ == 0 && max_value_ == 1
@@ -261,6 +262,7 @@ public:
LV2_PROPERTY_GETSET_SCALAR(toggled_property);
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(port_group);
LV2_PROPERTY_GETSET(comment);
LV2_PROPERTY_GETSET_SCALAR(units);
LV2_PROPERTY_GETSET(buffer_type);
@@ -417,6 +419,7 @@ public:
, toggled_property_(pPort->toggled_property())
, not_on_gui_(pPort->not_on_gui())
, scale_points_(pPort->scale_points())
, comment_(pPort->comment())
, units_(pPort->units())
{
// Use symbols to index port groups, instead of uris.
@@ -450,6 +453,7 @@ private:
std::vector<Lv2ScalePoint> scale_points_;
std::string port_group_;
Units units_ = Units::none;
std::string comment_;
public:
LV2_PROPERTY_GETSET(symbol);
LV2_PROPERTY_GETSET_SCALAR(index);
@@ -466,6 +470,7 @@ public:
LV2_PROPERTY_GETSET_SCALAR(not_on_gui);
LV2_PROPERTY_GETSET(scale_points);
LV2_PROPERTY_GETSET(units);
LV2_PROPERTY_GETSET(comment);
public:
static json_map::storage_type<Lv2PluginUiControlPort> jmap;
+168 -97
View File
@@ -18,8 +18,9 @@
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include "pch.h"
#include "DeviceIdFile.hpp"
#include "ServiceConfiguration.hpp"
#include "AudioConfig.hpp"
#include "ConfigUtil.hpp"
#include <sched.h>
#include "PiPedalModel.hpp"
#include "JackHost.hpp"
@@ -55,8 +56,6 @@ PiPedalModel::PiPedalModel()
#else
this->jackServerSettings = this->storage.GetJackServerSettings();
#endif
}
void PiPedalModel::Close()
@@ -84,37 +83,40 @@ void PiPedalModel::Close()
PiPedalModel::~PiPedalModel()
{
try {
adminClient.UnmonitorGovernor();
} catch (...) // noexcept here!
try
{
adminClient.UnmonitorGovernor();
}
catch (...) // noexcept here!
{
}
try {
try
{
CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset);
} catch (...)
{
}
try {
catch (...)
{
}
try
{
if (jackHost)
{
jackHost->Close();
}
} catch (...)
}
catch (...)
{
}
}
#include <fstream>
void PiPedalModel::Init(const PiPedalConfiguration&configuration)
void PiPedalModel::Init(const PiPedalConfiguration &configuration)
{
this->configuration = configuration;
storage.SetConfigRoot(configuration.GetDocRoot());
@@ -122,7 +124,6 @@ void PiPedalModel::Init(const PiPedalConfiguration&configuration)
storage.Initialize();
this->jackServerSettings.ReadJackConfiguration();
}
void PiPedalModel::LoadLv2PluginInfo()
@@ -147,14 +148,14 @@ void PiPedalModel::LoadLv2PluginInfo()
// Copy all presets out of Lilv data to json files
// so that we can close lilv while we're actually
// running.
for (const auto&plugin: lv2Host.GetPlugins())
for (const auto &plugin : lv2Host.GetPlugins())
{
if (plugin->has_factory_presets())
{
if (!storage.HasPluginPresets(plugin->uri()))
{
PluginPresets pluginPresets = lv2Host.GetFactoryPluginPresets(plugin->uri());
storage.SavePluginPresets(plugin->uri(),pluginPresets);
storage.SavePluginPresets(plugin->uri(), pluginPresets);
}
}
}
@@ -165,7 +166,6 @@ void PiPedalModel::Load()
this->webRoot = configuration.GetWebRoot();
this->webPort = (uint16_t)configuration.GetSocketServerPort();
adminClient.MonitorGovernor(storage.GetGovernorSettings());
// lv2Host.Load(configuration.GetLv2Path().c_str());
@@ -200,8 +200,8 @@ void PiPedalModel::Load()
struct sched_param scheduler_params;
scheduler_params.sched_priority = 10;
memset(&scheduler_params,0,sizeof(sched_param));
sched_setscheduler(0,SCHED_RR,&scheduler_params);
memset(&scheduler_params, 0, sizeof(sched_param));
sched_setscheduler(0, SCHED_RR, &scheduler_params);
#if JACK_HOST
this->jackConfiguration = this->jackConfiguration.JackInitialize();
#else
@@ -216,7 +216,7 @@ void PiPedalModel::Load()
this->lv2Host.OnConfigurationChanged(jackConfiguration, selection);
try
{
jackHost->Open(this->jackServerSettings,selection);
jackHost->Open(this->jackServerSettings, selection);
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard;
@@ -226,7 +226,9 @@ void PiPedalModel::Load()
{
Lv2Log::error("Failed to load initial plugin. (%s)", e.what());
}
} else {
}
else
{
Lv2Log::error("Audio device not configured.");
}
}
@@ -453,7 +455,7 @@ void PiPedalModel::FirePresetsChanged(int64_t clientId)
delete[] t;
}
}
void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
void PiPedalModel::FirePluginPresetsChanged(const std::string &pluginUri)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
{
@@ -473,7 +475,6 @@ void PiPedalModel::FirePluginPresetsChanged(const std::string & pluginUri)
}
}
void PiPedalModel::SaveCurrentPreset(int64_t clientId)
{
std::lock_guard<std::recursive_mutex> guard{mutex};
@@ -482,31 +483,31 @@ void PiPedalModel::SaveCurrentPreset(int64_t clientId)
this->SetPresetChanged(clientId, false);
}
uint64_t PiPedalModel::CopyPluginPreset(const std::string&pluginUri,uint64_t presetId)
uint64_t PiPedalModel::CopyPluginPreset(const std::string &pluginUri, uint64_t presetId)
{
uint64_t result = storage.CopyPluginPreset(pluginUri,presetId);
uint64_t result = storage.CopyPluginPreset(pluginUri, presetId);
FirePluginPresetsChanged(pluginUri);
return result;
return result;
}
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets&pluginPresets)
void PiPedalModel::UpdatePluginPresets(const PluginUiPresets &pluginPresets)
{
storage.UpdatePluginPresets(pluginPresets);
FirePluginPresetsChanged(pluginPresets.pluginUri_);
}
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string&name)
int64_t PiPedalModel::SavePluginPresetAs(int64_t instanceId, const std::string &name)
{
auto *item = this->pedalBoard.GetItem(instanceId);
if (!item)
{
throw PiPedalException("Plugin not found.");
}
std::map<std::string,float> values;
for (const auto & controlValue: item->controlValues())
std::map<std::string, float> values;
for (const auto &controlValue : item->controlValues())
{
values[controlValue.key()] = controlValue.value();
}
uint64_t presetId = storage.SavePluginPreset(item->uri(),name,values);
uint64_t presetId = storage.SavePluginPreset(item->uri(), name, values);
FirePluginPresetsChanged(item->uri());
return presetId;
}
@@ -522,16 +523,15 @@ int64_t PiPedalModel::SaveCurrentPresetAs(int64_t clientId, const std::string &n
return result;
}
void PiPedalModel::UploadPluginPresets(const PluginPresets&pluginPresets)
void PiPedalModel::UploadPluginPresets(const PluginPresets &pluginPresets)
{
if (pluginPresets.pluginUri_.length() == 0)
{
throw PiPedalException("Invalid plugin presets.");
}
std::lock_guard<std::recursive_mutex> guard{mutex};
storage.SavePluginPresets(pluginPresets.pluginUri_,pluginPresets);
storage.SavePluginPresets(pluginPresets.pluginUri_, pluginPresets);
FirePluginPresetsChanged(pluginPresets.pluginUri_);
}
int64_t PiPedalModel::UploadPreset(const BankFile &bankFile, int64_t uploadAfter)
{
@@ -623,7 +623,7 @@ int64_t PiPedalModel::DeletePreset(int64_t clientId, int64_t instanceId)
-1, // can't use cached version.
newSelection);
}
return newSelection;
return newSelection;
}
bool PiPedalModel::RenamePreset(int64_t clientId, int64_t instanceId, const std::string &name)
{
@@ -649,13 +649,13 @@ GovernorSettings PiPedalModel::GetGovernorSettings()
return result;
}
}
void PiPedalModel::SetGovernorSettings(const std::string& governor)
void PiPedalModel::SetGovernorSettings(const std::string &governor)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
adminClient.SetGovernorSettings(governor);
this->storage.SetGovernorSettings(governor);
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
{
for (size_t i = 0; i < subscribers.size(); ++i)
@@ -669,9 +669,8 @@ void PiPedalModel::SetGovernorSettings(const std::string& governor)
}
delete[] t;
}
}
void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSettings)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -698,22 +697,46 @@ void PiPedalModel::SetWifiConfigSettings(const WifiConfigSettings &wifiConfigSet
}
}
static std::string GetP2pdName()
{
std::string name = "/etc/pipedal/config/pipedal_p2pd.conf";
std::string result;
if (ConfigUtil::GetConfigLine(name,"p2p_device_name",&result))
{
return result;
}
return "";
}
void PiPedalModel::UpdateDnsSd()
{
avahiService.Unannounce();
DeviceIdFile deviceIdFile;
ServiceConfiguration deviceIdFile;
deviceIdFile.Load();
std::string p2pdName = GetP2pdName();
if (p2pdName != "") {
deviceIdFile.deviceName = p2pdName;
}
if (deviceIdFile.deviceName != "" && deviceIdFile.uuid != "")
{
avahiService.Announce(webPort,deviceIdFile.deviceName,deviceIdFile.uuid,"pipedal");
} else {
avahiService.Announce(webPort, deviceIdFile.deviceName, deviceIdFile.uuid, "pipedal");
}
else
{
// device_uuid file is written at install time. This warning is harmless if you're debugging.
// Without it, we can't pulblish the website via dnsDS.
// Run "pipedalconfig --install" to create the file.
Lv2Log::warning("Cant read device_uuid file. dnsSD announcement skipped.");
Lv2Log::warning("Cant read device_uuid file from service.conf file. dnsSD announcement skipped.");
}
}
void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &wifiDirectConfigSettings)
{
@@ -741,11 +764,7 @@ void PiPedalModel::SetWifiDirectConfigSettings(const WifiDirectConfigSettings &w
// update NSD-SD announement.
UpdateDnsSd();
}
}
WifiConfigSettings PiPedalModel::GetWifiConfigSettings()
@@ -786,7 +805,6 @@ bool PiPedalModel::GetShowStatusMonitor()
{
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
return storage.GetShowStatusMonitor();
}
JackConfiguration PiPedalModel::GetJackConfiguration()
@@ -795,37 +813,82 @@ JackConfiguration PiPedalModel::GetJackConfiguration()
return this->jackConfiguration;
}
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
void PiPedalModel::RestartAudio()
{
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
this->storage.SetJackChannelSelection(channelSelection);
this->FireChannelSelectionChanged(clientId);
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
if (this->jackHost->IsOpen())
{
this->jackHost->Close();
#ifdef JUNK
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
}
// restarting is a bit dodgy. It was impossible with Jack, but
// now very plausible with the ALSA audio stack.
// Still bugs wrt/ restarting the circular buffers for the audio thread.
// for the meantime, just rely on the fact that the admin service will restart
// for the meantime, just rely on the fact that the admin service will restart
// the process.
//...
// do a complete reload.
this->jackHost->SetPedalBoard(nullptr);
this->jackHost->Open(this->jackServerSettings,channelSelection);
this->jackHost->SetPedalBoard(nullptr);
this->jackConfiguration.AlsaInitialize(this->jackServerSettings);
if (this->jackConfiguration.isValid())
{
JackChannelSelection selection = storage.GetJackChannelSelection(jackConfiguration);
selection = selection.RemoveInvalidChannels(jackConfiguration);
} else {
jackConfiguration.SetErrorStatus("Error");
}
FireJackConfigurationChanged(jackConfiguration);
if (!jackServerSettings.IsValid() || !jackConfiguration.isValid())
{
Lv2Log::error("Audio configuration not valid.");
return;
}
try
{
JackChannelSelection channelSelection = GetJackChannelSelection();
if (!channelSelection.isValid())
{
Lv2Log::error("Audio configuration not valid.");
return;
}
this->jackHost->Open(this->jackServerSettings, channelSelection);
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
std::shared_ptr<Lv2PedalBoard> lv2PedalBoard{this->lv2Host.CreateLv2PedalBoard(this->pedalBoard)};
this->lv2PedalBoard = lv2PedalBoard;
jackHost->SetPedalBoard(lv2PedalBoard);
this->UpdateRealtimeVuSubscriptions();
#endif
UpdateRealtimeMonitorPortSubscriptions();
}
catch (const std::exception &e)
{
Lv2Log::error(SS("Failed to start audio. " << e.what()));
throw;
}
}
void PiPedalModel::SetJackChannelSelection(int64_t clientId, const JackChannelSelection &channelSelection)
{
{
std::lock_guard<std::recursive_mutex> guard(mutex); // copy atomically.
this->storage.SetJackChannelSelection(channelSelection);
this->lv2Host.OnConfigurationChanged(jackConfiguration, channelSelection);
}
RestartAudio(); // no lock to avoid mutex deadlock when reader thread is sending notifications..
this->FireChannelSelectionChanged(clientId);
}
void PiPedalModel::FireChannelSelectionChanged(int64_t clientId)
@@ -1024,7 +1087,6 @@ void PiPedalModel::UnmonitorPort(int64_t subscriptionHandle)
}
}
void PiPedalModel::OnNotifyMonitorPort(const MonitorPortUpdate &update)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1095,7 +1157,7 @@ void PiPedalModel::GetLv2Parameter(
BankIndex PiPedalModel::GetBankIndex() const
{
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex &>(mutex));
return storage.GetBanks();
}
@@ -1137,16 +1199,17 @@ JackServerSettings PiPedalModel::GetJackServerSettings()
void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSettings)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
std::unique_lock<std::recursive_mutex> guard(mutex);
#if JACK_HOST
if (!adminClient.CanUseShutdownClient())
{
throw PiPedalException("Can't change server settings when running a debug server.");
}
#endif
this->jackServerSettings = jackServerSettings;
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
IPiPedalModelSubscriber **t = new IPiPedalModelSubscriber *[this->subscribers.size()];
for (size_t i = 0; i < subscribers.size(); ++i)
@@ -1160,19 +1223,25 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
}
delete[] t;
#if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings);
FireJackConfigurationChanged(this->jackConfiguration);
guard.unlock();
RestartAudio();
#endif
#if JACK_HOST
if (adminClient.CanUseShutdownClient())
{
#if ALSA_HOST
storage.SetJackServerSettings(jackServerSettings);
#endif
// save the current (edited) preset now in case the service shutdown isn't clean.
CurrentPreset currentPreset;
currentPreset.modified_ = this->hasPresetChanged;
currentPreset.preset_ = this->pedalBoard;
storage.SaveCurrentPreset(currentPreset);
this->jackConfiguration.SetIsRestarting(true);
FireJackConfigurationChanged(this->jackConfiguration);
this->jackHost->UpdateServerConfiguration(
@@ -1195,8 +1264,8 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
}
else
{
// we now do a complete restart of the services,
// so just sit tight and wait for the restart.
// we now do a complete restart of the services,
// so just sit tight and wait for the restart.
#ifdef JUNK
this->jackConfiguration.SetErrorStatus("");
FireJackConfigurationChanged(this->jackConfiguration);
@@ -1211,7 +1280,9 @@ void PiPedalModel::SetJackServerSettings(const JackServerSettings &jackServerSet
#endif
}
});
}
}
#endif
}
void PiPedalModel::UpdateDefaults(PedalBoardItem *pedalBoardItem)
@@ -1259,15 +1330,14 @@ void PiPedalModel::UpdateDefaults(PedalBoard *pedalBoard)
}
}
PluginPresets PiPedalModel::GetPluginPresets(const std::string& pluginUri)
PluginPresets PiPedalModel::GetPluginPresets(const std::string &pluginUri)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
return storage.GetPluginPresets(pluginUri);
}
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string& pluginUri)
PluginUiPresets PiPedalModel::GetPluginUiPresets(const std::string &pluginUri)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1332,7 +1402,7 @@ void PiPedalModel::DeleteMidiListeners(int64_t clientId)
jackHost->SetListenForMidiEvent(midiEventListeners.size() != 0);
}
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&atomType,const std::string&atomJson)
void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string &atomType, const std::string &atomJson)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1344,19 +1414,18 @@ void PiPedalModel::OnNotifyAtomOutput(uint64_t instanceId, const std::string&ato
auto subscriber = this->GetNotificationSubscriber(listener.clientId);
if (subscriber)
{
subscriber->OnNotifyAtomOutput(listener.clientHandle,instanceId,atomType,atomJson);
} else {
subscriber->OnNotifyAtomOutput(listener.clientHandle, instanceId, atomType, atomJson);
}
else
{
atomOutputListeners.erase(atomOutputListeners.begin() + i);
--i;
}
}
}
jackHost->SetListenForAtomOutput(atomOutputListeners.size() != 0);
}
void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
@@ -1370,7 +1439,9 @@ void PiPedalModel::OnNotifyMidiListen(bool isNote, uint8_t noteOrControl)
if (subscriber)
{
subscriber->OnNotifyMidiListener(listener.clientHandle, isNote, noteOrControl);
} else {
}
else
{
midiEventListeners.erase(midiEventListeners.begin() + i);
--i;
}
@@ -1400,7 +1471,7 @@ void PiPedalModel::CancelListenForMidiEvent(int64_t clientId, int64_t clientHand
std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < midiEventListeners.size(); ++i)
{
const auto& listener = midiEventListeners[i];
const auto &listener = midiEventListeners[i];
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
{
midiEventListeners.erase(midiEventListeners.begin() + i);
@@ -1417,7 +1488,7 @@ void PiPedalModel::CancelListenForAtomOutputs(int64_t clientId, int64_t clientHa
std::lock_guard<std::recursive_mutex> guard(mutex);
for (size_t i = 0; i < atomOutputListeners.size(); ++i)
{
const auto&listener = atomOutputListeners[i];
const auto &listener = atomOutputListeners[i];
if (listener.clientId == clientId && listener.clientHandle == clientHandle)
{
atomOutputListeners.erase(atomOutputListeners.begin() + i);
@@ -1434,24 +1505,24 @@ std::vector<AlsaDeviceInfo> PiPedalModel::GetAlsaDevices()
return this->alsaDevices.GetAlsaDevices();
}
const std::filesystem::path& PiPedalModel::GetWebRoot() const
const std::filesystem::path &PiPedalModel::GetWebRoot() const
{
return webRoot;
}
std::map<std::string,bool> PiPedalModel::GetFavorites() const
std::map<std::string, bool> PiPedalModel::GetFavorites() const
{
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex&>(mutex));
std::lock_guard<std::recursive_mutex> guard(const_cast<std::recursive_mutex &>(mutex));
return storage.GetFavorites();
}
void PiPedalModel::SetFavorites(const std::map<std::string,bool> &favorites)
void PiPedalModel::SetFavorites(const std::map<std::string, bool> &favorites)
{
std::lock_guard<std::recursive_mutex> guard(mutex);
storage.SetFavorites(favorites);
// take a snapshot incase a client unsusbscribes in the notification handler (in which case the mutex won't protect us)
std::vector<IPiPedalModelSubscriber*> t;
std::vector<IPiPedalModelSubscriber *> t;
t.reserve(this->subscribers.size());
for (size_t i = 0; i < subscribers.size(); ++i)
+2
View File
@@ -142,6 +142,8 @@ private:
void UpdateRealtimeMonitorPortSubscriptions();
void OnVuUpdate(const std::vector<VuUpdate>& updates);
void RestartAudio();
std::vector<RealtimeParameterRequest*> outstandingParameterRequests;
IPiPedalModelSubscriber*GetNotificationSubscriber(int64_t clientId);
+12 -12
View File
@@ -836,10 +836,10 @@ public:
std::string governor;
pReader->read(&governor);
std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
// if (!IsOnLocalSubnet(fromAddress))
// {
// throw PiPedalException("Permission denied. Not on local subnet.");
// }
this->model.SetGovernorSettings(governor);
this->Reply(replyTo, "setGovernorSettings");
}
@@ -852,10 +852,10 @@ public:
throw PiPedalException("Can't change server settings when running interactively.");
}
std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
// if (!IsOnLocalSubnet(fromAddress))
// {
// throw PiPedalException("Permission denied. Not on local subnet.");
// }
this->model.SetWifiConfigSettings(wifiConfigSettings);
this->Reply(replyTo, "setWifiConfigSettings");
@@ -873,10 +873,10 @@ public:
throw PiPedalException("Can't change server settings when running interactively.");
}
std::string fromAddress = this->getFromAddress();
if (!IsOnLocalSubnet(fromAddress))
{
throw PiPedalException("Permission denied. Not on local subnet.");
}
// if (!IsOnLocalSubnet(fromAddress))
// {
// throw PiPedalException("Permission denied. Not on local subnet.");
// }
this->model.SetWifiDirectConfigSettings(wifiDirectConfigSettings);
this->Reply(replyTo, "setWifiDirectConfigSettings");
@@ -22,7 +22,7 @@
* SOFTWARE.
*/
#include "DeviceIdFile.hpp"
#include "ServiceConfiguration.hpp"
#include <fstream>
#include <stdexcept>
#include <grp.h>
@@ -33,44 +33,45 @@
using namespace pipedal;
using namespace std;
using namespace config_serializer;
const char DeviceIdFile::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/device_uuid";
const char ServiceConfiguration::DEVICEID_FILE_NAME[] = "/etc/pipedal/config/service.conf";
void DeviceIdFile::Load()
#define SERIALIZER_ENTRY(MEMBER_NAME) \
new ConfigSerializer<ServiceConfiguration,decltype(ServiceConfiguration::MEMBER_NAME)>(#MEMBER_NAME, &ServiceConfiguration::MEMBER_NAME, "")
static p2p::autoptr_vector<ConfigSerializerBase<ServiceConfiguration>>
deviceIdSerializers
{
SERIALIZER_ENTRY(uuid),
SERIALIZER_ENTRY(deviceName),
SERIALIZER_ENTRY(server_port)
};
ServiceConfiguration::ServiceConfiguration()
: base(deviceIdSerializers)
{
{
ifstream f;
f.open(DEVICEID_FILE_NAME);
if (f.is_open())
{
std::getline(f, uuid);
std::getline(f, deviceName);
}
}
}
void DeviceIdFile::Save()
{
{
std::string path = DEVICEID_FILE_NAME;
ofstream f;
f.open(path, ios_base::trunc);
if (!f.is_open())
{
throw invalid_argument("Can't write to file " + path);
}
void ServiceConfiguration::Load()
{
base::Load(DEVICEID_FILE_NAME,false);
}
void ServiceConfiguration::Save()
{
base::Save(DEVICEID_FILE_NAME);
{
struct group *group_;
group_ = getgrnam("pipedal_d");
if (group_ == nullptr)
{
throw logic_error("Group not found: pipedal_d");
}
std::ignore = chown(path.c_str(), -1, group_->gr_gid);
std::ignore = chmod(path.c_str(), S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
f << uuid << endl;
f << deviceName << endl;
std::ignore = chown(DEVICEID_FILE_NAME,-1,group_->gr_gid);
std::ignore = chmod(DEVICEID_FILE_NAME, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
}
}
@@ -25,10 +25,17 @@
#pragma once
#include <string>
#include "ConfigSerializer.h"
namespace pipedal {
class DeviceIdFile {
using namespace config_serializer;
class ServiceConfiguration: protected ConfigSerializable<ServiceConfiguration> {
public:
using base = ConfigSerializable<ServiceConfiguration>;
ServiceConfiguration();
static const char DEVICEID_FILE_NAME[];
void Load();
@@ -36,6 +43,7 @@ namespace pipedal {
std::string uuid;
std::string deviceName;
std::string deviceName = "PiPedal";
uint32_t server_port = 80;
};
}
+13 -6
View File
@@ -21,7 +21,7 @@
#include "ss.hpp"
#include "P2pConfigFiles.hpp"
#include "DeviceIdFile.hpp"
#include "ServiceConfiguration.hpp"
#include "SetWifiConfig.hpp"
#include "PiPedalException.hpp"
#include "SystemConfigFile.hpp"
@@ -410,6 +410,16 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
{
cout << "Disabling P2P" << endl;
UninstallP2p();
ServiceConfiguration deviceIdFile;
deviceIdFile.Load();
deviceIdFile.deviceName = settings.hotspotName_;
deviceIdFile.Save();
// Announce new mDNS service with potentially new deviceName.
sysExec(SYSTEMCTL_BIN " restart pipedald");
}
else
{
@@ -417,7 +427,7 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
settings.Save();
// ******************* device_uuid
DeviceIdFile deviceIdFile;
ServiceConfiguration deviceIdFile;
deviceIdFile.Load();
deviceIdFile.deviceName = settings.hotspotName_;
deviceIdFile.Save();
@@ -502,12 +512,9 @@ void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
sysExec(SYSTEMCTL_BIN " daemon-reload");
sysExec(SYSTEMCTL_BIN " stop pipedal_p2pd");
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
sysExec(SYSTEMCTL_BIN " stop wpa_supplicant");
sysExec(SYSTEMCTL_BIN " restart dhcpcd");
sysExec(SYSTEMCTL_BIN " unmask wpa_supplicant");
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant");
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant"); // ?? Not sure that's still right. :-/
sysExec(SYSTEMCTL_BIN " start dnsmasq");
sysExec(SYSTEMCTL_BIN " enable dnsmasq");
sysExec(SYSTEMCTL_BIN " start pipedal_p2pd");
+2 -2
View File
@@ -25,7 +25,7 @@
#include <stdexcept>
#include <random>
#include "ConfigUtil.hpp"
#include "DeviceIdFile.hpp"
#include "ServiceConfiguration.hpp"
#include "WriteTemplateFile.hpp"
#include "ss.hpp"
@@ -169,7 +169,7 @@ void WifiDirectConfigSettings::Load()
this->channel_ = "6";
}
try {
DeviceIdFile deviceIdFile;
ServiceConfiguration deviceIdFile;
deviceIdFile.Load();
if (deviceIdFile.deviceName == "")
{
+63
View File
@@ -0,0 +1,63 @@
/*
* MIT License
*
* Copyright (c) 2022 Robin E. R. 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 <vector>
namespace p2p {
template <typename T> class autoptr_vector
{
std::vector<T*> v;
private:
// no copy no move.
autoptr_vector(const autoptr_vector<T> &) = delete;
autoptr_vector(autoptr_vector<T> && ) = delete;
public:
using iterator = std::vector<T*>::const_iterator;
autoptr_vector() { }
autoptr_vector(std::initializer_list<T*> l)
:v(l)
{
}
~autoptr_vector() {
for (T *i: v)
{
delete i;
}
}
void push_back(T*value) { v.push_back(value);}
iterator begin() const { return v.begin(); }
iterator end() const { return v.end(); }
T*operator[](size_t index) const { return v[index]; }
size_t size() const { return v.size(); }
};
}
+2 -2
View File
@@ -23,7 +23,7 @@
#include "BeastServer.hpp"
#include <iostream>
#include "Lv2Log.hpp"
#include "DeviceIdFile.hpp"
#include "ServiceConfiguration.hpp"
#include "AvahiService.hpp"
#include "PiPedalSocket.hpp"
@@ -428,7 +428,7 @@ public:
#define LINK_LOCAL_WEB_SOCKET 1
#if LINK_LOCAL_WEB_SOCKET
std::string webSocketAddress = GetLinkLocalAddress(fromAddress);
Lv2Log::info(SS("Web Socket Adddress: " << webSocketAddress << ":" << portNumber));
Lv2Log::info(SS("Web Socket Address: " << webSocketAddress << ":" << portNumber));
#else
std::string webSocketAddress = "*";
#endif
@@ -1,14 +1,19 @@
[Unit]
Description=PiPedal P2P Session Manager
After=network.target
After=nss-lookup.target
After=wpa_supplicant.service
After=dhcpcd.service
StartLimitIntervalSec=300
StartLimitBurst=8
[Service]
ExecStart=${COMMAND} --systemd --log-level debug
Type=notify
WorkingDirectory=/var/pipedal
TimeoutStopSec=5
Restart=on-failure
RestartSec=10s
[Install]
WantedBy=multi-user.target
+1
View File
@@ -7,6 +7,7 @@ After=local-fs.target
[Service]
Restart=on-failure
Type=notify
LimitMEMLOCK=infinity
LimitRTPRIO=95