1 // Copyright (c) Benjamin Kietzman (github.com/bkietz)
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See accompanying
4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5 
6 #ifndef DBUS_WATCH_TIMEOUT_HPP
7 #define DBUS_WATCH_TIMEOUT_HPP
8 
9 #include <dbus/dbus.h>
10 #include <boost/asio/generic/stream_protocol.hpp>
11 #include <boost/asio/steady_timer.hpp>
12 
13 #include <chrono>
14 
15 namespace dbus {
16 namespace detail {
17 
18 static void watch_toggled(DBusWatch *dbus_watch, void *data);
19 struct watch_handler {
20   DBusWatchFlags flags;
21   DBusWatch *dbus_watch;
watch_handlerdbus::detail::watch_handler22   watch_handler(DBusWatchFlags f, DBusWatch *w) : flags(f), dbus_watch(w) {}
operator ()dbus::detail::watch_handler23   void operator()(boost::system::error_code ec, size_t) {
24     if (ec) return;
25     dbus_watch_handle(dbus_watch, flags);
26     auto data = dbus_watch_get_data(dbus_watch);
27     if (data != nullptr) {
28       boost::asio::generic::stream_protocol::socket &socket =
29           *static_cast<boost::asio::generic::stream_protocol::socket *>(data);
30 
31       watch_toggled(dbus_watch, &socket.get_io_service());
32     }
33   }
34 };
watch_toggled(DBusWatch * dbus_watch,void * data)35 static void watch_toggled(DBusWatch *dbus_watch, void *data) {
36   void *watch_data = dbus_watch_get_data(dbus_watch);
37   if (watch_data == nullptr) {
38     return;
39   }
40 
41   auto socket =
42       static_cast<boost::asio::generic::stream_protocol::socket *>(watch_data);
43   if (dbus_watch_get_enabled(dbus_watch)) {
44     if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_READABLE)
45       socket->async_read_some(boost::asio::null_buffers(),
46                               watch_handler(DBUS_WATCH_READABLE, dbus_watch));
47 
48     if (dbus_watch_get_flags(dbus_watch) & DBUS_WATCH_WRITABLE)
49       socket->async_write_some(boost::asio::null_buffers(),
50                                watch_handler(DBUS_WATCH_WRITABLE, dbus_watch));
51 
52   } else {
53     socket->cancel();
54   }
55 }
56 
add_watch(DBusWatch * dbus_watch,void * data)57 static dbus_bool_t add_watch(DBusWatch *dbus_watch, void *data) {
58   if (!dbus_watch_get_enabled(dbus_watch)) {
59     return TRUE;
60   }
61 
62   boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
63 
64   int fd = dbus_watch_get_unix_fd(dbus_watch);
65 
66   if (fd == -1)
67     // socket based watches
68     fd = dbus_watch_get_socket(dbus_watch);
69 
70   boost::asio::generic::stream_protocol::socket &socket =
71       *new boost::asio::generic::stream_protocol::socket(io);
72 
73   socket.assign(boost::asio::generic::stream_protocol(0, 0), fd);
74 
75   dbus_watch_set_data(dbus_watch, &socket, NULL);
76 
77   watch_toggled(dbus_watch, &io);
78   return TRUE;
79 }
80 
remove_watch(DBusWatch * dbus_watch,void * data)81 static void remove_watch(DBusWatch *dbus_watch, void *data) {
82   delete static_cast<boost::asio::generic::stream_protocol::socket *>(
83       dbus_watch_get_data(dbus_watch));
84 }
85 
86 struct timeout_handler {
87   DBusTimeout *dbus_timeout;
timeout_handlerdbus::detail::timeout_handler88   timeout_handler(DBusTimeout *t) : dbus_timeout(t) {}
operator ()dbus::detail::timeout_handler89   void operator()(boost::system::error_code ec) {
90     if (ec) return;
91     dbus_timeout_handle(dbus_timeout);
92   }
93 };
94 
timeout_toggled(DBusTimeout * dbus_timeout,void * data)95 static void timeout_toggled(DBusTimeout *dbus_timeout, void *data) {
96   boost::asio::steady_timer &timer = *static_cast<boost::asio::steady_timer *>(
97       dbus_timeout_get_data(dbus_timeout));
98 
99   if (dbus_timeout_get_enabled(dbus_timeout)) {
100     boost::asio::steady_timer::duration interval =
101         std::chrono::milliseconds(dbus_timeout_get_interval(dbus_timeout));
102     timer.expires_from_now(interval);
103     timer.cancel();
104     timer.async_wait(timeout_handler(dbus_timeout));
105   } else {
106     timer.cancel();
107   }
108 }
109 
add_timeout(DBusTimeout * dbus_timeout,void * data)110 static dbus_bool_t add_timeout(DBusTimeout *dbus_timeout, void *data) {
111   if (!dbus_timeout_get_enabled(dbus_timeout)) return TRUE;
112 
113   boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
114 
115   boost::asio::steady_timer &timer = *new boost::asio::steady_timer(io);
116 
117   dbus_timeout_set_data(dbus_timeout, &timer, NULL);
118 
119   timeout_toggled(dbus_timeout, &io);
120   return TRUE;
121 }
122 
remove_timeout(DBusTimeout * dbus_timeout,void * data)123 static void remove_timeout(DBusTimeout *dbus_timeout, void *data) {
124   delete static_cast<boost::asio::steady_timer *>(
125       dbus_timeout_get_data(dbus_timeout));
126 }
127 
128 struct dispatch_handler {
129   boost::asio::io_service &io;
130   DBusConnection *conn;
dispatch_handlerdbus::detail::dispatch_handler131   dispatch_handler(boost::asio::io_service &i, DBusConnection *c)
132       : io(i), conn(c) {}
operator ()dbus::detail::dispatch_handler133   void operator()() {
134     if (dbus_connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS)
135       io.post(dispatch_handler(io, conn));
136   }
137 };
138 
dispatch_status(DBusConnection * conn,DBusDispatchStatus new_status,void * data)139 static void dispatch_status(DBusConnection *conn, DBusDispatchStatus new_status,
140                             void *data) {
141   boost::asio::io_service &io = *static_cast<boost::asio::io_service *>(data);
142   if (new_status == DBUS_DISPATCH_DATA_REMAINS)
143     io.post(dispatch_handler(io, conn));
144 }
145 
set_watch_timeout_dispatch_functions(DBusConnection * conn,boost::asio::io_service & io)146 static void set_watch_timeout_dispatch_functions(DBusConnection *conn,
147                                                  boost::asio::io_service &io) {
148   dbus_connection_set_watch_functions(conn, &add_watch, &remove_watch,
149                                       &watch_toggled, &io, NULL);
150 
151   dbus_connection_set_timeout_functions(conn, &add_timeout, &remove_timeout,
152                                         &timeout_toggled, &io, NULL);
153 
154   dbus_connection_set_dispatch_status_function(conn, &dispatch_status, &io,
155                                                NULL);
156 }
157 
158 }  // namespace detail
159 }  // namespace dbus
160 
161 #endif  // DBUS_WATCH_TIMEOUT_HPP
162