1
0
mirror of https://github.com/openSUSE/snapper.git synced 2026-02-05 15:46:00 +01:00
Files
snapper/dbus/DBusMainLoop.cc
2025-08-06 11:36:07 +02:00

349 lines
8.1 KiB
C++

/*
* Copyright (c) [2012-2015] Novell, Inc.
*
* All Rights Reserved.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of version 2 of the GNU General Public License as published
* by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, contact Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail, you may
* find current contact information at www.novell.com.
*/
#include <unistd.h>
#include <poll.h>
#include "DBusMainLoop.h"
namespace DBus
{
MainLoop::Watch::Watch(DBusWatch* dbus_watch)
: dbus_watch(dbus_watch), enabled(false), fd(-1), events(0)
{
enabled = dbus_watch_get_enabled(dbus_watch);
fd = dbus_watch_get_unix_fd(dbus_watch);
unsigned int flags = dbus_watch_get_flags(dbus_watch);
if (flags & DBUS_WATCH_READABLE)
events |= POLLIN;
if (flags & DBUS_WATCH_WRITABLE)
events |= POLLOUT;
}
MainLoop::Timeout::Timeout(DBusTimeout* dbus_timeout)
: dbus_timeout(dbus_timeout), enabled(false), interval(0)
{
enabled = dbus_timeout_get_enabled(dbus_timeout);
interval = dbus_timeout_get_interval(dbus_timeout);
}
MainLoop::MainLoop(DBusBusType type)
: Connection(type), idle_timeout(-1)
{
if (pipe(wakeup_pipe) != 0)
SN_THROW(FatalException());
if (!dbus_connection_set_watch_functions(conn, add_watch, remove_watch, toggled_watch,
this, NULL))
SN_THROW(FatalException());
if (!dbus_connection_set_timeout_functions(conn, add_timeout, remove_timeout,
toggled_timeout, this, NULL))
SN_THROW(FatalException());
dbus_connection_set_wakeup_main_function(conn, wakeup_main, this, NULL);
}
MainLoop::~MainLoop()
{
close(wakeup_pipe[0]);
close(wakeup_pipe[1]);
}
void
DBus::MainLoop::run()
{
reset_idle_count();
while (true)
{
vector<struct pollfd> pollfds;
{
struct pollfd tmp;
tmp.fd = wakeup_pipe[0];
tmp.events = POLLIN;
tmp.revents = 0;
pollfds.push_back(tmp);
}
for (vector<Watch>::const_iterator it = watches.begin(); it != watches.end(); ++it)
{
if (it->enabled)
{
struct pollfd tmp;
tmp.fd = it->fd;
tmp.events = it->events;
tmp.revents = 0;
pollfds.push_back(tmp);
}
}
milliseconds timeout = periodic_timeout();
if (idle_timeout.count() >= 0)
{
steady_clock::duration time_left = idle_for() + idle_timeout;
if (timeout > time_left || timeout.count() < 0)
timeout = duration_cast<milliseconds>(time_left);
}
int r = poll(&pollfds[0], pollfds.size(), timeout.count());
if (r == -1)
SN_THROW(FatalException());
periodic();
for (vector<struct pollfd>::const_iterator it1 = pollfds.begin(); it1 != pollfds.end(); ++it1)
{
if (it1->fd == wakeup_pipe[0])
{
if (it1->revents & POLLIN)
{
char arbitrary;
read(wakeup_pipe[0], &arbitrary, 1);
}
}
else
{
unsigned int flags = 0;
if (it1->revents & POLLIN)
flags |= DBUS_WATCH_READABLE;
if (it1->revents & POLLOUT)
flags |= DBUS_WATCH_WRITABLE;
if (flags != 0)
{
// Do not iterate over watches here since calling dbus_watch_handle() can
// trigger a remove_watch() callback, thus invalidating the iterator.
// Instead always search for the watch.
vector<Watch>::const_iterator it2 = find_enabled_watch(it1->fd, it1->events);
if (it2 != watches.end())
{
boost::lock_guard<boost::mutex> lock(mutex);
dbus_watch_handle(it2->dbus_watch, flags);
}
}
}
}
DBusMessage* tmp = pop_message();
if (tmp)
{
DBus::Message msg(tmp, false);
dispatch_incoming(msg);
}
if (idle_timeout.count() >= 0)
{
steady_clock::duration time_left = idle_for() + idle_timeout;
if (time_left.count() <= 0)
break;
}
}
}
void
MainLoop::set_idle_timeout(milliseconds idle_timeout)
{
MainLoop::idle_timeout = idle_timeout;
}
void
MainLoop::reset_idle_count()
{
last_action = steady_clock::now();
}
milliseconds
MainLoop::idle_for() const
{
return duration_cast<milliseconds>(last_action - steady_clock::now());
}
vector<MainLoop::Watch>::iterator
MainLoop::find_watch(DBusWatch* dbus_watch)
{
for (vector<Watch>::iterator it = watches.begin(); it != watches.end(); ++it)
if (it->dbus_watch == dbus_watch)
return it;
SN_THROW(FatalException());
__builtin_unreachable();
}
vector<MainLoop::Watch>::iterator
MainLoop::find_enabled_watch(int fd, short events)
{
for (vector<Watch>::iterator it = watches.begin(); it != watches.end(); ++it)
if (it->enabled && it->fd == fd && it->events == events)
return it;
return watches.end();
}
vector<MainLoop::Timeout>::iterator
MainLoop::find_timeout(DBusTimeout* dbus_timeout)
{
for (vector<Timeout>::iterator it = timeouts.begin(); it != timeouts.end(); ++it)
if (it->dbus_timeout == dbus_timeout)
return it;
SN_THROW(FatalException());
__builtin_unreachable();
}
dbus_bool_t
MainLoop::add_watch(DBusWatch* dbus_watch, void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
s->watches.emplace_back(dbus_watch);
return true;
}
void
MainLoop::remove_watch(DBusWatch* dbus_watch, void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
vector<Watch>::iterator it = s->find_watch(dbus_watch);
s->watches.erase(it);
}
void
MainLoop::toggled_watch(DBusWatch* dbus_watch, void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
vector<Watch>::iterator it = s->find_watch(dbus_watch);
it->enabled = dbus_watch_get_enabled(dbus_watch);
}
dbus_bool_t
MainLoop::add_timeout(DBusTimeout* dbus_timeout, void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
s->timeouts.emplace_back(dbus_timeout);
return true;
}
void
MainLoop::remove_timeout(DBusTimeout* dbus_timeout, void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
vector<Timeout>::iterator it = s->find_timeout(dbus_timeout);
s->timeouts.erase(it);
}
void
MainLoop::toggled_timeout(DBusTimeout* dbus_timeout, void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
vector<Timeout>::iterator it = s->find_timeout(dbus_timeout);
it->enabled = dbus_timeout_get_enabled(dbus_timeout);
}
void
MainLoop::wakeup_main(void* data)
{
MainLoop* s = static_cast<MainLoop*>(data);
const char arbitrary = 42;
write(s->wakeup_pipe[1], &arbitrary, 1);
}
void
DBus::MainLoop::add_client_match(const string& name)
{
// Filtering for the sender doesn't work for me. So also check the
// sender later when handling the signal.
add_match("type='signal', sender='" DBUS_SERVICE_DBUS "', path='" DBUS_PATH_DBUS "', "
"interface='" DBUS_INTERFACE_DBUS "', member='NameOwnerChanged', "
"arg0='" + name + "'");
}
void
DBus::MainLoop::remove_client_match(const string& name)
{
// Filtering for the sender doesn't work for me. So also check the
// sender later when handling the signal.
remove_match("type='signal', sender='" DBUS_SERVICE_DBUS "', path='" DBUS_PATH_DBUS "', "
"interface='" DBUS_INTERFACE_DBUS "', member='NameOwnerChanged', "
"arg0='" + name + "'");
}
void
DBus::MainLoop::dispatch_incoming(Message& msg)
{
switch (msg.get_type())
{
case DBUS_MESSAGE_TYPE_METHOD_CALL:
{
method_call(msg);
}
break;
case DBUS_MESSAGE_TYPE_SIGNAL:
{
signal(msg);
if (msg.get_sender() == DBUS_SERVICE_DBUS &&
msg.is_signal(DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
{
string name, old_owner, new_owner;
DBus::Unmarshaller unmarshaller(msg);
unmarshaller >> name >> old_owner >> new_owner;
if (name == old_owner && new_owner.empty())
client_disconnected(name);
}
}
break;
}
}
}