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