From: Daniel Wagner <[email protected]>
Currently the statistic information is
stored into the profile file. This
results in rewriting the whole file
if connann_stats_save is called. This
results in too many disk I/Os for low
power devices.
Furthermore, only the current value is
stored. There is no way to find out
how many bytes have been transfered
in the last month.
First, each service statistic is
stored into a separate files under
/var/lib/connman having a *.data
extension. This file contains fixed sized
records of stats counters and will be mmap
into memory. It is used like a ring buffer.
If the buffer is full or after a certain
period (e.g. a month), the raw data
will be summerized into the *.info file.
---
Still messy but it gets better. I've learned a lot about mmap and
ftruncate. Funny stuff.
collect.c is just a debug program but I think I'm going to clean it
up and add it later to the test dir. Seems to be useful.
have a nice weekdend
Makefile.am | 2 +-
bootstrap-configure | 6 +-
configure.ac | 11 ++
src/collect.c | 202 ++++++++++++++++++++++++++
src/connman.h | 24 +++
src/main.c | 2 +
src/service.c | 131 +++---------------
src/stats.c | 389 +++++++++++++++++++++++++++++++++++++++++++++++++++
8 files changed, 649 insertions(+), 118 deletions(-)
create mode 100644 src/collect.c
create mode 100644 src/stats.c
diff --git a/Makefile.am b/Makefile.am
index 1bf7207..6a6fe6d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -63,7 +63,7 @@ src_connmand_SOURCES = $(gdbus_sources) $(gdhcp_sources)
$(gresolv_sources) \
src/wifi.c src/storage.c src/dbus.c src/config.c \
src/technology.c src/counter.c src/location.c \
src/session.c src/tethering.c src/ondemand.c \
- src/wpad.c
+ src/wpad.c src/stats.c
if UDEV
src_connmand_SOURCES += src/udev.c
diff --git a/bootstrap-configure b/bootstrap-configure
index 1045688..9b5237e 100755
--- a/bootstrap-configure
+++ b/bootstrap-configure
@@ -21,15 +21,13 @@ fi
--enable-wifi=builtin \
--enable-bluetooth=builtin \
--enable-ofono=builtin \
- --enable-dhclient=builtin \
--enable-openconnect=builtin \
--enable-dnsproxy=builtin \
--enable-google=builtin \
--enable-meego=builtin \
--enable-portal=builtin \
- --enable-iwmx \
--enable-iospm \
- --enable-polkit=builtin \
--enable-capng \
--enable-client \
- --enable-tools $*
+ --enable-tools \
+ --disable-optimization $*
diff --git a/configure.ac b/configure.ac
index 6923aa1..17a66c7 100644
--- a/configure.ac
+++ b/configure.ac
@@ -258,6 +258,17 @@ fi
AM_CONDITIONAL(NTPD, test "${enable_ntpd}" != "no")
AM_CONDITIONAL(NTPD_BUILTIN, test "${enable_ntpd}" = "builtin")
+AC_ARG_WITH(stats-max-file-size,
AC_HELP_STRING([--with-stats-max-file-size=SIZE],
+ [Maximal size of a statistics round robin
file]),
+ [stats_max_file_size=${withval}])
+
+if (test -z "${stats_max_file_size}"); then
+ # default size is 512 kByte
+ stats_max_file_size="512 * 8 * 128"
+fi
+
+AC_DEFINE_UNQUOTED([STATS_MAX_FILE_SIZE], (${stats_max_file_size}), [Maximal
size of a statistics round robin file])
+
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.16, dummy=yes,
AC_MSG_ERROR(GLib >= 2.16 is required))
AC_SUBST(GLIB_CFLAGS)
diff --git a/src/collect.c b/src/collect.c
new file mode 100644
index 0000000..9d818b1
--- /dev/null
+++ b/src/collect.c
@@ -0,0 +1,202 @@
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#define MAGIC 0xFA00B905
+
+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;
+ unsigned int time;
+};
+
+struct stats_file_header {
+ unsigned int magic;
+ unsigned int first;
+ unsigned int last;
+};
+
+struct stats_record {
+ struct timeval tv;
+ struct connman_stats_data data;
+};
+
+struct stats_file {
+ int fd;
+ char *addr;
+ size_t len;
+ size_t max_len;
+};
+
+static struct stats_file_header *get_hdr(struct stats_file *file)
+{
+ return (struct stats_file_header *)file->addr;
+}
+
+static struct stats_record *get_first(struct stats_file *file)
+{
+ return (struct stats_record *)(file->addr + get_hdr(file)->first);
+}
+
+static struct stats_record *get_last(struct stats_file *file)
+{
+ return (struct stats_record *)(file->addr + get_hdr(file)->last);
+}
+
+static struct stats_record *rec_inc_wrap(struct stats_file *file,
+ struct stats_record *cur)
+{
+ char *next, *next_end;
+
+ next = (char *)(cur + 1);
+ next_end = (char *)(cur + 2);
+
+ if (next_end >= file->addr + file->max_len)
+ next = file->addr + sizeof(struct stats_file_header);
+
+ return (struct stats_record *)next;
+}
+
+
+static int stats_collect(struct stats_file *file)
+{
+ struct stats_record *it, *first, *last;
+ struct stats_file_header *hdr;
+ int i;
+ unsigned int nr;
+
+ /* assume the file is valid */
+ hdr = get_hdr(file);
+ first = get_first(file);
+ last = get_last(file);
+ nr = (file->len - sizeof(struct stats_file_header)) / sizeof(struct
stats_record);
+
+ printf("hdr: %p\n", hdr);
+ printf("first: %p\n", first);
+ printf("last: %p\n", last);
+ printf("hdr magic: 0x%08x\n", hdr->magic);
+ printf("hdr first: 0x%08x\n", hdr->first);
+ printf("hdr last: 0x%08x\n", hdr->last);
+ printf("file addr: %p\n", file->addr);
+ printf("file len: %ld\n", file->len);
+ printf("file max_len: %ld\n", file->max_len);
+ printf("max nr entries: %d\n\n", nr);;
+
+ printf("[idx ptr] ts rx_packets tx_packets rx_bytes tx_bytes rx_errors
tx_errors rx_dropped tx_dropped time\n");
+
+ i = 0;
+ it = first;
+ while (it != last) {
+ char buffer[30];
+ time_t curtime;
+
+ if (i > nr)
+ break;
+
+ it = rec_inc_wrap(file, it);
+
+ curtime=it->tv.tv_sec;
+
+ strftime(buffer,30,"%d-%m-%Y %T", localtime(&curtime));
+ printf("[%04d %p] %s %d %d %d %d %d %d %d %d %d\n", i, it,
buffer,
+ it->data.rx_packets,
+ it->data.tx_packets,
+ it->data.rx_bytes,
+ it->data.tx_bytes,
+ it->data.rx_errors,
+ it->data.tx_errors,
+ it->data.rx_dropped,
+ it->data.tx_dropped,
+ it->data.time);
+
+ i++;
+ }
+
+ return 0;
+}
+
+static int stats_file_remap(struct stats_file *file, size_t size)
+{
+ size_t ps, ns;
+ void *np;
+ int err;
+
+ ps = sysconf(_SC_PAGESIZE);
+ ns = (size + ps - 1) & ~(ps - 1);
+
+ if (size < ns) {
+ err = ftruncate(file->fd, ns);
+ if (err < 0)
+ return -errno;
+ }
+
+ if (file->addr == NULL) {
+ np = mmap(NULL, ns, PROT_READ, MAP_SHARED, file->fd, 0);
+ } else {
+ np = mremap(file->addr, file->len, ns, MREMAP_MAYMOVE);
+ }
+
+ if (np == MAP_FAILED)
+ return -errno;
+
+ file->addr = np;
+ file->len = ns;
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct stats_file _file, *file;
+ const char *filename;
+ struct stat stat;
+ int err;
+
+ if (argc < 1) {
+ printf("%s FILENAME\n", argv[0]);
+ exit(0);
+ }
+
+ filename = argv[1];
+
+ file = &_file;
+
+ bzero(file, sizeof(struct stats_file));
+
+ file->fd = open(filename, O_RDONLY, 0644);
+ if (file->fd == -1)
+ return -errno;
+
+ err = fstat(file->fd, &stat);
+ if (err < 0)
+ return -errno;
+
+ file->max_len = stat.st_size;
+ err = stats_file_remap(file, stat.st_size);
+ if (err < 0)
+ return err;
+
+ stats_collect(file);
+
+ munmap(file->addr, file->len);
+ close(file->fd);
+
+ return 0;
+}
diff --git a/src/connman.h b/src/connman.h
index 7c723ba..af0e73e 100644
--- a/src/connman.h
+++ b/src/connman.h
@@ -455,6 +455,7 @@ void __connman_service_create_ipconfig(struct
connman_service *service,
int index);
struct connman_ipconfig *__connman_service_get_ipconfig(
struct connman_service *service);
+const char *__connman_service_get_ident(struct connman_service *service);
const char *__connman_service_get_path(struct connman_service *service);
unsigned int __connman_service_get_order(struct connman_service *service);
struct connman_network *__connman_service_get_network(struct connman_service
*service);
@@ -561,3 +562,26 @@ void __connman_session_cleanup(void);
int __connman_ondemand_init(void);
void __connman_ondemand_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;
+ unsigned int time;
+};
+
+int __connman_stats_init(void);
+void __connman_stats_cleanup(void);
+int __connman_stats_service_register(struct connman_service *service);
+void __connman_stats_service_unregister(struct connman_service *service);
+int __connman_stats_update(struct connman_service *service,
+ connman_bool_t roaming,
+ struct connman_stats_data *data);
+int __connman_stats_get(struct connman_service *service,
+ connman_bool_t roaming,
+ struct connman_stats_data *data);
diff --git a/src/main.c b/src/main.c
index aa63225..28e72ba 100644
--- a/src/main.c
+++ b/src/main.c
@@ -225,6 +225,7 @@ int main(int argc, char *argv[])
__connman_manager_init(option_compat);
__connman_profile_init();
__connman_config_init();
+ __connman_stats_init();
__connman_resolver_init();
__connman_ipconfig_init();
@@ -262,6 +263,7 @@ int main(int argc, char *argv[])
__connman_ipconfig_cleanup();
__connman_resolver_cleanup();
+ __connman_stats_cleanup();
__connman_config_cleanup();
__connman_profile_cleanup();
__connman_manager_cleanup();
diff --git a/src/service.c b/src/service.c
index 1af2e08..64d97d1 100644
--- a/src/service.c
+++ b/src/service.c
@@ -37,18 +37,6 @@ static GSequence *service_list = NULL;
static GHashTable *service_hash = NULL;
static GSList *counter_list = NULL;
-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;
- unsigned int time;
-};
-
struct connman_stats {
connman_bool_t valid;
connman_bool_t enabled;
@@ -486,104 +474,6 @@ static void stats_stop(struct connman_service *service)
stats->enabled = FALSE;
}
-static int stats_load(struct connman_service *service, GKeyFile *keyfile)
-{
- struct connman_stats_data *data;
-
- /* home */
- data = &service->stats.data;
- data->rx_packets = g_key_file_get_integer(keyfile,
- service->identifier, "Home.rx_packets", NULL);
- data->tx_packets = g_key_file_get_integer(keyfile,
- service->identifier, "Home.tx_packets", NULL);
- data->rx_bytes = g_key_file_get_integer(keyfile,
- service->identifier, "Home.rx_bytes", NULL);
- data->tx_bytes = g_key_file_get_integer(keyfile,
- service->identifier, "Home.tx_bytes", NULL);
- data->rx_errors = g_key_file_get_integer(keyfile,
- service->identifier, "Home.rx_errors", NULL);
- data->tx_errors = g_key_file_get_integer(keyfile,
- service->identifier, "Home.tx_errors", NULL);
- data->rx_dropped = g_key_file_get_integer(keyfile,
- service->identifier, "Home.rx_dropped", NULL);
- data->tx_dropped = g_key_file_get_integer(keyfile,
- service->identifier, "Home.tx_dropped", NULL);
- data->time = g_key_file_get_integer(keyfile,
- service->identifier, "Home.time", NULL);
-
- /* roaming */
- data = &service->stats_roaming.data;
- data->rx_packets = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.rx_packets", NULL);
- data->tx_packets = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.tx_packets", NULL);
- data->rx_bytes = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.rx_bytes", NULL);
- data->tx_bytes = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.tx_bytes", NULL);
- data->rx_errors = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.rx_errors", NULL);
- data->tx_errors = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.tx_errors", NULL);
- data->rx_dropped = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.rx_dropped", NULL);
- data->tx_dropped = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.tx_dropped", NULL);
- data->time = g_key_file_get_integer(keyfile,
- service->identifier, "Roaming.time", NULL);
-
- return 0;
-}
-
-static int stats_save(struct connman_service *service, GKeyFile *keyfile)
-{
- struct connman_stats_data *data;
-
- /* home */
- data = &service->stats.data;
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.rx_packets", data->rx_packets);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.tx_packets", data->tx_packets);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.rx_bytes", data->rx_bytes);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.tx_bytes", data->tx_bytes);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.rx_errors", data->rx_errors);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.tx_errors", data->tx_errors);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.rx_dropped", data->rx_dropped);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.tx_dropped", data->tx_dropped);
- g_key_file_set_integer(keyfile, service->identifier,
- "Home.time", data->time);
-
- /* roaming */
- data = &service->stats_roaming.data;
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.rx_packets", data->rx_packets);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.tx_packets", data->tx_packets);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.rx_bytes", data->rx_bytes);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.tx_bytes", data->tx_bytes);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.rx_errors", data->rx_errors);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.tx_errors", data->tx_errors);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.rx_dropped", data->rx_dropped);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.tx_dropped", data->tx_dropped);
- g_key_file_set_integer(keyfile, service->identifier,
- "Roaming.time", data->time);
-
- return 0;
-}
-
static void reset_stats(struct connman_service *service)
{
DBG("service %p", service);
@@ -1186,6 +1076,8 @@ void __connman_service_notify(struct connman_ipconfig
*ipconfig,
gpointer key, value;
const char *counter;
struct connman_stats_counter *counters;
+ struct connman_stats_data *data;
+ int err;
service = connman_ipconfig_get_data(ipconfig);
if (service == NULL)
@@ -1200,6 +1092,12 @@ void __connman_service_notify(struct connman_ipconfig
*ipconfig,
rx_errors, tx_errors,
rx_dropped, tx_dropped);
+ data = &stats_get(service)->data;
+ err = __connman_stats_update(service, service->roaming, data);
+ if (err < 0)
+ connman_error("Failed to store statistics for %s",
+ service->identifier);
+
g_hash_table_iter_init(&iter, service->counter_table);
while (g_hash_table_iter_next(&iter, &key, &value)) {
counter = key;
@@ -2267,6 +2165,7 @@ static void service_free(gpointer user_data)
stats_stop(service);
__connman_storage_save_service(service);
+ __connman_stats_service_unregister(service);
service->path = NULL;
@@ -3293,6 +3192,10 @@ static int service_register(struct connman_service
*service)
__connman_storage_load_service(service);
+ __connman_stats_service_register(service);
+ __connman_stats_get(service, FALSE, &service->stats.data);
+ __connman_stats_get(service, TRUE, &service->stats_roaming.data);
+
g_dbus_register_interface(connection, service->path,
CONNMAN_SERVICE_INTERFACE,
service_methods, service_signals,
@@ -3464,6 +3367,11 @@ struct connman_service
*__connman_service_lookup_from_index(int index)
return NULL;
}
+const char *__connman_service_get_ident(struct connman_service *service)
+{
+ return service->identifier;
+}
+
const char *__connman_service_get_path(struct connman_service *service)
{
return service->path;
@@ -4025,7 +3933,6 @@ static int service_load(struct connman_service *service)
service->domains = NULL;
}
- stats_load(service, keyfile);
done:
g_key_file_free(keyfile);
@@ -4187,8 +4094,6 @@ update:
g_key_file_remove_key(keyfile, service->identifier,
"Domains", NULL);
- stats_save(service, keyfile);
-
data = g_key_file_to_data(keyfile, &length, NULL);
if (g_file_set_contents(pathname, data, length, NULL) == FALSE)
diff --git a/src/stats.c b/src/stats.c
new file mode 100644
index 0000000..a9c349e
--- /dev/null
+++ b/src/stats.c
@@ -0,0 +1,389 @@
+/*
+ *
+ * Connection Manager
+ *
+ * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <sys/mman.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "connman.h"
+
+#define MAGIC 0xFA00B915
+
+struct stats_file_header {
+ unsigned int magic;
+ unsigned int first;
+ unsigned int last;
+};
+
+struct stats_record {
+ struct timeval tv;
+ struct connman_stats_data data;
+};
+
+struct stats_file {
+ int fd;
+ char *addr;
+ size_t len;
+ size_t max_len;
+};
+
+struct stats_handle {
+ struct stats_file home;
+ struct stats_file roaming;
+};
+
+GHashTable *stats_hash = NULL;
+
+static struct stats_file_header *get_hdr(struct stats_file *file)
+{
+ return (struct stats_file_header *)file->addr;
+}
+
+static struct stats_record *get_first(struct stats_file *file)
+{
+ return (struct stats_record *)(file->addr + get_hdr(file)->first);
+}
+
+static struct stats_record *get_last(struct stats_file *file)
+{
+ return (struct stats_record *)(file->addr + get_hdr(file)->last);
+}
+
+static unsigned int inc_off(size_t len, unsigned int off)
+{
+ off += sizeof(struct stats_record);
+
+ if (off + sizeof(struct stats_record) >= len)
+ off = sizeof(struct stats_file_header);
+
+ return off;
+}
+
+static void inc_first(struct stats_file *file)
+{
+ struct stats_file_header *hdr;
+
+ hdr = get_hdr(file);
+ hdr->first = inc_off(file->len, hdr->first);
+}
+
+static void inc_last(struct stats_file *file)
+{
+ struct stats_file_header *hdr;
+
+ hdr = get_hdr(file);
+ hdr->last = inc_off(file->len, hdr->last);
+}
+
+static struct stats_record *rec_inc_wrap(struct stats_file *file,
+ struct stats_record *cur)
+{
+ char *next, *next_end;
+
+ next = (char *)(cur + 1);
+ next_end = (char *)(cur + 2);
+
+ if (next_end >= file->addr + file->max_len)
+ next = file->addr + sizeof(struct stats_file_header);
+
+ return (struct stats_record *)next;
+}
+
+static struct stats_file *stats_file_get(struct stats_handle *handle,
+ connman_bool_t roaming)
+{
+ struct stats_file *file;
+
+ if (roaming == FALSE)
+ file = &handle->home;
+ else
+ file = &handle->roaming;
+
+ return file;
+}
+
+static void stats_close_file(struct stats_file *file)
+{
+ msync(file->addr, file->len, MS_SYNC);
+
+ munmap(file->addr, file->len);
+ file->addr = NULL;
+
+ close(file->fd);
+ file->fd = -1;
+}
+
+static void stats_free(gpointer user_data)
+{
+ struct stats_handle *handle = user_data;
+
+ stats_close_file(&handle->home);
+ stats_close_file(&handle->roaming);
+ g_free(handle);
+}
+
+static int stats_create(struct connman_service *service)
+{
+ struct stats_handle *handle;
+
+ handle = g_try_new0(struct stats_handle, 1);
+ if (handle == NULL)
+ return -ENOMEM;
+
+ g_hash_table_insert(stats_hash, service, handle);
+
+ return 0;
+}
+
+static int stats_file_remap(struct stats_file *file, size_t size)
+{
+ size_t ps, ns;
+ void *np;
+ int err;
+
+ ps = sysconf(_SC_PAGESIZE);
+ ns = (size + ps - 1) & ~(ps - 1);
+
+ err = ftruncate(file->fd, ns);
+ if (err < 0)
+ return -errno;
+
+ if (file->addr == NULL) {
+ np = mmap(NULL, ns, PROT_READ | PROT_WRITE, MAP_SHARED,
+ file->fd, 0);
+ } else {
+ np = mremap(file->addr, file->len, ns, MREMAP_MAYMOVE);
+ }
+
+ if (np == MAP_FAILED)
+ return -errno;
+
+ file->addr = np;
+ file->len = ns;
+
+ return 0;
+}
+
+static int stats_open_file(struct connman_service *service,
+ struct stats_file *file,
+ connman_bool_t roaming)
+{
+ struct stat stat;
+ gchar *filename;
+ int err;
+ size_t size;
+ struct stats_file_header *hdr;
+
+ if (roaming == FALSE) {
+ filename = g_strdup_printf("%s/%s.data", STORAGEDIR,
+ __connman_service_get_ident(service));
+ } else {
+ filename = g_strdup_printf("%s/%s-roaming.data", STORAGEDIR,
+ __connman_service_get_ident(service));
+ }
+
+ file->fd = open(filename, O_RDWR | O_CREAT, 0644);
+
+ g_free(filename);
+
+ if (file->fd == -1)
+ return -errno;
+
+ file->max_len = STATS_MAX_FILE_SIZE;
+
+ err = fstat(file->fd, &stat);
+ if (err < 0)
+ return -errno;
+
+ size = (size_t)stat.st_size > sizeof(struct stats_file_header)?
+ (size_t)stat.st_size : sizeof(struct stats_file_header);
+ err = stats_file_remap(file, size);
+ if (err < 0)
+ return err;
+
+ hdr = get_hdr(file);
+
+ if (hdr->magic != MAGIC ||
+ hdr->first < sizeof(struct stats_file_header) ||
+ hdr->last < sizeof(struct stats_file_header) ||
+ hdr->first > file->len ||
+ hdr->last > file->len) {
+ /* invalid header, reset */
+
+ DBG("invalid file");
+ hdr->magic = MAGIC;
+ hdr->first = sizeof(struct stats_file_header);
+ hdr->last = sizeof(struct stats_file_header);
+ }
+
+ return 0;
+}
+
+static int stats_open(struct connman_service *service,
+ struct stats_handle *handle)
+{
+ int err;
+
+ err = stats_open_file(service, &handle->home, FALSE);
+ if (err < 0)
+ return err;
+
+ err = stats_open_file(service, &handle->roaming, TRUE);
+ if (err < 0)
+ return err;
+
+ return 0;
+}
+
+int __connman_stats_service_register(struct connman_service *service)
+{
+ struct stats_handle *handle;
+ int err;
+
+ DBG("service %p", service);
+
+ handle = g_hash_table_lookup(stats_hash, service);
+ if (handle == NULL) {
+ err = stats_create(service);
+
+ if (err < 0)
+ return err;
+
+ handle = g_hash_table_lookup(stats_hash, service);
+ }
+
+ err = stats_open(service, handle);
+ if (err < 0)
+ g_hash_table_remove(stats_hash, service);
+
+ return err;
+}
+
+void __connman_stats_service_unregister(struct connman_service *service)
+{
+ DBG("service %p", service);
+
+ g_hash_table_remove(stats_hash, service);
+}
+
+static connman_bool_t should_grow(struct stats_file *file)
+{
+ struct stats_record *last;
+
+ if (file->len < file->max_len) {
+ last = get_last(file);
+ last += 2; /* advance forward + space */
+
+ if ((char *)last >= file->addr + file->len)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+int __connman_stats_update(struct connman_service *service,
+ connman_bool_t roaming,
+ struct connman_stats_data *data)
+{
+ struct stats_handle *handle;
+ struct stats_file *file;
+ struct stats_record *last, *next;
+ int err;
+
+ handle = g_hash_table_lookup(stats_hash, service);
+ if (handle == NULL)
+ return -EEXIST;
+
+ file = stats_file_get(handle, roaming);
+
+ if (should_grow(file) == TRUE) {
+ err = stats_file_remap(file, file->len + sysconf(_SC_PAGESIZE));
+ if (err < 0)
+ return err;
+ }
+
+ last = get_last(file);
+ next = rec_inc_wrap(file, last);
+
+ if (next == get_first(file))
+ inc_first(file);
+
+ err = gettimeofday(&next->tv, NULL);
+ if (err < 0)
+ return -errno;
+
+ memcpy(&next->data, data, sizeof(struct connman_stats_data));
+
+ inc_last(file);
+
+ return 0;
+}
+
+int __connman_stats_get(struct connman_service *service,
+ connman_bool_t roaming,
+ struct connman_stats_data *data)
+{
+ struct stats_handle *handle;
+ struct stats_file *file;
+ struct stats_file_header *hdr;
+
+ handle = g_hash_table_lookup(stats_hash, service);
+ if (handle == NULL)
+ return -EEXIST;
+
+ file = stats_file_get(handle, roaming);
+ hdr = get_hdr(file);
+
+ if (hdr->first == hdr->last)
+ bzero(data, sizeof(struct connman_stats_data));
+ else
+ memcpy(data, &get_last(file)->data,
+ sizeof(struct connman_stats_data));
+
+ return 0;
+}
+
+int __connman_stats_init(void)
+{
+ DBG("");
+
+ stats_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
+ NULL, stats_free);
+
+ return 0;
+}
+
+void __connman_stats_cleanup(void)
+{
+ DBG("");
+
+ g_hash_table_destroy(stats_hash);
+ stats_hash = NULL;
+}
--
1.7.2.2
_______________________________________________
connman mailing list
[email protected]
http://lists.connman.net/listinfo/connman