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_CONNECTION_HPP 7 #define DBUS_CONNECTION_HPP 8 9 #include <dbus/connection_service.hpp> 10 #include <dbus/element.hpp> 11 #include <dbus/message.hpp> 12 #include <chrono> 13 #include <string> 14 #include <boost/asio.hpp> 15 16 namespace dbus { 17 18 class filter; 19 class match; 20 21 /// Root D-Bus IO object 22 /** 23 * A connection to a bus, through which messages may be sent or received. 24 */ 25 class connection : public boost::asio::basic_io_object<connection_service> { 26 public: 27 /// Open a connection to a specified address. 28 /** 29 * @param io_service The io_service object that the connection will use to 30 * wire D-Bus for asynchronous operation. 31 * 32 * @param address The address of the bus to connect to. 33 * 34 * @throws boost::system::system_error When opening the connection failed. 35 */ connection(boost::asio::io_service & io,const string & address)36 connection(boost::asio::io_service& io, const string& address) 37 : basic_io_object<connection_service>(io) { 38 this->get_service().open(this->get_implementation(), address); 39 } 40 41 /// Open a connection to a well-known bus. 42 /** 43 * D-Bus connections are usually opened to well-known buses like the 44 * system or session bus. 45 * 46 * @param bus The well-known bus to connect to. 47 * 48 * @throws boost::system::system_error When opening the connection failed. 49 */ 50 // TODO: change this unsigned to an enumeration connection(boost::asio::io_service & io,const int bus)51 connection(boost::asio::io_service& io, const int bus) 52 : basic_io_object<connection_service>(io) { 53 this->get_service().open(this->get_implementation(), bus); 54 } 55 56 /// Request a name on the bus. 57 /** 58 * @param name The name requested on the bus 59 * 60 * @return 61 * 62 * @throws boost::system::system_error When the response timed out or 63 * there was some other error. 64 */ request_name(const string & name)65 void request_name(const string& name) { 66 this->get_implementation().request_name(name); 67 } 68 get_unique_name()69 std::string get_unique_name() { 70 return this->get_implementation().get_unique_name(); 71 } 72 73 /// Reply to a message. 74 /** 75 * @param m The message from which to create the reply 76 * 77 * @return The new reply message 78 * 79 * @throws boost::system::system_error When the response timed out or 80 * there was some other error. 81 */ reply(message & m)82 message reply(message& m) { 83 return this->get_implementation().new_method_return(m); 84 } 85 86 /// Send a message. 87 /** 88 * @param m The message to send. 89 * 90 * @return The reply received. 91 * 92 * @throws boost::system::system_error When the response timed out or 93 * there was some other error. 94 */ send(message & m)95 message send(message& m) { 96 return this->get_service().send(this->get_implementation(), m); 97 } 98 99 /// Send a message. 100 /** 101 * @param m The message to send. 102 * 103 * @param t Time to wait for a reply. Passing 0 as the timeout means 104 * that you wish to ignore the reply. (Or catch it later somehow...) 105 * 106 * @return The reply received. 107 * 108 * @throws boost::system::system_error When the response timed out (if 109 * timeout was not 0), or there was some other error. 110 */ 111 template <typename Duration> send(message & m,const Duration & t)112 message send(message& m, const Duration& t) { 113 return this->get_service().send(this->get_implementation(), m, t); 114 } 115 116 template <typename... InputArgs> method_call(const dbus::endpoint & e,const InputArgs &...a)117 message method_call(const dbus::endpoint& e, const InputArgs&... a) { 118 message m = dbus::message::new_call(e); 119 m.pack(a...); 120 return this->get_service().send(this->get_implementation(), m, 121 std::chrono::seconds(30)); 122 } 123 124 /// Send a message asynchronously. 125 /** 126 * @param m The message to send. 127 * 128 * @param handler Handler for the reply. 129 * 130 * @return Asynchronous result 131 */ 132 133 template <typename MessageHandler> BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler,void (boost::system::error_code,message))134 inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler, 135 void(boost::system::error_code, message)) 136 async_send(message& m, BOOST_ASIO_MOVE_ARG(MessageHandler) handler) { 137 return this->get_service().async_send( 138 this->get_implementation(), m, 139 BOOST_ASIO_MOVE_CAST(MessageHandler)(handler)); 140 } 141 142 // Small helper class for stipping off the error code from the function 143 // agrument definitions so unpack can be called appriately 144 template <typename T> 145 struct strip_first_arg {}; 146 147 template <typename FirstArg, typename... Rest> 148 struct strip_first_arg<std::tuple<FirstArg, Rest...>> { 149 typedef std::tuple<Rest...> type; 150 }; 151 152 template <typename MessageHandler, typename... InputArgs> async_method_call(MessageHandler handler,const dbus::endpoint & e,const InputArgs &...a)153 auto async_method_call(MessageHandler handler, const dbus::endpoint& e, 154 const InputArgs&... a) { 155 message m = dbus::message::new_call(e); 156 if (!m.pack(a...)) { 157 // TODO(ed) Set error code? 158 std::cerr << "Pack error\n"; 159 } else { 160 // explicit copy of handler here. At this time, not clear why a copy is 161 // needed 162 return async_send( 163 m, [handler](boost::system::error_code ec, dbus::message r) { 164 // Make a copy of the error code so we can modify it 165 typedef typename function_traits<MessageHandler>::decayed_arg_types 166 function_tuple; 167 typedef typename strip_first_arg<function_tuple>::type unpack_type; 168 unpack_type response_args; 169 if (!ec) { 170 if (!unpack_into_tuple(response_args, r)) { 171 // Set error code 172 ec = boost::system::errc::make_error_code( 173 boost::system::errc::invalid_argument); 174 } 175 } 176 // Should this (the callback) be done in a try catch block? 177 // should throwing from a handler flow all the way to the 178 // io_service? 179 180 // Note. Callback is called whether or not the unpack was sucessful 181 // to allow the user to implement their own handling 182 index_apply<std::tuple_size<unpack_type>{}>([&](auto... Is) { 183 handler(ec, std::get<Is>(response_args)...); 184 }); 185 }); 186 } 187 } 188 flush(void)189 void flush(void) { this->get_implementation().flush(); }; 190 191 /// Create a new match. new_match(match & m)192 void new_match(match& m) { 193 this->get_service().new_match(this->get_implementation(), m); 194 } 195 196 /// Destroy a match. delete_match(match & m)197 void delete_match(match& m) { 198 this->get_service().delete_match(this->get_implementation(), m); 199 } 200 201 /// Create a new filter. new_filter(filter & f)202 void new_filter(filter& f) { 203 this->get_service().new_filter(this->get_implementation(), f); 204 } 205 206 /// Destroy a filter. delete_filter(filter & f)207 void delete_filter(filter& f) { 208 this->get_service().delete_filter(this->get_implementation(), f); 209 } 210 211 // FIXME the only way around this I see is to expose start() here, which seems 212 // ugly 213 friend class filter; 214 }; 215 216 typedef std::shared_ptr<connection> connection_ptr; 217 218 } // namespace dbus 219 220 #endif // DBUS_CONNECTION_HPP 221