sdbus client: add retries

Handle busy responses from the server with retries.

Change-Id: I8c13faddec1bfaffe702609f2e682e8b1181946d
Signed-off-by: Brad Bishop <bradleyb@fuzziesquirrel.com>
diff --git a/libmapper/Makefile b/libmapper/Makefile
index 128a032..ae6a895 100644
--- a/libmapper/Makefile
+++ b/libmapper/Makefile
@@ -4,7 +4,7 @@
 
 PACKAGE_DEPS=libsystemd
 SONAME=libmapper.so
-VERSION=1
+VERSION=2
 LIBMAPPER=$(SONAME).$(VERSION)
 BIN=mapper
 INCLUDES=mapper.h
diff --git a/libmapper/app.c b/libmapper/app.c
index e2b94eb..f8f949e 100644
--- a/libmapper/app.c
+++ b/libmapper/app.c
@@ -118,7 +118,7 @@
 		goto finish;
 	}
 
-	r = mapper_wait_async(conn, argv+2, quit, loop, &wait);
+	r = mapper_wait_async(conn, loop, argv+2, quit, loop, &wait);
 	if(r < 0) {
 		fprintf(stderr, "Error configuring waitlist: %s\n",
 				strerror(-r));
diff --git a/libmapper/mapper.c b/libmapper/mapper.c
index 6e93e17..a2bb97a 100644
--- a/libmapper/mapper.c
+++ b/libmapper/mapper.c
@@ -17,7 +17,10 @@
 #include <string.h>
 #include <stdio.h>
 #include <errno.h>
+#include <unistd.h>
+#include <sys/timerfd.h>
 #include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
 #include "mapper.h"
 
 static const char *async_wait_name_owner_match =
@@ -32,11 +35,15 @@
 	"interface='org.freedesktop.DBus.ObjectManager',"
 	"member='InterfacesAdded'";
 
+static const int mapper_busy_retries = 5;
+static const uint64_t mapper_busy_delay_interval_usec = 1000000;
+
 struct mapper_async_wait
 {
 	char **objs;
 	void (*callback)(int, void *);
 	void *userdata;
+	sd_event *loop;
 	sd_bus *conn;
 	sd_bus_slot *name_owner_slot;
 	sd_bus_slot *intf_slot;
@@ -50,6 +57,8 @@
 {
 	mapper_async_wait *wait;
 	const char *path;
+	sd_event_source *event_source;
+	int retry;
 };
 
 static int async_wait_match_name_owner_changed(sd_bus_message *, void *,
@@ -59,6 +68,8 @@
 static int async_wait_check_done(mapper_async_wait *);
 static void async_wait_done(int r, mapper_async_wait *);
 static int async_wait_get_objects(mapper_async_wait *);
+static int async_wait_getobject_callback(sd_bus_message *,
+		void *, sd_bus_error *);
 
 static int sarraylen(char *array[])
 {
@@ -106,18 +117,78 @@
 	return NULL;
 }
 
+static int async_wait_timeout_callback(sd_event_source *s,
+		uint64_t usec, void *userdata)
+{
+	int r;
+	struct async_wait_callback_data *data = userdata;
+	mapper_async_wait *wait = data->wait;
+
+	sd_event_source_unref(data->event_source);
+	r = sd_bus_call_method_async(
+			wait->conn,
+			NULL,
+			"org.openbmc.ObjectMapper",
+			"/org/openbmc/ObjectMapper",
+			"org.openbmc.ObjectMapper",
+			"GetObject",
+			async_wait_getobject_callback,
+			data,
+			"s",
+			data->path);
+	if(r < 0) {
+		async_wait_done(r, wait);
+		free(data);
+	}
+
+	return 0;
+}
+
 static int async_wait_getobject_callback(sd_bus_message *m,
 		void *userdata,
 		sd_bus_error *e)
 {
-	int i;
+	int i, r;
 	struct async_wait_callback_data *data = userdata;
 	mapper_async_wait *wait = data->wait;
+	uint64_t now;
 
 	if(wait->finished)
+		goto exit;
+
+	r = sd_bus_message_get_errno(m);
+	if(r == EBUSY && data->retry < mapper_busy_retries) {
+		r = sd_event_now(wait->loop,
+				CLOCK_MONOTONIC,
+				&now);
+		if(r < 0) {
+			async_wait_done(r, wait);
+			goto exit;
+		}
+
+		++data->retry;
+		r = sd_event_add_time(wait->loop,
+				&data->event_source,
+				CLOCK_MONOTONIC,
+				now + mapper_busy_delay_interval_usec,
+				0,
+				async_wait_timeout_callback,
+				data);
+		if(r < 0) {
+			async_wait_done(r, wait);
+			goto exit;
+		}
+
 		return 0;
-	if(sd_bus_message_get_errno(m))
-		return 0;
+	}
+
+	if(r == EBUSY) {
+		async_wait_done(-r, wait);
+		goto exit;
+	}
+
+	if(r) // not found
+		goto exit;
 
 	for(i=0; i<wait->count; ++i) {
 		if(!strcmp(data->path, wait->objs[i])) {
@@ -125,10 +196,11 @@
 		}
 	}
 
-	free(data);
 	if(async_wait_check_done(wait))
 		async_wait_done(0, wait);
 
+exit:
+	free(data);
 	return 0;
 }
 
@@ -143,6 +215,8 @@
 		data = malloc(sizeof(*data));
 		data->wait = wait;
 		data->path = wait->objs[i];
+		data->retry = 0;
+		data->event_source = NULL;
 		r = sd_bus_call_method_async(
 				wait->conn,
 				NULL,
@@ -245,6 +319,7 @@
 }
 
 int mapper_wait_async(sd_bus *conn,
+		sd_event *loop,
 		char *objs[],
 		void (*callback)(int, void *),
 		void *userdata,
@@ -259,6 +334,7 @@
 
 	memset(wait, 0, sizeof(*wait));
 	wait->conn = conn;
+	wait->loop = loop;
 	wait->callback = callback;
 	wait->userdata = userdata;
 	wait->count = sarraylen(objs);
@@ -325,12 +401,11 @@
 	return r;
 }
 
-int mapper_get_service(sd_bus *conn, const char *obj, char **service)
+int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply)
 {
 	sd_bus_error error = SD_BUS_ERROR_NULL;
-	sd_bus_message *request = NULL, *reply = NULL;
-	const char *tmp;
-	int r;
+	sd_bus_message *request = NULL;
+	int r, retry = 0;
 
 	r = sd_bus_message_new_method_call(
 			conn,
@@ -346,7 +421,36 @@
 	if (r < 0)
 		goto exit;
 
-	r = sd_bus_call(conn, request, 0, &error, &reply);
+	while(retry < mapper_busy_retries) {
+		sd_bus_error_free(&error);
+		r = sd_bus_call(conn, request, 0, &error, reply);
+		if (r < 0 && sd_bus_error_get_errno(&error) == EBUSY) {
+			++retry;
+
+			if(retry != mapper_busy_retries)
+				usleep(mapper_busy_delay_interval_usec);
+			continue;
+		}
+		break;
+	}
+
+	if (r < 0)
+		goto exit;
+
+exit:
+	sd_bus_error_free(&error);
+	sd_bus_message_unref(request);
+
+	return r;
+}
+
+int mapper_get_service(sd_bus *conn, const char *obj, char **service)
+{
+	sd_bus_message *reply = NULL;
+	const char *tmp;
+	int r;
+
+	r = mapper_get_object(conn, obj, &reply);
 	if (r < 0)
 		goto exit;
 
@@ -365,8 +469,6 @@
 	*service = strdup(tmp);
 
 exit:
-	sd_bus_error_free(&error);
-	sd_bus_message_unref(request);
 	sd_bus_message_unref(reply);
 
 	return r;
diff --git a/libmapper/mapper.h b/libmapper/mapper.h
index b49b302..7459e15 100644
--- a/libmapper/mapper.h
+++ b/libmapper/mapper.h
@@ -1,4 +1,5 @@
 #include <systemd/sd-bus.h>
+#include <systemd/sd-event.h>
 
 #ifdef __cplusplus
 extern "C" {
@@ -6,9 +7,10 @@
 typedef struct mapper_async_wait mapper_async_wait;
 void mapper_wait_async_free(mapper_async_wait *);
 
-int mapper_wait_async(sd_bus *, char *[],
+int mapper_wait_async(sd_bus *, sd_event *, char *[],
 		void (*)(int, void *), void *, mapper_async_wait **);
 int mapper_get_service(sd_bus *conn, const char *obj, char **service);
+int mapper_get_object(sd_bus *conn, const char *obj, sd_bus_message **reply);
 #ifdef __cplusplus
 }
 #endif