static int stats_file_setup(struct stats_file *file) { struct stats_file_header *hdr; struct stat st; size_t size = 0; int err; DBG("file %p fd %d name %s", file, file->fd, file->name); err = fstat(file->fd, &st); if (err < 0) { connman_error("fstat error %s for %s\n", strerror(errno), file->name); TFR(close(file->fd)); g_free(file->name); file->name = NULL; return -errno; } size = (size_t)st.st_size; file->max_len = STATS_MAX_FILE_SIZE; if (size < (size_t)sysconf(_SC_PAGESIZE)) size = sysconf(_SC_PAGESIZE); err = stats_file_remap(file, size); if (err < 0) { TFR(close(file->fd)); g_free(file->name); file->name = NULL; return err; } hdr = get_hdr(file); if (hdr->magic != MAGIC || 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->begin = sizeof(struct stats_file_header); hdr->end = sizeof(struct stats_file_header); hdr->home = UINT_MAX; hdr->roaming = UINT_MAX; stats_file_update_cache(file); } return 0; }
static int stats_file_remap(struct stats_file *file, size_t size) { size_t page_size, new_size; void *addr; int err; DBG("file %p size %zu addr %p len %zu", file, size, file->addr, file->len); page_size = sysconf(_SC_PAGESIZE); new_size = (size + page_size - 1) & ~(page_size - 1); err = ftruncate(file->fd, new_size); if (err < 0) { connman_error("ftrunctate error %s for %s", strerror(errno), file->name); return -errno; } if (!file->addr) { /* * Though the buffer is not shared between processes, we still * have to take MAP_SHARED because MAP_PRIVATE does not * guarantee that writes will hit the file without an explicit * call to munmap or msync. For more details please read the * mmap man pages. */ addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, file->fd, 0); } else { addr = mremap(file->addr, file->len, new_size, MREMAP_MAYMOVE); } if (addr == MAP_FAILED) { connman_error("mmap error %s for %s", strerror(errno), file->name); if (errno == EINVAL) { connman_error("%s might be on a file system, such as " "JFFS2, that does not allow shared " "writable mappings.", file->name); } return -errno; } file->addr = addr; file->len = new_size; if (get_hdr(file)->magic == MAGIC32) stats_file_update_cache32(file); else stats_file_update_cache(file); return 0; }
static int append_record(struct stats_file *file, struct stats_record *rec) { struct stats_record *cur, *next; int err; if (file->last == get_end(file)) { err = stats_file_remap(file, file->len + sysconf(_SC_PAGESIZE)); if (err < 0) return err; stats_file_update_cache(file); } cur = get_end(file); next = get_next(file, cur); memcpy(next, rec, sizeof(struct stats_record)); set_end(file, next); return 0; }
int main(int argc, char *argv[]) { GOptionContext *context; GError *error = NULL; struct stats_file_header *hdr; struct stats_file data, *data_file; struct stats_record *rec; time_t start_ts; int err; rec = NULL; data_file = &data; putenv("TZ=GMT0"); context = g_option_context_new(NULL); g_option_context_add_main_entries(context, options, NULL); if (!g_option_context_parse(context, &argc, &argv, &error)) { if (error) { g_printerr("%s\n", error->message); g_error_free(error); } else g_printerr("An unknown error occurred\n"); exit(1); } g_option_context_free(context); if (argc < 2) { printf("Usage: %s [FILENAME]\n", argv[0]); exit(0); } err = stats_open(data_file, argv[1]); if (err < 0) { fprintf(stderr, "failed open file %s\n", argv[1]); exit(1); } if (option_last_file_name) { struct stats_file last; if (stats_open(&last, option_last_file_name) < 0) { fprintf(stderr, "failed open file %s\n", option_last_file_name); exit(1); } rec = get_end(&last); } if (option_start_ts == -1) start_ts = time(NULL); else start_ts = option_start_ts; if (option_create > 0) stats_create(data_file, option_create, option_interval, start_ts, rec); hdr = get_hdr(data_file); if (hdr->magic != MAGIC) { fprintf(stderr, "header file magic test failed\n"); goto err; } stats_file_update_cache(data_file); stats_hdr_info(data_file); if (option_dump) stats_print_entries(data_file); if (option_summary) stats_print_diff(data_file); if (option_info_file_name) history_file_update(data_file, option_info_file_name); err: stats_close(data_file); return 0; }
static int stats_create(struct stats_file *file, unsigned int nr, unsigned int interval, time_t start_ts, struct stats_record *start) { unsigned int i; int err; struct stats_record *cur, *next; struct stats_file_header *hdr; unsigned int pkt; unsigned int step_ts; unsigned int roaming = FALSE; hdr = get_hdr(file); hdr->magic = MAGIC; hdr->begin = sizeof(struct stats_file_header); hdr->end = sizeof(struct stats_file_header); hdr->home = UINT_MAX; hdr->roaming = UINT_MAX; stats_file_update_cache(file); if (start) { struct stats_record *rec; rec = get_end(file); memcpy(rec, start, sizeof(struct stats_record)); } else { get_end(file)->ts = start_ts; } for (i = 0; i < nr; i++) { if (file->last == get_end(file)) { err = stats_file_remap(file, file->len + sysconf(_SC_PAGESIZE)); if (err < 0) return err; stats_file_update_cache(file); } cur = get_end(file); next = get_next(file, cur); step_ts = (rand() % interval); if (step_ts == 0) step_ts = 1; next->ts = cur->ts + step_ts; next->roaming = roaming; next->data.time = cur->data.time + step_ts; next->data.rx_packets = cur->data.rx_packets; next->data.rx_bytes = cur->data.rx_bytes; if (rand() % 3 == 0) { pkt = rand() % 5; next->data.rx_packets += pkt; next->data.rx_bytes += pkt * (rand() % 1500); } next->data.tx_packets = cur->data.tx_packets; next->data.tx_bytes = cur->data.tx_bytes; if (rand() % 3 == 0) { pkt = rand() % 5; next->data.tx_packets += pkt; next->data.tx_bytes += pkt * (rand() % 1500); } set_end(file, next); if ((rand() % 50) == 0) roaming = roaming ? FALSE : TRUE; } return 0; }
static int stats_open(struct stats_file *file, const char *name) { struct stats_file_header *hdr; struct stat tm; int err; size_t size = 0; bzero(file, sizeof(struct stats_file)); if (name) { file->name = g_strdup(name); file->fd = TFR(open(file->name, O_RDWR | O_CREAT | O_CLOEXEC, 0644)); if (file->fd == -1) { fprintf(stderr, "open error %s for %s\n", strerror(errno), file->name); return -errno; } err = fstat(file->fd, &tm); if (err < 0) { fprintf(stderr, "fstat error %s for %s\n", strerror(errno), file->name); return err; } size = (size_t)tm.st_size; } else { file->name = g_strdup("stats.XXXXXX.tmp"); file->fd = g_mkstemp_full(file->name, O_RDWR | O_CREAT, 0644); if (file->fd == -1) { fprintf(stderr, "creating tmp failed\n"); return -1; } } if (size == 0) size = sysconf(_SC_PAGESIZE); err = stats_file_remap(file, size); if (err < 0) { fprintf(stderr, "remap failed\n"); return err; } /* Initialize new file */ hdr = get_hdr(file); if (hdr->magic != MAGIC || 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->begin = sizeof(struct stats_file_header); hdr->end = sizeof(struct stats_file_header); hdr->home = UINT_MAX; hdr->roaming = UINT_MAX; } stats_file_update_cache(file); return 0; }
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)); }
static int stats_file_setup(struct stats_file *file) { struct stats_file_header *hdr; struct stat st; size_t size = 0; int err; DBG("file %p fd %d name %s", file, file->fd, file->name); err = fstat(file->fd, &st); if (err < 0) { connman_error("fstat error %s for %s\n", strerror(errno), file->name); TFR(close(file->fd)); g_free(file->name); file->name = NULL; return -errno; } size = (size_t)st.st_size; file->max_len = STATS_MAX_FILE_SIZE; if (size < (size_t)sysconf(_SC_PAGESIZE)) size = sysconf(_SC_PAGESIZE); err = stats_file_remap(file, size); if (err < 0) { TFR(close(file->fd)); g_free(file->name); file->name = NULL; return err; } hdr = get_hdr(file); /* 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 = MAGIC64; hdr->begin = sizeof(struct stats_file_header); hdr->end = sizeof(struct stats_file_header); hdr->home = UINT_MAX; hdr->roaming = UINT_MAX; stats_file_update_cache(file); } return 0; }