[LEDE-DEV,v2] ubox/logread: add re-connect capability

Message ID 20171124130614.2875-1-zefir.kurtisi@neratec.com
State New
Headers show
Series
  • [LEDE-DEV,v2] ubox/logread: add re-connect capability
Related show

Commit Message

Zefir Kurtisi Nov. 24, 2017, 1:06 p.m.
When logd is restarted while 'logread -f' is running, the
logread process terminates, which cumbers debugging in
different use-cases.

This patch adds re-connect functionality to logread. In
follow mode, when the ustream to logd is disconnected,
instead of terminating, it tries to re-connect to logd
and re-issue the original request.

Signed-off-by: Zefir Kurtisi <zefir.kurtisi@neratec.com>
---
v2: in follow mode, don't exit if the initial
    ubus lookup for logd fails

 log/logread.c | 143 +++++++++++++++++++++++++++++++++++++---------------------
 1 file changed, 91 insertions(+), 52 deletions(-)

Patch

diff --git a/log/logread.c b/log/logread.c
index ad06f2a..8229c98 100644
--- a/log/logread.c
+++ b/log/logread.c
@@ -65,6 +65,10 @@  static int log_type = LOG_STDOUT;
 static int log_size, log_udp, log_follow, log_trailer_null = 0;
 static int log_timestamp;
 static int last_errno = 0;
+static struct ubus_context *ctx;
+static int lines;
+
+static void logread_reconnect_cb(struct uloop_timeout *timeout);
 
 static const char* getcodetext(int value, CODE *codetable) {
 	CODE *i;
@@ -268,29 +272,82 @@  static void logread_fd_data_cb(struct ustream *s, int bytes)
 	}
 }
 
+/*
+ * Reconnect Handling
+ * while following log
+ * - after logd removal
+ *   - destroy ustream
+ *   - cyclically try to re-connect to new logd object
+ *   - re-issue original request
+ *
+ * Note: if a re-connection appears while a 'logread -l' request is active, the
+ *       number of returned lines will not match (i.e. you get some lines from
+ *       the old instance plus the number of lines requested from the new one)
+ */
+
+/* flag to prevent printing error messages during reconnect cycles */
+static int object_reconnect_active;
+
+static void logread_restart_reconnect_timer(struct uloop_timeout *timeout)
+{
+	const int LOG_RECONNECT_TIMEOUT_MS = 250;
+	uloop_timeout_set(timeout, LOG_RECONNECT_TIMEOUT_MS);
+}
+
 static void logread_fd_state_cb(struct ustream *s)
 {
-	uloop_end();
+	static struct uloop_timeout ubus_timer;
+	/* force re-opening of stream */
+	s->free(s);
+
+	object_reconnect_active = 1;
+	ubus_timer.cb = logread_reconnect_cb;
+	logread_restart_reconnect_timer(&ubus_timer);
 }
 
 static void logread_fd_cb(struct ubus_request *req, int fd)
 {
 	static struct ustream_fd test_fd;
-
 	test_fd.stream.notify_read = logread_fd_data_cb;
 	test_fd.stream.notify_state = logread_fd_state_cb;
 	ustream_fd_init(&test_fd, fd);
 }
 
-int main(int argc, char **argv)
+static int logread_process(void)
 {
+	static struct blob_buf b;
 	static struct ubus_request req;
-	struct ubus_context *ctx;
 	uint32_t id;
+	int ret = ubus_lookup_id(ctx, "log", &id);
+	if (ret) {
+		if (!object_reconnect_active)
+			fprintf(stderr, "Failed to find log object\n");
+		return ret;
+	}
+	blob_buf_init(&b, 0);
+	blobmsg_add_u8(&b, "stream", 1);
+	if (lines)
+		blobmsg_add_u32(&b, "lines", lines);
+	else if (log_follow)
+		blobmsg_add_u32(&b, "lines", 0);
+
+	ubus_invoke_async(ctx, id, "read", b.head, &req);
+	req.fd_cb = logread_fd_cb;
+	ubus_complete_request_async(ctx, &req);
+
+	return 0;
+}
+
+static void logread_reconnect_cb(struct uloop_timeout *timeout)
+{
+	if (logread_process())
+		logread_restart_reconnect_timer(timeout);
+}
+
+int main(int argc, char **argv)
+{
 	const char *ubus_socket = NULL;
-	int ch, ret, lines = 0;
-	static struct blob_buf b;
-	int tries = 5;
+	int ch, ret;
 
 	signal(SIGPIPE, SIG_IGN);
 
@@ -354,58 +411,40 @@  int main(int argc, char **argv)
 	}
 	ubus_add_uloop(ctx);
 
-	/* ugly ugly ugly ... we need a real reconnect logic */
-	do {
-		ret = ubus_lookup_id(ctx, "log", &id);
-		if (ret) {
-			fprintf(stderr, "Failed to find log object: %s\n", ubus_strerror(ret));
-			sleep(1);
-			continue;
-		}
-
-		blob_buf_init(&b, 0);
-		blobmsg_add_u8(&b, "stream", 1);
-		blobmsg_add_u8(&b, "oneshot", !log_follow);
-		if (lines)
-			blobmsg_add_u32(&b, "lines", lines);
-		else if (log_follow)
-			blobmsg_add_u32(&b, "lines", 0);
-		if (log_follow) {
-			if (pid_file) {
-				FILE *fp = fopen(pid_file, "w+");
-				if (fp) {
-					fprintf(fp, "%d", getpid());
-					fclose(fp);
-				}
+	if (log_follow) {
+		if (pid_file) {
+			FILE *fp = fopen(pid_file, "w+");
+			if (fp) {
+				fprintf(fp, "%d", getpid());
+				fclose(fp);
 			}
 		}
+	}
 
-		if (log_ip && log_port) {
-			openlog("logread", LOG_PID, LOG_DAEMON);
-			log_type = LOG_NET;
-			sender.cb = log_handle_fd;
-			retry.cb = log_handle_reconnect;
-			uloop_timeout_set(&retry, 1000);
-		} else if (log_file) {
-			log_type = LOG_FILE;
-			sender.fd = open(log_file, O_CREAT | O_WRONLY| O_APPEND, 0600);
-			if (sender.fd < 0) {
-				fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno));
-				exit(-1);
-			}
-		} else {
-			sender.fd = STDOUT_FILENO;
+	if (log_ip && log_port) {
+		openlog("logread", LOG_PID, LOG_DAEMON);
+		log_type = LOG_NET;
+		sender.cb = log_handle_fd;
+		retry.cb = log_handle_reconnect;
+		uloop_timeout_set(&retry, 1000);
+	} else if (log_file) {
+		log_type = LOG_FILE;
+		sender.fd = open(log_file, O_CREAT | O_WRONLY| O_APPEND, 0600);
+		if (sender.fd < 0) {
+			fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno));
+			exit(-1);
 		}
+	} else {
+		sender.fd = STDOUT_FILENO;
+	}
 
-		ubus_invoke_async(ctx, id, "read", b.head, &req);
-		req.fd_cb = logread_fd_cb;
-		ubus_complete_request_async(ctx, &req);
-
+	ret = logread_process();
+	/* unless we are following, immediately exit when logd is not up */
+	if (!ret || log_follow)
 		uloop_run();
-		ubus_free(ctx);
-		uloop_done();
 
-	} while (ret && tries--);
+	ubus_free(ctx);
+	uloop_done();
 
 	return ret;
 }