2 * Copyright (C) 2013 Felix Fietkau <nbd@openwrt.org>
3 * Copyright (C) 2013 John Crispin <blogic@openwrt.org>
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
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.
15 #include <sys/types.h>
23 #include <sys/types.h>
24 #include <sys/socket.h>
29 #include <libubox/ustream.h>
30 #include <libubox/blobmsg_json.h>
31 #include <libubox/usock.h>
32 #include <libubox/uloop.h>
36 #define LOGD_CONNECT_RETRY 10
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
},
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
;
72 /* check for facility filter; return 0 if message shall be dropped */
73 static int check_facility_filter(int f
)
76 return !!(facility_include
& (1 << f
));
78 return !(facility_exclude
& (1 << f
));
82 static const char* getcodetext(int value
, CODE
*codetable
) {
86 for (i
= codetable
; i
->c_val
!= -1; i
++)
87 if (i
->c_val
== value
)
92 static void log_handle_reconnect(struct uloop_timeout
*timeout
)
94 sender
.fd
= usock((log_udp
) ? (USOCK_UDP
) : (USOCK_TCP
), log_ip
, log_port
);
96 fprintf(stderr
, "failed to connect: %m\n");
97 uloop_timeout_set(&retry
, 1000);
99 uloop_fd_add(&sender
, ULOOP_READ
);
102 syslog(LOG_INFO
, "Logread connected to %s:%s via %s\n",
103 log_ip
, log_port
, (log_udp
) ? ("udp") : ("tcp"));
110 static void log_handle_fd(struct uloop_fd
*u
, unsigned int events
)
116 uloop_timeout_set(&retry
, 1000);
120 static int log_notify(struct blob_attr
*msg
)
122 struct blob_attr
*tb
[__LOG_MAX
];
124 char buf
[LOG_LINE_SIZE
+ 128];
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
])
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);
144 sprintf(old
, "%s.old", log_file
);
145 rename(log_file
, old
);
148 sender
.fd
= open(log_file
, O_CREAT
| O_WRONLY
| O_APPEND
, 0600);
150 fprintf(stderr
, "failed to open %s: %m\n", log_file
);
154 p
= blobmsg_get_u32(tb
[LOG_PRIO
]);
156 if (!check_facility_filter(LOG_FAC(p
)))
159 m
= blobmsg_get_string(tb
[LOG_MSG
]);
160 if (regexp_pattern
&&
161 regexec(®exp_preg
, m
, 0, NULL
, 0) == REG_NOMATCH
)
163 t
= blobmsg_get_u64(tb
[LOG_TIME
]) / 1000;
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
);
170 c
[strlen(c
) - 1] = '\0';
172 if (log_type
== LOG_NET
) {
175 snprintf(buf
, sizeof(buf
), "<%u>", p
);
176 strncat(buf
, c
+ 4, 16);
178 strncat(buf
, buf_ts
, sizeof(buf
) - strlen(buf
) - 1);
181 strncat(buf
, hostname
, sizeof(buf
) - strlen(buf
) - 1);
182 strncat(buf
, " ", sizeof(buf
) - strlen(buf
) - 1);
185 strncat(buf
, log_prefix
, sizeof(buf
) - strlen(buf
) - 1);
186 strncat(buf
, ": ", sizeof(buf
) - strlen(buf
) - 1);
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);
192 err
= write(sender
.fd
, buf
, strlen(buf
));
194 size_t buflen
= strlen(buf
);
195 if (!log_trailer_null
)
197 err
= send(sender
.fd
, buf
, buflen
+ 1, 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
);
206 uloop_timeout_set(&retry
, 1000);
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
));
217 if (log_type
== LOG_FILE
)
223 static int usage(const char *prog
)
225 fprintf(stderr
, "Usage: %s [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"
246 static void logread_fd_data_cb(struct ustream
*s
, int bytes
)
252 a
= (void*) ustream_get_read_buf(s
, &len
);
253 if (len
< sizeof(*a
))
256 cur_len
= blob_len(a
) + sizeof(*a
);
261 ustream_consume(s
, cur_len
);
265 static void logread_fd_state_cb(struct ustream
*s
)
268 logd_conn_tries
= LOGD_CONNECT_RETRY
;
272 static void logread_fd_cb(struct ubus_request
*req
, int fd
)
274 static struct ustream_fd test_fd
;
276 memset(&test_fd
, 0, sizeof(test_fd
));
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
);
283 static void logread_setup_output(void)
285 if (sender
.fd
|| sender
.cb
)
288 if (log_ip
&& log_port
) {
289 openlog("logread", LOG_PID
, LOG_DAEMON
);
291 sender
.cb
= log_handle_fd
;
292 retry
.cb
= log_handle_reconnect
;
293 uloop_timeout_set(&retry
, 1000);
294 } else if (log_file
) {
296 sender
.fd
= open(log_file
, O_CREAT
| O_WRONLY
| O_APPEND
, 0600);
298 fprintf(stderr
, "failed to open %s: %m\n", log_file
);
302 sender
.fd
= STDOUT_FILENO
;
306 int main(int argc
, char **argv
)
308 struct ubus_context
*ctx
;
310 const char *ubus_socket
= NULL
;
311 int ch
, ret
, lines
= 0;
312 static struct blob_buf b
;
314 signal(SIGPIPE
, SIG_IGN
);
316 while ((ch
= getopt(argc
, argv
, "u0fcs:l:z:Z:r:F:p:S:P:h:e:t")) != -1) {
322 log_trailer_null
= 1;
325 ubus_socket
= optarg
;
329 log_port
= argv
[optind
++];
344 lines
= atoi(optarg
);
347 id
= strtoul(optarg
, NULL
, 0) & 0x1f;
348 facility_include
|= (1 << id
);
351 id
= strtoul(optarg
, NULL
, 0) & 0x1f;
352 facility_exclude
|= (1 << id
);
355 log_size
= atoi(optarg
);
364 if (!regcomp(®exp_preg
, optarg
, REG_NOSUB
)) {
365 regexp_pattern
= optarg
;
377 ctx
= ubus_connect(ubus_socket
);
379 fprintf(stderr
, "Failed to connect to ubus\n");
384 if (log_follow
&& pid_file
) {
385 FILE *fp
= fopen(pid_file
, "w+");
387 fprintf(fp
, "%d", getpid());
392 blob_buf_init(&b
, 0);
393 blobmsg_add_u8(&b
, "stream", 1);
394 blobmsg_add_u8(&b
, "oneshot", !log_follow
);
396 blobmsg_add_u32(&b
, "lines", lines
);
398 blobmsg_add_u32(&b
, "lines", 0);
400 /* ugly ugly ugly ... we need a real reconnect logic */
402 struct ubus_request req
= { 0 };
404 ret
= ubus_lookup_id(ctx
, "log", &id
);
406 fprintf(stderr
, "Failed to find log object: %s\n", ubus_strerror(ret
));
411 logread_setup_output();
413 ubus_invoke_async(ctx
, id
, "read", b
.head
, &req
);
414 req
.fd_cb
= logread_fd_cb
;
415 ubus_complete_request_async(ctx
, &req
);
419 } while (logd_conn_tries
--);
424 if (log_follow
&& pid_file
)