2 * Copyright (c) 2005-2008, The Android Open Source Project
3 * Copyright (c) 2009-2013, Samsung Electronics Co., Ltd. All rights reserved.
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
9 * http://www.apache.org/licenses/LICENSE-2.0
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.
34 #include <arpa/inet.h>
40 #define _D(...) printf(__VA_ARGS__)
42 #define _D(...) do { } while (0)
44 #define _E(...) fprintf(stderr, __VA_ARGS__)
48 #define FILE_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH)
51 #define MAX_QUEUED 4096
52 #define BUFFER_MAX 100
53 #define INTERVAL_MAX 60*60
55 #define CONFIG_FILE "/opt/etc/dlog_logger.conf"
57 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
61 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
62 struct logger_entry entry __attribute__((aligned(4)));
64 struct queued_entry *next;
73 int devices[LOG_ID_MAX];
85 struct log_work *next;
88 struct log_task_link {
89 struct log_work *work;
90 struct log_task_link *next;
96 struct queued_entry *queue;
97 struct log_task_link *task;
98 struct log_device *next;
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",
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,
119 static int buffer_size = 0;
120 static int min_interval = 0;
123 * get log device id from device path table by device name
125 static int get_device_id_by_name(const char *name)
131 for (i = 0; i < ARRAY_SIZE(device_path_table); i++) {
132 if (strstr(device_path_table[i], name) != NULL)
140 * check device registration on watch device list
142 static int check_device(int id)
144 if (id < 0 || LOG_ID_MAX <= id)
147 return (device_list[id] == true) ? 0 : -1;
151 * register device to watch device list
153 static int register_device(int id)
155 if (id < 0 || LOG_ID_MAX <= id)
157 device_list[id] = true;
163 * comparison function to distinct entries by time
165 static int cmp(struct queued_entry *a, struct queued_entry *b)
167 int n = a->entry.sec - b->entry.sec;
171 return a->entry.nsec - b->entry.nsec;
175 * enqueueing the log_entry into the log_device
177 static void enqueue(struct log_device *device, struct queued_entry *entry)
179 if (device->queue == NULL) {
180 device->queue = entry;
182 struct queued_entry **e = &device->queue;
183 while (*e && cmp(entry, *e) >= 0)
193 static int open_work(const char *path)
195 return open(path, O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
201 static void rotate_logs(struct log_work *logwork)
205 char file0[NAME_MAX];
206 char file1[NAME_MAX];
209 filename = logwork->filename;
210 for (i = logwork->max_rotated ; i > 0 ; i--) {
211 snprintf(file1, NAME_MAX, "%s.%d", filename, i);
213 snprintf(file0, NAME_MAX, "%s", filename);
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");
220 /* open log file again */
221 logwork->fd = open_work(filename);
222 if (logwork->fd < 0) {
223 _E("couldn't open log file");
232 * process to print log
233 * and check the log file size to rotate
235 static void process_buffer(struct log_device *dev, struct logger_entry *buf)
237 int bytes_written, err;
239 struct log_work *logwork;
240 struct log_task_link *task;
242 err = log_process_log_buffer(buf, &entry);
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)) {
251 log_print_log_line(logwork->format,
252 logwork->fd, &entry);
253 if (bytes_written < 0) {
257 logwork->size += bytes_written;
259 if (logwork->rotate_size > 0 &&
260 (logwork->size / 1024) >= logwork->rotate_size) {
261 rotate_logs(logwork);
270 * choose first device by log_entry
272 static void choose_first(struct log_device *dev, struct log_device **firstdev)
274 for (*firstdev = NULL; dev != NULL; dev = dev->next) {
275 if (dev->queue != NULL &&
276 (*firstdev == NULL ||
278 (*firstdev)->queue) < 0)) {
285 * print beginnig string into the log files
287 static void maybe_print_start(struct log_device *dev)
289 struct log_work *logwork;
290 struct log_task_link *task;
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");
311 static void skip_next_entry(struct log_device *dev)
313 maybe_print_start(dev);
314 struct queued_entry *entry = dev->queue;
315 dev->queue = entry->next;
322 static void print_next_entry(struct log_device *dev)
324 maybe_print_start(dev);
325 process_buffer(dev, &dev->queue->entry);
326 skip_next_entry(dev);
332 static void do_logger(struct log_device *dev)
334 time_t commit_time = 0, current_time = 0;
335 struct log_device *pdev;
339 int queued_lines = 0;
343 commit_time = current_time = time(NULL);
345 for (pdev = dev; pdev; pdev = pdev->next) {
352 struct timeval timeout = { 0, 5000 /* 5ms */ };
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);
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));
369 _E("failed to malloc queued_entry\n");
370 goto exit;//exit(EXIT_FAILURE);
373 ret = read(pdev->fd, entry->buf,
374 LOGGER_ENTRY_MAX_LEN);
376 if (errno == EINTR) {
380 if (errno == EAGAIN) {
385 goto exit;//exit(EXIT_FAILURE);
388 _E("read: Unexpected EOF!\n");
390 } else if (entry->entry.len !=
391 ret - sizeof(struct logger_entry)) {
393 _E("unexpected length. Expected %d, got %d\n",
395 ret - sizeof(struct logger_entry));
396 goto exit;//exit(EXIT_FAILURE);
399 entry->entry.msg[entry->entry.len] = '\0';
401 enqueue(pdev, entry);
404 if (MAX_QUEUED < queued_lines) {
405 _D("queued_lines = %d\n", queued_lines);
407 choose_first(dev, &pdev);
410 print_next_entry(pdev);
414 commit_time = time(NULL);
421 current_time = time(NULL);
422 if (current_time - commit_time < min_interval &&
423 queued_lines < buffer_size) {
432 choose_first(dev, &pdev);
435 print_next_entry(pdev);
438 commit_time = current_time;
441 /* print all that aren't the last in their list */
444 choose_first(dev, &pdev);
445 if (pdev == NULL || pdev->queue->next == NULL)
447 print_next_entry(pdev);
450 commit_time = current_time;
464 static struct log_work *work_new(void)
466 struct log_work *work;
468 work = malloc(sizeof(struct log_work));
470 _E("failed to malloc log_work\n");
473 work->filename = NULL;
475 work->printed = false;
478 _D("work alloc %p\n", work);
484 * add a new log_work to the tail of chain
486 static int work_add_to_tail(struct log_work *work, struct log_work *nwork)
488 struct log_work *tail = work;
506 * add a new work task to the tail of chain
508 static void work_add_to_device(struct log_device *dev, struct log_work *work)
510 struct log_task_link *tail;
514 _D("dev %p work %p\n", dev, work);
515 if (dev->task == NULL) {
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");
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");
542 * free work file descriptor
544 static void work_free(struct log_work *work)
548 if (work->filename) {
549 free(work->filename);
550 work->filename = NULL;
551 if (work->fd != -1) {
556 log_format_free(work->format);
563 * free all the nodes after the "work" and includes itself
565 static void work_chain_free(struct log_work *work)
570 struct log_work *tmp = work->next;
571 work->next = tmp->next;
579 * create a new log_device instance
582 static struct log_device *device_new(int id)
584 struct log_device *dev;
586 if (LOG_ID_MAX <= id)
588 dev = malloc(sizeof(struct log_device));
590 _E("failed to malloc log_device\n");
594 dev->fd = open(device_path_table[id], O_RDONLY);
596 _E("Unable to open log device '%s': %s\n",
597 device_path_table[id],
602 _D("device new id %d fd %d\n", dev->id, dev->fd);
611 * add a new log_device to the tail of chain
613 static int device_add_to_tail(struct log_device *dev, struct log_device *ndev)
615 struct log_device *tail = dev;
628 * add a new log_device or add to the tail of chain
630 static void device_add(int id)
632 if (check_device(id) < 0)
636 devices = device_new(id);
637 if (devices == NULL) {
638 _E("failed to device_new: %s\n",
639 device_path_table[id]);
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]);
653 * free one log_device and it doesn't take care of chain so it
654 * may break the chain list
656 static void device_free(struct log_device *dev)
661 while (dev->queue->next) {
662 struct queued_entry *tmp =
664 dev->queue->next = tmp->next;
671 while (dev->task->next) {
672 struct log_task_link *tmp =
674 dev->task->next = tmp->next;
685 * free all the nodes after the "dev" and includes itself
687 static void device_chain_free(struct log_device *dev)
692 struct log_device *tmp = dev->next;
693 dev->next = tmp->next;
702 * using getopt function
704 static int parse_command_line(char *linebuffer, struct log_command *cmd)
706 int i, ret, id, argc;
707 char *argv[MAX_ARGS];
710 if (linebuffer == NULL || cmd == NULL)
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);
722 _D("Parsing this line (%s)\n", linebuffer);
723 /* fill the argc and argv
724 for extract option from command line */
726 while (tok && (argc < MAX_ARGS)) {
727 argv[argc] = strdup(tok);
728 tok = strtok(NULL, DELIMITER);
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();
742 /* get option and fill the command struct */
743 while ((ret = getopt(argc, argv, "f:r:n:v:b:")) != -1) {
746 cmd->filename = strdup(optarg);
747 _D("command filename %s\n", cmd->filename);
748 cmd->option_file = true;
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)
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 */
762 if (!isdigit(optarg[0]))
764 cmd->rotate_size = atoi(optarg);
765 _D("command rotate size %d\n", cmd->rotate_size);
768 if (!isdigit(optarg[0]))
770 cmd->max_rotated = atoi(optarg);
771 _D("command max rotated %d\n", cmd->max_rotated);
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");
781 _D("command format %s\n", optarg);
782 log_set_print_format(cmd->format, print_format);
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]);
795 _E("Invalid filter expression '%s'\n", argv[i]);
800 ret = log_add_filter_string(cmd->format, "*:d");
802 _E("Invalid silent filter expression\n");
807 for (i = 0; i < argc; i++)
810 if (cmd->option_file == false)
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);
821 /* for use getopt again */
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
840 static int parse_command(struct log_command *command_list)
845 char linebuffer[1024];
847 fp = fopen(CONFIG_FILE, "re");
849 _E("no config file\n");
853 while (fgets(linebuffer, sizeof(linebuffer), fp) != NULL) {
854 line_p = strchr(linebuffer, '\n');
857 if (parse_command_line(linebuffer, &command_list[ncmd]) == 0)
859 if (COMMAND_MAX <= ncmd)
871 * free dynamically allocated memory
873 static void cleanup(void)
875 work_chain_free(works);
876 device_chain_free(devices);
880 * SIGINT, SIGTERM, SIGQUIT signal handler
882 static void sig_handler(int signo)
889 static int help(void) {
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);
901 static int parse_argv(int argc, char *argv[]) {
904 while ((option = getopt(argc, argv, "hb:t:")) != -1) {
907 if (!isdigit(optarg[0])) {
909 printf("Wrong argument!\n");
913 min_interval = atoi(optarg);
914 if (min_interval < 0 || INTERVAL_MAX < min_interval)
919 if (!isdigit(optarg[0])) {
921 printf("Wrong argument!\n");
925 buffer_size = atoi(optarg);
926 if (buffer_size < 0 || BUFFER_MAX < buffer_size)
946 int main(int argc, char **argv)
950 struct log_device *dev;
951 struct log_work *work;
952 struct log_command command_list[COMMAND_MAX];
953 struct sigaction act;
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;
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");
969 r = parse_argv(argc, argv);
973 /* parse command from command configuration file. */
974 ncmd = parse_command(command_list);
975 /* If it have nothing command, exit. */
979 /* create log device */
980 device_add(LOG_ID_MAIN);
981 device_add(LOG_ID_SYSTEM);
982 device_add(LOG_ID_RADIO);
984 /* create work from the parsed command */
985 for (i = 0; i < ncmd; i++) {
989 _E("failed to work_new\n");
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");
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");
1003 work->fd = open_work(work->filename);
1005 _E("failed to open log file");
1008 if (fstat(work->fd, &statbuf) == -1)
1011 work->size = statbuf.st_size;
1013 /* 2. set size limits for log files */
1014 work->rotate_size = command_list[i].rotate_size;
1016 /* 3. set limit on the number of rotated log files */
1017 work->max_rotated = command_list[i].max_rotated;
1019 /* 4. set log_format to filter logs*/
1020 work->format = command_list[i].format;
1022 /* 5. attatch the work to device task for logging */
1025 if (command_list[i].devices[dev->id] == true) {
1026 work_add_to_device(dev, work);
1036 work_chain_free(works);
1037 device_chain_free(devices);