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