xref: /openbmc/phosphor-objmgr/libmapper/app.c (revision aa9c24a6636dc6c89b289e7ae3457c489e152c37)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright 2016 IBM Corporation
3 
4 #include "mapper.h"
5 
6 #include <errno.h>
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <systemd/sd-bus.h>
10 #include <systemd/sd-event.h>
11 #include <unistd.h>
12 
quit(int r,void * loop)13 static void quit(int r, void* loop)
14 {
15     sd_event_exit((sd_event*)loop, r);
16 }
17 
wait_main(int argc,char * argv[])18 static int wait_main(int argc, char* argv[])
19 {
20     int r;
21     sd_bus* conn = NULL;
22     sd_event* loop = NULL;
23     mapper_async_wait* wait = NULL;
24     size_t attempts = 0;
25     const size_t max_attempts = 20;
26 
27     if (argc < 3)
28     {
29         fprintf(stderr, "Usage: %s wait OBJECTPATH...\n", argv[0]);
30         exit(EXIT_FAILURE);
31     }
32 
33     /* Mapper waits are typically run early in the boot process, and in some
34      * cases the CPU and/or object manager daemon are so busy that the
35      * GetObject call may fail with a timeout and cause the event loop to exit.
36      * If this happens, retry a few times.  Don't retry on other failures.
37      */
38     while (1)
39     {
40         attempts++;
41 
42         r = sd_bus_default(&conn);
43         if (r < 0)
44         {
45             fprintf(stderr, "Error connecting to system bus: %s\n",
46                     strerror(-r));
47             goto finish;
48         }
49 
50         r = sd_event_default(&loop);
51         if (r < 0)
52         {
53             fprintf(stderr, "Error obtaining event loop: %s\n", strerror(-r));
54 
55             goto finish;
56         }
57 
58         r = sd_bus_attach_event(conn, loop, SD_EVENT_PRIORITY_NORMAL);
59         if (r < 0)
60         {
61             fprintf(stderr,
62                     "Failed to attach system "
63                     "bus to event loop: %s\n",
64                     strerror(-r));
65             goto finish;
66         }
67 
68         r = mapper_wait_async(conn, loop, argv + 2, quit, loop, &wait);
69         if (r < 0)
70         {
71             fprintf(stderr, "Error configuring waitlist: %s\n", strerror(-r));
72             goto finish;
73         }
74 
75         r = sd_event_loop(loop);
76         if (r < 0)
77         {
78             fprintf(stderr, "Event loop exited: %s\n", strerror(-r));
79 
80             if (-r == ETIMEDOUT || -r == EHOSTUNREACH)
81             {
82                 if (attempts <= max_attempts)
83                 {
84                     fprintf(stderr, "Retrying in 1s\n");
85                     sleep(1);
86                     sd_event_unref(loop);
87                     sd_bus_unref(conn);
88                     continue;
89                 }
90                 else
91                 {
92                     fprintf(stderr, "Giving up\n");
93                 }
94             }
95             else
96             {
97                 goto finish;
98             }
99         }
100 
101         break;
102     }
103 
104 finish:
105     exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
106 }
107 
subtree_main(int argc,char * argv[])108 static int subtree_main(int argc, char* argv[])
109 {
110     int r = 0;
111     int op = 0;
112     static const char* token = ":";
113     char* tmp = NULL;
114     char* namespace = NULL;
115     char* interface = NULL;
116     sd_bus* conn = NULL;
117     sd_event* loop = NULL;
118     mapper_async_subtree* subtree = NULL;
119 
120     if (argc != 3)
121     {
122         fprintf(stderr,
123                 "Usage: %s subtree-remove "
124                 "NAMESPACE%sINTERFACE\n",
125                 argv[0], token);
126         exit(EXIT_FAILURE);
127     }
128 
129     op = MAPPER_OP_REMOVE;
130 
131     namespace = strtok_r(argv[2], token, &tmp);
132     interface = strtok_r(NULL, token, &tmp);
133     if ((namespace == NULL) || (interface == NULL))
134     {
135         fprintf(stderr, "Token '%s' was not found in '%s'\n", token, argv[2]);
136         exit(EXIT_FAILURE);
137     }
138 
139     r = sd_bus_default(&conn);
140     if (r < 0)
141     {
142         fprintf(stderr, "Error connecting to system bus: %s\n", strerror(-r));
143         goto finish;
144     }
145 
146     r = sd_event_default(&loop);
147     if (r < 0)
148     {
149         fprintf(stderr, "Error obtaining event loop: %s\n", strerror(-r));
150         goto finish;
151     }
152 
153     r = sd_bus_attach_event(conn, loop, SD_EVENT_PRIORITY_NORMAL);
154     if (r < 0)
155     {
156         fprintf(stderr, "Failed to attach system bus to event loop: %s\n",
157                 strerror(-r));
158         goto finish;
159     }
160 
161     r = mapper_subtree_async(conn, loop, namespace, interface, quit, loop,
162                              &subtree, op);
163     if (r < 0)
164     {
165         fprintf(stderr, "Error configuring subtree list: %s\n", strerror(-r));
166         goto finish;
167     }
168 
169     r = sd_event_loop(loop);
170     if (r < 0)
171     {
172         /* If this function has been called after the interface in   */
173         /* question has already been removed, then GetSubTree will   */
174         /* fail and it will show up here.  Treat as success instead. */
175         if (r == -ENXIO)
176         {
177             r = 0;
178         }
179         else
180         {
181             fprintf(stderr, "Error starting event loop: %d(%s)\n", r,
182                     strerror(-r));
183             goto finish;
184         }
185     }
186 
187 finish:
188     exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
189 }
190 
191 /* print out the distinct dbus service name for the input dbus path */
get_service_main(int argc,char * argv[])192 static int get_service_main(int argc, char* argv[])
193 {
194     int r;
195     sd_bus* conn = NULL;
196     char* service = NULL;
197 
198     if (argc != 3)
199     {
200         fprintf(stderr, "Usage: %s get-service OBJECTPATH\n", argv[0]);
201         exit(EXIT_FAILURE);
202     }
203 
204     r = sd_bus_default(&conn);
205     if (r < 0)
206     {
207         fprintf(stderr, "Error connecting to system bus: %s\n", strerror(-r));
208         goto finish;
209     }
210 
211     r = mapper_get_service(conn, argv[2], &service);
212     if (r < 0)
213     {
214         fprintf(stderr, "Error finding '%s' service: %s\n", argv[2],
215                 strerror(-r));
216         goto finish;
217     }
218 
219     printf("%s\n", service);
220 
221 finish:
222     exit(r < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
223 }
224 
main(int argc,char * argv[])225 int main(int argc, char* argv[])
226 {
227     static const char* usage =
228         "Usage: %s {COMMAND} ...\n"
229         "\nCOMMANDS:\n"
230         "  wait           wait for the specified objects to appear on the "
231         "DBus\n"
232         "  subtree-remove\n"
233         "                 wait until the specified interface is not present\n"
234         "                 in any of the subtrees of the specified namespace\n"
235         "  get-service    return the service identifier for input path\n";
236 
237     if (argc < 2)
238     {
239         fprintf(stderr, usage, argv[0]);
240         exit(EXIT_FAILURE);
241     }
242 
243     if (!strcmp(argv[1], "wait"))
244     {
245         wait_main(argc, argv);
246     }
247 
248     if (!strcmp(argv[1], "subtree-remove"))
249     {
250         subtree_main(argc, argv);
251     }
252 
253     if (!strcmp(argv[1], "get-service"))
254     {
255         get_service_main(argc, argv);
256     }
257 
258     fprintf(stderr, usage, argv[0]);
259     exit(EXIT_FAILURE);
260 }
261