Hi,

I have updated the patch. It now converts between the old format and the new 
format. connmand does this automatically when it first access a data file. 
stats-tool gains a -x option to enable conversion.

On Tue, 4 Mar 2014 11:37:25 Patrik Flykt wrote:
> On Tue, 2014-03-04 at 09:42 +0100, Daniel Wagner wrote:
> > > The data type of the counter fields are not documented in the
> > > counter-api.txt file.
> 
> That's a really bad omission on our part. That means everybody has
> fished out the data type from the D-Bus messages, i.e. it's been given
> an undocumented de-facto type of DBUS_TYPE_UINT32.
> 
> > > Depending on how the demarshaller is
> > > implemented this type change is potentially handled automatically.
> 
> This is probably not the case for whatever C and C++ implementations
> there might exist.

I guess that really depends on how you are interfacing with it. I was using Qt 
DBus to convert from the dict to a QVariantMap which parsed the values 
correctly into a QVariant object. Of course the surrounding code truncating 
the 64 bit values when extracting them from the QVariant.

> > > Extending the DBus interface (instead of just changing the
> > > signature) is easy enough.
> > 
> > Okay, sounds reasonable to me. I don't have a problem with this.
> 
> In order not to confuse anybody, at a minimum the new DBUS_TYPE_UINT64s
> must be given a new name. Unfortunately as the API has not been marked
> experimental we'll have to support the current wrap-around prone
> DBUS_TYPE_UINT32s also...

The net.connman.Counter interface has not been marked as experimental but the 
RegisterCounter/UnregisterCounter methods of net.connman.Manager interface 
have.

Is it allowable to just extend the number of fields in the dict parameters of 
the net.connman.Counter.Update(object service, dict home, dict roaming) method 
or should I add an extra method?

Cheers,

-- 
Aaron McCarthy
>From 4856fb55ae8468d48ec1982b92ed1fcd65a9a160 Mon Sep 17 00:00:00 2001
From: Aaron McCarthy <[email protected]>
Date: Fri, 7 Mar 2014 14:34:53 +1000
Subject: [PATCH] Change data counters to use 64 bit data.

The previous data counters used unsigned 32 bit types. This is
probably adequate for most of the data fields, but for the bytes
received and bytes transmitted it means that the counters wrap after
4GB of data.

Change to use the 64 bit interface to retrieve the data from the kernel
and to use 64 bit types in both the storage and DBus API.

This changes the type signature of the DBus interface and expects
applications to cope.

This changes the binary format of the data files. The record size are
now larger and the magic number changed. Both connmand will convert
from the old format to the new format. stats-tool has a new -x option
to convert the data files.
---
 src/connman.h      |  30 +++---
 src/ipconfig.c     |  26 ++---
 src/rtnl.c         |  14 +--
 src/service.c      |  32 +++---
 src/stats.c        | 207 ++++++++++++++++++++++++++++++++++-
 tools/stats-tool.c | 308 ++++++++++++++++++++++++++++++++++++++++++++++++++---
 vpn/vpn-ipconfig.c |   4 +-
 vpn/vpn-rtnl.c     |  14 +--
 vpn/vpn.h          |   6 +-
 9 files changed, 558 insertions(+), 83 deletions(-)

diff --git a/src/connman.h b/src/connman.h
index f5ed995..00af076 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -315,13 +315,13 @@ void __connman_ipconfig_enable_ipv6(struct connman_ipconfig *ipconfig);
 int __connman_ipconfig_init(void);
 void __connman_ipconfig_cleanup(void);
 
-struct rtnl_link_stats;
+struct rtnl_link_stats64;
 
 void __connman_ipconfig_newlink(int index, unsigned short type,
 				unsigned int flags, const char *address,
 							unsigned short mtu,
-						struct rtnl_link_stats *stats);
-void __connman_ipconfig_dellink(int index, struct rtnl_link_stats *stats);
+						struct rtnl_link_stats64 *stats);
+void __connman_ipconfig_dellink(int index, struct rtnl_link_stats64 *stats);
 void __connman_ipconfig_newaddr(int index, int family, const char *label,
 				unsigned char prefixlen, const char *address);
 void __connman_ipconfig_deladdr(int index, int family, const char *label,
@@ -761,10 +761,10 @@ int __connman_service_reset_ipconfig(struct connman_service *service,
 		enum connman_service_state *new_state);
 
 void __connman_service_notify(struct connman_service *service,
-			unsigned int rx_packets, unsigned int tx_packets,
-			unsigned int rx_bytes, unsigned int tx_bytes,
-			unsigned int rx_error, unsigned int tx_error,
-			unsigned int rx_dropped, unsigned int tx_dropped);
+			uint64_t rx_packets, uint64_t tx_packets,
+			uint64_t rx_bytes, uint64_t tx_bytes,
+			uint64_t rx_error, uint64_t tx_error,
+			uint64_t rx_dropped, uint64_t tx_dropped);
 
 int __connman_service_counter_register(const char *counter);
 void __connman_service_counter_unregister(const char *counter);
@@ -826,14 +826,14 @@ int __connman_session_init(void);
 void __connman_session_cleanup(void);
 
 struct connman_stats_data {
-	unsigned int rx_packets;
-	unsigned int tx_packets;
-	unsigned int rx_bytes;
-	unsigned int tx_bytes;
-	unsigned int rx_errors;
-	unsigned int tx_errors;
-	unsigned int rx_dropped;
-	unsigned int tx_dropped;
+	uint64_t rx_packets;
+	uint64_t tx_packets;
+	uint64_t rx_bytes;
+	uint64_t tx_bytes;
+	uint64_t rx_errors;
+	uint64_t tx_errors;
+	uint64_t rx_dropped;
+	uint64_t tx_dropped;
 	unsigned int time;
 };
 
diff --git a/src/ipconfig.c b/src/ipconfig.c
index 9452125..7b88107 100644
--- a/src/ipconfig.c
+++ b/src/ipconfig.c
@@ -66,14 +66,14 @@ struct connman_ipdevice {
 	unsigned int flags;
 	char *address;
 	uint16_t mtu;
-	uint32_t rx_packets;
-	uint32_t tx_packets;
-	uint32_t rx_bytes;
-	uint32_t tx_bytes;
-	uint32_t rx_errors;
-	uint32_t tx_errors;
-	uint32_t rx_dropped;
-	uint32_t tx_dropped;
+	uint64_t rx_packets;
+	uint64_t tx_packets;
+	uint64_t rx_bytes;
+	uint64_t tx_bytes;
+	uint64_t rx_errors;
+	uint64_t tx_errors;
+	uint64_t rx_dropped;
+	uint64_t tx_dropped;
 
 	GSList *address_list;
 	char *ipv4_gateway;
@@ -430,16 +430,16 @@ static void __connman_ipconfig_lower_down(struct connman_ipdevice *ipdevice)
 }
 
 static void update_stats(struct connman_ipdevice *ipdevice,
-			const char *ifname, struct rtnl_link_stats *stats)
+			const char *ifname, struct rtnl_link_stats64 *stats)
 {
 	struct connman_service *service;
 
 	if (stats->rx_packets == 0 && stats->tx_packets == 0)
 		return;
 
-	connman_info("%s {RX} %u packets %u bytes", ifname,
+	connman_info("%s {RX} %llu packets %llu bytes", ifname,
 					stats->rx_packets, stats->rx_bytes);
-	connman_info("%s {TX} %u packets %u bytes", ifname,
+	connman_info("%s {TX} %llu packets %llu bytes", ifname,
 					stats->tx_packets, stats->tx_bytes);
 
 	if (!ipdevice->config_ipv4 && !ipdevice->config_ipv6)
@@ -474,7 +474,7 @@ static void update_stats(struct connman_ipdevice *ipdevice,
 void __connman_ipconfig_newlink(int index, unsigned short type,
 				unsigned int flags, const char *address,
 							unsigned short mtu,
-						struct rtnl_link_stats *stats)
+						struct rtnl_link_stats64 *stats)
 {
 	struct connman_ipdevice *ipdevice;
 	GList *list;
@@ -586,7 +586,7 @@ out:
 	g_free(ifname);
 }
 
-void __connman_ipconfig_dellink(int index, struct rtnl_link_stats *stats)
+void __connman_ipconfig_dellink(int index, struct rtnl_link_stats64 *stats)
 {
 	struct connman_ipdevice *ipdevice;
 	GList *list;
diff --git a/src/rtnl.c b/src/rtnl.c
index c8708eb..9dab994 100644
--- a/src/rtnl.c
+++ b/src/rtnl.c
@@ -360,7 +360,7 @@ static const char *operstate2str(unsigned char operstate)
 static bool extract_link(struct ifinfomsg *msg, int bytes,
 				struct ether_addr *address, const char **ifname,
 				unsigned int *mtu, unsigned char *operstate,
-				struct rtnl_link_stats *stats)
+				struct rtnl_link_stats64 *stats)
 {
 	struct rtattr *attr;
 
@@ -379,10 +379,10 @@ static bool extract_link(struct ifinfomsg *msg, int bytes,
 			if (mtu)
 				*mtu = *((unsigned int *) RTA_DATA(attr));
 			break;
-		case IFLA_STATS:
+		case IFLA_STATS64:
 			if (stats)
 				memcpy(stats, RTA_DATA(attr),
-					sizeof(struct rtnl_link_stats));
+					sizeof(struct rtnl_link_stats64));
 			break;
 		case IFLA_OPERSTATE:
 			if (operstate)
@@ -403,7 +403,7 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
 {
 	struct ether_addr address = {{ 0, 0, 0, 0, 0, 0 }};
 	struct ether_addr compare = {{ 0, 0, 0, 0, 0, 0 }};
-	struct rtnl_link_stats stats;
+	struct rtnl_link_stats64 stats;
 	unsigned char operstate = 0xff;
 	struct interface_data *interface;
 	const char *ifname = NULL;
@@ -496,7 +496,7 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
 static void process_dellink(unsigned short type, int index, unsigned flags,
 			unsigned change, struct ifinfomsg *msg, int bytes)
 {
-	struct rtnl_link_stats stats;
+	struct rtnl_link_stats64 stats;
 	unsigned char operstate = 0xff;
 	const char *ifname = NULL;
 	GSList *list;
@@ -899,8 +899,8 @@ static void rtnl_link(struct nlmsghdr *hdr)
 		case IFLA_QDISC:
 			print_attr(attr, "qdisc");
 			break;
-		case IFLA_STATS:
-			print_attr(attr, "stats");
+		case IFLA_STATS64:
+			print_attr(attr, "stats64");
 			break;
 		case IFLA_COST:
 			print_attr(attr, "cost");
diff --git a/src/service.c b/src/service.c
index 288c68e..651814a 100644
--- a/src/service.c
+++ b/src/service.c
@@ -1990,49 +1990,49 @@ static void stats_append_counters(DBusMessageIter *dict,
 	if (counters->rx_packets != stats->rx_packets || append_all) {
 		counters->rx_packets = stats->rx_packets;
 		connman_dbus_dict_append_basic(dict, "RX.Packets",
-					DBUS_TYPE_UINT32, &stats->rx_packets);
+					DBUS_TYPE_UINT64, &stats->rx_packets);
 	}
 
 	if (counters->tx_packets != stats->tx_packets || append_all) {
 		counters->tx_packets = stats->tx_packets;
 		connman_dbus_dict_append_basic(dict, "TX.Packets",
-					DBUS_TYPE_UINT32, &stats->tx_packets);
+					DBUS_TYPE_UINT64, &stats->tx_packets);
 	}
 
 	if (counters->rx_bytes != stats->rx_bytes || append_all) {
 		counters->rx_bytes = stats->rx_bytes;
 		connman_dbus_dict_append_basic(dict, "RX.Bytes",
-					DBUS_TYPE_UINT32, &stats->rx_bytes);
+					DBUS_TYPE_UINT64, &stats->rx_bytes);
 	}
 
 	if (counters->tx_bytes != stats->tx_bytes || append_all) {
 		counters->tx_bytes = stats->tx_bytes;
 		connman_dbus_dict_append_basic(dict, "TX.Bytes",
-					DBUS_TYPE_UINT32, &stats->tx_bytes);
+					DBUS_TYPE_UINT64, &stats->tx_bytes);
 	}
 
 	if (counters->rx_errors != stats->rx_errors || append_all) {
 		counters->rx_errors = stats->rx_errors;
 		connman_dbus_dict_append_basic(dict, "RX.Errors",
-					DBUS_TYPE_UINT32, &stats->rx_errors);
+					DBUS_TYPE_UINT64, &stats->rx_errors);
 	}
 
 	if (counters->tx_errors != stats->tx_errors || append_all) {
 		counters->tx_errors = stats->tx_errors;
 		connman_dbus_dict_append_basic(dict, "TX.Errors",
-					DBUS_TYPE_UINT32, &stats->tx_errors);
+					DBUS_TYPE_UINT64, &stats->tx_errors);
 	}
 
 	if (counters->rx_dropped != stats->rx_dropped || append_all) {
 		counters->rx_dropped = stats->rx_dropped;
 		connman_dbus_dict_append_basic(dict, "RX.Dropped",
-					DBUS_TYPE_UINT32, &stats->rx_dropped);
+					DBUS_TYPE_UINT64, &stats->rx_dropped);
 	}
 
 	if (counters->tx_dropped != stats->tx_dropped || append_all) {
 		counters->tx_dropped = stats->tx_dropped;
 		connman_dbus_dict_append_basic(dict, "TX.Dropped",
-					DBUS_TYPE_UINT32, &stats->tx_dropped);
+					DBUS_TYPE_UINT64, &stats->tx_dropped);
 	}
 
 	if (counters->time != stats->time || append_all) {
@@ -2081,10 +2081,10 @@ static void stats_append(struct connman_service *service,
 }
 
 static void stats_update(struct connman_service *service,
-				unsigned int rx_packets, unsigned int tx_packets,
-				unsigned int rx_bytes, unsigned int tx_bytes,
-				unsigned int rx_errors, unsigned int tx_errors,
-				unsigned int rx_dropped, unsigned int tx_dropped)
+				uint64_t rx_packets, uint64_t tx_packets,
+				uint64_t rx_bytes, uint64_t tx_bytes,
+				uint64_t rx_errors, uint64_t tx_errors,
+				uint64_t rx_dropped, uint64_t tx_dropped)
 {
 	struct connman_stats *stats = stats_get(service);
 	struct connman_stats_data *data_last = &stats->data_last;
@@ -2128,10 +2128,10 @@ static void stats_update(struct connman_service *service,
 }
 
 void __connman_service_notify(struct connman_service *service,
-			unsigned int rx_packets, unsigned int tx_packets,
-			unsigned int rx_bytes, unsigned int tx_bytes,
-			unsigned int rx_errors, unsigned int tx_errors,
-			unsigned int rx_dropped, unsigned int tx_dropped)
+			uint64_t rx_packets, uint64_t tx_packets,
+			uint64_t rx_bytes, uint64_t tx_bytes,
+			uint64_t rx_errors, uint64_t tx_errors,
+			uint64_t rx_dropped, uint64_t tx_dropped)
 {
 	GHashTableIter iter;
 	gpointer key, value;
diff --git a/src/stats.c b/src/stats.c
index df5ab4e..fa91d1d 100644
--- a/src/stats.c
+++ b/src/stats.c
@@ -34,6 +34,7 @@
 #include <string.h>
 #include <limits.h>
 #include <sys/stat.h>
+#include <stdio.h>
 
 #include "connman.h"
 
@@ -46,7 +47,8 @@
 #define TFR
 #endif
 
-#define MAGIC 0xFA00B916
+#define MAGIC32 0xFA00B916
+#define MAGIC64 0xFA01B916
 
 /*
  * Statistics counters are stored into a ring buffer which is stored
@@ -100,6 +102,24 @@ struct stats_record {
 	struct connman_stats_data data;
 };
 
+struct connman_stats_data32 {
+	unsigned int rx_packets;
+	unsigned int tx_packets;
+	unsigned int rx_bytes;
+	unsigned int tx_bytes;
+	unsigned int rx_errors;
+	unsigned int tx_errors;
+	unsigned int rx_dropped;
+	unsigned int tx_dropped;
+	unsigned int time;
+};
+
+struct stats_record32 {
+	time_t ts;
+	unsigned int roaming;
+	struct connman_stats_data32 data;
+};
+
 struct stats_file {
 	int fd;
 	char *name;
@@ -125,6 +145,15 @@ struct stats_iter {
 	struct stats_record *it;
 };
 
+struct stats_iter32 {
+	struct stats_file *file;
+	struct stats_record32 *begin;
+	struct stats_record32 *end;
+	struct stats_record32 *it;
+};
+
+static void stats_convert(struct stats_file *file);
+
 static GHashTable *stats_hash = NULL;
 
 static struct stats_file_header *get_hdr(struct stats_file *file)
@@ -139,6 +168,13 @@ static struct stats_record *get_begin(struct stats_file *file)
 	return (struct stats_record *)(file->addr + off);
 }
 
+static struct stats_record32 *get_begin32(struct stats_file *file)
+{
+	unsigned int off = get_hdr(file)->begin;
+
+	return (struct stats_record32 *)(file->addr + off);
+}
+
 static struct stats_record *get_end(struct stats_file *file)
 {
 	unsigned int off = get_hdr(file)->end;
@@ -146,6 +182,13 @@ static struct stats_record *get_end(struct stats_file *file)
 	return (struct stats_record *)(file->addr + off);
 }
 
+static struct stats_record32 *get_end32(struct stats_file *file)
+{
+	unsigned int off = get_hdr(file)->end;
+
+	return (struct stats_record32 *)(file->addr + off);
+}
+
 static struct stats_record *get_home(struct stats_file *file)
 {
 	struct stats_file_header *hdr;
@@ -205,16 +248,36 @@ static struct stats_record *get_next(struct stats_file *file,
 	return cur;
 }
 
+static struct stats_record32 *get_next32(struct stats_file *file, struct stats_record32 *cur)
+{
+	cur++;
+
+	if (cur > (struct stats_record32 *)file->last)
+		cur = (struct stats_record32 *)file->first;
+
+	return cur;
+}
+
 static struct stats_record *get_iterator_begin(struct stats_file *file)
 {
 	return get_next(file, get_begin(file));
 }
 
+static struct stats_record32 *get_iterator_begin32(struct stats_file *file)
+{
+	return get_next32(file, get_begin32(file));
+}
+
 static struct stats_record *get_iterator_end(struct stats_file *file)
 {
 	return get_next(file, get_end(file));
 }
 
+static struct stats_record32 *get_iterator_end32(struct stats_file *file)
+{
+	return get_next32(file, get_end32(file));
+}
+
 static void stats_free(gpointer user_data)
 {
 	struct stats_file *file = user_data;
@@ -258,6 +321,15 @@ static void update_last(struct stats_file *file)
 	file->last = file->first + max_entries - 1;
 }
 
+static void update_last32(struct stats_file *file)
+{
+	unsigned int max_entries;
+
+	max_entries = (file->len - sizeof(struct stats_file_header)) /
+			sizeof(struct stats_record32);
+	file->last = file->first + max_entries - 1;
+}
+
 static void update_home(struct stats_file *file)
 {
 	file->home = get_home(file);
@@ -276,6 +348,14 @@ static void stats_file_update_cache(struct stats_file *file)
 	update_roaming(file);
 }
 
+static void stats_file_update_cache32(struct stats_file *file)
+{
+	update_first(file);
+	update_last32(file);
+	update_home(file);
+	update_roaming(file);
+}
+
 static int stats_file_remap(struct stats_file *file, size_t size)
 {
 	size_t page_size, new_size;
@@ -323,7 +403,10 @@ static int stats_file_remap(struct stats_file *file, size_t size)
 	file->addr = addr;
 	file->len = new_size;
 
-	stats_file_update_cache(file);
+	if (get_hdr(file)->magic == MAGIC32)
+		stats_file_update_cache32(file);
+	else
+		stats_file_update_cache(file);
 
 	return 0;
 }
@@ -401,14 +484,27 @@ static int stats_file_setup(struct stats_file *file)
 
 	hdr = get_hdr(file);
 
-	if (hdr->magic != MAGIC ||
+	/* Check if data file is in old format and convert */
+	if (hdr->magic == MAGIC32 &&
+			hdr->begin >= sizeof(struct stats_file_header) &&
+			hdr->end >= sizeof(struct stats_file_header) &&
+			hdr->home >= sizeof(struct stats_file_header) &&
+			hdr->roaming >= sizeof(struct stats_file_header) &&
+			hdr->begin <= file->len &&
+			hdr->end <= file->len) {
+			stats_convert(file);
+			/* conversion changes the mapped address */
+			hdr = get_hdr(file);
+	}
+
+	if (hdr->magic != MAGIC64 ||
 			hdr->begin < sizeof(struct stats_file_header) ||
 			hdr->end < sizeof(struct stats_file_header) ||
 			hdr->home < sizeof(struct stats_file_header) ||
 			hdr->roaming < sizeof(struct stats_file_header) ||
 			hdr->begin > file->len ||
 			hdr->end > file->len) {
-		hdr->magic = MAGIC;
+		hdr->magic = MAGIC64;
 		hdr->begin = sizeof(struct stats_file_header);
 		hdr->end = sizeof(struct stats_file_header);
 		hdr->home = UINT_MAX;
@@ -435,6 +531,20 @@ static struct stats_record *get_next_record(struct stats_iter *iter)
 	return NULL;
 }
 
+static struct stats_record32 *get_next_record32(struct stats_iter32 *iter)
+{
+	if (iter->it != iter->end) {
+		struct stats_record32 *tmp;
+
+		tmp = iter->it;
+		iter->it = get_next32(iter->file, iter->it);
+
+		return tmp;
+	}
+
+	return NULL;
+}
+
 static int append_record(struct stats_file *file,
 				struct stats_record *rec)
 {
@@ -668,6 +778,95 @@ static int stats_file_history_update(struct stats_file *data_file)
 	return err;
 }
 
+static void stats_convert(struct stats_file *file)
+{
+	struct stats_file temp_file;
+	int err;
+
+	DBG("converting data file %s", file->name);
+
+	stats_file_update_cache32(file);
+
+	bzero(&temp_file, sizeof(struct stats_file));
+
+	err = stats_open_temp(&temp_file);
+	if (err < 0) {
+		connman_error("failed to open temporary file during data conversion");
+		return;
+	}
+	stats_file_setup(&temp_file);
+
+	struct stats_iter32 data_iter;
+	struct stats_record32 *record;
+
+	data_iter.file = file;
+	data_iter.begin = get_iterator_begin32(data_iter.file);
+	data_iter.end = get_iterator_end32(data_iter.file);
+	data_iter.it = data_iter.begin;
+
+	record = get_next_record32(&data_iter);
+	while (record) {
+		struct stats_record *next;
+
+		if (temp_file.last == get_end(&temp_file)) {
+			err = stats_file_remap(&temp_file, temp_file.len + sysconf(_SC_PAGESIZE));
+			if (err < 0) {
+				connman_error("failed to extend file %s", temp_file.name);
+				unlink(temp_file.name);
+				stats_file_unmap(&temp_file);
+				TFR(close(temp_file.fd));
+				stats_file_cleanup(&temp_file);
+				return;
+			}
+
+			stats_file_update_cache(&temp_file);
+		}
+
+		next = get_next(&temp_file, get_end(&temp_file));
+		if (next == get_begin(&temp_file)) {
+			connman_error("ring buffer is full");
+			unlink(temp_file.name);
+			stats_file_unmap(&temp_file);
+			TFR(close(temp_file.fd));
+			stats_file_cleanup(&temp_file);
+			return;
+		}
+
+		next->ts = record->ts;
+		next->roaming = record->roaming;
+		next->data.rx_packets = record->data.rx_packets;
+		next->data.tx_packets = record->data.tx_packets;
+		next->data.rx_bytes = record->data.rx_bytes;
+		next->data.tx_bytes = record->data.tx_bytes;
+		next->data.rx_errors = record->data.rx_errors;
+		next->data.tx_errors = record->data.tx_errors;
+		next->data.rx_dropped = record->data.rx_dropped;
+		next->data.tx_dropped = record->data.tx_dropped;
+		next->data.time = record->data.time;
+
+		if (next->roaming)
+			set_roaming(&temp_file, next);
+		else
+			set_home(&temp_file, next);
+
+		set_end(&temp_file, next);
+
+		record = get_next_record32(&data_iter);
+	}
+
+	// close and swap
+	stats_file_unmap(file);
+	TFR(close(file->fd));
+	err = rename(temp_file.name, file->name);
+	if (err < 0)
+		connman_error("failed to rename converted data file %s to %s", temp_file.name, file->name);
+
+	g_free(temp_file.name);
+	temp_file.name = file->name;
+
+	memcpy(file, &temp_file, sizeof(struct stats_file));
+}
+
 int __connman_stats_service_register(struct connman_service *service)
 {
 	struct stats_file *file;
diff --git a/tools/stats-tool.c b/tools/stats-tool.c
index 7957c47..a5eb3ea 100644
--- a/tools/stats-tool.c
+++ b/tools/stats-tool.c
@@ -37,6 +37,7 @@
 #include <stdlib.h>
 #include <stdbool.h>
 #include <errno.h>
+#include <inttypes.h>
 
 #include <glib.h>
 #include <glib/gstdio.h>
@@ -47,9 +48,22 @@
 #define TFR
 #endif
 
-#define MAGIC 0xFA00B916
+#define MAGIC32 0xFA00B916
+#define MAGIC64 0xFA01B916
 
 struct connman_stats_data {
+	uint64_t rx_packets;
+	uint64_t tx_packets;
+	uint64_t rx_bytes;
+	uint64_t tx_bytes;
+	uint64_t rx_errors;
+	uint64_t tx_errors;
+	uint64_t rx_dropped;
+	uint64_t tx_dropped;
+	unsigned int time;
+};
+
+struct connman_stats_data32 {
 	unsigned int rx_packets;
 	unsigned int tx_packets;
 	unsigned int rx_bytes;
@@ -75,6 +89,12 @@ struct stats_record {
 	struct connman_stats_data data;
 };
 
+struct stats_record32 {
+	time_t ts;
+	unsigned int roaming;
+	struct connman_stats_data32 data;
+};
+
 struct stats_file {
 	int fd;
 	char *name;
@@ -98,6 +118,13 @@ struct stats_iter {
 	struct stats_record *it;
 };
 
+struct stats_iter32 {
+    struct stats_file *file;
+    struct stats_record32 *begin;
+    struct stats_record32 *end;
+    struct stats_record32 *it;
+};
+
 static gint option_create = 0;
 static gint option_interval = 3;
 static bool option_dump = false;
@@ -105,6 +132,7 @@ static bool option_summary = false;
 static char *option_info_file_name = NULL;
 static time_t option_start_ts = -1;
 static char *option_last_file_name = NULL;
+static bool option_convert = false;
 
 static bool parse_start_ts(const char *key, const char *value,
 					gpointer user_data, GError **error)
@@ -135,6 +163,8 @@ static GOptionEntry options[] = {
 			"(example 2010-11-05T23:00:12Z)", "TS"},
 	{ "last", 'l', 0, G_OPTION_ARG_FILENAME, &option_last_file_name,
 			  "Start values from last .data file" },
+    { "convert", 'x', 0, G_OPTION_ARG_NONE, &option_convert,
+              "Convert .data file to new version" },
 	{ NULL },
 };
 
@@ -150,6 +180,13 @@ static struct stats_record *get_begin(struct stats_file *file)
 	return (struct stats_record *)(file->addr + off);
 }
 
+static struct stats_record32 *get_begin32(struct stats_file *file)
+{
+	unsigned int off = get_hdr(file)->begin;
+
+	return (struct stats_record32 *)(file->addr + off);
+}
+
 static struct stats_record *get_end(struct stats_file *file)
 {
 	unsigned int off = get_hdr(file)->end;
@@ -157,6 +194,13 @@ static struct stats_record *get_end(struct stats_file *file)
 	return (struct stats_record *)(file->addr + off);
 }
 
+static struct stats_record32 *get_end32(struct stats_file *file)
+{
+	unsigned int off = get_hdr(file)->end;
+
+	return (struct stats_record32 *)(file->addr + off);
+}
+
 static struct stats_record *get_home(struct stats_file *file)
 {
 	struct stats_file_header *hdr;
@@ -194,6 +238,11 @@ static int get_index(struct stats_file *file, struct stats_record *rec)
 	return rec - file->first;
 }
 
+static int get_index32(struct stats_file *file, struct stats_record32 *rec)
+{
+	return rec - (struct stats_record32 *)file->first;
+}
+
 static struct stats_record *get_next(struct stats_file *file,
 					struct stats_record *cur)
 {
@@ -205,22 +254,42 @@ static struct stats_record *get_next(struct stats_file *file,
 	return cur;
 }
 
+static struct stats_record32 *get_next32(struct stats_file *file, struct stats_record32 *cur)
+{
+	cur++;
+
+	if (cur > (struct stats_record32 *)file->last)
+		cur = (struct stats_record32 *)file->first;
+
+	return cur;
+}
+
 static struct stats_record *get_iterator_begin(struct stats_file *file)
 {
 	return get_next(file, get_begin(file));
 }
 
+struct stats_record32 *get_iterator_begin32(struct stats_file *file)
+{
+	return get_next32(file, get_begin32(file));
+}
+
 static struct stats_record *get_iterator_end(struct stats_file *file)
 {
 	return get_next(file, get_end(file));
 }
 
+struct stats_record32 *get_iterator_end32(struct stats_file *file)
+{
+	return get_next32(file, get_end32(file));
+}
+
 static void stats_print_record(struct stats_record *rec)
 {
 	char buffer[30];
 
 	strftime(buffer, 30, "%d-%m-%Y %T", localtime(&rec->ts));
-	printf("%p %lld %s %01d %d %d %d %d %d %d %d %d %d\n",
+	printf("%p %lld %s %01d %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %" PRIu64 " %d\n",
 		rec, (long long int)rec->ts, buffer,
 		rec->roaming,
 		rec->data.rx_packets,
@@ -310,21 +379,21 @@ static void stats_print_entries(struct stats_file *file)
 static void stats_print_rec_diff(struct stats_record *begin,
 					struct stats_record *end)
 {
-	printf("\trx_packets: %d\n",
+	printf("\trx_packets: %" PRIu64 "\n",
 		end->data.rx_packets - begin->data.rx_packets);
-	printf("\ttx_packets: %d\n",
+	printf("\ttx_packets: %" PRIu64 "\n",
 		end->data.tx_packets - begin->data.tx_packets);
-	printf("\trx_bytes:   %d\n",
+	printf("\trx_bytes:   %" PRIu64 "\n",
 		end->data.rx_bytes - begin->data.rx_bytes);
-	printf("\ttx_bytes:   %d\n",
+	printf("\ttx_bytes:   %" PRIu64 "\n",
 		end->data.tx_bytes - begin->data.tx_bytes);
-	printf("\trx_errors:  %d\n",
+	printf("\trx_errors:  %" PRIu64 "\n",
 		end->data.rx_errors - begin->data.rx_errors);
-	printf("\ttx_errors:  %d\n",
+	printf("\ttx_errors:  %" PRIu64 "\n",
 		end->data.tx_errors - begin->data.tx_errors);
-	printf("\trx_dropped: %d\n",
+	printf("\trx_dropped: %" PRIu64 "\n",
 		end->data.rx_dropped - begin->data.rx_dropped);
-	printf("\ttx_dropped: %d\n",
+	printf("\ttx_dropped: %" PRIu64 "\n",
 		end->data.tx_dropped - begin->data.tx_dropped);
 	printf("\ttime:       %d\n",
 		end->data.time - begin->data.time);
@@ -362,6 +431,12 @@ static void update_max_nr_entries(struct stats_file *file)
 		sizeof(struct stats_record);
 }
 
+static void update_max_nr_entries32(struct stats_file *file)
+{
+	file->max_nr = (file->len - sizeof(struct stats_file_header)) /
+		sizeof(struct stats_record32);
+}
+
 static void update_nr_entries(struct stats_file *file)
 {
 	struct stats_record *begin, *end;
@@ -378,6 +453,22 @@ static void update_nr_entries(struct stats_file *file)
 	file->nr = nr;
 }
 
+static void update_nr_entries32(struct stats_file *file)
+{
+	struct stats_record32 *begin, *end;
+	int nr;
+
+	begin = get_begin32(file);
+	end = get_end32(file);
+
+	nr = get_index32(file, end) - get_index32(file, begin);
+
+	if (nr < 0)
+		nr += file->max_nr;
+
+	file->nr = nr;
+}
+
 static void update_first(struct stats_file *file)
 {
 	file->first = (struct stats_record *)(file->addr +
@@ -394,7 +485,17 @@ static void update_last(struct stats_file *file)
 	file->last = last;
 }
 
-static int stats_file_update_cache(struct stats_file *file)
+static void update_last32(struct stats_file *file)
+{
+	struct stats_record32 *last;
+
+	last = (struct stats_record32 *)file->first;
+	last += file->max_nr - 1;
+
+	file->last = (struct stats_record *)last;
+}
+
+static int stats_file_update_cache64(struct stats_file *file)
 {
 	struct stats_record *it, *end;
 
@@ -423,6 +524,43 @@ static int stats_file_update_cache(struct stats_file *file)
 	return 0;
 }
 
+static int stats_file_update_cache32(struct stats_file *file)
+{
+	struct stats_record32 *it, *end;
+
+	update_max_nr_entries32(file);
+	update_nr_entries32(file);
+	update_first(file);
+	update_last32(file);
+	file->home_first = NULL;
+	file->roaming_first = NULL;
+
+	end = get_iterator_end32(file);
+	for (it = get_iterator_begin32(file);
+			it != end;
+			it = get_next32(file, it)) {
+
+		if (!file->home_first && it->roaming == 0)
+			file->home_first = (struct stats_record *)it;
+
+		if (!file->roaming_first && it->roaming == 1)
+			file->roaming_first = (struct stats_record *)it;
+
+		if (file->home_first && file->roaming_first)
+			break;
+	}
+
+	return 0;
+}
+
+int stats_file_update_cache(struct stats_file *file)
+{
+	if (get_hdr(file)->magic == MAGIC32)
+		return stats_file_update_cache32(file);
+	else
+		return stats_file_update_cache64(file);
+}
+
 static int stats_file_remap(struct stats_file *file, size_t size)
 {
 	size_t page_size, new_size;
@@ -507,14 +645,14 @@ static int stats_open(struct stats_file *file, const char *name)
 
 	/* Initialize new file */
 	hdr = get_hdr(file);
-	if (hdr->magic != MAGIC ||
+	if ((hdr->magic != MAGIC64 && hdr->magic != MAGIC32) ||
 			hdr->begin < sizeof(struct stats_file_header) ||
 			hdr->end < sizeof(struct stats_file_header) ||
 			hdr->home < sizeof(struct stats_file_header) ||
 			hdr->roaming < sizeof(struct stats_file_header) ||
 			hdr->begin > file->len ||
 			hdr->end > file->len) {
-		hdr->magic = MAGIC;
+		hdr->magic = MAGIC64;
 		hdr->begin = sizeof(struct stats_file_header);
 		hdr->end = sizeof(struct stats_file_header);
 		hdr->home = UINT_MAX;
@@ -547,7 +685,7 @@ static int stats_create(struct stats_file *file, unsigned int nr,
 
 	hdr = get_hdr(file);
 
-	hdr->magic = MAGIC;
+	hdr->magic = MAGIC64;
 	hdr->begin = sizeof(struct stats_file_header);
 	hdr->end = sizeof(struct stats_file_header);
 	hdr->home = UINT_MAX;
@@ -626,6 +764,20 @@ static struct stats_record *get_next_record(struct stats_iter *iter)
 	return NULL;
 }
 
+struct stats_record32 *get_next_record32(struct stats_iter32 *iter)
+{
+	if (iter->it != iter->end) {
+		struct stats_record32 *tmp;
+
+		tmp = iter->it;
+		iter->it = get_next32(iter->file, iter->it);
+
+		return tmp;
+	}
+
+	return NULL;
+}
+
 static int append_record(struct stats_file *file,
 				struct stats_record *rec)
 {
@@ -813,9 +965,16 @@ static void history_file_update(struct stats_file *data_file,
 
 	struct stats_file *history_file = NULL;
 
-	if (stats_open(&_history_file, history_file_name) == 0)
+	if (stats_open(&_history_file, history_file_name) == 0) {
 		history_file = &_history_file;
 
+		if (get_hdr(history_file)->magic != MAGIC64) {
+			fprintf(stderr, "history file %s in old format, needs converting\n", history_file_name);
+			stats_close(history_file);
+			return;
+		}
+	}
+
 	if (stats_open(&tempory_file, NULL) < 0) {
 		if (history_file)
 			stats_close(history_file);
@@ -827,6 +986,103 @@ static void history_file_update(struct stats_file *data_file,
 	swap_and_close_files(history_file, &tempory_file);
 }
 
+void set_home(struct stats_file *file, struct stats_record *home)
+{
+	struct stats_file_header *hdr;
+
+	hdr = get_hdr(file);
+	hdr->home = (char *)home - file->addr;
+}
+
+void set_roaming(struct stats_file *file, struct stats_record *roaming)
+{
+	struct stats_file_header *hdr;
+
+	hdr = get_hdr(file);
+	hdr->roaming = (char *)roaming - file->addr;
+}
+
+int stats_convert(struct stats_file *data_file)
+{
+	struct stats_file temp_file;
+	int err;
+
+	err = stats_open(&temp_file, NULL);
+	if (err < 0) {
+		fprintf(stderr, "failed to open temporary file during data conversion\n");
+		return -1;
+	}
+
+	struct stats_iter32 data_iter;
+	struct stats_record32 *record;
+
+	data_iter.file = data_file;
+	data_iter.begin = get_iterator_begin32(data_iter.file);
+	data_iter.end = get_iterator_end32(data_iter.file);
+	data_iter.it = data_iter.begin;
+
+	record = get_next_record32(&data_iter);
+	while (record) {
+		struct stats_record *next;
+
+		if (temp_file.last == get_end(&temp_file)) {
+			err = stats_file_remap(&temp_file, temp_file.len + sysconf(_SC_PAGESIZE));
+			if (err < 0) {
+				fprintf(stderr, "failed to extend file %s\n", temp_file.name);
+				unlink(temp_file.name);
+				stats_close(&temp_file);
+				return -1;
+			}
+
+			stats_file_update_cache(&temp_file);
+		}
+
+		next = get_next(&temp_file, get_end(&temp_file));
+		if (next == get_begin(&temp_file)) {
+			fprintf(stderr, "ring buffer is full\n");
+			unlink(temp_file.name);
+			stats_close(&temp_file);
+			return -1;
+		}
+
+		next->ts = record->ts;
+		next->roaming = record->roaming;
+		next->data.rx_packets = record->data.rx_packets;
+		next->data.tx_packets = record->data.tx_packets;
+		next->data.rx_bytes = record->data.rx_bytes;
+		next->data.tx_bytes = record->data.tx_bytes;
+		next->data.rx_errors = record->data.rx_errors;
+		next->data.tx_errors = record->data.tx_errors;
+		next->data.rx_dropped = record->data.rx_dropped;
+		next->data.tx_dropped = record->data.tx_dropped;
+		next->data.time = record->data.time;
+
+		if (next->roaming)
+			set_roaming(&temp_file, next);
+		else
+			set_home(&temp_file, next);
+
+		set_end(&temp_file, next);
+
+		record = get_next_record32(&data_iter);
+	}
+
+	// close and swap
+	munmap(data_file->addr, data_file->len);
+	close(data_file->fd);
+	err = rename(temp_file.name, data_file->name);
+	if (err < 0) {
+		fprintf(stderr, "failed to rename %s to %s\n", temp_file.name, data_file->name);
+		return -1;
+	}
+	g_free(temp_file.name);
+	temp_file.name = data_file->name;
+
+	memcpy(data_file, &temp_file, sizeof(struct stats_file));
+
+	return 0;
+}
+
 int main(int argc, char *argv[])
 {
 	GOptionContext *context;
@@ -869,6 +1125,20 @@ int main(int argc, char *argv[])
 		exit(1);
 	}
 
+	hdr = get_hdr(data_file);
+	if (hdr->magic != MAGIC64) {
+		if (option_convert) {
+			err = stats_convert(data_file);
+			if (err < 0) {
+				fprintf(stderr, "failed to convert data file %s\n", argv[1]);
+				exit(1);
+			}
+		} else {
+			fprintf(stderr, "data file %s in old format, needs converting\n", argv[1]);
+			exit(1);
+		}
+	}
+
 	if (option_last_file_name) {
 		struct stats_file last;
 		if (stats_open(&last, option_last_file_name) < 0) {
@@ -877,6 +1147,12 @@ int main(int argc, char *argv[])
 			exit(1);
 		}
 
+		hdr = get_hdr(&last);
+		if (hdr->magic != MAGIC64) {
+			fprintf(stderr, "last file %s in old format, needs converting\n",
+			option_last_file_name);
+			exit(1);
+		}
 		rec = get_end(&last);
 	}
 
@@ -890,7 +1166,7 @@ int main(int argc, char *argv[])
 				start_ts, rec);
 
 	hdr = get_hdr(data_file);
-	if (hdr->magic != MAGIC) {
+	if (hdr->magic != MAGIC64) {
 		fprintf(stderr, "header file magic test failed\n");
 		goto err;
 	}
diff --git a/vpn/vpn-ipconfig.c b/vpn/vpn-ipconfig.c
index c3e6145..791dee3 100644
--- a/vpn/vpn-ipconfig.c
+++ b/vpn/vpn-ipconfig.c
@@ -362,7 +362,7 @@ void __vpn_ipconfig_newlink(int index, unsigned short type,
 				unsigned int flags,
 				const char *address,
 				unsigned short mtu,
-				struct rtnl_link_stats *stats)
+				struct rtnl_link_stats64 *stats)
 {
 	struct vpn_ipdevice *ipdevice;
 	GString *str;
@@ -418,7 +418,7 @@ update:
 	g_string_free(str, TRUE);
 }
 
-void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats *stats)
+void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats64 *stats)
 {
 	struct vpn_ipdevice *ipdevice;
 
diff --git a/vpn/vpn-rtnl.c b/vpn/vpn-rtnl.c
index af24674..4a441ee 100644
--- a/vpn/vpn-rtnl.c
+++ b/vpn/vpn-rtnl.c
@@ -227,7 +227,7 @@ static const char *operstate2str(unsigned char operstate)
 static void extract_link(struct ifinfomsg *msg, int bytes,
 				struct ether_addr *address, const char **ifname,
 				unsigned int *mtu, unsigned char *operstate,
-						struct rtnl_link_stats *stats)
+						struct rtnl_link_stats64 *stats)
 {
 	struct rtattr *attr;
 
@@ -246,10 +246,10 @@ static void extract_link(struct ifinfomsg *msg, int bytes,
 			if (mtu)
 				*mtu = *((unsigned int *) RTA_DATA(attr));
 			break;
-		case IFLA_STATS:
+		case IFLA_STATS64:
 			if (stats)
 				memcpy(stats, RTA_DATA(attr),
-					sizeof(struct rtnl_link_stats));
+					sizeof(struct rtnl_link_stats64));
 			break;
 		case IFLA_OPERSTATE:
 			if (operstate)
@@ -266,7 +266,7 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
 {
 	struct ether_addr address = {{ 0, 0, 0, 0, 0, 0 }};
 	struct ether_addr compare = {{ 0, 0, 0, 0, 0, 0 }};
-	struct rtnl_link_stats stats;
+	struct rtnl_link_stats64 stats;
 	unsigned char operstate = 0xff;
 	struct interface_data *interface;
 	const char *ifname = NULL;
@@ -343,7 +343,7 @@ static void process_newlink(unsigned short type, int index, unsigned flags,
 static void process_dellink(unsigned short type, int index, unsigned flags,
 			unsigned change, struct ifinfomsg *msg, int bytes)
 {
-	struct rtnl_link_stats stats;
+	struct rtnl_link_stats64 stats;
 	unsigned char operstate = 0xff;
 	const char *ifname = NULL;
 	GSList *list;
@@ -612,8 +612,8 @@ static void rtnl_link(struct nlmsghdr *hdr)
 		case IFLA_QDISC:
 			print_attr(attr, "qdisc");
 			break;
-		case IFLA_STATS:
-			print_attr(attr, "stats");
+		case IFLA_STATS64:
+			print_attr(attr, "stats64");
 			break;
 		case IFLA_COST:
 			print_attr(attr, "cost");
diff --git a/vpn/vpn.h b/vpn/vpn.h
index 1398d05..743fd32 100644
--- a/vpn/vpn.h
+++ b/vpn/vpn.h
@@ -60,13 +60,13 @@ void __vpn_ipconfig_unref_debug(struct vpn_ipconfig *ipconfig,
 struct vpn_ipconfig *__vpn_ipconfig_create(int index, int family);
 void __vpn_ipconfig_set_index(struct vpn_ipconfig *ipconfig,
 								int index);
-struct rtnl_link_stats;
+struct rtnl_link_stats64;
 
 void __vpn_ipconfig_newlink(int index, unsigned short type,
 				unsigned int flags, const char *address,
 				unsigned short mtu,
-				struct rtnl_link_stats *stats);
-void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats *stats);
+				struct rtnl_link_stats64 *stats);
+void __vpn_ipconfig_dellink(int index, struct rtnl_link_stats64 *stats);
 int __vpn_ipconfig_init(void);
 void __vpn_ipconfig_cleanup(void);
 
-- 
1.9.0

_______________________________________________
connman mailing list
[email protected]
https://lists.connman.net/mailman/listinfo/connman

Reply via email to