2 * Copyright (c) 2019-22 David Lamparter, for NetDEF, Inc.
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.
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.
19 #include "zlog_live.h"
27 DEFINE_MTYPE_STATIC(LOG
, LOG_LIVE
, "log vtysh live target");
36 struct zlog_target zt
;
38 atomic_uint_fast32_t fd
;
39 struct rcu_head_close head_close
;
40 struct rcu_head head_self
;
42 atomic_uint_fast32_t state
;
43 atomic_uint_fast32_t lost_msgs
;
46 static void zlog_live(struct zlog_target
*zt
, struct zlog_msg
*msgs
[],
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
;
58 fd
= atomic_load_explicit(&zte
->fd
, memory_order_relaxed
);
63 memset(mmhs
, 0, sizeof(mmhs
));
64 memset(hdrs
, 0, sizeof(hdrs
));
66 for (i
= 0; i
< nmsgs
; i
++) {
67 const struct fmt_outpos
*argpos
;
68 size_t n_argpos
, texthdrlen
;
69 struct zlog_msg
*msg
= msgs
[i
];
70 int prio
= zlog_msg_prio(msg
);
71 const struct xref_logmsg
*xref
;
74 if (prio
> zt
->prio_min
)
77 zlog_msg_args(msg
, &texthdrlen
, &n_argpos
, &argpos
);
79 mmh
->msg_hdr
.msg_iov
= iov
;
82 iov
->iov_len
= sizeof(*hdr
);
86 iov
->iov_base
= (char *)argpos
;
87 iov
->iov_len
= sizeof(*argpos
) * n_argpos
;
91 iov
->iov_base
= (char *)zlog_msg_text(msg
, &textlen
);
92 iov
->iov_len
= textlen
;
95 zlog_msg_tsraw(msg
, &ts
);
96 zlog_msg_pid(msg
, &pid
, &tid
);
97 xref
= zlog_msg_xref(msg
);
99 hdr
->ts_sec
= ts
.tv_sec
;
100 hdr
->ts_nsec
= ts
.tv_nsec
;
103 hdr
->lost_msgs
= atomic_load_explicit(&zte
->lost_msgs
,
104 memory_order_relaxed
);
107 hdr
->textlen
= textlen
;
108 hdr
->texthdrlen
= texthdrlen
;
109 hdr
->n_argpos
= n_argpos
;
111 memcpy(hdr
->uid
, xref
->xref
.xrefdata
->uid
,
115 memset(hdr
->uid
, 0, sizeof(hdr
->uid
));
118 hdr
->hdrlen
= sizeof(*hdr
) + sizeof(*argpos
) * n_argpos
;
120 mmh
->msg_hdr
.msg_iovlen
= iov
- mmh
->msg_hdr
.msg_iov
;
125 size_t msgtotal
= mmh
- mmhs
;
128 for (size_t msgpos
= 0; msgpos
< msgtotal
; msgpos
+= sent
) {
129 sent
= sendmmsg(fd
, mmhs
+ msgpos
, msgtotal
- msgpos
, 0);
131 if (sent
<= 0 && (errno
== EAGAIN
|| errno
== EWOULDBLOCK
)) {
132 atomic_fetch_add_explicit(&zte
->lost_msgs
,
134 memory_order_relaxed
);
143 fd
= atomic_exchange_explicit(&zte
->fd
, -1, memory_order_relaxed
);
147 rcu_close(&zte
->head_close
, fd
);
148 zlog_target_replace(zt
, NULL
);
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
);
158 static void zlog_live_sigsafe(struct zlog_target
*zt
, const char *text
,
161 struct zlt_live
*zte
= container_of(zt
, struct zlt_live
, zt
);
162 struct zlog_live_hdr hdr
[1] = {};
163 struct iovec iovs
[2], *iov
= iovs
;
167 fd
= atomic_load_explicit(&zte
->fd
, memory_order_relaxed
);
171 clock_gettime(CLOCK_REALTIME
, &ts
);
173 hdr
->ts_sec
= ts
.tv_sec
;
174 hdr
->ts_nsec
= ts
.tv_nsec
;
175 hdr
->prio
= LOG_CRIT
;
178 iov
->iov_base
= (char *)hdr
;
179 iov
->iov_len
= sizeof(hdr
);
182 iov
->iov_base
= (char *)text
;
186 writev(fd
, iovs
, iov
- iovs
);
189 void zlog_live_open(struct zlog_live_cfg
*cfg
, int prio_min
, int *other_fd
)
194 zlog_live_close(cfg
);
197 if (prio_min
== ZLOG_DISABLED
)
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.
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");
212 /* SEQPACKET only: try to zap read direction */
213 shutdown(sockets
[0], SHUT_RD
);
215 *other_fd
= sockets
[1];
216 zlog_live_open_fd(cfg
, prio_min
, sockets
[0]);
219 void zlog_live_open_fd(struct zlog_live_cfg
*cfg
, int prio_min
, int fd
)
221 struct zlt_live
*zte
;
222 struct zlog_target
*zt
;
225 zlog_live_close(cfg
);
227 zt
= zlog_target_clone(MTYPE_LOG_LIVE
, NULL
, sizeof(*zte
));
228 zte
= container_of(zt
, struct zlt_live
, zt
);
233 zte
->zt
.prio_min
= prio_min
;
234 zte
->zt
.logfn
= zlog_live
;
235 zte
->zt
.logfn_sigsafe
= zlog_live_sigsafe
;
237 zlog_target_replace(NULL
, zt
);
240 void zlog_live_close(struct zlog_live_cfg
*cfg
)
242 struct zlt_live
*zte
;
251 fd
= atomic_exchange_explicit(&zte
->fd
, -1, memory_order_relaxed
);
254 rcu_close(&zte
->head_close
, fd
);
255 zlog_target_replace(&zte
->zt
, NULL
);
257 rcu_free(MTYPE_LOG_LIVE
, zte
, head_self
);
260 void zlog_live_disown(struct zlog_live_cfg
*cfg
)
262 struct zlt_live
*zte
;
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
);