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