1 #ifndef DBUS_CONNECTION_SERVICE_HPP 2 #define DBUS_CONNECTION_SERVICE_HPP 3 4 #include <string> 5 #include <boost/asio.hpp> 6 7 #include <dbus/message.hpp> 8 #include <dbus/detail/watch_timeout.hpp> 9 #include <dbus/detail/queue.hpp> 10 #include <dbus/match.hpp> 11 12 namespace dbus { 13 14 namespace bus { 15 static const int session = DBUS_BUS_SESSION; 16 static const int system = DBUS_BUS_SYSTEM; 17 static const int starter = DBUS_BUS_STARTER; 18 } // namespace bus 19 20 using std::string; 21 using namespace boost::asio; 22 23 class connection_service 24 : public io_service::service 25 { 26 public: 27 static io_service::id id; 28 29 typedef DBusConnection *implementation_type; 30 31 explicit connection_service(io_service& io) 32 : service(io) 33 { 34 } 35 36 void construct(implementation_type& impl) 37 { 38 } 39 40 void destroy(implementation_type& impl) 41 { 42 dbus_connection_unref(impl); 43 } 44 45 void shutdown_service() 46 { 47 //TODO is there anything that needs shutting down? 48 } 49 50 void open(implementation_type& impl, 51 const string& address, bool shared=true) 52 { 53 io_service& io = this->get_io_service(); 54 55 DBusError error; 56 dbus_error_init(&error); 57 impl = dbus_connection_open(address.c_str(), &error); 58 //TODO actually deal with that error 59 60 detail::set_watch_timeout_dispatch_functions(impl, io); 61 } 62 63 void open(implementation_type& impl, 64 const int bus = (int) DBUS_BUS_SYSTEM, 65 bool shared=true) 66 { 67 io_service& io = this->get_io_service(); 68 69 DBusError error; 70 dbus_error_init(&error); 71 impl = dbus_bus_get((DBusBusType)bus, &error); 72 //TODO actually deal with that error 73 74 detail::set_watch_timeout_dispatch_functions(impl, io); 75 } 76 77 message send(implementation_type& impl, 78 message& m) 79 { 80 DBusError error; 81 dbus_error_init(&error); 82 return message(dbus_connection_send_with_reply_and_block(impl, 83 m, -1, &error)); 84 //TODO deal with that error 85 } 86 87 template <typename Duration> 88 message send(implementation_type& impl, 89 message& m, 90 const Duration& timeout) 91 { 92 //TODO generically convert timeout to milliseconds 93 if(timeout == Duration::zero()) { 94 //TODO this can return false if it failed 95 dbus_connection_send(impl, m, &m.serial); 96 return message(); 97 } else { 98 DBusError error; 99 dbus_error_init(&error); 100 return message(dbus_connection_send_with_reply_and_block(impl, 101 m, chrono::milliseconds(timeout).count(), &error)); 102 //TODO deal with that error 103 } 104 } 105 106 template<typename MessageHandler> 107 inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler, 108 void(boost::system::error_code, message)) 109 async_send(implementation_type& impl, 110 message& m, 111 BOOST_ASIO_MOVE_ARG(MessageHandler) handler) 112 { 113 DBusPendingCall *p; 114 dbus_connection_send_with_reply(impl, 115 m, &p, -1); 116 /* 117 dbus_pending_call_set_notify(p, 118 &pending_call_notify, &get_io_service(), NULL); 119 120 //FIXME Race condition: another thread might have 121 // processed the pending call's reply before a notify 122 // function could be set. If so, the notify function 123 // will never trigger, so it must be called manually: 124 if(dbus_pending_call_get_completed(p)) 125 { 126 //TODO: does this work, or might it call the notify 127 // function too many times? Might have to use steal_reply 128 pending_call_notify(p, &get_io_service()); 129 } 130 */ 131 } 132 133 void new_match(implementation_type& impl, 134 match& m) 135 { 136 DBusError err; 137 dbus_error_init(&err); 138 dbus_bus_add_match(impl, m.get_expression().c_str(), &err); 139 //TODO deal with that error 140 // eventually, for complete asynchronicity, this should connect to 141 // org.freedesktop.DBus and call org.freedesktop.DBus.AddMatch 142 } 143 144 void delete_match(implementation_type& impl, 145 match& m) 146 { 147 DBusError err; 148 dbus_error_init(&err); 149 dbus_bus_remove_match(impl, m.get_expression().c_str(), &err); 150 } 151 152 static DBusHandlerResult filter_wrap( 153 DBusConnection *c, 154 DBusMessage *m, 155 void *userdata) 156 { 157 try 158 { 159 filter& f = *static_cast<filter *>(userdata); 160 if(f.offer(message(m))) 161 { 162 return DBUS_HANDLER_RESULT_HANDLED; 163 } 164 } catch(...) { 165 // do not throw in C callbacks. Just don't. 166 } 167 168 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 169 } 170 171 void new_filter(implementation_type& impl, 172 filter& f) 173 { 174 dbus_connection_add_filter(impl, 175 &filter_wrap, &f, NULL); 176 } 177 178 void delete_filter(implementation_type& impl, 179 filter& f) 180 { 181 dbus_connection_remove_filter(impl, 182 &filter_wrap, &f); 183 } 184 }; 185 186 io_service::id connection_service::id; 187 188 189 190 191 } // namespace dbus 192 193 #endif // DBUS_CONNECTION_SERVICE_HPP 194