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