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