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)
52 #define CONFIG_FILE "/opt/etc/dlog_logger.conf"
54 #define ARRAY_SIZE(name) (sizeof(name)/sizeof(name[0]))
58 unsigned char buf[LOGGER_ENTRY_MAX_LEN + 1] __attribute__((aligned(4)));
59 struct logger_entry entry __attribute__((aligned(4)));
61 struct queued_entry *next;
70 int devices[LOG_ID_MAX];
82 struct log_work *next;
85 struct log_task_link {
86 struct log_work *work;
87 struct log_task_link *next;
93 struct queued_entry *queue;
94 struct log_task_link *task;
95 struct log_device *next;
98 static const char *device_path_table[] = {
99 [LOG_ID_MAIN] = "/dev/log_main",
100 [LOG_ID_RADIO] = "/dev/log_radio",
101 [LOG_ID_SYSTEM] = "/dev/log_system",
102 [LOG_ID_APPS] = "/dev/log_apps",
106 static struct log_work *works;
107 static struct log_device *devices;
108 static int device_list[] = {
109 [LOG_ID_MAIN] = false,
110 [LOG_ID_RADIO] = false,
111 [LOG_ID_SYSTEM] = false,
112 [LOG_ID_APPS] = false,
113 [LOG_ID_MAX] = false,
117 * get log device id from device path table by device name
119 static int get_device_id_by_name(const char *name)
125 for (i = 0; i < ARRAY_SIZE(device_path_table); i++) {
126 if (strstr(device_path_table[i], name) != NULL)
134 * check device registration on watch device list
136 static int check_device(int id)
138 if (id < 0 || LOG_ID_MAX < id)
141 return (device_list[id] == true) ? 0 : -1;
145 * register device to watch device list
147 static int register_device(int id)
149 if (id < 0 || LOG_ID_MAX < id)
151 device_list[id] = true;
157 * comparison function to distinct entries by time
159 static int cmp(struct queued_entry *a, struct queued_entry *b)
161 int n = a->entry.sec - b->entry.sec;
165 return a->entry.nsec - b->entry.nsec;
169 * enqueueing the log_entry into the log_device
171 static void enqueue(struct log_device *device, struct queued_entry *entry)
173 if (device->queue == NULL) {
174 device->queue = entry;
176 struct queued_entry **e = &device->queue;
177 while (*e && cmp(entry, *e) >= 0)
187 static int open_work(const char *path)
189 return open(path, O_WRONLY | O_APPEND | O_CREAT, FILE_PERMS);
195 static void rotate_logs(struct log_work *logwork)
199 char file0[NAME_MAX];
200 char file1[NAME_MAX];
203 filename = logwork->filename;
204 for (i = logwork->max_rotated ; i > 0 ; i--) {
205 snprintf(file1, NAME_MAX, "%s.%d", filename, i);
207 snprintf(file0, NAME_MAX, "%s", filename);
209 snprintf(file0, NAME_MAX, "%s.%d", filename, i - 1);
210 ret = rename(file0, file1);
211 if (ret < 0 && errno != ENOENT)
212 _E("while rotating log works");
214 /* open log file again */
215 logwork->fd = open_work(filename);
216 if (logwork->fd < 0) {
217 _E("couldn't open log file");
226 * process to print log
227 * and check the log file size to rotate
229 static void process_buffer(struct log_device *dev, struct logger_entry *buf)
231 int bytes_written, err;
233 struct log_work *logwork;
234 struct log_task_link *task;
236 err = log_process_log_buffer(buf, &entry);
241 for (task = dev->task; task; task = task->next) {
242 logwork = task->work;
243 if (log_should_print_line(logwork->format,
244 entry.tag, entry.priority)) {
246 log_print_log_line(logwork->format,
247 logwork->fd, &entry);
248 if (bytes_written < 0) {
252 logwork->size += bytes_written;
254 if (logwork->rotate_size > 0 &&
255 (logwork->size / 1024) >= logwork->rotate_size) {
256 rotate_logs(logwork);
264 * choose first device by log_entry
266 static void choose_first(struct log_device *dev, struct log_device **firstdev)
268 for (*firstdev = NULL; dev != NULL; dev = dev->next) {
269 if (dev->queue != NULL &&
270 (*firstdev == NULL ||
272 (*firstdev)->queue) < 0)) {
279 * print beginnig string into the log files
281 static void maybe_print_start(struct log_device *dev)
283 struct log_work *logwork;
284 struct log_task_link *task;
287 for (task = dev->task; task; task = task->next) {
288 logwork = task->work;
289 if (!logwork->printed) {
290 logwork->printed = true;
291 snprintf(buf, sizeof(buf),
292 "--------- beginning of %s\n",
293 device_path_table[dev->id]);
294 if (write(logwork->fd, buf, strlen(buf)) < 0) {
295 _E("maybe work error");
305 static void skip_next_entry(struct log_device *dev)
307 maybe_print_start(dev);
308 struct queued_entry *entry = dev->queue;
309 dev->queue = entry->next;
316 static void print_next_entry(struct log_device *dev)
318 maybe_print_start(dev);
319 process_buffer(dev, &dev->queue->entry);
320 skip_next_entry(dev);
326 static void do_logger(struct log_device *dev)
328 struct log_device *pdev;
332 int queued_lines = 0;
335 for (pdev = dev; pdev; pdev = pdev->next) {
342 struct timeval timeout = { 0, 5000 /* 5ms */ };
344 for (pdev = dev; pdev; pdev = pdev->next)
345 FD_SET(pdev->fd, &readset);
346 result = select(max + 1, &readset, NULL, NULL,
347 sleep ? NULL : &timeout);
348 } while (result == -1 && errno == EINTR);
353 for (pdev = dev; pdev; pdev = pdev->next) {
354 if (FD_ISSET(pdev->fd, &readset)) {
355 struct queued_entry *entry =
356 (struct queued_entry *)
357 malloc(sizeof(struct queued_entry));
359 _E("failed to malloc queued_entry\n");
360 goto exit;//exit(EXIT_FAILURE);
363 ret = read(pdev->fd, entry->buf,
364 LOGGER_ENTRY_MAX_LEN);
366 if (errno == EINTR) {
370 if (errno == EAGAIN) {
375 goto exit;//exit(EXIT_FAILURE);
378 _E("read: Unexpected EOF!\n");
380 } else if (entry->entry.len !=
381 ret - sizeof(struct logger_entry)) {
383 _E("unexpected length. Expected %d, got %d\n",
385 ret - sizeof(struct logger_entry));
386 goto exit;//exit(EXIT_FAILURE);
389 entry->entry.msg[entry->entry.len] = '\0';
391 enqueue(pdev, entry);
399 choose_first(dev, &pdev);
402 print_next_entry(pdev);
406 /* print all that aren't the last in their list */
409 choose_first(dev, &pdev);
410 if (pdev == NULL || pdev->queue->next == NULL)
412 print_next_entry(pdev);
427 static struct log_work *work_new(void)
429 struct log_work *work;
431 work = malloc(sizeof(*work));
433 _E("failed to malloc log_work\n");
436 work->filename = NULL;
438 work->printed = false;
441 _D("work alloc %p\n", work);
447 * add a new log_work to the tail of chain
449 static int work_add_to_tail(struct log_work *work, struct log_work *nwork)
451 struct log_work *tail = work;
469 * add a new work task to the tail of chain
471 static void work_add_to_device(struct log_device *dev, struct log_work *work)
473 struct log_task_link *tail;
477 _D("dev %p work %p\n", dev, work);
478 if (dev->task == NULL) {
480 (struct log_task_link *)
481 malloc(sizeof(struct log_task_link));
482 if (dev->task == NULL) {
483 _E("failed to malloc log_task_link\n");
492 (struct log_task_link *)
493 malloc(sizeof(struct log_task_link));
494 if (tail->next == NULL) {
495 _E("failed to malloc log_task_link\n");
505 * free work file descriptor
507 static void work_free(struct log_work *work)
511 if (work->filename) {
512 free(work->filename);
513 work->filename = NULL;
514 if (work->fd != -1) {
519 log_format_free(work->format);
526 * free all the nodes after the "work" and includes itself
528 static void work_chain_free(struct log_work *work)
533 struct log_work *tmp = work->next;
534 work->next = tmp->next;
542 * create a new log_device instance
545 static struct log_device *device_new(int id)
547 struct log_device *dev;
551 dev = malloc(sizeof(*dev));
553 _E("failed to malloc log_device\n");
557 dev->fd = open(device_path_table[id], O_RDONLY);
559 _E("Unable to open log device '%s': %s\n",
560 device_path_table[id],
564 _D("device new id %d fd %d\n", dev->id, dev->fd);
573 * add a new log_device to the tail of chain
575 static int device_add_to_tail(struct log_device *dev, struct log_device *ndev)
577 struct log_device *tail = dev;
590 * add a new log_device or add to the tail of chain
592 static void device_add(int id)
594 if (check_device(id) < 0)
598 devices = device_new(id);
599 if (devices == NULL) {
600 _E("failed to device_new: %s\n",
601 device_path_table[id]);
605 if (device_add_to_tail(devices, device_new(id)) < 0) {
606 _E("failed to device_add_to_tail: %s\n",
607 device_path_table[id]);
615 * free one log_device and it doesn't take care of chain so it
616 * may break the chain list
618 static void device_free(struct log_device *dev)
623 while (dev->queue->next) {
624 struct queued_entry *tmp =
626 dev->queue->next = tmp->next;
633 while (dev->task->next) {
634 struct log_task_link *tmp =
636 dev->task->next = tmp->next;
647 * free all the nodes after the "dev" and includes itself
649 static void device_chain_free(struct log_device *dev)
654 struct log_device *tmp = dev->next;
655 dev->next = tmp->next;
664 * using getopt function
666 static int parse_command_line(char *linebuffer, struct log_command *cmd)
668 int i, ret, id, argc;
669 char *argv[MAX_ARGS];
672 if (linebuffer == NULL || cmd == NULL)
674 /* copy command line */
675 cmdline = strdup(linebuffer);
676 tok = strtok(cmdline, DELIMITER);
677 /* check the availability of command line
678 by comparing first word with dlogutil*/
679 if (!tok || strcmp(tok, "dlogutil")) {
680 _D("Ignore this line (%s)\n", linebuffer);
684 _D("Parsing this line (%s)\n", linebuffer);
685 /* fill the argc and argv
686 for extract option from command line */
688 while (tok && (argc < MAX_ARGS)) {
689 argv[argc] = strdup(tok);
690 tok = strtok(NULL, DELIMITER);
695 /* initialize the command struct with the default value */
696 memset(cmd->devices, 0, sizeof(cmd->devices));
697 cmd->option_file = false;
698 cmd->option_buffer = false;
699 cmd->filename = NULL;
700 cmd->rotate_size = 0;
701 cmd->max_rotated = MAX_ROTATED;
702 cmd->format = (log_format *)log_format_new();
704 /* get option and fill the command struct */
705 while ((ret = getopt(argc, argv, "f:r:n:v:b:")) != -1) {
708 cmd->filename = strdup(optarg);
709 _D("command filename %s\n", cmd->filename);
710 cmd->option_file = true;
713 id = get_device_id_by_name(optarg);
714 _D("command device name %s id %d\n", optarg, id);
715 if (id < 0 || LOG_ID_MAX < id)
717 cmd->option_buffer = true;
718 /* enable to log in device on/off struct */
719 cmd->devices[id] = true;
720 /* enable to open in global device on/off struct */
724 if (!isdigit(optarg[0]))
726 cmd->rotate_size = atoi(optarg);
727 _D("command rotate size %d\n", cmd->rotate_size);
730 if (!isdigit(optarg[0]))
732 cmd->max_rotated = atoi(optarg);
733 _D("command max rotated %d\n", cmd->max_rotated);
737 log_print_format print_format;
738 print_format = log_format_from_string(optarg);
739 if (print_format == FORMAT_OFF) {
740 _E("failed to add format\n");
743 _D("command format %s\n", optarg);
744 log_set_print_format(cmd->format, print_format);
751 /* add filter string, when command line have tags */
752 if (argc != optind) {
753 for (i = optind ; i < argc ; i++) {
754 ret = log_add_filter_string(cmd->format, argv[i]);
755 _D("command add fileter string %s\n", argv[i]);
757 _E("Invalid filter expression '%s'\n", argv[i]);
762 ret = log_add_filter_string(cmd->format, "*:d");
764 _E("Invalid silent filter expression\n");
769 for (i = 0; i < argc; i++)
772 if (cmd->option_file == false)
774 /* If it have not the -b option,
775 set the default devices to open and log */
776 if (cmd->option_buffer == false) {
777 _D("set default device\n", cmd->filename);
778 cmd->devices[LOG_ID_MAIN] = true;
779 cmd->devices[LOG_ID_SYSTEM] = true;
780 register_device(LOG_ID_MAIN);
781 register_device(LOG_ID_SYSTEM);
783 /* for use getopt again */
797 * parse command from configuration file
798 * and return the command list with number of command
799 * if an command was successfully parsed, then it returns number of parsed command.
800 * on error or not founded, 0 is returned
802 static int parse_command(struct log_command *command_list)
807 char linebuffer[1024];
809 fp = fopen(CONFIG_FILE, "re");
811 _E("no config file\n");
815 while (fgets(linebuffer, sizeof(linebuffer), fp) != NULL) {
816 line_p = strchr(linebuffer, '\n');
819 if (parse_command_line(linebuffer, &command_list[ncmd]) == 0)
821 if (COMMAND_MAX <= ncmd)
833 * free dynamically allocated memory
835 static void cleanup(void)
837 work_chain_free(works);
838 device_chain_free(devices);
842 * SIGINT, SIGTERM, SIGQUIT signal handler
844 static void sig_handler(int signo)
851 int main(int argc, char **argv)
855 struct log_device *dev;
856 struct log_work *work;
857 struct log_command command_list[COMMAND_MAX];
858 struct sigaction act;
860 /* set the signal handler for free dynamically allocated memory. */
861 memset(&act, 0, sizeof(act));
862 sigemptyset(&act.sa_mask);
863 act.sa_handler = (void *)sig_handler;
866 if (sigaction(SIGQUIT, &act, NULL) < 0)
867 _E("failed to sigaction for SIGQUIT");
868 if (sigaction(SIGINT, &act, NULL) < 0)
869 _E("failed to sigaction for SIGINT");
870 if (sigaction(SIGTERM, &act, NULL) < 0)
871 _E("failed to sigaction for SIGTERM");
873 /* parse command from command configuration file. */
874 ncmd = parse_command(command_list);
876 /* If it have nothing command, exit. */
880 /* create log device */
881 device_add(LOG_ID_MAIN);
882 device_add(LOG_ID_SYSTEM);
883 device_add(LOG_ID_RADIO);
885 /* create work from the parsed command */
886 for (i = 0; i < ncmd; i++) {
890 _E("failed to work_new\n");
893 /* attatch the work to global works variable */
894 if (work_add_to_tail(works, work) < 0) {
895 _E("failed to work_add_to_tail\n");
898 /* 1. set filename, fd and file current size */
899 work->filename = command_list[i].filename;
900 if (work->filename == NULL) {
901 _E("file name is NULL");
904 work->fd = open_work(work->filename);
906 _E("failed to open log file");
909 if (fstat(work->fd, &statbuf) == -1)
912 work->size = statbuf.st_size;
914 /* 2. set size limits for log files */
915 work->rotate_size = command_list[i].rotate_size;
917 /* 3. set limit on the number of rotated log files */
918 work->max_rotated = command_list[i].max_rotated;
920 /* 4. set log_format to filter logs*/
921 work->format = command_list[i].format;
923 /* 5. attatch the work to device task for logging */
926 if (command_list[i].devices[dev->id] == true) {
927 work_add_to_device(dev, work);
937 work_chain_free(works);
938 device_chain_free(devices);