• source navigation  • diff markup  • identifier search  • freetext search  • 

Sources/ubox/log/syslog.c

  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