5 * Copyright (C) 2010 BMW Car IT GmbH. All rights reserved.
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
28 #include <sys/types.h>
37 #define MAGIC 0xFA00B915
40 * Statistics counters are stored into a ring buffer which is stored
44 * The ring buffer is mmap to a file
45 * Initialy only the smallest possible amount of disk space is allocated
46 * The files grow to the configured maximal size
47 * The grows by _SC_PAGESIZE step size
48 * For each service home and service roaming counter set a file is created
49 * Each file has a header where the indexes are stored
52 * The entries are fixed sized (stats_record)
53 * Each entry has a timestamp
55 * Ring buffer properties:
56 * There are to indexes 'begin' and 'end'
57 * 'begin' points to the oldest entry
58 * 'end' points to the newest/current entry
59 * If 'begin' == 'end' then the buffer is empty
60 * If 'end' + 1 == 'begin then it's full
61 * The ring buffer is valid in the range (begin, end]
62 * 'first' points to the first entry in the ring buffer
63 * 'last' points to the last entry in the ring buffer
66 struct stats_file_header {
74 struct connman_stats_data data;
85 struct stats_record *first;
86 struct stats_record *last;
90 struct stats_file home;
91 struct stats_file roaming;
94 GHashTable *stats_hash = NULL;
96 static struct stats_file_header *get_hdr(struct stats_file *file)
98 return (struct stats_file_header *)file->addr;
101 static struct stats_record *get_begin(struct stats_file *file)
103 unsigned int off = get_hdr(file)->begin;
105 return (struct stats_record *)(file->addr + off);
108 static struct stats_record *get_end(struct stats_file *file)
110 unsigned int off = get_hdr(file)->end;
112 return (struct stats_record *)(file->addr + off);
115 static void set_begin(struct stats_file *file, struct stats_record *begin)
117 struct stats_file_header *hdr;
120 hdr->begin = (char *)begin - file->addr;
123 static void set_end(struct stats_file *file, struct stats_record *end)
125 struct stats_file_header *hdr;
128 hdr->end = (char *)end - file->addr;
131 static struct stats_record *get_next(struct stats_file *file,
132 struct stats_record *cur)
136 if (cur > file->last)
142 static struct stats_file *stats_file_get(struct stats_handle *handle,
143 connman_bool_t roaming)
145 struct stats_file *file;
147 if (roaming == FALSE)
148 file = &handle->home;
150 file = &handle->roaming;
155 static void stats_close_file(struct stats_file *file)
157 msync(file->addr, file->len, MS_SYNC);
159 munmap(file->addr, file->len);
168 static void stats_free(gpointer user_data)
170 struct stats_handle *handle = user_data;
172 stats_close_file(&handle->home);
173 stats_close_file(&handle->roaming);
178 static int stats_create(struct connman_service *service)
180 struct stats_handle *handle;
182 handle = g_try_new0(struct stats_handle, 1);
186 g_hash_table_insert(stats_hash, service, handle);
191 static void update_first(struct stats_file *file)
193 file->first = (struct stats_record *)
194 (file->addr + sizeof(struct stats_file_header));
197 static void update_last(struct stats_file *file)
199 unsigned int max_entries;
201 max_entries = (file->len - sizeof(struct stats_file_header)) /
202 sizeof(struct stats_record);
203 file->last = file->first + max_entries - 1;
206 static void stats_file_update_cache(struct stats_file *file)
212 static int stats_file_remap(struct stats_file *file, size_t size)
214 size_t page_size, new_size;
218 page_size = sysconf(_SC_PAGESIZE);
219 new_size = (size + page_size - 1) & ~(page_size - 1);
221 err = ftruncate(file->fd, new_size);
223 connman_error("ftrunctate error %s for %s",
224 strerror(errno), file->name);
228 if (file->addr == NULL) {
229 addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE,
230 MAP_SHARED, file->fd, 0);
232 addr = mremap(file->addr, file->len, new_size, MREMAP_MAYMOVE);
235 if (addr == MAP_FAILED) {
236 connman_error("mmap error %s for %s",
237 strerror(errno), file->name);
242 file->len = new_size;
244 stats_file_update_cache(file);
249 static int stats_open_file(struct connman_service *service,
250 struct stats_file *file,
251 connman_bool_t roaming)
257 struct stats_file_header *hdr;
259 if (roaming == FALSE) {
260 name = g_strdup_printf("%s/%s.data", STORAGEDIR,
261 __connman_service_get_ident(service));
263 name = g_strdup_printf("%s/%s-roaming.data", STORAGEDIR,
264 __connman_service_get_ident(service));
268 file->fd = open(name, O_RDWR | O_CREAT, 0644);
270 if (file->fd == -1) {
271 connman_error("open error %s for %s",
272 strerror(errno), file->name);
276 file->max_len = STATS_MAX_FILE_SIZE;
278 err = fstat(file->fd, &stat);
282 if (stat.st_size < sysconf(_SC_PAGESIZE))
283 size = sysconf(_SC_PAGESIZE);
285 size = (size_t)stat.st_size;
287 err = stats_file_remap(file, size);
293 if (hdr->magic != MAGIC ||
294 hdr->begin < sizeof(struct stats_file_header) ||
295 hdr->end < sizeof(struct stats_file_header) ||
296 hdr->begin > file->len ||
297 hdr->end > file->len) {
298 connman_warn("invalid file header for %s", file->name);
301 hdr->begin = sizeof(struct stats_file_header);
302 hdr->end = sizeof(struct stats_file_header);
308 static int stats_open(struct connman_service *service,
309 struct stats_handle *handle)
313 err = stats_open_file(service, &handle->home, FALSE);
317 err = stats_open_file(service, &handle->roaming, TRUE);
324 int __connman_stats_service_register(struct connman_service *service)
326 struct stats_handle *handle;
329 DBG("service %p", service);
331 handle = g_hash_table_lookup(stats_hash, service);
332 if (handle == NULL) {
333 err = stats_create(service);
338 handle = g_hash_table_lookup(stats_hash, service);
341 err = stats_open(service, handle);
343 g_hash_table_remove(stats_hash, service);
348 void __connman_stats_service_unregister(struct connman_service *service)
350 DBG("service %p", service);
352 g_hash_table_remove(stats_hash, service);
355 int __connman_stats_update(struct connman_service *service,
356 connman_bool_t roaming,
357 struct connman_stats_data *data)
359 struct stats_handle *handle;
360 struct stats_file *file;
361 struct stats_record *next;
364 handle = g_hash_table_lookup(stats_hash, service);
368 file = stats_file_get(handle, roaming);
370 if (file->len < file->max_len &&
371 file->last == get_end(file)) {
372 DBG("grow file %s", file->name);
374 err = stats_file_remap(file, file->len + sysconf(_SC_PAGESIZE));
379 next = get_next(file, get_end(file));
381 if (next == get_begin(file))
382 set_begin(file, get_next(file, next));
384 next->ts = time(NULL);
385 memcpy(&next->data, data, sizeof(struct connman_stats_data));
392 int __connman_stats_get(struct connman_service *service,
393 connman_bool_t roaming,
394 struct connman_stats_data *data)
396 struct stats_handle *handle;
397 struct stats_file *file;
398 struct stats_file_header *hdr;
400 handle = g_hash_table_lookup(stats_hash, service);
404 file = stats_file_get(handle, roaming);
407 if (hdr->begin != hdr->end) {
408 memcpy(data, &get_end(file)->data,
409 sizeof(struct connman_stats_data));
415 int __connman_stats_init(void)
419 stats_hash = g_hash_table_new_full(g_direct_hash, g_direct_equal,
425 void __connman_stats_cleanup(void)
429 g_hash_table_destroy(stats_hash);