xref: /openbmc/phosphor-objmgr/libmapper/mapper.c (revision 2afe718f96f1a6d947de4ea0e7d06853400a72a7)
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 <stdlib.h>
17 #include <string.h>
18 #include <stdio.h>
19 #include <errno.h>
20 #include <systemd/sd-bus.h>
21 #include "mapper.h"
22 
23 static const char *async_wait_name_owner_match =
24 	"type='signal',"
25 	"sender='org.freedesktop.DBus',"
26 	"interface='org.freedesktop.DBus',"
27 	"member='NameOwnerChanged',"
28 	"path='/org/freedesktop/DBus'";
29 
30 static const char *async_wait_interfaces_added_match =
31 	"type='signal',"
32 	"interface='org.freedesktop.DBus.ObjectManager',"
33 	"member='InterfacesAdded'";
34 
35 struct mapper_async_wait
36 {
37 	char **objs;
38 	void (*callback)(int, void *);
39 	void *userdata;
40 	sd_bus *conn;
41 	sd_bus_slot *name_owner_slot;
42 	sd_bus_slot *intf_slot;
43 	int *status;
44 	int count;
45 	int finished;
46 	int r;
47 };
48 
49 struct async_wait_callback_data
50 {
51 	mapper_async_wait *wait;
52 	const char *path;
53 };
54 
55 static int async_wait_match_name_owner_changed(sd_bus_message *, void *,
56 		sd_bus_error *);
57 static int async_wait_match_interfaces_added(sd_bus_message *, void *,
58 		sd_bus_error *);
59 static int async_wait_check_done(mapper_async_wait *);
60 static void async_wait_done(int r, mapper_async_wait *);
61 static int async_wait_get_objects(mapper_async_wait *);
62 
63 static int sarraylen(char *array[])
64 {
65 	int count = 0;
66 	char **p = array;
67 
68 	while(*p != NULL) {
69 		++count;
70 		++p;
71 	}
72 
73 	return count;
74 }
75 
76 static void sarrayfree(char *array[])
77 {
78 	char **p = array;
79 	while(*p != NULL) {
80 		free(*p);
81 		++p;
82 	}
83 	free(array);
84 }
85 
86 static char **sarraydup(char *array[])
87 {
88 	int count = sarraylen(array);
89 	int i;
90 	char **ret = NULL;
91 
92 	ret = malloc(sizeof(*ret) * count);
93 	if(!ret)
94 		return NULL;
95 
96 	for(i=0; i<count; ++i) {
97 		ret[i] = strdup(array[i]);
98 		if(!ret[i])
99 			goto error;
100 	}
101 
102 	return ret;
103 
104 error:
105 	sarrayfree(ret);
106 	return NULL;
107 }
108 
109 static int async_wait_getobject_callback(sd_bus_message *m,
110 		void *userdata,
111 		sd_bus_error *e)
112 {
113 	int i, r;
114 	const char *msg;
115 	struct async_wait_callback_data *data = userdata;
116 	mapper_async_wait *wait = data->wait;
117 
118 	if(wait->finished)
119 		return 0;
120 	if(sd_bus_message_get_errno(m))
121 		return 0;
122 
123 	for(i=0; i<wait->count; ++i) {
124 		if(!strcmp(data->path, wait->objs[i])) {
125 			wait->status[i] = 1;
126 		}
127 	}
128 
129 	free(data);
130 	if(async_wait_check_done(wait))
131 		async_wait_done(0, wait);
132 
133 	return 0;
134 }
135 
136 static int async_wait_get_objects(mapper_async_wait *wait)
137 {
138 	int i, r;
139 	struct async_wait_callback_data *data = NULL;
140 
141 	for(i=0; i<wait->count; ++i) {
142 		if(wait->status[i])
143 			continue;
144 		data = malloc(sizeof(*data));
145 		data->wait = wait;
146 		data->path = wait->objs[i];
147 		r = sd_bus_call_method_async(
148 				wait->conn,
149 				NULL,
150 				"org.openbmc.ObjectMapper",
151 				"/org/openbmc/ObjectMapper",
152 				"org.openbmc.ObjectMapper",
153 				"GetObject",
154 				async_wait_getobject_callback,
155 				data,
156 				"s",
157 				wait->objs[i]);
158 		if(r < 0) {
159 			free(data);
160 			fprintf(stderr, "Error invoking method: %s\n",
161 					strerror(-r));
162 			return r;
163 		}
164 	}
165 
166 	return 0;
167 }
168 
169 static int async_wait_match_name_owner_changed(sd_bus_message *m, void *w,
170 		sd_bus_error *e)
171 {
172 	int i, r;
173 
174 	mapper_async_wait *wait = w;
175 	if(wait->finished)
176 		return 0;
177 
178 	r = async_wait_get_objects(wait);
179 	if(r < 0)
180 		async_wait_done(r, wait);
181 
182 	return 0;
183 }
184 
185 static int async_wait_match_interfaces_added(sd_bus_message *m, void *w,
186 		sd_bus_error *e)
187 {
188 	int i, r;
189 	mapper_async_wait *wait = w;
190 	const char *path;
191 
192 	if(wait->finished)
193 		return 0;
194 
195 	r = sd_bus_message_read(m, "o", &path);
196 	if (r < 0) {
197 		fprintf(stderr, "Error reading message: %s\n",
198 				strerror(-r));
199 		goto finished;
200 	}
201 
202 	for(i=0; i<wait->count; ++i) {
203 		if(!strcmp(path, wait->objs[i]))
204 			wait->status[i] = 1;
205 	}
206 
207 finished:
208 	if(r < 0 || async_wait_check_done(wait))
209 		async_wait_done(r < 0 ? r : 0, wait);
210 
211 	return 0;
212 }
213 
214 static void async_wait_done(int r, mapper_async_wait *w)
215 {
216 	if(w->finished)
217 		return;
218 
219 	w->finished = 1;
220 	sd_bus_slot_unref(w->name_owner_slot);
221 	sd_bus_slot_unref(w->intf_slot);
222 
223 	if(w->callback)
224 		w->callback(r, w->userdata);
225 }
226 
227 static int async_wait_check_done(mapper_async_wait *w)
228 {
229 	int i;
230 
231 	if(w->finished)
232 		return 1;
233 
234 	for(i=0; i<w->count; ++i)
235 		if(!w->status[i])
236 			return 0;
237 
238 	return 1;
239 }
240 
241 void mapper_wait_async_free(mapper_async_wait *w)
242 {
243 	free(w->status);
244 	sarrayfree(w->objs);
245 	free(w);
246 }
247 
248 int mapper_wait_async(sd_bus *conn,
249 		char *objs[],
250 		void (*callback)(int, void *),
251 		void *userdata,
252 		mapper_async_wait **w)
253 {
254 	int r;
255 	mapper_async_wait *wait = NULL;
256 
257 	wait = malloc(sizeof(*wait));
258 	if(!wait)
259 		return -ENOMEM;
260 
261 	memset(wait, 0, sizeof(*wait));
262 	wait->conn = conn;
263 	wait->callback = callback;
264 	wait->userdata = userdata;
265 	wait->count = sarraylen(objs);
266 	if(!wait->count)
267 		return 0;
268 
269 	wait->objs = sarraydup(objs);
270 	if(!wait->objs) {
271 		r = -ENOMEM;
272 		goto free_wait;
273 	}
274 
275 	wait->status = malloc(sizeof(*wait->status) * wait->count);
276 	if(!wait->status) {
277 		r = -ENOMEM;
278 		goto free_objs;
279 	}
280 	memset(wait->status, 0, sizeof(*wait->status) * wait->count);
281 
282 	r = sd_bus_add_match(conn,
283                         &wait->name_owner_slot,
284 			async_wait_name_owner_match,
285                         async_wait_match_name_owner_changed,
286                         wait);
287 	if(r < 0) {
288 		fprintf(stderr, "Error adding match rule: %s\n",
289 				strerror(-r));
290 		goto free_status;
291 	}
292 
293 	r = sd_bus_add_match(conn,
294                         &wait->intf_slot,
295 			async_wait_interfaces_added_match,
296                         async_wait_match_interfaces_added,
297                         wait);
298 	if(r < 0) {
299 		fprintf(stderr, "Error adding match rule: %s\n",
300 				strerror(-r));
301 		goto unref_name_slot;
302 	}
303 
304 	r = async_wait_get_objects(wait);
305 	if(r < 0) {
306 		fprintf(stderr, "Error calling method: %s\n",
307 				strerror(-r));
308 		goto unref_intf_slot;
309 	}
310 
311 	*w = wait;
312 
313 	return 0;
314 
315 unref_intf_slot:
316 	sd_bus_slot_unref(wait->intf_slot);
317 unref_name_slot:
318 	sd_bus_slot_unref(wait->name_owner_slot);
319 free_status:
320 	free(wait->status);
321 free_objs:
322 	sarrayfree(wait->objs);
323 free_wait:
324 	free(wait);
325 
326 	return r;
327 }
328 
329 int mapper_get_service(sd_bus *conn, const char *obj, char **service)
330 {
331 	sd_bus_error error = SD_BUS_ERROR_NULL;
332 	sd_bus_message *request = NULL, *reply = NULL;
333 	const char *tmp;
334 	int r;
335 
336 	r = sd_bus_message_new_method_call(
337 			conn,
338 			&request,
339 			"org.openbmc.ObjectMapper",
340 			"/org/openbmc/ObjectMapper",
341 			"org.openbmc.ObjectMapper",
342 			"GetObject");
343 	if (r < 0)
344 		goto exit;
345 
346 	r = sd_bus_message_append(request, "s", obj);
347 	if (r < 0)
348 		goto exit;
349 
350 	r = sd_bus_call(conn, request, 0, &error, &reply);
351 	if (r < 0)
352 		goto exit;
353 
354 	r = sd_bus_message_enter_container(reply, 0, NULL);
355 	if (r < 0)
356 		goto exit;
357 
358 	r = sd_bus_message_enter_container(reply, 0, NULL);
359 	if (r < 0)
360 		goto exit;
361 
362 	r = sd_bus_message_read(reply, "s", &tmp);
363 	if (r < 0)
364 		goto exit;
365 
366 	*service = strdup(tmp);
367 
368 exit:
369 	sd_bus_error_free(&error);
370 	sd_bus_message_unref(request);
371 	sd_bus_message_unref(reply);
372 
373 	return r;
374 }
375