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