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