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 	sd_event_source *event_source;
79 	int finished;
80 	int op;
81 	int retry;
82 };
83 
84 static int async_wait_match_introspection_complete(sd_bus_message *, void *,
85 		sd_bus_error *);
86 static int async_wait_check_done(mapper_async_wait *);
87 static void async_wait_done(int r, mapper_async_wait *);
88 static int async_wait_get_objects(mapper_async_wait *);
89 static int async_wait_getobject_callback(sd_bus_message *,
90 		void *, sd_bus_error *);
91 
92 static int async_subtree_match_callback(sd_bus_message *, void *,
93 		sd_bus_error *);
94 static void async_subtree_done(int r, mapper_async_subtree *);
95 static int async_subtree_getpaths(mapper_async_subtree *);
96 static int async_subtree_getpaths_callback(sd_bus_message *,
97 		void *, sd_bus_error *);
98 
99 static int sarraylen(char *array[])
100 {
101 	int count = 0;
102 	char **p = array;
103 
104 	while(*p != NULL) {
105 		++count;
106 		++p;
107 	}
108 
109 	return count;
110 }
111 
112 static void sarrayfree(char *array[])
113 {
114 	char **p = array;
115 	while(*p != NULL) {
116 		free(*p);
117 		++p;
118 	}
119 	free(array);
120 }
121 
122 static char **sarraydup(char *array[])
123 {
124 	int count = sarraylen(array);
125 	int i;
126 	char **ret = NULL;
127 
128 	ret = malloc(sizeof(*ret) * count);
129 	if(!ret)
130 		return NULL;
131 
132 	for(i=0; i<count; ++i) {
133 		ret[i] = strdup(array[i]);
134 		if(!ret[i])
135 			goto error;
136 	}
137 
138 	return ret;
139 
140 error:
141 	sarrayfree(ret);
142 	return NULL;
143 }
144 
145 static int async_wait_timeout_callback(sd_event_source *s,
146 		uint64_t usec, void *userdata)
147 {
148 	int r;
149 	struct async_wait_callback_data *data = userdata;
150 	mapper_async_wait *wait = data->wait;
151 
152 	sd_event_source_unref(data->event_source);
153 	r = sd_bus_call_method_async(
154 			wait->conn,
155 			NULL,
156 			MAPPER_BUSNAME,
157 			MAPPER_PATH,
158 			MAPPER_INTERFACE,
159 			"GetObject",
160 			async_wait_getobject_callback,
161 			data,
162 			"sas",
163 			data->path,
164 			0,
165 			NULL);
166 	if(r < 0) {
167 		async_wait_done(r, wait);
168 		free(data);
169 	}
170 
171 	return 0;
172 }
173 
174 static int async_wait_getobject_callback(sd_bus_message *m,
175 		void *userdata,
176 		sd_bus_error *e)
177 {
178 	int i, r;
179 	struct async_wait_callback_data *data = userdata;
180 	mapper_async_wait *wait = data->wait;
181 	uint64_t now;
182 
183 	if(wait->finished)
184 		goto exit;
185 
186 	r = sd_bus_message_get_errno(m);
187 	if(r == ENOENT)
188 		goto exit;
189 
190 	if(r == EBUSY && data->retry < mapper_busy_retries) {
191 		r = sd_event_now(wait->loop,
192 				CLOCK_MONOTONIC,
193 				&now);
194 		if(r < 0) {
195 			async_wait_done(r, wait);
196 			goto exit;
197 		}
198 
199 		++data->retry;
200 		r = sd_event_add_time(wait->loop,
201 				&data->event_source,
202 				CLOCK_MONOTONIC,
203 				now + mapper_busy_delay_interval_usec,
204 				0,
205 				async_wait_timeout_callback,
206 				data);
207 		if(r < 0) {
208 			async_wait_done(r, wait);
209 			goto exit;
210 		}
211 
212 		return 0;
213 	}
214 
215 	if(r) {
216 		async_wait_done(-r, wait);
217 		goto exit;
218 	}
219 
220 	for(i=0; i<wait->count; ++i) {
221 		if(!strcmp(data->path, wait->objs[i])) {
222 			wait->status[i] = 1;
223 		}
224 	}
225 
226 	if(async_wait_check_done(wait))
227 		async_wait_done(0, wait);
228 
229 exit:
230 	free(data);
231 	return 0;
232 }
233 
234 static int async_wait_get_objects(mapper_async_wait *wait)
235 {
236 	int i, r;
237 	struct async_wait_callback_data *data = NULL;
238 
239 	for(i=0; i<wait->count; ++i) {
240 		if(wait->status[i])
241 			continue;
242 		data = malloc(sizeof(*data));
243 		data->wait = wait;
244 		data->path = wait->objs[i];
245 		data->retry = 0;
246 		data->event_source = NULL;
247 		r = sd_bus_call_method_async(
248 				wait->conn,
249 				NULL,
250 				MAPPER_BUSNAME,
251 				MAPPER_PATH,
252 				MAPPER_INTERFACE,
253 				"GetObject",
254 				async_wait_getobject_callback,
255 				data,
256 				"sas",
257 				wait->objs[i],
258 				0,
259 				NULL);
260 		if(r < 0) {
261 			free(data);
262 			fprintf(stderr, "Error invoking method: %s\n",
263 					strerror(-r));
264 			return r;
265 		}
266 	}
267 
268 	return 0;
269 }
270 
271 static int async_wait_match_introspection_complete(sd_bus_message *m, void *w,
272 		sd_bus_error *e)
273 {
274 	int r;
275 
276 	mapper_async_wait *wait = w;
277 	if(wait->finished)
278 		return 0;
279 
280 	r = async_wait_get_objects(wait);
281 	if(r < 0)
282 		async_wait_done(r, wait);
283 
284 	return 0;
285 }
286 
287 static void async_wait_done(int r, mapper_async_wait *w)
288 {
289 	if(w->finished)
290 		return;
291 
292 	w->finished = 1;
293 	sd_bus_slot_unref(w->introspection_slot);
294 	sd_bus_slot_unref(w->intf_slot);
295 
296 	if(w->callback)
297 		w->callback(r, w->userdata);
298 }
299 
300 static int async_wait_check_done(mapper_async_wait *w)
301 {
302 	int i;
303 
304 	if(w->finished)
305 		return 1;
306 
307 	for(i=0; i<w->count; ++i)
308 		if(!w->status[i])
309 			return 0;
310 
311 	return 1;
312 }
313 
314 void mapper_wait_async_free(mapper_async_wait *w)
315 {
316 	free(w->status);
317 	sarrayfree(w->objs);
318 	free(w);
319 }
320 
321 int mapper_wait_async(sd_bus *conn,
322 		sd_event *loop,
323 		char *objs[],
324 		void (*callback)(int, void *),
325 		void *userdata,
326 		mapper_async_wait **w)
327 {
328 	int r;
329 	mapper_async_wait *wait = NULL;
330 
331 	wait = malloc(sizeof(*wait));
332 	if(!wait)
333 		return -ENOMEM;
334 
335 	memset(wait, 0, sizeof(*wait));
336 	wait->conn = conn;
337 	wait->loop = loop;
338 	wait->callback = callback;
339 	wait->userdata = userdata;
340 	wait->count = sarraylen(objs);
341 	if(!wait->count)
342 		return 0;
343 
344 	wait->objs = sarraydup(objs);
345 	if(!wait->objs) {
346 		r = -ENOMEM;
347 		goto free_wait;
348 	}
349 
350 	wait->status = malloc(sizeof(*wait->status) * wait->count);
351 	if(!wait->status) {
352 		r = -ENOMEM;
353 		goto free_objs;
354 	}
355 	memset(wait->status, 0, sizeof(*wait->status) * wait->count);
356 
357 	r = sd_bus_add_match(conn,
358 			&wait->introspection_slot,
359 			async_wait_introspection_match,
360 			async_wait_match_introspection_complete,
361 			wait);
362 	if(r < 0) {
363 		fprintf(stderr, "Error adding match rule: %s\n",
364 				strerror(-r));
365 		goto free_status;
366 	}
367 
368 	r = sd_bus_add_match(conn,
369                         &wait->intf_slot,
370 			async_wait_interfaces_added_match,
371                         async_wait_match_introspection_complete,
372                         wait);
373 	if(r < 0) {
374 		fprintf(stderr, "Error adding match rule: %s\n",
375 				strerror(-r));
376 		goto unref_name_slot;
377 	}
378 
379 	r = async_wait_get_objects(wait);
380 	if(r < 0) {
381 		fprintf(stderr, "Error calling method: %s\n",
382 				strerror(-r));
383 		goto unref_intf_slot;
384 	}
385 
386 	*w = wait;
387 
388 	return 0;
389 
390 unref_intf_slot:
391 	sd_bus_slot_unref(wait->intf_slot);
392 unref_name_slot:
393 	sd_bus_slot_unref(wait->introspection_slot);
394 free_status:
395 	free(wait->status);
396 free_objs:
397 	sarrayfree(wait->objs);
398 free_wait:
399 	free(wait);
400 
401 	return r;
402 }
403 
404 static int async_subtree_timeout_callback(sd_event_source *s,
405 		uint64_t usec, void *userdata)
406 {
407 	int r;
408 	struct mapper_async_subtree *subtree = userdata;
409 
410 	sd_event_source_unref(subtree->event_source);
411 	r = sd_bus_call_method_async(
412 			subtree->conn,
413 			NULL,
414 			MAPPER_BUSNAME,
415 			MAPPER_PATH,
416 			MAPPER_INTERFACE,
417 			"GetSubTreePaths",
418 			async_subtree_getpaths_callback,
419 			subtree,
420 			"sias",
421 			subtree->namespace,
422 			0, 1,
423 			subtree->interface);
424 	if(r < 0)
425 		async_subtree_done(r, subtree);
426 
427 	return 0;
428 }
429 
430 static int async_subtree_getpaths_callback(sd_bus_message *m,
431 		void *userdata,
432 		sd_bus_error *e)
433 {
434 	int r;
435 	char *intf = NULL;
436 	struct mapper_async_subtree *subtree = userdata;
437 	uint64_t now;
438 
439 	if(subtree->finished)
440 		goto exit;
441 
442 	r = sd_bus_message_get_errno(m);
443 
444 	if(r == ENOENT) {
445 		if (subtree->op == MAPPER_OP_REMOVE)
446 			r = 0;
447 		else
448 			goto exit;
449 	}
450 
451 	if(r == EBUSY && subtree->retry < mapper_busy_retries) {
452 		r = sd_event_now(subtree->loop,
453 				CLOCK_MONOTONIC,
454 				&now);
455 		if(r < 0) {
456 			async_subtree_done(r, subtree);
457 			goto exit;
458 		}
459 
460 		++subtree->retry;
461 		r = sd_event_add_time(subtree->loop,
462 				&subtree->event_source,
463 				CLOCK_MONOTONIC,
464 				now + mapper_busy_delay_interval_usec,
465 				0,
466 				async_subtree_timeout_callback,
467 				subtree);
468 		if(r < 0) {
469 			async_subtree_done(r, subtree);
470 			goto exit;
471 		}
472 
473 		return 0;
474 	}
475 
476 	if(r) {
477 		async_subtree_done(-r, subtree);
478 		goto exit;
479 	}
480 
481 	sd_bus_message_read(m, "as", 1, &intf);
482 	if (subtree->op == MAPPER_OP_REMOVE) {
483 		/* For remove, operation is complete when the interface is not present */
484 		if (intf == NULL)
485 			async_subtree_done(0, subtree);
486 	}
487 
488 exit:
489 	return 0;
490 }
491 
492 static int async_subtree_getpaths(mapper_async_subtree *subtree)
493 {
494 	int r = 0;
495 
496 	subtree->retry = 0;
497 	subtree->event_source = NULL;
498 	r = sd_bus_call_method_async(
499 			subtree->conn,
500 			NULL,
501 			MAPPER_BUSNAME,
502 			MAPPER_PATH,
503 			MAPPER_INTERFACE,
504 			"GetSubTreePaths",
505 			async_subtree_getpaths_callback,
506 			subtree,
507 			"sias",
508 			subtree->namespace,
509 			0, 1,
510 			subtree->interface);
511 	if (r < 0) {
512 		fprintf(stderr, "Error invoking method: %s\n", strerror(-r));
513 		return r;
514 	}
515 
516 	return 0;
517 }
518 
519 static int async_subtree_match_callback(sd_bus_message *m,
520 		void *t,
521 		sd_bus_error *e)
522 {
523 	int r;
524 
525 	mapper_async_subtree *subtree = t;
526 	if(subtree->finished)
527 		return 0;
528 
529 	r = async_subtree_getpaths(subtree);
530 	if(r < 0)
531 		async_subtree_done(r, subtree);
532 
533 	return 0;
534 }
535 
536 static void async_subtree_done(int r, mapper_async_subtree *t)
537 {
538 	if(t->finished)
539 		return;
540 
541 	t->finished = 1;
542 	sd_bus_slot_unref(t->slot);
543 
544 	if(t->callback)
545 		t->callback(r, t->userdata);
546 }
547 
548 int mapper_subtree_async(sd_bus *conn,
549 		sd_event *loop,
550 		char *namespace,
551 		char *interface,
552 		void (*callback)(int, void *),
553 		void *userdata,
554 		mapper_async_subtree **t,
555 		int op)
556 {
557 	int r = 0;
558 	mapper_async_subtree *subtree = NULL;
559 
560 	subtree = malloc(sizeof(*subtree));
561 	if(!subtree)
562 		return -ENOMEM;
563 
564 	memset(subtree, 0, sizeof(*subtree));
565 	subtree->conn = conn;
566 	subtree->loop = loop;
567 	subtree->namespace = namespace;
568 	subtree->interface = interface;
569 	subtree->callback = callback;
570 	subtree->userdata = userdata;
571 	subtree->op = op;
572 
573 	if (subtree->op == MAPPER_OP_REMOVE) {
574 		r = sd_bus_add_match(
575 				conn,
576 				&subtree->slot,
577 				interfaces_removed_match,
578 				async_subtree_match_callback,
579 				subtree);
580 		if(r < 0) {
581 			fprintf(stderr, "Error adding match rule: %s\n",
582 					strerror(-r));
583 			goto unref_slot;
584 		}
585 	} else {
586 		/* Operation not supported */
587 		r = -EINVAL;
588 		goto free_subtree;
589 	}
590 
591 	r = async_subtree_getpaths(subtree);
592 	if(r < 0) {
593 		fprintf(stderr, "Error calling method: %s\n",
594 				strerror(-r));
595 		goto unref_slot;
596 	}
597 
598 	*t = subtree;
599 
600 	return 0;
601 
602 unref_slot:
603 	sd_bus_slot_unref(subtree->slot);
604 free_subtree:
605 	free(subtree);
606 
607 	return r;
608 }
609 
610 int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply)
611 {
612 	sd_bus_error error = SD_BUS_ERROR_NULL;
613 	sd_bus_message *request = NULL;
614 	int r, retry = 0;
615 
616 	r = sd_bus_message_new_method_call(
617 			conn,
618 			&request,
619 			MAPPER_BUSNAME,
620 			MAPPER_PATH,
621 			MAPPER_INTERFACE,
622 			"GetObject");
623 	if (r < 0)
624 		goto exit;
625 
626 	r = sd_bus_message_append(request, "s", obj);
627 	if (r < 0)
628 		goto exit;
629 	r = sd_bus_message_append(request, "as", 0, NULL);
630 	if (r < 0)
631 		goto exit;
632 
633 	while(retry < mapper_busy_retries) {
634 		sd_bus_error_free(&error);
635 		r = sd_bus_call(conn, request, 0, &error, reply);
636 		if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) {
637 			++retry;
638 
639 			if(retry != mapper_busy_retries)
640 				usleep(mapper_busy_delay_interval_usec);
641 			continue;
642 		}
643 		break;
644 	}
645 
646 	if (r < 0)
647 		goto exit;
648 
649 exit:
650 	sd_bus_error_free(&error);
651 	sd_bus_message_unref(request);
652 
653 	return r;
654 }
655 
656 int mapper_get_service(sd_bus *conn, const char *obj, char **service)
657 {
658 	sd_bus_message *reply = NULL;
659 	const char *tmp;
660 	int r;
661 
662 	r = mapper_get_object(conn, obj, &reply);
663 	if (r < 0)
664 		goto exit;
665 
666 	r = sd_bus_message_enter_container(reply, 0, NULL);
667 	if (r < 0)
668 		goto exit;
669 
670 	r = sd_bus_message_enter_container(reply, 0, NULL);
671 	if (r < 0)
672 		goto exit;
673 
674 	r = sd_bus_message_read(reply, "s", &tmp);
675 	if (r < 0)
676 		goto exit;
677 
678 	*service = strdup(tmp);
679 
680 exit:
681 	sd_bus_message_unref(reply);
682 
683 	return r;
684 }
685