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