xref: /openbmc/phosphor-objmgr/libmapper/mapper.c (revision a6797f83a23778b6f99d1e0f471b908fd01cb7b5)
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;
114 	struct async_wait_callback_data *data = userdata;
115 	mapper_async_wait *wait = data->wait;
116 
117 	if(wait->finished)
118 		return 0;
119 	if(sd_bus_message_get_errno(m))
120 		return 0;
121 
122 	for(i=0; i<wait->count; ++i) {
123 		if(!strcmp(data->path, wait->objs[i])) {
124 			wait->status[i] = 1;
125 		}
126 	}
127 
128 	free(data);
129 	if(async_wait_check_done(wait))
130 		async_wait_done(0, wait);
131 
132 	return 0;
133 }
134 
135 static int async_wait_get_objects(mapper_async_wait *wait)
136 {
137 	int i, r;
138 	struct async_wait_callback_data *data = NULL;
139 
140 	for(i=0; i<wait->count; ++i) {
141 		if(wait->status[i])
142 			continue;
143 		data = malloc(sizeof(*data));
144 		data->wait = wait;
145 		data->path = wait->objs[i];
146 		r = sd_bus_call_method_async(
147 				wait->conn,
148 				NULL,
149 				"org.openbmc.ObjectMapper",
150 				"/org/openbmc/ObjectMapper",
151 				"org.openbmc.ObjectMapper",
152 				"GetObject",
153 				async_wait_getobject_callback,
154 				data,
155 				"s",
156 				wait->objs[i]);
157 		if(r < 0) {
158 			free(data);
159 			fprintf(stderr, "Error invoking method: %s\n",
160 					strerror(-r));
161 			return r;
162 		}
163 	}
164 
165 	return 0;
166 }
167 
168 static int async_wait_match_name_owner_changed(sd_bus_message *m, void *w,
169 		sd_bus_error *e)
170 {
171 	int r;
172 
173 	mapper_async_wait *wait = w;
174 	if(wait->finished)
175 		return 0;
176 
177 	r = async_wait_get_objects(wait);
178 	if(r < 0)
179 		async_wait_done(r, wait);
180 
181 	return 0;
182 }
183 
184 static int async_wait_match_interfaces_added(sd_bus_message *m, void *w,
185 		sd_bus_error *e)
186 {
187 	int i, r;
188 	mapper_async_wait *wait = w;
189 	const char *path;
190 
191 	if(wait->finished)
192 		return 0;
193 
194 	r = sd_bus_message_read(m, "o", &path);
195 	if (r < 0) {
196 		fprintf(stderr, "Error reading message: %s\n",
197 				strerror(-r));
198 		goto finished;
199 	}
200 
201 	for(i=0; i<wait->count; ++i) {
202 		if(!strcmp(path, wait->objs[i]))
203 			wait->status[i] = 1;
204 	}
205 
206 finished:
207 	if(r < 0 || async_wait_check_done(wait))
208 		async_wait_done(r < 0 ? r : 0, wait);
209 
210 	return 0;
211 }
212 
213 static void async_wait_done(int r, mapper_async_wait *w)
214 {
215 	if(w->finished)
216 		return;
217 
218 	w->finished = 1;
219 	sd_bus_slot_unref(w->name_owner_slot);
220 	sd_bus_slot_unref(w->intf_slot);
221 
222 	if(w->callback)
223 		w->callback(r, w->userdata);
224 }
225 
226 static int async_wait_check_done(mapper_async_wait *w)
227 {
228 	int i;
229 
230 	if(w->finished)
231 		return 1;
232 
233 	for(i=0; i<w->count; ++i)
234 		if(!w->status[i])
235 			return 0;
236 
237 	return 1;
238 }
239 
240 void mapper_wait_async_free(mapper_async_wait *w)
241 {
242 	free(w->status);
243 	sarrayfree(w->objs);
244 	free(w);
245 }
246 
247 int mapper_wait_async(sd_bus *conn,
248 		char *objs[],
249 		void (*callback)(int, void *),
250 		void *userdata,
251 		mapper_async_wait **w)
252 {
253 	int r;
254 	mapper_async_wait *wait = NULL;
255 
256 	wait = malloc(sizeof(*wait));
257 	if(!wait)
258 		return -ENOMEM;
259 
260 	memset(wait, 0, sizeof(*wait));
261 	wait->conn = conn;
262 	wait->callback = callback;
263 	wait->userdata = userdata;
264 	wait->count = sarraylen(objs);
265 	if(!wait->count)
266 		return 0;
267 
268 	wait->objs = sarraydup(objs);
269 	if(!wait->objs) {
270 		r = -ENOMEM;
271 		goto free_wait;
272 	}
273 
274 	wait->status = malloc(sizeof(*wait->status) * wait->count);
275 	if(!wait->status) {
276 		r = -ENOMEM;
277 		goto free_objs;
278 	}
279 	memset(wait->status, 0, sizeof(*wait->status) * wait->count);
280 
281 	r = sd_bus_add_match(conn,
282                         &wait->name_owner_slot,
283 			async_wait_name_owner_match,
284                         async_wait_match_name_owner_changed,
285                         wait);
286 	if(r < 0) {
287 		fprintf(stderr, "Error adding match rule: %s\n",
288 				strerror(-r));
289 		goto free_status;
290 	}
291 
292 	r = sd_bus_add_match(conn,
293                         &wait->intf_slot,
294 			async_wait_interfaces_added_match,
295                         async_wait_match_interfaces_added,
296                         wait);
297 	if(r < 0) {
298 		fprintf(stderr, "Error adding match rule: %s\n",
299 				strerror(-r));
300 		goto unref_name_slot;
301 	}
302 
303 	r = async_wait_get_objects(wait);
304 	if(r < 0) {
305 		fprintf(stderr, "Error calling method: %s\n",
306 				strerror(-r));
307 		goto unref_intf_slot;
308 	}
309 
310 	*w = wait;
311 
312 	return 0;
313 
314 unref_intf_slot:
315 	sd_bus_slot_unref(wait->intf_slot);
316 unref_name_slot:
317 	sd_bus_slot_unref(wait->name_owner_slot);
318 free_status:
319 	free(wait->status);
320 free_objs:
321 	sarrayfree(wait->objs);
322 free_wait:
323 	free(wait);
324 
325 	return r;
326 }
327 
328 int mapper_get_service(sd_bus *conn, const char *obj, char **service)
329 {
330 	sd_bus_error error = SD_BUS_ERROR_NULL;
331 	sd_bus_message *request = NULL, *reply = NULL;
332 	const char *tmp;
333 	int r;
334 
335 	r = sd_bus_message_new_method_call(
336 			conn,
337 			&request,
338 			"org.openbmc.ObjectMapper",
339 			"/org/openbmc/ObjectMapper",
340 			"org.openbmc.ObjectMapper",
341 			"GetObject");
342 	if (r < 0)
343 		goto exit;
344 
345 	r = sd_bus_message_append(request, "s", obj);
346 	if (r < 0)
347 		goto exit;
348 
349 	r = sd_bus_call(conn, request, 0, &error, &reply);
350 	if (r < 0)
351 		goto exit;
352 
353 	r = sd_bus_message_enter_container(reply, 0, NULL);
354 	if (r < 0)
355 		goto exit;
356 
357 	r = sd_bus_message_enter_container(reply, 0, NULL);
358 	if (r < 0)
359 		goto exit;
360 
361 	r = sd_bus_message_read(reply, "s", &tmp);
362 	if (r < 0)
363 		goto exit;
364 
365 	*service = strdup(tmp);
366 
367 exit:
368 	sd_bus_error_free(&error);
369 	sd_bus_message_unref(request);
370 	sd_bus_message_unref(reply);
371 
372 	return r;
373 }
374