1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2016 IBM Corporation
3
4 #include "mapper.h"
5
6 #include "internal.h"
7
8 #include <errno.h>
9 #include <stdbool.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 #include <sys/timerfd.h>
15 #include <systemd/sd-bus.h>
16 #include <systemd/sd-event.h>
17 #include <unistd.h>
18
19 #define _public_ __attribute__((__visibility__("default")))
20 #define _unused_ __attribute__((unused))
21
22 static const char* async_wait_introspection_match =
23 "type='signal',"
24 "sender='xyz.openbmc_project.ObjectMapper',"
25 "interface='xyz.openbmc_project.ObjectMapper.Private',"
26 "member='IntrospectionComplete'";
27
28 static const char* async_wait_interfaces_added_match =
29 "type='signal',"
30 "interface='org.freedesktop.DBus.ObjectManager',"
31 "member='InterfacesAdded'";
32
33 static const char* interfaces_removed_match =
34 "type='signal',"
35 "interface='org.freedesktop.DBus.ObjectManager',"
36 "member='InterfacesRemoved'";
37
38 static const int mapper_busy_retries = 5;
39 static const uint64_t mapper_busy_delay_interval_usec = 1000000;
40
41 struct mapper_async_wait
42 {
43 char** objs;
44 void (*callback)(int, void*);
45 void* userdata;
46 sd_event* loop;
47 sd_bus* conn;
48 sd_bus_slot* introspection_slot;
49 sd_bus_slot* intf_slot;
50 int* status;
51 size_t count;
52 int finished;
53 int r;
54 };
55
56 struct async_wait_callback_data
57 {
58 mapper_async_wait* wait;
59 const char* path;
60 sd_event_source* event_source;
61 int retry;
62 };
63
64 struct mapper_async_subtree
65 {
66 char* namespace;
67 char* interface;
68 void (*callback)(int, void*);
69 void* userdata;
70 sd_event* loop;
71 sd_bus* conn;
72 sd_bus_slot* slot;
73 sd_event_source* event_source;
74 int finished;
75 int op;
76 int retry;
77 };
78
79 static int async_wait_match_introspection_complete(sd_bus_message*, void*,
80 sd_bus_error*);
81 static int async_wait_check_done(mapper_async_wait*);
82 static void async_wait_done(int r, mapper_async_wait*);
83 static int async_wait_get_objects(mapper_async_wait*);
84 static int async_wait_getobject_callback(sd_bus_message*, void*, sd_bus_error*);
85
86 static int async_subtree_match_callback(sd_bus_message*, void*, sd_bus_error*);
87 static void async_subtree_done(int r, mapper_async_subtree*);
88 static int async_subtree_getpaths(mapper_async_subtree*);
89 static int async_subtree_getpaths_callback(sd_bus_message*, void*,
90 sd_bus_error*);
91
sarraylen(char * array[])92 size_t sarraylen(char* array[])
93 {
94 size_t count = 0;
95 char** p = array;
96
97 while (*p != NULL)
98 {
99 ++count;
100 ++p;
101 }
102
103 return count;
104 }
105
sarrayfree(char * array[])106 void sarrayfree(char* array[])
107 {
108 char** p = array;
109 while (*p != NULL)
110 {
111 free(*p);
112 ++p;
113 }
114 free(array);
115 }
116
sarraydup(char * array[])117 char** sarraydup(char* array[])
118 {
119 size_t count = sarraylen(array);
120 size_t i;
121 char** ret = NULL;
122
123 ret = calloc(count + 1, sizeof(*ret));
124 if (!ret)
125 {
126 return NULL;
127 }
128
129 for (i = 0; i < count; ++i)
130 {
131 ret[i] = strdup(array[i]);
132 if (!ret[i])
133 {
134 goto error;
135 }
136 }
137
138 return ret;
139
140 error:
141 sarrayfree(ret);
142 return NULL;
143 }
144
async_wait_timeout_callback(_unused_ sd_event_source * s,_unused_ uint64_t usec,void * userdata)145 static int async_wait_timeout_callback(_unused_ sd_event_source* s,
146 _unused_ 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, NULL, "xyz.openbmc_project.ObjectMapper",
155 "/xyz/openbmc_project/object_mapper",
156 "xyz.openbmc_project.ObjectMapper", "GetObject",
157 async_wait_getobject_callback, data, "sas", data->path, 0, NULL);
158 if (r < 0)
159 {
160 async_wait_done(r, wait);
161 free(data);
162 }
163
164 return 0;
165 }
166
async_wait_getobject_callback(sd_bus_message * m,void * userdata,_unused_ sd_bus_error * e)167 static int async_wait_getobject_callback(sd_bus_message* m, void* userdata,
168 _unused_ sd_bus_error* e)
169 {
170 size_t i;
171 int r;
172 struct async_wait_callback_data* data = userdata;
173 mapper_async_wait* wait = data->wait;
174 uint64_t next_retry;
175
176 if (wait->finished)
177 {
178 goto exit;
179 }
180
181 if (sd_bus_message_is_method_error(
182 m, "xyz.openbmc_project.Common.Error.ResourceNotFound"))
183 {
184 goto exit;
185 }
186
187 r = sd_bus_message_get_errno(m);
188
189 if ((r == EBUSY || r == ENOBUFS) && data->retry < mapper_busy_retries)
190 {
191 r = sd_event_now(wait->loop, CLOCK_MONOTONIC, &next_retry);
192 if (r < 0)
193 {
194 async_wait_done(r, wait);
195 goto exit;
196 }
197
198 next_retry += mapper_busy_delay_interval_usec * (1 << data->retry);
199 r = sd_event_add_time(wait->loop, &data->event_source, CLOCK_MONOTONIC,
200 next_retry, 0, async_wait_timeout_callback, data);
201 ++data->retry;
202 if (r < 0)
203 {
204 async_wait_done(r, wait);
205 goto exit;
206 }
207
208 return 0;
209 }
210
211 if (r)
212 {
213 async_wait_done(-r, wait);
214 goto exit;
215 }
216
217 for (i = 0; i < wait->count; ++i)
218 {
219 if (!strcmp(data->path, wait->objs[i]))
220 {
221 wait->status[i] = 1;
222 }
223 }
224
225 if (async_wait_check_done(wait))
226 {
227 async_wait_done(0, wait);
228 }
229
230 exit:
231 free(data);
232 return 0;
233 }
234
async_wait_get_objects(mapper_async_wait * wait)235 static int async_wait_get_objects(mapper_async_wait* wait)
236 {
237 size_t i;
238 int r;
239 struct async_wait_callback_data* data = NULL;
240
241 for (i = 0; i < wait->count; ++i)
242 {
243 if (wait->status[i])
244 {
245 continue;
246 }
247 data = malloc(sizeof(*data));
248 data->wait = wait;
249 data->path = wait->objs[i];
250 data->retry = 0;
251 data->event_source = NULL;
252 r = sd_bus_call_method_async(
253 wait->conn, NULL, "xyz.openbmc_project.ObjectMapper",
254 "/xyz/openbmc_project/object_mapper",
255 "xyz.openbmc_project.ObjectMapper", "GetObject",
256 async_wait_getobject_callback, data, "sas", wait->objs[i], 0, NULL);
257 if (r < 0)
258 {
259 free(data);
260 fprintf(stderr, "Error invoking method: %s\n", strerror(-r));
261 return r;
262 }
263 }
264
265 return 0;
266 }
267
async_wait_match_introspection_complete(_unused_ sd_bus_message * m,void * w,_unused_ sd_bus_error * e)268 static int async_wait_match_introspection_complete(
269 _unused_ sd_bus_message* m, void* w, _unused_ sd_bus_error* e)
270 {
271 int r;
272
273 mapper_async_wait* wait = w;
274 if (wait->finished)
275 {
276 return 0;
277 }
278
279 r = async_wait_get_objects(wait);
280 if (r < 0)
281 {
282 async_wait_done(r, wait);
283 }
284
285 return 0;
286 }
287
async_wait_done(int r,mapper_async_wait * w)288 static void async_wait_done(int r, mapper_async_wait* w)
289 {
290 if (w->finished)
291 {
292 return;
293 }
294
295 w->finished = 1;
296 sd_bus_slot_unref(w->introspection_slot);
297 sd_bus_slot_unref(w->intf_slot);
298
299 if (w->callback)
300 {
301 w->callback(r, w->userdata);
302 }
303 }
304
async_wait_check_done(mapper_async_wait * w)305 static int async_wait_check_done(mapper_async_wait* w)
306 {
307 size_t i;
308
309 if (w->finished)
310 {
311 return 1;
312 }
313
314 for (i = 0; i < w->count; ++i)
315 {
316 if (!w->status[i])
317 {
318 return 0;
319 }
320 }
321
322 return 1;
323 }
324
mapper_wait_async_free(mapper_async_wait * w)325 _public_ void mapper_wait_async_free(mapper_async_wait* w)
326 {
327 free(w->status);
328 sarrayfree(w->objs);
329 free(w);
330 }
331
mapper_wait_async(sd_bus * conn,sd_event * loop,char * objs[],void (* callback)(int,void *),void * userdata,mapper_async_wait ** w)332 _public_ int mapper_wait_async(sd_bus* conn, sd_event* loop, char* objs[],
333 void (*callback)(int, void*), void* userdata,
334 mapper_async_wait** w)
335 {
336 int r;
337 mapper_async_wait* wait = NULL;
338
339 wait = malloc(sizeof(*wait));
340 if (!wait)
341 {
342 return -ENOMEM;
343 }
344
345 memset(wait, 0, sizeof(*wait));
346 wait->conn = conn;
347 wait->loop = loop;
348 wait->callback = callback;
349 wait->userdata = userdata;
350 wait->count = sarraylen(objs);
351 if (!wait->count)
352 {
353 r = 0;
354 goto free_wait;
355 }
356
357 wait->objs = sarraydup(objs);
358 if (!wait->objs)
359 {
360 r = -ENOMEM;
361 goto free_wait;
362 }
363
364 wait->status = malloc(sizeof(*wait->status) * wait->count);
365 if (!wait->status)
366 {
367 r = -ENOMEM;
368 goto free_objs;
369 }
370 memset(wait->status, 0, sizeof(*wait->status) * wait->count);
371
372 r = sd_bus_add_match(conn, &wait->introspection_slot,
373 async_wait_introspection_match,
374 async_wait_match_introspection_complete, wait);
375 if (r < 0)
376 {
377 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
378 goto free_status;
379 }
380
381 r = sd_bus_add_match(conn, &wait->intf_slot,
382 async_wait_interfaces_added_match,
383 async_wait_match_introspection_complete, wait);
384 if (r < 0)
385 {
386 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
387 goto unref_name_slot;
388 }
389
390 r = async_wait_get_objects(wait);
391 if (r < 0)
392 {
393 fprintf(stderr, "Error calling method: %s\n", strerror(-r));
394 goto unref_intf_slot;
395 }
396
397 *w = wait;
398
399 return 0;
400
401 unref_intf_slot:
402 sd_bus_slot_unref(wait->intf_slot);
403 unref_name_slot:
404 sd_bus_slot_unref(wait->introspection_slot);
405 free_status:
406 free(wait->status);
407 free_objs:
408 sarrayfree(wait->objs);
409 free_wait:
410 free(wait);
411
412 return r;
413 }
414
async_subtree_timeout_callback(_unused_ sd_event_source * s,_unused_ uint64_t usec,void * userdata)415 static int async_subtree_timeout_callback(
416 _unused_ sd_event_source* s, _unused_ uint64_t usec, void* userdata)
417 {
418 int r;
419 struct mapper_async_subtree* subtree = userdata;
420
421 sd_event_source_unref(subtree->event_source);
422 r = sd_bus_call_method_async(
423 subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper",
424 "/xyz/openbmc_project/object_mapper",
425 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
426 async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0,
427 1, subtree->interface);
428 if (r < 0)
429 {
430 async_subtree_done(r, subtree);
431 }
432
433 return 0;
434 }
435
async_subtree_getpaths_callback(sd_bus_message * m,void * userdata,_unused_ sd_bus_error * e)436 static int async_subtree_getpaths_callback(sd_bus_message* m, void* userdata,
437 _unused_ sd_bus_error* e)
438 {
439 int r;
440 struct mapper_async_subtree* subtree = userdata;
441 uint64_t next_retry;
442
443 if (subtree->finished)
444 {
445 goto exit;
446 }
447
448 r = sd_bus_message_get_errno(m);
449
450 if (sd_bus_message_is_method_error(
451 m, "xyz.openbmc_project.Common.Error.ResourceNotFound"))
452 {
453 if (subtree->op == MAPPER_OP_REMOVE)
454 {
455 r = 0;
456 }
457 else
458 {
459 goto exit;
460 }
461 }
462
463 if ((r == EBUSY || r == ENOBUFS) && subtree->retry < mapper_busy_retries)
464 {
465 r = sd_event_now(subtree->loop, CLOCK_MONOTONIC, &next_retry);
466 if (r < 0)
467 {
468 async_subtree_done(r, subtree);
469 goto exit;
470 }
471
472 next_retry += mapper_busy_delay_interval_usec * (1 << subtree->retry);
473 r = sd_event_add_time(subtree->loop, &subtree->event_source,
474 CLOCK_MONOTONIC, next_retry, 0,
475 async_subtree_timeout_callback, subtree);
476 ++subtree->retry;
477 if (r < 0)
478 {
479 async_subtree_done(r, subtree);
480 goto exit;
481 }
482
483 return 0;
484 }
485
486 if (r)
487 {
488 async_subtree_done(-r, subtree);
489 goto exit;
490 }
491
492 if (subtree->op == MAPPER_OP_REMOVE)
493 {
494 r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
495 if (r < 0)
496 {
497 async_subtree_done(r, subtree);
498 goto exit;
499 }
500
501 r = sd_bus_message_at_end(m, false);
502 if (r < 0)
503 {
504 async_subtree_done(r, subtree);
505 goto exit;
506 }
507
508 /* For remove, operation is complete when the interface is not present
509 * we know it is empty if the returned array is empty
510 */
511 if (r)
512 async_subtree_done(0, subtree);
513 }
514
515 exit:
516 return 0;
517 }
518
async_subtree_getpaths(mapper_async_subtree * subtree)519 static int async_subtree_getpaths(mapper_async_subtree* subtree)
520 {
521 int r = 0;
522
523 subtree->retry = 0;
524 subtree->event_source = NULL;
525 r = sd_bus_call_method_async(
526 subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper",
527 "/xyz/openbmc_project/object_mapper",
528 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
529 async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0,
530 1, subtree->interface);
531 if (r < 0)
532 {
533 fprintf(stderr, "Error invoking method: %s\n", strerror(-r));
534 return r;
535 }
536
537 return 0;
538 }
539
async_subtree_match_callback(_unused_ sd_bus_message * m,void * t,_unused_ sd_bus_error * e)540 static int async_subtree_match_callback(_unused_ sd_bus_message* m, void* t,
541 _unused_ sd_bus_error* e)
542 {
543 int r;
544
545 mapper_async_subtree* subtree = t;
546 if (subtree->finished)
547 {
548 return 0;
549 }
550
551 r = async_subtree_getpaths(subtree);
552 if (r < 0)
553 {
554 async_subtree_done(r, subtree);
555 }
556
557 return 0;
558 }
559
async_subtree_done(int r,mapper_async_subtree * t)560 static void async_subtree_done(int r, mapper_async_subtree* t)
561 {
562 if (t->finished)
563 {
564 return;
565 }
566
567 t->finished = 1;
568 sd_bus_slot_unref(t->slot);
569
570 if (t->callback)
571 {
572 t->callback(r, t->userdata);
573 }
574 }
575
mapper_subtree_async(sd_bus * conn,sd_event * loop,char * namespace,char * interface,void (* callback)(int,void *),void * userdata,mapper_async_subtree ** t,int op)576 _public_ int mapper_subtree_async(sd_bus* conn, sd_event* loop, char* namespace,
577 char* interface, void (*callback)(int, void*),
578 void* userdata, mapper_async_subtree** t,
579 int op)
580 {
581 int r = 0;
582 mapper_async_subtree* subtree = NULL;
583
584 subtree = malloc(sizeof(*subtree));
585 if (!subtree)
586 {
587 return -ENOMEM;
588 }
589
590 memset(subtree, 0, sizeof(*subtree));
591 subtree->conn = conn;
592 subtree->loop = loop;
593 subtree->namespace = namespace;
594 subtree->interface = interface;
595 subtree->callback = callback;
596 subtree->userdata = userdata;
597 subtree->op = op;
598
599 if (subtree->op == MAPPER_OP_REMOVE)
600 {
601 r = sd_bus_add_match(conn, &subtree->slot, interfaces_removed_match,
602 async_subtree_match_callback, subtree);
603 if (r < 0)
604 {
605 fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
606 goto unref_slot;
607 }
608 }
609 else
610 {
611 /* Operation not supported */
612 r = -EINVAL;
613 goto free_subtree;
614 }
615
616 r = async_subtree_getpaths(subtree);
617 if (r < 0)
618 {
619 fprintf(stderr, "Error calling method: %s\n", strerror(-r));
620 goto unref_slot;
621 }
622
623 *t = subtree;
624
625 return 0;
626
627 unref_slot:
628 sd_bus_slot_unref(subtree->slot);
629 free_subtree:
630 free(subtree);
631
632 return r;
633 }
634
mapper_get_object(sd_bus * conn,const char * obj,sd_bus_message ** reply)635 _public_ int mapper_get_object(sd_bus* conn, const char* obj,
636 sd_bus_message** reply)
637 {
638 sd_bus_message* request = NULL;
639 int r, retry = 0;
640
641 r = sd_bus_message_new_method_call(
642 conn, &request, "xyz.openbmc_project.ObjectMapper",
643 "/xyz/openbmc_project/object_mapper",
644 "xyz.openbmc_project.ObjectMapper", "GetObject");
645 if (r < 0)
646 {
647 goto exit;
648 }
649
650 r = sd_bus_message_append(request, "s", obj);
651 if (r < 0)
652 {
653 goto exit;
654 }
655 r = sd_bus_message_append(request, "as", 0, NULL);
656 if (r < 0)
657 {
658 goto exit;
659 }
660
661 while (true)
662 {
663 r = sd_bus_call(conn, request, 0, NULL, reply);
664 if (r == -EBUSY || r == -ENOBUFS)
665 {
666 if (retry >= mapper_busy_retries)
667 break;
668
669 usleep(mapper_busy_delay_interval_usec * (1 << retry));
670 ++retry;
671 continue;
672 }
673 break;
674 }
675
676 if (r < 0)
677 {
678 goto exit;
679 }
680
681 exit:
682 sd_bus_message_unref(request);
683
684 return r;
685 }
686
mapper_get_service(sd_bus * conn,const char * obj,char ** service)687 _public_ int mapper_get_service(sd_bus* conn, const char* obj, char** service)
688 {
689 sd_bus_message* reply = NULL;
690 const char* tmp;
691 int r;
692
693 r = mapper_get_object(conn, obj, &reply);
694 if (r < 0)
695 {
696 goto exit;
697 }
698
699 r = sd_bus_message_enter_container(reply, 0, NULL);
700 if (r < 0)
701 {
702 goto exit;
703 }
704
705 r = sd_bus_message_enter_container(reply, 0, NULL);
706 if (r < 0)
707 {
708 goto exit;
709 }
710
711 r = sd_bus_message_read(reply, "s", &tmp);
712 if (r < 0)
713 {
714 goto exit;
715 }
716
717 *service = strdup(tmp);
718
719 exit:
720 sd_bus_message_unref(reply);
721
722 return r;
723 }
724