1 /* 2 * Copyright (C) 2013 John Crispin <blogic@openwrt.org> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU Lesser General Public License version 2.1 6 * as published by the Free Software Foundation 7 * 8 * This program is distributed in the hope that it will be useful, 9 * but WITHOUT ANY WARRANTY; without even the implied warranty of 10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 * GNU General Public License for more details. 12 */ 13 14 #include <linux/un.h> 15 16 #include <sys/types.h> 17 #include <sys/socket.h> 18 #include <sys/stat.h> 19 20 #include <fcntl.h> 21 #include <regex.h> 22 #include <time.h> 23 #include <unistd.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <stdio.h> 27 #include <syslog.h> 28 #include <errno.h> 29 #include <ctype.h> 30 31 #include <libubox/uloop.h> 32 #include <libubox/usock.h> 33 #include <libubox/ustream.h> 34 #include <libubox/utils.h> 35 36 #include "syslog.h" 37 38 #define LOG_DEFAULT_SIZE (16 * 1024) 39 #define LOG_DEFAULT_SOCKET "/dev/log" 40 #define SYSLOG_PADDING 16 41 42 #define KLOG_DEFAULT_PROC "/proc/kmsg" 43 44 #define PAD(x) (x % 4) ? (((x) - (x % 4)) + 4) : (x) 45 46 static char *log_dev = LOG_DEFAULT_SOCKET; 47 static int log_size = LOG_DEFAULT_SIZE; 48 static struct log_head *log, *log_end, *oldest, *newest; 49 static int current_id = 0; 50 static regex_t pat_prio; 51 static regex_t pat_tstamp; 52 static struct udebug ud; 53 static struct udebug_buf udb_kernel, udb_user, udb_debug; 54 static const struct udebug_buf_meta meta_kernel = { 55 .name = "kernel", 56 .format = UDEBUG_FORMAT_STRING, 57 }; 58 static const struct udebug_buf_meta meta_user = { 59 .name = "syslog", 60 .format = UDEBUG_FORMAT_STRING, 61 }; 62 static const struct udebug_buf_meta meta_debug = { 63 .name = "debug", 64 .format = UDEBUG_FORMAT_STRING, 65 }; 66 static struct udebug_ubus_ring rings[] = { 67 { 68 .buf = &udb_kernel, 69 .meta = &meta_kernel, 70 .default_entries = 1024, 71 .default_size = 65536, 72 }, 73 { 74 .buf = &udb_user, 75 .meta = &meta_user, 76 .default_entries = 1024, 77 .default_size = 65536, 78 }, 79 { 80 .buf = &udb_debug, 81 .meta = &meta_debug, 82 .default_entries = 1024, 83 .default_size = 65536, 84 }, 85 }; 86 87 static struct log_head* 88 log_next(struct log_head *h, int size) 89 { 90 struct log_head *n = (struct log_head *) &h->data[PAD(size)]; 91 92 return (n >= log_end) ? (log) : (n); 93 } 94 95 static uint64_t 96 get_kernel_ts(const char *ts_sec, const char *ts_nsec) 97 { 98 uint64_t ts = strtoull(ts_sec, NULL, 10) * UDEBUG_TS_SEC + 99 strtoull(ts_nsec, NULL, 10) / 1000; 100 struct timespec wall, mono; 101 102 if (clock_gettime(CLOCK_REALTIME, &wall) || 103 clock_gettime(CLOCK_MONOTONIC, &mono)) 104 return 0; 105 106 ts += (wall.tv_sec - mono.tv_sec) * UDEBUG_TS_SEC; 107 ts += (wall.tv_nsec - mono.tv_nsec) / 1000; 108 109 return ts; 110 } 111 112 static void 113 log_add_udebug(int priority, char *buf, int size, int source) 114 { 115 regmatch_t matches[4]; 116 struct udebug_buf *udb; 117 uint64_t ts = 0; 118 119 if (source == SOURCE_KLOG) 120 udb = &udb_kernel; 121 else if ((priority & LOG_FACMASK) == LOG_LOCAL7) 122 udb = &udb_debug; 123 else 124 udb = &udb_user; 125 126 if (!udebug_buf_valid(udb)) 127 return; 128 129 if (source == SOURCE_KLOG && 130 !regexec(&pat_tstamp, buf, 4, matches, 0)) { 131 ts = get_kernel_ts(&buf[matches[1].rm_so], &buf[matches[2].rm_so]); 132 buf += matches[3].rm_so; 133 size -= matches[3].rm_so; 134 } 135 136 if (!ts) 137 ts = udebug_timestamp(); 138 139 udebug_entry_init_ts(udb, ts); 140 udebug_entry_printf(udb, "<%d>", priority); 141 udebug_entry_append(udb, buf, size - 1); 142 udebug_entry_add(udb); 143 } 144 145 146 void 147 log_add(char *buf, int size, int source) 148 { 149 regmatch_t matches[3]; 150 struct log_head *next; 151 int priority = 0; 152 int ret; 153 char *c; 154 155 /* bounce out if we don't have init'ed yet (regmatch etc will blow) */ 156 if (!log) { 157 fprintf(stderr, "%s", buf); 158 return; 159 } 160 161 for (c = buf; *c; c++) { 162 if (*c == '\n') 163 *c = ' '; 164 } 165 166 c = buf + size - 2; 167 while (isspace(*c)) { 168 size--; 169 c--; 170 } 171 172 buf[size - 1] = 0; 173 174 /* strip the priority */ 175 ret = regexec(&pat_prio, buf, 3, matches, 0); 176 if (!ret) { 177 priority = atoi(&buf[matches[1].rm_so]); 178 size -= matches[2].rm_so; 179 buf += matches[2].rm_so; 180 } 181 182 /* strip syslog timestamp */ 183 if ((source == SOURCE_SYSLOG) && (size > SYSLOG_PADDING) && (buf[SYSLOG_PADDING - 1] == ' ')) { 184 size -= SYSLOG_PADDING; 185 buf += SYSLOG_PADDING; 186 } 187 188 log_add_udebug(priority, buf, size, source); 189 190 /* debug message */ 191 if ((priority & LOG_FACMASK) == LOG_LOCAL7) 192 return; 193 194 /* find new oldest entry */ 195 next = log_next(newest, size); 196 if (next > newest) { 197 while ((oldest > newest) && (oldest <= next) && (oldest != log)) 198 oldest = log_next(oldest, oldest->size); 199 } else { 200 //fprintf(stderr, "Log wrap\n"); 201 newest->size = 0; 202 next = log_next(log, size); 203 for (oldest = log; oldest <= next; oldest = log_next(oldest, oldest->size)) 204 ; 205 newest = log; 206 } 207 208 /* add the log message */ 209 newest->size = size; 210 newest->id = current_id++; 211 newest->priority = priority; 212 newest->source = source; 213 clock_gettime(CLOCK_REALTIME, &newest->ts); 214 strcpy(newest->data, buf); 215 216 ubus_notify_log(newest); 217 218 newest = next; 219 } 220 221 static void 222 syslog_handle_fd(struct uloop_fd *fd, unsigned int events) 223 { 224 static char buf[LOG_LINE_SIZE]; 225 int len; 226 227 while (1) { 228 len = recv(fd->fd, buf, LOG_LINE_SIZE - 1, 0); 229 if (len < 0) { 230 if (errno == EINTR) 231 continue; 232 233 break; 234 } 235 if (!len) 236 break; 237 238 buf[len] = 0; 239 240 log_add(buf, strlen(buf) + 1, SOURCE_SYSLOG); 241 } 242 } 243 244 static void 245 klog_cb(struct ustream *s, int bytes) 246 { 247 struct ustream_buf *buf = s->r.head; 248 char *newline, *str; 249 int len; 250 251 do { 252 str = ustream_get_read_buf(s, NULL); 253 if (!str) 254 break; 255 newline = strchr(buf->data, '\n'); 256 if (!newline) 257 break; 258 *newline = 0; 259 len = newline + 1 - str; 260 log_add(buf->data, len, SOURCE_KLOG); 261 ustream_consume(s, len); 262 } while (1); 263 } 264 265 static struct uloop_fd syslog_fd = { 266 .cb = syslog_handle_fd 267 }; 268 269 static struct ustream_fd klog = { 270 .stream.string_data = true, 271 .stream.notify_read = klog_cb, 272 }; 273 274 static int 275 klog_open(void) 276 { 277 int fd; 278 279 fd = open(KLOG_DEFAULT_PROC, O_RDONLY | O_NONBLOCK); 280 if (fd < 0) { 281 fprintf(stderr, "Failed to open %s\n", KLOG_DEFAULT_PROC); 282 return -1; 283 } 284 fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); 285 ustream_fd_init(&klog, fd); 286 return 0; 287 } 288 289 static int 290 syslog_open(void) 291 { 292 unlink(log_dev); 293 syslog_fd.fd = usock(USOCK_UNIX | USOCK_UDP | USOCK_SERVER | USOCK_NONBLOCK, log_dev, NULL); 294 if (syslog_fd.fd < 0) { 295 fprintf(stderr,"Failed to open %s\n", log_dev); 296 return -1; 297 } 298 chmod(log_dev, 0666); 299 uloop_fd_add(&syslog_fd, ULOOP_READ | ULOOP_EDGE_TRIGGER); 300 301 return 0; 302 } 303 304 struct log_head* 305 log_list(int count, struct log_head *h) 306 { 307 unsigned int min = count; 308 309 if (count) 310 min = (count < current_id) ? (current_id - count) : (0); 311 if (!h && oldest->id >= min) 312 return oldest; 313 if (!h) 314 h = oldest; 315 316 while (h != newest) { 317 h = log_next(h, h->size); 318 if (!h->size && (h > newest)) 319 h = log; 320 if (h->id >= min && (h != newest)) 321 return h; 322 } 323 324 return NULL; 325 } 326 327 int 328 log_buffer_init(int size) 329 { 330 struct log_head *_log = calloc(1, size); 331 332 if (!_log) { 333 fprintf(stderr, "Failed to initialize log buffer with size %d\n", log_size); 334 return -1; 335 } 336 337 if (log && ((log_size + sizeof(struct log_head)) < size)) { 338 struct log_head *start = _log; 339 struct log_head *end = ((void*) _log) + size; 340 struct log_head *l; 341 342 l = log_list(0, NULL); 343 while ((start < end) && l && l->size) { 344 memcpy(start, l, PAD(sizeof(struct log_head) + l->size)); 345 start = (struct log_head *) &l->data[PAD(l->size)]; 346 l = log_list(0, l); 347 } 348 free(log); 349 newest = start; 350 newest->size = 0; 351 oldest = log = _log; 352 log_end = ((void*) log) + size; 353 } else { 354 oldest = newest = log = _log; 355 log_end = ((void*) log) + size; 356 } 357 log_size = size; 358 359 return 0; 360 } 361 362 void log_udebug_config(struct udebug_ubus *ctx, struct blob_attr *data, 363 bool enabled) 364 { 365 udebug_ubus_apply_config(&ud, rings, ARRAY_SIZE(rings), data, enabled); 366 } 367 368 void 369 log_init(int _log_size) 370 { 371 if (_log_size > 0) 372 log_size = _log_size; 373 374 regcomp(&pat_prio, "^<([0-9]*)>(.*)", REG_EXTENDED); 375 regcomp(&pat_tstamp, "^\\[[ 0]*([0-9]*).([0-9]*)\\] (.*)", REG_EXTENDED); 376 377 if (log_buffer_init(log_size)) { 378 fprintf(stderr, "Failed to allocate log memory\n"); 379 exit(-1); 380 } 381 382 udebug_init(&ud); 383 udebug_auto_connect(&ud, NULL); 384 for (size_t i = 0; i < ARRAY_SIZE(rings); i++) 385 udebug_ubus_ring_init(&ud, &rings[i]); 386 387 syslog_open(); 388 klog_open(); 389 openlog("sysinit", LOG_CONS, LOG_DAEMON); 390 } 391 392 void 393 log_shutdown(void) 394 { 395 if (syslog_fd.registered) { 396 uloop_fd_delete(&syslog_fd); 397 close(syslog_fd.fd); 398 } 399 400 ustream_free(&klog.stream); 401 close(klog.fd.fd); 402 free(log); 403 regfree(&pat_prio); 404 regfree(&pat_tstamp); 405 } 406
This page was automatically generated by LXR 0.3.1. • OpenWrt