Ubox
OpenWrt core utilities
logread.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3  * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU Lesser General Public License version 2.1
7  * as published by the Free Software Foundation
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  */
14 
15 #include <sys/types.h>
16 #include <sys/stat.h>
17 
18 #include <fcntl.h>
19 #include <time.h>
20 #include <regex.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
25 
26 #define SYSLOG_NAMES
27 #include <syslog.h>
28 
29 #include <libubox/ustream.h>
30 #include <libubox/blobmsg_json.h>
31 #include <libubox/usock.h>
32 #include <libubox/uloop.h>
33 #include "libubus.h"
34 #include "syslog.h"
35 
36 #define LOGD_CONNECT_RETRY 10
37 
38 enum {
42 };
43 
44 enum {
50  __LOG_MAX
51 };
52 
53 static const struct blobmsg_policy log_policy[] = {
54  [LOG_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING },
55  [LOG_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
56  [LOG_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
57  [LOG_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_INT32 },
58  [LOG_TIME] = { .name = "time", .type = BLOBMSG_TYPE_INT64 },
59 };
60 
61 static struct uloop_timeout retry;
62 static struct uloop_fd sender;
63 static regex_t regexp_preg;
65 static int log_type = LOG_STDOUT;
67 static int log_timestamp;
69 static int facility_include;
70 static int facility_exclude;
71 
72 /* check for facility filter; return 0 if message shall be dropped */
73 static int check_facility_filter(int f)
74 {
75  if (facility_include)
76  return !!(facility_include & (1 << f));
77  if (facility_exclude)
78  return !(facility_exclude & (1 << f));
79  return 1;
80 }
81 
82 static const char* getcodetext(int value, CODE *codetable) {
83  CODE *i;
84 
85  if (value >= 0)
86  for (i = codetable; i->c_val != -1; i++)
87  if (i->c_val == value)
88  return (i->c_name);
89  return "<unknown>";
90 };
91 
92 static void log_handle_reconnect(struct uloop_timeout *timeout)
93 {
94  sender.fd = usock((log_udp) ? (USOCK_UDP) : (USOCK_TCP), log_ip, log_port);
95  if (sender.fd < 0) {
96  fprintf(stderr, "failed to connect: %m\n");
97  uloop_timeout_set(&retry, 1000);
98  } else {
99  uloop_fd_add(&sender, ULOOP_READ);
100 
101  if (log_udp < 2)
102  syslog(LOG_INFO, "Logread connected to %s:%s via %s\n",
103  log_ip, log_port, (log_udp) ? ("udp") : ("tcp"));
104 
105  if (log_udp == 1)
106  ++log_udp;
107  }
108 }
109 
110 static void log_handle_fd(struct uloop_fd *u, unsigned int events)
111 {
112  if (u->eof) {
113  uloop_fd_delete(u);
114  close(sender.fd);
115  sender.fd = -1;
116  uloop_timeout_set(&retry, 1000);
117  }
118 }
119 
120 static int log_notify(struct blob_attr *msg)
121 {
122  struct blob_attr *tb[__LOG_MAX];
123  struct stat s;
124  char buf[LOG_LINE_SIZE + 128];
125  char buf_ts[32];
126  uint32_t p;
127  time_t t;
128  uint32_t t_ms = 0;
129  char *c, *m;
130  int ret = 0;
131 
132  if (sender.fd < 0)
133  return 0;
134 
135  blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blob_data(msg), blob_len(msg));
136  if (!tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME] || !tb[LOG_MSG])
137  return 1;
138 
139  if ((log_type == LOG_FILE) && log_size && (!stat(log_file, &s)) && (s.st_size > log_size)) {
140  char *old = malloc(strlen(log_file) + 5);
141 
142  close(sender.fd);
143  if (old) {
144  sprintf(old, "%s.old", log_file);
145  rename(log_file, old);
146  free(old);
147  }
148  sender.fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, 0600);
149  if (sender.fd < 0) {
150  fprintf(stderr, "failed to open %s: %m\n", log_file);
151  exit(-1);
152  }
153  }
154  p = blobmsg_get_u32(tb[LOG_PRIO]);
155 
156  if (!check_facility_filter(LOG_FAC(p)))
157  return 0;
158 
159  m = blobmsg_get_string(tb[LOG_MSG]);
160  if (regexp_pattern &&
161  regexec(&regexp_preg, m, 0, NULL, 0) == REG_NOMATCH)
162  return 0;
163  t = blobmsg_get_u64(tb[LOG_TIME]) / 1000;
164  if (log_timestamp) {
165  t_ms = blobmsg_get_u64(tb[LOG_TIME]) % 1000;
166  snprintf(buf_ts, sizeof(buf_ts), "[%lu.%03u] ",
167  (unsigned long)t, t_ms);
168  }
169  c = ctime(&t);
170  c[strlen(c) - 1] = '\0';
171 
172  if (log_type == LOG_NET) {
173  int err;
174 
175  snprintf(buf, sizeof(buf), "<%u>", p);
176  strncat(buf, c + 4, 16);
177  if (log_timestamp) {
178  strncat(buf, buf_ts, sizeof(buf) - strlen(buf) - 1);
179  }
180  if (hostname) {
181  strncat(buf, hostname, sizeof(buf) - strlen(buf) - 1);
182  strncat(buf, " ", sizeof(buf) - strlen(buf) - 1);
183  }
184  if (log_prefix) {
185  strncat(buf, log_prefix, sizeof(buf) - strlen(buf) - 1);
186  strncat(buf, ": ", sizeof(buf) - strlen(buf) - 1);
187  }
188  if (blobmsg_get_u32(tb[LOG_SOURCE]) == SOURCE_KLOG)
189  strncat(buf, "kernel: ", sizeof(buf) - strlen(buf) - 1);
190  strncat(buf, m, sizeof(buf) - strlen(buf) - 1);
191  if (log_udp)
192  err = write(sender.fd, buf, strlen(buf));
193  else {
194  size_t buflen = strlen(buf);
195  if (!log_trailer_null)
196  buf[buflen] = '\n';
197  err = send(sender.fd, buf, buflen + 1, 0);
198  }
199 
200  if (err < 0) {
201  syslog(LOG_INFO, "Failed to send log data to %s:%s via %s\n",
202  log_ip, log_port, (log_udp) ? ("udp") : ("tcp"));
203  uloop_fd_delete(&sender);
204  close(sender.fd);
205  sender.fd = -1;
206  uloop_timeout_set(&retry, 1000);
207  }
208  } else {
209  snprintf(buf, sizeof(buf), "%s %s%s.%s%s %s\n",
210  c, log_timestamp ? buf_ts : "",
211  getcodetext(LOG_FAC(p) << 3, facilitynames),
212  getcodetext(LOG_PRI(p), prioritynames),
213  (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"), m);
214  ret = write(sender.fd, buf, strlen(buf));
215  }
216 
217  if (log_type == LOG_FILE)
218  fsync(sender.fd);
219 
220  return ret;
221 }
222 
223 static int usage(const char *prog)
224 {
225  fprintf(stderr, "Usage: %s [options]\n"
226  "Options:\n"
227  " -s <path> Path to ubus socket\n"
228  " -l <count> Got only the last 'count' messages\n"
229  " -e <pattern> Filter messages with a regexp\n"
230  " -r <server> <port> Stream message to a server\n"
231  " -F <file> Log file\n"
232  " -S <bytes> Log size\n"
233  " -p <file> PID file\n"
234  " -h <hostname> Add hostname to the message\n"
235  " -P <prefix> Prefix custom text to streamed messages\n"
236  " -z <facility> handle only messages with given facility (0-23), repeatable\n"
237  " -Z <facility> ignore messages with given facility (0-23), repeatable\n"
238  " -f Follow log messages\n"
239  " -u Use UDP as the protocol\n"
240  " -t Add an extra timestamp\n"
241  " -0 Use \\0 instead of \\n as trailer when using TCP\n"
242  "\n", prog);
243  return 1;
244 }
245 
246 static void logread_fd_data_cb(struct ustream *s, int bytes)
247 {
248  while (true) {
249  struct blob_attr *a;
250  int len, cur_len;
251 
252  a = (void*) ustream_get_read_buf(s, &len);
253  if (len < sizeof(*a))
254  break;
255 
256  cur_len = blob_len(a) + sizeof(*a);
257  if (len < cur_len)
258  break;
259 
260  log_notify(a);
261  ustream_consume(s, cur_len);
262  }
263 }
264 
265 static void logread_fd_state_cb(struct ustream *s)
266 {
267  if (log_follow)
269  uloop_end();
270 }
271 
272 static void logread_fd_cb(struct ubus_request *req, int fd)
273 {
274  static struct ustream_fd test_fd;
275 
276  memset(&test_fd, 0, sizeof(test_fd));
277 
278  test_fd.stream.notify_read = logread_fd_data_cb;
279  test_fd.stream.notify_state = logread_fd_state_cb;
280  ustream_fd_init(&test_fd, fd);
281 }
282 
283 static void logread_setup_output(void)
284 {
285  if (sender.fd || sender.cb)
286  return;
287 
288  if (log_ip && log_port) {
289  openlog("logread", LOG_PID, LOG_DAEMON);
290  log_type = LOG_NET;
291  sender.cb = log_handle_fd;
293  uloop_timeout_set(&retry, 1000);
294  } else if (log_file) {
295  log_type = LOG_FILE;
296  sender.fd = open(log_file, O_CREAT | O_WRONLY| O_APPEND, 0600);
297  if (sender.fd < 0) {
298  fprintf(stderr, "failed to open %s: %m\n", log_file);
299  exit(-1);
300  }
301  } else {
302  sender.fd = STDOUT_FILENO;
303  }
304 }
305 
306 int main(int argc, char **argv)
307 {
308  struct ubus_context *ctx;
309  uint32_t id;
310  const char *ubus_socket = NULL;
311  int ch, ret, lines = 0;
312  static struct blob_buf b;
313 
314  signal(SIGPIPE, SIG_IGN);
315 
316  while ((ch = getopt(argc, argv, "u0fcs:l:z:Z:r:F:p:S:P:h:e:t")) != -1) {
317  switch (ch) {
318  case 'u':
319  log_udp = 1;
320  break;
321  case '0':
322  log_trailer_null = 1;
323  break;
324  case 's':
325  ubus_socket = optarg;
326  break;
327  case 'r':
328  log_ip = optarg++;
329  log_port = argv[optind++];
330  break;
331  case 'F':
332  log_file = optarg;
333  break;
334  case 'p':
335  pid_file = optarg;
336  break;
337  case 'P':
338  log_prefix = optarg;
339  break;
340  case 'f':
341  log_follow = 1;
342  break;
343  case 'l':
344  lines = atoi(optarg);
345  break;
346  case 'z':
347  id = strtoul(optarg, NULL, 0) & 0x1f;
348  facility_include |= (1 << id);
349  break;
350  case 'Z':
351  id = strtoul(optarg, NULL, 0) & 0x1f;
352  facility_exclude |= (1 << id);
353  break;
354  case 'S':
355  log_size = atoi(optarg);
356  if (log_size < 1)
357  log_size = 1;
358  log_size *= 1024;
359  break;
360  case 'h':
361  hostname = optarg;
362  break;
363  case 'e':
364  if (!regcomp(&regexp_preg, optarg, REG_NOSUB)) {
365  regexp_pattern = optarg;
366  }
367  break;
368  case 't':
369  log_timestamp = 1;
370  break;
371  default:
372  return usage(*argv);
373  }
374  }
375  uloop_init();
376 
377  ctx = ubus_connect(ubus_socket);
378  if (!ctx) {
379  fprintf(stderr, "Failed to connect to ubus\n");
380  return -1;
381  }
382  ubus_add_uloop(ctx);
383 
384  if (log_follow && pid_file) {
385  FILE *fp = fopen(pid_file, "w+");
386  if (fp) {
387  fprintf(fp, "%d", getpid());
388  fclose(fp);
389  }
390  }
391 
392  blob_buf_init(&b, 0);
393  blobmsg_add_u8(&b, "stream", 1);
394  blobmsg_add_u8(&b, "oneshot", !log_follow);
395  if (lines)
396  blobmsg_add_u32(&b, "lines", lines);
397  else if (log_follow)
398  blobmsg_add_u32(&b, "lines", 0);
399 
400  /* ugly ugly ugly ... we need a real reconnect logic */
401  do {
402  struct ubus_request req = { 0 };
403 
404  ret = ubus_lookup_id(ctx, "log", &id);
405  if (ret) {
406  fprintf(stderr, "Failed to find log object: %s\n", ubus_strerror(ret));
407  sleep(1);
408  continue;
409  }
410  logd_conn_tries = 0;
412 
413  ubus_invoke_async(ctx, id, "read", b.head, &req);
414  req.fd_cb = logread_fd_cb;
415  ubus_complete_request_async(ctx, &req);
416 
417  uloop_run();
418 
419  } while (logd_conn_tries--);
420 
421  ubus_free(ctx);
422  uloop_done();
423 
424  if (log_follow && pid_file)
425  unlink(pid_file);
426 
427  return ret;
428 }
static struct blob_buf b
Definition: logd.c:33
static const char * getcodetext(int value, CODE *codetable)
Definition: logread.c:82
static int log_size
Definition: logread.c:66
static const char * log_port
Definition: logread.c:64
static const char * pid_file
Definition: logread.c:64
static regex_t regexp_preg
Definition: logread.c:63
static int log_follow
Definition: logread.c:66
int main(int argc, char **argv)
Definition: logread.c:306
static void logread_fd_cb(struct ubus_request *req, int fd)
Definition: logread.c:272
static void logread_fd_state_cb(struct ustream *s)
Definition: logread.c:265
static int check_facility_filter(int f)
Definition: logread.c:73
static const char * regexp_pattern
Definition: logread.c:64
static void logread_setup_output(void)
Definition: logread.c:283
#define LOGD_CONNECT_RETRY
Definition: logread.c:36
static int log_timestamp
Definition: logread.c:67
static struct uloop_fd sender
Definition: logread.c:62
static const char * hostname
Definition: logread.c:64
static struct uloop_timeout retry
Definition: logread.c:61
static const char * log_prefix
Definition: logread.c:64
@ LOG_FILE
Definition: logread.c:40
@ LOG_NET
Definition: logread.c:41
@ LOG_STDOUT
Definition: logread.c:39
static void log_handle_reconnect(struct uloop_timeout *timeout)
Definition: logread.c:92
static int logd_conn_tries
Definition: logread.c:68
static int facility_exclude
Definition: logread.c:70
static int facility_include
Definition: logread.c:69
static const char * log_ip
Definition: logread.c:64
static const struct blobmsg_policy log_policy[]
Definition: logread.c:53
@ LOG_TIME
Definition: logread.c:49
@ __LOG_MAX
Definition: logread.c:50
@ LOG_MSG
Definition: logread.c:45
@ LOG_PRIO
Definition: logread.c:47
@ LOG_ID
Definition: logread.c:46
@ LOG_SOURCE
Definition: logread.c:48
static void logread_fd_data_cb(struct ustream *s, int bytes)
Definition: logread.c:246
static void log_handle_fd(struct uloop_fd *u, unsigned int events)
Definition: logread.c:110
static int log_type
Definition: logread.c:65
static int log_trailer_null
Definition: logread.c:66
static int usage(const char *prog)
Definition: logread.c:223
static const char * log_file
Definition: logread.c:64
static int log_notify(struct blob_attr *msg)
Definition: logread.c:120
static int log_udp
Definition: logread.c:66
static struct ubus_context * ctx
Definition: lsbloader.c:43
#define LOG_LINE_SIZE
Definition: syslog.h:17
@ SOURCE_KLOG
Definition: syslog.h:22