Add new net command for text logging

Encapsulate the text in a payload, that also includes the
logging level and the time the event took place. The text
client will do as it always has done, but the gui client
can now do proper logging and knows if this is debug/info
or error messages.

No functional change in the GUI.

Signed-off-by: Jens Axboe <axboe@kernel.dk>
diff --git a/client.c b/client.c
index feb1eba..ff3983b 100644
--- a/client.c
+++ b/client.c
@@ -21,29 +21,15 @@
 #include "flist.h"
 #include "hash.h"
 
-static void fio_client_text_op(struct fio_client *client,
-		FILE *f, __u16 pdu_len, const char *buf)
-{
-	const char *name;
-	int fio_unused ret;
-
-	name = client->name ? client->name : client->hostname;
-
-	if (!client->skip_newline)
-		fprintf(f, "<%s> ", name);
-	ret = fwrite(buf, pdu_len, 1, f);
-	fflush(f);
-	client->skip_newline = strchr(buf, '\n') == NULL;
-}
-
 static void handle_du(struct fio_client *client, struct fio_net_cmd *cmd);
 static void handle_ts(struct fio_net_cmd *cmd);
 static void handle_gs(struct fio_net_cmd *cmd);
 static void handle_eta(struct fio_client *client, struct fio_net_cmd *cmd);
 static void handle_probe(struct fio_client *client, struct fio_net_cmd *cmd);
+static void handle_text(struct fio_client *client, struct fio_net_cmd *cmd);
 
 struct client_ops fio_client_ops = {
-	.text_op	= fio_client_text_op,
+	.text_op	= handle_text,
 	.disk_util	= handle_du,
 	.thread_status	= handle_ts,
 	.group_stats	= handle_gs,
@@ -698,6 +684,23 @@
 	dus->msec		= le64_to_cpu(dus->msec);
 }
 
+static void handle_text(struct fio_client *client, struct fio_net_cmd *cmd)
+{
+	struct cmd_text_pdu *pdu = (struct cmd_text_pdu *) cmd->payload;
+	const char *buf = (const char *) pdu->buf;
+	const char *name;
+	int fio_unused ret;
+
+	name = client->name ? client->name : client->hostname;
+
+	if (!client->skip_newline)
+		fprintf(f_out, "<%s> ", name);
+	ret = fwrite(buf, pdu->buf_len, 1, f_out);
+	fflush(f_out);
+	client->skip_newline = strchr(buf, '\n') == NULL;
+}
+
+
 static void handle_du(struct fio_client *client, struct fio_net_cmd *cmd)
 {
 	struct cmd_du_pdu *du = (struct cmd_du_pdu *) cmd->payload;
@@ -851,6 +854,16 @@
 		log_info("client <%s>: exited with error %d\n", client->hostname, client->error);
 }
 
+static void convert_text(struct fio_net_cmd *cmd)
+{
+	struct cmd_text_pdu *pdu = (struct cmd_text_pdu *) cmd->payload;
+
+	pdu->level	= le32_to_cpu(pdu->level);
+	pdu->buf_len	= le32_to_cpu(pdu->buf_len);
+	pdu->log_sec	= le64_to_cpu(pdu->log_sec);
+	pdu->log_usec	= le64_to_cpu(pdu->log_usec);
+}
+
 int fio_handle_client(struct fio_client *client, struct client_ops *ops)
 {
 	struct fio_net_cmd *cmd;
@@ -871,12 +884,11 @@
 		remove_client(client);
 		free(cmd);
 		break;
-	case FIO_NET_CMD_TEXT: {
-		const char *buf = (const char *) cmd->payload;
-		ops->text_op(client, f_out, cmd->pdu_len, buf);
+	case FIO_NET_CMD_TEXT:
+		convert_text(cmd);
+		ops->text_op(client, cmd);
 		free(cmd);
 		break;
-		}
 	case FIO_NET_CMD_DU:
 		ops->disk_util(client, cmd);
 		free(cmd);
diff --git a/client.h b/client.h
index de6c197..7ab4c83 100644
--- a/client.h
+++ b/client.h
@@ -46,8 +46,7 @@
 	void *client_data;
 };
 
-typedef void (*client_text_op_func)(struct fio_client *client,
-		FILE *f, __u16 pdu_len, const char *buf);
+typedef void (*client_text_op_func)(struct fio_client *client, struct fio_net_cmd *cmd);
 typedef void (*client_disk_util_op_func)(struct fio_client *client, struct fio_net_cmd *cmd);
 typedef void (*client_thread_status_op)(struct fio_net_cmd *cmd);
 typedef void (*client_group_stats_op)(struct fio_net_cmd *cmd);
diff --git a/gfio.c b/gfio.c
index 34b21ab..32e012e 100644
--- a/gfio.c
+++ b/gfio.c
@@ -130,8 +130,7 @@
 	}
 }
 
-static void gfio_text_op(struct fio_client *client,
-                FILE *f, __u16 pdu_len, const char *buf)
+static void gfio_text_op(struct fio_client *client, struct fio_net_cmd *cmd)
 {
 #if 0
 	GtkTextBuffer *buffer;
@@ -145,7 +144,7 @@
 	gtk_text_view_scroll_to_iter(GTK_TEXT_VIEW(ui.textview),
 					&end, 0.0, FALSE, 0.0,0.0);
 #else
-	fio_client_ops.text_op(client, f, pdu_len, buf);
+	fio_client_ops.text_op(client, cmd);
 #endif
 }
 
diff --git a/log.c b/log.c
index af974f8..362ab23 100644
--- a/log.c
+++ b/log.c
@@ -60,7 +60,7 @@
 	va_end(args);
 
 	if (is_backend)
-		return fio_server_text_output(buffer, len);
+		return fio_server_text_output(FIO_LOG_INFO, buffer, len);
 	else if (log_syslog) {
 		syslog(LOG_INFO, "%s", buffer);
 		return len;
@@ -79,7 +79,7 @@
 	va_end(args);
 
 	if (is_backend)
-		return fio_server_text_output(buffer, len);
+		return fio_server_text_output(FIO_LOG_ERR, buffer, len);
 	else if (log_syslog) {
 		syslog(LOG_INFO, "%s", buffer);
 		return len;
diff --git a/log.h b/log.h
index fdf3d7b..2b2cae0 100644
--- a/log.h
+++ b/log.h
@@ -13,4 +13,10 @@
 extern int log_valist(const char *str, va_list);
 extern int log_local_buf(const char *buf, size_t);
 
+enum {
+	FIO_LOG_DEBUG	= 1,
+	FIO_LOG_INFO	= 2,
+	FIO_LOG_ERR	= 3,
+};
+
 #endif
diff --git a/server.c b/server.c
index 299c3e1..1e2fcd0 100644
--- a/server.c
+++ b/server.c
@@ -222,12 +222,19 @@
 		cmdret = NULL;
 	} else if (cmdret) {
 		/* zero-terminate text input */
-		if (cmdret->pdu_len && (cmdret->opcode == FIO_NET_CMD_TEXT ||
-		    cmdret->opcode == FIO_NET_CMD_JOB)) {
-			char *buf = (char *) cmdret->payload;
+		if (cmdret->pdu_len) {
+			if (cmdret->opcode == FIO_NET_CMD_TEXT) {
+				struct cmd_text_pdu *pdu = (struct cmd_text_pdu *) cmdret->payload;
+				char *buf = (char *) pdu->buf;
 
-			buf[cmdret->pdu_len ] = '\0';
+				buf[pdu->buf_len ] = '\0';
+			} else if (cmdret->opcode == FIO_NET_CMD_JOB) {
+				char *buf = (char *) cmdret->payload;
+
+				buf[cmdret->pdu_len ] = '\0';
+			}
 		}
+
 		/* frag flag is internal */
 		cmdret->flags &= ~FIO_NET_CMD_F_MORE;
 	}
@@ -619,12 +626,30 @@
 	return exitval;
 }
 
-int fio_server_text_output(const char *buf, size_t len)
+int fio_server_text_output(int level, const char *buf, size_t len)
 {
-	if (server_fd != -1)
-		return fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, buf, len, 0);
+	struct cmd_text_pdu *pdu;
+	unsigned int tlen;
+	struct timeval tv;
 
-	return log_local_buf(buf, len);
+	if (server_fd == -1)
+		return log_local_buf(buf, len);
+
+	tlen = sizeof(*pdu) + len;
+	pdu = malloc(tlen);
+
+	pdu->level	= __cpu_to_le32(level);
+	pdu->buf_len	= __cpu_to_le32(len);
+
+	gettimeofday(&tv, NULL);
+	pdu->log_sec	= __cpu_to_le64(tv.tv_sec);
+	pdu->log_usec	= __cpu_to_le64(tv.tv_usec);
+
+	memcpy(pdu->buf, buf, len);
+
+	fio_net_send_cmd(server_fd, FIO_NET_CMD_TEXT, pdu, tlen, 0);
+	free(pdu);
+	return len;
 }
 
 static void convert_io_stat(struct io_stat *dst, struct io_stat *src)
@@ -826,21 +851,6 @@
 	fio_net_send_cmd(server_fd, FIO_NET_CMD_ADD_JOB, &pdu, sizeof(pdu), 0);
 }
 
-int fio_server_log(const char *format, ...)
-{
-	char buffer[1024];
-	va_list args;
-	size_t len;
-
-	dprint(FD_NET, "server log\n");
-
-	va_start(args, format);
-	len = vsnprintf(buffer, sizeof(buffer), format, args);
-	va_end(args);
-
-	return fio_server_text_output(buffer, len);
-}
-
 static int fio_init_server_ip(void)
 {
 	struct sockaddr *addr;
diff --git a/server.h b/server.h
index 5c7c8db..299c7a0 100644
--- a/server.h
+++ b/server.h
@@ -38,7 +38,7 @@
 };
 
 enum {
-	FIO_SERVER_VER		= 8,
+	FIO_SERVER_VER		= 9,
 
 	FIO_SERVER_MAX_PDU	= 1024,
 
@@ -118,9 +118,16 @@
 	uint32_t group_reporting;
 };
 
+struct cmd_text_pdu {
+	uint32_t level;
+	uint32_t buf_len;
+	uint64_t log_sec;
+	uint64_t log_usec;
+	uint8_t buf[0];
+};
+
 extern int fio_start_server(char *);
-extern int fio_server_text_output(const char *, size_t);
-extern int fio_server_log(const char *format, ...);
+extern int fio_server_text_output(int, const char *, size_t);
 extern int fio_net_send_cmd(int, uint16_t, const void *, off_t, uint64_t);
 extern int fio_net_send_simple_cmd(int, uint16_t, uint64_t, struct flist_head *);
 extern void fio_server_set_arg(const char *);