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 "internal.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 #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 
sarraylen(char * array[])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 
sarrayfree(char * array[])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 
sarraydup(char * array[])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     {
138         return NULL;
139     }
140 
141     for (i = 0; i < count; ++i)
142     {
143         ret[i] = strdup(array[i]);
144         if (!ret[i])
145         {
146             goto error;
147         }
148     }
149 
150     return ret;
151 
152 error:
153     sarrayfree(ret);
154     return NULL;
155 }
156 
async_wait_timeout_callback(_unused_ sd_event_source * s,_unused_ uint64_t usec,void * userdata)157 static int async_wait_timeout_callback(_unused_ sd_event_source* s,
158                                        _unused_ uint64_t usec, void* userdata)
159 {
160     int r;
161     struct async_wait_callback_data* data = userdata;
162     mapper_async_wait* wait = data->wait;
163 
164     sd_event_source_unref(data->event_source);
165     r = sd_bus_call_method_async(
166         wait->conn, NULL, "xyz.openbmc_project.ObjectMapper",
167         "/xyz/openbmc_project/object_mapper",
168         "xyz.openbmc_project.ObjectMapper", "GetObject",
169         async_wait_getobject_callback, data, "sas", data->path, 0, NULL);
170     if (r < 0)
171     {
172         async_wait_done(r, wait);
173         free(data);
174     }
175 
176     return 0;
177 }
178 
async_wait_getobject_callback(sd_bus_message * m,void * userdata,_unused_ sd_bus_error * e)179 static int async_wait_getobject_callback(sd_bus_message* m, void* userdata,
180                                          _unused_ sd_bus_error* e)
181 {
182     size_t i;
183     int r;
184     struct async_wait_callback_data* data = userdata;
185     mapper_async_wait* wait = data->wait;
186     uint64_t next_retry;
187 
188     if (wait->finished)
189     {
190         goto exit;
191     }
192 
193     if (sd_bus_message_is_method_error(
194             m, "xyz.openbmc_project.Common.Error.ResourceNotFound"))
195     {
196         goto exit;
197     }
198 
199     r = sd_bus_message_get_errno(m);
200 
201     if ((r == EBUSY || r == ENOBUFS) && data->retry < mapper_busy_retries)
202     {
203         r = sd_event_now(wait->loop, CLOCK_MONOTONIC, &next_retry);
204         if (r < 0)
205         {
206             async_wait_done(r, wait);
207             goto exit;
208         }
209 
210         next_retry += mapper_busy_delay_interval_usec * (1 << data->retry);
211         r = sd_event_add_time(wait->loop, &data->event_source, CLOCK_MONOTONIC,
212                               next_retry, 0, async_wait_timeout_callback, data);
213         ++data->retry;
214         if (r < 0)
215         {
216             async_wait_done(r, wait);
217             goto exit;
218         }
219 
220         return 0;
221     }
222 
223     if (r)
224     {
225         async_wait_done(-r, wait);
226         goto exit;
227     }
228 
229     for (i = 0; i < wait->count; ++i)
230     {
231         if (!strcmp(data->path, wait->objs[i]))
232         {
233             wait->status[i] = 1;
234         }
235     }
236 
237     if (async_wait_check_done(wait))
238     {
239         async_wait_done(0, wait);
240     }
241 
242 exit:
243     free(data);
244     return 0;
245 }
246 
async_wait_get_objects(mapper_async_wait * wait)247 static int async_wait_get_objects(mapper_async_wait* wait)
248 {
249     size_t i;
250     int r;
251     struct async_wait_callback_data* data = NULL;
252 
253     for (i = 0; i < wait->count; ++i)
254     {
255         if (wait->status[i])
256         {
257             continue;
258         }
259         data = malloc(sizeof(*data));
260         data->wait = wait;
261         data->path = wait->objs[i];
262         data->retry = 0;
263         data->event_source = NULL;
264         r = sd_bus_call_method_async(
265             wait->conn, NULL, "xyz.openbmc_project.ObjectMapper",
266             "/xyz/openbmc_project/object_mapper",
267             "xyz.openbmc_project.ObjectMapper", "GetObject",
268             async_wait_getobject_callback, data, "sas", wait->objs[i], 0, NULL);
269         if (r < 0)
270         {
271             free(data);
272             fprintf(stderr, "Error invoking method: %s\n", strerror(-r));
273             return r;
274         }
275     }
276 
277     return 0;
278 }
279 
async_wait_match_introspection_complete(_unused_ sd_bus_message * m,void * w,_unused_ sd_bus_error * e)280 static int async_wait_match_introspection_complete(
281     _unused_ sd_bus_message* m, void* w, _unused_ sd_bus_error* e)
282 {
283     int r;
284 
285     mapper_async_wait* wait = w;
286     if (wait->finished)
287     {
288         return 0;
289     }
290 
291     r = async_wait_get_objects(wait);
292     if (r < 0)
293     {
294         async_wait_done(r, wait);
295     }
296 
297     return 0;
298 }
299 
async_wait_done(int r,mapper_async_wait * w)300 static void async_wait_done(int r, mapper_async_wait* w)
301 {
302     if (w->finished)
303     {
304         return;
305     }
306 
307     w->finished = 1;
308     sd_bus_slot_unref(w->introspection_slot);
309     sd_bus_slot_unref(w->intf_slot);
310 
311     if (w->callback)
312     {
313         w->callback(r, w->userdata);
314     }
315 }
316 
async_wait_check_done(mapper_async_wait * w)317 static int async_wait_check_done(mapper_async_wait* w)
318 {
319     size_t i;
320 
321     if (w->finished)
322     {
323         return 1;
324     }
325 
326     for (i = 0; i < w->count; ++i)
327     {
328         if (!w->status[i])
329         {
330             return 0;
331         }
332     }
333 
334     return 1;
335 }
336 
mapper_wait_async_free(mapper_async_wait * w)337 _public_ void mapper_wait_async_free(mapper_async_wait* w)
338 {
339     free(w->status);
340     sarrayfree(w->objs);
341     free(w);
342 }
343 
mapper_wait_async(sd_bus * conn,sd_event * loop,char * objs[],void (* callback)(int,void *),void * userdata,mapper_async_wait ** w)344 _public_ int mapper_wait_async(sd_bus* conn, sd_event* loop, char* objs[],
345                                void (*callback)(int, void*), void* userdata,
346                                mapper_async_wait** w)
347 {
348     int r;
349     mapper_async_wait* wait = NULL;
350 
351     wait = malloc(sizeof(*wait));
352     if (!wait)
353     {
354         return -ENOMEM;
355     }
356 
357     memset(wait, 0, sizeof(*wait));
358     wait->conn = conn;
359     wait->loop = loop;
360     wait->callback = callback;
361     wait->userdata = userdata;
362     wait->count = sarraylen(objs);
363     if (!wait->count)
364     {
365         r = 0;
366         goto free_wait;
367     }
368 
369     wait->objs = sarraydup(objs);
370     if (!wait->objs)
371     {
372         r = -ENOMEM;
373         goto free_wait;
374     }
375 
376     wait->status = malloc(sizeof(*wait->status) * wait->count);
377     if (!wait->status)
378     {
379         r = -ENOMEM;
380         goto free_objs;
381     }
382     memset(wait->status, 0, sizeof(*wait->status) * wait->count);
383 
384     r = sd_bus_add_match(conn, &wait->introspection_slot,
385                          async_wait_introspection_match,
386                          async_wait_match_introspection_complete, wait);
387     if (r < 0)
388     {
389         fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
390         goto free_status;
391     }
392 
393     r = sd_bus_add_match(conn, &wait->intf_slot,
394                          async_wait_interfaces_added_match,
395                          async_wait_match_introspection_complete, wait);
396     if (r < 0)
397     {
398         fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
399         goto unref_name_slot;
400     }
401 
402     r = async_wait_get_objects(wait);
403     if (r < 0)
404     {
405         fprintf(stderr, "Error calling method: %s\n", strerror(-r));
406         goto unref_intf_slot;
407     }
408 
409     *w = wait;
410 
411     return 0;
412 
413 unref_intf_slot:
414     sd_bus_slot_unref(wait->intf_slot);
415 unref_name_slot:
416     sd_bus_slot_unref(wait->introspection_slot);
417 free_status:
418     free(wait->status);
419 free_objs:
420     sarrayfree(wait->objs);
421 free_wait:
422     free(wait);
423 
424     return r;
425 }
426 
async_subtree_timeout_callback(_unused_ sd_event_source * s,_unused_ uint64_t usec,void * userdata)427 static int async_subtree_timeout_callback(
428     _unused_ sd_event_source* s, _unused_ uint64_t usec, void* userdata)
429 {
430     int r;
431     struct mapper_async_subtree* subtree = userdata;
432 
433     sd_event_source_unref(subtree->event_source);
434     r = sd_bus_call_method_async(
435         subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper",
436         "/xyz/openbmc_project/object_mapper",
437         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
438         async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0,
439         1, subtree->interface);
440     if (r < 0)
441     {
442         async_subtree_done(r, subtree);
443     }
444 
445     return 0;
446 }
447 
async_subtree_getpaths_callback(sd_bus_message * m,void * userdata,_unused_ sd_bus_error * e)448 static int async_subtree_getpaths_callback(sd_bus_message* m, void* userdata,
449                                            _unused_ sd_bus_error* e)
450 {
451     int r;
452     struct mapper_async_subtree* subtree = userdata;
453     uint64_t next_retry;
454 
455     if (subtree->finished)
456     {
457         goto exit;
458     }
459 
460     r = sd_bus_message_get_errno(m);
461 
462     if (sd_bus_message_is_method_error(
463             m, "xyz.openbmc_project.Common.Error.ResourceNotFound"))
464     {
465         if (subtree->op == MAPPER_OP_REMOVE)
466         {
467             r = 0;
468         }
469         else
470         {
471             goto exit;
472         }
473     }
474 
475     if ((r == EBUSY || r == ENOBUFS) && subtree->retry < mapper_busy_retries)
476     {
477         r = sd_event_now(subtree->loop, CLOCK_MONOTONIC, &next_retry);
478         if (r < 0)
479         {
480             async_subtree_done(r, subtree);
481             goto exit;
482         }
483 
484         next_retry += mapper_busy_delay_interval_usec * (1 << subtree->retry);
485         r = sd_event_add_time(subtree->loop, &subtree->event_source,
486                               CLOCK_MONOTONIC, next_retry, 0,
487                               async_subtree_timeout_callback, subtree);
488         ++subtree->retry;
489         if (r < 0)
490         {
491             async_subtree_done(r, subtree);
492             goto exit;
493         }
494 
495         return 0;
496     }
497 
498     if (r)
499     {
500         async_subtree_done(-r, subtree);
501         goto exit;
502     }
503 
504     if (subtree->op == MAPPER_OP_REMOVE)
505     {
506         r = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s");
507         if (r < 0)
508         {
509             async_subtree_done(r, subtree);
510             goto exit;
511         }
512 
513         r = sd_bus_message_at_end(m, false);
514         if (r < 0)
515         {
516             async_subtree_done(r, subtree);
517             goto exit;
518         }
519 
520         /* For remove, operation is complete when the interface is not present
521          * we know it is empty if the returned array is empty
522          */
523         if (r)
524             async_subtree_done(0, subtree);
525     }
526 
527 exit:
528     return 0;
529 }
530 
async_subtree_getpaths(mapper_async_subtree * subtree)531 static int async_subtree_getpaths(mapper_async_subtree* subtree)
532 {
533     int r = 0;
534 
535     subtree->retry = 0;
536     subtree->event_source = NULL;
537     r = sd_bus_call_method_async(
538         subtree->conn, NULL, "xyz.openbmc_project.ObjectMapper",
539         "/xyz/openbmc_project/object_mapper",
540         "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
541         async_subtree_getpaths_callback, subtree, "sias", subtree->namespace, 0,
542         1, subtree->interface);
543     if (r < 0)
544     {
545         fprintf(stderr, "Error invoking method: %s\n", strerror(-r));
546         return r;
547     }
548 
549     return 0;
550 }
551 
async_subtree_match_callback(_unused_ sd_bus_message * m,void * t,_unused_ sd_bus_error * e)552 static int async_subtree_match_callback(_unused_ sd_bus_message* m, void* t,
553                                         _unused_ sd_bus_error* e)
554 {
555     int r;
556 
557     mapper_async_subtree* subtree = t;
558     if (subtree->finished)
559     {
560         return 0;
561     }
562 
563     r = async_subtree_getpaths(subtree);
564     if (r < 0)
565     {
566         async_subtree_done(r, subtree);
567     }
568 
569     return 0;
570 }
571 
async_subtree_done(int r,mapper_async_subtree * t)572 static void async_subtree_done(int r, mapper_async_subtree* t)
573 {
574     if (t->finished)
575     {
576         return;
577     }
578 
579     t->finished = 1;
580     sd_bus_slot_unref(t->slot);
581 
582     if (t->callback)
583     {
584         t->callback(r, t->userdata);
585     }
586 }
587 
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)588 _public_ int mapper_subtree_async(sd_bus* conn, sd_event* loop, char* namespace,
589                                   char* interface, void (*callback)(int, void*),
590                                   void* userdata, mapper_async_subtree** t,
591                                   int op)
592 {
593     int r = 0;
594     mapper_async_subtree* subtree = NULL;
595 
596     subtree = malloc(sizeof(*subtree));
597     if (!subtree)
598     {
599         return -ENOMEM;
600     }
601 
602     memset(subtree, 0, sizeof(*subtree));
603     subtree->conn = conn;
604     subtree->loop = loop;
605     subtree->namespace = namespace;
606     subtree->interface = interface;
607     subtree->callback = callback;
608     subtree->userdata = userdata;
609     subtree->op = op;
610 
611     if (subtree->op == MAPPER_OP_REMOVE)
612     {
613         r = sd_bus_add_match(conn, &subtree->slot, interfaces_removed_match,
614                              async_subtree_match_callback, subtree);
615         if (r < 0)
616         {
617             fprintf(stderr, "Error adding match rule: %s\n", strerror(-r));
618             goto unref_slot;
619         }
620     }
621     else
622     {
623         /* Operation not supported */
624         r = -EINVAL;
625         goto free_subtree;
626     }
627 
628     r = async_subtree_getpaths(subtree);
629     if (r < 0)
630     {
631         fprintf(stderr, "Error calling method: %s\n", strerror(-r));
632         goto unref_slot;
633     }
634 
635     *t = subtree;
636 
637     return 0;
638 
639 unref_slot:
640     sd_bus_slot_unref(subtree->slot);
641 free_subtree:
642     free(subtree);
643 
644     return r;
645 }
646 
mapper_get_object(sd_bus * conn,const char * obj,sd_bus_message ** reply)647 _public_ int mapper_get_object(sd_bus* conn, const char* obj,
648                                sd_bus_message** reply)
649 {
650     sd_bus_message* request = NULL;
651     int r, retry = 0;
652 
653     r = sd_bus_message_new_method_call(
654         conn, &request, "xyz.openbmc_project.ObjectMapper",
655         "/xyz/openbmc_project/object_mapper",
656         "xyz.openbmc_project.ObjectMapper", "GetObject");
657     if (r < 0)
658     {
659         goto exit;
660     }
661 
662     r = sd_bus_message_append(request, "s", obj);
663     if (r < 0)
664     {
665         goto exit;
666     }
667     r = sd_bus_message_append(request, "as", 0, NULL);
668     if (r < 0)
669     {
670         goto exit;
671     }
672 
673     while (true)
674     {
675         r = sd_bus_call(conn, request, 0, NULL, reply);
676         if (r == -EBUSY || r == -ENOBUFS)
677         {
678             if (retry >= mapper_busy_retries)
679                 break;
680 
681             usleep(mapper_busy_delay_interval_usec * (1 << retry));
682             ++retry;
683             continue;
684         }
685         break;
686     }
687 
688     if (r < 0)
689     {
690         goto exit;
691     }
692 
693 exit:
694     sd_bus_message_unref(request);
695 
696     return r;
697 }
698 
mapper_get_service(sd_bus * conn,const char * obj,char ** service)699 _public_ int mapper_get_service(sd_bus* conn, const char* obj, char** service)
700 {
701     sd_bus_message* reply = NULL;
702     const char* tmp;
703     int r;
704 
705     r = mapper_get_object(conn, obj, &reply);
706     if (r < 0)
707     {
708         goto exit;
709     }
710 
711     r = sd_bus_message_enter_container(reply, 0, NULL);
712     if (r < 0)
713     {
714         goto exit;
715     }
716 
717     r = sd_bus_message_enter_container(reply, 0, NULL);
718     if (r < 0)
719     {
720         goto exit;
721     }
722 
723     r = sd_bus_message_read(reply, "s", &tmp);
724     if (r < 0)
725     {
726         goto exit;
727     }
728 
729     *service = strdup(tmp);
730 
731 exit:
732     sd_bus_message_unref(reply);
733 
734     return r;
735 }
736