tizen 2.3 release
[framework/system/dlog.git] / src / logger / logger.c
1 /*
2  * Copyright (c) 2005-2008, The Android Open Source Project
3  * Copyright (c) 2009-2013, Samsung Electronics Co., Ltd. All rights reserved.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 #define _GNU_SOURCE
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <stdbool.h>
22 #include <stdarg.h>
23 #include <string.h>
24 #include <unistd.h>
25 #include <limits.h>
26 #include <signal.h>
27 #include <fcntl.h>
28 #include <time.h>
29 #include <sys/time.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <assert.h>
33 #include <sys/stat.h>
34 #include <arpa/inet.h>
35
36 #include <logger.h>
37 #include <logprint.h>
38
39 #ifdef DEBUG_ON
40 #define _D(...) printf(__VA_ARGS__)
41 #else
42 #define _D(...) do { } while (0)
43 #endif
44 #define _E(...) fprintf(stderr, __VA_ARGS__)
45
46 #define COMMAND_MAX 5
47 #define DELIMITER " "
48 #define FILE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
49 #define MAX_ARGS 16
50 #define MAX_ROTATED 4
51 #define MAX_QUEUED 4096
52 #define BUFFER_MAX 100
53 #define INTERVAL_MAX 60*60
54
55 #define CONFIG_FILE "/opt/etc/dlog_logger.conf"
56
57 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
58
59 struct queued_entry {
60         union {
61                 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
62                 struct logger_entry entry __attribute__((aligned(4)));
63         };
64         struct queued_entry *next;
65 };
66
67 struct log_command {
68         char *filename;
69         int option_file;
70         int option_buffer;
71         int rotate_size;
72         int max_rotated;
73         int devices[LOG_ID_MAX];
74         log_format *format;
75 };
76
77 struct log_work {
78         char *filename;
79         bool printed;
80         int fd;
81         int size;
82         int rotate_size;
83         int max_rotated;
84         log_format *format;
85         struct log_work *next;
86 };
87
88 struct log_task_link {
89         struct log_work *work;
90         struct log_task_link *next;
91 };
92
93 struct log_device {
94         int id;
95         int fd;
96         struct queued_entry *queue;
97         struct log_task_link *task;
98         struct log_device *next;
99 };
100
101 static const char *device_path_table[] = {
102         [LOG_ID_MAIN] = "/dev/log_main",
103         [LOG_ID_RADIO] = "/dev/log_radio",
104         [LOG_ID_SYSTEM] = "/dev/log_system",
105         [LOG_ID_APPS] = "/dev/log_apps",
106         [LOG_ID_MAX] = NULL
107 };
108
109 static struct log_work *works;
110 static struct log_device *devices;
111 static int device_list[] = {
112         [LOG_ID_MAIN] = false,
113         [LOG_ID_RADIO] = false,
114         [LOG_ID_SYSTEM] = false,
115         [LOG_ID_APPS] = false,
116         [LOG_ID_MAX] = false,
117 };
118
119 static int buffer_size = 0;
120 static int min_interval = 0;
121
122 /*
123  * get log device id from device path table by device name
124  */
125 static int get_device_id_by_name(const char *name)
126 {
127         int i;
128
129         if (name == NULL)
130                 return -1;
131         for (i = 0; i < ARRAY_SIZE(device_path_table); i++) {
132                 if (strstr(device_path_table[i], name) != NULL)
133                         return i;
134         }
135
136         return -1;
137 }
138
139 /*
140  * check device registration on watch device list
141  */
142 static int check_device(int id)
143 {
144         if (id < 0 || LOG_ID_MAX <= id)
145                 return 0;
146
147         return (device_list[id] == true) ? 0 : -1;
148 }
149
150 /*
151  * register device to watch device list
152  */
153 static int register_device(int id)
154 {
155         if (id < 0 || LOG_ID_MAX <= id)
156                 return -1;
157         device_list[id] = true;
158
159         return 0;
160 }
161
162 /*
163  * comparison function to distinct entries by time
164  */
165 static int cmp(struct queued_entry *a, struct queued_entry *b)
166 {
167         int n = a->entry.sec - b->entry.sec;
168         if (n != 0)
169                 return n;
170
171         return a->entry.nsec - b->entry.nsec;
172 }
173
174 /*
175  * enqueueing the log_entry into the log_device
176  */
177 static void enqueue(struct log_device *device, struct queued_entry *entry)
178 {
179         if (device->queue == NULL) {
180                 device->queue = entry;
181         } else {
182                 struct queued_entry **e = &device->queue;
183                 while (*e && cmp(entry, *e) >= 0)
184                         e = &((*e)->next);
185                 entry->next = *e;
186                 *e = entry;
187         }
188 }
189
190 /*
191  * open file
192  */
193 static int open_work(const char *path)
194 {
195         return open(path, O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
196 }
197
198 /*
199  * rotate log files
200  */
201 static void rotate_logs(struct log_work *logwork)
202 {
203         int i, ret;
204         char *filename;
205         char file0[NAME_MAX];
206         char file1[NAME_MAX];
207
208         close(logwork->fd);
209         filename = logwork->filename;
210         for (i = logwork->max_rotated ; i > 0 ; i--) {
211                 snprintf(file1, NAME_MAX, "%s.%d", filename, i);
212                 if (i - 1 == 0)
213                         snprintf(file0, NAME_MAX, "%s",  filename);
214                 else
215                         snprintf(file0, NAME_MAX, "%s.%d", filename, i - 1);
216                 ret = rename(file0, file1);
217                 if (ret < 0 && errno != ENOENT)
218                         _E("while rotating log works");
219         }
220         /* open log file again */
221         logwork->fd = open_work(filename);
222         if (logwork->fd < 0) {
223                 _E("couldn't open log file");
224                 exit(EXIT_FAILURE);
225         }
226         logwork->size = 0;
227
228         return;
229 }
230
231 /*
232  * process to print log
233  * and check the log file size to rotate
234  */
235 static void process_buffer(struct log_device *dev, struct logger_entry *buf)
236 {
237         int bytes_written, err;
238         log_entry entry;
239         struct log_work *logwork;
240         struct log_task_link *task;
241
242         err = log_process_log_buffer(buf, &entry);
243         if (err < 0)
244                 goto exit;
245
246         for (task = dev->task; task; task = task->next) {
247                 logwork = task->work;
248                 if (log_should_print_line(logwork->format,
249                                         entry.tag, entry.priority)) {
250                         bytes_written =
251                                 log_print_log_line(logwork->format,
252                                                 logwork->fd, &entry);
253                         if (bytes_written < 0) {
254                                 _E("work error");
255                                 exit(EXIT_FAILURE);
256                         }
257                         logwork->size += bytes_written;
258                 }
259                 if (logwork->rotate_size > 0 &&
260                                 (logwork->size / 1024) >= logwork->rotate_size) {
261                         rotate_logs(logwork);
262                 }
263         }
264
265 exit:
266         return;
267 }
268
269 /*
270  * choose first device by log_entry
271  */
272 static void choose_first(struct log_device *dev, struct log_device **firstdev)
273 {
274         for (*firstdev = NULL; dev != NULL; dev = dev->next) {
275                 if (dev->queue != NULL &&
276                                 (*firstdev == NULL ||
277                                  cmp(dev->queue,
278                                          (*firstdev)->queue) < 0)) {
279                         *firstdev = dev;
280                 }
281         }
282 }
283
284 /*
285  * print beginnig string into the log files
286  */
287 static void maybe_print_start(struct log_device *dev)
288 {
289         struct log_work *logwork;
290         struct log_task_link *task;
291         char buf[1024];
292
293         for (task = dev->task; task; task = task->next) {
294                 logwork = task->work;
295                 if (!logwork->printed) {
296                         logwork->printed = true;
297                         snprintf(buf, sizeof(buf),
298                                         "--------- beginning of %s\n",
299                                         device_path_table[dev->id]);
300                         if (write(logwork->fd, buf, strlen(buf)) < 0) {
301                                 _E("maybe work error");
302                                 exit(EXIT_FAILURE);
303                         }
304                 }
305         }
306 }
307
308 /*
309  * skip log_entry
310  */
311 static void skip_next_entry(struct log_device *dev)
312 {
313         maybe_print_start(dev);
314         struct queued_entry *entry = dev->queue;
315         dev->queue = entry->next;
316         free(entry);
317 }
318
319 /*
320  * print log_entry
321  */
322 static void print_next_entry(struct log_device *dev)
323 {
324         maybe_print_start(dev);
325         process_buffer(dev, &dev->queue->entry);
326         skip_next_entry(dev);
327 }
328
329 /*
330  * do logging
331  */
332 static void do_logger(struct log_device *dev)
333 {
334         time_t commit_time = 0, current_time = 0;
335         struct log_device *pdev;
336         int ret, result;
337         fd_set readset;
338         bool sleep = false;
339         int queued_lines = 0;
340         int max = 0;
341
342         if (min_interval)
343                 commit_time = current_time = time(NULL);
344
345         for (pdev = dev; pdev; pdev = pdev->next) {
346                 if (pdev->fd > max)
347                         max = pdev->fd;
348         }
349
350         while (1) {
351                 do {
352                         struct timeval timeout = { 0, 5000 /* 5ms */ };
353                         FD_ZERO(&readset);
354                         for (pdev = dev; pdev; pdev = pdev->next)
355                                 FD_SET(pdev->fd, &readset);
356                         result = select(max + 1, &readset, NULL, NULL,
357                                         sleep ? NULL : &timeout);
358                 } while (result == -1 && errno == EINTR);
359
360                 if (result < 0)
361                         continue;
362
363                 for (pdev = dev; pdev; pdev = pdev->next) {
364                         if (FD_ISSET(pdev->fd, &readset)) {
365                                 struct queued_entry *entry =
366                                         (struct queued_entry *)
367                                         malloc(sizeof(struct queued_entry));
368                                 if (entry == NULL) {
369                                         _E("failed to malloc queued_entry\n");
370                                         goto exit;//exit(EXIT_FAILURE);
371                                 }
372                                 entry->next = NULL;
373                                 ret = read(pdev->fd, entry->buf,
374                                                 LOGGER_ENTRY_MAX_LEN);
375                                 if (ret < 0) {
376                                         if (errno == EINTR) {
377                                                 free(entry);
378                                                 goto next;
379                                         }
380                                         if (errno == EAGAIN) {
381                                                 free(entry);
382                                                 break;
383                                         }
384                                         _E("dlogutil read");
385                                         goto exit;//exit(EXIT_FAILURE);
386                                 } else if (!ret) {
387                                         free(entry);
388                                         _E("read: Unexpected EOF!\n");
389                                         exit(EXIT_FAILURE);
390                                 } else if (entry->entry.len !=
391                                                 ret - sizeof(struct logger_entry)) {
392                                         free(entry);
393                                         _E("unexpected length. Expected %d, got %d\n",
394                                                         entry->entry.len,
395                                                         ret - sizeof(struct logger_entry));
396                                         goto exit;//exit(EXIT_FAILURE);
397                                 }
398
399                                 entry->entry.msg[entry->entry.len] = '\0';
400
401                                 enqueue(pdev, entry);
402                                 ++queued_lines;
403
404                                 if (MAX_QUEUED < queued_lines) {
405                                         _D("queued_lines = %d\n", queued_lines);
406                                         while (true) {
407                                                 choose_first(dev, &pdev);
408                                                 if (pdev == NULL)
409                                                         break;
410                                                 print_next_entry(pdev);
411                                                 --queued_lines;
412                                         }
413                                         if (min_interval)
414                                                 commit_time = time(NULL);
415                                         break;
416                                 }
417                         }
418                 }
419
420                 if (min_interval) {
421                         current_time = time(NULL);
422                         if (current_time - commit_time < min_interval &&
423                                         queued_lines < buffer_size) {
424                                 sleep = true;
425                                 continue;
426                         }
427                 }
428
429                 if (result == 0) {
430                         sleep = true;
431                         while (true) {
432                                 choose_first(dev, &pdev);
433                                 if (pdev == NULL)
434                                         break;
435                                 print_next_entry(pdev);
436                                 --queued_lines;
437                                 if (min_interval)
438                                         commit_time = current_time;
439                         }
440                 } else {
441                         /* print all that aren't the last in their list */
442                         sleep = false;
443                         while (true) {
444                                 choose_first(dev, &pdev);
445                                 if (pdev == NULL || pdev->queue->next == NULL)
446                                         break;
447                                 print_next_entry(pdev);
448                                 --queued_lines;
449                                 if (min_interval)
450                                         commit_time = current_time;
451                         }
452                 }
453 next:
454                 ;
455         }
456 exit:
457         exit(EXIT_FAILURE);
458 }
459
460
461 /*
462  * create a work
463  */
464 static struct log_work *work_new(void)
465 {
466         struct log_work *work;
467
468         work = malloc(sizeof(struct log_work));
469         if (work == NULL) {
470                 _E("failed to malloc log_work\n");
471                 return NULL;
472         }
473         work->filename = NULL;
474         work->fd = -1;
475         work->printed = false;
476         work->size = 0;
477         work->next = NULL;
478         _D("work alloc %p\n", work);
479
480         return work;
481 }
482
483 /*
484  * add a new log_work to the tail of chain
485  */
486 static int work_add_to_tail(struct log_work *work, struct log_work *nwork)
487 {
488         struct log_work *tail = work;
489
490         if (!nwork)
491                 return -1;
492
493         if (work == NULL) {
494                 work = nwork;
495                 return 0;
496         }
497
498         while (tail->next)
499                 tail = tail->next;
500         tail->next = nwork;
501
502         return 0;
503 }
504
505 /*
506  * add a new work task to the tail of chain
507  */
508 static void work_add_to_device(struct log_device *dev, struct log_work *work)
509 {
510         struct log_task_link *tail;
511
512         if (!dev || !work)
513                 return;
514         _D("dev %p work %p\n", dev, work);
515         if (dev->task == NULL) {
516                 dev->task =
517                         (struct log_task_link *)
518                         malloc(sizeof(struct log_task_link));
519                 if (dev->task == NULL) {
520                         _E("failed to malloc log_task_link\n");
521                         return;
522                 }
523                 tail = dev->task;
524         } else {
525                 tail = dev->task;
526                 while (tail->next)
527                         tail = tail->next;
528                 tail->next =
529                         (struct log_task_link *)
530                         malloc(sizeof(struct log_task_link));
531                 if (tail->next == NULL) {
532                         _E("failed to malloc log_task_link\n");
533                         return;
534                 }
535                 tail = tail->next;
536         }
537         tail->work = work;
538         tail->next = NULL;
539 }
540
541 /*
542  * free work file descriptor
543  */
544 static void work_free(struct log_work *work)
545 {
546         if (!work)
547                 return;
548         if (work->filename) {
549                 free(work->filename);
550                 work->filename = NULL;
551                 if (work->fd != -1) {
552                         close(work->fd);
553                         work->fd = -1;
554                 }
555         }
556         log_format_free(work->format);
557         work->format = NULL;
558         free(work);
559         work = NULL;
560 }
561
562 /*
563  * free all the nodes after the "work" and includes itself
564  */
565 static void work_chain_free(struct log_work *work)
566 {
567         if (!work)
568                 return;
569         while (work->next) {
570                 struct log_work *tmp = work->next;
571                 work->next = tmp->next;
572                 work_free(tmp);
573         }
574         work_free(work);
575         work = NULL;
576 }
577
578 /*
579  * create a new log_device instance
580  * and open device
581  */
582 static struct log_device *device_new(int id)
583 {
584         struct log_device *dev;
585
586         if (LOG_ID_MAX <= id)
587                 return NULL;
588         dev = malloc(sizeof(struct log_device));
589         if (dev == NULL) {
590                 _E("failed to malloc log_device\n");
591                 return NULL;
592         }
593         dev->id = id;
594         dev->fd = open(device_path_table[id], O_RDONLY);
595         if (dev->fd < 0) {
596                 _E("Unable to open log device '%s': %s\n",
597                                 device_path_table[id],
598                                 strerror(errno));
599                 free(dev);
600                 return NULL;
601         }
602         _D("device new id %d fd %d\n", dev->id, dev->fd);
603         dev->task = NULL;
604         dev->queue = NULL;
605         dev->next = NULL;
606
607         return dev;
608 }
609
610 /*
611  * add a new log_device to the tail of chain
612  */
613 static int device_add_to_tail(struct log_device *dev, struct log_device *ndev)
614 {
615         struct log_device *tail = dev;
616
617         if (!dev || !ndev)
618                 return -1;
619
620         while (tail->next)
621                 tail = tail->next;
622         tail->next = ndev;
623
624         return 0;
625 }
626
627 /*
628  * add a new log_device or add to the tail of chain
629  */
630 static void device_add(int id)
631 {
632         if (check_device(id) < 0)
633                 return;
634
635         if (!devices) {
636                         devices = device_new(id);
637                         if (devices == NULL) {
638                                 _E("failed to device_new: %s\n",
639                                                 device_path_table[id]);
640                                 exit(EXIT_FAILURE);
641                         }
642         } else {
643                 if (device_add_to_tail(devices, device_new(id)) < 0) {
644                         _E("failed to device_add_to_tail: %s\n",
645                                         device_path_table[id]);
646                         exit(EXIT_FAILURE);
647                 }
648         }
649         return;
650 }
651
652 /*
653  * free one log_device  and it doesn't take care of chain so it
654  * may break the chain list
655  */
656 static void device_free(struct log_device *dev)
657 {
658         if (!dev)
659                 return;
660         if (dev->queue) {
661                 while (dev->queue->next) {
662                         struct queued_entry *tmp =
663                                 dev->queue->next;
664                         dev->queue->next = tmp->next;
665                         free(tmp);
666                 }
667                 free(dev->queue);
668                 dev->queue = NULL;
669         }
670         if (dev->task) {
671                 while (dev->task->next) {
672                         struct log_task_link *tmp =
673                                 dev->task->next;
674                         dev->task->next = tmp->next;
675                         free(tmp);
676                 }
677                 free(dev->task);
678                 dev->task = NULL;
679         }
680         free(dev);
681         dev = NULL;
682 }
683
684 /*
685  * free all the nodes after the "dev" and includes itself
686  */
687 static void device_chain_free(struct log_device *dev)
688 {
689         if (!dev)
690                 return;
691         while (dev->next) {
692                 struct log_device *tmp = dev->next;
693                 dev->next = tmp->next;
694                 device_free(tmp);
695         }
696         device_free(dev);
697         dev = NULL;
698 }
699
700 /*
701  * parse command line
702  * using getopt function
703  */
704 static int parse_command_line(char *linebuffer, struct log_command *cmd)
705 {
706         int i, ret, id, argc;
707         char *argv[MAX_ARGS];
708         char *tok, *cmdline;
709
710         if (linebuffer == NULL || cmd == NULL)
711                 return -1;
712         /* copy command line */
713         cmdline = strdup(linebuffer);
714         tok = strtok(cmdline, DELIMITER);
715         /* check the availability of command line
716            by comparing first word with dlogutil*/
717         if (!tok || strcmp(tok, "dlogutil")) {
718                 _D("Ignore this line (%s)\n", linebuffer);
719                 free(cmdline);
720                 return -1;
721         }
722         _D("Parsing this line (%s)\n", linebuffer);
723         /* fill the argc and argv
724            for extract option from command line */
725         argc = 0;
726         while (tok && (argc < MAX_ARGS)) {
727                 argv[argc] = strdup(tok);
728                 tok = strtok(NULL, DELIMITER);
729                 argc++;
730         }
731         free(cmdline);
732
733         /* initialize the command struct with the default value */
734         memset(cmd->devices, 0, sizeof(cmd->devices));
735         cmd->option_file = false;
736         cmd->option_buffer = false;
737         cmd->filename = NULL;
738         cmd->rotate_size = 0;
739         cmd->max_rotated = MAX_ROTATED;
740         cmd->format = (log_format *)log_format_new();
741
742         /* get option and fill the command struct */
743         while ((ret = getopt(argc, argv, "f:r:n:v:b:")) != -1) {
744                 switch (ret) {
745                 case 'f':
746                         cmd->filename = strdup(optarg);
747                         _D("command filename %s\n", cmd->filename);
748                         cmd->option_file = true;
749                         break;
750                 case 'b':
751                         id = get_device_id_by_name(optarg);
752                         _D("command device name %s id %d\n", optarg, id);
753                         if (id < 0 || LOG_ID_MAX <= id)
754                                 break;
755                         cmd->option_buffer = true;
756                         /* enable to log in device on/off struct */
757                         cmd->devices[id] = true;
758                         /* enable to open in global device on/off struct */
759                         register_device(id);
760                         break;
761                 case 'r':
762                         if (!isdigit(optarg[0]))
763                                 goto exit_free;
764                         cmd->rotate_size = atoi(optarg);
765                         _D("command rotate size %d\n", cmd->rotate_size);
766                         break;
767                 case 'n':
768                         if (!isdigit(optarg[0]))
769                                 goto exit_free;
770                         cmd->max_rotated = atoi(optarg);
771                         _D("command max rotated %d\n", cmd->max_rotated);
772                         break;
773                 case 'v':
774                         {
775                                 log_print_format print_format;
776                                 print_format = log_format_from_string(optarg);
777                                 if (print_format == FORMAT_OFF) {
778                                         _E("failed to add format\n");
779                                         goto exit_free;
780                                 }
781                                 _D("command format %s\n", optarg);
782                                 log_set_print_format(cmd->format, print_format);
783                         }
784                         break;
785                 default:
786                         break;
787                 }
788         }
789         /* add filter string, when command line have tags */
790         if (argc != optind) {
791                 for (i = optind ; i < argc ; i++) {
792                         ret = log_add_filter_string(cmd->format, argv[i]);
793                         _D("command add fileter string %s\n", argv[i]);
794                         if (ret < 0) {
795                                 _E("Invalid filter expression '%s'\n", argv[i]);
796                                 goto exit_free;
797                         }
798                 }
799         } else {
800                 ret = log_add_filter_string(cmd->format, "*:d");
801                 if (ret < 0) {
802                         _E("Invalid silent filter expression\n");
803                         goto exit_free;
804                 }
805         }
806         /* free argv */
807         for (i = 0; i < argc; i++)
808                 free(argv[i]);
809
810         if (cmd->option_file == false)
811                 goto exit_free;
812         /* If it have not the -b option,
813            set the default devices to open and log */
814         if (cmd->option_buffer == false) {
815                 _D("set default device\n");
816                 cmd->devices[LOG_ID_MAIN] = true;
817                 cmd->devices[LOG_ID_SYSTEM] = true;
818                 register_device(LOG_ID_MAIN);
819                 register_device(LOG_ID_SYSTEM);
820         }
821         /* for use getopt again */
822         optarg = NULL;
823         optind = 1;
824         optopt = 0;
825
826         return 0;
827
828 exit_free:
829         if (cmd->filename)
830                 free(cmd->filename);
831         return -1;
832 }
833
834 /*
835  * parse command from configuration file
836  * and return the command list with number of command
837  * if an command was successfully parsed, then it returns number of parsed command.
838  * on error or not founded, 0 is returned
839  */
840 static int parse_command(struct log_command *command_list)
841 {
842         FILE *fp;
843         int ncmd;
844         char *line_p;
845         char linebuffer[1024];
846
847         fp = fopen(CONFIG_FILE, "re");
848         if (!fp) {
849                 _E("no config file\n");
850                 goto exit;
851         }
852         ncmd = 0;
853         while (fgets(linebuffer, sizeof(linebuffer), fp) != NULL) {
854                 line_p = strchr(linebuffer, '\n');
855                 if (line_p != NULL)
856                         *line_p = '\0';
857                 if (parse_command_line(linebuffer, &command_list[ncmd]) == 0)
858                         ncmd++;
859                 if (COMMAND_MAX <= ncmd)
860                         break;
861         }
862         fclose(fp);
863
864         return ncmd;
865
866 exit:
867         return 0;
868 }
869
870 /*
871  * free dynamically allocated memory
872  */
873 static void cleanup(void)
874 {
875         work_chain_free(works);
876         device_chain_free(devices);
877 }
878
879 /*
880  * SIGINT, SIGTERM, SIGQUIT signal handler
881  */
882 static void sig_handler(int signo)
883 {
884         _D("sig_handler\n");
885         cleanup();
886         exit(EXIT_SUCCESS);
887 }
888
889 static int help(void) {
890
891         printf("%s [OPTIONS...] \n\n"
892                         "Logger, records log messages to files.\n\n"
893                         "  -h      Show this help\n"
894                         "  -b N    Set number of logs to keep logs in memory buffer bafore write files (default:0, MAX:100)\n"
895                         "  -t N    Set minimum interval time to write files (default:0, MAX:3600, seconds)\n",
896                         program_invocation_short_name);
897
898         return 0;
899 }
900
901 static int parse_argv(int argc, char *argv[]) {
902         int ret = 1, option;
903
904         while ((option = getopt(argc, argv, "hb:t:")) != -1) {
905                 switch (option) {
906                         case 't':
907                                 if (!isdigit(optarg[0])) {
908                                         ret = -EINVAL;
909                                         printf("Wrong argument!\n");
910                                         help();
911                                         goto exit;
912                                 }
913                                 min_interval = atoi(optarg);
914                                 if (min_interval < 0 || INTERVAL_MAX < min_interval)
915                                         min_interval = 0;
916                                 ret = 1;
917                                 break;
918                         case 'b':
919                                 if (!isdigit(optarg[0])) {
920                                         ret = -EINVAL;
921                                         printf("Wrong argument!\n");
922                                         help();
923                                         goto exit;
924                                 }
925                                 buffer_size = atoi(optarg);
926                                 if (buffer_size < 0 || BUFFER_MAX < buffer_size)
927                                         buffer_size = 0;
928                                 ret = 1;
929                                 break;
930                         case 'h':
931                                 help();
932                                 ret = 0;
933                                 goto exit;
934                         default:
935                                 ret = -EINVAL;
936                 }
937         }
938 exit:
939         optarg = NULL;
940         optind = 1;
941         optopt = 0;
942         return ret;
943 }
944
945
946 int main(int argc, char **argv)
947 {
948         int i, r, ncmd;
949         struct stat statbuf;
950         struct log_device *dev;
951         struct log_work *work;
952         struct log_command command_list[COMMAND_MAX];
953         struct sigaction act;
954
955         /* set the signal handler for free dynamically allocated memory. */
956         memset(&act, 0, sizeof(struct sigaction));
957         sigemptyset(&act.sa_mask);
958         act.sa_handler = (void *)sig_handler;
959         act.sa_flags = 0;
960
961         if (sigaction(SIGQUIT, &act, NULL) < 0)
962                 _E("failed to sigaction for SIGQUIT");
963         if (sigaction(SIGINT, &act, NULL) < 0)
964                 _E("failed to sigaction for SIGINT");
965         if (sigaction(SIGTERM, &act, NULL) < 0)
966                 _E("failed to sigaction for SIGTERM");
967
968         if (argc != 1) {
969                 r = parse_argv(argc, argv);
970                 if (r <= 0)
971                         goto exit;
972         }
973         /* parse command from command configuration file. */
974         ncmd = parse_command(command_list);
975         /* If it have nothing command, exit. */
976         if (!ncmd)
977                 goto exit;
978
979         /* create log device */
980         device_add(LOG_ID_MAIN);
981         device_add(LOG_ID_SYSTEM);
982         device_add(LOG_ID_RADIO);
983
984         /* create work from the parsed command */
985         for (i = 0; i < ncmd; i++) {
986                 work = work_new();
987                 _D("work new\n");
988                 if (work == NULL) {
989                         _E("failed to work_new\n");
990                         goto clean_exit;
991                 }
992                 /* attatch the work to global works variable */
993                 if (work_add_to_tail(works, work) < 0) {
994                         _E("failed to work_add_to_tail\n");
995                         goto clean_exit;
996                 }
997                 /* 1. set filename, fd and file current size */
998                 work->filename = command_list[i].filename;
999                 if (work->filename == NULL) {
1000                         _E("file name is NULL");
1001                         goto clean_exit;
1002                 }
1003                 work->fd = open_work(work->filename);
1004                 if (work->fd < 0) {
1005                         _E("failed to open log file");
1006                         exit(EXIT_FAILURE);
1007                 }
1008                 if (fstat(work->fd, &statbuf) == -1)
1009                         work->size = 0;
1010                 else
1011                         work->size = statbuf.st_size;
1012
1013                 /* 2. set size limits for log files */
1014                 work->rotate_size = command_list[i].rotate_size;
1015
1016                 /* 3. set limit on the number of rotated log files */
1017                 work->max_rotated = command_list[i].max_rotated;
1018
1019                 /* 4. set log_format to filter logs*/
1020                 work->format = command_list[i].format;
1021
1022                 /* 5. attatch the work to device task for logging */
1023                 dev = devices;
1024                 while (dev) {
1025                         if (command_list[i].devices[dev->id] == true) {
1026                                 work_add_to_device(dev, work);
1027                         }
1028                         dev = dev->next;
1029                 }
1030         }
1031
1032         /* do log */
1033         do_logger(devices);
1034
1035 clean_exit:
1036         work_chain_free(works);
1037         device_chain_free(devices);
1038 exit:
1039         return 0;
1040 }