diff --git a/core/pctl.c b/core/pctl.c
index 481f07773e11..ec21bbd9c8f4 100644
--- a/core/pctl.c
+++ b/core/pctl.c
@@ -264,6 +264,9 @@ int run_system_cmd(const char *cmd)
 	} else {
 		int fds[2];
 		pid_t w;
+		char buf[2][SWUPDATE_GENERAL_STRING_SIZE];
+		int cindex[2] = {0, 0}; /* this is the first free char inside buffer */
+		LOGLEVEL levels[2] = { TRACELEVEL, ERRORLEVEL };
 
 		close(stdoutpipe[PIPE_WRITE]);
 		close(stderrpipe[PIPE_WRITE]);
@@ -271,6 +274,14 @@ int run_system_cmd(const char *cmd)
 		fds[0] = stdoutpipe[PIPE_READ];
 		fds[1] = stderrpipe[PIPE_READ];
 
+		/*
+		 * Use buffers (for stdout and stdin) to collect data from
+		 * the cmd. Data can contain multiple lines or just a part
+		 * of a line and must be parsed
+		 */
+		memset(buf[0], 0, SWUPDATE_GENERAL_STRING_SIZE);
+		memset(buf[1], 0, SWUPDATE_GENERAL_STRING_SIZE);
+
 		/*
 		 * Now waits until the child process exits and checks
 		 * for the output. Forward data from stdout as TRACE
@@ -281,9 +292,6 @@ int run_system_cmd(const char *cmd)
 			struct timeval tv;
 			fd_set readfds;
 			int n, i;
-			char buf[2][SWUPDATE_GENERAL_STRING_SIZE];
-			char *pbuf;
-			int cindex[2] = {0, 0}; /* this is the first free char inside buffer */
 
 			w = waitpid(process_id, &wstatus, WNOHANG | WUNTRACED | WCONTINUED);
 			if (w == -1) {
@@ -293,14 +301,6 @@ int run_system_cmd(const char *cmd)
 				return -EFAULT;
 			}
 
-			/*
-			 * Use buffers (for stdout and stdin) to collect data from
-			 * the cmd. Data can contain multiple lines or just a part
-			 * of a line and must be parsed
-			 */
-			memset(buf[0], 0, SWUPDATE_GENERAL_STRING_SIZE);
-			memset(buf[1], 0, SWUPDATE_GENERAL_STRING_SIZE);
-
 			tv.tv_sec = 1;
 			tv.tv_usec = 0;
 
@@ -316,65 +316,35 @@ int run_system_cmd(const char *cmd)
 					break;
 
 				for (i = 0; i < 2 ; i++) {
-					pbuf = buf[i];
-					int c = cindex[i];
+					char *pbuf = buf[i];
+					int *c = &cindex[i];
+					LOGLEVEL level = levels[i];
 
 					if (FD_ISSET(fds[i], &readfds)) {
-						int last;
-
-						n = read(fds[i], &pbuf[c], SWUPDATE_GENERAL_STRING_SIZE - c);
+						n = read_lines_notify(fds[i], pbuf, SWUPDATE_GENERAL_STRING_SIZE,
+								      c, level);
 						if (n < 0)
 							continue;
-						n += c;	/* add previous data, if any */
 						n1 += n;
-						if (n > 0) {
-
-							/* check if just a part of a line was sent. In that
-							 * case, search for the last line and then copy the rest
-							 * to the begin of the buffer for next read
-							 */
-							last = n - 1;
-							while (last > 0 && pbuf[last] != '\0' && pbuf[last] != '\n')
-									last--;
-							pbuf[last] = '\0';
-							/*
-							 * compute the truncate line that should be
-							 * parsed next time when the rest is received
-							 */
-							int left = n - 1 - last;
-							char **lines = string_split(pbuf, '\n');
-							int nlines = count_string_array((const char **)lines);
-
-							if (last < SWUPDATE_GENERAL_STRING_SIZE - 1) {
-								last++;
-								memcpy(pbuf, &pbuf[last], left);
-								if (last + left < SWUPDATE_GENERAL_STRING_SIZE) {
-									memset(&pbuf[left], 0, SWUPDATE_GENERAL_STRING_SIZE - left);
-								}
-								cindex[i] = left;
-							} else { /* no need to copy, reuse all buffer */
-								memset(pbuf, 0, SWUPDATE_GENERAL_STRING_SIZE);
-								cindex[i] = 0;
-							}
-
-							for (unsigned int index = 0; index < nlines; index++) {
-								switch (i) {
-								case 0:
-									TRACE("%s", lines[index]);
-									break;
-								case 1:
-									ERROR("%s", lines[index]);
-									break;
-								}
-							}
-
-							free_string_array(lines);
-						}
 					}
 				}
 			} while (ret > 0 && n1 > 0);
 		} while (w != process_id);
 
+		/* print any unfinished line */
+		for (int i = 0; i < 2; i++) {
+			if (cindex[i]) {
+				switch(i) {
+				case 0:
+					TRACE("%s", buf[i]);
+					break;
+				case 1:
+					ERROR("%s", buf[i]);
+					break;
+				}
+			}
+		}
+
 		close(stdoutpipe[PIPE_READ]);
 		close(stderrpipe[PIPE_READ]);
 
diff --git a/core/util.c b/core/util.c
index 2025276445dd..0ee536f4b8a7 100644
--- a/core/util.c
+++ b/core/util.c
@@ -842,3 +842,54 @@ size_t snescape(char *dst, size_t n, const char *src)
 
 	return len;
 }
+
+int read_lines_notify(int fd, char *buf, int buf_size, int *buf_offset,
+		      LOGLEVEL level)
+{
+	int n;
+	int offset = *buf_offset;
+	int print_last = 0;
+
+	n = read(fd, &buf[offset], buf_size - offset - 1);
+	if (n <= 0)
+		return -errno;
+
+	n += offset;
+	buf[n] = '\0';
+
+	/*
+	 * Check if last line was finished, or if we should copy
+	 * it back for next call.
+	 * If the buffer is full and there is only one line it
+	 * will be printed anyway
+	 */
+	if (buf[n-1] == '\n') {
+		print_last = 1;
+	}
+
+	char **lines = string_split(buf, '\n');
+	int nlines = count_string_array((const char **)lines);
+	if (n >= buf_size - 1 && nlines == 1)
+		print_last = 1;
+
+	/* copy leftover data for next call */
+	if (!print_last) {
+		int left = snprintf(buf, buf_size, "%s", lines[nlines-1]);
+		memset(&buf[left], 0, SWUPDATE_GENERAL_STRING_SIZE - left);
+		*buf_offset = left;
+		nlines--;
+		n -= left;
+	} else { /* no need to copy, reuse all buffer */
+		memset(buf, 0, n);
+		*buf_offset = 0;
+	}
+
+	for (unsigned int index = 0; index < nlines; index++) {
+		RECOVERY_STATUS status = level == ERRORLEVEL ? FAILURE : RUN;
+		swupdate_notify(status, "%s", level, lines[index]);
+	}
+
+	free_string_array(lines);
+
+	return n;
+}
diff --git a/include/util.h b/include/util.h
index ad2e90cabd12..76448504f303 100644
--- a/include/util.h
+++ b/include/util.h
@@ -216,6 +216,8 @@ int check_hw_compatibility(struct swupdate_cfg *cfg);
 int count_elem_list(struct imglist *list);
 unsigned int count_string_array(const char **nodes);
 void free_string_array(char **nodes);
+int read_lines_notify(int fd, char *buf, int buf_size, int *buf_offset,
+		      LOGLEVEL level);
 
 /* Decryption key functions */
 int load_decryption_key(char *fname);
