Line data Source code
1 : /*
2 : Authors:
3 : Pavel Březina <pbrezina@redhat.com>
4 :
5 : Copyright (C) 2015 Red Hat
6 :
7 : This program is free software; you can redistribute it and/or modify
8 : it under the terms of the GNU General Public License as published by
9 : the Free Software Foundation; either version 3 of the License, or
10 : (at your option) any later version.
11 :
12 : This program is distributed in the hope that it will be useful,
13 : but WITHOUT ANY WARRANTY; without even the implied warranty of
14 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 : GNU General Public License for more details.
16 :
17 : You should have received a copy of the GNU General Public License
18 : along with this program. If not, see <http://www.gnu.org/licenses/>.
19 : */
20 :
21 : #include <talloc.h>
22 : #include <dbus/dbus.h>
23 : #include <dhash.h>
24 :
25 : #include "util/util.h"
26 : #include "sbus/sssd_dbus.h"
27 : #include "sbus/sssd_dbus_private.h"
28 :
29 0 : static int sbus_incoming_signal_destructor(struct sbus_incoming_signal *a_signal)
30 : {
31 0 : dbus_message_unref(a_signal->message);
32 0 : return 0;
33 : }
34 :
35 : static struct sbus_incoming_signal *
36 0 : sbus_new_incoming_signal(struct sbus_connection *conn,
37 : DBusMessage *message)
38 : {
39 : struct sbus_incoming_signal *a_signal;
40 :
41 0 : a_signal = talloc_zero(conn, struct sbus_incoming_signal);
42 0 : if (a_signal == NULL) {
43 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Out of memory allocating D-Bus signal\n");
44 0 : return NULL;
45 : }
46 :
47 0 : a_signal->conn = conn;
48 0 : a_signal->message = dbus_message_ref(message);
49 0 : a_signal->interface = dbus_message_get_interface(message);
50 0 : a_signal->signal = dbus_message_get_member(message);
51 0 : a_signal->path = dbus_message_get_path(message);
52 :
53 0 : talloc_set_destructor(a_signal, sbus_incoming_signal_destructor);
54 :
55 0 : return a_signal;
56 : }
57 :
58 : struct sbus_incoming_signal_data {
59 : sbus_incoming_signal_fn handler_fn;
60 : void *handler_data;
61 : };
62 :
63 : errno_t
64 0 : sbus_incoming_signal_hash_init(TALLOC_CTX *mem_ctx,
65 : hash_table_t **_table)
66 : {
67 0 : return sss_hash_create(mem_ctx, 10, _table);
68 : }
69 :
70 : static errno_t
71 0 : sbus_incoming_signal_hash_add(hash_table_t *table,
72 : const char *iface,
73 : const char *a_signal,
74 : sbus_incoming_signal_fn handler_fn,
75 : void *handler_data)
76 : {
77 : TALLOC_CTX *tmp_ctx;
78 : struct sbus_incoming_signal_data *data;
79 : hash_key_t key;
80 : hash_value_t value;
81 : errno_t ret;
82 : bool has_key;
83 : int hret;
84 :
85 0 : tmp_ctx = talloc_new(NULL);
86 0 : if (tmp_ctx == NULL) {
87 0 : return ENOMEM;
88 : }
89 :
90 0 : key.type = HASH_KEY_STRING;
91 0 : key.str = talloc_asprintf(tmp_ctx, "%s.%s", iface, a_signal);
92 0 : if (key.str == NULL) {
93 0 : ret = ENOMEM;
94 0 : goto done;
95 : }
96 :
97 0 : has_key = hash_has_key(table, &key);
98 0 : if (has_key) {
99 0 : ret = EEXIST;
100 0 : goto done;
101 : }
102 :
103 0 : data = talloc_zero(tmp_ctx, struct sbus_incoming_signal_data);
104 0 : if (data == NULL) {
105 0 : ret = ENOMEM;
106 0 : goto done;
107 : }
108 :
109 0 : data->handler_data = handler_data;
110 0 : data->handler_fn = handler_fn;
111 :
112 0 : value.type = HASH_VALUE_PTR;
113 0 : value.ptr = data;
114 :
115 0 : hret = hash_enter(table, &key, &value);
116 0 : if (hret != HASH_SUCCESS) {
117 0 : ret = EIO;
118 0 : goto done;
119 : }
120 :
121 0 : talloc_steal(table, key.str);
122 0 : talloc_steal(table, data);
123 :
124 0 : ret = EOK;
125 :
126 : done:
127 0 : talloc_free(tmp_ctx);
128 0 : return ret;
129 : }
130 :
131 : static struct sbus_incoming_signal_data *
132 0 : sbus_incoming_signal_hash_lookup(hash_table_t *table,
133 : const char *iface,
134 : const char *a_signal)
135 : {
136 : struct sbus_incoming_signal_data *data;
137 : hash_key_t key;
138 : hash_value_t value;
139 : int hret;
140 :
141 0 : key.type = HASH_KEY_STRING;
142 0 : key.str = talloc_asprintf(NULL, "%s.%s", iface, a_signal);
143 0 : if (key.str == NULL) {
144 0 : return NULL;
145 : }
146 :
147 0 : hret = hash_lookup(table, &key, &value);
148 0 : if (hret == HASH_ERROR_KEY_NOT_FOUND) {
149 0 : data = NULL;
150 0 : goto done;
151 0 : } else if (hret != HASH_SUCCESS) {
152 0 : DEBUG(SSSDBG_OP_FAILURE,
153 : "Unable to search hash table: hret=%d\n", hret);
154 0 : data = NULL;
155 0 : goto done;
156 : }
157 :
158 0 : data = talloc_get_type(value.ptr, struct sbus_incoming_signal_data);
159 :
160 : done:
161 0 : talloc_free(key.str);
162 0 : return data;
163 : }
164 :
165 : errno_t
166 0 : sbus_signal_listen(struct sbus_connection *conn,
167 : const char *iface,
168 : const char *a_signal,
169 : sbus_incoming_signal_fn handler_fn,
170 : void *handler_data)
171 : {
172 : TALLOC_CTX *tmp_ctx;
173 : const char *rule;
174 : DBusError error;
175 : errno_t ret;
176 :
177 0 : tmp_ctx = talloc_new(NULL);
178 0 : if (tmp_ctx == NULL) {
179 0 : DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new() failed\n");
180 0 : return ENOMEM;
181 : }
182 :
183 0 : dbus_error_init(&error);
184 :
185 0 : ret = sbus_incoming_signal_hash_add(conn->incoming_signals, iface,
186 : a_signal, handler_fn, handler_data);
187 0 : if (ret != EOK) {
188 0 : DEBUG(SSSDBG_CRIT_FAILURE, "Unable to register signal handler "
189 : "[%d]: %s\n", ret, sss_strerror(ret));
190 0 : goto done;
191 : }
192 :
193 0 : rule = talloc_asprintf(tmp_ctx, "type='signal',interface='%s',member='%s'",
194 : iface, a_signal);
195 0 : if (rule == NULL) {
196 0 : ret = ENOMEM;
197 0 : goto done;
198 : }
199 :
200 0 : dbus_bus_add_match(conn->dbus.conn, rule, &error);
201 0 : if (dbus_error_is_set(&error)) {
202 0 : DEBUG(SSSDBG_CRIT_FAILURE,
203 : "Cannot add D-Bus match rule, cause: %s\n", error.message);
204 0 : ret = EIO;
205 0 : goto done;
206 : }
207 :
208 0 : DEBUG(SSSDBG_TRACE_FUNC, "Listening to signal %s.%s\n", iface, a_signal);
209 :
210 : done:
211 0 : dbus_error_free(&error);
212 0 : talloc_free(tmp_ctx);
213 :
214 0 : return ret;
215 : }
216 :
217 : static void
218 : sbus_signal_handler_got_caller_id(struct tevent_req *req);
219 :
220 : DBusHandlerResult
221 0 : sbus_signal_handler(DBusConnection *dbus_conn,
222 : DBusMessage *message,
223 : void *handler_data)
224 : {
225 : struct tevent_req *req;
226 : struct sbus_connection *conn;
227 : struct sbus_incoming_signal *a_signal;
228 : const char *sender;
229 : int type;
230 :
231 0 : type = dbus_message_get_type(message);
232 0 : if (type != DBUS_MESSAGE_TYPE_SIGNAL) {
233 : /* We ignore other types here. */
234 0 : return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
235 : }
236 :
237 0 : conn = talloc_get_type(handler_data, struct sbus_connection);
238 0 : sender = dbus_message_get_sender(message);
239 :
240 : /* we have a valid handler, create D-Bus request */
241 0 : a_signal = sbus_new_incoming_signal(conn, message);
242 0 : if (a_signal == NULL) {
243 0 : return DBUS_HANDLER_RESULT_NEED_MEMORY;
244 : }
245 :
246 0 : DEBUG(SSSDBG_TRACE_INTERNAL, "Received D-Bus signal %s.%s\n",
247 : a_signal->interface, a_signal->signal);
248 :
249 : /* now get the sender ID */
250 0 : req = sbus_get_sender_id_send(a_signal, conn->ev, conn, sender);
251 0 : if (req == NULL) {
252 0 : talloc_free(a_signal);
253 0 : return DBUS_HANDLER_RESULT_NEED_MEMORY;
254 : }
255 0 : tevent_req_set_callback(req, sbus_signal_handler_got_caller_id, a_signal);
256 :
257 0 : return DBUS_HANDLER_RESULT_HANDLED;
258 : }
259 :
260 : static void
261 0 : sbus_signal_handler_got_caller_id(struct tevent_req *req)
262 : {
263 : struct sbus_incoming_signal_data *signal_data;
264 : struct sbus_incoming_signal *a_signal;
265 : errno_t ret;
266 :
267 0 : a_signal = tevent_req_callback_data(req, struct sbus_incoming_signal);
268 :
269 0 : ret = sbus_get_sender_id_recv(req, &a_signal->client);
270 0 : if (ret == ERR_SBUS_SENDER_BUS) {
271 0 : DEBUG(SSSDBG_TRACE_FUNC, "Got a signal from the bus..\n");
272 0 : } else if (ret != EOK) {
273 0 : DEBUG(SSSDBG_CRIT_FAILURE,
274 : "Failed to resolve caller's ID: %s\n", sss_strerror(ret));
275 0 : goto done;
276 : }
277 :
278 0 : signal_data = sbus_incoming_signal_hash_lookup(
279 0 : a_signal->conn->incoming_signals,
280 : a_signal->interface,
281 : a_signal->signal);
282 0 : if (signal_data == NULL) {
283 0 : DEBUG(SSSDBG_MINOR_FAILURE, "Received signal %s.%s that we are "
284 : "not listening to.\n", a_signal->interface, a_signal->signal);
285 0 : goto done;
286 : }
287 :
288 0 : signal_data->handler_fn(a_signal, signal_data->handler_data);
289 :
290 : done:
291 0 : talloc_free(a_signal);
292 0 : }
|