pipedalconfig Wifi Direct configuration

This commit is contained in:
Robin Davies
2022-04-18 23:37:05 -04:00
parent dafe5abaf1
commit 64d5dc00b2
15 changed files with 1157 additions and 129 deletions
+36 -1
View File
@@ -4,6 +4,7 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"name": "(gdb) pipedaltest Launch", "name": "(gdb) pipedaltest Launch",
"type": "cppdbg", "type": "cppdbg",
@@ -111,7 +112,41 @@
"ignoreFailures": true "ignoreFailures": true
} }
] ]
} },
{
"name": "(gdb) pipedalconfig",
"type": "cppdbg",
"request": "launch",
// Resolved by CMake Tools:
"program": "${command:cmake.launchTargetPath}",
"args": [
],
"stopAtEntry": false,
"cwd": "${workspaceFolder}",
"environment": [
{
// add the directory where our target was built to the PATHs
// it gets resolved by CMake Tools:
"name": "PATH",
"value": "$PATH:${command:cmake.launchTargetDirectory}"
},
{
"name": "OTHER_VALUE",
"value": "Something something"
}
],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
] ]
} }
+7
View File
@@ -14,10 +14,14 @@ enable_testing()
# Frameworks # Frameworks
# catch # catch
add_subdirectory("submodules/pipedal_p2pd")
add_subdirectory("react") add_subdirectory("react")
add_subdirectory("src") add_subdirectory("src")
install (TARGETS pipedal_p2pd DESTINATION ${CMAKE_INSTALL_PREFIX}/bin
)
# select LV2 source directory for the current build architecture # select LV2 source directory for the current build architecture
@@ -51,6 +55,9 @@ install (FILES ${PROJECT_SOURCE_DIR}/src/template.service
${PROJECT_SOURCE_DIR}/src/templateAdmin.service ${PROJECT_SOURCE_DIR}/src/templateAdmin.service
${PROJECT_SOURCE_DIR}/src/templateJack.service ${PROJECT_SOURCE_DIR}/src/templateJack.service
${PROJECT_SOURCE_DIR}/src/defaultFavorites.json ${PROJECT_SOURCE_DIR}/src/defaultFavorites.json
${PROJECT_SOURCE_DIR}/src/templateP2pd.service
${PROEJCT_SOURCE_DIR}/src/template_p2pd.conf
DESTINATION /etc/pipedal/config ) DESTINATION /etc/pipedal/config )
install (DIRECTORY ${LV2_SOURCE_DIRECTORY} install (DIRECTORY ${LV2_SOURCE_DIRECTORY}
+11 -1
View File
@@ -102,6 +102,7 @@ add_custom_command(
set (PIPEDAL_SOURCES set (PIPEDAL_SOURCES
P2pConfigFiles.hpp
StdErrorCapture.hpp StdErrorCapture.cpp StdErrorCapture.hpp StdErrorCapture.cpp
Ipv6Helpers.cpp Ipv6Helpers.hpp Ipv6Helpers.cpp Ipv6Helpers.hpp
PluginPreset.cpp PluginPreset.hpp PluginPreset.cpp PluginPreset.hpp
@@ -110,6 +111,7 @@ set (PIPEDAL_SOURCES
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
WifiDirectConfigSettings.hpp WifiDirectConfigSettings.cpp
RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp RequestHandler.hpp json.cpp json.hpp Scratch.cpp Lv2Host.hpp Lv2Host.cpp
PluginType.hpp PluginType.cpp PluginType.hpp PluginType.cpp
Lv2Log.hpp Lv2Log.cpp Lv2Log.hpp Lv2Log.cpp
@@ -219,19 +221,24 @@ add_test(NAME DevTest COMMAND pipedaltest "[Dev]")
################################# #################################
add_executable(pipedalconfig json.cpp json.hpp add_executable(pipedalconfig
PrettyPrinter.hpp
json.cpp json.hpp
HtmlHelper.cpp HtmlHelper.hpp HtmlHelper.cpp HtmlHelper.hpp
CommandLineParser.hpp CommandLineParser.hpp
WriteTemplateFile.hpp WriteTemplateFile.cpp WriteTemplateFile.hpp WriteTemplateFile.cpp
PiPedalException.hpp PiPedalException.hpp
ConfigMain.cpp ConfigMain.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp WifiConfigSettings.cpp WifiConfigSettings.hpp
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.hpp
JackServerSettings.hpp JackServerSettings.cpp JackServerSettings.hpp JackServerSettings.cpp
SetWifiConfig.hpp SetWifiConfig.cpp SetWifiConfig.hpp SetWifiConfig.cpp
SystemConfigFile.hpp SystemConfigFile.cpp SystemConfigFile.hpp SystemConfigFile.cpp
SysExec.hpp SysExec.cpp SysExec.hpp SysExec.cpp
asan_options.cpp asan_options.cpp
Lv2Log.cpp Lv2Log.hpp Lv2Log.cpp Lv2Log.hpp
P2pConfigFiles.hpp
) )
target_link_libraries(pipedalconfig PRIVATE pthread atomic stdc++fs target_link_libraries(pipedalconfig PRIVATE pthread atomic stdc++fs
@@ -242,11 +249,14 @@ add_executable(pipedaladmind ShutdownMain.cpp CommandLineParser.hpp
json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp json.hpp json.cpp HtmlHelper.hpp HtmlHelper.cpp Lv2Log.hpp Lv2Log.cpp
Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp Lv2SystemdLogger.hpp Lv2SystemdLogger.cpp
WifiConfigSettings.cpp WifiConfigSettings.hpp WifiConfigSettings.cpp WifiConfigSettings.hpp
WifiDirectConfigSettings.cpp WifiDirectConfigSettings.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 CpuGovernor.cpp CpuGovernor.hpp
asan_options.cpp asan_options.cpp
P2pConfigFiles.hpp
) )
target_link_libraries(pipedaladmind PRIVATE pthread atomic stdc++fs systemd) target_link_libraries(pipedaladmind PRIVATE pthread atomic stdc++fs systemd)
+205 -103
View File
@@ -31,8 +31,10 @@
#include <sys/wait.h> #include <sys/wait.h>
#include <pwd.h> #include <pwd.h>
#include "JackServerSettings.hpp" #include "JackServerSettings.hpp"
#include "P2pConfigFiles.hpp"
#include "PrettyPrinter.hpp"
#define SS(x) ( ((std::stringstream&)(std::stringstream() << x )).str()) #define SS(x) (((std::stringstream &)(std::stringstream() << x)).str())
using namespace std; using namespace std;
using namespace pipedal; using namespace pipedal;
@@ -54,6 +56,7 @@ using namespace pipedal;
#define SERVICE_PATH "/usr/lib/systemd/system" #define SERVICE_PATH "/usr/lib/systemd/system"
#define NATIVE_SERVICE "pipedald" #define NATIVE_SERVICE "pipedald"
#define ADMIN_SERVICE "pipedaladmind" #define ADMIN_SERVICE "pipedaladmind"
#define PIPEDAL_P2PD_SERVICE "pipedal_p2pd"
#define JACK_SERVICE "jack" #define JACK_SERVICE "jack"
#define REMOVE_OLD_SERVICE 1 // Grandfathering: whether to remove the old shutdown service (now pipedaladmind) #define REMOVE_OLD_SERVICE 1 // Grandfathering: whether to remove the old shutdown service (now pipedaladmind)
@@ -110,12 +113,12 @@ void DisableService()
{ {
cout << "Error: Failed to disable the " ADMIN_SERVICE " service."; cout << "Error: Failed to disable the " ADMIN_SERVICE " service.";
} }
#if REMOVE_OLD_SERVICE #if REMOVE_OLD_SERVICE
if (sysExec(SYSTEMCTL_BIN " disable " OLD_SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS) if (sysExec(SYSTEMCTL_BIN " disable " OLD_SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{ {
cout << "Error: Failed to disable the " OLD_SHUTDOWN_SERVICE " service."; cout << "Error: Failed to disable the " OLD_SHUTDOWN_SERVICE " service.";
} }
#endif #endif
} }
void StopService(bool excludeShutdownService = false) void StopService(bool excludeShutdownService = false)
@@ -130,12 +133,12 @@ void StopService(bool excludeShutdownService = false)
{ {
cout << "Error: Failed to stop the " ADMIN_SERVICE " service."; cout << "Error: Failed to stop the " ADMIN_SERVICE " service.";
} }
#if REMOVE_OLD_SERVICE #if REMOVE_OLD_SERVICE
if (sysExec(SYSTEMCTL_BIN " stop " OLD_SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS) if (sysExec(SYSTEMCTL_BIN " stop " OLD_SHUTDOWN_SERVICE ".service") != EXIT_SUCCESS)
{ {
cout << "Error: Failed to stop the " OLD_SHUTDOWN_SERVICE " service."; cout << "Error: Failed to stop the " OLD_SHUTDOWN_SERVICE " service.";
} }
#endif #endif
} }
if (sysExec(SYSTEMCTL_BIN " stop " JACK_SERVICE ".service") != EXIT_SUCCESS) if (sysExec(SYSTEMCTL_BIN " stop " JACK_SERVICE ".service") != EXIT_SUCCESS)
{ {
@@ -202,7 +205,7 @@ static bool userExists(const char *userName)
return true; return true;
} }
static void RemoveLine(const std::string&path, const std::string lineToRemove) static void RemoveLine(const std::string &path, const std::string lineToRemove)
{ {
std::vector<std::string> lines; std::vector<std::string> lines;
try try
@@ -234,16 +237,16 @@ static void RemoveLine(const std::string&path, const std::string lineToRemove)
{ {
throw PiPedalException(SS("Can't write to " << path)); throw PiPedalException(SS("Can't write to " << path));
} }
for (auto&line: lines) for (auto &line : lines)
{ {
f << line << endl; f << line << "\n";
} }
} }
} }
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
cout << "Failed to update " << path << ". " << e.what(); cout << "Failed to update " << path << ". " << e.what();
} }
} }
@@ -252,8 +255,7 @@ void UninstallPamEnv()
{ {
RemoveLine( RemoveLine(
"/etc/security/pam_env.conf", "/etc/security/pam_env.conf",
PAM_LINE PAM_LINE);
);
} }
void InstallPamEnv() void InstallPamEnv()
{ {
@@ -295,16 +297,16 @@ void InstallPamEnv()
{ {
throw PiPedalException(SS("Can't write to " << path)); throw PiPedalException(SS("Can't write to " << path));
} }
for (auto&line: lines) for (auto &line : lines)
{ {
f << line << endl; f << line << "\n";
} }
} }
} }
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
cout << "Failed to update " << path << ". " << e.what(); cout << "Failed to update " << path << ". " << e.what();
} }
} }
@@ -320,9 +322,9 @@ void InstallLimits()
if (!std::filesystem::exists(limitsConfig)) if (!std::filesystem::exists(limitsConfig))
{ {
ofstream output(limitsConfig); ofstream output(limitsConfig);
output << "# Realtime priority group used by pipedal/jack daemon" << endl; output << "# Realtime priority group used by pipedal/jack daemon" << "\n";
output << "@audio - rtprio 95" << endl; output << "@audio - rtprio 95" << "\n";
output << "@audio - memlock unlimited" << endl; output << "@audio - memlock unlimited" << "\n";
} }
} }
@@ -365,15 +367,15 @@ void InstallJackService()
} }
// lock account for login. // lock account for login.
silentSysExec("passwd -l " JACK_SERVICE_ACCOUNT_NAME); silentSysExec("passwd -l " JACK_SERVICE_ACCOUNT_NAME);
} }
// Add to audio groups. // Add to audio groups.
// sysExec(USERMOD_BIN " -a -G " JACK_SERVICE_GROUP_NAME " " JACK_SERVICE_ACCOUNT_NAME); // sysExec(USERMOD_BIN " -a -G " JACK_SERVICE_GROUP_NAME " " JACK_SERVICE_ACCOUNT_NAME);
sysExec(USERMOD_BIN " -a -G" AUDIO_SERVICE_GROUP_NAME " " JACK_SERVICE_ACCOUNT_NAME); sysExec(USERMOD_BIN " -a -G" AUDIO_SERVICE_GROUP_NAME " " JACK_SERVICE_ACCOUNT_NAME);
// If ONBOARDING, add to bluetooth group. // If ONBOARDING, add to bluetooth group.
#ifdef ONBOARDING #ifdef ONBOARDING
sysExec(USERMOD_BIN " -a -G bluetooth " JACK_SERVICE_ACCOUNT_NAME); sysExec(USERMOD_BIN " -a -G bluetooth " JACK_SERVICE_ACCOUNT_NAME);
#endif #endif
// deploy the systemd service file // deploy the systemd service file
std::map<std::string, std::string> map; // nothing to customize. std::map<std::string, std::string> map; // nothing to customize.
@@ -383,7 +385,6 @@ void InstallJackService()
MaybeStartJackService(); MaybeStartJackService();
} }
int SudoExec(char **argv) int SudoExec(char **argv)
{ {
int pbPid; int pbPid;
@@ -404,21 +405,25 @@ int SudoExec(char **argv)
void Uninstall() void Uninstall()
{ {
OnWifiUninstall();
StopService(); StopService();
DisableService(); DisableService();
silentSysExec(SYSTEMCTL_BIN " stop jack"); silentSysExec(SYSTEMCTL_BIN " stop jack");
silentSysExec(SYSTEMCTL_BIN " disable jack"); silentSysExec(SYSTEMCTL_BIN " disable jack");
OnWifiUninstall();
std::filesystem::remove("/usr/bin/systemd/system/" OLD_SHUTDOWN_SERVICE ".service"); std::filesystem::remove("/usr/bin/systemd/system/" OLD_SHUTDOWN_SERVICE ".service");
std::filesystem::remove("/usr/bin/systemd/system/" ADMIN_SERVICE ".service"); std::filesystem::remove("/usr/bin/systemd/system/" ADMIN_SERVICE ".service");
std::filesystem::remove("/usr/bin/systemd/system/" PIPEDAL_P2PD_SERVICE ".service");
std::filesystem::remove("/usr/bin/systemd/system/" NATIVE_SERVICE ".service"); std::filesystem::remove("/usr/bin/systemd/system/" NATIVE_SERVICE ".service");
std::filesystem::remove("/usr/bin/systemd/system/" JACK_SERVICE ".service"); std::filesystem::remove("/usr/bin/systemd/system/" JACK_SERVICE ".service");
UninstallPamEnv(); UninstallPamEnv();
UninstallLimits(); UninstallLimits();
sysExec(SYSTEMCTL_BIN " daemon-reload"); sysExec(SYSTEMCTL_BIN " daemon-reload");
} }
void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress) void Install(const std::filesystem::path &programPrefix, const std::string endpointAddress)
{ {
if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS) if (sysExec(GROUPADD_BIN " -f " AUDIO_SERVICE_GROUP_NAME) != EXIT_SUCCESS)
@@ -470,7 +475,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
} }
// lock account for login. // lock account for login.
silentSysExec("passwd -l " SERVICE_ACCOUNT_NAME); silentSysExec("passwd -l " SERVICE_ACCOUNT_NAME);
} }
// Add to audio groups. // Add to audio groups.
sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME); sysExec(USERMOD_BIN " -a -G " AUDIO_SERVICE_GROUP_NAME " " SERVICE_ACCOUNT_NAME);
@@ -512,7 +517,8 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
{ {
// create it. // create it.
std::ofstream f(portAuthFile); std::ofstream f(portAuthFile);
if (!f.is_open()) { if (!f.is_open())
{
throw PiPedalException("Failed to create " + portAuthFile.string()); throw PiPedalException("Failed to create " + portAuthFile.string());
} }
} }
@@ -535,7 +541,7 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
} }
} }
cout << "Creating Systemd file." << endl; cout << "Creating Systemd file." << "\n";
std::map<string, string> map; std::map<string, string> map;
@@ -568,15 +574,147 @@ void Install(const std::filesystem::path &programPrefix, const std::string endpo
} }
WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/templateAdmin.service"), GetServiceFileName(ADMIN_SERVICE)); WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/templateAdmin.service"), GetServiceFileName(ADMIN_SERVICE));
// /usr/bin/pipedal_p2pd --config-file /etc/pipedal/config/template_p2pd.conf
std::string pipedal_p2pd_cmd = SS(
(programPrefix / "bin" / "pipedal_p2pd").string()
<< " --config-file " << PIPEDAL_P2PD_CONF_PATH);
map["COMMAND"] = pipedal_p2pd_cmd;
WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/templateP2pd.service"), GetServiceFileName(PIPEDAL_P2PD_SERVICE));
sysExec(SYSTEMCTL_BIN " daemon-reload"); sysExec(SYSTEMCTL_BIN " daemon-reload");
cout << "Starting services" << endl; cout << "Starting services" << "\n";
RestartService(false); RestartService(false);
EnableService(); EnableService();
cout << "Complete" << endl;
cout << "Complete" << "\n";
} }
static void PrintHelp()
{
PrettyPrinter pp;
pp << "pipedalconfig - Command-line post-install configuration for PiPedal" << "\n"
<< "Copyright (c) 2022 Robin Davies. All rights reserved." << "\n"
<< "\n"
<< "See https://rerdavies.github.io/pipedal/Documentation.html for "
<< "online documentation." << "\n"
<< "\n"
<< "Syntax:" << "\n";
pp << Indent(4)
<< "pipedalconfig [Options...]" << "\n"
<< "\n";
pp.Indent(0)
<< "Options: " << "\n\n";
pp.Indent(20);
pp
<< HangingIndent() << " -h --help\t"
<< "Display this message." << "\n"
<< "\n"
<< HangingIndent() << " --install [--port <port#>]\t"
<< "Install or re-install services and service accounts." << "\n"
<< "\n"
<< "The --port option controls which port the web server uses."
<< "\n\n"
<< HangingIndent() << " --uninstall\t"
<< "Remove installed services." << "\n"
<< "\n"
<< HangingIndent() << " --enable\t"
<< "Start the pipedal service at boot time." << "\n"
<< "\n"
<< HangingIndent() << " --disable\tDo not start the pipedal service at boot time." << "\n"
<< "\n"
<< HangingIndent() << " --start\tStart the pipedal services." << "\n"
<< "\n"
<< HangingIndent() << " --stop\tStop the pipedal services." << "\n"
<< "\n"
<< HangingIndent() << " --restart\tRestart the pipedal services." << "\n"
<< "\n"
<< HangingIndent() << " --enable-p2p <country_code> <ssid> [[<pin>] <channel>]\t"
<< "Enable the P2P (Wi-Fi Direct) hotspot." << "\n\n"
<< "<country_code> is the 2-letter ISO-3166 country code for "
"the country you are in. see below for further notes."
<< "\n\n"
<< "<ssid> is the name you see when connecting. "
<< "\n\n"
<< "<pin> is an exactly-eight-digit pin number that you must "
<< "enter when connecting to the hotspot. If you don't "
<< "provide a pin, pipedalconfig will generate and "
<< "display a random pin for you. The pin is a "
<< "so-called \"label\" pin, which is the same every "
<< "time you are asked to enter it (unlike a keypad pin "
<< "which changes every time you need to enter it."
<< "\n\n"
<< "Consider attaching a label to the bottom of your device "
<< "so you can can remember the pin if you wan't to connect a new "
<< "device to PiPedal. (It's also available on the Settings page of PiPedal, if you have access to PiPedal UI on another device.)"
<< "\n\n"
<< "For best performance, the channel number should be 1, 6, or 11 (the Wifi Direct \"social\" channels). "
<< "If you don't supply a channel number, PiPedal will select the least-busy of channels 1, 6 and 11."
<< "\n\n"
<< HangingIndent() << " --disable-p2p\tDisabled Wi-Fi Direct access."
<< "\n\n"
<< HangingIndent() << " --enable-legacy-ap\t <country_code> <ssid> <wep_password> <channel>\tEnable a legacy Wi-Fi access point."
<< "\n\n"
<< "Enable a legacy Wi-Fi access point. \n\n"
<< "country_code is the 2-letter ISO-3166 country code for "
<< "the country you are in. see below for further notes."
<< "\n\n"
<< "See below for an explanation of when you might want to use a legacy Wi-Fi access point instead of Wifi-Direct access."
<< "an explanation of when you might want to use a legacy Access Point instead of "
<< "a P2P (Wi-Fi Direct) connection. Generally, you should prefer a P2p connection "
<< "to an ordinary Hotspot connection."
<< "\n\n"
<< HangingIndent() << " --disable-legacy-ap\tDisabled the legacy Wi-Fi access point."
<< "\n\n"
<< Indent(0) << "Country codes:"
<< "\n\n"
<< Indent(4)
<< "Country codes are used to determine Wi-Fi-regulatory regime - the "
<< "channels you are legally allowed to use, and the features which must "
<< "be enabled or disabled on the channels you can use. "
<< "\n\n"
<< "Without a country code, Wi-Fi must be restricted to channels 1 through 11 "
<< "with reduced amplitude and feature sets."
<< "\n\n"
<< "For the most part, Wi-Fi country codes are taken from the list of ISO 3661 "
<< "2-letter country codes; although there are a handful of exceptions for small "
<< "countries and islands. See the Alpha-2 code column of "
<< "\n\n"
<< Indent(8) << "https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes."
<< "\n\n"
<< Indent(0) << "Legacy Wi-Fi Access Points:"
<< "\n\n"
<< Indent(4)
<< "Some older devices may not be able to connect to PiPedal using Wi-Fi Direct connections. "
"Old Apple devices, for example, do not support Wi-Fi Direct. In theory, Wi-Fi Direct should "
"allow legacy Wi-Fi devices to connect to a Wi-Fi Direct access points as if it were an "
"ordinary Access Point. If this turns out "
"not to be the case, you can configure PiPedal to provide a legacy Wi-Fi access point instead. "
"\n\n"
"Unlike Wi-Fi Direct connections, legacy Access Points will prevent both the connecting device "
"and the PiPedal host machine from connecting to the Internet over a simultanous Wi-Fi connection Access "
"Point. On a connecting Android device, you won't be able to use the data connection either when a legacy "
"Wi-Fi connection is active."
"\n\n"
"Wi-Fi Direct connections are "
"therefore preferrable under almost all circumstances.\n\n"
;
}
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
@@ -588,6 +726,7 @@ int main(int argc, char **argv)
bool stop = false, start = false; bool stop = false, start = false;
bool enable = false, disable = false, restart = false; bool enable = false, disable = false, restart = false;
bool enable_ap = false, disable_ap = false; bool enable_ap = false, disable_ap = false;
bool enable_p2p = false, disable_p2p = false;
bool nopkexec = false; bool nopkexec = false;
bool excludeShutdownService = false; bool excludeShutdownService = false;
std::string prefixOption; std::string prefixOption;
@@ -605,14 +744,17 @@ int main(int argc, char **argv)
parser.AddOption("--help", &help); parser.AddOption("--help", &help);
parser.AddOption("--prefix", &prefixOption); parser.AddOption("--prefix", &prefixOption);
parser.AddOption("--port", &portOption); parser.AddOption("--port", &portOption);
parser.AddOption("--enable_ap", &enable_ap); parser.AddOption("--enable-ap", &enable_ap);
parser.AddOption("--disable_ap", &disable_ap); parser.AddOption("--disable-ap", &disable_ap);
parser.AddOption("--enable-p2p", &enable_p2p);
parser.AddOption("--disable-p2p", &disable_p2p);
parser.AddOption("--excludeShutdownService", &excludeShutdownService); // private (unstable) option used by shutdown service. parser.AddOption("--excludeShutdownService", &excludeShutdownService); // private (unstable) option used by shutdown service.
try try
{ {
parser.Parse(argc, (const char **)argv); parser.Parse(argc, (const char **)argv);
int actionCount = install + uninstall + stop + start + enable + disable + enable_ap + disable_ap + restart; int actionCount = install + uninstall + stop + start + enable + disable + enable_ap + disable_ap + restart + enable_p2p + disable_p2p;
if (actionCount > 1) if (actionCount > 1)
{ {
throw PiPedalException("Please provide only one action."); throw PiPedalException("Please provide only one action.");
@@ -621,20 +763,11 @@ int main(int argc, char **argv)
{ {
help = true; help = true;
} }
if (enable_ap) if ((!enable_p2p) && (!enable_ap))
{
if (parser.Arguments().size() != 4)
{
cout << "Error: "
<< "Incorrect number of arguments supplied for --enable_ap option." << endl;
helpError = true;
}
}
else
{ {
if (parser.Arguments().size() != 0) if (parser.Arguments().size() != 0)
{ {
cout << "Error: Unexpected argument : " << parser.Arguments()[0] << endl; cout << "Error: Unexpected argument : " << parser.Arguments()[0] << "\n";
helpError = true; helpError = true;
} }
} }
@@ -642,64 +775,23 @@ int main(int argc, char **argv)
catch (const std::exception &e) catch (const std::exception &e)
{ {
std::string s = e.what(); std::string s = e.what();
cout << "Error: " << s << endl; cout << "Error: " << s << "\n";
helpError = true; helpError = true;
} }
if (helpError)
if (help || helpError)
{ {
if (helpError) cout << "\n";
cout << endl; return EXIT_FAILURE; // don't scroll the error off the screen.
}
cout << "pipedalconfig - Post-install configuration for pipdeal" << endl if (help)
<< "Copyright (c) 2022 Robin Davies. All rights reserved." << endl {
<< endl PrintHelp();
<< "Syntax:" << endl return EXIT_SUCCESS;
<< " pipedalconfig [Options...]" << endl
<< "Options: " << endl
<< " -h --help Display this message." << endl
<< endl
<< " --install Install services and service accounts." << endl
<< endl
<< " --prefix Either /usr/local or /usr as appropriate for the install" << endl
<< " (only valid with the -install option). Usually determined " << endl
<< " automatically." << endl
<< endl
<< " --uninstall Remove installed services." << endl
<< endl
<< " --enable Start the pipedal service at boot time." << endl
<< endl
<< " --disable Do not start the pipedal service at boot time." << endl
<< endl
<< " --stop Stop the pipedal services." << endl
<< endl
<< " --start Start the pipedal services." << endl
<< endl
<< " --restart Restart the pipedal services." << endl
<< endl
<< " --port Port for the web server (only with -install option)." << endl
<< " Either a port (e.g. '81'), or a full endpoint (e.g.: " << endl
<< " '127.0.0.1:8080'). If no address is specified, the " << endl
<< " web server will listen on 0.0.0.0 (any)." << endl
<< endl
<< " --enable_ap <country_code> <ssid> <wep_password> <channel>" << endl
<< " Enable the Wi-Fi access point. <country_code> is the " << endl
<< " 2-letter ISO-3166 country code for the country in " << endl
<< " which you are currently located. The country code " << 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
<< endl
<< " --disable_ap Disabled the Wi-Fi access point." << endl
<< endl
<< "Description:" << endl
<< " pipedalconfig performs post-install configuration of pipedal." << endl
<< endl;
exit(helpError ? EXIT_FAILURE : EXIT_SUCCESS);
} }
if (portOption.size() != 0 && !install) if (portOption.size() != 0 && !install)
{ {
cout << "Error: -port option can only be specified with the -install option." << endl; cout << "Error: -port option can only be specified with the -install option." << "\n";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
@@ -780,17 +872,27 @@ int main(int argc, char **argv)
{ {
DisableService(); DisableService();
} }
else if (enable_p2p)
{
auto argv = parser.Arguments();
WifiDirectConfigSettings settings;
settings.ParseArguments(argv);
settings.valid_ = true;
settings.enable_ = false;
SetWifiDirectConfig(settings);
}
else if (disable_p2p)
{
WifiDirectConfigSettings settings;
settings.valid_ = true;
settings.enable_ = false;
SetWifiDirectConfig(settings);
}
else if (enable_ap) else if (enable_ap)
{ {
auto argv = parser.Arguments(); auto argv = parser.Arguments();
WifiConfigSettings settings; WifiConfigSettings settings;
settings.valid_ = true; settings.ParseArguments(argv);
settings.enable_ = true;
settings.countryCode_ = argv[0];
settings.hotspotName_ = argv[1];
settings.password_ = argv[2];
settings.channel_ = argv[3];
settings.hasPassword_ = settings.password_.length() != 0;
SetWifiConfig(settings); SetWifiConfig(settings);
} }
@@ -804,7 +906,7 @@ int main(int argc, char **argv)
} }
catch (const std::exception &e) catch (const std::exception &e)
{ {
cout << "Error: " << e.what() << endl; cout << "Error: " << e.what() << "\n";
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
} }
+31
View File
@@ -0,0 +1,31 @@
/*
* 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
#define DEVICE_GUID_FILE "/etc/pipedal/config/device_uuid"
#define PIPEDAL_P2PD_CONF_PATH "/etc/pipedal/config/template_p2pd.conf"
+239
View File
@@ -0,0 +1,239 @@
/*
* 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 <iostream>
#include <string>
#include <vector>
#include <functional>
namespace pipedal
{
using namespace std;
class PrettyPrinter
{
private:
std::ostream &s;
size_t lineWidth = 80;
std::vector<char> lineBuffer;
size_t column = 0;
size_t indent = 0;
size_t tabSize = 4;
bool hangingIndent = false;
bool hungIndent = false;
public:
PrettyPrinter()
: s(std::cout)
{
lineBuffer.resize(lineWidth * 2);
}
std::ostream&Stream() const { return s;}
PrettyPrinter& Indent(size_t indent)
{
this->indent = indent;
return *this;
}
void WriteLine() {
lineBuffer.push_back('\n');
s.write(&lineBuffer[0],lineBuffer.size());
lineBuffer.clear();
column = 0;
}
PrettyPrinter& HangingIndent()
{
hangingIndent = true;
return *this;
}
void Write(const std::string &text)
{
for (char c : text)
{
if (c == '\n')
{
WriteLine();
hungIndent = false;
hangingIndent = false;
}
else
{
if (column <= indent)
{
if (hangingIndent)
{
hangingIndent = false;
hungIndent = true;
} else if (!hungIndent) {
if (c == ' ')
{
continue;
}
while (column < indent)
{
lineBuffer.push_back(' ');
++column;
}
}
}
if (c == '\t')
{
if (hungIndent)
{
if (column < indent-1)
{
while (column < indent)
{
lineBuffer.push_back(' ');
++column;
}
} else {
WriteLine();
column = 0;
while (column < indent)
{
lineBuffer.push_back(' ');
++column;
}
}
hungIndent = false;
} else {
lineBuffer.push_back(' ');
++column;
while ((column % tabSize) != 0)
{
lineBuffer.push_back(' ');
++column;
}
}
}
else if ((c & 0x80) == 0)
{
lineBuffer.push_back(c);
++column;
}
else
{
//utf-8 sequence.
lineBuffer.push_back(c);
if ((c & 0xC0) == 0x80)
{
++column;
}
}
if (column >= lineWidth - 1)
{
size_t breakPos = FindBreak();
size_t startOfOverflow = breakPos;
while (startOfOverflow < lineBuffer.size() && lineBuffer[startOfOverflow] == ' ')
{
++startOfOverflow;
}
std::string overflow = string(&lineBuffer[startOfOverflow], lineBuffer.size() - startOfOverflow);
lineBuffer.resize(breakPos);
WriteLine();
this->Write(overflow);
}
}
}
}
private:
size_t FindBreak()
{
size_t i = lineBuffer.size();
while (i > indent && lineBuffer[i-1] != ' ')
{
--i;
}
if (i > indent)
return i;
return lineBuffer.size();
}
};
template <typename T>
PrettyPrinter& operator<< (PrettyPrinter &p, T &v)
{
std::stringstream s;
s << v;
p.Write(s.str());
return p;
}
inline PrettyPrinter& operator << (PrettyPrinter& p,const std::string&s)
{
p.Write(s);
return p;
}
// iomanip
// template<typename _CharT, typename _Traits>
// inline basic_ostream<_CharT, _Traits>&
// endl(basic_ostream<_CharT, _Traits>& __os)
// { return flush(__os.put(__os.widen('\n'))); }
inline PrettyPrinter& operator << (PrettyPrinter& p,PrettyPrinter& (*iomanip)(PrettyPrinter&os))
{
return (*iomanip)(p);
}
using pp_manip = std::function<PrettyPrinter& (PrettyPrinter&)>;
inline
PrettyPrinter& operator << (PrettyPrinter&pp,pp_manip manip)
{
return manip(pp);
}
pp_manip Indent(int n)
{
return [n] (PrettyPrinter&pp) ->PrettyPrinter& {
pp.Indent(n);
return pp;
};
}
pp_manip HangingIndent()
{
return [] (PrettyPrinter&pp) ->PrettyPrinter&{
pp.HangingIndent();
return pp;
};
}
}
+333 -23
View File
@@ -18,22 +18,91 @@
// 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.
#include "pch.h" #include "pch.h"
#include "ss.hpp"
#include "P2pConfigFiles.hpp"
#include "SetWifiConfig.hpp" #include "SetWifiConfig.hpp"
#include "PiPedalException.hpp" #include "PiPedalException.hpp"
#include "SystemConfigFile.hpp" #include "SystemConfigFile.hpp"
#include "SysExec.hpp" #include "SysExec.hpp"
#include "WriteTemplateFile.hpp"
using namespace pipedal; using namespace pipedal;
using namespace std;
#define IP_RANGE "172.22.0" #define APD_IP_PREFIX "172.22.0"
#define PIPEDAL_IP IP_RANGE ".1" #define APD_IP_ADDRESS APD_IP_PREFIX ".1"
#define APD_INTERFACE_ADDR APD_IP_ADDRESS "/24"
#define PIPEDAL_NETWORK PIPEDAL_IP "/24" #define P2P_IP_PREFIX "172.23.0"
#define P2P_IP_ADDRESS P2P_IP_PREFIX ".2" // no gateway!
#define P2P_INTERFACE_ADDR P2P_IP_ADDRESS "/24"
#define SYSTEMCTL_BIN "/usr/bin/systemctl" #define SYSTEMCTL_BIN "/usr/bin/systemctl"
static void restoreDhcpcdConfFile()
static char DNSMASQ_APD_PATH[] = "/etc/dnsmasq.d/30-pipedal-apd.conf";
static char DNSMASQ_P2P_PATH[] = "/etc/dnsmasqdnsmaqsq.d/30-pipedal-p2p.conf";
static bool IsApdInstalled() {
return std::filesystem::exists(DNSMASQ_APD_PATH);
}
static bool IsP2pInstalled() {
return std::filesystem::exists(DNSMASQ_P2P_PATH);
}
uint32_t pipedal::ChannelToWifiFrequency(const std::string &channel)
{
std::string t = channel;
// remove dprecated band specs.
if (t.size() > 1 && t[0] == 'a' || t[0] == 'g')
{
t = t.substr(1);
}
size_t size = t.length();
unsigned long long lChannel = std::stoull(t,&size);
if (size != t.length())
{
throw invalid_argument("Expecting a number: '" + t + "'.");
}
return ChannelToWifiFrequency((uint32_t)lChannel);
}
uint32_t pipedal::ChannelToWifiFrequency(uint32_t channel)
{
// 2.4GHz.
if (channel >= 1 && channel <= 13)
{
return 2412 + 5*(channel-1);
}
if (channel == 14)
{
return 2484;
}
// 802.11y
if (channel >= 131 && channel < 137)
{
return 3660 + (channel-131)*5;
}
if (channel >= 32 && channel <= 68 && (channel & 1) == 0)
{
return 5160 + (channel-32)/2*10;
}
if (channel == 96) return 5480;
if (channel >= 100 && channel <= 196)
{
return 5500 + (channel-100)/5;
}
throw invalid_argument(SS("Invalid channel: " << channel));
}
static void restoreApdDhcpdConfFile()
{ {
// remove the interface wlan0 section. // remove the interface wlan0 section.
std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf"); std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf");
@@ -71,35 +140,48 @@ static void restoreDhcpcdConfFile()
} }
} }
static void restoreDnsmasqConfFile()
static void restoreApdDnsmasqConfFile()
{ {
std::filesystem::path path("/etc/dnsmasq.c/pipedal.conf"); std::filesystem::path path(DNSMASQ_APD_PATH);
if (std::filesystem::exists(path)) if (std::filesystem::exists(path))
{ {
std::filesystem::remove(path); std::filesystem::remove(path);
} }
} }
static void UninstallHostApd()
{
if (IsApdInstalled())
{
sysExec(SYSTEMCTL_BIN " stop hostapd");
sysExec(SYSTEMCTL_BIN " disable hostapd");
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
sysExec(SYSTEMCTL_BIN " disable dnsmasq");
restoreApdDhcpdConfFile();
restoreApdDnsmasqConfFile();
sysExec(SYSTEMCTL_BIN " unmask wpa_supplicant"); // TODO: Do we need this now we have no_hook line.
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant");
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
}
}
static void UninstallP2p();
void pipedal::SetWifiConfig(const WifiConfigSettings &settings) void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
{ {
char band; char band;
if (!settings.enable_) if (!settings.enable_)
{ {
sysExec(SYSTEMCTL_BIN " stop hostapd"); UninstallHostApd();
sysExec(SYSTEMCTL_BIN " disable hostapd");
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
sysExec(SYSTEMCTL_BIN " disable dnsmasq");
restoreDhcpcdConfFile();
restoreDnsmasqConfFile();
sysExec(SYSTEMCTL_BIN " unmask wpa_supplicant");
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant");
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
} }
else else
{ {
UninstallP2p();
std::filesystem::path path("/etc/hostapd/hostapd.conf"); std::filesystem::path path("/etc/hostapd/hostapd.conf");
SystemConfigFile apdConfig; SystemConfigFile apdConfig;
if (std::filesystem::exists(path)) if (std::filesystem::exists(path))
@@ -160,7 +242,7 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
apdConfig.Set("channel", channel); apdConfig.Set("channel", channel);
// ******************** dsnmasq ****** // ******************** dsnmasq ******
std::filesystem::path dnsMasqPath("/etc/dnsmasq.d/pipedal.conf"); std::filesystem::path dnsMasqPath(DNSMASQ_APD_PATH);
SystemConfigFile dnsMasq; SystemConfigFile dnsMasq;
try try
@@ -173,12 +255,12 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
} }
dnsMasq.Set("interface", "wlan0", "Name of the Wi-Fi interface"); dnsMasq.Set("interface", "wlan0", "Name of the Wi-Fi interface");
dnsMasq.Set("listen-address", PIPEDAL_IP); dnsMasq.Set("listen-address", APD_IP_ADDRESS);
dnsMasq.SetDefault("dhcp-range", IP_RANGE ".3," IP_RANGE ".127,12h", "dhcp configuration"); dnsMasq.SetDefault("dhcp-range", APD_IP_PREFIX ".3," APD_IP_PREFIX ".127,12h", "dhcp configuration");
dnsMasq.SetDefault("domain", "local"); dnsMasq.SetDefault("domain", "local");
std::stringstream sAddress; std::stringstream sAddress;
sAddress << "/" << settings.mdnsName_ << ".local/" IP_RANGE << ".1"; sAddress << "/" << settings.mdnsName_ << ".local/" APD_IP_PREFIX << ".1";
dnsMasq.Set("address", sAddress.str()); dnsMasq.Set("address", sAddress.str());
// ****** dhcpd.conf ***/ // ****** dhcpd.conf ***/
@@ -228,7 +310,7 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
line = dhcpcd.GetLineCount(); line = dhcpcd.GetLineCount();
} }
dhcpcd.InsertLine(line++, "interface wlan0"); dhcpcd.InsertLine(line++, "interface wlan0");
dhcpcd.InsertLine(line++, " static ip_address=" PIPEDAL_NETWORK); dhcpcd.InsertLine(line++, " static ip_address=" APD_INTERFACE_ADDR);
dhcpcd.InsertLine(line++, " nohook wpa_supplicant"); dhcpcd.InsertLine(line++, " nohook wpa_supplicant");
dhcpcd.Save(dhcpcdConfig); dhcpcd.Save(dhcpcdConfig);
} }
@@ -263,3 +345,231 @@ void pipedal::SetWifiConfig(const WifiConfigSettings &settings)
sysExec(SYSTEMCTL_BIN " enable dnsmasq"); sysExec(SYSTEMCTL_BIN " enable dnsmasq");
} }
} }
/*********************************************************************************
p2p configuration:
dnsmaqsq.d/30-pipedal-p2p.conf:
interface=p2p-wlan0-0
dhcp-range=p2p-wlan0,172.24.0.3,172.24.0.127,1h
domain=local
address=/pipedal.local/172.24.0.1 # do this through DNS-SD?
except-interface=eth0
except-interface=wlan0
except-interface=lo
dhcpcd.conf:
======
interface p2p-wlan0-0
static ip_address=172.24.0.1/16
domain_name_server=172.24.0.1
Watch resolv.conf to make sure we don't lose DNS servers.
************************************************************************************/
void UninstallP2p()
{
if (IsP2pInstalled())
{
sysExec(SYSTEMCTL_BIN " stop pipidal_p2pd");
sysExec(SYSTEMCTL_BIN " disable pipidal_p2pd");
sysExec(SYSTEMCTL_BIN " stop dnsmasq");
sysExec(SYSTEMCTL_BIN " disable dnsmasq");
restoreApdDhcpdConfFile();
restoreApdDnsmasqConfFile();
sysExec(SYSTEMCTL_BIN " enable wpa_supplicant");
sysExec(SYSTEMCTL_BIN " start wpa_supplicant");
}
}
void pipedal::SetWifiDirectConfig(const WifiDirectConfigSettings &settings)
{
char band;
if (!settings.enable_)
{
UninstallP2p();
}
else
{
// ******************* pipedal_p2pd ******
{
// ${COUNTRY_CODE}
//${PIN}
// ${DEVICE_NAME}
// ${WIFI_GROUP_FREQUENCY}
// ${INSTANCE_ID_FILE} /var/pipedal/instance_id
std::map<string, string> map;
map["COUNTRY_CODE"] = settings.countryCode_;
map["PIN"] = settings.pin_;
map["DEVICE_NAME"] = settings.hotspotName_;
map["WIFI_GROUP_FREQUENCY"] = SS(ChannelToWifiFrequency(settings.channel_));
map["INSTANCE_ID_FILE"] = DEVICE_GUID_FILE;
WriteTemplateFile(map, std::filesystem::path("/etc/pipedal/config/template_p2pd.conf"), PIPEDAL_P2PD_CONF_PATH);
}
// ******************** dsnmasq ******
std::filesystem::path dnsMasqPath(DNSMASQ_P2P_PATH);
SystemConfigFile dnsMasq;
try
{
dnsMasq.Load(dnsMasqPath);
}
catch (const std::exception &)
{
// ignore.
}
// interface=p2p-wlan0-0
// dhcp-range=172.24.0.3,172.24.0.127,1h
// domain=local
// address=/pipedal.local/172.24.0.1 # do this through DNS-SD?
// except-interface=eth0
// except-interface=wlan0
// except-interface=lo
dnsMasq.Set("interface", "p2p-wlan0-0", "Name of the Wi-Fi interface");
dnsMasq.Set("listen-address", APD_IP_ADDRESS);
dnsMasq.SetDefault("dhcp-range", P2P_IP_PREFIX ".3," P2P_IP_PREFIX ".127,1h", "dhcp configuration");
dnsMasq.SetDefault("domain", "local");
std::string strAddress = SS("/" << settings.mdnsName_ << ".local/" P2P_IP_PREFIX << ".2");
dnsMasq.Set("address", strAddress);
dnsMasq.AppendLine("except-interface=eth0");
dnsMasq.AppendLine("except-interface=wlan0");
// ****** dhcpd.conf ***/
std::filesystem::path dhcpcdConfig("/etc/dhcpcd.conf");
if (std::filesystem::exists(dhcpcdConfig))
{
SystemConfigFile dhcpcd(dhcpcdConfig);
// dhcpcd.conf:
// ======
// interface p2p-wlan0-0
// static ip_address=172.24.0.1/16
// domain_name_server=172.24.0.1
int line = dhcpcd.GetLineThatStartsWith("hostname");
std::string hostNameLine;
if (settings.mdnsName_ != "")
{
hostNameLine = "hostname " + settings.mdnsName_;
}
else
{
hostNameLine = "hostname"; // the default value.
}
if (line != -1)
{
dhcpcd.SetLineValue(line, hostNameLine);
}
else
{
dhcpcd.AppendLine(hostNameLine);
}
// erase any existing interface for p2p-wlan0-0
line = dhcpcd.GetLineNumber("interface p2p-wlan0-0");
if (line != -1)
{
dhcpcd.EraseLine(line);
while (line < dhcpcd.GetLineCount())
{
const std::string &lineValue = dhcpcd.GetLineValue(line);
if (lineValue.length() > 0 && (lineValue[0] == ' ' || lineValue[0] == '\t'))
{
dhcpcd.EraseLine(line);
}
else
{
break;
}
}
}
if (line == -1)
{
dhcpcd.AppendLine("");
line = dhcpcd.GetLineCount();
}
// interface p2p-wlan0-0
// static ip_address=172.24.0.1/16 (trying int on .1.)
// domain_name_server=172.24.0.1
dhcpcd.InsertLine(line++, "interface p2p-wlan0-0");
dhcpcd.InsertLine(line++, " static ip_address=" P2P_INTERFACE_ADDR);
dhcpcd.InsertLine(line++, " domain_name_servier=" P2P_IP_ADDRESS);
dhcpcd.Save(dhcpcdConfig);
}
// ***** save the config files ***
dnsMasq.Save(dnsMasqPath);
// **************** start services ************
sysExec("rfkill unblock wlan");
sysExec(SYSTEMCTL_BIN " daemon-reload");
sysExec(SYSTEMCTL_BIN " restart wpa_supplicant");
sysExec(SYSTEMCTL_BIN " unmask pipedal_p2pd");
if (sysExec(SYSTEMCTL_BIN " restart pipedal_p2pd") != 0)
{
throw PiPedalException("Unable to start the pipedal_p2pd.");
}
if (sysExec("systemctl is-active --quiet pipedal_p2pd") != 0)
{
throw PiPedalException("Unable to start the access point.");
}
sysExec(SYSTEMCTL_BIN " enable pipedal_p2pd");
sysExec(SYSTEMCTL_BIN " restart dnsmasq");
sysExec(SYSTEMCTL_BIN " enable dnsmasq");
}
}
void pipedal::OnWifiUninstall()
{
// intaller hook
if (IsApdInstalled())
{
UninstallHostApd();
} else if (IsP2pInstalled())
{
UninstallP2p();
}
}
void pipedal::OnWifiInstallComplete()
{
// install has already done a daemon-reload.
// I can't think of anything that needs to be done.
// if (IsApdInstalled())
// {
// sysExec(SYSTEMCTL_BIN " daemon-reload");
// } else if (IsP2pInstalled())
// {
// sysExec(SYSTEMCTL_BIN " daemon-reload");
// }
}
+9
View File
@@ -19,7 +19,16 @@
#pragma once #pragma once
#include "WifiConfigSettings.hpp" #include "WifiConfigSettings.hpp"
#include "WifiDirectConfigSettings.hpp"
namespace pipedal { namespace pipedal {
uint32_t ChannelToWifiFrequency(const std::string &channel);
uint32_t ChannelToWifiFrequency(uint32_t channel);
void SetWifiConfig(const WifiConfigSettings&settings); void SetWifiConfig(const WifiConfigSettings&settings);
void SetWifiDirectConfig(const WifiDirectConfigSettings&settings);
void OnWifiUninstall();
void OnWifiInstallComplete();
} // namespace. } // namespace.
+47
View File
@@ -19,8 +19,10 @@
#include "pch.h" #include "pch.h"
#include "WifiConfigSettings.hpp" #include "WifiConfigSettings.hpp"
#include <stdexcept>
using namespace pipedal; using namespace pipedal;
using namespace std;
JSON_MAP_BEGIN(WifiConfigSettings) JSON_MAP_BEGIN(WifiConfigSettings)
JSON_MAP_REFERENCE(WifiConfigSettings,valid) JSON_MAP_REFERENCE(WifiConfigSettings,valid)
@@ -35,3 +37,48 @@ JSON_MAP_BEGIN(WifiConfigSettings)
JSON_MAP_REFERENCE(WifiConfigSettings,channel) JSON_MAP_REFERENCE(WifiConfigSettings,channel)
JSON_MAP_END() JSON_MAP_END()
bool WifiConfigSettings::ValidateCountryCode(const std::string&value)
{
if (value.size() < 2 || value.size() > 3) return false;
return true;
}
bool WifiConfigSettings::ValidateChannel(const std::string&arguments)
{
// 1) frequency in khz.
// 2) unadorned channel number 1, 2,3 &c.
// 3) With band annotated: g1, a51.
// come back to this.
return true;
}
void WifiConfigSettings::ParseArguments(const std::vector<std::string> &argv)
{
this->valid_ = false;
if (argv.size() != 4) {
throw invalid_argument("Invalid number of arguments.");
}
this->enable_ = true;
this->countryCode_ = argv[0];
this->hotspotName_ = argv[1];
this->mdnsName_ = this->hotspotName_;
this->password_ = argv[2];
this->channel_ = argv[3];
this->hasPassword_ = this->password_.length() != 0;
if (!ValidateCountryCode(this->countryCode_))
{
throw invalid_argument("Invalid country code.");
}
if (this->hotspotName_.length() > 31) throw invalid_argument("Hotspot name is too long.");
if (this->hotspotName_.length() < 1) throw invalid_argument("Hotspot name is too short.");
if (this->password_.size() != 0 && this->password_.size() < 8) throw invalid_argument("Passphrase must be at least 8 characters long.");
if (!ValidateChannel(this->channel_))
{
throw invalid_argument("Channel is not valid.");
}
this->valid_ = true;
}
+4
View File
@@ -35,6 +35,10 @@ namespace pipedal {
bool hasPassword_ = false; bool hasPassword_ = false;
std::string password_; std::string password_;
std::string channel_ = "g6"; std::string channel_ = "g6";
void ParseArguments(const std::vector<std::string> &arguments);
static bool ValidateChannel(const std::string&value);
static bool ValidateCountryCode(const std::string&value);
public: public:
DECLARE_JSON_MAP(WifiConfigSettings); DECLARE_JSON_MAP(WifiConfigSettings);
}; };
+101
View File
@@ -0,0 +1,101 @@
// 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 "pch.h"
#include "WifiDirectConfigSettings.hpp"
#include "WifiConfigSettings.hpp"
#include <stdexcept>
#include <random>
using namespace pipedal;
using namespace std;
JSON_MAP_BEGIN(WifiDirectConfigSettings)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,valid)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,rebootRequired)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,enable)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,hotspotName)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,hasPin)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,pin)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,countryCode)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,channel)
JSON_MAP_REFERENCE(WifiDirectConfigSettings,mdnsName)
JSON_MAP_END()
static std::string MakePin()
{
std::random_device rand_dev;
std::mt19937 gen(rand_dev());
std::uniform_int_distribution<int> dist { 0,9};
std::stringstream s;
for (size_t i = 0; i < 8; ++i)
{
int r = dist(gen);
s << (char)('0' + r);
}
return s.str();
}
static void ValidatePin(const std::string&pin)
{
for (char c : pin)
{
if (c < '0' || c > '9')
{
throw invalid_argument("Pin must consist of digits only.");
}
}
if (pin.length() != 8)
{
throw invalid_argument("Pin must have exctly 8 digits.");
}
}
void WifiDirectConfigSettings::ParseArguments(const std::vector<std::string> &arguments)
{
this->valid_ = false;
if (arguments.size() < 2 || arguments.size() > 4)
{
throw invalid_argument("Incorrect number of arguments supplied");
}
this->enable_ = true;
this->countryCode_ = arguments[0];
this->hotspotName_ = arguments[1];
this->pin_ = arguments.size() >= 3 ? arguments[2] : "";
this->channel_ = arguments.size() >= 4? arguments[3]: "";
size_t maxLength = 32-string("DIRECT-XX-").length();
if (hotspotName_.size() >= maxLength) { throw invalid_argument("Device name is too long."); }
if (hotspotName_.size() < 1) { throw invalid_argument("Device name is too short."); }
if (!WifiConfigSettings::ValidateCountryCode(countryCode_))
{
throw invalid_argument("Invalid country code.");
}
if (pin_.size() == 0)
{
pin_ = MakePin();
}
ValidatePin(pin_); // throws.
if (!WifiConfigSettings::ValidateChannel(channel_))
{
throw invalid_argument("Invalid channel.");
}
}
+47
View File
@@ -0,0 +1,47 @@
/*
* 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 "json.hpp"
namespace pipedal {
class WifiDirectConfigSettings {
public:
bool valid_ = false;
bool rebootRequired_ = false;
bool enable_ = false;
std::string countryCode_ = "US"; // iso 3661
std::string hotspotName_ = "pipedal";
std::string mdnsName_ = "pipedal";
bool hasPin_ = false;
std::string pin_;
std::string channel_ = "g6";
void ParseArguments(const std::vector<std::string> &arguments);
public:
DECLARE_JSON_MAP(WifiDirectConfigSettings);
};
}
+1 -1
View File
@@ -10,4 +10,4 @@ WorkingDirectory=/var/pipedal
[Install] [Install]
WantedBy=multi-user.target WantedBy=multi-user.target
+38
View File
@@ -0,0 +1,38 @@
/*
* 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.
*/
[Unit]
Description=PiPedal P2P Session Manager
After=network.target
[Service]
ExecStart=${COMMAND}
Type=notify
Restart=always
RestartSec=60
WorkingDirectory=/var/pipedal
[Install]
WantedBy=multi-user.target
+48
View File
@@ -0,0 +1,48 @@
${COUNTRY_CODE}
${PIN}
${DEVICE_NAME}
${WIFI_GROUP_FREQUENCY}
${INSTANCE_ID_FILE} /var/pipedal/instance_id
# WiFi regdomain 2-letter country code.
# see: http://www.davros.org/misc/iso3166.txt
country_code=${COUNTRY_CODE}
# keypad/label pin
p2p_pin=${PIN}
# Name that appears when you connect.
p2p_device_name=${DEVICE_NAME}
# DIRECT-XX-postfix (appears on Android p2p Group names)
p2p_ssid_postfix=${DEVICE_NAME}
# Wifi frequency (kHz).
# Should almost always be 2412 (ch1),2437 (ch6), or 2462 (ch11).
wifiGroupFrequency=${WIFI_GROUP_FREQUENCY}
# P2P Device info
p2p_model_name=PiPedal
p2p_model_number=1
p2p_manufacturer="The PiPedal Project"
p2p_serial_number=1
p2p_device_type=1-0050F204-1
# Wi-Fi configuration
wlanInterface=wlan0
p2pInterface=p2p-dev-wlan0
p2p_go_ht40=0
p2p_go_vht=0
p2p_go_he=0
# Ipv4 address for the P2P group interface
p2p_ip_address=172.24.0.2/16
# File containing the a globally-unique id identifying the service on this machine.
# Syntax: 0a6045b0-1753-4104-b3e4-b9713b9cc356
#
service_guid_file=${INSTANCE_ID_FILE}
# GUID identifying the PiPedal service
# (if service_guid_file is not provided.)
service_guid=