]> git.proxmox.com Git - mirror_frr.git/blame - lib/zlog_live.c
Merge pull request #11003 from anlancs/bgpd-mh-trival-remove
[mirror_frr.git] / lib / zlog_live.c
CommitLineData
0798d276
DL
1/*
2 * Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc.
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
17#include "zebra.h"
18
19#include "zlog_live.h"
20
21#include "memory.h"
22#include "frrcu.h"
23#include "zlog.h"
24#include "printfrr.h"
2eda953a 25#include "network.h"
0798d276
DL
26
27DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target");
28
29enum {
30 STATE_NORMAL = 0,
31 STATE_FD_DEAD,
32 STATE_DISOWNED,
33};
34
35struct zlt_live {
36 struct zlog_target zt;
37
38 atomic_uint_fast32_t fd;
39 struct rcu_head_close head_close;
40 struct rcu_head head_self;
41
42 atomic_uint_fast32_t state;
a4af82ee 43 atomic_uint_fast32_t lost_msgs;
0798d276
DL
44};
45
46static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[],
47 size_t nmsgs)
48{
49 struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
50 struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs;
51 struct mmsghdr mmhs[nmsgs], *mmh = mmhs;
52 struct iovec iovs[nmsgs * 3], *iov = iovs;
53 struct timespec ts;
54 size_t i, textlen;
55 int fd;
56 uint_fast32_t state;
57
58 fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
59
60 if (fd < 0)
61 return;
62
63 memset(mmhs, 0, sizeof(mmhs));
64 memset(hdrs, 0, sizeof(hdrs));
65
66 for (i = 0; i < nmsgs; i++) {
67 const struct fmt_outpos *argpos;
834585bd 68 size_t n_argpos, texthdrlen;
0798d276
DL
69 struct zlog_msg *msg = msgs[i];
70 int prio = zlog_msg_prio(msg);
834585bd
DL
71 const struct xref_logmsg *xref;
72 intmax_t pid, tid;
0798d276
DL
73
74 if (prio > zt->prio_min)
75 continue;
76
834585bd 77 zlog_msg_args(msg, &texthdrlen, &n_argpos, &argpos);
0798d276
DL
78
79 mmh->msg_hdr.msg_iov = iov;
80
81 iov->iov_base = hdr;
82 iov->iov_len = sizeof(*hdr);
83 iov++;
84
85 if (n_argpos) {
86 iov->iov_base = (char *)argpos;
87 iov->iov_len = sizeof(*argpos) * n_argpos;
88 iov++;
89 }
90
91 iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
92 iov->iov_len = textlen;
93 iov++;
94
95 zlog_msg_tsraw(msg, &ts);
834585bd
DL
96 zlog_msg_pid(msg, &pid, &tid);
97 xref = zlog_msg_xref(msg);
0798d276
DL
98
99 hdr->ts_sec = ts.tv_sec;
100 hdr->ts_nsec = ts.tv_nsec;
834585bd
DL
101 hdr->pid = pid;
102 hdr->tid = tid;
a4af82ee
DL
103 hdr->lost_msgs = atomic_load_explicit(&zte->lost_msgs,
104 memory_order_relaxed);
834585bd 105 hdr->prio = prio;
0798d276
DL
106 hdr->flags = 0;
107 hdr->textlen = textlen;
834585bd 108 hdr->texthdrlen = texthdrlen;
0798d276 109 hdr->n_argpos = n_argpos;
834585bd
DL
110 if (xref) {
111 memcpy(hdr->uid, xref->xref.xrefdata->uid,
112 sizeof(hdr->uid));
113 hdr->ec = xref->ec;
114 } else {
115 memset(hdr->uid, 0, sizeof(hdr->uid));
116 hdr->ec = 0;
117 }
118 hdr->hdrlen = sizeof(*hdr) + sizeof(*argpos) * n_argpos;
0798d276
DL
119
120 mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov;
121 mmh++;
122 hdr++;
123 }
124
125 size_t msgtotal = mmh - mmhs;
126 ssize_t sent;
127
128 for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) {
129 sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0);
130
a4af82ee
DL
131 if (sent <= 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
132 atomic_fetch_add_explicit(&zte->lost_msgs,
133 msgtotal - msgpos,
134 memory_order_relaxed);
2eda953a 135 break;
a4af82ee 136 }
0798d276
DL
137 if (sent <= 0)
138 goto out_err;
139 }
140 return;
141
142out_err:
143 fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
144 if (fd < 0)
145 return;
146
147 rcu_close(&zte->head_close, fd);
148 zlog_target_replace(zt, NULL);
149
150 state = STATE_NORMAL;
151 atomic_compare_exchange_strong_explicit(
152 &zte->state, &state, STATE_FD_DEAD, memory_order_relaxed,
153 memory_order_relaxed);
154 if (state == STATE_DISOWNED)
155 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
156}
157
158static void zlog_live_sigsafe(struct zlog_target *zt, const char *text,
159 size_t len)
160{
161 struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
1609a9d6 162 struct zlog_live_hdr hdr[1] = {};
0798d276
DL
163 struct iovec iovs[2], *iov = iovs;
164 struct timespec ts;
165 int fd;
166
167 fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
168 if (fd < 0)
169 return;
170
1609a9d6 171 clock_gettime(CLOCK_REALTIME, &ts);
0798d276
DL
172
173 hdr->ts_sec = ts.tv_sec;
174 hdr->ts_nsec = ts.tv_nsec;
175 hdr->prio = LOG_CRIT;
0798d276 176 hdr->textlen = len;
0798d276
DL
177
178 iov->iov_base = (char *)hdr;
179 iov->iov_len = sizeof(hdr);
180 iov++;
181
182 iov->iov_base = (char *)text;
183 iov->iov_len = len;
184 iov++;
185
186 writev(fd, iovs, iov - iovs);
187}
188
189void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd)
190{
191 int sockets[2];
0798d276
DL
192
193 if (cfg->target)
194 zlog_live_close(cfg);
195
196 *other_fd = -1;
197 if (prio_min == ZLOG_DISABLED)
198 return;
199
200 /* the only reason for SEQPACKET here is getting close notifications.
201 * otherwise if you open a bunch of vtysh connections with live logs
202 * and close them all, the fds will stick around until we get an error
203 * when trying to log something to them at some later point -- which
204 * eats up fds and might be *much* later for some daemons.
205 */
206 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) {
207 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
208 zlog_warn("%% could not open socket pair: %m");
209 return;
210 }
211 } else
212 /* SEQPACKET only: try to zap read direction */
213 shutdown(sockets[0], SHUT_RD);
214
215 *other_fd = sockets[1];
3bcdae10
DL
216 zlog_live_open_fd(cfg, prio_min, sockets[0]);
217}
218
219void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd)
220{
221 struct zlt_live *zte;
222 struct zlog_target *zt;
223
224 if (cfg->target)
225 zlog_live_close(cfg);
0798d276
DL
226
227 zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
228 zte = container_of(zt, struct zlt_live, zt);
229 cfg->target = zte;
230
3bcdae10
DL
231 set_nonblocking(fd);
232 zte->fd = fd;
0798d276
DL
233 zte->zt.prio_min = prio_min;
234 zte->zt.logfn = zlog_live;
235 zte->zt.logfn_sigsafe = zlog_live_sigsafe;
236
237 zlog_target_replace(NULL, zt);
238}
239
240void zlog_live_close(struct zlog_live_cfg *cfg)
241{
242 struct zlt_live *zte;
243 int fd;
244
245 if (!cfg->target)
246 return;
247
248 zte = cfg->target;
249 cfg->target = NULL;
250
251 fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
252
253 if (fd >= 0) {
254 rcu_close(&zte->head_close, fd);
255 zlog_target_replace(&zte->zt, NULL);
256 }
257 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
258}
259
260void zlog_live_disown(struct zlog_live_cfg *cfg)
261{
262 struct zlt_live *zte;
263 uint_fast32_t state;
264
265 if (!cfg->target)
266 return;
267
268 zte = cfg->target;
269 cfg->target = NULL;
270
271 state = STATE_NORMAL;
272 atomic_compare_exchange_strong_explicit(
273 &zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
274 memory_order_relaxed);
275 if (state == STATE_FD_DEAD)
276 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
277}