logread.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  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. #include <sys/types.h>
  15. #include <sys/stat.h>
  16. #include <fcntl.h>
  17. #include <time.h>
  18. #include <stdio.h>
  19. #include <unistd.h>
  20. #include <sys/types.h>
  21. #include <sys/socket.h>
  22. #define SYSLOG_NAMES
  23. #include <syslog.h>
  24. #include <libubox/blobmsg_json.h>
  25. #include <libubox/usock.h>
  26. #include <libubox/uloop.h>
  27. #include "libubus.h"
  28. enum {
  29. LOG_STDOUT,
  30. LOG_FILE,
  31. LOG_NET,
  32. };
  33. enum {
  34. LOG_MSG,
  35. LOG_ID,
  36. LOG_PRIO,
  37. LOG_SOURCE,
  38. LOG_TIME,
  39. __LOG_MAX
  40. };
  41. static const struct blobmsg_policy log_policy[] = {
  42. [LOG_MSG] = { .name = "msg", .type = BLOBMSG_TYPE_STRING },
  43. [LOG_ID] = { .name = "id", .type = BLOBMSG_TYPE_INT32 },
  44. [LOG_PRIO] = { .name = "priority", .type = BLOBMSG_TYPE_INT32 },
  45. [LOG_SOURCE] = { .name = "source", .type = BLOBMSG_TYPE_INT32 },
  46. [LOG_TIME] = { .name = "time", .type = BLOBMSG_TYPE_INT64 },
  47. };
  48. static struct ubus_subscriber log_event;
  49. static struct uloop_timeout retry;
  50. static struct uloop_fd sender;
  51. static const char *log_file, *log_ip, *log_port, *pid_file;
  52. static int log_type = LOG_STDOUT;
  53. static int log_size, log_udp;
  54. static const char* getcodetext(int value, CODE *codetable) {
  55. CODE *i;
  56. if (value >= 0)
  57. for (i = codetable; i->c_val != -1; i++)
  58. if (i->c_val == value)
  59. return (i->c_name);
  60. return "<unknown>";
  61. };
  62. static void log_handle_reconnect(struct uloop_timeout *timeout)
  63. {
  64. sender.fd = usock((log_udp) ? (USOCK_UDP) : (USOCK_TCP), log_ip, log_port);
  65. if (sender.fd < 0) {
  66. fprintf(stderr, "failed to connect: %s\n", strerror(errno));
  67. uloop_timeout_set(&retry, 1000);
  68. } else {
  69. uloop_fd_add(&sender, ULOOP_READ);
  70. syslog(0, "Logread connected to %s:%s\n", log_ip, log_port);
  71. }
  72. }
  73. static void log_handle_remove(struct ubus_context *ctx, struct ubus_subscriber *s,
  74. uint32_t id)
  75. {
  76. fprintf(stderr, "Object %08x went away\n", id);
  77. }
  78. static void log_handle_fd(struct uloop_fd *u, unsigned int events)
  79. {
  80. if (u->eof) {
  81. uloop_fd_delete(u);
  82. close(sender.fd);
  83. sender.fd = -1;
  84. uloop_timeout_set(&retry, 1000);
  85. }
  86. }
  87. static int log_notify(struct ubus_context *ctx, struct ubus_object *obj,
  88. struct ubus_request_data *req, const char *method,
  89. struct blob_attr *msg)
  90. {
  91. struct blob_attr *tb[__LOG_MAX];
  92. struct stat s;
  93. char buf[256];
  94. uint32_t p;
  95. char *str;
  96. time_t t;
  97. char *c;
  98. if (sender.fd < 0)
  99. return 0;
  100. blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blob_data(msg), blob_len(msg));
  101. if (!tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME])
  102. return 1;
  103. if ((log_type == LOG_FILE) && log_size && (!stat(log_file, &s)) && (s.st_size > log_size)) {
  104. char *old = malloc(strlen(log_file) + 5);
  105. close(sender.fd);
  106. if (old) {
  107. sprintf(old, "%s.old", log_file);
  108. rename(log_file, old);
  109. free(old);
  110. }
  111. sender.fd = open(log_file, O_CREAT | O_WRONLY | O_APPEND, 0600);
  112. if (sender.fd < 0) {
  113. // fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno));
  114. exit(-1);
  115. }
  116. }
  117. t = blobmsg_get_u64(tb[LOG_TIME]) / 1000;
  118. c = ctime(&t);
  119. p = blobmsg_get_u32(tb[LOG_PRIO]);
  120. c[strlen(c) - 1] = '\0';
  121. str = blobmsg_format_json(msg, true);
  122. if (log_type == LOG_NET) {
  123. int err;
  124. snprintf(buf, sizeof(buf), "%s%s\n",
  125. (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : ("kernel: "),
  126. method);
  127. if (log_udp)
  128. err = write(sender.fd, buf, strlen(buf));
  129. else
  130. err = send(sender.fd, buf, strlen(buf), 0);
  131. if (err < 0) {
  132. syslog(0, "failed to send log data to %s:%s via %s\n",
  133. log_ip, log_port, (log_udp) ? ("udp") : ("tcp"));
  134. uloop_fd_delete(&sender);
  135. close(sender.fd);
  136. sender.fd = -1;
  137. uloop_timeout_set(&retry, 1000);
  138. }
  139. } else {
  140. snprintf(buf, sizeof(buf), "%s %s.%s%s %s\n",
  141. c, getcodetext(LOG_FAC(p) << 3, facilitynames), getcodetext(LOG_PRI(p), prioritynames),
  142. (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"),
  143. method);
  144. write(sender.fd, buf, strlen(buf));
  145. }
  146. free(str);
  147. if (log_type == LOG_FILE)
  148. fsync(sender.fd);
  149. return 0;
  150. }
  151. static void follow_log(struct ubus_context *ctx, int id)
  152. {
  153. FILE *fp;
  154. int ret;
  155. signal(SIGPIPE, SIG_IGN);
  156. if (pid_file) {
  157. fp = fopen(pid_file, "w+");
  158. if (fp) {
  159. fprintf(fp, "%d", getpid());
  160. fclose(fp);
  161. }
  162. }
  163. uloop_init();
  164. ubus_add_uloop(ctx);
  165. log_event.remove_cb = log_handle_remove;
  166. log_event.cb = log_notify;
  167. ret = ubus_register_subscriber(ctx, &log_event);
  168. if (ret)
  169. fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret));
  170. ret = ubus_subscribe(ctx, &log_event, id);
  171. if (ret)
  172. fprintf(stderr, "Failed to add watch handler: %s\n", ubus_strerror(ret));
  173. if (log_ip && log_port) {
  174. openlog("logread", LOG_PID, LOG_DAEMON);
  175. log_type = LOG_NET;
  176. sender.cb = log_handle_fd;
  177. retry.cb = log_handle_reconnect;
  178. uloop_timeout_set(&retry, 1000);
  179. } else if (log_file) {
  180. log_type = LOG_FILE;
  181. sender.fd = open(log_file, O_CREAT | O_WRONLY| O_APPEND, 0600);
  182. if (sender.fd < 0) {
  183. fprintf(stderr, "failed to open %s: %s\n", log_file, strerror(errno));
  184. exit(-1);
  185. }
  186. } else {
  187. sender.fd = STDOUT_FILENO;
  188. }
  189. uloop_run();
  190. ubus_free(ctx);
  191. uloop_done();
  192. }
  193. enum {
  194. READ_LINE,
  195. __READ_MAX
  196. };
  197. static const struct blobmsg_policy read_policy[] = {
  198. [READ_LINE] = { .name = "lines", .type = BLOBMSG_TYPE_ARRAY },
  199. };
  200. static void read_cb(struct ubus_request *req, int type, struct blob_attr *msg)
  201. {
  202. struct blob_attr *cur;
  203. struct blob_attr *_tb[__READ_MAX];
  204. time_t t;
  205. int rem;
  206. if (!msg)
  207. return;
  208. blobmsg_parse(read_policy, ARRAY_SIZE(read_policy), _tb, blob_data(msg), blob_len(msg));
  209. if (!_tb[READ_LINE])
  210. return;
  211. blobmsg_for_each_attr(cur, _tb[READ_LINE], rem) {
  212. struct blob_attr *tb[__LOG_MAX];
  213. uint32_t p;
  214. char *c;
  215. if (blobmsg_type(cur) != BLOBMSG_TYPE_TABLE)
  216. continue;
  217. blobmsg_parse(log_policy, ARRAY_SIZE(log_policy), tb, blobmsg_data(cur), blobmsg_data_len(cur));
  218. if (!tb[LOG_MSG] || !tb[LOG_ID] || !tb[LOG_PRIO] || !tb[LOG_SOURCE] || !tb[LOG_TIME])
  219. continue;
  220. t = blobmsg_get_u64(tb[LOG_TIME]);
  221. p = blobmsg_get_u32(tb[LOG_PRIO]);
  222. c = ctime(&t);
  223. c[strlen(c) - 1] = '\0';
  224. printf("%s %s.%s%s %s\n",
  225. c, getcodetext(LOG_FAC(p) << 3, facilitynames), getcodetext(LOG_PRI(p), prioritynames),
  226. (blobmsg_get_u32(tb[LOG_SOURCE])) ? ("") : (" kernel:"),
  227. blobmsg_get_string(tb[LOG_MSG]));
  228. }
  229. }
  230. static int usage(const char *prog)
  231. {
  232. fprintf(stderr, "Usage: %s [options]\n"
  233. "Options:\n"
  234. " -s <path> Path to ubus socket\n"
  235. " -l <count> Got only the last 'count' messages\n"
  236. " -r <server> <port> Stream message to a server\n"
  237. " -F <file> Log file\n"
  238. " -S <bytes> Log size\n"
  239. " -p <file> PID file\n"
  240. " -f Follow log messages\n"
  241. " -u Use UDP as the protocol\n"
  242. "\n", prog);
  243. return 1;
  244. }
  245. int main(int argc, char **argv)
  246. {
  247. struct ubus_context *ctx;
  248. uint32_t id;
  249. const char *ubus_socket = NULL;
  250. int ch, ret, subscribe = 0, lines = 0;
  251. static struct blob_buf b;
  252. while ((ch = getopt(argc, argv, "ufcs:l:r:F:p:S:")) != -1) {
  253. switch (ch) {
  254. case 'u':
  255. log_udp = 1;
  256. break;
  257. case 's':
  258. ubus_socket = optarg;
  259. break;
  260. case 'r':
  261. log_ip = optarg++;
  262. log_port = argv[optind++];
  263. break;
  264. case 'F':
  265. log_file = optarg;
  266. break;
  267. case 'p':
  268. pid_file = optarg;
  269. break;
  270. case 'f':
  271. subscribe = 1;
  272. break;
  273. case 'l':
  274. lines = atoi(optarg);
  275. break;
  276. case 'S':
  277. log_size = atoi(optarg);
  278. if (log_size < 1)
  279. log_size = 1;
  280. log_size *= 1024;
  281. break;
  282. default:
  283. return usage(*argv);
  284. }
  285. }
  286. ctx = ubus_connect(ubus_socket);
  287. if (!ctx) {
  288. fprintf(stderr, "Failed to connect to ubus\n");
  289. return -1;
  290. }
  291. ret = ubus_lookup_id(ctx, "log", &id);
  292. if (ret)
  293. fprintf(stderr, "Failed to find log object: %s\n", ubus_strerror(ret));
  294. if (!subscribe || lines) {
  295. blob_buf_init(&b, 0);
  296. if (lines)
  297. blobmsg_add_u32(&b, "lines", lines);
  298. ubus_invoke(ctx, id, "read", b.head, read_cb, 0, 3000);
  299. }
  300. if (subscribe)
  301. follow_log(ctx, id);
  302. return 0;
  303. }