Line data Source code
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 : #include "network.h"
26 :
27 12 : DEFINE_MTYPE_STATIC(LOG, LOG_LIVE, "log vtysh live target");
28 :
29 : enum {
30 : STATE_NORMAL = 0,
31 : STATE_FD_DEAD,
32 : STATE_DISOWNED,
33 : };
34 :
35 : struct 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;
43 : atomic_uint_fast32_t lost_msgs;
44 : };
45 :
46 79 : static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[],
47 : size_t nmsgs)
48 79 : {
49 79 : struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
50 79 : struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs;
51 79 : struct mmsghdr mmhs[nmsgs], *mmh = mmhs;
52 79 : struct iovec iovs[nmsgs * 3], *iov = iovs;
53 79 : struct timespec ts;
54 79 : size_t i, textlen;
55 79 : int fd;
56 79 : uint_fast32_t state;
57 :
58 79 : fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
59 :
60 79 : if (fd < 0)
61 79 : return;
62 :
63 79 : memset(mmhs, 0, sizeof(mmhs));
64 79 : memset(hdrs, 0, sizeof(hdrs));
65 :
66 162 : for (i = 0; i < nmsgs; i++) {
67 83 : const struct fmt_outpos *argpos;
68 83 : size_t n_argpos, texthdrlen;
69 83 : struct zlog_msg *msg = msgs[i];
70 83 : int prio = zlog_msg_prio(msg);
71 83 : const struct xref_logmsg *xref;
72 83 : intmax_t pid, tid;
73 :
74 83 : if (prio > zt->prio_min)
75 0 : continue;
76 :
77 83 : zlog_msg_args(msg, &texthdrlen, &n_argpos, &argpos);
78 :
79 83 : mmh->msg_hdr.msg_iov = iov;
80 :
81 83 : iov->iov_base = hdr;
82 83 : iov->iov_len = sizeof(*hdr);
83 83 : iov++;
84 :
85 83 : if (n_argpos) {
86 75 : iov->iov_base = (char *)argpos;
87 75 : iov->iov_len = sizeof(*argpos) * n_argpos;
88 75 : iov++;
89 : }
90 :
91 83 : iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
92 83 : iov->iov_len = textlen;
93 83 : iov++;
94 :
95 83 : zlog_msg_tsraw(msg, &ts);
96 83 : zlog_msg_pid(msg, &pid, &tid);
97 83 : xref = zlog_msg_xref(msg);
98 :
99 83 : hdr->ts_sec = ts.tv_sec;
100 83 : hdr->ts_nsec = ts.tv_nsec;
101 83 : hdr->pid = pid;
102 83 : hdr->tid = tid;
103 83 : hdr->lost_msgs = atomic_load_explicit(&zte->lost_msgs,
104 : memory_order_relaxed);
105 83 : hdr->prio = prio;
106 83 : hdr->flags = 0;
107 83 : hdr->textlen = textlen;
108 83 : hdr->texthdrlen = texthdrlen;
109 83 : hdr->n_argpos = n_argpos;
110 83 : if (xref) {
111 83 : memcpy(hdr->uid, xref->xref.xrefdata->uid,
112 : sizeof(hdr->uid));
113 83 : hdr->ec = xref->ec;
114 : } else {
115 0 : memset(hdr->uid, 0, sizeof(hdr->uid));
116 0 : hdr->ec = 0;
117 : }
118 83 : hdr->hdrlen = sizeof(*hdr) + sizeof(*argpos) * n_argpos;
119 :
120 83 : mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov;
121 83 : mmh++;
122 83 : hdr++;
123 : }
124 :
125 79 : size_t msgtotal = mmh - mmhs;
126 79 : ssize_t sent;
127 :
128 158 : for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) {
129 79 : sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0);
130 :
131 79 : if (sent <= 0 && (errno == EAGAIN || errno == EWOULDBLOCK)) {
132 0 : atomic_fetch_add_explicit(&zte->lost_msgs,
133 : msgtotal - msgpos,
134 : memory_order_relaxed);
135 0 : break;
136 : }
137 0 : if (sent <= 0)
138 0 : goto out_err;
139 : }
140 : return;
141 :
142 0 : out_err:
143 0 : fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
144 0 : if (fd < 0)
145 : return;
146 :
147 0 : rcu_close(&zte->head_close, fd);
148 0 : zlog_target_replace(zt, NULL);
149 :
150 0 : state = STATE_NORMAL;
151 0 : atomic_compare_exchange_strong_explicit(
152 : &zte->state, &state, STATE_FD_DEAD, memory_order_relaxed,
153 : memory_order_relaxed);
154 0 : if (state == STATE_DISOWNED)
155 0 : rcu_free(MTYPE_LOG_LIVE, zte, head_self);
156 : }
157 :
158 0 : static void zlog_live_sigsafe(struct zlog_target *zt, const char *text,
159 : size_t len)
160 : {
161 0 : struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
162 0 : struct zlog_live_hdr hdr[1] = {};
163 0 : struct iovec iovs[2], *iov = iovs;
164 0 : struct timespec ts;
165 0 : int fd;
166 :
167 0 : fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
168 0 : if (fd < 0)
169 0 : return;
170 :
171 0 : clock_gettime(CLOCK_REALTIME, &ts);
172 :
173 0 : hdr->ts_sec = ts.tv_sec;
174 0 : hdr->ts_nsec = ts.tv_nsec;
175 0 : hdr->prio = LOG_CRIT;
176 0 : hdr->textlen = len;
177 :
178 0 : iov->iov_base = (char *)hdr;
179 0 : iov->iov_len = sizeof(hdr);
180 0 : iov++;
181 :
182 0 : iov->iov_base = (char *)text;
183 0 : iov->iov_len = len;
184 0 : iov++;
185 :
186 0 : writev(fd, iovs, iov - iovs);
187 : }
188 :
189 0 : void zlog_live_open(struct zlog_live_cfg *cfg, int prio_min, int *other_fd)
190 : {
191 0 : int sockets[2];
192 :
193 0 : if (cfg->target)
194 0 : zlog_live_close(cfg);
195 :
196 0 : *other_fd = -1;
197 0 : if (prio_min == ZLOG_DISABLED)
198 0 : 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 0 : if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets) < 0) {
207 0 : if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
208 0 : zlog_warn("%% could not open socket pair: %m");
209 0 : return;
210 : }
211 : } else
212 : /* SEQPACKET only: try to zap read direction */
213 0 : shutdown(sockets[0], SHUT_RD);
214 :
215 0 : *other_fd = sockets[1];
216 0 : zlog_live_open_fd(cfg, prio_min, sockets[0]);
217 : }
218 :
219 4 : void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd)
220 : {
221 4 : struct zlt_live *zte;
222 4 : struct zlog_target *zt;
223 :
224 4 : if (cfg->target)
225 0 : zlog_live_close(cfg);
226 :
227 4 : zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
228 4 : zte = container_of(zt, struct zlt_live, zt);
229 4 : cfg->target = zte;
230 :
231 4 : set_nonblocking(fd);
232 4 : zte->fd = fd;
233 4 : zte->zt.prio_min = prio_min;
234 4 : zte->zt.logfn = zlog_live;
235 4 : zte->zt.logfn_sigsafe = zlog_live_sigsafe;
236 :
237 4 : zlog_target_replace(NULL, zt);
238 4 : }
239 :
240 18 : void zlog_live_close(struct zlog_live_cfg *cfg)
241 : {
242 18 : struct zlt_live *zte;
243 18 : int fd;
244 :
245 18 : if (!cfg->target)
246 : return;
247 :
248 0 : zte = cfg->target;
249 0 : cfg->target = NULL;
250 :
251 0 : fd = atomic_exchange_explicit(&zte->fd, -1, memory_order_relaxed);
252 :
253 0 : if (fd >= 0) {
254 0 : rcu_close(&zte->head_close, fd);
255 0 : zlog_target_replace(&zte->zt, NULL);
256 : }
257 0 : rcu_free(MTYPE_LOG_LIVE, zte, head_self);
258 : }
259 :
260 4 : void zlog_live_disown(struct zlog_live_cfg *cfg)
261 : {
262 4 : struct zlt_live *zte;
263 4 : uint_fast32_t state;
264 :
265 4 : if (!cfg->target)
266 4 : return;
267 :
268 4 : zte = cfg->target;
269 4 : cfg->target = NULL;
270 :
271 4 : state = STATE_NORMAL;
272 4 : atomic_compare_exchange_strong_explicit(
273 : &zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
274 : memory_order_relaxed);
275 4 : if (state == STATE_FD_DEAD)
276 0 : rcu_free(MTYPE_LOG_LIVE, zte, head_self);
277 : }
|