]> git.proxmox.com Git - mirror_frr.git/blame - lib/zlog_live.c
lib: fix live log fields for crashlog
[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);
1609a9d6 137 struct zlog_live_hdr hdr[1] = {};
0798d276
DL
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
1609a9d6 146 clock_gettime(CLOCK_REALTIME, &ts);
0798d276
DL
147
148 hdr->ts_sec = ts.tv_sec;
149 hdr->ts_nsec = ts.tv_nsec;
150 hdr->prio = LOG_CRIT;
0798d276 151 hdr->textlen = len;
0798d276
DL
152
153 iov->iov_base = (char *)hdr;
154 iov->iov_len = sizeof(hdr);
155 iov++;
156
157 iov->iov_base = (char *)text;
158 iov->iov_len = len;
159 iov++;
160
161 writev(fd, iovs, iov - iovs);
162}
163
164void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd)
165{
166 int sockets[2];
167 struct zlt_live *zte;
168 struct zlog_target *zt;
169
170 if (cfg->target)
171 zlog_live_close(cfg);
172
173 *other_fd = -1;
174 if (prio_min == ZLOG_DISABLED)
175 return;
176
177 /* the only reason for SEQPACKET here is getting close notifications.
178 * otherwise if you open a bunch of vtysh connections with live logs
179 * and close them all, the fds will stick around until we get an error
180 * when trying to log something to them at some later point -- which
181 * eats up fds and might be *much* later for some daemons.
182 */
183 if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) {
184 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
185 zlog_warn("%% could not open socket pair: %m");
186 return;
187 }
188 } else
189 /* SEQPACKET only: try to zap read direction */
190 shutdown(sockets[0], SHUT_RD);
191
192 *other_fd = sockets[1];
193
194 zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
195 zte = container_of(zt, struct zlt_live, zt);
196 cfg->target = zte;
197
198 zte->fd = sockets[0];
199 zte->zt.prio_min = prio_min;
200 zte->zt.logfn = zlog_live;
201 zte->zt.logfn_sigsafe = zlog_live_sigsafe;
202
203 zlog_target_replace(NULL, zt);
204}
205
206void zlog_live_close(struct zlog_live_cfg *cfg)
207{
208 struct zlt_live *zte;
209 int fd;
210
211 if (!cfg->target)
212 return;
213
214 zte = cfg->target;
215 cfg->target = NULL;
216
217 fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
218
219 if (fd >= 0) {
220 rcu_close(&zte->head_close, fd);
221 zlog_target_replace(&zte->zt, NULL);
222 }
223 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
224}
225
226void zlog_live_disown(struct zlog_live_cfg *cfg)
227{
228 struct zlt_live *zte;
229 uint_fast32_t state;
230
231 if (!cfg->target)
232 return;
233
234 zte = cfg->target;
235 cfg->target = NULL;
236
237 state = STATE_NORMAL;
238 atomic_compare_exchange_strong_explicit(
239 &zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
240 memory_order_relaxed);
241 if (state == STATE_FD_DEAD)
242 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
243}