1 /**
2  * Copyright © 2016 IBM Corporation
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include "config.h"
17 #include <stdlib.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <sys/timerfd.h>
23 #include <systemd/sd-bus.h>
24 #include <systemd/sd-event.h>
25 #include "mapper.h"
26 
27 static const char *async_wait_introspection_match =
28 	"type='signal',"
29 	"sender='xyz.openbmc_project.ObjectMapper',"
30 	"interface='xyz.openbmc_project.ObjectMapper.Private',"
31 	"member='IntrospectionComplete'";
32 
33 static const char *async_wait_interfaces_added_match =
34 	"type='signal',"
35 	"interface='org.freedesktop.DBus.ObjectManager',"
36 	"member='InterfacesAdded'";
37 
38 static const char *interfaces_removed_match =
39 	"type='signal',"
40 	"interface='org.freedesktop.DBus.ObjectManager',"
41 	"member='InterfacesRemoved'";
42 
43 static const int mapper_busy_retries = 5;
44 static const uint64_t mapper_busy_delay_interval_usec = 1000000;
45 
46 struct mapper_async_wait
47 {
48 	char **objs;
49 	void (*callback)(int, void *);
50 	void *userdata;
51 	sd_event *loop;
52 	sd_bus *conn;
53 	sd_bus_slot *introspection_slot;
54 	sd_bus_slot *intf_slot;
55 	int *status;
56 	int count;
57 	int finished;
58 	int r;
59 };
60 
61 struct async_wait_callback_data
62 {
63 	mapper_async_wait *wait;
64 	const char *path;
65 	sd_event_source *event_source;
66 	int retry;
67 };
68 
69 struct mapper_async_subtree
70 {
71 	char *namespace;
72 	char *interface;
73 	void (*callback)(int, void *);
74 	void *userdata;
75 	sd_event *loop;
76 	sd_bus *conn;
77 	sd_bus_slot *slot;
78 	int op;
79 };
80 
81 static int async_wait_match_introspection_complete(sd_bus_message *, void *,
82 		sd_bus_error *);
83 static int async_wait_check_done(mapper_async_wait *);
84 static void async_wait_done(int r, mapper_async_wait *);
85 static int async_wait_get_objects(mapper_async_wait *);
86 static int async_wait_getobject_callback(sd_bus_message *,
87 		void *, sd_bus_error *);
88 
89 static int async_subtree_match_callback(sd_bus_message *, void *,
90 		sd_bus_error *);
91 
92 static int sarraylen(char *array[])
93 {
94 	int count = 0;
95 	char **p = array;
96 
97 	while(*p != NULL) {
98 		++count;
99 		++p;
100 	}
101 
102 	return count;
103 }
104 
105 static void sarrayfree(char *array[])
106 {
107 	char **p = array;
108 	while(*p != NULL) {
109 		free(*p);
110 		++p;
111 	}
112 	free(array);
113 }
114 
115 static char **sarraydup(char *array[])
116 {
117 	int count = sarraylen(array);
118 	int i;
119 	char **ret = NULL;
120 
121 	ret = malloc(sizeof(*ret) * count);
122 	if(!ret)
123 		return NULL;
124 
125 	for(i=0; i<count; ++i) {
126 		ret[i] = strdup(array[i]);
127 		if(!ret[i])
128 			goto error;
129 	}
130 
131 	return ret;
132 
133 error:
134 	sarrayfree(ret);
135 	return NULL;
136 }
137 
138 static int async_wait_timeout_callback(sd_event_source *s,
139 		uint64_t usec, void *userdata)
140 {
141 	int r;
142 	struct async_wait_callback_data *data = userdata;
143 	mapper_async_wait *wait = data->wait;
144 
145 	sd_event_source_unref(data->event_source);
146 	r = sd_bus_call_method_async(
147 			wait->conn,
148 			NULL,
149 			MAPPER_BUSNAME,
150 			MAPPER_PATH,
151 			MAPPER_INTERFACE,
152 			"GetObject",
153 			async_wait_getobject_callback,
154 			data,
155 			"sas",
156 			data->path,
157 			0,
158 			NULL);
159 	if(r < 0) {
160 		async_wait_done(r, wait);
161 		free(data);
162 	}
163 
164 	return 0;
165 }
166 
167 static int async_wait_getobject_callback(sd_bus_message *m,
168 		void *userdata,
169 		sd_bus_error *e)
170 {
171 	int i, r;
172 	struct async_wait_callback_data *data = userdata;
173 	mapper_async_wait *wait = data->wait;
174 	uint64_t now;
175 
176 	if(wait->finished)
177 		goto exit;
178 
179 	r = sd_bus_message_get_errno(m);
180 	if(r == ENOENT)
181 		goto exit;
182 
183 	if(r == EBUSY && data->retry < mapper_busy_retries) {
184 		r = sd_event_now(wait->loop,
185 				CLOCK_MONOTONIC,
186 				&now);
187 		if(r < 0) {
188 			async_wait_done(r, wait);
189 			goto exit;
190 		}
191 
192 		++data->retry;
193 		r = sd_event_add_time(wait->loop,
194 				&data->event_source,
195 				CLOCK_MONOTONIC,
196 				now + mapper_busy_delay_interval_usec,
197 				0,
198 				async_wait_timeout_callback,
199 				data);
200 		if(r < 0) {
201 			async_wait_done(r, wait);
202 			goto exit;
203 		}
204 
205 		return 0;
206 	}
207 
208 	if(r) {
209 		async_wait_done(-r, wait);
210 		goto exit;
211 	}
212 
213 	for(i=0; i<wait->count; ++i) {
214 		if(!strcmp(data->path, wait->objs[i])) {
215 			wait->status[i] = 1;
216 		}
217 	}
218 
219 	if(async_wait_check_done(wait))
220 		async_wait_done(0, wait);
221 
222 exit:
223 	free(data);
224 	return 0;
225 }
226 
227 static int async_wait_get_objects(mapper_async_wait *wait)
228 {
229 	int i, r;
230 	struct async_wait_callback_data *data = NULL;
231 
232 	for(i=0; i<wait->count; ++i) {
233 		if(wait->status[i])
234 			continue;
235 		data = malloc(sizeof(*data));
236 		data->wait = wait;
237 		data->path = wait->objs[i];
238 		data->retry = 0;
239 		data->event_source = NULL;
240 		r = sd_bus_call_method_async(
241 				wait->conn,
242 				NULL,
243 				MAPPER_BUSNAME,
244 				MAPPER_PATH,
245 				MAPPER_INTERFACE,
246 				"GetObject",
247 				async_wait_getobject_callback,
248 				data,
249 				"sas",
250 				wait->objs[i],
251 				0,
252 				NULL);
253 		if(r < 0) {
254 			free(data);
255 			fprintf(stderr, "Error invoking method: %s\n",
256 					strerror(-r));
257 			return r;
258 		}
259 	}
260 
261 	return 0;
262 }
263 
264 static int async_wait_match_introspection_complete(sd_bus_message *m, void *w,
265 		sd_bus_error *e)
266 {
267 	int r;
268 
269 	mapper_async_wait *wait = w;
270 	if(wait->finished)
271 		return 0;
272 
273 	r = async_wait_get_objects(wait);
274 	if(r < 0)
275 		async_wait_done(r, wait);
276 
277 	return 0;
278 }
279 
280 static void async_wait_done(int r, mapper_async_wait *w)
281 {
282 	if(w->finished)
283 		return;
284 
285 	w->finished = 1;
286 	sd_bus_slot_unref(w->introspection_slot);
287 	sd_bus_slot_unref(w->intf_slot);
288 
289 	if(w->callback)
290 		w->callback(r, w->userdata);
291 }
292 
293 static int async_wait_check_done(mapper_async_wait *w)
294 {
295 	int i;
296 
297 	if(w->finished)
298 		return 1;
299 
300 	for(i=0; i<w->count; ++i)
301 		if(!w->status[i])
302 			return 0;
303 
304 	return 1;
305 }
306 
307 void mapper_wait_async_free(mapper_async_wait *w)
308 {
309 	free(w->status);
310 	sarrayfree(w->objs);
311 	free(w);
312 }
313 
314 int mapper_wait_async(sd_bus *conn,
315 		sd_event *loop,
316 		char *objs[],
317 		void (*callback)(int, void *),
318 		void *userdata,
319 		mapper_async_wait **w)
320 {
321 	int r;
322 	mapper_async_wait *wait = NULL;
323 
324 	wait = malloc(sizeof(*wait));
325 	if(!wait)
326 		return -ENOMEM;
327 
328 	memset(wait, 0, sizeof(*wait));
329 	wait->conn = conn;
330 	wait->loop = loop;
331 	wait->callback = callback;
332 	wait->userdata = userdata;
333 	wait->count = sarraylen(objs);
334 	if(!wait->count)
335 		return 0;
336 
337 	wait->objs = sarraydup(objs);
338 	if(!wait->objs) {
339 		r = -ENOMEM;
340 		goto free_wait;
341 	}
342 
343 	wait->status = malloc(sizeof(*wait->status) * wait->count);
344 	if(!wait->status) {
345 		r = -ENOMEM;
346 		goto free_objs;
347 	}
348 	memset(wait->status, 0, sizeof(*wait->status) * wait->count);
349 
350 	r = sd_bus_add_match(conn,
351 			&wait->introspection_slot,
352 			async_wait_introspection_match,
353 			async_wait_match_introspection_complete,
354 			wait);
355 	if(r < 0) {
356 		fprintf(stderr, "Error adding match rule: %s\n",
357 				strerror(-r));
358 		goto free_status;
359 	}
360 
361 	r = sd_bus_add_match(conn,
362                         &wait->intf_slot,
363 			async_wait_interfaces_added_match,
364                         async_wait_match_introspection_complete,
365                         wait);
366 	if(r < 0) {
367 		fprintf(stderr, "Error adding match rule: %s\n",
368 				strerror(-r));
369 		goto unref_name_slot;
370 	}
371 
372 	r = async_wait_get_objects(wait);
373 	if(r < 0) {
374 		fprintf(stderr, "Error calling method: %s\n",
375 				strerror(-r));
376 		goto unref_intf_slot;
377 	}
378 
379 	*w = wait;
380 
381 	return 0;
382 
383 unref_intf_slot:
384 	sd_bus_slot_unref(wait->intf_slot);
385 unref_name_slot:
386 	sd_bus_slot_unref(wait->introspection_slot);
387 free_status:
388 	free(wait->status);
389 free_objs:
390 	sarrayfree(wait->objs);
391 free_wait:
392 	free(wait);
393 
394 	return r;
395 }
396 
397 static int async_subtree_match_callback(sd_bus_message *m,
398 		void *t,
399 		sd_bus_error *e)
400 {
401 	return 0;
402 }
403 
404 int mapper_subtree_async(sd_bus *conn,
405 		sd_event *loop,
406 		char *namespace,
407 		char *interface,
408 		void (*callback)(int, void *),
409 		void *userdata,
410 		mapper_async_subtree **t,
411 		int op)
412 {
413 	int r = 0;
414 	mapper_async_subtree *subtree = NULL;
415 
416 	subtree = malloc(sizeof(*subtree));
417 	if(!subtree)
418 		return -ENOMEM;
419 
420 	memset(subtree, 0, sizeof(*subtree));
421 	subtree->conn = conn;
422 	subtree->loop = loop;
423 	subtree->namespace = namespace;
424 	subtree->interface = interface;
425 	subtree->callback = callback;
426 	subtree->userdata = userdata;
427 	subtree->op = op;
428 
429 	if (subtree->op == MAPPER_OP_REMOVE) {
430 		r = sd_bus_add_match(
431 				conn,
432 				&subtree->slot,
433 				interfaces_removed_match,
434 				async_subtree_match_callback,
435 				subtree);
436 		if(r < 0) {
437 			fprintf(stderr, "Error adding match rule: %s\n",
438 					strerror(-r));
439 			goto unref_slot;
440 		}
441 	} else {
442 		/* Operation not supported */
443 		r = -EINVAL;
444 		goto free_subtree;
445 	}
446 
447 	*t = subtree;
448 
449 	return 0;
450 
451 unref_slot:
452 	sd_bus_slot_unref(subtree->slot);
453 free_subtree:
454 	free(subtree);
455 
456 	return r;
457 }
458 
459 int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply)
460 {
461 	sd_bus_error error = SD_BUS_ERROR_NULL;
462 	sd_bus_message *request = NULL;
463 	int r, retry = 0;
464 
465 	r = sd_bus_message_new_method_call(
466 			conn,
467 			&request,
468 			MAPPER_BUSNAME,
469 			MAPPER_PATH,
470 			MAPPER_INTERFACE,
471 			"GetObject");
472 	if (r < 0)
473 		goto exit;
474 
475 	r = sd_bus_message_append(request, "s", obj);
476 	if (r < 0)
477 		goto exit;
478 	r = sd_bus_message_append(request, "as", 0, NULL);
479 	if (r < 0)
480 		goto exit;
481 
482 	while(retry < mapper_busy_retries) {
483 		sd_bus_error_free(&error);
484 		r = sd_bus_call(conn, request, 0, &error, reply);
485 		if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) {
486 			++retry;
487 
488 			if(retry != mapper_busy_retries)
489 				usleep(mapper_busy_delay_interval_usec);
490 			continue;
491 		}
492 		break;
493 	}
494 
495 	if (r < 0)
496 		goto exit;
497 
498 exit:
499 	sd_bus_error_free(&error);
500 	sd_bus_message_unref(request);
501 
502 	return r;
503 }
504 
505 int mapper_get_service(sd_bus *conn, const char *obj, char **service)
506 {
507 	sd_bus_message *reply = NULL;
508 	const char *tmp;
509 	int r;
510 
511 	r = mapper_get_object(conn, obj, &reply);
512 	if (r < 0)
513 		goto exit;
514 
515 	r = sd_bus_message_enter_container(reply, 0, NULL);
516 	if (r < 0)
517 		goto exit;
518 
519 	r = sd_bus_message_enter_container(reply, 0, NULL);
520 	if (r < 0)
521 		goto exit;
522 
523 	r = sd_bus_message_read(reply, "s", &tmp);
524 	if (r < 0)
525 		goto exit;
526 
527 	*service = strdup(tmp);
528 
529 exit:
530 	sd_bus_message_unref(reply);
531 
532 	return r;
533 }
534