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