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