1062 lines
32 KiB
Plaintext
1062 lines
32 KiB
Plaintext
#ifndef USE_NETWORKMANAGER
|
|
#include "P2PManager.hpp"
|
|
#include "ss.hpp"
|
|
#include <future>
|
|
#include <chrono>
|
|
#include <poll.h>
|
|
|
|
#include "WpaInterfaces.hpp"
|
|
#include "NMP2pSettings.hpp"
|
|
#include <thread>
|
|
#include <chrono>
|
|
#include <memory>
|
|
|
|
#include "DBusDispatcher.hpp"
|
|
#include "NetworkManagerInterfaces.hpp"
|
|
#include "DBusVariantHelper.hpp"
|
|
#include "SignalHandler.hpp"
|
|
|
|
static const char CONFIG_METHODS[] = "keypad";
|
|
static const char CONFIG_CONNECT_METHOD[] = "display";
|
|
class P2PManagerImpl : public P2PManager
|
|
{
|
|
public:
|
|
P2PManagerImpl() : dispatcher(true) {}
|
|
virtual ~P2PManagerImpl();
|
|
|
|
void Init() override;
|
|
void Run() override;
|
|
void Stop() override;
|
|
bool IsFinished() override;
|
|
void Wait() override;
|
|
bool ReloadRequested() override;
|
|
|
|
private:
|
|
using clock = std::chrono::steady_clock;
|
|
|
|
|
|
void StartListen();
|
|
void SignalStop();
|
|
void SignalHangup();
|
|
void ExecuteOrderlyShutdown();
|
|
void ShutdownStage2();
|
|
void TurnOnWifi();
|
|
void UnInitialize();
|
|
void StartWaitUntilDone();
|
|
void PostWaitUntilDone();
|
|
void MaybeRemoveGroup();
|
|
void CancelMaybeRemoveGroup();
|
|
void MaybeCancel();
|
|
|
|
void StartGroup();
|
|
bool HasPersistentGroup();
|
|
void AddExistingInterfaces();
|
|
void RemoveExistingInterfaces();
|
|
void HandleInterfaceAdded(const std::string &objectPath);
|
|
void HandleInterfaceRemoved(const std::string &objectPath);
|
|
void InitWpaP2PDevice();
|
|
void InitWpaP2PGoDevice();
|
|
void ConnectDevice(const sdbus::ObjectPath &peer_object);
|
|
bool HasClient();
|
|
std::string UpnpServiceName();
|
|
void StartServiceBroadcast();
|
|
void StopServiceBroadcast();
|
|
|
|
std::atomic<bool> reloadRequested{false};
|
|
std::atomic<bool> signalStop{false};
|
|
bool signalStopStarted = false;
|
|
bool shutdownExecuted = false;
|
|
bool waited = false;
|
|
bool serviceBroadcastStarted = false;
|
|
DBusDispatcher dispatcher;
|
|
DBusDispatcher::PostHandle maybeCloseHandle = 0;
|
|
std::mutex postMutex;
|
|
std::atomic<bool> stopping;
|
|
std::vector<std::string> pendingAddedInterfaces;
|
|
|
|
using PostCallback = DBusDispatcher::PostCallback;
|
|
void Post(PostCallback &&fn);
|
|
|
|
WpaSupplicant::ptr wpaSupplicant;
|
|
|
|
WpaInterface::ptr deviceInterface;
|
|
WpaP2PDevice::ptr p2pDevice;
|
|
WpaP2PDevice::ptr p2pGoDevice;
|
|
WpaWPS::ptr wpsDevice;
|
|
sdbus::ObjectPath persistentGroupPath;
|
|
P2pSettings settings{};
|
|
WpaGroup::ptr wpaGroup;
|
|
|
|
std::vector<WpaPeer::ptr> connectedPeers;
|
|
|
|
std::string oldWpasDebugLevel;
|
|
clock::time_point stage2Timeout;
|
|
clock::time_point waitUntilDoneTimeout;
|
|
};
|
|
|
|
P2PManager::ptr P2PManager::Create()
|
|
{
|
|
return std::make_unique<P2PManagerImpl>();
|
|
}
|
|
|
|
P2PManagerImpl::~P2PManagerImpl()
|
|
{
|
|
Stop();
|
|
}
|
|
|
|
static const char hexDigits[] = "0123456789abcdef";
|
|
|
|
static std::string AddressToString(const std::vector<uint8_t> &address)
|
|
{
|
|
std::stringstream ss;
|
|
bool firstTime = true;
|
|
for (auto byte : address)
|
|
{
|
|
if (!firstTime)
|
|
{
|
|
ss << ":";
|
|
}
|
|
firstTime = false;
|
|
ss << hexDigits[(byte >> 4) & 0x0F] << hexDigits[byte & 0x0F];
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
void P2PManagerImpl::RemoveExistingInterfaces()
|
|
{
|
|
try
|
|
{
|
|
while (true)
|
|
{
|
|
if (!this->wpaSupplicant)
|
|
break;
|
|
|
|
auto interfaces = this->wpaSupplicant->Interfaces();
|
|
if (interfaces.size() == 0)
|
|
break;
|
|
|
|
wpaSupplicant->RemoveInterface(interfaces[0]);
|
|
}
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogWarning("P2PManagerImpl", "RemoveExistingInterfaces", e.what());
|
|
}
|
|
}
|
|
void P2PManagerImpl::Init()
|
|
{
|
|
settings.Load();
|
|
|
|
|
|
LogInfo(
|
|
"P2PManager",
|
|
"Init",
|
|
SS(
|
|
"Configuration:"
|
|
<< " " << settings.country()
|
|
<< " " << settings.wlan()
|
|
<< " " << settings.device_name()
|
|
<< " " << settings.web_server_port()
|
|
<< " " << settings.listen_channel()
|
|
<< " " << settings.op_channel()
|
|
));
|
|
SignalHandler::HandleSignal(
|
|
SignalHandler::Interrupt,
|
|
[this]()
|
|
{
|
|
this->SignalStop();
|
|
});
|
|
SignalHandler::HandleSignal(
|
|
SignalHandler::Terminate,
|
|
[this]()
|
|
{
|
|
this->SignalStop();
|
|
});
|
|
SignalHandler::HandleSignal(
|
|
SignalHandler::Hangup,
|
|
[this]()
|
|
{
|
|
this->SignalStop(); // we can't restart, but at lease we can do an orderly shutdown.
|
|
// disable this, because sdbus::dispatcher doesn't seem to be able to handle a restart.
|
|
// this->SignalHangup();
|
|
});
|
|
|
|
#ifdef DEBUG
|
|
dispatcher.Connection().setMethodCallTimeout(500 * 1000 * 1000);
|
|
#endif
|
|
|
|
for (int retry = 0; retry < 4 ; ++retry) // defend against slow starup of wpa_supplicant
|
|
{
|
|
try {
|
|
this->wpaSupplicant = WpaSupplicant::Create(dispatcher.Connection());
|
|
this->wpaSupplicant->DebugLevel();
|
|
if (retry != 0)
|
|
{
|
|
LogInfo("P2PManager","Init","wpa_supplicant DBus connection established.");
|
|
|
|
}
|
|
break;
|
|
} catch (std::exception&e)
|
|
{
|
|
if (retry == 0)
|
|
{
|
|
LogInfo("P2PManager","Init","Waiting for wpa_supplicant DBus connection.");
|
|
}
|
|
this->wpaSupplicant = nullptr;
|
|
std::this_thread::sleep_for(std::chrono::seconds(5));
|
|
}
|
|
}
|
|
if (!this->wpaSupplicant)
|
|
{
|
|
throw std::runtime_error("wpa_supplicant service is not reachable by DBus.");
|
|
}
|
|
|
|
std::string wpaLogLevel = "info";
|
|
switch (GetDBusLogLevel())
|
|
{
|
|
default:
|
|
case DBusLogLevel::Info:
|
|
wpaLogLevel = "info";
|
|
break;
|
|
case DBusLogLevel::Debug:
|
|
wpaLogLevel = "debug";
|
|
break;
|
|
case DBusLogLevel::Warning:
|
|
wpaLogLevel = "warning";
|
|
break;
|
|
case DBusLogLevel::Error:
|
|
wpaLogLevel = "error";
|
|
break;
|
|
case DBusLogLevel::Trace:
|
|
wpaLogLevel = "debug";
|
|
break;
|
|
}
|
|
oldWpasDebugLevel = wpaSupplicant->DebugLevel();
|
|
wpaSupplicant->DebugLevel(wpaLogLevel);
|
|
|
|
if (!oldWpasDebugLevel.empty())
|
|
{
|
|
wpaSupplicant->DebugLevel(oldWpasDebugLevel);
|
|
}
|
|
|
|
TurnOnWifi();
|
|
|
|
this->wpaSupplicant->OnInterfaceAdded.add(
|
|
[this](const sdbus::ObjectPath &path, const std::map<std::string, sdbus::Variant> &properties)
|
|
{
|
|
this->HandleInterfaceAdded(path);
|
|
});
|
|
this->wpaSupplicant->OnInterfaceRemoved.add(
|
|
[this](const sdbus::ObjectPath &path)
|
|
{
|
|
this->HandleInterfaceRemoved(path);
|
|
});
|
|
|
|
RemoveExistingInterfaces();
|
|
if (wpaSupplicant->Interfaces().size() == 0)
|
|
{
|
|
std::map<std::string, sdbus::Variant> createArgs;
|
|
createArgs["Ifname"] = settings.wlan();
|
|
createArgs["Driver"] = settings.driver();
|
|
createArgs["ConfigFile"] = settings.wpas_config_file();
|
|
wpaSupplicant->CreateInterface(createArgs);
|
|
}
|
|
}
|
|
|
|
void P2PManagerImpl::AddExistingInterfaces()
|
|
{
|
|
if (!this->p2pDevice)
|
|
{
|
|
for (const auto &objectPath : this->wpaSupplicant->Interfaces())
|
|
{
|
|
HandleInterfaceAdded(objectPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
void P2PManagerImpl::HandleInterfaceRemoved(const std::string &objectPath)
|
|
{
|
|
bool deleted = false;
|
|
|
|
// removal from pending list can be done in a handler.
|
|
for (auto i = this->pendingAddedInterfaces.begin(); i != pendingAddedInterfaces.end(); ++i)
|
|
{
|
|
if (*i == objectPath)
|
|
{
|
|
pendingAddedInterfaces.erase(i);
|
|
return;
|
|
}
|
|
}
|
|
// removal of existing objects needs to be posted.
|
|
Post([this, objectPath]()
|
|
{
|
|
if (this->p2pDevice && this->p2pDevice->getObjectPath() == objectPath)
|
|
{
|
|
this->p2pDevice = nullptr;
|
|
this->deviceInterface = nullptr;
|
|
this->wpsDevice = nullptr;
|
|
}
|
|
if (this->p2pGoDevice && this->p2pGoDevice->getObjectPath() == objectPath)
|
|
{
|
|
this->wpaGroup = nullptr;
|
|
this->p2pGoDevice = nullptr;
|
|
StartListen();
|
|
} });
|
|
}
|
|
|
|
void P2PManagerImpl::HandleInterfaceAdded(const std::string &objectPath)
|
|
{
|
|
|
|
// Create the objects in the callback, but defer processing.
|
|
// Guard against the interface being synchronously removed before the async post handler excecutes./
|
|
this->pendingAddedInterfaces.push_back(objectPath.c_str());
|
|
{
|
|
std::string msg = SS("Device added: " << objectPath.c_str());
|
|
LogDebug("P2PManagerImpl", "HandleInterfaceAdded", msg);
|
|
}
|
|
|
|
Post([this,
|
|
objectPath]() mutable
|
|
{
|
|
// check to see whether the new interface was removed from pending list.
|
|
auto found = std::find(pendingAddedInterfaces.begin(),pendingAddedInterfaces.end(),objectPath);
|
|
if (found != pendingAddedInterfaces.end())
|
|
{
|
|
pendingAddedInterfaces.erase(found);
|
|
try {
|
|
auto interface = WpaInterface::Create(dispatcher.Connection(), objectPath);
|
|
auto p2pDevice = WpaP2PDevice::Create(dispatcher.Connection(), objectPath);
|
|
|
|
LogDebug(objectPath,"HandleInterfaceAdded",SS("Role: " << p2pDevice->Role()));
|
|
|
|
if (interface)
|
|
{
|
|
if (p2pDevice->Role() == "device" && ((!this->p2pDevice) || this->p2pDevice->getObjectPath() != objectPath))
|
|
{
|
|
LogDebug("P2PManagerImpl", "HandleInterfaceAdded", "P2P device found.");
|
|
this->deviceInterface = std::move(interface);
|
|
this->p2pDevice = std::move(p2pDevice);
|
|
this->wpsDevice = WpaWPS::Create(dispatcher.Connection(), objectPath);
|
|
InitWpaP2PDevice();
|
|
}
|
|
else if (p2pDevice->Role() == "GO" && ((!this->p2pGoDevice) || this->p2pGoDevice->getObjectPath() != objectPath))
|
|
{
|
|
LogDebug("P2PManagerImpl", "HandleInterfaceAdded", "P2P GO group interface found.");
|
|
|
|
this->p2pGoDevice = std::move(p2pDevice);
|
|
InitWpaP2PGoDevice();
|
|
}
|
|
}
|
|
} catch (const std::exception&e)
|
|
{
|
|
LogError("P2PManagerImpl","HandleInterfaceAdded",SS("Failed to add " << objectPath.c_str() << " (" << e.what() << ")"));
|
|
}
|
|
} });
|
|
}
|
|
|
|
|
|
void P2PManagerImpl::InitWpaP2PGoDevice()
|
|
{
|
|
if (IsDBusLoggingEnabled(DBusLogLevel::Trace))
|
|
{
|
|
auto deviceConfig = p2pGoDevice->P2PDeviceConfig();
|
|
std::stringstream s;
|
|
s << deviceConfig;
|
|
LogTrace("P2PManagerImpl", "InitWpaP2PGoDevice", SS("GO Device config: " << s.str()));
|
|
}
|
|
LogTrace("P2PManagerImpl", "InitWpaP2PGoDevice", SS("ConfigMethods: " << p2pGoDevice->ConfigMethods()));
|
|
p2pGoDevice->OnStaAuthorized.add(
|
|
[this](const std::string&name) {
|
|
LogInfo(
|
|
p2pGoDevice->getObjectPath(),
|
|
"OnStationAuthorized",
|
|
SS("Station authorized: " << name));
|
|
});
|
|
p2pGoDevice->OnStaDeauthorized.add(
|
|
[this](const std::string&name) {
|
|
LogInfo(
|
|
p2pGoDevice->getObjectPath(),
|
|
"OnStationDeauthorized",
|
|
SS("Station deauthorized: " << name));
|
|
});
|
|
|
|
p2pGoDevice->OnWpsFailed.add(
|
|
[this](const std::string &name, const std::map<std::string, sdbus::Variant> &args)
|
|
{
|
|
{
|
|
std::stringstream ss;
|
|
ss << name << " " << args << " ";
|
|
LogError("P2PManagerImp", "OnWpsFailed", ss.str());
|
|
}
|
|
try
|
|
{
|
|
this->p2pGoDevice->Cancel();
|
|
}
|
|
catch (const std::exception &)
|
|
{
|
|
}
|
|
MaybeRemoveGroup();
|
|
});
|
|
p2pGoDevice->OnGONegotiationFailure.add(
|
|
[this](const std::map<std::string, sdbus::Variant> &info)
|
|
{
|
|
std::stringstream ss;
|
|
ss << info;
|
|
LogError(p2pGoDevice->getObjectPath().c_str(),"OnGoNegotiationFailure",ss.str()); });
|
|
p2pGoDevice->OnGroupFormationFailure.add(
|
|
[this](const std::string &message)
|
|
{
|
|
std::stringstream ss;
|
|
ss << message;
|
|
LogError(p2pGoDevice->getObjectPath().c_str(), "OnGroupFormationFailure", ss.str());
|
|
MaybeRemoveGroup();
|
|
});
|
|
|
|
p2pGoDevice->OnStationAdded.add(
|
|
[this](const sdbus::ObjectPath &path, const std::map<std::string, sdbus::Variant> &properties) {
|
|
|
|
});
|
|
p2pGoDevice->OnStationRemoved.add(
|
|
[this](const sdbus::ObjectPath &)
|
|
{
|
|
MaybeRemoveGroup();
|
|
});
|
|
LogTrace("P2PManagerImpl", "InitWpaP2PGoDevice", SS("GoCountry: " << p2pGoDevice->Country()));
|
|
|
|
p2pGoDevice->OnProvisionDiscoveryRequestDisplayPin.add(
|
|
[this](const sdbus::ObjectPath &peer_object, const std::string &pin)
|
|
{
|
|
LogTrace("P2PManagerImpl", "InitWpaP2PGoDevice",
|
|
SS("GO OnProvisionDiscoveryRequestDisplayPin - " << peer_object.c_str() << " pin: " << pin));
|
|
});
|
|
}
|
|
|
|
bool P2PManagerImpl::HasClient()
|
|
{
|
|
return this->p2pDevice->Stations().size() != 0;
|
|
}
|
|
void P2PManagerImpl::ConnectDevice(const sdbus::ObjectPath &peer_object)
|
|
{
|
|
|
|
#if 1
|
|
try
|
|
{
|
|
int retry = 0;
|
|
while (true)
|
|
{
|
|
try
|
|
{
|
|
std::map<std::string, sdbus::Variant> connectArgs;
|
|
connectArgs["peer"] = peer_object;
|
|
connectArgs["persistent"] = true;
|
|
// connecconnectArgs["frequency"] = this->settings.frequency();
|
|
connectArgs["go_intent"] = (int32_t)this->settings.p2p_go_intent();
|
|
connectArgs["wps_method"] = CONFIG_CONNECT_METHOD;
|
|
connectArgs["pin"] = this->settings.pin();
|
|
connectArgs["authorize_only"] = false;
|
|
connectArgs["join"] = false; //HasClient();
|
|
|
|
this->p2pDevice->Connect(connectArgs);
|
|
break;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
try
|
|
{
|
|
p2pDevice->Cancel();
|
|
}
|
|
catch (const std::exception &)
|
|
{
|
|
}
|
|
if (++retry == 2)
|
|
{
|
|
MaybeRemoveGroup();
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogError("P2pManager", "Connect", e.what());
|
|
return;
|
|
}
|
|
#else
|
|
std::map<std::string, sdbus::Variant> inviteArgs;
|
|
inviteArgs["peer"] = peer_object;
|
|
inviteArgs["persistent_group_object"] = this->persistentGroupPath;
|
|
p2pDevice->Invite(inviteArgs);
|
|
#endif
|
|
}
|
|
|
|
static bool ValidateSetting(const std::map<std::string, sdbus::Variant> &properties, const std::string &key, int32_t value)
|
|
{
|
|
auto iter = properties.find(key);
|
|
if (iter == properties.end())
|
|
return false;
|
|
if (!iter->second.containsValueOfType<int32_t>())
|
|
return false;
|
|
return iter->second.get<int32_t>() == value;
|
|
}
|
|
static bool ValidateSetting(const std::map<std::string, sdbus::Variant> &properties, const std::string &key, const std::string &value)
|
|
{
|
|
auto iter = properties.find(key);
|
|
if (iter == properties.end())
|
|
return false;
|
|
if (!iter->second.containsValueOfType<std::string>())
|
|
return false;
|
|
return iter->second.get<std::string>() == value;
|
|
}
|
|
bool P2PManagerImpl::HasPersistentGroup()
|
|
{
|
|
auto persistentGroups = p2pDevice->PersistentGroups();
|
|
return persistentGroups.size() != 0;
|
|
}
|
|
|
|
void P2PManagerImpl::InitWpaP2PDevice()
|
|
{
|
|
p2pDevice->Country(settings.country());
|
|
|
|
std::map<std::string, sdbus::Variant> deviceConfig;
|
|
deviceConfig["DeviceName"] = SS(settings.device_name());
|
|
deviceConfig["PrimaryDeviceType"] = std::vector<uint8_t>{0x00, 0x01, 0x00, 0x50, 0xF2, 0x04, 0x00, 0x01};
|
|
deviceConfig["GOIntent"] = (uint32_t)15;
|
|
deviceConfig["ListenChannel"] = (uint32_t)settings.listen_channel();
|
|
deviceConfig["OperChannel"] = (uint32_t)settings.op_channel();
|
|
|
|
// deviceConfig["ListenRegClass"] = (uint32_t)81;
|
|
// deviceConfig["ListenChannel"] = (uint32_t)settings.listen_channel();
|
|
// deviceConfig["OperRegClass"] = (uint32_t)81;
|
|
// deviceConfig["OperChannel"] = (uint32_t)settings.op_channel(); // ch 1
|
|
|
|
deviceConfig["SsidPostfix"] = SS("-" << settings.device_name());
|
|
deviceConfig["IpAddrGo"] = std::vector<uint8_t>{172, 23, 0, 2};
|
|
deviceConfig["IpAddrMask"] = std::vector<uint8_t>{255, 255, 0, 0};
|
|
deviceConfig["IpAddrStart"] = std::vector<uint8_t>{172, 23, 0, 4};
|
|
deviceConfig["IpAddrEnd"] = std::vector<uint8_t>{172, 23, 0, 9};
|
|
deviceConfig["IntraBss"] = false;
|
|
deviceConfig["PersistentReconnect"] = true;
|
|
// per_sta_psk
|
|
|
|
p2pDevice->P2PDeviceConfig(deviceConfig);
|
|
{
|
|
auto deviceConfig = p2pDevice->P2PDeviceConfig();
|
|
std::stringstream s;
|
|
s << deviceConfig;
|
|
LogTrace("P2PManager", "InitWpaP2PDevice", SS("P2PDeviceConfig: " << s.str()));
|
|
}
|
|
|
|
LogDebug("P2PManager","InitWpaP2PDevice",SS("ConfigMethods" << p2pDevice->ConfigMethods()));
|
|
p2pDevice->ConfigMethods(CONFIG_METHODS);
|
|
// deviceInterface->P2pGoHt40(settings.p2p_go_ht40() ? "1" : "0");
|
|
|
|
std::string groupName = p2pDevice->Group().c_str();
|
|
|
|
auto t = wpsDevice->DeviceType();
|
|
|
|
wpsDevice->DeviceName(settings.device_name());
|
|
wpsDevice->Manufacturer(settings.manufacturer());
|
|
wpsDevice->ModelName(settings.model_name());
|
|
wpsDevice->ModelNumber(settings.model_number());
|
|
wpsDevice->SerialNumber(settings.serial_number());
|
|
wpsDevice->DeviceType(settings.device_type());
|
|
|
|
p2pDevice->OnGroupStarted.add(
|
|
[this](const std::map<std::string, sdbus::Variant> &args)
|
|
{
|
|
try
|
|
{
|
|
LogDebug("P2PManager", "OnGroupStarted", "GroupStarted");
|
|
|
|
const sdbus::Variant &vGroup = args.at("group_object");
|
|
sdbus::ObjectPath group = vGroup;
|
|
if (!group.empty())
|
|
{
|
|
wpaGroup = WpaGroup::Create(dispatcher.Connection(), group);
|
|
wpaGroup->OnPeerJoined.add(
|
|
[this](const sdbus::ObjectPath &path)
|
|
{
|
|
Post(
|
|
[this, path]()
|
|
{
|
|
try
|
|
{
|
|
LogDebug("P2PManager", "OnPeerJoined", path.c_str());
|
|
this->connectedPeers.push_back(WpaPeer::Create(dispatcher.Connection(), path));
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogError("P2PManager", "OnPeerJoined", e.what());
|
|
}
|
|
});
|
|
});
|
|
wpaGroup->OnPeerDisconnected.add(
|
|
[this](const sdbus::ObjectPath &path)
|
|
{
|
|
Post(
|
|
[this, path]()
|
|
{
|
|
LogDebug("P2PManager","OnPeerDisconnected",path.c_str());
|
|
this->connectedPeers.push_back(WpaPeer::Create(dispatcher.Connection(),path));
|
|
for (auto iter = this->connectedPeers.begin(); iter != this->connectedPeers.end(); iter++)
|
|
{
|
|
if ((*iter)->getObjectPath() == path)
|
|
{
|
|
this->connectedPeers.erase(iter);
|
|
break;
|
|
}
|
|
} });
|
|
});
|
|
}
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogError("InitWpaP2PDevice", "OnGroupAdded", e.what());
|
|
}
|
|
});
|
|
p2pDevice->OnGONegotiationFailure.add(
|
|
[this](const std::map<std::string, sdbus::Variant> &info)
|
|
{
|
|
std::stringstream ss;
|
|
ss << info;
|
|
LogError(p2pDevice->getObjectPath().c_str(), "OnGoNegotiationFailure", ss.str());
|
|
});
|
|
p2pDevice->OnGroupFormationFailure.add(
|
|
[this](const std::string &reason)
|
|
{
|
|
LogError(p2pDevice->getObjectPath().c_str(), "OnGroupFormationFailure", reason);
|
|
});
|
|
|
|
p2pDevice->OnGroupFinished.add([this](const std::map<std::string, sdbus::Variant> &properties)
|
|
{
|
|
LogInfo("P2PManager","OnGroupFinished","");
|
|
wpaGroup = nullptr; });
|
|
|
|
p2pDevice->OnGroupFormationFailure.add(
|
|
[this](const std::string &reason)
|
|
{
|
|
Post([this]()
|
|
{
|
|
if (this->p2pDevice)
|
|
{
|
|
try {
|
|
StartListen();
|
|
} catch (const std::exception&e)
|
|
{
|
|
LogError("P2PManager","OnGroupFormationFailure", SS("Listen failed: " << e.what()));
|
|
}
|
|
} });
|
|
});
|
|
p2pDevice->OnGONegotiationFailure.add(
|
|
[this](const std::map<std::string, sdbus::Variant> &info)
|
|
{
|
|
Post([this]()
|
|
{
|
|
if (this->p2pDevice)
|
|
{
|
|
try {
|
|
StartListen();
|
|
} catch (const std::exception&e)
|
|
{
|
|
LogError("P2PManager","OnGONegotiationFailure", SS("Listen failed: " << e.what()));
|
|
}
|
|
} });
|
|
});
|
|
|
|
p2pDevice->OnProvisionDiscoveryEnterPin.add(
|
|
[this](const sdbus::ObjectPath &peer_object)
|
|
{
|
|
// old android device picks wrong method. pretend they asked for OnProvisionDiscoveryRequestDisplayPin instead
|
|
LogDebug(p2pDevice->getObjectPath(), "OnProvisionDiscoveryEnterPin",
|
|
peer_object.c_str());
|
|
|
|
Post(
|
|
[this, peer_object]()
|
|
{
|
|
if (!this->p2pDevice)
|
|
return;
|
|
|
|
ConnectDevice(peer_object);
|
|
});
|
|
});
|
|
|
|
p2pDevice->OnProvisionDiscoveryRequestDisplayPin.add(
|
|
[this](const sdbus::ObjectPath &peer_object, const std::string &pin)
|
|
{
|
|
LogDebug(p2pDevice->getObjectPath(), "OnProvisionDiscoveryRequestDisplayPin",
|
|
SS(peer_object.c_str()));
|
|
|
|
Post(
|
|
[this, peer_object]()
|
|
{
|
|
if (!this->p2pDevice)
|
|
return;
|
|
|
|
ConnectDevice(peer_object);
|
|
});
|
|
});
|
|
|
|
if (!HasPersistentGroup() || settings.auth_changed())
|
|
{
|
|
if (settings.auth_changed())
|
|
{
|
|
LogInfo("P2PManagerImpl","InitWpaP2PDevice","Auth has changed. Resetting persistent groups");
|
|
}
|
|
p2pDevice->RemoveAllPersistentGroups();
|
|
|
|
std::map<std::string, sdbus::Variant> persistentGroupAddParams;
|
|
persistentGroupAddParams["bssid"] = settings.bssid();
|
|
persistentGroupAddParams["ssid"] = settings.device_name();
|
|
persistentGroupAddParams["psk"] = settings.pin();
|
|
persistentGroupAddParams["mode"] = (uint32_t)3; //"3";
|
|
sdbus::ObjectPath persistentGroupPath = p2pDevice->AddPersistentGroup(persistentGroupAddParams);
|
|
this->persistentGroupPath = persistentGroupPath;
|
|
if (settings.auth_changed())
|
|
{
|
|
settings.auth_changed(false);
|
|
settings.Save();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
this->persistentGroupPath = p2pDevice->PersistentGroups()[0];
|
|
}
|
|
|
|
StartListen();
|
|
StartServiceBroadcast();
|
|
|
|
LogInfo(this->p2pDevice->getObjectPath(), "InitWpaP2PDevice", "Listening");
|
|
}
|
|
|
|
void P2PManagerImpl::Post(PostCallback &&fn)
|
|
{
|
|
dispatcher.Post(std::move(fn));
|
|
}
|
|
|
|
void P2PManagerImpl::Run()
|
|
{
|
|
|
|
dispatcher.Run();
|
|
}
|
|
|
|
void P2PManagerImpl::ExecuteOrderlyShutdown()
|
|
{
|
|
dispatcher.Post(
|
|
[this]()
|
|
{
|
|
if (this->shutdownExecuted)
|
|
return;
|
|
this->shutdownExecuted = true;
|
|
|
|
StopServiceBroadcast();
|
|
|
|
CancelMaybeRemoveGroup();
|
|
|
|
if (this->p2pDevice)
|
|
{
|
|
// no parameters cancels Extended listen.
|
|
std::map<std::string, sdbus::Variant> listenArgs;
|
|
this->p2pDevice->ExtendedListen(listenArgs);
|
|
}
|
|
|
|
|
|
if (this->p2pGoDevice)
|
|
{
|
|
try
|
|
{
|
|
this->p2pGoDevice->Disconnect();
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogWarning("P2PManagerImpl", "Stop", SS("Can't disconnect " << this->p2pGoDevice->Ifname()));
|
|
}
|
|
}
|
|
stage2Timeout = clock::now() + std::chrono::duration_cast<clock::duration>(std::chrono::milliseconds(1500));
|
|
ShutdownStage2();
|
|
});
|
|
}
|
|
|
|
void P2PManagerImpl::ShutdownStage2()
|
|
{
|
|
if (clock::now() > stage2Timeout)
|
|
{
|
|
LogError("P2PManagerImpl","ShutdownStage2","Stage2 shutdown timeout exipred.");
|
|
} else {
|
|
// wait until the Disconnect has completed.
|
|
if (this->p2pGoDevice)
|
|
{
|
|
dispatcher.PostDelayed(std::chrono::milliseconds(50),
|
|
[this]() {
|
|
ShutdownStage2();
|
|
});
|
|
return;
|
|
}
|
|
}
|
|
LogDebug("P2PManagerImpl","ShutdownStage2","Sutdown Stage2 complete.");
|
|
if (this->p2pDevice)
|
|
{
|
|
this->wpaSupplicant->RemoveInterface(p2pDevice->getObjectPath());
|
|
}
|
|
StartWaitUntilDone();
|
|
}
|
|
void P2PManagerImpl::Stop()
|
|
{
|
|
SignalStop();
|
|
Wait();
|
|
}
|
|
bool P2PManagerImpl::IsFinished()
|
|
{
|
|
return dispatcher.IsFinished();
|
|
}
|
|
|
|
bool P2PManagerImpl::ReloadRequested()
|
|
{
|
|
return this->reloadRequested;
|
|
}
|
|
|
|
void P2PManagerImpl::Wait()
|
|
{
|
|
if (!waited)
|
|
{
|
|
waited = true;
|
|
dispatcher.Wait();
|
|
LogInfo("P2PManager", "Stop", "Shutdown complete.");
|
|
}
|
|
}
|
|
|
|
void P2PManagerImpl::StartWaitUntilDone()
|
|
{
|
|
this->waitUntilDoneTimeout = clock::now() + std::chrono::duration_cast<clock::duration>(std::chrono::milliseconds(1000));
|
|
|
|
PostWaitUntilDone();
|
|
}
|
|
void P2PManagerImpl::PostWaitUntilDone()
|
|
{
|
|
if (clock::now() > this->waitUntilDoneTimeout)
|
|
{
|
|
LogError("P2PManager","PostWaitUntilDone", "WaitUntilDone timeout expired.");
|
|
} else {
|
|
auto interfaces = this->wpaSupplicant->Interfaces();
|
|
if (interfaces.size() > 0)
|
|
{
|
|
dispatcher.PostDelayed(
|
|
std::chrono::milliseconds(100),
|
|
[this]()
|
|
{
|
|
PostWaitUntilDone();
|
|
});
|
|
}
|
|
|
|
}
|
|
// the dispatcher won't exit while there's a pending post.
|
|
dispatcher.PostDelayed(
|
|
std::chrono::milliseconds(100),
|
|
[this]()
|
|
{
|
|
UnInitialize();
|
|
});
|
|
}
|
|
void P2PManagerImpl::StartGroup()
|
|
{
|
|
std::map<std::string, sdbus::Variant> args;
|
|
args["persistent_group_object"] = this->persistentGroupPath;
|
|
this->p2pDevice->GroupAdd(args);
|
|
}
|
|
|
|
void P2PManagerImpl::CancelMaybeRemoveGroup()
|
|
{
|
|
if (maybeCloseHandle)
|
|
{
|
|
dispatcher.CancelPost(maybeCloseHandle);
|
|
maybeCloseHandle = 0;
|
|
}
|
|
}
|
|
void P2PManagerImpl::MaybeRemoveGroup()
|
|
{
|
|
CancelMaybeRemoveGroup();
|
|
if (dispatcher.IsStopping())
|
|
{
|
|
return;
|
|
}
|
|
maybeCloseHandle = dispatcher.PostDelayed(
|
|
std::chrono::milliseconds(100),
|
|
[this]()
|
|
{
|
|
maybeCloseHandle = 0;
|
|
|
|
if (this->p2pDevice->Stations().size() == 0)
|
|
{
|
|
if (this->p2pGoDevice)
|
|
{
|
|
try
|
|
{
|
|
p2pGoDevice->Disconnect();
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogTrace(p2pGoDevice->getObjectPath(), "MaybeRemoveGroup", "Failed to disconnect.");
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
std::vector<std::string> s_idleStates{
|
|
"disconnected",
|
|
"inactive",
|
|
"interface_disabled",
|
|
"scanning",
|
|
//"authenticating",
|
|
//"associating",
|
|
//"associated",
|
|
//"4way_handshake",
|
|
//"group_handshake",
|
|
"completed",
|
|
//"unknown",
|
|
};
|
|
|
|
void P2PManagerImpl::MaybeCancel()
|
|
{
|
|
std::string state = this->p2pDevice->State();
|
|
for (const auto &idleState : s_idleStates)
|
|
{
|
|
if (idleState == state)
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
try
|
|
{
|
|
this->p2pDevice->Cancel();
|
|
}
|
|
catch (const std::exception &)
|
|
{
|
|
}
|
|
}
|
|
|
|
std::string P2PManagerImpl::UpnpServiceName()
|
|
{
|
|
return SS("uuid:" << settings.service_uuid() << "::urn:schemas-twoplay-com:service:PiPedal:1"
|
|
<< "::port:" << settings.web_server_port());
|
|
}
|
|
|
|
void P2PManagerImpl::StartServiceBroadcast()
|
|
{
|
|
// Upnp service advertising allows the android client to automatically discover pipedal Wifi P2P devices.
|
|
try
|
|
{
|
|
std::map<std::string, sdbus::Variant> args;
|
|
args["service_type"] = "upnp";
|
|
args["version"] = (int32_t)10;
|
|
args["service"] = UpnpServiceName();
|
|
std::vector<std::uint8_t> response;
|
|
args["response"] = response;
|
|
p2pDevice->AddService(
|
|
args);
|
|
serviceBroadcastStarted = true;
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogError(
|
|
p2pDevice->getObjectPath(),
|
|
"StartServiceBroadcast",
|
|
SS("Failed to start Wi-Fi service broadcast. " << e.what()));
|
|
}
|
|
}
|
|
void P2PManagerImpl::StopServiceBroadcast()
|
|
{
|
|
try
|
|
{
|
|
if (serviceBroadcastStarted)
|
|
{
|
|
serviceBroadcastStarted = false;
|
|
|
|
std::map<std::string, sdbus::Variant> args;
|
|
args["service_type"] = "upnp";
|
|
args["version"] = (int32_t)10;
|
|
args["service"] = UpnpServiceName();
|
|
p2pDevice->DeleteService(args);
|
|
}
|
|
}
|
|
catch (std::exception &e)
|
|
{
|
|
LogDebug("P2PManager", "StopServiceBroadcast", e.what());
|
|
}
|
|
}
|
|
|
|
void P2PManagerImpl::UnInitialize()
|
|
{
|
|
// should all have been released at this point,
|
|
// but still best to check.
|
|
this->connectedPeers.clear();
|
|
this->wpaGroup = nullptr;
|
|
this->p2pGoDevice = nullptr;
|
|
this->p2pDevice = nullptr;
|
|
this->deviceInterface = nullptr;
|
|
this->wpsDevice = nullptr;
|
|
|
|
wpaSupplicant->DebugLevel(oldWpasDebugLevel);
|
|
|
|
wpaSupplicant = nullptr; // completely shut down after this.
|
|
}
|
|
|
|
void P2PManagerImpl::TurnOnWifi()
|
|
{
|
|
bool wirelessEnabled;
|
|
auto networkManager = NetworkManager::Create(dispatcher);
|
|
|
|
try
|
|
{
|
|
wirelessEnabled = networkManager->WirelessEnabled();
|
|
}
|
|
catch (const std::exception &e)
|
|
{
|
|
LogDebug("P2PManager", "TurnOnWifi", "NetworkManager is not available.");
|
|
return;
|
|
}
|
|
|
|
if (!wirelessEnabled)
|
|
{
|
|
networkManager->WirelessEnabled(true);
|
|
}
|
|
}
|
|
|
|
void P2PManagerImpl::SignalHangup()
|
|
{
|
|
if (signalStop)
|
|
return;
|
|
//reloadRequested = true;
|
|
|
|
dispatcher.SignalStop([this]()
|
|
{
|
|
dispatcher.Post([this]()
|
|
{
|
|
LogInfo("P2PManager","SignalHangup","Reload requested.");
|
|
ExecuteOrderlyShutdown();
|
|
});
|
|
ExecuteOrderlyShutdown(); });
|
|
signalStop = true;
|
|
}
|
|
void P2PManagerImpl::SignalStop()
|
|
{
|
|
if (signalStop)
|
|
return;
|
|
dispatcher.SignalStop([this]()
|
|
{ ExecuteOrderlyShutdown(); });
|
|
signalStop = true;
|
|
}
|
|
void P2PManagerImpl::StartListen()
|
|
{
|
|
if (!dispatcher.IsStopping())
|
|
{
|
|
p2pDevice->Listen((int32_t)5);
|
|
|
|
std::map<std::string, sdbus::Variant> listenArgs;
|
|
listenArgs["interval"] = (int32_t)3000; // listen for...
|
|
listenArgs["period"] = (int32_t)250; // check every...
|
|
this->p2pDevice->ExtendedListen(listenArgs);
|
|
}
|
|
|
|
|
|
}
|
|
#endif |