blob: f3e18ccb20c09bf0d49d84a8c6ef07a9e917950a [file] [log] [blame]
Willy Tarreau67b5a162019-08-11 16:38:56 +02001/*
2 * Event sink management
3 *
4 * Copyright (C) 2000-2019 Willy Tarreau - w@1wt.eu
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation, version 2.1
9 * exclusively.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21#include <sys/uio.h>
22#include <common/compat.h>
23#include <common/config.h>
24#include <common/ist.h>
25#include <common/mini-clist.h>
26#include <proto/log.h>
27#include <proto/sink.h>
28
29struct list sink_list = LIST_HEAD_INIT(sink_list);
30
31struct sink *sink_find(const char *name)
32{
33 struct sink *sink;
34
35 list_for_each_entry(sink, &sink_list, sink_list)
36 if (strcmp(sink->name, name) == 0)
37 return sink;
38 return NULL;
39}
40
41/* creates a new sink and adds it to the list, it's still generic and not fully
42 * initialized. Returns NULL on allocation failure. If another one already
43 * exists with the same name, it will be returned. The caller can detect it as
44 * a newly created one has type SINK_TYPE_NEW.
45 */
Willy Tarreau973e6622019-08-20 11:57:52 +020046static struct sink *__sink_new(const char *name, const char *desc, enum sink_fmt fmt)
Willy Tarreau67b5a162019-08-11 16:38:56 +020047{
48 struct sink *sink;
49
50 sink = sink_find(name);
51 if (sink)
52 goto end;
53
54 sink = malloc(sizeof(*sink));
55 if (!sink)
56 goto end;
57
58 sink->name = name;
59 sink->desc = desc;
60 sink->fmt = fmt;
61 sink->type = SINK_TYPE_NEW;
62 /* set defaults for syslog ones */
63 sink->syslog_facility = 0;
64 sink->syslog_minlvl = 0;
65 sink->maxlen = MAX_SYSLOG_LEN;
66 /* address will be filled by the caller if needed */
Willy Tarreau973e6622019-08-20 11:57:52 +020067 sink->ctx.fd = -1;
Willy Tarreau67b5a162019-08-11 16:38:56 +020068 sink->ctx.dropped = 0;
69 HA_RWLOCK_INIT(&sink->ctx.lock);
70 LIST_ADDQ(&sink_list, &sink->sink_list);
71 end:
72 return sink;
73}
74
Willy Tarreau973e6622019-08-20 11:57:52 +020075/* creates a sink called <name> of type FD associated to fd <fd>, format <fmt>,
76 * and description <desc>. Returns NULL on allocation failure or conflict.
77 * Perfect duplicates are merged (same type, fd, and name).
78 */
79struct sink *sink_new_fd(const char *name, const char *desc, enum sink_fmt fmt, int fd)
80{
81 struct sink *sink;
82
83 sink = __sink_new(name, desc, fmt);
84 if (!sink || (sink->type == SINK_TYPE_FD && sink->ctx.fd == fd))
85 goto end;
86
87 if (sink->type != SINK_TYPE_NEW) {
88 sink = NULL;
89 goto end;
90 }
91
92 sink->type = SINK_TYPE_FD;
93 sink->ctx.fd = fd;
94 end:
95 return sink;
96}
97
Willy Tarreau67b5a162019-08-11 16:38:56 +020098/* tries to send <nmsg> message parts (up to 8, ignored above) from message
99 * array <msg> to sink <sink>. Formating according to the sink's preference is
100 * done here. Lost messages are accounted for in the sink's counter.
101 */
102void sink_write(struct sink *sink, const struct ist msg[], size_t nmsg)
103{
104 struct iovec iovec[10];
105 char short_hdr[4];
106 size_t maxlen = sink->maxlen ? sink->maxlen : ~0;
107 size_t sent = 0;
108 int vec = 0;
109
110 /* keep one char for a possible trailing '\n' in any case */
111 maxlen--;
112
113 if (sink->fmt == SINK_FMT_SHORT) {
114 short_hdr[0] = '<';
115 short_hdr[1] = '0' + sink->syslog_minlvl;
116 short_hdr[2] = '>';
117
118 iovec[vec].iov_base = short_hdr;
119 iovec[vec].iov_len = MIN(maxlen, 3);
120 maxlen -= iovec[vec].iov_len;
121 vec++;
122 }
123
124 /* copy the remaining entries from the original message. Skip empty fields and
125 * truncate the whole message to maxlen.
126 */
127 while (nmsg && vec < (sizeof(iovec) / sizeof(iovec[0]) - 1)) {
128 iovec[vec].iov_base = msg->ptr;
129 iovec[vec].iov_len = MIN(maxlen, msg->len);
130 maxlen -= iovec[vec].iov_len;
131 if (iovec[vec].iov_len)
132 vec++;
133 msg++; nmsg--;
134 }
135
Willy Tarreau973e6622019-08-20 11:57:52 +0200136 if (sink->type == SINK_TYPE_FD) {
137 /* For the FD we always emit the trailing \n. It was already provisioned above. */
138 iovec[vec].iov_base = "\n";
139 iovec[vec].iov_len = 1;
140 vec++;
141
142 HA_RWLOCK_WRLOCK(LOGSRV_LOCK, &sink->ctx.lock);
143 sent = writev(sink->ctx.fd, iovec, vec);
144 HA_RWLOCK_WRUNLOCK(LOGSRV_LOCK, &sink->ctx.lock);
145 /* sent > 0 if the message was delivered */
146 }
Willy Tarreau67b5a162019-08-11 16:38:56 +0200147
148 /* account for errors now */
149 if (sent <= 0)
150 HA_ATOMIC_ADD(&sink->ctx.dropped, 1);
151}
152
Willy Tarreau973e6622019-08-20 11:57:52 +0200153static void sink_init()
154{
155 sink_new_fd("stdout", "standard output (fd#1)", SINK_FMT_RAW, 1);
156 sink_new_fd("stderr", "standard output (fd#2)", SINK_FMT_RAW, 2);
157}
158
159INITCALL0(STG_REGISTER, sink_init);
160
Willy Tarreau67b5a162019-08-11 16:38:56 +0200161/*
162 * Local variables:
163 * c-indent-level: 8
164 * c-basic-offset: 8
165 * End:
166 */