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 #include <stdlib.h>
18 #include <string.h>
19 #include <stdio.h>
20 #include <errno.h>
21 #include <unistd.h>
22 #include <sys/timerfd.h>
23 #include <systemd/sd-bus.h>
24 #include <systemd/sd-event.h>
25 #include "mapper.h"
26 
27 static const char *async_wait_introspection_match =
28 	"type='signal',"
29 	"sender='xyz.openbmc_project.ObjectMapper',"
30 	"interface='xyz.openbmc_project.ObjectMapper.Private',"
31 	"member='IntrospectionComplete'";
32 
33 static const char *async_wait_interfaces_added_match =
34 	"type='signal',"
35 	"interface='org.freedesktop.DBus.ObjectManager',"
36 	"member='InterfacesAdded'";
37 
38 static const int mapper_busy_retries = 5;
39 static const uint64_t mapper_busy_delay_interval_usec = 1000000;
40 
41 struct mapper_async_wait
42 {
43 	char **objs;
44 	void (*callback)(int, void *);
45 	void *userdata;
46 	sd_event *loop;
47 	sd_bus *conn;
48 	sd_bus_slot *introspection_slot;
49 	sd_bus_slot *intf_slot;
50 	int *status;
51 	int count;
52 	int finished;
53 	int r;
54 };
55 
56 struct async_wait_callback_data
57 {
58 	mapper_async_wait *wait;
59 	const char *path;
60 	sd_event_source *event_source;
61 	int retry;
62 };
63 
64 static int async_wait_match_introspection_complete(sd_bus_message *, void *,
65 		sd_bus_error *);
66 static int async_wait_check_done(mapper_async_wait *);
67 static void async_wait_done(int r, mapper_async_wait *);
68 static int async_wait_get_objects(mapper_async_wait *);
69 static int async_wait_getobject_callback(sd_bus_message *,
70 		void *, sd_bus_error *);
71 
72 static int sarraylen(char *array[])
73 {
74 	int count = 0;
75 	char **p = array;
76 
77 	while(*p != NULL) {
78 		++count;
79 		++p;
80 	}
81 
82 	return count;
83 }
84 
85 static void sarrayfree(char *array[])
86 {
87 	char **p = array;
88 	while(*p != NULL) {
89 		free(*p);
90 		++p;
91 	}
92 	free(array);
93 }
94 
95 static char **sarraydup(char *array[])
96 {
97 	int count = sarraylen(array);
98 	int i;
99 	char **ret = NULL;
100 
101 	ret = malloc(sizeof(*ret) * count);
102 	if(!ret)
103 		return NULL;
104 
105 	for(i=0; i<count; ++i) {
106 		ret[i] = strdup(array[i]);
107 		if(!ret[i])
108 			goto error;
109 	}
110 
111 	return ret;
112 
113 error:
114 	sarrayfree(ret);
115 	return NULL;
116 }
117 
118 static int async_wait_timeout_callback(sd_event_source *s,
119 		uint64_t usec, void *userdata)
120 {
121 	int r;
122 	struct async_wait_callback_data *data = userdata;
123 	mapper_async_wait *wait = data->wait;
124 
125 	sd_event_source_unref(data->event_source);
126 	r = sd_bus_call_method_async(
127 			wait->conn,
128 			NULL,
129 			MAPPER_BUSNAME,
130 			MAPPER_PATH,
131 			MAPPER_INTERFACE,
132 			"GetObject",
133 			async_wait_getobject_callback,
134 			data,
135 			"sas",
136 			data->path,
137 			0,
138 			NULL);
139 	if(r < 0) {
140 		async_wait_done(r, wait);
141 		free(data);
142 	}
143 
144 	return 0;
145 }
146 
147 static int async_wait_getobject_callback(sd_bus_message *m,
148 		void *userdata,
149 		sd_bus_error *e)
150 {
151 	int i, r;
152 	struct async_wait_callback_data *data = userdata;
153 	mapper_async_wait *wait = data->wait;
154 	uint64_t now;
155 
156 	if(wait->finished)
157 		goto exit;
158 
159 	r = sd_bus_message_get_errno(m);
160 	if(r == ENOENT)
161 		goto exit;
162 
163 	if(r == EBUSY && data->retry < mapper_busy_retries) {
164 		r = sd_event_now(wait->loop,
165 				CLOCK_MONOTONIC,
166 				&now);
167 		if(r < 0) {
168 			async_wait_done(r, wait);
169 			goto exit;
170 		}
171 
172 		++data->retry;
173 		r = sd_event_add_time(wait->loop,
174 				&data->event_source,
175 				CLOCK_MONOTONIC,
176 				now + mapper_busy_delay_interval_usec,
177 				0,
178 				async_wait_timeout_callback,
179 				data);
180 		if(r < 0) {
181 			async_wait_done(r, wait);
182 			goto exit;
183 		}
184 
185 		return 0;
186 	}
187 
188 	if(r) {
189 		async_wait_done(-r, wait);
190 		goto exit;
191 	}
192 
193 	for(i=0; i<wait->count; ++i) {
194 		if(!strcmp(data->path, wait->objs[i])) {
195 			wait->status[i] = 1;
196 		}
197 	}
198 
199 	if(async_wait_check_done(wait))
200 		async_wait_done(0, wait);
201 
202 exit:
203 	free(data);
204 	return 0;
205 }
206 
207 static int async_wait_get_objects(mapper_async_wait *wait)
208 {
209 	int i, r;
210 	struct async_wait_callback_data *data = NULL;
211 
212 	for(i=0; i<wait->count; ++i) {
213 		if(wait->status[i])
214 			continue;
215 		data = malloc(sizeof(*data));
216 		data->wait = wait;
217 		data->path = wait->objs[i];
218 		data->retry = 0;
219 		data->event_source = NULL;
220 		r = sd_bus_call_method_async(
221 				wait->conn,
222 				NULL,
223 				MAPPER_BUSNAME,
224 				MAPPER_PATH,
225 				MAPPER_INTERFACE,
226 				"GetObject",
227 				async_wait_getobject_callback,
228 				data,
229 				"sas",
230 				wait->objs[i],
231 				0,
232 				NULL);
233 		if(r < 0) {
234 			free(data);
235 			fprintf(stderr, "Error invoking method: %s\n",
236 					strerror(-r));
237 			return r;
238 		}
239 	}
240 
241 	return 0;
242 }
243 
244 static int async_wait_match_introspection_complete(sd_bus_message *m, void *w,
245 		sd_bus_error *e)
246 {
247 	int r;
248 
249 	mapper_async_wait *wait = w;
250 	if(wait->finished)
251 		return 0;
252 
253 	r = async_wait_get_objects(wait);
254 	if(r < 0)
255 		async_wait_done(r, wait);
256 
257 	return 0;
258 }
259 
260 static void async_wait_done(int r, mapper_async_wait *w)
261 {
262 	if(w->finished)
263 		return;
264 
265 	w->finished = 1;
266 	sd_bus_slot_unref(w->introspection_slot);
267 	sd_bus_slot_unref(w->intf_slot);
268 
269 	if(w->callback)
270 		w->callback(r, w->userdata);
271 }
272 
273 static int async_wait_check_done(mapper_async_wait *w)
274 {
275 	int i;
276 
277 	if(w->finished)
278 		return 1;
279 
280 	for(i=0; i<w->count; ++i)
281 		if(!w->status[i])
282 			return 0;
283 
284 	return 1;
285 }
286 
287 void mapper_wait_async_free(mapper_async_wait *w)
288 {
289 	free(w->status);
290 	sarrayfree(w->objs);
291 	free(w);
292 }
293 
294 int mapper_wait_async(sd_bus *conn,
295 		sd_event *loop,
296 		char *objs[],
297 		void (*callback)(int, void *),
298 		void *userdata,
299 		mapper_async_wait **w)
300 {
301 	int r;
302 	mapper_async_wait *wait = NULL;
303 
304 	wait = malloc(sizeof(*wait));
305 	if(!wait)
306 		return -ENOMEM;
307 
308 	memset(wait, 0, sizeof(*wait));
309 	wait->conn = conn;
310 	wait->loop = loop;
311 	wait->callback = callback;
312 	wait->userdata = userdata;
313 	wait->count = sarraylen(objs);
314 	if(!wait->count)
315 		return 0;
316 
317 	wait->objs = sarraydup(objs);
318 	if(!wait->objs) {
319 		r = -ENOMEM;
320 		goto free_wait;
321 	}
322 
323 	wait->status = malloc(sizeof(*wait->status) * wait->count);
324 	if(!wait->status) {
325 		r = -ENOMEM;
326 		goto free_objs;
327 	}
328 	memset(wait->status, 0, sizeof(*wait->status) * wait->count);
329 
330 	r = sd_bus_add_match(conn,
331 			&wait->introspection_slot,
332 			async_wait_introspection_match,
333 			async_wait_match_introspection_complete,
334 			wait);
335 	if(r < 0) {
336 		fprintf(stderr, "Error adding match rule: %s\n",
337 				strerror(-r));
338 		goto free_status;
339 	}
340 
341 	r = sd_bus_add_match(conn,
342                         &wait->intf_slot,
343 			async_wait_interfaces_added_match,
344                         async_wait_match_introspection_complete,
345                         wait);
346 	if(r < 0) {
347 		fprintf(stderr, "Error adding match rule: %s\n",
348 				strerror(-r));
349 		goto unref_name_slot;
350 	}
351 
352 	r = async_wait_get_objects(wait);
353 	if(r < 0) {
354 		fprintf(stderr, "Error calling method: %s\n",
355 				strerror(-r));
356 		goto unref_intf_slot;
357 	}
358 
359 	*w = wait;
360 
361 	return 0;
362 
363 unref_intf_slot:
364 	sd_bus_slot_unref(wait->intf_slot);
365 unref_name_slot:
366 	sd_bus_slot_unref(wait->introspection_slot);
367 free_status:
368 	free(wait->status);
369 free_objs:
370 	sarrayfree(wait->objs);
371 free_wait:
372 	free(wait);
373 
374 	return r;
375 }
376 
377 int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply)
378 {
379 	sd_bus_error error = SD_BUS_ERROR_NULL;
380 	sd_bus_message *request = NULL;
381 	int r, retry = 0;
382 
383 	r = sd_bus_message_new_method_call(
384 			conn,
385 			&request,
386 			MAPPER_BUSNAME,
387 			MAPPER_PATH,
388 			MAPPER_INTERFACE,
389 			"GetObject");
390 	if (r < 0)
391 		goto exit;
392 
393 	r = sd_bus_message_append(request, "s", obj);
394 	if (r < 0)
395 		goto exit;
396 	r = sd_bus_message_append(request, "as", 0, NULL);
397 	if (r < 0)
398 		goto exit;
399 
400 	while(retry < mapper_busy_retries) {
401 		sd_bus_error_free(&error);
402 		r = sd_bus_call(conn, request, 0, &error, reply);
403 		if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) {
404 			++retry;
405 
406 			if(retry != mapper_busy_retries)
407 				usleep(mapper_busy_delay_interval_usec);
408 			continue;
409 		}
410 		break;
411 	}
412 
413 	if (r < 0)
414 		goto exit;
415 
416 exit:
417 	sd_bus_error_free(&error);
418 	sd_bus_message_unref(request);
419 
420 	return r;
421 }
422 
423 int mapper_get_service(sd_bus *conn, const char *obj, char **service)
424 {
425 	sd_bus_message *reply = NULL;
426 	const char *tmp;
427 	int r;
428 
429 	r = mapper_get_object(conn, obj, &reply);
430 	if (r < 0)
431 		goto exit;
432 
433 	r = sd_bus_message_enter_container(reply, 0, NULL);
434 	if (r < 0)
435 		goto exit;
436 
437 	r = sd_bus_message_enter_container(reply, 0, NULL);
438 	if (r < 0)
439 		goto exit;
440 
441 	r = sd_bus_message_read(reply, "s", &tmp);
442 	if (r < 0)
443 		goto exit;
444 
445 	*service = strdup(tmp);
446 
447 exit:
448 	sd_bus_message_unref(reply);
449 
450 	return r;
451 }
452