]> git.ipfire.org Git - thirdparty/snapper.git/commitdiff
- work on dbus interface
authorArvin Schnell <aschnell@suse.de>
Fri, 27 Jul 2012 20:20:34 +0000 (22:20 +0200)
committerArvin Schnell <aschnell@suse.de>
Fri, 27 Jul 2012 20:20:34 +0000 (22:20 +0200)
client/types.h
dbus/DBusConnection.cc
dbus/DBusConnection.h
dbus/DBusMessage.cc
dbus/DBusMessage.h
dbus/DBusServer.cc [new file with mode: 0644]
dbus/DBusServer.h [new file with mode: 0644]
dbus/Makefile.am
server/Makefile.am
server/snapperd.cc
snapper/Logger.cc

index 033626f77ee870b647a16bf9803972142288759d..da3f7ad17957ae9b6ee52319959871cd8843e9f8 100644 (file)
@@ -28,8 +28,8 @@ using std::string;
 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"
 
index 49651dc9337223400d3e8e8679840ff3199ba250..1d101c3bc744f35dcdad9438f595b5a89af37fa2 100644 (file)
@@ -58,8 +58,6 @@ namespace DBus
     void
     Connection::request_name(const char* name, unsigned int flags)
     {
-       boost::lock_guard<boost::mutex> lock(mutex);
-
        DBusError err;
        dbus_error_init(&err);
 
@@ -77,20 +75,9 @@ namespace DBus
     }
 
 
-    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();
@@ -101,8 +88,6 @@ namespace DBus
     Message
     Connection::send_with_reply_and_block(Message& m)
     {
-       boost::lock_guard<boost::mutex> lock(mutex);
-
        DBusError err;
        dbus_error_init(&err);
 
@@ -121,8 +106,6 @@ namespace DBus
     void
     Connection::add_match(const char* rule)
     {
-       boost::lock_guard<boost::mutex> lock(mutex);
-
        DBusError err;
        dbus_error_init(&err);
 
@@ -136,11 +119,18 @@ namespace DBus
     }
 
 
+    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())
        {
index 0f77a3030ccfdb7cd7375d04bbf199a852406f84..80d6cac6b302530718543561ac58ef4369efc73f 100644 (file)
  */
 
 
-#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"
 
@@ -46,19 +45,18 @@ namespace DBus
 
        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;
index 2141b6195a43e09776d076e783cb0410140e06ac..efb75837302e1a35bf5f216a03a118ac24610211 100644 (file)
@@ -33,6 +33,7 @@ namespace DBus
        : msg(m)
     {
        assert(msg);
+       dbus_message_ref(msg);
     }
 
 
index c5aa0792172eae03477c00951942299c7967920e..c4f82fa92319c9f404ef5024b10e59e3c5574fb2 100644 (file)
@@ -20,8 +20,8 @@
  */
 
 
-#ifndef SNAPPER_DBUSMSG_H
-#define SNAPPER_DBUSMSG_H
+#ifndef SNAPPER_DBUSMESSAGE_H
+#define SNAPPER_DBUSMESSAGE_H
 
 
 #include <dbus/dbus.h>
diff --git a/dbus/DBusServer.cc b/dbus/DBusServer.cc
new file mode 100644 (file)
index 0000000..840af5f
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * 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);
+    }
+
+}
diff --git a/dbus/DBusServer.h b/dbus/DBusServer.h
new file mode 100644 (file)
index 0000000..31cf9fc
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * 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
index 2eb389646d606140560914e42abfcea89c23f312..f67887c90887f5a64d423b402fe1deb5307c492f 100644 (file)
@@ -7,6 +7,6 @@ INCLUDES = -I$(top_srcdir) -I/usr/include/dbus-1.0 -I/usr/lib/dbus-1.0/include
 noinst_LTLIBRARIES = libdbus.la
 
 libdbus_la_SOURCES =                                   \
+       DBusMessage.cc          DBusMessage.h           \
        DBusConnection.cc       DBusConnection.h        \
-       DBusMessage.cc          DBusMessage.h
-
+       DBusServer.cc           DBusServer.h
index 18c99a438212b35b31fdca0947d5a8c92546909a..e21853157219b612715d8d933860d04573a62834 100644 (file)
@@ -14,5 +14,5 @@ snapperd_SOURCES =                                    \
        Types.cc                Types.h
 
 snapperd_LDADD = ../snapper/libsnapper.la ../dbus/libdbus.la   \
-       -ldbus-1 -lboost_thread
+       -ldbus-1 -lboost_thread -lrt
 
index 3077cea4cbe8defda6d400e11f46b9b1165a01de..0fc2bb2f275f24eda0d457e7cdd22e8703927f1d 100644 (file)
@@ -37,8 +37,9 @@
 #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"
@@ -1193,99 +1194,108 @@ Commands::dispatch(DBus::Connection& conn, DBus::Message& msg)
 
 
 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;
 }
 
 
@@ -1298,9 +1308,25 @@ main(int argc, char** argv)
     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;
 }
index fbbff3d07586646fdb7738494afd7c90bea812c3..a6b6e2a7104f2f962ed1de12eabac22c8e9d45ec 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/types.h>
 #include <libxml/tree.h>
 #include <string>
+#include <boost/thread.hpp>
 
 #include "config.h"
 
@@ -66,6 +67,10 @@ namespace snapper
        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)
        {