aboutsummaryrefslogtreecommitdiff
path: root/toys/pending/klogd.c
blob: 0b638294508ec4489768892bc731666053b24c1f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/* klogd.c - Klogd, The kernel log Dameon.
 *
 * Copyright 2013 Sandeep Sharma <sandeep.jack2756@gmail.com>
 *
 * No standard

USE_KLOGD(NEWTOY(klogd, "c#<1>8n", TOYFLAG_SBIN))

config KLOGD
    bool "klogd"
    default n
    help
    usage: klogd [-n] [-c N]

    -c  N   Print to console messages more urgent than prio N (1-8)"
    -n    Run in foreground.

config KLOGD_SOURCE_RING_BUFFER
    bool "enable kernel ring buffer as log source."
    default n
    depends on KLOGD
*/

#define FOR_klogd
#include "toys.h"
#include <signal.h>
#include <sys/klog.h>
GLOBALS(
  long level;

  int fd;
)

static void set_log_level(int level)
{   
  if (CFG_KLOGD_SOURCE_RING_BUFFER)
    klogctl(8, NULL, level);
  else {
    FILE *fptr = xfopen("/proc/sys/kernel/printk", "w");
    fprintf(fptr, "%u\n", level);
    fclose(fptr);
    fptr = NULL;
  }
}

static void handle_signal(int sig)
{
  if (CFG_KLOGD_SOURCE_RING_BUFFER) {
    klogctl(7, NULL, 0); 
    klogctl(0, NULL, 0);
  } else {
    set_log_level(7);
    xclose(TT.fd);
  }
  syslog(LOG_NOTICE,"KLOGD: Daemon exiting......");
  exit(1);
}

/*
 * Read kernel ring buffer in local buff and keep track of
 * "used" amount to track next read to start.
 */
void klogd_main(void)
{
  int prio, size, used = 0;
  char *start, *line_start, msg_buffer[16348]; //LOG_LINE_LENGTH - Ring buffer size

  sigatexit(handle_signal);
  if (toys.optflags & FLAG_c) set_log_level(TT.level);    //set log level
  if (!(toys.optflags & FLAG_n)) {                       //Make it daemon
    pid_t pid;        
    int fd = open("/dev/null", O_RDWR);
    if (fd < 0) fd = open("/", O_RDONLY, 0666);
    if((pid = fork()) < 0) perror_exit("DAEMON: fail to fork");
    if (pid) exit(EXIT_SUCCESS);

    setsid();      
    dup2(fd, 0);   
    dup2(fd, 1);   
    dup2(fd, 2);   
    if (fd > 2) close(fd);   
  }

  if (CFG_KLOGD_SOURCE_RING_BUFFER) {
    syslog(LOG_NOTICE, "KLOGD: started with Kernel ring buffer as log source\n");
    klogctl(1, NULL, 0);
  } else {
    TT.fd = xopen("/proc/kmsg", O_RDONLY); //_PATH_KLOG in paths.h
    syslog(LOG_NOTICE, "KLOGD: started with /proc/kmsg as log source\n");
  }
  openlog("Kernel", 0, LOG_KERN);    //open connection to system logger..

  while(1) {
    start = msg_buffer + used; //start updated for re-read.
    if (CFG_KLOGD_SOURCE_RING_BUFFER) {
      size = klogctl(2, start, sizeof(msg_buffer) - used - 1);
    } else {
      size = xread(TT.fd, start, sizeof(msg_buffer) - used - 1);
    }
    if (size < 0) perror_exit("error reading file:");
    start[size] = '\0';  //Ensure last line to be NUL terminated.
    if (used) start = msg_buffer;
    while(start) {
      if ((line_start = strsep(&start, "\n")) != NULL && start != NULL) used = 0;
      else {                            //Incomplete line, copy it to start of buff.
        used = strlen(line_start);
        strcpy(msg_buffer, line_start);
        if (used < (sizeof(msg_buffer) - 1)) break;
        used = 0; //we have buffer full, log it as it is.
      }
      prio = LOG_INFO;  //we dont know priority, mark it INFO
      if (*line_start == '<') {  //we have new line to syslog
        line_start++;
        if (line_start) prio = (int)strtoul(line_start, &line_start, 10);
        if (*line_start == '>') line_start++;
      }
      if (*line_start) syslog(prio, "%s", line_start);
    }
  }
}