Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2016, Citrix Systems, Inc.
3 : : *
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions are met:
8 : : *
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer.
11 : : * 2. Redistributions in binary form must reproduce the above copyright
12 : : * notice, this list of conditions and the following disclaimer in the
13 : : * documentation and/or other materials provided with the distribution.
14 : : * 3. Neither the name of the copyright holder nor the names of its
15 : : * contributors may be used to endorse or promote products derived from
16 : : * this software without specific prior written permission.
17 : : *
18 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 : : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
22 : : * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
23 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
24 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
25 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 : : */
30 : :
31 : : /*
32 : : * A non-blocking, buffered BSD syslog client.
33 : : *
34 : : * http://www.ietf.org/rfc/rfc3164.txt (FIXME: Read this.)
35 : : */
36 : :
37 : : #ifdef HAVE_CONFIG_H
38 : : #include "config.h"
39 : : #endif
40 : :
41 : : #define _ISOC99_SOURCE
42 : : #include <stdlib.h>
43 : : #include <stdio.h>
44 : : #include <errno.h>
45 : : #include <unistd.h>
46 : : #include <time.h>
47 : : #include <fcntl.h>
48 : : #include <stdarg.h>
49 : : #include <sys/mman.h>
50 : : #include <sys/socket.h>
51 : : #include <sys/un.h>
52 : :
53 : : #include "tapdisk-server.h"
54 : : #include "tapdisk-syslog.h"
55 : : #include "tapdisk-utils.h"
56 : : #include "timeout-math.h"
57 : :
58 : : #define MIN(a,b) (((a) < (b)) ? (a) : (b))
59 : :
60 : : static int tapdisk_syslog_sock_send(td_syslog_t *log,
61 : : const void *msg, size_t size);
62 : : static int tapdisk_syslog_sock_connect(td_syslog_t *log);
63 : :
64 : : static void tapdisk_syslog_sock_mask(td_syslog_t *log);
65 : : static void tapdisk_syslog_sock_unmask(td_syslog_t *log);
66 : :
67 : : static const struct sockaddr_un syslog_addr = {
68 : : .sun_family = AF_UNIX,
69 : : .sun_path = "/dev/log"
70 : : };
71 : :
72 : : #define RING_PTR(_log, _idx) \
73 : : (&(_log)->ring[(_idx) % (_log)->ringsz])
74 : :
75 : : #define RING_FREE(_log) \
76 : : ((_log)->ringsz - ((_log)->prod - (_log)->cons))
77 : :
78 : : /*
79 : : * NB. Ring buffer.
80 : : *
81 : : * We allocate a number of pages as indicated by @bufsz during
82 : : * initialization. From that, 1K is reserved for message staging, the
83 : : * rest is cyclic ring space.
84 : : *
85 : : * All producer/consumer offsets wrap on size_t range, not buffer
86 : : * size. Hence the RING() macros.
87 : : */
88 : :
89 : : static void
90 : : __tapdisk_syslog_ring_init(td_syslog_t *log)
91 : : {
92 : 0 : log->buf = NULL;
93 : 0 : log->bufsz = 0;
94 : 0 : log->msg = NULL;
95 : 0 : log->ring = NULL;
96 : 0 : log->ringsz = 0;
97 : : }
98 : :
99 : : static inline size_t
100 : : page_align(size_t size)
101 : : {
102 : 0 : size_t page_size = sysconf(_SC_PAGE_SIZE);
103 : 0 : return (size + page_size - 1) & ~(page_size - 1);
104 : : }
105 : :
106 : : static void
107 : 0 : tapdisk_syslog_ring_uninit(td_syslog_t *log)
108 : : {
109 [ # # ]: 0 : if (log->buf)
110 : 0 : munmap(log->buf, log->bufsz);
111 : :
112 : : __tapdisk_syslog_ring_init(log);
113 : 0 : }
114 : :
115 : : static int
116 : 0 : tapdisk_syslog_ring_init(td_syslog_t *log, size_t size)
117 : : {
118 : : int prot, flags, err;
119 : :
120 : : __tapdisk_syslog_ring_init(log);
121 : :
122 : 0 : log->bufsz = page_align(size);
123 : :
124 : 0 : prot = PROT_READ|PROT_WRITE;
125 : 0 : flags = MAP_ANONYMOUS|MAP_PRIVATE;
126 : :
127 : 0 : log->buf = mmap(NULL, log->bufsz, prot, flags, -1, 0);
128 [ # # ]: 0 : if (log->buf == MAP_FAILED) {
129 : 0 : log->buf = NULL;
130 : 0 : err = -ENOMEM;
131 : 0 : goto fail;
132 : : }
133 : :
134 : 0 : err = mlock(log->buf, size);
135 [ # # ]: 0 : if (err) {
136 : 0 : err = -errno;
137 : 0 : goto fail;
138 : : }
139 : :
140 : 0 : log->msg = log->buf;
141 : 0 : log->ring = log->buf + TD_SYSLOG_PACKET_MAX;
142 : 0 : log->ringsz = size - TD_SYSLOG_PACKET_MAX;
143 : :
144 : 0 : return 0;
145 : :
146 : : fail:
147 : 0 : tapdisk_syslog_ring_uninit(log);
148 : :
149 : 0 : return err;
150 : : }
151 : :
152 : : static int
153 : 0 : tapdisk_syslog_ring_write_str(td_syslog_t *log, const char *msg, size_t len)
154 : : {
155 : : size_t size, prod, i;
156 : :
157 : 0 : len = MIN(len, TD_SYSLOG_PACKET_MAX);
158 : 0 : size = len + 1;
159 : :
160 [ # # ]: 0 : if (size > RING_FREE(log))
161 : : return -ENOBUFS;
162 : :
163 : : prod = log->prod;
164 : :
165 [ # # ]: 0 : for (i = 0; i < len; ++i) {
166 : : char c;
167 : :
168 : 0 : c = msg[i];
169 [ # # ]: 0 : if (c == 0)
170 : : break;
171 : :
172 : 0 : *RING_PTR(log, prod) = c;
173 : 0 : prod++;
174 : : }
175 : :
176 : 0 : *RING_PTR(log, prod) = 0;
177 : :
178 : 0 : log->prod = prod + 1;
179 : :
180 : 0 : return 0;
181 : : }
182 : :
183 : : static ssize_t
184 : 0 : tapdisk_syslog_ring_read_pkt(td_syslog_t *log, char *msg, size_t size)
185 : : {
186 : : size_t cons;
187 : : ssize_t sz;
188 : :
189 : 0 : size = MIN(size, TD_SYSLOG_PACKET_MAX);
190 : :
191 : 0 : sz = 0;
192 : 0 : cons = log->cons;
193 : :
194 [ # # ]: 0 : while (sz < size) {
195 : : char c;
196 : :
197 [ # # ]: 0 : if (cons == log->prod)
198 : : break;
199 : :
200 : 0 : c = *RING_PTR(log, cons);
201 : 0 : msg[sz++] = c;
202 : 0 : cons++;
203 : :
204 [ # # ]: 0 : if (c == 0)
205 : : break;
206 : : }
207 : :
208 : 0 : return sz - 1;
209 : : }
210 : :
211 : : static int
212 : 0 : tapdisk_syslog_ring_dispatch_one(td_syslog_t *log)
213 : : {
214 : : size_t len;
215 : : int err;
216 : :
217 : 0 : len = tapdisk_syslog_ring_read_pkt(log, log->msg,
218 : : TD_SYSLOG_PACKET_MAX);
219 [ # # ]: 0 : if (len == -1)
220 : : return -ENOMSG;
221 : :
222 : 0 : err = tapdisk_syslog_sock_send(log, log->msg, len);
223 : :
224 [ # # ]: 0 : if (err == -EAGAIN)
225 : : return err;
226 : :
227 [ # # ]: 0 : if (err)
228 : : goto fail;
229 : :
230 : : done:
231 : 0 : log->cons += len + 1;
232 : 0 : return 0;
233 : :
234 : : fail:
235 : 0 : log->stats.fails++;
236 : 0 : goto done;
237 : : }
238 : :
239 : : static void
240 : 0 : tapdisk_syslog_ring_warning(td_syslog_t *log)
241 : : {
242 : : int n, err;
243 : :
244 : 0 : n = log->oom;
245 : 0 : log->oom = 0;
246 : :
247 : 0 : err = tapdisk_syslog(log, TLOG_WARN,
248 : : "tapdisk-syslog: %d messages dropped", n);
249 [ # # ]: 0 : if (err)
250 : 0 : log->oom = n;
251 : 0 : }
252 : :
253 : : static void
254 : 0 : tapdisk_syslog_ring_dispatch(td_syslog_t *log)
255 : : {
256 : : int err;
257 : :
258 : : do {
259 : 0 : err = tapdisk_syslog_ring_dispatch_one(log);
260 [ # # ]: 0 : } while (!err);
261 : :
262 [ # # ]: 0 : if (log->oom)
263 : 0 : tapdisk_syslog_ring_warning(log);
264 : 0 : }
265 : :
266 : : static int
267 : 0 : tapdisk_syslog_vsprintf(char *buf, size_t size,
268 : : int prio, int facility, const struct timeval *tv,
269 : : const char *ident, const char *fmt, va_list ap)
270 : : {
271 : : char tsbuf[TD_SYSLOG_STRTIME_LEN+1];
272 : : size_t len;
273 : :
274 : : /*
275 : : * PKT := PRI HEADER MSG
276 : : * PRI := "<" {"0" .. "9"} ">"
277 : : * HEADER := TIMESTAMP HOSTNAME
278 : : * MSG := <TAG> <SEP> <CONTENT>
279 : : * SEP := ":" | " " | "["
280 : : */
281 : :
282 : 0 : tapdisk_syslog_strftime(tsbuf, sizeof(tsbuf), tv);
283 : :
284 : 0 : len = 0;
285 : :
286 : : /* NB. meant to work with c99 null buffers */
287 : :
288 [ # # ][ # # ]: 0 : len += snprintf(buf ? buf + len : NULL, buf ? size - len : 0,
289 : : "<%d>%s %s: ", prio | facility, tsbuf, ident);
290 : :
291 [ # # ]: 0 : if (LOG_WARNING == prio)
292 [ # # ][ # # ]: 0 : len += snprintf(buf ? buf + len : NULL, buf ? size - len : 0,
293 : : "tap-err:");
294 : :
295 [ # # ][ # # ]: 0 : len += vsnprintf(buf ? buf + len : NULL, buf ? size - len : 0,
296 : : fmt, ap);
297 : :
298 : 0 : return MIN(len, size);
299 : : }
300 : :
301 : : /*
302 : : * NB. Sockets.
303 : : *
304 : : * Syslog is based on a connectionless (DGRAM) unix transport.
305 : : *
306 : : * While it is reliable, we cannot block on syslogd because -- as with
307 : : * any IPC in tapdisk -- we could deadlock in page I/O writeback.
308 : : * Hence the syslog(3) avoidance on the datapath, which this code
309 : : * facilitates.
310 : : *
311 : : * This type of socket has a single (global) receive buffer on
312 : : * syslogd's end, but no send buffer at all. The does just that:
313 : : * headroom on the sender side.
314 : : *
315 : : * The transport is rather stateless, but we still need to connect()
316 : : * the socket, or select() will find no receive buffer to block
317 : : * on. While we never disconnect, connections are unreliable because
318 : : * syslog may shut down.
319 : : *
320 : : * Reconnection will be attempted with every user message submitted.
321 : : * Any send() or connect() failure other than EAGAIN discards the
322 : : * message. Also, the write event handler will go on to discard any
323 : : * remaining ring contents as well, once the socket is disconnected.
324 : : *
325 : : * In summary, no attempts to mask service blackouts in here.
326 : : */
327 : :
328 : : int
329 : 0 : tapdisk_vsyslog(td_syslog_t *log, int prio, const char *fmt, va_list ap)
330 : : {
331 : : struct timeval now;
332 : : size_t len;
333 : : int err;
334 : :
335 : 0 : gettimeofday(&now, NULL);
336 : :
337 : 0 : len = tapdisk_syslog_vsprintf(log->msg, TD_SYSLOG_PACKET_MAX,
338 : : prio, log->facility,
339 : 0 : &now, log->ident, fmt, ap);
340 : :
341 : 0 : log->stats.count += 1;
342 : 0 : log->stats.bytes += len;
343 : :
344 [ # # ]: 0 : if (log->cons != log->prod)
345 : : goto busy;
346 : :
347 : : send:
348 : 0 : err = tapdisk_syslog_sock_send(log, log->msg, len);
349 [ # # ]: 0 : if (!err)
350 : 0 : return 0;
351 : :
352 [ # # ]: 0 : if (err == -ENOTCONN) {
353 : 0 : err = tapdisk_syslog_sock_connect(log);
354 [ # # ]: 0 : if (!err)
355 : : goto send;
356 : : }
357 : :
358 [ # # ]: 0 : if (err != -EAGAIN)
359 : : goto fail;
360 : :
361 : 0 : tapdisk_syslog_sock_unmask(log);
362 : :
363 : : busy:
364 [ # # ]: 0 : if (log->oom) {
365 : : err = -ENOBUFS;
366 : : goto oom;
367 : : }
368 : :
369 : 0 : err = tapdisk_syslog_ring_write_str(log, log->msg, len);
370 [ # # ]: 0 : if (!err)
371 : : return 0;
372 : :
373 : 0 : log->oom_tv = now;
374 : :
375 : : oom:
376 : 0 : log->oom++;
377 : 0 : log->stats.drops++;
378 : 0 : return err;
379 : :
380 : : fail:
381 : 0 : log->stats.fails++;
382 : 0 : return err;
383 : : }
384 : :
385 : : int
386 : 0 : tapdisk_syslog(td_syslog_t *log, int prio, const char *fmt, ...)
387 : : {
388 : : va_list ap;
389 : : int err;
390 : :
391 : 0 : va_start(ap, fmt);
392 : 0 : err = tapdisk_vsyslog(log, prio, fmt, ap);
393 : 0 : va_end(ap);
394 : :
395 : 0 : return err;
396 : : }
397 : :
398 : : static int
399 : 0 : tapdisk_syslog_sock_send(td_syslog_t *log, const void *msg, size_t size)
400 : : {
401 : : ssize_t n;
402 : :
403 : 0 : log->stats.xmits++;
404 : :
405 : 0 : n = send(log->sock, msg, size, MSG_DONTWAIT);
406 [ # # ]: 0 : if (n < 0)
407 : 0 : return -errno;
408 : :
409 : : return 0;
410 : : }
411 : :
412 : : static void
413 : 0 : tapdisk_syslog_sock_event(event_id_t id, char mode, void *private)
414 : : {
415 : 0 : td_syslog_t *log = private;
416 : :
417 : 0 : tapdisk_syslog_ring_dispatch(log);
418 : :
419 [ # # ]: 0 : if (log->cons == log->prod)
420 : 0 : tapdisk_syslog_sock_mask(log);
421 : 0 : }
422 : :
423 : : static void
424 : 0 : __tapdisk_syslog_sock_init(td_syslog_t *log)
425 : : {
426 : 0 : log->sock = -1;
427 : 0 : log->event_id = -1;
428 : 0 : }
429 : :
430 : : static void
431 : 0 : tapdisk_syslog_sock_close(td_syslog_t *log)
432 : : {
433 [ # # ]: 0 : if (log->sock >= 0)
434 : 0 : close(log->sock);
435 : :
436 [ # # ]: 0 : if (log->event_id >= 0)
437 : 0 : tapdisk_server_unregister_event(log->event_id);
438 : :
439 : 0 : __tapdisk_syslog_sock_init(log);
440 : 0 : }
441 : :
442 : : static int
443 : 0 : tapdisk_syslog_sock_open(td_syslog_t *log)
444 : : {
445 : : event_id_t id;
446 : : int s, err;
447 : :
448 : 0 : __tapdisk_syslog_sock_init(log);
449 : :
450 : 0 : s = socket(PF_UNIX, SOCK_DGRAM, 0);
451 [ # # ]: 0 : if (s < 0) {
452 : 0 : err = -errno;
453 : 0 : goto fail;
454 : : }
455 : :
456 : 0 : log->sock = s;
457 : :
458 : : #if 0
459 : : err = fcntl(s, F_SETFL, O_NONBLOCK);
460 : : if (err < 0) {
461 : : err = -errno;
462 : : goto fail;
463 : : }
464 : : #endif
465 : :
466 : 0 : id = tapdisk_server_register_event(SCHEDULER_POLL_WRITE_FD,
467 : 0 : s, TV_ZERO,
468 : : tapdisk_syslog_sock_event,
469 : : log);
470 [ # # ]: 0 : if (id < 0) {
471 : : err = id;
472 : : goto fail;
473 : : }
474 : :
475 : 0 : log->event_id = id;
476 : :
477 : 0 : tapdisk_syslog_sock_mask(log);
478 : :
479 : 0 : return 0;
480 : :
481 : : fail:
482 : 0 : tapdisk_syslog_sock_close(log);
483 : 0 : return err;
484 : : }
485 : :
486 : : static int
487 : 0 : tapdisk_syslog_sock_connect(td_syslog_t *log)
488 : : {
489 : : int err;
490 : :
491 : 0 : err = connect(log->sock, &syslog_addr, sizeof(syslog_addr));
492 [ # # ]: 0 : if (err < 0)
493 : 0 : err = -errno;
494 : :
495 : 0 : return err;
496 : : }
497 : :
498 : : static void
499 : 0 : tapdisk_syslog_sock_mask(td_syslog_t *log)
500 : : {
501 : 0 : tapdisk_server_mask_event(log->event_id, 1);
502 : 0 : }
503 : :
504 : : static void
505 : 0 : tapdisk_syslog_sock_unmask(td_syslog_t *log)
506 : : {
507 : 0 : tapdisk_server_mask_event(log->event_id, 0);
508 : 0 : }
509 : :
510 : : void
511 : 0 : __tapdisk_syslog_init(td_syslog_t *log)
512 : : {
513 : : memset(log, 0, sizeof(td_syslog_t));
514 : 0 : __tapdisk_syslog_sock_init(log);
515 : : __tapdisk_syslog_ring_init(log);
516 : 0 : }
517 : :
518 : : void
519 : 0 : tapdisk_syslog_close(td_syslog_t *log)
520 : : {
521 : 0 : tapdisk_syslog_ring_uninit(log);
522 : 0 : tapdisk_syslog_sock_close(log);
523 : :
524 [ # # ]: 0 : if (log->ident)
525 : 0 : free(log->ident);
526 : :
527 : 0 : __tapdisk_syslog_init(log);
528 : 0 : }
529 : :
530 : : int
531 : 0 : tapdisk_syslog_open(td_syslog_t *log, const char *ident, int facility, size_t bufsz)
532 : : {
533 : : int err;
534 : :
535 : 0 : __tapdisk_syslog_init(log);
536 : :
537 : 0 : log->facility = facility;
538 [ # # ]: 0 : log->ident = ident ? strndup(ident, TD_SYSLOG_IDENT_MAX) : NULL;
539 : :
540 : 0 : err = tapdisk_syslog_sock_open(log);
541 [ # # ]: 0 : if (err)
542 : : goto fail;
543 : :
544 : 0 : err = tapdisk_syslog_ring_init(log, bufsz);
545 [ # # ]: 0 : if (err)
546 : : goto fail;
547 : :
548 : : return 0;
549 : :
550 : : fail:
551 : 0 : tapdisk_syslog_close(log);
552 : :
553 : 0 : return err;
554 : : }
555 : :
556 : : void
557 : 0 : tapdisk_syslog_stats(td_syslog_t *log, int prio)
558 : : {
559 : 0 : struct _td_syslog_stats *s = &log->stats;
560 : :
561 : 0 : tapdisk_syslog(log, prio,
562 : : "tapdisk-syslog: %llu messages, %llu bytes, "
563 : : "xmits: %llu, failed: %llu, dropped: %llu",
564 : : s->count, s->bytes,
565 : : s->xmits, s->fails, s->drops);
566 : 0 : }
567 : :
568 : : void
569 : 0 : tapdisk_syslog_flush(td_syslog_t *log)
570 : : {
571 [ # # ]: 0 : while (log->cons != log->prod)
572 : 0 : tapdisk_server_iterate();
573 : 0 : }
|