From 9d065b401c2c393bef5a6e58b5deeda7d59d4f39 Mon Sep 17 00:00:00 2001
From: Ed Okerson <ed@okerson.com>
Date: Mon, 11 Feb 2013 15:46:10 -0600
Subject: [PATCH 16/22] Add support for OpenVPN 2.3.0 status files.

Fix a bug that breaks this module if a server is running multiple instances of OpenVPN and one instance does not have any clients connected.
---
 src/openvpn.c | 90 +++++++++++++++++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 84 insertions(+), 6 deletions(-)

diff --git a/src/openvpn.c b/src/openvpn.c
index 2aca414..d2b6f17 100644
--- a/src/openvpn.c
+++ b/src/openvpn.c
@@ -32,6 +32,7 @@
 #define V1STRING "Common Name,Real Address,Bytes Received,Bytes Sent,Connected Since\n"
 #define V2STRING "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t)\n"
 #define V3STRING "HEADER CLIENT_LIST Common Name Real Address Virtual Address Bytes Received Bytes Sent Connected Since Connected Since (time_t)\n"
+#define V4STRING "HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username\n"
 #define VSSTRING "OpenVPN STATISTICS\n"
 
 
@@ -43,6 +44,7 @@ struct vpn_status_s
 		MULTI1 = 1, /* status-version 1 */
 		MULTI2,     /* status-version 2 */
 		MULTI3,     /* status-version 3 */
+		MULTI4,     /* status-version 4 */
 		SINGLE = 10 /* currently no versions for single mode, maybe in the future */
 	} version;
 	char *name;
@@ -452,13 +454,77 @@ static int multi3_read (char *name, FILE *fh)
 	return (read);
 } /* int multi3_read */
 
+/* for reading status version 4 */
+static int multi4_read (char *name, FILE *fh)
+{
+	char buffer[1024];
+	char *fields[11];
+	const int max_fields = STATIC_ARRAY_SIZE (fields);
+	int  fields_num, read = 0;
+	long long sum_users    = 0;
+
+	while (fgets (buffer, sizeof (buffer), fh) != NULL)
+	{
+		fields_num = openvpn_strsplit (buffer, fields, max_fields);
+
+		/* status file is generated by openvpn/multi.c:multi_print_status()
+		 * http://svn.openvpn.net/projects/openvpn/trunk/openvpn/multi.c
+		 *
+		 * The line we're expecting has 9 fields. We ignore all lines
+		 *  with more or less fields.
+		 */
+		if (fields_num != 9)
+			continue;
+
+
+		if (strcmp (fields[0], "CLIENT_LIST") != 0)
+			continue;
+
+
+		if (collect_user_count)
+			/* If so, sum all users, ignore the individuals*/
+		{
+			sum_users += 1;
+		}
+		if (collect_individual_users)
+		{
+			if (new_naming_schema)
+			{
+				/* plugin inst = file name, type inst = fields[1] */
+				iostats_submit (name,               /* vpn instance */
+						fields[1],          /* "Common Name" */
+						atoll (fields[4]),  /* "Bytes Received" */
+						atoll (fields[5])); /* "Bytes Sent" */
+			}
+			else
+			{
+				/* plugin inst = fields[1], type inst = "" */
+				iostats_submit (fields[1],          /* "Common Name" */
+						NULL,               /* unused when in multimode */
+						atoll (fields[4]),  /* "Bytes Received" */
+						atoll (fields[5])); /* "Bytes Sent" */
+			}
+		}
+
+		read = 1;
+	}
+
+	if (collect_user_count)
+	{
+		numusers_submit(name, name, sum_users);
+		read = 1;
+	}
+
+	return (read);
+} /* int multi4_read */
+
 /* read callback */
 static int openvpn_read (void)
 {
 	FILE *fh;
-	int  i, read;
+	int  i, vpn_read, read;
 
-	read = 0;
+	vpn_read = read = 0;
 
 	/* call the right read function for every status entry in the list */
 	for (i = 0; i < vpn_num; i++)
@@ -476,23 +542,28 @@ static int openvpn_read (void)
 		switch (vpn_list[i]->version)
 		{
 			case SINGLE:
-				read = single_read(vpn_list[i]->name, fh);
+				vpn_read = single_read(vpn_list[i]->name, fh);
 				break;
 
 			case MULTI1:
-				read = multi1_read(vpn_list[i]->name, fh);
+				vpn_read = multi1_read(vpn_list[i]->name, fh);
 				break;
 
 			case MULTI2:
-				read = multi2_read(vpn_list[i]->name, fh);
+				vpn_read = multi2_read(vpn_list[i]->name, fh);
 				break;
 
 			case MULTI3:
-				read = multi3_read(vpn_list[i]->name, fh);
+				vpn_read = multi3_read(vpn_list[i]->name, fh);
+				break;
+
+			case MULTI4:
+				vpn_read = multi4_read(vpn_list[i]->name, fh);
 				break;
 		}
 
 		fclose (fh);
+		read += vpn_read;
 	}
 
 	return (read ? 0 : -1);
@@ -549,6 +620,13 @@ static int version_detect (const char *filename)
 			version = MULTI3;
 			break;
 		}
+		/* searching for multi version 4 */
+		else if (strcmp (buffer, V4STRING) == 0)
+		{
+			DEBUG ("openvpn plugin: found status file version MULTI4");
+			version = MULTI4;
+			break;
+		}
 	}
 
 	if (version == 0)
-- 
1.9.3

