diff mbox series

[3/3] swupdate_async_start: fix early termination blocking in client

Message ID b3aac7a8-0264-4176-a09f-5a7c68710daf@witekio.com
State Changes Requested
Headers show
Series [1/3] progress: acknowledge client registration | expand

Commit Message

fhoerni.opensource Oct. 11, 2024, 1:18 p.m. UTC
From: Frederic Hoerni <fhoerni@witekio.com>

It could happen on the client side, that the termination status of the
installation was missed, leading to the client waiting forever.

This modification connects the progress socket before sending the image
payload, to be sure not to miss the termination status (SUCCESS or FAILURE).

We also need to consume the events during the image transfer, so that the pipe
does not get full and block the daemon.

Signed-off-by: Frederic Hoerni <fhoerni@witekio.com>
---
  ipc/network_ipc-if.c | 96 ++++++++++++++++++++++++++++++++++++++------
  1 file changed, 84 insertions(+), 12 deletions(-)
diff mbox series

Patch

diff --git a/ipc/network_ipc-if.c b/ipc/network_ipc-if.c
index 65b60b48..f7727346 100644
--- a/ipc/network_ipc-if.c
+++ b/ipc/network_ipc-if.c
@@ -102,6 +102,52 @@  static void unstack_installation_status(getstatus callback)
  	} while (ipcmsg.data.status.current != IDLE);
  }
  
+/* Consume progress events
+ *
+ * Returns:
+ *        -1 error
+ *   FAILURE the installation failed
+ *   SUCCESS the installation suceeded
+ *         0 no events or other events consumed
+ *
+ * On error, progressfd is closed and set to -1.
+ */
+static int consume_progress_events(int *progressfd)
+{
+	struct progress_msg progressmsg;
+	int ret = -1;
+
+	/* Wait until the end of the installation (FAILURE or SUCCESS) */
+	while (1) {
+		ret = progress_ipc_receive_nb(progressfd, &progressmsg);
+		if (ret == -1) {
+			/* Note that progressfd may have been closed by progress_ipc_receive
+			 * and set to -1 */
+			fprintf(stderr, "progress_ipc_receive_nb failed (%d)\n", ret);
+			break;
+		} else if (ret == 0) {
+			/* no pending message */
+			break;
+		}
+
+		if (progressmsg.status == FAILURE || progressmsg.status == SUCCESS) {
+			/* We have the final result of the installation */
+			ret = progressmsg.status;
+			break;
+		} else {
+			/* Other status (START, RUN, PROGRESS) */
+			/* continue consuming messages */
+			continue;
+		}
+	}
+
+	if (ret == -1 && *progressfd >= 0) {
+		close(*progressfd);
+		*progressfd = -1;
+	}
+
+	return ret;
+}
  
  static void *swupdate_async_thread(void *data)
  {
@@ -113,6 +159,8 @@  static void *swupdate_async_thread(void *data)
  	struct async_lib *rq = (struct async_lib *)data;
  	int swupdate_result = FAILURE;
  	int progressfd = -1;
+	int ret;
+	int early_status = -1;
  
  	sigemptyset(&sigpipe_mask);
  	sigaddset(&sigpipe_mask, SIGPIPE);
@@ -122,6 +170,16 @@  static void *swupdate_async_thread(void *data)
  		swupdate_result = FAILURE;
  		goto out;
  	}
+	/* Start listening to progress events, before sending
+	 * the image so that we don't miss the result event.
+	 */
+	progressfd = progress_ipc_connect(0 /* no reconnect */);
+	if (progressfd < 0) {
+		fprintf(stderr, "progress_ipc_connect failed\n");
+		ipc_end(rq->connfd);
+		goto out;
+	}
+
  	/* Start writing the image until end */
  
  	do {
@@ -136,26 +194,40 @@  static void *swupdate_async_thread(void *data)
  				goto out;
  			}
  		}
+		/* Consume progress events so that the pipe does not get full
+		 * and block the daemon */
+		ret = consume_progress_events(&progressfd);
+		if (ret == -1) {
+			/* If we cannot get events, then we won't be able to get the result.
+			 * Quit and fail */
+			fprintf(stderr, "Cannot consume progress events. Fail.\n");
+			early_status = FAILURE;
+			break;
+		} else if (ret == FAILURE || ret == SUCCESS) {
+			/* early termination */
+			fprintf(stderr, "early termination while sending the image: %s\n",
+			        ret==SUCCESS?"SUCCESS":"FAILURE");
+			early_status = ret;
+			/* interrupt the transfer */
+			break;
+		}
  	} while(size > 0);
  
-	/* Start listening to progress events, before ipc_end
-	 * so that we don't miss the result event.
-	 */
-	progressfd = progress_ipc_connect(0 /* no reconnect */);
-
  	ipc_end(rq->connfd);
  
-	if (progressfd < 0) {
-		fprintf(stderr, "progress_ipc_connect failed\n");
-		goto out;
-	}
-
  	/*
  	 * Everything sent, wait for completion of the installation
  	 */
  
-	/* Wait until the end of the installation and get the final result */
-	swupdate_result = inst_wait_for_complete(&progressfd); /* progressfd closed by the call */
+	if (early_status >= 0) {
+		swupdate_result = early_status;
+		close(progressfd);
+		progressfd = -1;
+
+	} else {
+		/* Wait until the end of the installation and get the final result */
+		swupdate_result = inst_wait_for_complete(&progressfd); /* progressfd closed by the call */
+	}
  
  	/*
  	 * Get and print all status lines, for compatibility with legacy programs