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 */ 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 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 */ 65 void request_name(const string& name) { 66 this->get_implementation().request_name(name); 67 } 68 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 */ 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 */ 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> 112 message send(message& m, const Duration& t) { 113 return this->get_service().send(this->get_implementation(), m, t); 114 } 115 116 /// Send a message asynchronously. 117 /** 118 * @param m The message to send. 119 * 120 * @param handler Handler for the reply. 121 * 122 * @return Asynchronous result 123 */ 124 125 template <typename MessageHandler> 126 inline BOOST_ASIO_INITFN_RESULT_TYPE(MessageHandler, 127 void(boost::system::error_code, message)) 128 async_send(message& m, BOOST_ASIO_MOVE_ARG(MessageHandler) handler) { 129 return this->get_service().async_send( 130 this->get_implementation(), m, 131 BOOST_ASIO_MOVE_CAST(MessageHandler)(handler)); 132 } 133 134 // Small helper class for stipping off the error code from the function 135 // agrument definitions so unpack can be called appriately 136 template <typename T> 137 struct strip_first_arg {}; 138 139 template <typename FirstArg, typename... Rest> 140 struct strip_first_arg<std::tuple<FirstArg, Rest...>> { 141 typedef std::tuple<Rest...> type; 142 }; 143 144 template <typename MessageHandler, typename... InputArgs> 145 void async_method_call(MessageHandler handler, const dbus::endpoint& e, 146 const InputArgs&... a) { 147 message m = dbus::message::new_call(e); 148 if (!m.pack(a...)) { 149 // TODO(ed) Set error code? 150 151 } else { 152 async_send(m, [&](boost::system::error_code ec, dbus::message r) { 153 // Make a copy of the error code so we can modify it 154 typedef typename function_traits<MessageHandler>::decayed_arg_types 155 function_tuple; 156 typedef typename strip_first_arg<function_tuple>::type unpack_type; 157 unpack_type response_args; 158 if (!ec) { 159 if (!unpack_into_tuple(response_args, r)) { 160 // Set error code 161 ec = boost::system::errc::make_error_code( 162 boost::system::errc::invalid_argument); 163 } 164 } 165 // Should this (the callback) be done in a try catch block? 166 // should throwing from a handler flow all the way to the io_service? 167 168 // Note. Callback is called whether or not the unpack was sucessful 169 // to allow the user to implement their own handling 170 index_apply<std::tuple_size<unpack_type>{}>( 171 [&](auto... Is) { handler(ec, std::get<Is>(response_args)...); }); 172 }); 173 } 174 } 175 176 /// Create a new match. 177 void new_match(match& m) { 178 this->get_service().new_match(this->get_implementation(), m); 179 } 180 181 /// Destroy a match. 182 void delete_match(match& m) { 183 this->get_service().delete_match(this->get_implementation(), m); 184 } 185 186 /// Create a new filter. 187 void new_filter(filter& f) { 188 this->get_service().new_filter(this->get_implementation(), f); 189 } 190 191 /// Destroy a filter. 192 void delete_filter(filter& f) { 193 this->get_service().delete_filter(this->get_implementation(), f); 194 } 195 196 // FIXME the only way around this I see is to expose start() here, which seems 197 // ugly 198 friend class filter; 199 }; 200 201 typedef std::shared_ptr<connection> connection_ptr; 202 203 } // namespace dbus 204 205 #endif // DBUS_CONNECTION_HPP 206