Check if the virtual camera is in use before modifying the devices. Fixed clients detection in Windows.

This commit is contained in:
Gonzalo Exequiel Pedone 2021-06-16 16:49:23 -03:00
parent 03a21de594
commit e3bedce3ce
No known key found for this signature in database
GPG key ID: B8B09E63E9B85BAF
13 changed files with 288 additions and 257 deletions

View file

@ -93,8 +93,11 @@ namespace AkVCam {
size_t column);
std::vector<size_t> maxColumnsLength(const StringVector &table,
size_t width);
void drawTableHLine(const std::vector<size_t> &columnsLength);
void drawTable(const StringVector &table, size_t width);
void drawTableHLine(const std::vector<size_t> &columnsLength,
bool toStdErr=false);
void drawTable(const StringVector &table,
size_t width,
bool toStdErr=false);
CmdParserCommand *parserCommand(const std::string &command);
const CmdParserFlags *parserFlag(const std::vector<CmdParserFlags> &cmdFlags,
const std::string &flag);
@ -422,6 +425,31 @@ int AkVCam::CmdParser::parse(int argc, char **argv)
}
}
if (this->d->m_ipcBridge.isBusyFor(command->command)) {
std::cerr << "This operation is not permitted." << std::endl;
std::cerr << "The virtual camera is in use. Stop or close the virtual "
<< "camera clients and try again." << std::endl;
std::cerr << std::endl;
auto clients = this->d->m_ipcBridge.clientsPids();
if (!clients.empty()) {
std::vector<std::string> table {
"Pid",
"Executable"
};
auto columns = table.size();
for (auto &pid: clients) {
table.push_back(std::to_string(pid));
table.push_back(this->d->m_ipcBridge.clientExe(pid));
}
this->d->drawTable(table, columns, true);
}
return -1;
}
if (this->d->m_ipcBridge.needsRoot(command->command)
|| (command->command == "hack"
&& arguments.size() >= 2
@ -619,38 +647,49 @@ std::vector<size_t> AkVCam::CmdParserPrivate::maxColumnsLength(const AkVCam::Str
return lengths;
}
void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector<size_t> &columnsLength)
void AkVCam::CmdParserPrivate::drawTableHLine(const std::vector<size_t> &columnsLength,
bool toStdErr)
{
std::cout << '+';
std::ostream *out = &std::cout;
if (toStdErr)
out = &std::cerr;
*out << '+';
for (auto &len: columnsLength)
std::cout << std::string("-") * (len + 2) << '+';
*out << std::string("-") * (len + 2) << '+';
std::cout << std::endl;
*out << std::endl;
}
void AkVCam::CmdParserPrivate::drawTable(const AkVCam::StringVector &table,
size_t width)
size_t width,
bool toStdErr)
{
size_t height = table.size() / width;
auto columnsLength = this->maxColumnsLength(table, width);
this->drawTableHLine(columnsLength);
this->drawTableHLine(columnsLength, toStdErr);
std::ostream *out = &std::cout;
if (toStdErr)
out = &std::cerr;
for (size_t y = 0; y < height; y++) {
std::cout << "|";
*out << "|";
for (size_t x = 0; x < width; x++) {
auto &element = table[x + y * width];
std::cout << " " << fill(element, columnsLength[x]) << " |";
*out << " " << fill(element, columnsLength[x]) << " |";
}
std::cout << std::endl;
*out << std::endl;
if (y == 0 && height > 1)
this->drawTableHLine(columnsLength);
this->drawTableHLine(columnsLength, toStdErr);
}
this->drawTableHLine(columnsLength);
this->drawTableHLine(columnsLength, toStdErr);
}
AkVCam::CmdParserCommand *AkVCam::CmdParserPrivate::parserCommand(const std::string &command)
@ -2026,7 +2065,8 @@ int AkVCam::CmdParserPrivate::hacks(const AkVCam::StringMap &flags,
"and advanced users only." << std::endl;
std::cout << std::endl;
std::cout << "WARNING: Unsafe hacks can brick your system, make it "
"unstable, or expose it to a serious security risk. You "
"unstable, or expose it to a serious security risk, "
"remember to make a backup of your files and system. You "
"are solely responsible of whatever happens for using "
"them. You been warned, don't come and cry later."
<< std::endl;
@ -2105,7 +2145,8 @@ int AkVCam::CmdParserPrivate::hack(const AkVCam::StringMap &flags,
if (!accepted && !this->m_parseable) {
std::cout << "WARNING: Applying this hack can brick your system, make "
"it unstable, or expose it to a serious security risk. "
"it unstable, or expose it to a serious security risk, "
"remember to make a backup of your files and system. "
"Agreeing to continue, you accept the full responsability "
"of whatever happens from now on."
<< std::endl;

View file

@ -91,7 +91,7 @@ namespace AkVCam
const std::map<std::string, int> &controls)
public:
IpcBridge();
IpcBridge(bool isVCam=false);
~IpcBridge();
/* Server & Client */
@ -103,7 +103,7 @@ namespace AkVCam
std::string logPath(const std::string &logName={}) const;
// Register the peer to the global server.
bool registerPeer();
bool registerPeer(bool isVCam=false);
// Unregister the peer to the global server.
void unregisterPeer();
@ -172,6 +172,8 @@ namespace AkVCam
// Decrement the count of device listeners
bool removeListener(const std::string &deviceId);
bool isBusyFor(const std::string &operation) const;
bool needsRoot(const std::string &operation) const;
int sudo(int argc, char **argv) const;

View file

@ -133,18 +133,22 @@
std::vector<CallbackName##Callback> m_##CallbackName##Callback;
#define AKVCAM_EMIT(owner, CallbackName, ...) \
{ \
AkLogDebug() << "Emiting: " << #CallbackName << std::endl; \
\
for (auto &callback: owner->m_##CallbackName##Callback) \
if (callback.second) \
callback.second(callback.first, __VA_ARGS__); \
}
#define AKVCAM_EMIT_NOARGS(owner, CallbackName) \
{ \
AkLogDebug() << "Emiting: " << #CallbackName << std::endl; \
\
for (auto &callback: owner->m_##CallbackName##Callback) \
if (callback.second) \
callback.second(callback.first); \
}
namespace AkVCam
{

View file

@ -51,7 +51,7 @@ namespace AkVCam
class AssistantPrivate
{
public:
AssistantPeers m_clients;
AssistantPeers m_peers;
DeviceConfigs m_deviceConfigs;
std::map<int64_t, XpcMessage> m_messageHandlers;
CFRunLoopTimerRef m_timer {nullptr};
@ -203,10 +203,10 @@ void AkVCam::AssistantPrivate::peerDied()
AkLogFunction();
std::vector<std::string> deadPeers;
for (auto &client: this->m_clients) {
for (auto &peer: this->m_peers) {
auto dictionary = xpc_dictionary_create(nullptr, nullptr, 0);
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ISALIVE);
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
auto reply = xpc_connection_send_message_with_reply_sync(peer.second,
dictionary);
xpc_release(dictionary);
auto replyType = xpc_get_type(reply);
@ -218,7 +218,7 @@ void AkVCam::AssistantPrivate::peerDied()
xpc_release(reply);
if (!alive)
deadPeers.push_back(client.first);
deadPeers.push_back(peer.first);
}
for (auto &peer: deadPeers)
@ -230,15 +230,15 @@ void AkVCam::AssistantPrivate::removePortByName(const std::string &portName)
AkLogFunction();
AkLogInfo() << "Port: " << portName << std::endl;
for (auto &peer: this->m_clients)
for (auto &peer: this->m_peers)
if (peer.first == portName) {
xpc_release(peer.second);
this->m_clients.erase(portName);
this->m_peers.erase(portName);
break;
}
if (this->m_clients.empty())
if (this->m_peers.empty())
this->startTimer();
this->releaseDevicesFromPeer(portName);
@ -257,8 +257,8 @@ void AkVCam::AssistantPrivate::releaseDevicesFromPeer(const std::string &portNam
xpc_dictionary_set_string(dictionary, "device", config.first.c_str());
xpc_dictionary_set_string(dictionary, "broadcaster", "");
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, dictionary);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, dictionary);
xpc_release(dictionary);
} else {
@ -298,8 +298,8 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
xpc_connection_resume(connection);
bool ok = true;
for (auto &client: this->m_clients)
if (client.first == portName) {
for (auto &peer: this->m_peers)
if (peer.first == portName) {
ok = false;
break;
@ -307,7 +307,7 @@ void AkVCam::AssistantPrivate::addPort(xpc_connection_t client,
if (ok) {
AkLogInfo() << "Adding Peer: " << portName << std::endl;
this->m_clients[portName] = connection;
this->m_peers[portName] = connection;
this->stopTimer();
}
@ -349,8 +349,8 @@ void AkVCam::AssistantPrivate::devicesUpdate(xpc_connection_t client,
if (xpc_dictionary_get_bool(event, "propagate")) {
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, notification);
xpc_release(notification);
}
@ -371,8 +371,8 @@ void AkVCam::AssistantPrivate::setBroadcasting(xpc_connection_t client,
this->m_deviceConfigs[deviceId].broadcaster = broadcaster;
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, notification);
xpc_release(notification);
}
@ -387,9 +387,9 @@ void AkVCam::AssistantPrivate::frameReady(xpc_connection_t client,
auto reply = xpc_dictionary_create_reply(event);
bool ok = true;
for (auto &client: this->m_clients) {
AkLogDebug() << "Sending frame to " << client.first << std::endl;
auto reply = xpc_connection_send_message_with_reply_sync(client.second,
for (auto &peer: this->m_peers) {
AkLogDebug() << "Sending frame to " << peer.first << std::endl;
auto reply = xpc_connection_send_message_with_reply_sync(peer.second,
event);
auto replyType = xpc_get_type(reply);
bool isOk = false;
@ -417,8 +417,8 @@ void AkVCam::AssistantPrivate::pictureUpdated(xpc_connection_t client,
AkLogFunction();
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, notification);
xpc_release(notification);
}
@ -502,8 +502,8 @@ void AkVCam::AssistantPrivate::listenerAdd(xpc_connection_t client,
listeners.push_back(listener);
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, notification);
xpc_release(notification);
ok = true;
@ -532,8 +532,8 @@ void AkVCam::AssistantPrivate::listenerRemove(xpc_connection_t client,
listeners.erase(it);
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, notification);
xpc_release(notification);
ok = true;
@ -564,8 +564,8 @@ void AkVCam::AssistantPrivate::controlsUpdated(xpc_connection_t client,
auto notification = xpc_copy(event);
for (auto &client: this->m_clients)
xpc_connection_send_message(client.second, notification);
for (auto &peer: this->m_peers)
xpc_connection_send_message(peer.second, notification);
xpc_release(notification);
}

View file

@ -134,14 +134,14 @@ namespace AkVCam
}
}
AkVCam::IpcBridge::IpcBridge()
AkVCam::IpcBridge::IpcBridge(bool isVCam)
{
AkLogFunction();
this->d = new IpcBridgePrivate(this);
auto loglevel = AkVCam::Preferences::logLevel();
AkVCam::Logger::setLogLevel(loglevel);
ipcBridgePrivate().add(this);
this->registerPeer();
this->registerPeer(isVCam);
}
AkVCam::IpcBridge::~IpcBridge()
@ -191,7 +191,7 @@ std::string AkVCam::IpcBridge::logPath(const std::string &logName) const
"/tmp/" + logName + ".log");
}
bool AkVCam::IpcBridge::registerPeer()
bool AkVCam::IpcBridge::registerPeer(bool isVCam)
{
AkLogFunction();
@ -271,6 +271,7 @@ bool AkVCam::IpcBridge::registerPeer()
xpc_dictionary_set_int64(dictionary, "message", AKVCAM_ASSISTANT_MSG_ADD_PORT);
xpc_dictionary_set_string(dictionary, "port", portName.c_str());
xpc_dictionary_set_connection(dictionary, "connection", messagePort);
xpc_dictionary_set_bool(dictionary, "isvcam", isVCam);
reply = xpc_connection_send_message_with_reply_sync(serverMessagePort,
dictionary);
xpc_release(dictionary);
@ -810,6 +811,26 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
return status;
}
bool AkVCam::IpcBridge::isBusyFor(const std::string &operation) const
{
static const std::vector<std::string> operations {
"add-device",
"add-format",
"load",
"remove-device",
"remove-devices",
"remove-format",
"remove-formats",
"set-description",
"update",
"hack"
};
auto it = std::find(operations.begin(), operations.end(), operation);
return it != operations.end() && !this->clientsPids().empty();
}
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
{
static const std::vector<std::string> operations;

View file

@ -39,7 +39,7 @@ namespace AkVCam
PluginInterface *self;
ULONG m_ref;
ULONG m_reserved;
IpcBridge m_ipcBridge;
IpcBridge m_ipcBridge {true};
void updateDevices();
static HRESULT QueryInterface(void *self,

View file

@ -41,7 +41,9 @@ target_link_libraries(Assistant
PlatformUtils
advapi32
gdi32
kernel32
ole32
psapi
shell32
strmiids
uuid)

View file

@ -26,6 +26,7 @@
#include <thread>
#include <vector>
#include <windows.h>
#include <psapi.h>
#include <shellapi.h>
#include <sddl.h>
@ -39,13 +40,20 @@
namespace AkVCam
{
struct PeerInfo
{
std::string pipeName;
uint64_t pid {0};
bool isVCam {false};
};
struct AssistantDevice
{
std::string broadcaster;
std::vector<std::string> listeners;
};
typedef std::map<std::string, std::string> AssistantPeers;
typedef std::map<std::string, PeerInfo> AssistantPeers;
typedef std::map<std::string, AssistantDevice> DeviceConfigs;
class ServicePrivate
@ -54,7 +62,7 @@ namespace AkVCam
SERVICE_STATUS m_status;
SERVICE_STATUS_HANDLE m_statusHandler;
MessageServer m_messageServer;
AssistantPeers m_clients;
AssistantPeers m_peers;
DeviceConfigs m_deviceConfigs;
Timer m_timer;
std::mutex m_peerMutex;
@ -64,6 +72,7 @@ namespace AkVCam
static void stateChanged(void *userData,
MessageServer::State state);
void sendStatus(DWORD currentState, DWORD exitCode, DWORD wait);
static std::vector<uint64_t> systemProcesses();
static void checkPeers(void *userData);
void removePortByName(const std::string &portName);
void releaseDevicesFromPeer(const std::string &portName);
@ -80,6 +89,8 @@ namespace AkVCam
void listenerAdd(Message *message);
void listenerRemove(Message *message);
void controlsUpdated(Message *message);
void clients(Message *message);
void client(Message *message);
};
GLOBAL_STATIC(ServicePrivate, servicePrivate)
@ -279,8 +290,10 @@ AkVCam::ServicePrivate::ServicePrivate()
{AKVCAM_ASSISTANT_MSG_DEVICE_BROADCASTING , AKVCAM_BIND_FUNC(ServicePrivate::broadcasting) },
{AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING , AKVCAM_BIND_FUNC(ServicePrivate::setBroadCasting)},
{AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED, AKVCAM_BIND_FUNC(ServicePrivate::controlsUpdated)},
{AKVCAM_ASSISTANT_MSG_CLIENTS , AKVCAM_BIND_FUNC(ServicePrivate::clients) },
{AKVCAM_ASSISTANT_MSG_CLIENT , AKVCAM_BIND_FUNC(ServicePrivate::client) },
});
this->m_timer.setInterval(60000);
this->m_timer.setInterval(5000);
this->m_timer.connectTimeout(this, &ServicePrivate::checkPeers);
this->m_timer.start();
}
@ -335,23 +348,41 @@ void AkVCam::ServicePrivate::sendStatus(DWORD currentState,
SetServiceStatus(this->m_statusHandler, &this->m_status);
}
std::vector<uint64_t> AkVCam::ServicePrivate::systemProcesses()
{
std::vector<uint64_t> pids;
const DWORD nElements = 4096;
DWORD process[nElements];
memset(process, 0, nElements * sizeof(DWORD));
DWORD needed = 0;
if (!EnumProcesses(process, nElements * sizeof(DWORD), &needed))
return {};
size_t nProcess = needed / sizeof(DWORD);
for (size_t i = 0; i < nProcess; i++)
if (process[i] > 0)
pids.push_back(process[i]);
return pids;
}
void AkVCam::ServicePrivate::checkPeers(void *userData)
{
AkLogFunction();
auto self = reinterpret_cast<ServicePrivate *>(userData);
std::vector<std::string> deadPeers;
auto pids = systemProcesses();
self->m_peerMutex.lock();
for (auto &client: self->m_clients) {
Message message;
message.messageId = AKVCAM_ASSISTANT_MSG_ISALIVE;
message.dataSize = sizeof(MsgIsAlive);
MessageServer::sendMessage(client.second, &message);
auto requestData = messageData<MsgIsAlive>(&message);
for (auto &peer: self->m_peers) {
auto it = std::find(pids.begin(), pids.end(), peer.second.pid);
if (!requestData->alive)
deadPeers.push_back(client.first);
if (it == pids.end())
deadPeers.push_back(peer.first);
}
self->m_peerMutex.unlock();
@ -369,9 +400,9 @@ void AkVCam::ServicePrivate::removePortByName(const std::string &portName)
this->m_peerMutex.lock();
for (auto &peer: this->m_clients)
for (auto &peer: this->m_peers)
if (peer.first == portName) {
this->m_clients.erase(portName);
this->m_peers.erase(portName);
break;
}
@ -397,8 +428,8 @@ void AkVCam::ServicePrivate::releaseDevicesFromPeer(const std::string &portName)
(std::min<size_t>)(config.first.size(), MAX_STRING));
this->m_peerMutex.lock();
for (auto &client: this->m_clients)
MessageServer::sendMessage(client.second, &message);
for (auto &peer: this->m_peers)
MessageServer::sendMessage(peer.second.pipeName, &message);
this->m_peerMutex.unlock();
} else {
@ -437,8 +468,8 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
this->m_peerMutex.lock();
for (auto &client: this->m_clients)
if (client.first == portName) {
for (auto &peer: this->m_peers)
if (peer.first == portName) {
ok = false;
break;
@ -446,7 +477,11 @@ void AkVCam::ServicePrivate::addPort(AkVCam::Message *message)
if (ok) {
AkLogInfo() << "Adding Peer: " << portName << std::endl;
this->m_clients[portName] = pipeName;
PeerInfo peerInfo;
peerInfo.pipeName = pipeName;
peerInfo.pid = data->pid;
peerInfo.isVCam = data->isVCam;
this->m_peers[portName] = peerInfo;
}
this->m_peerMutex.unlock();
@ -480,8 +515,8 @@ void AkVCam::ServicePrivate::devicesUpdate(AkVCam::Message *message)
this->m_deviceConfigs = configs;
if (data->propagate)
for (auto &client: this->m_clients)
MessageServer::sendMessage(client.second, message);
for (auto &peer: this->m_peers)
MessageServer::sendMessage(peer.second.pipeName, message);
this->m_peerMutex.unlock();
}
@ -503,9 +538,9 @@ void AkVCam::ServicePrivate::setBroadCasting(AkVCam::Message *message)
this->m_peerMutex.lock();
for (auto &client: this->m_clients) {
for (auto &peer: this->m_peers) {
Message msg(message);
MessageServer::sendMessage(client.second, &msg);
MessageServer::sendMessage(peer.second.pipeName, &msg);
}
this->m_peerMutex.unlock();
@ -517,8 +552,8 @@ void AkVCam::ServicePrivate::frameReady(AkVCam::Message *message)
AkLogFunction();
this->m_peerMutex.lock();
for (auto &client: this->m_clients)
MessageServer::sendMessage(client.second, message);
for (auto &peer: this->m_peers)
MessageServer::sendMessage(peer.second.pipeName, message);
this->m_peerMutex.unlock();
}
@ -528,8 +563,8 @@ void AkVCam::ServicePrivate::pictureUpdated(AkVCam::Message *message)
AkLogFunction();
this->m_peerMutex.lock();
for (auto &client: this->m_clients)
MessageServer::sendMessage(client.second, message);
for (auto &peer: this->m_peers)
MessageServer::sendMessage(peer.second.pipeName, message);
this->m_peerMutex.unlock();
}
@ -616,9 +651,9 @@ void AkVCam::ServicePrivate::listenerAdd(AkVCam::Message *message)
this->m_peerMutex.lock();
for (auto &client: this->m_clients) {
for (auto &peer: this->m_peers) {
Message msg(message);
MessageServer::sendMessage(client.second, &msg);
MessageServer::sendMessage(peer.second.pipeName, &msg);
}
this->m_peerMutex.unlock();
@ -648,9 +683,9 @@ void AkVCam::ServicePrivate::listenerRemove(AkVCam::Message *message)
this->m_peerMutex.lock();
for (auto &client: this->m_clients) {
for (auto &peer: this->m_peers) {
Message msg(message);
MessageServer::sendMessage(client.second, &msg);
MessageServer::sendMessage(peer.second.pipeName, &msg);
}
this->m_peerMutex.unlock();
@ -665,12 +700,55 @@ void AkVCam::ServicePrivate::controlsUpdated(AkVCam::Message *message)
AkLogFunction();
this->m_peerMutex.lock();
for (auto &client: this->m_clients)
MessageServer::sendMessage(client.second, message);
for (auto &peer: this->m_peers)
MessageServer::sendMessage(peer.second.pipeName, message);
this->m_peerMutex.unlock();
}
void AkVCam::ServicePrivate::clients(AkVCam::Message *message)
{
AkLogFunction();
auto data = messageData<MsgClients>(message);
this->m_peerMutex.lock();
data->nclient = 0;
for (auto &peer: this->m_peers)
if (peer.second.isVCam)
data->nclient++;
data->status = true;
this->m_peerMutex.unlock();
}
void AkVCam::ServicePrivate::client(AkVCam::Message *message)
{
AkLogFunction();
auto data = messageData<MsgClients>(message);
std::vector<uint64_t> pids;
this->m_peerMutex.lock();
for (auto &peer: this->m_peers) {
AkLogDebug() << "PID: " << peer.second.pid << std::endl;
AkLogDebug() << "Is vcam: " << peer.second.isVCam << std::endl;
if (peer.second.isVCam)
pids.push_back(peer.second.pid);
}
this->m_peerMutex.unlock();
std::sort(pids.begin(), pids.end());
if (data->nclient >= pids.size()) {
data->status = false;
return;
}
data->pid = pids[data->nclient];
data->status = true;
}
DWORD WINAPI controlHandler(DWORD control,
DWORD eventType,
LPVOID eventData,

View file

@ -53,6 +53,10 @@
#define AKVCAM_ASSISTANT_MSG_DEVICE_SETBROADCASTING 0x401
#define AKVCAM_ASSISTANT_MSG_DEVICE_CONTROLS_UPDATED 0x402
// Virtual camera clients
#define AKVCAM_ASSISTANT_MSG_CLIENTS 0x500
#define AKVCAM_ASSISTANT_MSG_CLIENT 0x501
#define MSG_BUFFER_SIZE 4096
#define MAX_STRING 1024
@ -133,6 +137,8 @@ namespace AkVCam
{
char port[MAX_STRING];
char pipeName[MAX_STRING];
uint64_t pid;
bool isVCam;
bool status;
};
@ -181,6 +187,13 @@ namespace AkVCam
{
char device[MAX_STRING];
};
struct MsgClients
{
uint64_t pid;
size_t nclient;
bool status;
};
}
#endif // MESSAGECOMMONS_H

View file

@ -47,22 +47,17 @@ namespace AkVCam
std::string m_pipeName;
std::map<uint32_t, MessageHandler> m_handlers;
MessageServer::ServerMode m_mode {MessageServer::ServerModeReceive};
MessageServer::PipeState m_pipeState {MessageServer::PipeStateGone};
std::thread m_mainThread;
std::vector<PipeThreadPtr> m_clientsThreads;
std::mutex m_mutex;
std::condition_variable_any m_exitCheckLoop;
int m_checkInterval {5000};
bool m_running {false};
explicit MessageServerPrivate(MessageServer *self);
bool startReceive(bool wait=false);
void stopReceive(bool wait=false);
bool startSend();
void stopSend();
void messagesLoop();
void processPipe(PipeThreadPtr pipeThread, HANDLE pipe);
void checkLoop();
};
}
@ -107,21 +102,6 @@ void AkVCam::MessageServer::setMode(ServerMode mode)
this->d->m_mode = mode;
}
int AkVCam::MessageServer::checkInterval() const
{
return this->d->m_checkInterval;
}
int &AkVCam::MessageServer::checkInterval()
{
return this->d->m_checkInterval;
}
void AkVCam::MessageServer::setCheckInterval(int checkInterval)
{
this->d->m_checkInterval = checkInterval;
}
void AkVCam::MessageServer::setHandlers(const std::map<uint32_t, MessageHandler> &handlers)
{
this->d->m_handlers = handlers;
@ -139,8 +119,6 @@ bool AkVCam::MessageServer::start(bool wait)
case ServerModeSend:
AkLogInfo() << "Starting mode send" << std::endl;
return this->d->startSend();
}
return false;
@ -152,8 +130,6 @@ void AkVCam::MessageServer::stop(bool wait)
if (this->d->m_mode == ServerModeReceive)
this->d->stopReceive(wait);
else
this->d->stopSend();
}
BOOL AkVCam::MessageServer::sendMessage(Message *message,
@ -265,32 +241,6 @@ void AkVCam::MessageServerPrivate::stopReceive(bool wait)
this->m_mainThread.join();
}
bool AkVCam::MessageServerPrivate::startSend()
{
AkLogFunction();
AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl;
this->m_running = true;
this->m_mainThread = std::thread(&MessageServerPrivate::checkLoop, this);
return true;
}
void AkVCam::MessageServerPrivate::stopSend()
{
AkLogFunction();
if (!this->m_running)
return;
AkLogDebug() << "Pipe: " << this->m_pipeName << std::endl;
this->m_running = false;
this->m_mutex.lock();
this->m_exitCheckLoop.notify_all();
this->m_mutex.unlock();
this->m_mainThread.join();
this->m_pipeState = MessageServer::PipeStateGone;
}
void AkVCam::MessageServerPrivate::messagesLoop()
{
AkLogFunction();
@ -460,34 +410,3 @@ void AkVCam::MessageServerPrivate::processPipe(PipeThreadPtr pipeThread,
pipeThread->finished = true;
AkLogDebug() << "Pipe thread finished." << std::endl;
}
void AkVCam::MessageServerPrivate::checkLoop()
{
AkLogFunction();
while (this->m_running) {
AkLogDebug() << "Waiting for pipe: " << this->m_pipeName << std::endl;
auto result = WaitNamedPipeA(this->m_pipeName.c_str(), NMPWAIT_NOWAIT);
if (result
&& this->m_pipeState != AkVCam::MessageServer::PipeStateAvailable) {
AkLogInfo() << "Pipe Available: " << this->m_pipeName << std::endl;
this->m_pipeState = AkVCam::MessageServer::PipeStateAvailable;
AKVCAM_EMIT(this->self, PipeStateChanged, this->m_pipeState)
} else if (!result
&& this->m_pipeState != AkVCam::MessageServer::PipeStateGone
&& GetLastError() != ERROR_SEM_TIMEOUT) {
AkLogInfo() << "Pipe Gone: " << this->m_pipeName << std::endl;
this->m_pipeState = AkVCam::MessageServer::PipeStateGone;
AKVCAM_EMIT(this->self, PipeStateChanged, this->m_pipeState)
}
if (!this->m_running)
break;
this->m_mutex.lock();
this->m_exitCheckLoop.wait_for(this->m_mutex,
std::chrono::milliseconds(this->m_checkInterval));
this->m_mutex.unlock();
}
}

View file

@ -52,14 +52,7 @@ namespace AkVCam
StateStopped
};
enum PipeState
{
PipeStateAvailable,
PipeStateGone
};
AKVCAM_SIGNAL(StateChanged, State state)
AKVCAM_SIGNAL(PipeStateChanged, PipeState state)
public:
MessageServer();
@ -72,9 +65,6 @@ namespace AkVCam
ServerMode mode() const;
ServerMode &mode();
void setMode(ServerMode mode);
int checkInterval() const;
int &checkInterval();
void setCheckInterval(int checkInterval);
void setHandlers(const std::map<uint32_t,
MessageHandler> &handlers);
bool start(bool wait=false);

View file

@ -143,14 +143,14 @@ namespace AkVCam
static const size_t maxBufferSize = sizeof(Frame) + 3 * maxFrameSize;
}
AkVCam::IpcBridge::IpcBridge()
AkVCam::IpcBridge::IpcBridge(bool isVCam)
{
AkLogFunction();
this->d = new IpcBridgePrivate(this);
auto loglevel = AkVCam::Preferences::logLevel();
AkVCam::Logger::setLogLevel(loglevel);
this->d->m_mainServer.start();
this->registerPeer();
this->registerPeer(isVCam);
this->d->updateDeviceSharedProperties();
this->d->startServiceStatusCheck();
}
@ -204,7 +204,7 @@ std::string AkVCam::IpcBridge::logPath(const std::string &logName) const
return AkVCam::Preferences::readString("logfile", defaultLogFile);
}
bool AkVCam::IpcBridge::registerPeer()
bool AkVCam::IpcBridge::registerPeer(bool isVCam)
{
AkLogFunction();
@ -254,6 +254,8 @@ bool AkVCam::IpcBridge::registerPeer()
memcpy(addData->pipeName,
pipeName.c_str(),
(std::min<size_t>)(pipeName.size(), MAX_STRING));
addData->pid = GetCurrentProcessId();
addData->isVCam = isVCam;
if (!MessageServer::sendMessage("\\\\.\\pipe\\" DSHOW_PLUGIN_ASSISTANT_NAME,
&message)) {
@ -488,10 +490,11 @@ std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceI
if (!data->status)
return {};
message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER;
size_t nlisteners = data->nlistener;
message.messageId = AKVCAM_ASSISTANT_MSG_DEVICE_LISTENER;
std::vector<std::string> listeners;
for (size_t i = 0; i < data->nlistener; i++) {
for (size_t i = 0; i < nlisteners; i++) {
data->nlistener = i;
if (!this->d->m_mainServer.sendMessage(&message))
@ -509,98 +512,36 @@ std::vector<std::string> AkVCam::IpcBridge::listeners(const std::string &deviceI
std::vector<uint64_t> AkVCam::IpcBridge::clientsPids() const
{
AkLogFunction();
auto pluginPath = locatePluginPath();
AkLogDebug() << "Plugin path: " << pluginPath << std::endl;
if (pluginPath.empty())
Message message;
message.messageId = AKVCAM_ASSISTANT_MSG_CLIENTS;
message.dataSize = sizeof(MsgClients);
auto data = messageData<MsgClients>(&message);
if (!this->d->m_mainServer.sendMessage(&message))
return {};
std::vector<std::string> plugins;
// First check for the existence of the main plugin binary.
auto path = pluginPath + "\\" DSHOW_PLUGIN_NAME ".dll";
AkLogDebug() << "Plugin binary: " << path << std::endl;
if (fileExists(path))
plugins.push_back(path);
// Check if the alternative architecture plugin exists.
auto altPlugin = this->d->alternativePlugin();
if (!altPlugin.empty())
plugins.push_back(path);
if (plugins.empty())
if (!data->status)
return {};
auto currentPid = GetCurrentProcessId();
size_t nclients = data->nclient;
message.messageId = AKVCAM_ASSISTANT_MSG_CLIENT;
std::vector<uint64_t> pids;
const DWORD nElements = 4096;
DWORD process[nElements];
memset(process, 0, nElements * sizeof(DWORD));
DWORD needed = 0;
for (size_t i = 0; i < nclients; i++) {
data->nclient = i;
if (!EnumProcesses(process, nElements * sizeof(DWORD), &needed))
return {};
size_t nProcess = needed / sizeof(DWORD);
auto currentPid = GetCurrentProcessId();
for (size_t i = 0; i < nProcess; i++) {
auto processHnd = OpenProcess(PROCESS_QUERY_INFORMATION
| PROCESS_VM_READ,
FALSE,
process[i]);
if (!processHnd)
if (!this->d->m_mainServer.sendMessage(&message))
continue;
char processName[MAX_PATH];
memset(&processName, 0, sizeof(MAX_PATH));
if (!data->status)
continue;
if (GetModuleBaseNameA(processHnd,
nullptr,
processName,
MAX_PATH))
AkLogDebug() << "Enumerating modules for '" << processName << "'" << std::endl;
if (data->pid == currentPid)
continue;
HMODULE modules[nElements];
memset(modules, 0, nElements * sizeof(HMODULE));
if (EnumProcessModules(processHnd,
modules,
nElements * sizeof(HMODULE),
&needed)) {
size_t nModules =
std::min<DWORD>(needed / sizeof(HMODULE), nElements);
for (size_t j = 0; j < nModules; j++) {
CHAR moduleName[MAX_PATH];
memset(moduleName, 0, MAX_PATH * sizeof(CHAR));
if (GetModuleFileNameExA(processHnd,
modules[j],
moduleName,
MAX_PATH)) {
auto it = std::find(plugins.begin(),
plugins.end(),
moduleName);
if (it != plugins.end()) {
auto pidsIt = std::find(pids.begin(),
pids.end(),
process[i]);
if (process[i] > 0
&& pidsIt == pids.end()
&& process[i] != currentPid)
pids.push_back(process[i]);
}
}
}
}
CloseHandle(processHnd);
pids.push_back(data->pid);
}
return pids;
@ -875,6 +816,26 @@ bool AkVCam::IpcBridge::removeListener(const std::string &deviceId)
return data->status;
}
bool AkVCam::IpcBridge::isBusyFor(const std::string &operation) const
{
static const std::vector<std::string> operations {
"add-device",
"add-format",
"load",
"remove-device",
"remove-devices",
"remove-format",
"remove-formats",
"set-description",
"update",
"hack"
};
auto it = std::find(operations.begin(), operations.end(), operation);
return it != operations.end() && !this->clientsPids().empty();
}
bool AkVCam::IpcBridge::needsRoot(const std::string &operation) const
{
static const std::vector<std::string> operations {

View file

@ -62,7 +62,7 @@ namespace AkVCam
std::string m_vendor;
std::string m_filterName;
IFilterGraph *m_filterGraph {nullptr};
IpcBridge m_ipcBridge;
IpcBridge m_ipcBridge {true};
IpcBridge::ServerState m_serverState {IpcBridge::ServerStateGone};
BaseFilterPrivate(BaseFilter *self,