trace-cmd listen: Update the V2 protocol to handle old and new

Change the protocol slightly where V1 will fail right away without creating
a blank trace.dat file by appending '-1' to the V2 name. The old trace-cmd
listen will see that as a negative CPU count and bail.

Also change the hand shake a bit such that if we go to a V3, a V2 server
could just let the newer trace-cmd client know what version it can support.

Here's the update:

     <server>                 <client>
      listen to socket fd
                              connect to socket fd
      accept the client
      send "tracecmd"
               +------------> receive "tracecmd"
                              check "tracecmd"
*                             send "-1V2\0"
* try_again:
*     receive "-1V2" <------------+
*     check "-1V2"
*     if (!"-1V2") {
*       if (memcmp("-1V",3))
*         error();
*       send "V2\0"
*       goto try_again;
*     }
*     send "V2"
*           +---------------> receive "V2"
*                             check "V2"
*                             send "<MAGIC_NUMBER>\00" as the v2 protocol

      read "<MAGIC_NUMBER>\00"
*     send "OK"
*           +---------------> receive "OK"
*                             check "OK"
                              send cpus,pagesize,option(MSG_TINIT)
      receive MSG_TINIT <-------+
      print "cpus=XXX"
      print "pagesize=XXX"
      understand option
      send port_array
               +--MSG_RINIT-> receive MSG_RINIT
                              understand port_array
                              send meta data(MSG_SENDMETA)
      receive MSG_SENDMETA <----+
      record meta data
                         (snip)
                              send a message to finish sending meta data
                                |                           (MSG_FINMETA)
      receive MSG_FINMETA <-----+
      read block
     --- start sending trace data on child processes ---

     --- When client finishes sending trace data ---
                              send MSG_CLOSE
      receive MSG_CLOSE <-------+
      close(socket fd)        close(socket fd)

The '*' represents what was updated.

Signed-off-by: Steven Rostedt <rostedt@goodmis.org>
diff --git a/trace-listen.c b/trace-listen.c
index 77df7c3..16375f8 100644
--- a/trace-listen.c
+++ b/trace-listen.c
@@ -317,15 +317,18 @@
 
 static int communicate_with_client(int fd, int *cpus, int *pagesize)
 {
+	char *last_proto = NULL;
 	char buf[BUFSIZ];
 	char *option;
 	int options;
 	int size;
 	int n, s, t, i;
+	int ret = -EINVAL;
 
 	/* Let the client know what we are */
 	write(fd, "tracecmd", 8);
 
+ try_again:
 	/* read back the CPU count */
 	n = read_string(fd, buf, BUFSIZ);
 	if (n == BUFSIZ)
@@ -335,41 +338,66 @@
 	*cpus = atoi(buf);
 
 	/* Is the client using the new protocol? */
-	if (!*cpus) {
-		if (memcmp(buf, "V2", 2) != 0) {
-			plog("Cannot handle the protocol %s", buf);
-			return -EINVAL;
+	if (*cpus == -1) {
+		if (memcmp(buf, V2_CPU, n) != 0) {
+			/* If it did not send a version, then bail */
+			if (memcmp(buf, "-1V", 3)) {
+				plog("Unknown string %s\n", buf);
+				goto out;
+			}
+			/* Skip "-1" */
+			plog("Cannot handle the protocol %s\n", buf+2);
+
+			/* If it returned the same command as last time, bail! */
+			if (last_proto && strncmp(last_proto, buf, n) == 0) {
+				plog("Repeat of version %s sent\n", last_proto);
+				goto out;
+			}
+			free(last_proto);
+			last_proto = malloc(n + 1);
+			if (last_proto) {
+				memcpy(last_proto, buf, n);
+				last_proto[n] = 0;
+			}
+			/* Return the highest protocol we can use */
+			write(fd, "V2", 3);
+			goto try_again;
 		}
 
-		/* read the rest of dummy data, but not use */
-		read(fd, buf, sizeof(V2_MAGIC)+1);
+		/* Let the client know we use v2 protocol */
+		write(fd, "V2", 3);
+
+		/* read the rest of dummy data */
+		n = read(fd, buf, sizeof(V2_MAGIC));
+		if (memcmp(buf, V2_MAGIC, n) != 0)
+			goto out;
+
+		/* We're off! */
+		write(fd, "OK", 2);
 
 		proto_ver = V2_PROTOCOL;
 
-		/* Let the client know we use v2 protocol */
-		write(fd, "V2", 2);
-
 		/* read the CPU count, the page size, and options */
 		if (tracecmd_msg_initial_setting(fd, cpus, pagesize) < 0)
-			return -EINVAL;
+			goto out;
 	} else {
 		/* The client is using the v1 protocol */
 
 		plog("cpus=%d\n", *cpus);
 		if (*cpus < 0)
-			return -EINVAL;
+			goto out;
 
 		/* next read the page size */
 		n = read_string(fd, buf, BUFSIZ);
 		if (n == BUFSIZ)
 			/** ERROR **/
-			return -EINVAL;
+			goto out;
 
 		*pagesize = atoi(buf);
 
 		plog("pagesize=%d\n", *pagesize);
 		if (*pagesize <= 0)
-			return -EINVAL;
+			goto out;
 
 		/* Now the number of options */
 		n = read_string(fd, buf, BUFSIZ);
@@ -384,20 +412,24 @@
 			n = read_string(fd, buf, BUFSIZ);
 			if (n == BUFSIZ)
 				/** ERROR **/
-				return -EINVAL;
+				goto out;
 			size = atoi(buf);
 			/* prevent a client from killing us */
 			if (size > MAX_OPTION_SIZE)
-				return -EINVAL;
+				goto out;
+
+			ret = -ENOMEM;
 			option = malloc(size);
 			if (!option)
-				return -ENOMEM;
+				goto out;
+
+			ret = -EIO;
 			do {
 				t = size;
 				s = 0;
 				s = read(fd, option+s, t);
 				if (s <= 0)
-					return -EIO;
+					goto out;
 				t -= s;
 				s = size - t;
 			} while (t);
@@ -405,15 +437,20 @@
 			s = process_option(option);
 			free(option);
 			/* do we understand this option? */
+			ret = -EINVAL;
 			if (!s)
-				return -EINVAL;
+				goto out;
 		}
 	}
 
 	if (use_tcp)
 		plog("Using TCP for live connection\n");
 
-	return 0;
+	ret = 0;
+ out:
+	free(last_proto);
+
+	return ret;
 }
 
 static int create_client_file(const char *node, const char *port)
diff --git a/trace-msg.h b/trace-msg.h
index b23e72b..0cc972c 100644
--- a/trace-msg.h
+++ b/trace-msg.h
@@ -5,6 +5,7 @@
 
 #define UDP_MAX_PACKET	(65536 - 20)
 #define V2_MAGIC	"677768\0"
+#define V2_CPU		"-1V2"
 
 #define V1_PROTOCOL	1
 #define V2_PROTOCOL	2
diff --git a/trace-record.c b/trace-record.c
index b6f58f9..4706f90 100644
--- a/trace-record.c
+++ b/trace-record.c
@@ -2699,6 +2699,7 @@
 static void check_protocol_version(int fd)
 {
 	char buf[BUFSIZ];
+	int n;
 
 	check_first_msg_from_server(fd);
 
@@ -2714,19 +2715,28 @@
 	 * So, we add the dummy number (the magic number and 0 option) to the
 	 * first client message.
 	 */
-	write(fd, "V2\0"V2_MAGIC"0", sizeof(V2_MAGIC)+4);
+	write(fd, V2_CPU, sizeof(V2_CPU));
 
 	/* read a reply message */
-	read(fd, buf, BUFSIZ);
+	n = read(fd, buf, BUFSIZ);
 
-	if (!buf[0]) {
+	if (n < 0 || !buf[0]) {
 		/* the server uses the v1 protocol, so we'll use it */
 		proto_ver = V1_PROTOCOL;
 		plog("Use the v1 protocol\n");
 	} else {
-		if (memcmp(buf, "V2", 2) != 0)
+		if (memcmp(buf, "V2", n) != 0)
 			die("Cannot handle the protocol %s", buf);
 		/* OK, let's use v2 protocol */
+		write(fd, V2_MAGIC, sizeof(V2_MAGIC));
+
+		n = read(fd, buf, BUFSIZ - 1);
+		if (n != 2 || memcmp(buf, "OK", 2) != 0) {
+			if (n < 0)
+				n  = 0;
+			buf[n] = 0;
+			die("Cannot handle the protocol %s", buf);
+		}
 	}
 }