xref: /openbmc/phosphor-objmgr/libmapper/mapper.c (revision aa9c24a6636dc6c89b289e7ae3457c489e152c37)
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