]> git.proxmox.com Git - mirror_frr.git/blame - lib/zlog_live.c
lib: implement `terminal monitor` for vtysh
[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"
25
26DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target");
27
28enum {
29 STATE_NORMAL = 0,
30 STATE_FD_DEAD,
31 STATE_DISOWNED,
32};
33
34struct zlt_live {
35 struct zlog_target zt;
36
37 atomic_uint_fast32_t fd;
38 struct rcu_head_close head_close;
39 struct rcu_head head_self;
40
41 atomic_uint_fast32_t state;
42};
43
44static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[],
45 size_t nmsgs)
46{
47 struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
48 struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs;
49 struct mmsghdr mmhs[nmsgs], *mmh = mmhs;
50 struct iovec iovs[nmsgs * 3], *iov = iovs;
51 struct timespec ts;
52 size_t i, textlen;
53 int fd;
54 uint_fast32_t state;
55
56 fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
57
58 if (fd < 0)
59 return;
60
61 memset(mmhs, 0, sizeof(mmhs));
62 memset(hdrs, 0, sizeof(hdrs));
63
64 for (i = 0; i < nmsgs; i++) {
65 const struct fmt_outpos *argpos;
66 size_t n_argpos, arghdrlen;
67 struct zlog_msg *msg = msgs[i];
68 int prio = zlog_msg_prio(msg);
69
70 if (prio > zt->prio_min)
71 continue;
72
73 zlog_msg_args(msg, &arghdrlen, &n_argpos, &argpos);
74
75 mmh->msg_hdr.msg_iov = iov;
76
77 iov->iov_base = hdr;
78 iov->iov_len = sizeof(*hdr);
79 iov++;
80
81 if (n_argpos) {
82 iov->iov_base = (char *)argpos;
83 iov->iov_len = sizeof(*argpos) * n_argpos;
84 iov++;
85 }
86
87 iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
88 iov->iov_len = textlen;
89 iov++;
90
91 zlog_msg_tsraw(msg, &ts);
92
93 hdr->ts_sec = ts.tv_sec;
94 hdr->ts_nsec = ts.tv_nsec;
95 hdr->prio = zlog_msg_prio(msg);
96 hdr->flags = 0;
97 hdr->textlen = textlen;
98 hdr->arghdrlen = arghdrlen;
99 hdr->n_argpos = n_argpos;
100
101 mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov;
102 mmh++;
103 hdr++;
104 }
105
106 size_t msgtotal = mmh - mmhs;
107 ssize_t sent;
108
109 for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) {
110 sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0);
111
112 if (sent <= 0)
113 goto out_err;
114 }
115 return;
116
117out_err:
118 fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
119 if (fd < 0)
120 return;
121
122 rcu_close(&zte->head_close, fd);
123 zlog_target_replace(zt, NULL);
124
125 state = STATE_NORMAL;
126 atomic_compare_exchange_strong_explicit(
127 &zte->state, &state, STATE_FD_DEAD, memory_order_relaxed,
128 memory_order_relaxed);
129 if (state == STATE_DISOWNED)
130 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
131}
132
133static void zlog_live_sigsafe(struct zlog_target *zt, const char *text,
134 size_t len)
135{
136 struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
137 struct zlog_live_hdr hdr[1];
138 struct iovec iovs[2], *iov = iovs;
139 struct timespec ts;
140 int fd;
141
142 fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
143 if (fd < 0)
144 return;
145
146 clock_gettime(CLOCK_MONOTONIC, &ts);
147
148 hdr->ts_sec = ts.tv_sec;
149 hdr->ts_nsec = ts.tv_nsec;
150 hdr->prio = LOG_CRIT;
151 hdr->flags = 0;
152 hdr->textlen = len;
153 hdr->n_argpos = 0;
154
155 iov->iov_base = (char *)hdr;
156 iov->iov_len = sizeof(hdr);
157 iov++;
158
159 iov->iov_base = (char *)text;
160 iov->iov_len = len;
161 iov++;
162
163 writev(fd, iovs, iov - iovs);
164}
165
166void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd)
167{
168 int sockets[2];
169 struct zlt_live *zte;
170 struct zlog_target *zt;
171
172 if (cfg->target)
173 zlog_live_close(cfg);
174
175 *other_fd = -1;
176 if (prio_min == ZLOG_DISABLED)
177 return;
178
179 /* the only reason for SEQPACKET here is getting close notifications.
180 * otherwise if you open a bunch of vtysh connections with live logs
181 * and close them all, the fds will stick around until we get an error
182 * when trying to log something to them at some later point -- which
183 * eats up fds and might be *much* later for some daemons.
184 */
185 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) {
186 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
187 zlog_warn("%% could not open socket pair: %m");
188 return;
189 }
190 } else
191 /* SEQPACKET only: try to zap read direction */
192 shutdown(sockets[0], SHUT_RD);
193
194 *other_fd = sockets[1];
195
196 zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
197 zte = container_of(zt, struct zlt_live, zt);
198 cfg->target = zte;
199
200 zte->fd = sockets[0];
201 zte->zt.prio_min = prio_min;
202 zte->zt.logfn = zlog_live;
203 zte->zt.logfn_sigsafe = zlog_live_sigsafe;
204
205 zlog_target_replace(NULL, zt);
206}
207
208void zlog_live_close(struct zlog_live_cfg *cfg)
209{
210 struct zlt_live *zte;
211 int fd;
212
213 if (!cfg->target)
214 return;
215
216 zte = cfg->target;
217 cfg->target = NULL;
218
219 fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
220
221 if (fd >= 0) {
222 rcu_close(&zte->head_close, fd);
223 zlog_target_replace(&zte->zt, NULL);
224 }
225 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
226}
227
228void zlog_live_disown(struct zlog_live_cfg *cfg)
229{
230 struct zlt_live *zte;
231 uint_fast32_t state;
232
233 if (!cfg->target)
234 return;
235
236 zte = cfg->target;
237 cfg->target = NULL;
238
239 state = STATE_NORMAL;
240 atomic_compare_exchange_strong_explicit(
241 &zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
242 memory_order_relaxed);
243 if (state == STATE_FD_DEAD)
244 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
245}