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 <errno.h>
19 #include <stdbool.h>
20 #include <stddef.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <sys/timerfd.h>
25 #include <systemd/sd-bus.h>
26 #include <systemd/sd-event.h>
27 #include <unistd.h>
28 
29 #include "internal.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 
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 
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 
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         return NULL;
138 
139     for (i = 0; i < count; ++i)
140     {
141         ret[i] = strdup(array[i]);
142         if (!ret[i])
143             goto error;
144     }
145 
146     return ret;
147 
148 error:
149     sarrayfree(ret);
150     return NULL;
151 }
152 
153 static int async_wait_timeout_callback(_unused_ sd_event_source* s,
154                                        _unused_ uint64_t usec, void* userdata)
155 {
156     int r;
157     struct async_wait_callback_data* data = userdata;
158     mapper_async_wait* wait = data->wait;
159 
160     sd_event_source_unref(data->event_source);
161     r = sd_bus_call_method_async(
162         wait->conn, NULL, "xyz.openbmc_project.ObjectMapper",
163         "/xyz/openbmc_project/object_mapper",
164         "xyz.openbmc_project.ObjectMapper", "GetObject",
165         async_wait_getobject_callback, data, "sas", data->path, 0, NULL);
166     if (r < 0)
167     {
168         async_wait_done(r, wait);
169         free(data);
170     }
171 
172     return 0;
173 }
174 
175 static int async_wait_getobject_callback(sd_bus_message* m, void* userdata,
176                                          _unused_ sd_bus_error* e)
177 {
178     size_t i;
179     int r;
180     struct async_wait_callback_data* data = userdata;
181     mapper_async_wait* wait = data->wait;
182     uint64_t next_retry;
183 
184     if (wait->finished)
185         goto exit;
186 
187     if (sd_bus_message_is_method_error(
188             m, "xyz.openbmc_project.Common.Error.ResourceNotFound"))
189         goto exit;
190 
191     r = sd_bus_message_get_errno(m);
192 
193     if ((r == EBUSY || r == ENOBUFS) && data->retry < mapper_busy_retries)
194     {
195         r = sd_event_now(wait->loop, CLOCK_MONOTONIC, &next_retry);
196         if (r < 0)
197         {
198             async_wait_done(r, wait);
199             goto exit;
200         }
201 
202         next_retry += mapper_busy_delay_interval_usec * (1 << data->retry);
203         r = sd_event_add_time(wait->loop, &data->event_source, CLOCK_MONOTONIC,
204                               next_retry, 0, async_wait_timeout_callback, data);
205         ++data->retry;
206         if (r < 0)
207         {
208             async_wait_done(r, wait);
209             goto exit;
210         }
211 
212         return 0;
213     }
214 
215     if (r)
216     {
217         async_wait_done(-r, wait);
218         goto exit;
219     }
220 
221     for (i = 0; i < wait->count; ++i)
222     {
223         if (!strcmp(data->path, wait->objs[i]))
224         {
225             wait->status[i] = 1;
226         }
227     }
228 
229     if (async_wait_check_done(wait))
230         async_wait_done(0, wait);
231 
232 exit:
233     free(data);
234     return 0;
235 }
236 
237 static int async_wait_get_objects(mapper_async_wait* wait)
238 {
239     size_t i;
240     int r;
241     struct async_wait_callback_data* data = NULL;
242 
243     for (i = 0; i < wait->count; ++i)
244     {
245         if (wait->status[i])
246             continue;
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 
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, "xyz.openbmc_project.ObjectMapper",
410         "/xyz/openbmc_project/object_mapper",
411         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
412         async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0,
413         1, subtree->interface);
414     if (r < 0)
415         async_subtree_done(r, subtree);
416 
417     return 0;
418 }
419 
420 static int async_subtree_getpaths_callback(sd_bus_message* m, void* userdata,
421                                            _unused_ sd_bus_error* e)
422 {
423     int r;
424     struct mapper_async_subtree* subtree = userdata;
425     uint64_t next_retry;
426 
427     if (subtree->finished)
428         goto exit;
429 
430     r = sd_bus_message_get_errno(m);
431 
432     if (sd_bus_message_is_method_error(
433             m, "xyz.openbmc_project.Common.Error.ResourceNotFound"))
434     {
435         if (subtree->op == MAPPER_OP_REMOVE)
436             r = 0;
437         else
438             goto exit;
439     }
440 
441     if ((r == EBUSY || r == ENOBUFS) && subtree->retry < mapper_busy_retries)
442     {
443         r = sd_event_now(subtree->loop, CLOCK_MONOTONIC, &next_retry);
444         if (r < 0)
445         {
446             async_subtree_done(r, subtree);
447             goto exit;
448         }
449 
450         next_retry += mapper_busy_delay_interval_usec * (1 << subtree->retry);
451         r = sd_event_add_time(subtree->loop, &subtree->event_source,
452                               CLOCK_MONOTONIC, next_retry, 0,
453                               async_subtree_timeout_callback, subtree);
454         ++subtree->retry;
455         if (r < 0)
456         {
457             async_subtree_done(r, subtree);
458             goto exit;
459         }
460 
461         return 0;
462     }
463 
464     if (r)
465     {
466         async_subtree_done(-r, subtree);
467         goto exit;
468     }
469 
470     if (subtree->op == MAPPER_OP_REMOVE)
471     {
472         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
473         if (r < 0)
474         {
475             async_subtree_done(r, subtree);
476             goto exit;
477         }
478 
479         r = sd_bus_message_at_end(m, false);
480         if (r < 0)
481         {
482             async_subtree_done(r, subtree);
483             goto exit;
484         }
485 
486         /* For remove, operation is complete when the interface is not present
487          * we know it is empty if the returned array is empty
488          */
489         if (r)
490             async_subtree_done(0, subtree);
491     }
492 
493 exit:
494     return 0;
495 }
496 
497 static int async_subtree_getpaths(mapper_async_subtree* subtree)
498 {
499     int r = 0;
500 
501     subtree->retry = 0;
502     subtree->event_source = NULL;
503     r = sd_bus_call_method_async(
504         subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper",
505         "/xyz/openbmc_project/object_mapper",
506         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
507         async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0,
508         1, subtree->interface);
509     if (r < 0)
510     {
511         fprintf(stderr, "Error invoking method: %s\n", strerror(-r));
512         return r;
513     }
514 
515     return 0;
516 }
517 
518 static int async_subtree_match_callback(_unused_ sd_bus_message* m, void* t,
519                                         _unused_ sd_bus_error* e)
520 {
521     int r;
522 
523     mapper_async_subtree* subtree = t;
524     if (subtree->finished)
525         return 0;
526 
527     r = async_subtree_getpaths(subtree);
528     if (r < 0)
529         async_subtree_done(r, subtree);
530 
531     return 0;
532 }
533 
534 static void async_subtree_done(int r, mapper_async_subtree* t)
535 {
536     if (t->finished)
537         return;
538 
539     t->finished = 1;
540     sd_bus_slot_unref(t->slot);
541 
542     if (t->callback)
543         t->callback(r, t->userdata);
544 }
545 
546 _public_ int mapper_subtree_async(sd_bus* conn, sd_event* loop, char* namespace,
547                                   char* interface, void (*callback)(int, void*),
548                                   void* userdata, mapper_async_subtree** t,
549                                   int op)
550 {
551     int r = 0;
552     mapper_async_subtree* subtree = NULL;
553 
554     subtree = malloc(sizeof(*subtree));
555     if (!subtree)
556         return -ENOMEM;
557 
558     memset(subtree, 0, sizeof(*subtree));
559     subtree->conn = conn;
560     subtree->loop = loop;
561     subtree->namespace = namespace;
562     subtree->interface = interface;
563     subtree->callback = callback;
564     subtree->userdata = userdata;
565     subtree->op = op;
566 
567     if (subtree->op == MAPPER_OP_REMOVE)
568     {
569         r = sd_bus_add_match(conn, &subtree->slot, interfaces_removed_match,
570                              async_subtree_match_callback, subtree);
571         if (r < 0)
572         {
573             fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
574             goto unref_slot;
575         }
576     }
577     else
578     {
579         /* Operation not supported */
580         r = -EINVAL;
581         goto free_subtree;
582     }
583 
584     r = async_subtree_getpaths(subtree);
585     if (r < 0)
586     {
587         fprintf(stderr, "Error calling method: %s\n", strerror(-r));
588         goto unref_slot;
589     }
590 
591     *t = subtree;
592 
593     return 0;
594 
595 unref_slot:
596     sd_bus_slot_unref(subtree->slot);
597 free_subtree:
598     free(subtree);
599 
600     return r;
601 }
602 
603 _public_ int mapper_get_object(sd_bus* conn, const char* obj,
604                                sd_bus_message** reply)
605 {
606     sd_bus_message* request = NULL;
607     int r, retry = 0;
608 
609     r = sd_bus_message_new_method_call(
610         conn, &request, "xyz.openbmc_project.ObjectMapper",
611         "/xyz/openbmc_project/object_mapper",
612         "xyz.openbmc_project.ObjectMapper", "GetObject");
613     if (r < 0)
614         goto exit;
615 
616     r = sd_bus_message_append(request, "s", obj);
617     if (r < 0)
618         goto exit;
619     r = sd_bus_message_append(request, "as", 0, NULL);
620     if (r < 0)
621         goto exit;
622 
623     while (true)
624     {
625         r = sd_bus_call(conn, request, 0, NULL, reply);
626         if (r == -EBUSY || r == -ENOBUFS)
627         {
628             if (retry >= mapper_busy_retries)
629                 break;
630 
631             usleep(mapper_busy_delay_interval_usec * (1 << retry));
632             ++retry;
633             continue;
634         }
635         break;
636     }
637 
638     if (r < 0)
639         goto exit;
640 
641 exit:
642     sd_bus_message_unref(request);
643 
644     return r;
645 }
646 
647 _public_ int mapper_get_service(sd_bus* conn, const char* obj, char** service)
648 {
649     sd_bus_message* reply = NULL;
650     const char* tmp;
651     int r;
652 
653     r = mapper_get_object(conn, obj, &reply);
654     if (r < 0)
655         goto exit;
656 
657     r = sd_bus_message_enter_container(reply, 0, NULL);
658     if (r < 0)
659         goto exit;
660 
661     r = sd_bus_message_enter_container(reply, 0, NULL);
662     if (r < 0)
663         goto exit;
664 
665     r = sd_bus_message_read(reply, "s", &tmp);
666     if (r < 0)
667         goto exit;
668 
669     *service = strdup(tmp);
670 
671 exit:
672     sd_bus_message_unref(reply);
673 
674     return r;
675 }
676