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 { 39 LOG_STDOUT, 40 LOG_FILE, 41 LOG_NET, 42 }; 43 44 enum { 45 LOG_MSG, 46 LOG_ID, 47 LOG_PRIO, 48 LOG_SOURCE, 49 LOG_TIME, 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; 64 static const char *log_file, *log_ip, *log_port, *log_prefix, *pid_file, *hostname, *regexp_pattern; 65 static int log_type = LOG_STDOUT; 66 static int log_size, log_udp, log_follow, log_trailer_null = 0; 67 static int log_timestamp; 68 static int logd_conn_tries = LOGD_CONNECT_RETRY; 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(®exp_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) 268 logd_conn_tries = LOGD_CONNECT_RETRY; 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; 292 retry.cb = log_handle_reconnect; 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 '': 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(®exp_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; 411 logread_setup_output(); 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 } 429
This page was automatically generated by LXR 0.3.1. • OpenWrt