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