ipa: Change ipa_msg_recv() to support partial receive

Currently ipa_msg_recv() fails, when messages are received partially.

This patch provides a new function ipa_msg_recv_buffered() that uses
an additional ** to a message buffer to store partial data.  When
this happens, -EAGAIN is returned. If NULL is used, the function
behaves similar to ipa_msg_recv() and fails on partial read.
In addition in case of errors the return value is now always -EXXX
and the contents of errno is undefined.

Note that this feature needs support by the calling code insofar that
*tmp_msg must be set to NULL initially and it must be freed and
set to NULL manually when the socket is closed.

Note also that ipa_msg_recv() is then a wrapper around
ipa_msg_recv_buffered() which mimics the old error behaviour by
setting errno explicitely to -rc and returning -1 when an error has
happened.

Ticket: OW#728
Sponsored-by: On-Waves ehf
diff --git a/tests/ipa_recv/ipa_recv_test.c b/tests/ipa_recv/ipa_recv_test.c
index af87c27..0f56926 100644
--- a/tests/ipa_recv/ipa_recv_test.c
+++ b/tests/ipa_recv/ipa_recv_test.c
@@ -65,7 +65,7 @@
 		strcpy((char *)l2, text);
 }
 
-static int receive_messages(int fd)
+static int receive_messages(int fd, struct msgb **pending_msg)
 {
 	struct msgb *msg;
 	char dummy;
@@ -76,13 +76,22 @@
 			break;
 		}
 		msg = NULL;
-		rc = ipa_msg_recv(fd, &msg);
-		if (rc == -1)
-			rc = -errno;
+		rc = ipa_msg_recv_buffered(fd, &msg, pending_msg);
+
 		fprintf(stderr,
-			"ipa_msg_recv: %d, msg %s NULL\n",
-			rc, msg ? "!=" : "==");
-		if (rc == -EAGAIN)
+			"ipa_msg_recv_buffered: %d, msg %s NULL, "
+			"pending_msg %s NULL\n",
+			rc, msg ? "!=" : "==",
+			!pending_msg ? "??" : *pending_msg ? "!=" : "==");
+		if (pending_msg && !!msg == !!*pending_msg)
+			printf( "got msg %s NULL, pending_msg %s NULL, "
+				"returned: %s\n",
+				msg ?  "!=" : "==",
+				*pending_msg ? "!=" : "==",
+				rc == 0 ? "EOF" :
+				rc > 0 ? "OK" :
+				strerror(-rc));
+		else if (!pending_msg && rc == -EAGAIN)
 			printf( "got msg %s NULL, "
 				"returned: %s\n",
 				msg ?  "!=" : "==",
@@ -94,7 +103,8 @@
 		if (rc == -EAGAIN)
 			break;
 		if (rc < 0) {
-			printf("ipa_msg_recv failed with: %s\n", strerror(-rc));
+			printf("ipa_msg_recv_buffered failed with: %s\n",
+			       strerror(-rc));
 			return rc;
 		}
 		printf("got IPA message, size=%d, proto=%d, text=\"%s\"\n",
@@ -121,13 +131,15 @@
 	return count;
 };
 
-static void test_complete_recv(void)
+static void test_complete_recv(int do_not_assemble)
 {
 	int sv[2];
 	struct msgb *msg_out = msgb_alloc(4096, "msg_out");
+	struct msgb *pending_msg = NULL;
 	int rc, i;
 
-	printf("Testing IPA recv with complete messages.\n");
+	printf("Testing IPA recv with complete messages%s.\n",
+	       do_not_assemble ? "" : " with assembling enabled");
 
 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
 		err(1, "socketpair");
@@ -145,7 +157,11 @@
 	}
 
 	for (i=0; i < ARRAY_SIZE(ipa_test_messages); i++) {
-		rc = receive_messages(sv[0]);
+		rc = receive_messages(sv[0],
+				      do_not_assemble ? NULL : &pending_msg);
+		if (pending_msg)
+			printf("Unexpected partial message: size=%d\n",
+			       pending_msg->len);
 		if (rc == 0)
 			break;
 
@@ -160,16 +176,19 @@
 	close(sv[0]);
 
 	msgb_free(msg_out);
+	msgb_free(pending_msg);
 }
 
 
-static void test_partial_recv(void)
+static void test_partial_recv(int do_not_assemble)
 {
 	int sv[2];
 	struct msgb *msg_out = msgb_alloc(4096, "msg_out");
+	struct msgb *pending_msg = NULL;
 	int rc, i;
 
-	printf("Testing IPA recv with partitioned messages.\n");
+	printf("Testing IPA recv with partitioned messages%s.\n",
+	       do_not_assemble ? "" : " with assembling enabled");
 
 	if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1)
 		err(1, "socketpair");
@@ -190,7 +209,8 @@
 		if (msg_out->len == 0)
 			shutdown(sv[1], SHUT_WR);
 
-		rc = receive_messages(sv[0]);
+		rc = receive_messages(sv[0],
+				      do_not_assemble ? NULL : &pending_msg);
 
 		if (rc == 0)
 			break;
@@ -205,6 +225,7 @@
 	close(sv[0]);
 
 	msgb_free(msg_out);
+	msgb_free(pending_msg);
 }
 
 static struct log_info info = {};
@@ -218,8 +239,10 @@
 	printf("Testing the IPA layer.\n");
 
 	/* run the tests */
-	test_complete_recv();
-	test_partial_recv();
+	test_complete_recv(1);
+	test_partial_recv(1);
+	test_complete_recv(0);
+	test_partial_recv(0);
 
 	printf("No crashes.\n");
 	return 0;