mirror of
https://github.com/openSUSE/snapper.git
synced 2026-02-05 15:46:00 +01:00
349 lines
8.1 KiB
C++
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;
|
|
}
|
|
}
|
|
|
|
}
|