Imported Upstream version 1.24
[platform/upstream/connman.git] / tools / stats-tool.c
1 /*
2  *
3  *  Connection Manager
4  *
5  *  Copyright (C) 2010  BMW Car IT GmbH. All rights reserved.
6  *
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.
10  *
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.
15  *
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
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #define _GNU_SOURCE
26 #include <sys/mman.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/time.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <string.h>
33
34 #include <sys/time.h>
35 #include <time.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <stdbool.h>
39 #include <errno.h>
40
41 #include <glib.h>
42 #include <glib/gstdio.h>
43
44 #ifdef TEMP_FAILURE_RETRY
45 #define TFR TEMP_FAILURE_RETRY
46 #else
47 #define TFR
48 #endif
49
50 #define MAGIC 0xFA00B916
51
52 struct connman_stats_data {
53         unsigned int rx_packets;
54         unsigned int tx_packets;
55         unsigned int rx_bytes;
56         unsigned int tx_bytes;
57         unsigned int rx_errors;
58         unsigned int tx_errors;
59         unsigned int rx_dropped;
60         unsigned int tx_dropped;
61         unsigned int time;
62 };
63
64 struct stats_file_header {
65         unsigned int magic;
66         unsigned int begin;
67         unsigned int end;
68         unsigned int home;
69         unsigned int roaming;
70 };
71
72 struct stats_record {
73         time_t ts;
74         unsigned int roaming;
75         struct connman_stats_data data;
76 };
77
78 struct stats_file {
79         int fd;
80         char *name;
81         char *addr;
82         size_t len;
83         size_t max_len;
84
85         /* cached values */
86         int max_nr;
87         int nr;
88         struct stats_record *first;
89         struct stats_record *last;
90         struct stats_record *home_first;
91         struct stats_record *roaming_first;
92 };
93
94 struct stats_iter {
95         struct stats_file *file;
96         struct stats_record *begin;
97         struct stats_record *end;
98         struct stats_record *it;
99 };
100
101 static gint option_create = 0;
102 static gint option_interval = 3;
103 static bool option_dump = false;
104 static bool option_summary = false;
105 static char *option_info_file_name = NULL;
106 static time_t option_start_ts = -1;
107 static char *option_last_file_name = NULL;
108
109 static bool parse_start_ts(const char *key, const char *value,
110                                         gpointer user_data, GError **error)
111 {
112         GTimeVal time_val;
113
114         if (!g_time_val_from_iso8601(value, &time_val))
115                 return false;
116
117         option_start_ts = time_val.tv_sec;
118
119         return true;
120 }
121
122 static GOptionEntry options[] = {
123         { "create", 'c', 0, G_OPTION_ARG_INT, &option_create,
124                         "Create a .data file with NR faked entries", "NR" },
125         { "interval", 'i', 0, G_OPTION_ARG_INT, &option_interval,
126                         "Interval in seconds (used with create)", "INTERVAL" },
127         { "dump", 'd', 0, G_OPTION_ARG_NONE, &option_dump,
128                         "Dump contents of .data file" },
129         { "summary", 's', 0, G_OPTION_ARG_NONE, &option_summary,
130                         "Summary of .data file" },
131         { "info", 'f', 0, G_OPTION_ARG_FILENAME, &option_info_file_name,
132                         ".info file name" },
133         { "startts", 't', 0, G_OPTION_ARG_CALLBACK, parse_start_ts,
134                         "Set start time for creating .data file "
135                         "(example 2010-11-05T23:00:12Z)", "TS"},
136         { "last", 'l', 0, G_OPTION_ARG_FILENAME, &option_last_file_name,
137                           "Start values from last .data file" },
138         { NULL },
139 };
140
141 static struct stats_file_header *get_hdr(struct stats_file *file)
142 {
143         return (struct stats_file_header *)file->addr;
144 }
145
146 static struct stats_record *get_begin(struct stats_file *file)
147 {
148         unsigned int off = get_hdr(file)->begin;
149
150         return (struct stats_record *)(file->addr + off);
151 }
152
153 static struct stats_record *get_end(struct stats_file *file)
154 {
155         unsigned int off = get_hdr(file)->end;
156
157         return (struct stats_record *)(file->addr + off);
158 }
159
160 static struct stats_record *get_home(struct stats_file *file)
161 {
162         struct stats_file_header *hdr;
163
164         hdr = get_hdr(file);
165
166         if (hdr->home == UINT_MAX)
167                 return NULL;
168
169         return (struct stats_record *)(file->addr + hdr->home);
170 }
171
172 static struct stats_record *get_roaming(struct stats_file *file)
173 {
174         struct stats_file_header *hdr;
175
176         hdr = get_hdr(file);
177
178         if (hdr->roaming == UINT_MAX)
179                 return NULL;
180
181         return (struct stats_record *)(file->addr + hdr->roaming);
182 }
183
184 static void set_end(struct stats_file *file, struct stats_record *end)
185 {
186         struct stats_file_header *hdr;
187
188         hdr = get_hdr(file);
189         hdr->end = (char *)end - file->addr;
190 }
191
192 static int get_index(struct stats_file *file, struct stats_record *rec)
193 {
194         return rec - file->first;
195 }
196
197 static struct stats_record *get_next(struct stats_file *file,
198                                         struct stats_record *cur)
199 {
200         cur++;
201
202         if (cur > file->last)
203                 cur = file->first;
204
205         return cur;
206 }
207
208 static struct stats_record *get_iterator_begin(struct stats_file *file)
209 {
210         return get_next(file, get_begin(file));
211 }
212
213 static struct stats_record *get_iterator_end(struct stats_file *file)
214 {
215         return get_next(file, get_end(file));
216 }
217
218 static void stats_print_record(struct stats_record *rec)
219 {
220         char buffer[30];
221
222         strftime(buffer, 30, "%d-%m-%Y %T", localtime(&rec->ts));
223         printf("%p %lld %s %01d %d %d %d %d %d %d %d %d %d\n",
224                 rec, (long long int)rec->ts, buffer,
225                 rec->roaming,
226                 rec->data.rx_packets,
227                 rec->data.tx_packets,
228                 rec->data.rx_bytes,
229                 rec->data.tx_bytes,
230                 rec->data.rx_errors,
231                 rec->data.tx_errors,
232                 rec->data.rx_dropped,
233                 rec->data.tx_dropped,
234                 rec->data.time);
235 }
236
237 static void stats_hdr_info(struct stats_file *file)
238 {
239         struct stats_file_header *hdr;
240         struct stats_record *begin, *end, *home, *roaming;
241         unsigned int home_idx, roaming_idx;
242
243         hdr = get_hdr(file);
244         begin = get_begin(file);
245         end = get_end(file);
246
247         home = get_home(file);
248         if (!home)
249                 home_idx = UINT_MAX;
250         else
251                 home_idx = get_index(file, home);
252
253         roaming = get_roaming(file);
254         if (!roaming)
255                 roaming_idx = UINT_MAX;
256         else
257                 roaming_idx = get_index(file, roaming);
258
259         printf("Data Structure Sizes\n");
260         printf("  sizeof header   %zd/0x%02zx\n",
261                 sizeof(struct stats_file_header),
262                 sizeof(struct stats_file_header));
263         printf("  sizeof entry    %zd/0%02zx\n\n",
264                 sizeof(struct stats_record),
265                 sizeof(struct stats_record));
266
267         printf("File\n");
268         printf("  addr            %p\n",  file->addr);
269         printf("  len             %zd\n", file->len);
270
271         printf("  max nr entries  %d\n", file->max_nr);
272         printf("  nr entries      %d\n\n", file->nr);
273
274         printf("Header\n");
275         printf("  magic           0x%08x\n", hdr->magic);
276         printf("  begin           [%d] 0x%08x\n",
277                 get_index(file, begin), hdr->begin);
278         printf("  end             [%d] 0x%08x\n",
279                 get_index(file, end), hdr->end);
280         printf("  home            [%d] 0x%08x\n",
281                 home_idx, hdr->home);
282         printf("  roaming         [%d] 0x%08x\n\n",
283                 roaming_idx, hdr->roaming);
284
285
286         printf("Pointers\n");
287         printf("  hdr             %p\n", hdr);
288         printf("  begin           %p\n", begin);
289         printf("  end             %p\n", end);
290         printf("  home            %p\n", home);
291         printf("  romaing         %p\n", roaming);
292         printf("  first           %p\n", file->first);
293         printf("  last            %p\n\n", file->last);
294 }
295
296 static void stats_print_entries(struct stats_file *file)
297 {
298         struct stats_record *it;
299         int i;
300
301         printf("[ idx] ptr ts ts rx_packets tx_packets rx_bytes "
302                 "tx_bytes rx_errors tx_errors rx_dropped tx_dropped time\n\n");
303
304         for (i = 0, it = file->first; it <= file->last; it++, i++) {
305                 printf("[%04d] ", i);
306                 stats_print_record(it);
307         }
308 }
309
310 static void stats_print_rec_diff(struct stats_record *begin,
311                                         struct stats_record *end)
312 {
313         printf("\trx_packets: %d\n",
314                 end->data.rx_packets - begin->data.rx_packets);
315         printf("\ttx_packets: %d\n",
316                 end->data.tx_packets - begin->data.tx_packets);
317         printf("\trx_bytes:   %d\n",
318                 end->data.rx_bytes - begin->data.rx_bytes);
319         printf("\ttx_bytes:   %d\n",
320                 end->data.tx_bytes - begin->data.tx_bytes);
321         printf("\trx_errors:  %d\n",
322                 end->data.rx_errors - begin->data.rx_errors);
323         printf("\ttx_errors:  %d\n",
324                 end->data.tx_errors - begin->data.tx_errors);
325         printf("\trx_dropped: %d\n",
326                 end->data.rx_dropped - begin->data.rx_dropped);
327         printf("\ttx_dropped: %d\n",
328                 end->data.tx_dropped - begin->data.tx_dropped);
329         printf("\ttime:       %d\n",
330                 end->data.time - begin->data.time);
331 }
332
333 static void stats_print_diff(struct stats_file *file)
334 {
335         struct stats_record *begin, *end;
336
337         begin = get_begin(file);
338         begin = get_next(file, begin);
339         end = get_end(file);
340
341         printf("\n(begin + 1)\n");
342         printf("\t[%04d] ", get_index(file, begin));
343         stats_print_record(begin);
344         printf("end\n");
345         printf("\t[%04d] ", get_index(file, end));
346         stats_print_record(end);
347
348         if (file->home_first && get_home(file)) {
349                 printf("\nhome\n");
350                 stats_print_rec_diff(file->home_first, get_home(file));
351         }
352
353         if (file->roaming_first && get_roaming(file)) {
354                 printf("\roaming\n");
355                 stats_print_rec_diff(file->roaming_first, get_roaming(file));
356         }
357 }
358
359 static void update_max_nr_entries(struct stats_file *file)
360 {
361         file->max_nr = (file->len - sizeof(struct stats_file_header)) /
362                 sizeof(struct stats_record);
363 }
364
365 static void update_nr_entries(struct stats_file *file)
366 {
367         struct stats_record *begin, *end;
368         int nr;
369
370         begin = get_begin(file);
371         end = get_end(file);
372
373         nr = get_index(file, end) - get_index(file, begin);
374
375         if (nr < 0)
376                 nr += file->max_nr;
377
378         file->nr = nr;
379 }
380
381 static void update_first(struct stats_file *file)
382 {
383         file->first = (struct stats_record *)(file->addr +
384                                         sizeof(struct stats_file_header));
385 }
386
387 static void update_last(struct stats_file *file)
388 {
389         struct stats_record *last;
390
391         last = file->first;
392         last += file->max_nr - 1;
393
394         file->last = last;
395 }
396
397 static int stats_file_update_cache(struct stats_file *file)
398 {
399         struct stats_record *it, *end;
400
401         update_max_nr_entries(file);
402         update_nr_entries(file);
403         update_first(file);
404         update_last(file);
405         file->home_first = NULL;
406         file->roaming_first = NULL;
407
408         end = get_iterator_end(file);
409         for (it = get_iterator_begin(file);
410                         it != end;
411                         it = get_next(file, it)) {
412
413                 if (!file->home_first && it->roaming == 0)
414                         file->home_first = it;
415
416                 if (!file->roaming_first && it->roaming == 1)
417                         file->roaming_first = it;
418
419                 if (file->home_first && file->roaming_first)
420                         break;
421         }
422
423         return 0;
424 }
425
426 static int stats_file_remap(struct stats_file *file, size_t size)
427 {
428         size_t page_size, new_size;
429         void *addr;
430         int err;
431
432         page_size = sysconf(_SC_PAGESIZE);
433         new_size = (size + page_size - 1) & ~(page_size - 1);
434
435         err = ftruncate(file->fd, new_size);
436         if (err < 0) {
437                 fprintf(stderr, "ftrunctate error %s for %s",
438                                 strerror(errno), file->name);
439                 return -errno;
440         }
441
442         if (!file->addr) {
443                 addr = mmap(NULL, new_size, PROT_READ | PROT_WRITE,
444                                 MAP_SHARED, file->fd, 0);
445         } else {
446                 addr = mremap(file->addr, file->len, new_size, MREMAP_MAYMOVE);
447         }
448
449         if (addr == MAP_FAILED) {
450                 fprintf(stderr, "mmap error %s for %s\n",
451                         strerror(errno), file->name);
452                 return -errno;
453         }
454
455         file->addr = addr;
456         file->len = new_size;
457         file->max_len = new_size;
458
459         return 0;
460 }
461
462 static int stats_open(struct stats_file *file, const char *name)
463 {
464         struct stats_file_header *hdr;
465         struct stat tm;
466         int err;
467         size_t size = 0;
468
469         bzero(file, sizeof(struct stats_file));
470
471         if (name) {
472                 file->name = g_strdup(name);
473
474                 file->fd = TFR(open(file->name,
475                                         O_RDWR | O_CREAT | O_CLOEXEC, 0644));
476                 if (file->fd == -1) {
477                         fprintf(stderr, "open error %s for %s\n",
478                                 strerror(errno), file->name);
479                         return -errno;
480                 }
481
482                 err = fstat(file->fd, &tm);
483                 if (err < 0) {
484                         fprintf(stderr, "fstat error %s for %s\n",
485                                 strerror(errno), file->name);
486                         return err;
487                 }
488
489                 size = (size_t)tm.st_size;
490         } else {
491                 file->name = g_strdup("stats.XXXXXX.tmp");
492                 file->fd = g_mkstemp_full(file->name, O_RDWR | O_CREAT, 0644);
493                 if (file->fd == -1) {
494                         fprintf(stderr, "creating tmp failed\n");
495                         return -1;
496                 }
497         }
498
499         if (size == 0)
500                 size = sysconf(_SC_PAGESIZE);
501
502         err = stats_file_remap(file, size);
503         if (err < 0) {
504                 fprintf(stderr, "remap failed\n");
505                 return err;
506         }
507
508         /* Initialize new file */
509         hdr = get_hdr(file);
510         if (hdr->magic != MAGIC ||
511                         hdr->begin < sizeof(struct stats_file_header) ||
512                         hdr->end < sizeof(struct stats_file_header) ||
513                         hdr->home < sizeof(struct stats_file_header) ||
514                         hdr->roaming < sizeof(struct stats_file_header) ||
515                         hdr->begin > file->len ||
516                         hdr->end > file->len) {
517                 hdr->magic = MAGIC;
518                 hdr->begin = sizeof(struct stats_file_header);
519                 hdr->end = sizeof(struct stats_file_header);
520                 hdr->home = UINT_MAX;
521                 hdr->roaming = UINT_MAX;
522
523         }
524         stats_file_update_cache(file);
525
526         return 0;
527 }
528
529 static void stats_close(struct stats_file *file)
530 {
531         munmap(file->addr, file->len);
532         close(file->fd);
533         g_free(file->name);
534 }
535
536 static int stats_create(struct stats_file *file, unsigned int nr,
537                         unsigned int interval, time_t start_ts,
538                         struct stats_record *start)
539 {
540         unsigned int i;
541         int err;
542         struct stats_record *cur, *next;
543         struct stats_file_header *hdr;
544         unsigned int pkt;
545         unsigned int step_ts;
546         unsigned int roaming = FALSE;
547
548         hdr = get_hdr(file);
549
550         hdr->magic = MAGIC;
551         hdr->begin = sizeof(struct stats_file_header);
552         hdr->end = sizeof(struct stats_file_header);
553         hdr->home = UINT_MAX;
554         hdr->roaming = UINT_MAX;
555
556         stats_file_update_cache(file);
557
558         if (start) {
559                 struct stats_record *rec;
560
561                 rec = get_end(file);
562                 memcpy(rec, start, sizeof(struct stats_record));
563         } else {
564                 get_end(file)->ts = start_ts;
565         }
566
567         for (i = 0; i < nr; i++) {
568                 if (file->last == get_end(file)) {
569                         err = stats_file_remap(file, file->len +
570                                                 sysconf(_SC_PAGESIZE));
571                         if (err < 0)
572                                 return err;
573
574                         stats_file_update_cache(file);
575                 }
576                 cur = get_end(file);
577                 next = get_next(file, cur);
578
579                 step_ts = (rand() % interval);
580                 if (step_ts == 0)
581                         step_ts = 1;
582
583                 next->ts = cur->ts + step_ts;
584                 next->roaming = roaming;
585                 next->data.time = cur->data.time + step_ts;
586
587                 next->data.rx_packets = cur->data.rx_packets;
588                 next->data.rx_bytes = cur->data.rx_bytes;
589
590                 if (rand() % 3 == 0) {
591                         pkt = rand() % 5;
592                         next->data.rx_packets += pkt;
593                         next->data.rx_bytes += pkt * (rand() % 1500);
594                 }
595
596                 next->data.tx_packets = cur->data.tx_packets;
597                 next->data.tx_bytes = cur->data.tx_bytes;
598
599                 if (rand() % 3 == 0) {
600                         pkt = rand() % 5;
601                         next->data.tx_packets += pkt;
602                         next->data.tx_bytes += pkt * (rand() % 1500);
603                 }
604
605                 set_end(file, next);
606
607                 if ((rand() % 50) == 0)
608                         roaming = roaming ? FALSE : TRUE;
609
610         }
611
612         return 0;
613 }
614
615 static struct stats_record *get_next_record(struct stats_iter *iter)
616 {
617         if (iter->it != iter->end) {
618                 struct stats_record *tmp;
619
620                 tmp = iter->it;
621                 iter->it = get_next(iter->file, iter->it);
622
623                 return tmp;
624         }
625
626         return NULL;
627 }
628
629 static int append_record(struct stats_file *file,
630                                 struct stats_record *rec)
631 {
632         struct stats_record *cur, *next;
633         int err;
634
635         if (file->last == get_end(file)) {
636                 err = stats_file_remap(file, file->len +
637                                         sysconf(_SC_PAGESIZE));
638                 if (err < 0)
639                         return err;
640
641                 stats_file_update_cache(file);
642         }
643
644         cur = get_end(file);
645         next = get_next(file, cur);
646
647         memcpy(next, rec, sizeof(struct stats_record));
648
649         set_end(file, next);
650
651         return 0;
652 }
653
654 static struct stats_record *process_file(struct stats_iter *iter,
655                                         struct stats_file *temp_file,
656                                         struct stats_record *cur,
657                                         GDate *date_change_step_size,
658                                         int account_period_offset)
659 {
660         struct stats_record *home, *roaming;
661         struct stats_record *next;
662
663         home = NULL;
664         roaming = NULL;
665
666         if (!cur)
667                 cur = get_next_record(iter);
668         next = get_next_record(iter);
669
670         while (next) {
671                 GDate date_cur;
672                 GDate date_next;
673                 int append;
674
675                 append = FALSE;
676
677                 if (cur->roaming)
678                         roaming = cur;
679                 else
680                         home = cur;
681
682                 g_date_set_time_t(&date_cur, cur->ts);
683                 g_date_set_time_t(&date_next, next->ts);
684
685                 if (g_date_compare(&date_cur, date_change_step_size) < 0) {
686                         /* month period size */
687                         GDateDay day_cur, day_next;
688                         GDateMonth month_cur, month_next;
689
690                         month_cur = g_date_get_month(&date_cur);
691                         month_next = g_date_get_month(&date_next);
692
693                         day_cur = g_date_get_day(&date_cur);
694                         day_next = g_date_get_day(&date_next);
695
696                         if (day_cur == day_next && month_cur != month_next)
697                                 append = TRUE;
698                         else if (day_cur < account_period_offset
699                                         && day_next >= account_period_offset)
700                                 append = TRUE;
701                 } else {
702                         /* day period size */
703                         if (g_date_days_between(&date_cur, &date_next) > 0)
704                                 append = TRUE;
705                 }
706
707                 if (append) {
708                         if (home) {
709                                 append_record(temp_file, home);
710                                 home = NULL;
711                         }
712
713                         if (roaming) {
714                                 append_record(temp_file, roaming);
715                                 roaming = NULL;
716                         }
717                 }
718
719                 cur = next;
720                 next = get_next_record(iter);
721         }
722
723         return cur;
724 }
725
726 static int summarize(struct stats_file *data_file,
727                         struct stats_file *history_file,
728                         struct stats_file *temp_file,
729                         int account_period_offset)
730 {
731         struct stats_iter data_iter;
732         struct stats_iter history_iter;
733         struct stats_record *cur, *next;
734
735         GDate today, date_change_step_size;
736
737         /*
738          * First calculate the date when switch from monthly
739          * accounting period size to daily size
740          */
741         g_date_set_time_t(&today, time(NULL));
742
743         date_change_step_size = today;
744         if (g_date_get_day(&today) - account_period_offset >= 0)
745                 g_date_subtract_months(&date_change_step_size, 2);
746         else
747                 g_date_subtract_months(&date_change_step_size, 3);
748
749         g_date_set_day(&date_change_step_size, account_period_offset);
750
751
752         /* Now process history file */
753         cur = NULL;
754
755         if (history_file) {
756                 history_iter.file = history_file;
757                 history_iter.begin = get_iterator_begin(history_iter.file);
758                 history_iter.end = get_iterator_end(history_iter.file);
759                 history_iter.it = history_iter.begin;
760
761                 cur = process_file(&history_iter, temp_file, NULL,
762                                         &date_change_step_size,
763                                         account_period_offset);
764         }
765
766         data_iter.file = data_file;
767         data_iter.begin = get_iterator_begin(data_iter.file);
768         data_iter.end = get_iterator_end(data_iter.file);
769         data_iter.it = data_iter.begin;
770
771         /*
772          * Ensure date_file records are newer than the history_file
773          * record
774          */
775         if (cur) {
776                 next = get_next_record(&data_iter);
777                 while (next && cur->ts > next->ts)
778                         next = get_next_record(&data_iter);
779         }
780
781         /* And finally process the new data records */
782         cur = process_file(&data_iter, temp_file, cur,
783                                 &date_change_step_size, account_period_offset);
784
785         if (cur)
786                 append_record(temp_file, cur);
787
788         return 0;
789 }
790
791 static void swap_and_close_files(struct stats_file *history_file,
792                         struct stats_file *temp_file)
793 {
794         munmap(history_file->addr, history_file->len);
795         munmap(temp_file->addr, temp_file->len);
796
797         TFR(close(temp_file->fd));
798
799         unlink(history_file->name);
800
801         if (link(temp_file->name, history_file->name) < 0)
802                 return;
803
804         unlink(temp_file->name);
805         TFR(close(history_file->fd));
806 }
807
808 static void history_file_update(struct stats_file *data_file,
809                                 const char *history_file_name)
810 {
811         struct stats_file _history_file;
812         struct stats_file tempory_file;
813
814         struct stats_file *history_file = NULL;
815
816         if (stats_open(&_history_file, history_file_name) == 0)
817                 history_file = &_history_file;
818
819         if (stats_open(&tempory_file, NULL) < 0) {
820                 if (history_file)
821                         stats_close(history_file);
822                 return;
823         }
824
825         summarize(data_file, history_file, &tempory_file, 13);
826
827         swap_and_close_files(history_file, &tempory_file);
828 }
829
830 int main(int argc, char *argv[])
831 {
832         GOptionContext *context;
833         GError *error = NULL;
834
835         struct stats_file_header *hdr;
836         struct stats_file data, *data_file;
837         struct stats_record *rec;
838         time_t start_ts;
839         int err;
840
841         rec = NULL;
842
843         data_file = &data;
844
845         putenv("TZ=GMT0");
846
847         context = g_option_context_new(NULL);
848         g_option_context_add_main_entries(context, options, NULL);
849
850         if (!g_option_context_parse(context, &argc, &argv, &error)) {
851                 if (error) {
852                         g_printerr("%s\n", error->message);
853                         g_error_free(error);
854                 } else
855                         g_printerr("An unknown error occurred\n");
856                 exit(1);
857         }
858
859         g_option_context_free(context);
860
861         if (argc < 2) {
862                 printf("Usage: %s [FILENAME]\n", argv[0]);
863                 exit(0);
864         }
865
866         err = stats_open(data_file, argv[1]);
867         if (err < 0) {
868                 fprintf(stderr, "failed open file %s\n", argv[1]);
869                 exit(1);
870         }
871
872         if (option_last_file_name) {
873                 struct stats_file last;
874                 if (stats_open(&last, option_last_file_name) < 0) {
875                         fprintf(stderr, "failed open file %s\n",
876                                 option_last_file_name);
877                         exit(1);
878                 }
879
880                 rec = get_end(&last);
881         }
882
883         if (option_start_ts == -1)
884                 start_ts = time(NULL);
885         else
886                 start_ts = option_start_ts;
887
888         if (option_create > 0)
889                 stats_create(data_file, option_create, option_interval,
890                                 start_ts, rec);
891
892         hdr = get_hdr(data_file);
893         if (hdr->magic != MAGIC) {
894                 fprintf(stderr, "header file magic test failed\n");
895                 goto err;
896         }
897
898         stats_file_update_cache(data_file);
899
900         stats_hdr_info(data_file);
901
902         if (option_dump)
903                 stats_print_entries(data_file);
904
905         if (option_summary)
906                 stats_print_diff(data_file);
907
908         if (option_info_file_name)
909                 history_file_update(data_file, option_info_file_name);
910
911 err:
912         stats_close(data_file);
913
914         return 0;
915 }