using std::list;
using std::map;
-#include "dbus/DBusConnection.h"
#include "dbus/DBusMessage.h"
+#include "dbus/DBusConnection.h"
#include "snapper/Snapshot.h"
#include "snapper/File.h"
void
Connection::request_name(const char* name, unsigned int flags)
{
- boost::lock_guard<boost::mutex> lock(mutex);
-
DBusError err;
dbus_error_init(&err);
}
- void
- Connection::read_write(int timeout)
- {
- // TODO
-
- dbus_connection_read_write(conn, timeout);
- }
-
-
void
Connection::send(Message& m)
{
- boost::lock_guard<boost::mutex> lock(mutex);
-
if (!dbus_connection_send(conn, m.get_message(), NULL))
{
throw FatalException();
Message
Connection::send_with_reply_and_block(Message& m)
{
- boost::lock_guard<boost::mutex> lock(mutex);
-
DBusError err;
dbus_error_init(&err);
void
Connection::add_match(const char* rule)
{
- boost::lock_guard<boost::mutex> lock(mutex);
-
DBusError err;
dbus_error_init(&err);
}
+ void
+ Connection::register_object_path(const char* path, const DBusObjectPathVTable* vtable,
+ void* user_data)
+ {
+ if (!dbus_connection_register_object_path(conn, path, vtable, user_data))
+ throw FatalException();
+ }
+
+
unsigned long
Connection::get_unix_userid(const Message& m)
{
- boost::lock_guard<boost::mutex> lock(mutex);
-
string sender = m.get_sender();
if (sender.empty())
{
*/
-#ifndef SNAPPER_DBUSCONN_H
-#define SNAPPER_DBUSCONN_H
+#ifndef SNAPPER_DBUSCONNECTION_H
+#define SNAPPER_DBUSCONNECTION_H
#include <dbus/dbus.h>
#include <boost/noncopyable.hpp>
-#include <boost/thread.hpp>
#include "DBusMessage.h"
void request_name(const char* name, unsigned int flags);
- void read_write(int timeout);
-
void send(Message& m);
Message send_with_reply_and_block(Message& m);
void add_match(const char* rule);
+ void register_object_path(const char* path, const DBusObjectPathVTable* vtable,
+ void* user_data);
+
unsigned long get_unix_userid(const Message& m);
static string get_unix_username(unsigned long userid); // TODO
- boost::mutex mutex;
-
private:
DBusConnection* conn;
: msg(m)
{
assert(msg);
+ dbus_message_ref(msg);
}
*/
-#ifndef SNAPPER_DBUSMSG_H
-#define SNAPPER_DBUSMSG_H
+#ifndef SNAPPER_DBUSMESSAGE_H
+#define SNAPPER_DBUSMESSAGE_H
#include <dbus/dbus.h>
--- /dev/null
+/*
+ * Copyright (c) 2012 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 <poll.h>
+#include <time.h>
+
+#include "DBusServer.h"
+
+
+namespace DBus
+{
+
+ Server::Server(DBusBusType type)
+ : Connection(type), idle_timeout(-1)
+ {
+ if (pipe(wakeup_pipe) != 0)
+ throw FatalException();
+
+ if (!dbus_connection_set_watch_functions(get_connection(), add_watch, remove_watch,
+ toggled_watch, this, NULL))
+ throw FatalException();
+
+ if (!dbus_connection_set_timeout_functions(get_connection(), add_timeout, remove_timeout,
+ toggled_timeout, this, NULL))
+ throw FatalException();
+
+ dbus_connection_set_wakeup_main_function(get_connection(), wakeup_main, this, NULL);
+ }
+
+
+ Server::~Server()
+ {
+ close(wakeup_pipe[0]);
+ close(wakeup_pipe[1]);
+ }
+
+
+ void
+ DBus::Server::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);
+ }
+ }
+
+ int timeout = -1;
+
+ if (idle_timeout >= 0)
+ {
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+ int time_left = last_action - tmp.tv_sec + idle_timeout;
+
+ if (timeout > time_left * 1000 || timeout == -1)
+ timeout = time_left * 1000;
+ }
+
+ int r = poll(&pollfds[0], pollfds.size(), timeout);
+ if (r == -1)
+ throw FatalException();
+
+ {
+ for (vector<struct pollfd>::const_iterator it2 = pollfds.begin(); it2 != pollfds.end(); ++it2)
+ {
+ if (it2->fd == wakeup_pipe[0] && (it2->revents & POLLIN))
+ {
+ char arbitrary;
+ read(wakeup_pipe[0], &arbitrary, 1);
+ }
+ }
+ }
+
+ for (vector<Watch>::const_iterator it = watches.begin(); it != watches.end(); ++it)
+ {
+ if (it->enabled)
+ {
+ for (vector<struct pollfd>::const_iterator it2 = pollfds.begin(); it2 != pollfds.end(); ++it2)
+ {
+ if (it2->fd == it->fd)
+ {
+ if ((it2->revents & POLLIN) && (it->flags & DBUS_WATCH_READABLE))
+ dbus_watch_handle(it->dbus_watch, DBUS_WATCH_READABLE);
+
+ if ((it2->revents & POLLOUT) && (it->flags & DBUS_WATCH_WRITABLE))
+ dbus_watch_handle(it->dbus_watch, DBUS_WATCH_WRITABLE);
+ }
+ }
+ }
+ }
+
+ while (dbus_connection_get_dispatch_status(get_connection()) == DBUS_DISPATCH_DATA_REMAINS)
+ {
+ dbus_connection_dispatch(get_connection());
+ }
+
+ if (idle_timeout >= 0)
+ {
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+ int time_left = last_action - tmp.tv_sec + idle_timeout;
+
+ if (time_left <= 0)
+ break;
+ }
+ }
+ }
+
+
+ void
+ Server::set_idle_timeout(int s)
+ {
+ idle_timeout = s;
+ }
+
+
+ void
+ Server::reset_idle_count()
+ {
+ struct timespec tmp;
+ clock_gettime(CLOCK_MONOTONIC, &tmp);
+ last_action = tmp.tv_sec;
+ }
+
+
+ vector<Server::Watch>::iterator
+ Server::find_watch(DBusWatch* dbus_watch)
+ {
+ for (vector<Watch>::iterator it = watches.begin(); it != watches.end(); ++it)
+ if (it->dbus_watch == dbus_watch)
+ return it;
+
+ throw FatalException();
+ }
+
+
+ vector<Server::Timeout>::iterator
+ Server::find_timeout(DBusTimeout* dbus_timeout)
+ {
+ for (vector<Timeout>::iterator it = timeouts.begin(); it != timeouts.end(); ++it)
+ if (it->dbus_timeout == dbus_timeout)
+ return it;
+
+ throw FatalException();
+ }
+
+
+ dbus_bool_t
+ Server::add_watch(DBusWatch* dbus_watch, void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ Watch tmp;
+ tmp.enabled = dbus_watch_get_enabled(dbus_watch);
+ tmp.fd = dbus_watch_get_unix_fd(dbus_watch);
+ tmp.flags = dbus_watch_get_flags(dbus_watch);
+
+ tmp.events = 0;
+ if (tmp.flags & DBUS_WATCH_READABLE)
+ tmp.events |= POLLIN;
+ if (tmp.flags & DBUS_WATCH_WRITABLE)
+ tmp.events |= POLLOUT;
+
+ tmp.dbus_watch = dbus_watch;
+ s->watches.push_back(tmp);
+
+ return true;
+ }
+
+
+ void
+ Server::remove_watch(DBusWatch* dbus_watch, void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ vector<Watch>::iterator it = s->find_watch(dbus_watch);
+ s->watches.erase(it);
+ }
+
+
+ void
+ Server::toggled_watch(DBusWatch* dbus_watch, void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ vector<Watch>::iterator it = s->find_watch(dbus_watch);
+ it->enabled = dbus_watch_get_enabled(dbus_watch);
+ }
+
+
+ dbus_bool_t
+ Server::add_timeout(DBusTimeout* dbus_timeout, void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ Timeout tmp;
+ tmp.enabled = dbus_timeout_get_enabled(dbus_timeout);
+ tmp.interval = dbus_timeout_get_interval(dbus_timeout);
+ tmp.dbus_timeout = dbus_timeout;
+ s->timeouts.push_back(tmp);
+
+ return true;
+ }
+
+
+ void
+ Server::remove_timeout(DBusTimeout* dbus_timeout, void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ vector<Timeout>::iterator it = s->find_timeout(dbus_timeout);
+ s->timeouts.erase(it);
+ }
+
+
+ void
+ Server::toggled_timeout(DBusTimeout* dbus_timeout, void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ vector<Timeout>::iterator it = s->find_timeout(dbus_timeout);
+ it->enabled = dbus_timeout_get_enabled(dbus_timeout);
+ }
+
+
+ void
+ Server::wakeup_main(void* data)
+ {
+ Server* s = static_cast<Server*>(data);
+
+ const char arbitrary = 42;
+ write(s->wakeup_pipe[1], &arbitrary, 1);
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2012 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.
+ */
+
+
+#ifndef SNAPPER_DBUSSERVER_H
+#define SNAPPER_DBUSSERVER_H
+
+
+#include <dbus/dbus.h>
+
+#include "DBusConnection.h"
+
+
+namespace DBus
+{
+
+ class Server : public Connection
+ {
+ public:
+
+ Server(DBusBusType type);
+ ~Server();
+
+ void run();
+
+ void set_idle_timeout(int s);
+ void reset_idle_count();
+
+ protected:
+
+ struct Watch
+ {
+ bool enabled;
+ int fd;
+ unsigned int flags;
+ int events;
+ DBusWatch* dbus_watch;
+ };
+
+ struct Timeout
+ {
+ bool enabled;
+ int interval;
+ DBusTimeout* dbus_timeout;
+ };
+
+ vector<Watch> watches;
+ vector<Timeout> timeouts;
+ int wakeup_pipe[2];
+
+ vector<Watch>::iterator find_watch(DBusWatch* dbus_watch);
+ vector<Timeout>::iterator find_timeout(DBusTimeout* dbus_timeout);
+
+ static dbus_bool_t add_watch(DBusWatch* dbus_watch, void* data);
+ static void remove_watch(DBusWatch* dbus_watch, void* data);
+ static void toggled_watch(DBusWatch* dbus_watch, void* data);
+
+ static dbus_bool_t add_timeout(DBusTimeout* dbus_timeout, void* data);
+ static void remove_timeout(DBusTimeout* dbus_timeout, void* data);
+ static void toggled_timeout(DBusTimeout* dbus_timeout, void* data);
+
+ static void wakeup_main(void* data);
+
+ int idle_timeout;
+ time_t last_action;
+
+ };
+
+}
+
+
+#endif
noinst_LTLIBRARIES = libdbus.la
libdbus_la_SOURCES = \
+ DBusMessage.cc DBusMessage.h \
DBusConnection.cc DBusConnection.h \
- DBusMessage.cc DBusMessage.h
-
+ DBusServer.cc DBusServer.h
Types.cc Types.h
snapperd_LDADD = ../snapper/libsnapper.la ../dbus/libdbus.la \
- -ldbus-1 -lboost_thread
+ -ldbus-1 -lboost_thread -lrt
#include <snapper/SnapperTmpl.h>
#include <snapper/AsciiFile.h>
-#include "dbus/DBusConnection.h"
#include "dbus/DBusMessage.h"
+#include "dbus/DBusConnection.h"
+#include "dbus/DBusServer.h"
#include "MetaSnapper.h"
#include "Client.h"
void
-listen(DBus::Connection& conn)
+unregister_func(DBusConnection* connection, void* data)
{
- y2mil("Requesting DBus name");
+}
- conn.request_name("org.opensuse.Snapper", DBUS_NAME_FLAG_REPLACE_EXISTING);
- y2mil("Listening for method calls and signals");
+DBusHandlerResult
+message_func1(DBusConnection* connection, DBusMessage* message, void* data)
+{
+ DBus::Server* s = static_cast<DBus::Server*>(data);
- conn.add_match("type='signal', interface='" DBUS_INTERFACE_DBUS "', member='NameOwnerChanged'");
+ DBus::Message msg(message);
- int idle = 0;
- while (++idle < 1000 || !clients.empty())
+ if (msg.get_type() == DBUS_MESSAGE_TYPE_METHOD_CALL)
{
- boost::this_thread::sleep(boost::posix_time::milliseconds(100)); // TODO
+ y2mil("method call sender:'" << msg.get_sender() << "' path:'" <<
+ msg.get_path() << "' interface:'" << msg.get_interface() <<
+ "' member:'" << msg.get_member() << "'");
+
+ s->reset_idle_count();
+
+ if (msg.is_method_call(DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
+ {
+ Commands::introspect(*s, msg);
+ }
+ else
+ {
+ Clients::iterator client = clients.find(msg.get_sender());
+ if (client == clients.end())
+ {
+ y2mil("client connected invisible '" << msg.get_sender() << "'");
+ client = client_connected(msg.get_sender());
+ s->set_idle_timeout(-1);
+ }
+
+ client->add_task(*s, msg);
+ }
- boost::unique_lock<boost::mutex> dbus_lock(conn.mutex);
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
- conn.read_write(10); // TODO
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
- DBusMessage* tmp = dbus_connection_pop_message(conn.get_connection());
- if (!tmp)
- continue;
- dbus_lock.unlock();
+DBusHandlerResult
+message_func2(DBusConnection* connection, DBusMessage* message, void* data)
+{
+ DBus::Server* s = static_cast<DBus::Server*>(data);
+
+ DBus::Message msg(message);
- DBus::Message msg(tmp);
+ if (msg.get_type() == DBUS_MESSAGE_TYPE_SIGNAL)
+ {
+ y2mil("signal sender:'" << msg.get_sender() << "' path:'" <<
+ msg.get_path() << "' interface:'" << msg.get_interface() <<
+ "' member:'" << msg.get_member() << "'");
- switch (msg.get_type())
+ if (msg.is_signal(DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
{
- case DBUS_MESSAGE_TYPE_METHOD_CALL:
+ string name, old_owner, new_owner;
+
+ DBus::Hihi hihi(msg);
+ hihi >> name >> old_owner >> new_owner;
+
+ if (name == new_owner && old_owner.empty())
{
- y2mil("method call sender:'" << msg.get_sender() << "' path:'" <<
- msg.get_path() << "' interface:'" << msg.get_interface() <<
- "' member:'" << msg.get_member() << "'");
-
- if (msg.is_method_call(DBUS_INTERFACE_INTROSPECTABLE, "Introspect"))
- {
- Commands::introspect(conn, msg);
- break;
- }
-
- Clients::iterator client = clients.find(msg.get_sender());
- if (client == clients.end())
- {
- y2mil("client connected invisible '" << msg.get_sender() << "'");
- client = client_connected(msg.get_sender());
- }
-
- client->add_task(conn, msg);
+ y2mil("client connected '" << name << "'");
+ client_connected(name);
+ s->reset_idle_count();
+ s->set_idle_timeout(-1);
}
- break;
- case DBUS_MESSAGE_TYPE_SIGNAL:
+ if (name == old_owner && new_owner.empty())
{
- y2mil("signal sender:'" << msg.get_sender() << "' path:'" <<
- msg.get_path() << "' interface:'" << msg.get_interface() <<
- "' member:'" << msg.get_member() << "'");
-
- if (msg.is_signal(DBUS_INTERFACE_DBUS, "NameOwnerChanged"))
- {
- string name, old_owner, new_owner;
-
- DBus::Hihi hihi(msg);
- hihi >> name >> old_owner >> new_owner;
-
- if (name == new_owner && old_owner.empty())
- {
- y2mil("client connected '" << name << "'");
- client_connected(name);
- }
-
- if (name == old_owner && new_owner.empty())
- {
- y2mil("client disconnected '" << name << "'");
- client_disconnected(name);
- }
- }
+ y2mil("client disconnected '" << name << "'");
+ client_disconnected(name);
+ if (clients.empty())
+ s->set_idle_timeout(30);
}
- break;
- }
- idle = 0;
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
}
- y2mil("Idle - exiting");
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
+DBusObjectPathVTable dbus_vtable1 = {
+ unregister_func, message_func1, NULL, NULL, NULL, NULL
+};
+
+DBusObjectPathVTable dbus_vtable2 = {
+ unregister_func, message_func2, NULL, NULL, NULL, NULL
+};
+
+
void
log_do(LogLevel level, const string& component, const char* file, const int line, const char* func,
const string& text)
{
- cerr << text << endl;
+ cout << /* boost::this_thread::get_id() << " " << */ text << endl;
}
setLogDo(&log_do);
#endif
- DBus::Connection conn(DBUS_BUS_SYSTEM);
+ dbus_threads_init_default();
+
+ DBus::Server server(DBUS_BUS_SYSTEM);
+
+ server.set_idle_timeout(30);
+
+ y2mil("Requesting DBus name");
+
+ server.request_name("org.opensuse.Snapper", DBUS_NAME_FLAG_REPLACE_EXISTING);
+
+ y2mil("Listening for method calls and signals");
+
+ server.register_object_path(PATH, &dbus_vtable1, &server);
+ server.register_object_path(DBUS_PATH_DBUS, &dbus_vtable2, &server);
+ server.add_match("type='signal', interface='" DBUS_INTERFACE_DBUS "', member='NameOwnerChanged'");
+
+ server.run();
- listen(conn);
+ y2mil("Exiting");
return 0;
}
#include <sys/types.h>
#include <libxml/tree.h>
#include <string>
+#include <boost/thread.hpp>
#include "config.h"
string prefix = sformat("%s %s libsnapper(%d) %s(%s):%d", datetime(time(0), false, true).c_str(),
ln[level], getpid(), file, func, line);
+ static boost::mutex mutex;
+
+ boost::lock_guard<boost::mutex> lock(mutex);
+
FILE* f = fopen(filename.c_str(), "a");
if (f)
{