back to topotato report
topotato coverage report
Current view: top level - lib - zlog_live.c (source / functions) Hit Total Coverage
Test: test_bgp_disable_addpath_rx.py::BGPDisableAddpathRx Lines: 89 149 59.7 %
Date: 2023-02-24 18:37:08 Functions: 6 8 75.0 %

          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          24 : 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         239 : static void zlog_live(struct zlog_target *zt, struct zlog_msg *msgs[],
      47             :                       size_t nmsgs)
      48         239 : {
      49         239 :         struct zlt_live *zte = container_of(zt, struct zlt_live, zt);
      50         239 :         struct zlog_live_hdr hdrs[nmsgs], *hdr = hdrs;
      51         239 :         struct mmsghdr mmhs[nmsgs], *mmh = mmhs;
      52         239 :         struct iovec iovs[nmsgs * 3], *iov = iovs;
      53         239 :         struct timespec ts;
      54         239 :         size_t i, textlen;
      55         239 :         int fd;
      56         239 :         uint_fast32_t state;
      57             : 
      58         239 :         fd = atomic_load_explicit(&zte->fd, memory_order_relaxed);
      59             : 
      60         239 :         if (fd < 0)
      61         239 :                 return;
      62             : 
      63         239 :         memset(mmhs, 0, sizeof(mmhs));
      64         239 :         memset(hdrs, 0, sizeof(hdrs));
      65             : 
      66         488 :         for (i = 0; i < nmsgs; i++) {
      67         249 :                 const struct fmt_outpos *argpos;
      68         249 :                 size_t n_argpos, texthdrlen;
      69         249 :                 struct zlog_msg *msg = msgs[i];
      70         249 :                 int prio = zlog_msg_prio(msg);
      71         249 :                 const struct xref_logmsg *xref;
      72         249 :                 intmax_t pid, tid;
      73             : 
      74         249 :                 if (prio > zt->prio_min)
      75           0 :                         continue;
      76             : 
      77         249 :                 zlog_msg_args(msg, &texthdrlen, &n_argpos, &argpos);
      78             : 
      79         249 :                 mmh->msg_hdr.msg_iov = iov;
      80             : 
      81         249 :                 iov->iov_base = hdr;
      82         249 :                 iov->iov_len = sizeof(*hdr);
      83         249 :                 iov++;
      84             : 
      85         249 :                 if (n_argpos) {
      86         233 :                         iov->iov_base = (char *)argpos;
      87         233 :                         iov->iov_len = sizeof(*argpos) * n_argpos;
      88         233 :                         iov++;
      89             :                 }
      90             : 
      91         249 :                 iov->iov_base = (char *)zlog_msg_text(msg, &textlen);
      92         249 :                 iov->iov_len = textlen;
      93         249 :                 iov++;
      94             : 
      95         249 :                 zlog_msg_tsraw(msg, &ts);
      96         249 :                 zlog_msg_pid(msg, &pid, &tid);
      97         249 :                 xref = zlog_msg_xref(msg);
      98             : 
      99         249 :                 hdr->ts_sec = ts.tv_sec;
     100         249 :                 hdr->ts_nsec = ts.tv_nsec;
     101         249 :                 hdr->pid = pid;
     102         249 :                 hdr->tid = tid;
     103         249 :                 hdr->lost_msgs = atomic_load_explicit(&zte->lost_msgs,
     104             :                                                       memory_order_relaxed);
     105         249 :                 hdr->prio = prio;
     106         249 :                 hdr->flags = 0;
     107         249 :                 hdr->textlen = textlen;
     108         249 :                 hdr->texthdrlen = texthdrlen;
     109         249 :                 hdr->n_argpos = n_argpos;
     110         249 :                 if (xref) {
     111         249 :                         memcpy(hdr->uid, xref->xref.xrefdata->uid,
     112             :                                sizeof(hdr->uid));
     113         249 :                         hdr->ec = xref->ec;
     114             :                 } else {
     115           0 :                         memset(hdr->uid, 0, sizeof(hdr->uid));
     116           0 :                         hdr->ec = 0;
     117             :                 }
     118         249 :                 hdr->hdrlen = sizeof(*hdr) + sizeof(*argpos) * n_argpos;
     119             : 
     120         249 :                 mmh->msg_hdr.msg_iovlen = iov - mmh->msg_hdr.msg_iov;
     121         249 :                 mmh++;
     122         249 :                 hdr++;
     123             :         }
     124             : 
     125         239 :         size_t msgtotal = mmh - mmhs;
     126         239 :         ssize_t sent;
     127             : 
     128         478 :         for (size_t msgpos = 0; msgpos < msgtotal; msgpos += sent) {
     129         239 :                 sent = sendmmsg(fd, mmhs + msgpos, msgtotal - msgpos, 0);
     130             : 
     131         239 :                 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           8 : void zlog_live_open_fd(struct zlog_live_cfg *cfg, int prio_min, int fd)
     220             : {
     221           8 :         struct zlt_live *zte;
     222           8 :         struct zlog_target *zt;
     223             : 
     224           8 :         if (cfg->target)
     225           0 :                 zlog_live_close(cfg);
     226             : 
     227           8 :         zt = zlog_target_clone(MTYPE_LOG_LIVE, NULL, sizeof(*zte));
     228           8 :         zte = container_of(zt, struct zlt_live, zt);
     229           8 :         cfg->target = zte;
     230             : 
     231           8 :         set_nonblocking(fd);
     232           8 :         zte->fd = fd;
     233           8 :         zte->zt.prio_min = prio_min;
     234           8 :         zte->zt.logfn = zlog_live;
     235           8 :         zte->zt.logfn_sigsafe = zlog_live_sigsafe;
     236             : 
     237           8 :         zlog_target_replace(NULL, zt);
     238           8 : }
     239             : 
     240          43 : void zlog_live_close(struct zlog_live_cfg *cfg)
     241             : {
     242          43 :         struct zlt_live *zte;
     243          43 :         int fd;
     244             : 
     245          43 :         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           8 : void zlog_live_disown(struct zlog_live_cfg *cfg)
     261             : {
     262           8 :         struct zlt_live *zte;
     263           8 :         uint_fast32_t state;
     264             : 
     265           8 :         if (!cfg->target)
     266           8 :                 return;
     267             : 
     268           8 :         zte = cfg->target;
     269           8 :         cfg->target = NULL;
     270             : 
     271           8 :         state = STATE_NORMAL;
     272           8 :         atomic_compare_exchange_strong_explicit(
     273             :                 &zte->state, &state, STATE_DISOWNED, memory_order_relaxed,
     274             :                 memory_order_relaxed);
     275           8 :         if (state == STATE_FD_DEAD)
     276           0 :                 rcu_free(MTYPE_LOG_LIVE, zte, head_self);
     277             : }

Generated by: LCOV version v1.16-topotato