LCOV - code coverage report
Current view: top level - libsystemd/sd-netlink - sd-netlink.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 365 500 73.0 %
Date: 2019-08-23 13:36:53 Functions: 31 34 91.2 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 229 462 49.6 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : 
       3                 :            : #include <poll.h>
       4                 :            : #include <sys/socket.h>
       5                 :            : 
       6                 :            : #include "sd-netlink.h"
       7                 :            : 
       8                 :            : #include "alloc-util.h"
       9                 :            : #include "fd-util.h"
      10                 :            : #include "hashmap.h"
      11                 :            : #include "macro.h"
      12                 :            : #include "missing.h"
      13                 :            : #include "netlink-internal.h"
      14                 :            : #include "netlink-slot.h"
      15                 :            : #include "netlink-util.h"
      16                 :            : #include "process-util.h"
      17                 :            : #include "socket-util.h"
      18                 :            : #include "string-util.h"
      19                 :            : #include "util.h"
      20                 :            : 
      21                 :         60 : static int sd_netlink_new(sd_netlink **ret) {
      22                 :         60 :         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
      23                 :            : 
      24   [ -  +  -  + ]:         60 :         assert_return(ret, -EINVAL);
      25                 :            : 
      26                 :         60 :         rtnl = new(sd_netlink, 1);
      27         [ -  + ]:         60 :         if (!rtnl)
      28                 :          0 :                 return -ENOMEM;
      29                 :            : 
      30                 :        120 :         *rtnl = (sd_netlink) {
      31                 :            :                 .n_ref = 1,
      32                 :            :                 .fd = -1,
      33                 :            :                 .sockaddr.nl.nl_family = AF_NETLINK,
      34                 :         60 :                 .original_pid = getpid_cached(),
      35                 :            :                 .protocol = -1,
      36                 :            : 
      37                 :            :                 /* Change notification responses have sequence 0, so we must
      38                 :            :                  * start our request sequence numbers at 1, or we may confuse our
      39                 :            :                  * responses with notifications from the kernel */
      40                 :            :                 .serial = 1,
      41                 :            : 
      42                 :            :         };
      43                 :            : 
      44                 :            :         /* We guarantee that the read buffer has at least space for
      45                 :            :          * a message header */
      46         [ -  + ]:         60 :         if (!greedy_realloc((void**)&rtnl->rbuffer, &rtnl->rbuffer_allocated,
      47                 :            :                             sizeof(struct nlmsghdr), sizeof(uint8_t)))
      48                 :          0 :                 return -ENOMEM;
      49                 :            : 
      50                 :         60 :         *ret = TAKE_PTR(rtnl);
      51                 :            : 
      52                 :         60 :         return 0;
      53                 :            : }
      54                 :            : 
      55                 :          0 : int sd_netlink_new_from_netlink(sd_netlink **ret, int fd) {
      56                 :          0 :         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
      57                 :            :         socklen_t addrlen;
      58                 :            :         int r;
      59                 :            : 
      60   [ #  #  #  # ]:          0 :         assert_return(ret, -EINVAL);
      61                 :            : 
      62                 :          0 :         r = sd_netlink_new(&rtnl);
      63         [ #  # ]:          0 :         if (r < 0)
      64                 :          0 :                 return r;
      65                 :            : 
      66                 :          0 :         addrlen = sizeof(rtnl->sockaddr);
      67                 :            : 
      68                 :          0 :         r = getsockname(fd, &rtnl->sockaddr.sa, &addrlen);
      69         [ #  # ]:          0 :         if (r < 0)
      70                 :          0 :                 return -errno;
      71                 :            : 
      72         [ #  # ]:          0 :         if (rtnl->sockaddr.nl.nl_family != AF_NETLINK)
      73                 :          0 :                 return -EINVAL;
      74                 :            : 
      75                 :          0 :         rtnl->fd = fd;
      76                 :            : 
      77                 :          0 :         *ret = TAKE_PTR(rtnl);
      78                 :            : 
      79                 :          0 :         return 0;
      80                 :            : }
      81                 :            : 
      82                 :        588 : static bool rtnl_pid_changed(sd_netlink *rtnl) {
      83         [ -  + ]:        588 :         assert(rtnl);
      84                 :            : 
      85                 :            :         /* We don't support people creating an rtnl connection and
      86                 :            :          * keeping it around over a fork(). Let's complain. */
      87                 :            : 
      88                 :        588 :         return rtnl->original_pid != getpid_cached();
      89                 :            : }
      90                 :            : 
      91                 :         60 : int sd_netlink_open_fd(sd_netlink **ret, int fd) {
      92                 :         60 :         _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
      93                 :            :         int r;
      94                 :            :         int protocol;
      95                 :            :         socklen_t l;
      96                 :            : 
      97   [ -  +  -  + ]:         60 :         assert_return(ret, -EINVAL);
      98   [ -  +  -  + ]:         60 :         assert_return(fd >= 0, -EBADF);
      99                 :            : 
     100                 :         60 :         r = sd_netlink_new(&rtnl);
     101         [ -  + ]:         60 :         if (r < 0)
     102                 :          0 :                 return r;
     103                 :            : 
     104                 :         60 :         l = sizeof(protocol);
     105                 :         60 :         r = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &protocol, &l);
     106         [ -  + ]:         60 :         if (r < 0)
     107                 :          0 :                 return r;
     108                 :            : 
     109                 :         60 :         rtnl->fd = fd;
     110                 :         60 :         rtnl->protocol = protocol;
     111                 :            : 
     112                 :         60 :         r = socket_bind(rtnl);
     113         [ -  + ]:         60 :         if (r < 0) {
     114                 :          0 :                 rtnl->fd = -1; /* on failure, the caller remains owner of the fd, hence don't close it here */
     115                 :          0 :                 rtnl->protocol = -1;
     116                 :          0 :                 return r;
     117                 :            :         }
     118                 :            : 
     119                 :         60 :         *ret = TAKE_PTR(rtnl);
     120                 :            : 
     121                 :         60 :         return 0;
     122                 :            : }
     123                 :            : 
     124                 :         60 : int netlink_open_family(sd_netlink **ret, int family) {
     125                 :         60 :         _cleanup_close_ int fd = -1;
     126                 :            :         int r;
     127                 :            : 
     128                 :         60 :         fd = socket_open(family);
     129         [ -  + ]:         60 :         if (fd < 0)
     130                 :          0 :                 return fd;
     131                 :            : 
     132                 :         60 :         r = sd_netlink_open_fd(ret, fd);
     133         [ -  + ]:         60 :         if (r < 0)
     134                 :          0 :                 return r;
     135                 :            : 
     136                 :         60 :         fd = -1;
     137                 :            : 
     138                 :         60 :         return 0;
     139                 :            : }
     140                 :            : 
     141                 :         52 : int sd_netlink_open(sd_netlink **ret) {
     142                 :         52 :         return netlink_open_family(ret, NETLINK_ROUTE);
     143                 :            : }
     144                 :            : 
     145                 :          8 : int sd_netlink_inc_rcvbuf(sd_netlink *rtnl, size_t size) {
     146   [ -  +  -  + ]:          8 :         assert_return(rtnl, -EINVAL);
     147   [ -  +  -  + ]:          8 :         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
     148                 :            : 
     149                 :          8 :         return fd_inc_rcvbuf(rtnl->fd, size);
     150                 :            : }
     151                 :            : 
     152                 :         60 : static sd_netlink *netlink_free(sd_netlink *rtnl) {
     153                 :            :         sd_netlink_slot *s;
     154                 :            :         unsigned i;
     155                 :            : 
     156         [ -  + ]:         60 :         assert(rtnl);
     157                 :            : 
     158         [ +  + ]:         72 :         for (i = 0; i < rtnl->rqueue_size; i++)
     159                 :         12 :                 sd_netlink_message_unref(rtnl->rqueue[i]);
     160                 :         60 :         free(rtnl->rqueue);
     161                 :            : 
     162         [ -  + ]:         60 :         for (i = 0; i < rtnl->rqueue_partial_size; i++)
     163                 :          0 :                 sd_netlink_message_unref(rtnl->rqueue_partial[i]);
     164                 :         60 :         free(rtnl->rqueue_partial);
     165                 :            : 
     166                 :         60 :         free(rtnl->rbuffer);
     167                 :            : 
     168         [ +  + ]:        136 :         while ((s = rtnl->slots)) {
     169         [ -  + ]:         76 :                 assert(s->floating);
     170                 :         76 :                 netlink_slot_disconnect(s, true);
     171                 :            :         }
     172                 :         60 :         hashmap_free(rtnl->reply_callbacks);
     173                 :         60 :         prioq_free(rtnl->reply_callbacks_prioq);
     174                 :            : 
     175                 :         60 :         sd_event_source_unref(rtnl->io_event_source);
     176                 :         60 :         sd_event_source_unref(rtnl->time_event_source);
     177                 :         60 :         sd_event_unref(rtnl->event);
     178                 :            : 
     179                 :         60 :         hashmap_free(rtnl->broadcast_group_refs);
     180                 :            : 
     181                 :         60 :         safe_close(rtnl->fd);
     182                 :         60 :         return mfree(rtnl);
     183                 :            : }
     184                 :            : 
     185   [ -  +  -  +  :        180 : DEFINE_TRIVIAL_REF_UNREF_FUNC(sd_netlink, sd_netlink, netlink_free);
                   +  + ]
     186                 :            : 
     187                 :        108 : static void rtnl_seal_message(sd_netlink *rtnl, sd_netlink_message *m) {
     188         [ -  + ]:        108 :         assert(rtnl);
     189         [ -  + ]:        108 :         assert(!rtnl_pid_changed(rtnl));
     190         [ -  + ]:        108 :         assert(m);
     191         [ -  + ]:        108 :         assert(m->hdr);
     192                 :            : 
     193                 :            :         /* don't use seq == 0, as that is used for broadcasts, so we
     194                 :            :            would get confused by replies to such messages */
     195         [ +  - ]:        108 :         m->hdr->nlmsg_seq = rtnl->serial++ ? : rtnl->serial++;
     196                 :            : 
     197                 :        108 :         rtnl_message_seal(m);
     198                 :            : 
     199                 :        108 :         return;
     200                 :            : }
     201                 :            : 
     202                 :        112 : int sd_netlink_send(sd_netlink *nl,
     203                 :            :                     sd_netlink_message *message,
     204                 :            :                     uint32_t *serial) {
     205                 :            :         int r;
     206                 :            : 
     207   [ -  +  -  + ]:        112 :         assert_return(nl, -EINVAL);
     208   [ -  +  -  + ]:        112 :         assert_return(!rtnl_pid_changed(nl), -ECHILD);
     209   [ -  +  -  + ]:        112 :         assert_return(message, -EINVAL);
     210   [ +  +  +  + ]:        112 :         assert_return(!message->sealed, -EPERM);
     211                 :            : 
     212                 :        108 :         rtnl_seal_message(nl, message);
     213                 :            : 
     214                 :        108 :         r = socket_write_message(nl, message);
     215         [ -  + ]:        108 :         if (r < 0)
     216                 :          0 :                 return r;
     217                 :            : 
     218         [ +  - ]:        108 :         if (serial)
     219                 :        108 :                 *serial = rtnl_message_get_serial(message);
     220                 :            : 
     221                 :        108 :         return 1;
     222                 :            : }
     223                 :            : 
     224                 :         88 : int rtnl_rqueue_make_room(sd_netlink *rtnl) {
     225         [ -  + ]:         88 :         assert(rtnl);
     226                 :            : 
     227         [ -  + ]:         88 :         if (rtnl->rqueue_size >= RTNL_RQUEUE_MAX)
     228         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
     229                 :            :                                        "rtnl: exhausted the read queue size (%d)",
     230                 :            :                                        RTNL_RQUEUE_MAX);
     231                 :            : 
     232         [ -  + ]:         88 :         if (!GREEDY_REALLOC(rtnl->rqueue, rtnl->rqueue_allocated, rtnl->rqueue_size + 1))
     233                 :          0 :                 return -ENOMEM;
     234                 :            : 
     235                 :         88 :         return 0;
     236                 :            : }
     237                 :            : 
     238                 :         20 : int rtnl_rqueue_partial_make_room(sd_netlink *rtnl) {
     239         [ -  + ]:         20 :         assert(rtnl);
     240                 :            : 
     241         [ -  + ]:         20 :         if (rtnl->rqueue_partial_size >= RTNL_RQUEUE_MAX)
     242         [ #  # ]:          0 :                 return log_debug_errno(SYNTHETIC_ERRNO(ENOBUFS),
     243                 :            :                                        "rtnl: exhausted the partial read queue size (%d)",
     244                 :            :                                        RTNL_RQUEUE_MAX);
     245                 :            : 
     246         [ -  + ]:         20 :         if (!GREEDY_REALLOC(rtnl->rqueue_partial, rtnl->rqueue_partial_allocated,
     247                 :            :                             rtnl->rqueue_partial_size + 1))
     248                 :          0 :                 return -ENOMEM;
     249                 :            : 
     250                 :         20 :         return 0;
     251                 :            : }
     252                 :            : 
     253                 :         40 : static int dispatch_rqueue(sd_netlink *rtnl, sd_netlink_message **message) {
     254                 :            :         int r;
     255                 :            : 
     256         [ -  + ]:         40 :         assert(rtnl);
     257         [ -  + ]:         40 :         assert(message);
     258                 :            : 
     259         [ +  - ]:         40 :         if (rtnl->rqueue_size <= 0) {
     260                 :            :                 /* Try to read a new message */
     261                 :         40 :                 r = socket_read_message(rtnl);
     262         [ -  + ]:         40 :                 if (r == -ENOBUFS) { /* FIXME: ignore buffer overruns for now */
     263         [ #  # ]:          0 :                         log_debug_errno(r, "Got ENOBUFS from netlink socket, ignoring.");
     264                 :          0 :                         return 1;
     265                 :            :                 }
     266         [ -  + ]:         40 :                 if (r <= 0)
     267                 :          0 :                         return r;
     268                 :            :         }
     269                 :            : 
     270                 :            :         /* Dispatch a queued message */
     271                 :         40 :         *message = rtnl->rqueue[0];
     272                 :         40 :         rtnl->rqueue_size--;
     273                 :         40 :         memmove(rtnl->rqueue, rtnl->rqueue + 1, sizeof(sd_netlink_message*) * rtnl->rqueue_size);
     274                 :            : 
     275                 :         40 :         return 1;
     276                 :            : }
     277                 :            : 
     278                 :         40 : static int process_timeout(sd_netlink *rtnl) {
     279                 :         40 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
     280                 :            :         struct reply_callback *c;
     281                 :            :         sd_netlink_slot *slot;
     282                 :            :         usec_t n;
     283                 :            :         int r;
     284                 :            : 
     285         [ -  + ]:         40 :         assert(rtnl);
     286                 :            : 
     287                 :         40 :         c = prioq_peek(rtnl->reply_callbacks_prioq);
     288         [ -  + ]:         40 :         if (!c)
     289                 :          0 :                 return 0;
     290                 :            : 
     291                 :         40 :         n = now(CLOCK_MONOTONIC);
     292         [ +  - ]:         40 :         if (c->timeout > n)
     293                 :         40 :                 return 0;
     294                 :            : 
     295                 :          0 :         r = rtnl_message_new_synthetic_error(rtnl, -ETIMEDOUT, c->serial, &m);
     296         [ #  # ]:          0 :         if (r < 0)
     297                 :          0 :                 return r;
     298                 :            : 
     299         [ #  # ]:          0 :         assert_se(prioq_pop(rtnl->reply_callbacks_prioq) == c);
     300                 :          0 :         c->timeout = 0;
     301                 :          0 :         hashmap_remove(rtnl->reply_callbacks, &c->serial);
     302                 :            : 
     303                 :          0 :         slot = container_of(c, sd_netlink_slot, reply_callback);
     304                 :            : 
     305                 :          0 :         r = c->callback(rtnl, m, slot->userdata);
     306         [ #  # ]:          0 :         if (r < 0)
     307   [ #  #  #  #  :          0 :                 log_debug_errno(r, "sd-netlink: timedout callback %s%s%sfailed: %m",
                   #  # ]
     308                 :            :                                 slot->description ? "'" : "",
     309                 :            :                                 strempty(slot->description),
     310                 :            :                                 slot->description ? "' " : "");
     311                 :            : 
     312         [ #  # ]:          0 :         if (slot->floating)
     313                 :          0 :                 netlink_slot_disconnect(slot, true);
     314                 :            : 
     315                 :          0 :         return 1;
     316                 :            : }
     317                 :            : 
     318                 :         40 : static int process_reply(sd_netlink *rtnl, sd_netlink_message *m) {
     319                 :            :         struct reply_callback *c;
     320                 :            :         sd_netlink_slot *slot;
     321                 :            :         uint64_t serial;
     322                 :            :         uint16_t type;
     323                 :            :         int r;
     324                 :            : 
     325         [ -  + ]:         40 :         assert(rtnl);
     326         [ -  + ]:         40 :         assert(m);
     327                 :            : 
     328                 :         40 :         serial = rtnl_message_get_serial(m);
     329                 :         40 :         c = hashmap_remove(rtnl->reply_callbacks, &serial);
     330         [ +  + ]:         40 :         if (!c)
     331                 :          4 :                 return 0;
     332                 :            : 
     333         [ +  + ]:         36 :         if (c->timeout != 0) {
     334                 :         28 :                 prioq_remove(rtnl->reply_callbacks_prioq, c, &c->prioq_idx);
     335                 :         28 :                 c->timeout = 0;
     336                 :            :         }
     337                 :            : 
     338                 :         36 :         r = sd_netlink_message_get_type(m, &type);
     339         [ -  + ]:         36 :         if (r < 0)
     340                 :          0 :                 return r;
     341                 :            : 
     342         [ -  + ]:         36 :         if (type == NLMSG_DONE)
     343                 :          0 :                 m = NULL;
     344                 :            : 
     345                 :         36 :         slot = container_of(c, sd_netlink_slot, reply_callback);
     346                 :            : 
     347                 :         36 :         r = c->callback(rtnl, m, slot->userdata);
     348         [ -  + ]:         36 :         if (r < 0)
     349   [ #  #  #  #  :          0 :                 log_debug_errno(r, "sd-netlink: reply callback %s%s%sfailed: %m",
                   #  # ]
     350                 :            :                                 slot->description ? "'" : "",
     351                 :            :                                 strempty(slot->description),
     352                 :            :                                 slot->description ? "' " : "");
     353                 :            : 
     354         [ +  + ]:         36 :         if (slot->floating)
     355                 :         32 :                 netlink_slot_disconnect(slot, true);
     356                 :            : 
     357                 :         36 :         return 1;
     358                 :            : }
     359                 :            : 
     360                 :          0 : static int process_match(sd_netlink *rtnl, sd_netlink_message *m) {
     361                 :            :         struct match_callback *c;
     362                 :            :         sd_netlink_slot *slot;
     363                 :            :         uint16_t type;
     364                 :            :         int r;
     365                 :            : 
     366         [ #  # ]:          0 :         assert(rtnl);
     367         [ #  # ]:          0 :         assert(m);
     368                 :            : 
     369                 :          0 :         r = sd_netlink_message_get_type(m, &type);
     370         [ #  # ]:          0 :         if (r < 0)
     371                 :          0 :                 return r;
     372                 :            : 
     373         [ #  # ]:          0 :         LIST_FOREACH(match_callbacks, c, rtnl->match_callbacks) {
     374         [ #  # ]:          0 :                 if (type == c->type) {
     375                 :          0 :                         slot = container_of(c, sd_netlink_slot, match_callback);
     376                 :            : 
     377                 :          0 :                         r = c->callback(rtnl, m, slot->userdata);
     378         [ #  # ]:          0 :                         if (r != 0) {
     379         [ #  # ]:          0 :                                 if (r < 0)
     380   [ #  #  #  #  :          0 :                                         log_debug_errno(r, "sd-netlink: match callback %s%s%sfailed: %m",
                   #  # ]
     381                 :            :                                                         slot->description ? "'" : "",
     382                 :            :                                                         strempty(slot->description),
     383                 :            :                                                         slot->description ? "' " : "");
     384                 :            : 
     385                 :          0 :                                 break;
     386                 :            :                         }
     387                 :            :                 }
     388                 :            :         }
     389                 :            : 
     390                 :          0 :         return 1;
     391                 :            : }
     392                 :            : 
     393                 :         40 : static int process_running(sd_netlink *rtnl, sd_netlink_message **ret) {
     394                 :         40 :         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL;
     395                 :            :         int r;
     396                 :            : 
     397         [ -  + ]:         40 :         assert(rtnl);
     398                 :            : 
     399                 :         40 :         r = process_timeout(rtnl);
     400         [ -  + ]:         40 :         if (r != 0)
     401                 :          0 :                 goto null_message;
     402                 :            : 
     403                 :         40 :         r = dispatch_rqueue(rtnl, &m);
     404         [ -  + ]:         40 :         if (r < 0)
     405                 :          0 :                 return r;
     406         [ -  + ]:         40 :         if (!m)
     407                 :          0 :                 goto null_message;
     408                 :            : 
     409         [ -  + ]:         40 :         if (sd_netlink_message_is_broadcast(m)) {
     410                 :          0 :                 r = process_match(rtnl, m);
     411         [ #  # ]:          0 :                 if (r != 0)
     412                 :          0 :                         goto null_message;
     413                 :            :         } else {
     414                 :         40 :                 r = process_reply(rtnl, m);
     415         [ +  + ]:         40 :                 if (r != 0)
     416                 :         36 :                         goto null_message;
     417                 :            :         }
     418                 :            : 
     419         [ -  + ]:          4 :         if (ret) {
     420                 :          0 :                 *ret = TAKE_PTR(m);
     421                 :            : 
     422                 :          0 :                 return 1;
     423                 :            :         }
     424                 :            : 
     425                 :          4 :         return 1;
     426                 :            : 
     427                 :         36 : null_message:
     428   [ +  -  +  + ]:         36 :         if (r >= 0 && ret)
     429                 :         12 :                 *ret = NULL;
     430                 :            : 
     431                 :         36 :         return r;
     432                 :            : }
     433                 :            : 
     434                 :         40 : int sd_netlink_process(sd_netlink *rtnl, sd_netlink_message **ret) {
     435                 :         80 :         NETLINK_DONT_DESTROY(rtnl);
     436                 :            :         int r;
     437                 :            : 
     438   [ -  +  -  + ]:         40 :         assert_return(rtnl, -EINVAL);
     439   [ -  +  -  + ]:         40 :         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
     440   [ -  +  -  + ]:         40 :         assert_return(!rtnl->processing, -EBUSY);
     441                 :            : 
     442                 :         40 :         rtnl->processing = true;
     443                 :         40 :         r = process_running(rtnl, ret);
     444                 :         40 :         rtnl->processing = false;
     445                 :            : 
     446                 :         40 :         return r;
     447                 :            : }
     448                 :            : 
     449                 :        108 : static usec_t calc_elapse(uint64_t usec) {
     450         [ +  + ]:        108 :         if (usec == (uint64_t) -1)
     451                 :         20 :                 return 0;
     452                 :            : 
     453         [ +  + ]:         88 :         if (usec == 0)
     454                 :         84 :                 usec = RTNL_DEFAULT_TIMEOUT;
     455                 :            : 
     456                 :         88 :         return now(CLOCK_MONOTONIC) + usec;
     457                 :            : }
     458                 :            : 
     459                 :         76 : static int rtnl_poll(sd_netlink *rtnl, bool need_more, uint64_t timeout_usec) {
     460                 :         76 :         struct pollfd p[1] = {};
     461                 :            :         struct timespec ts;
     462                 :         76 :         usec_t m = USEC_INFINITY;
     463                 :            :         int r, e;
     464                 :            : 
     465         [ -  + ]:         76 :         assert(rtnl);
     466                 :            : 
     467                 :         76 :         e = sd_netlink_get_events(rtnl);
     468         [ -  + ]:         76 :         if (e < 0)
     469                 :          0 :                 return e;
     470                 :            : 
     471         [ +  + ]:         76 :         if (need_more)
     472                 :            :                 /* Caller wants more data, and doesn't care about
     473                 :            :                  * what's been read or any other timeouts. */
     474                 :         40 :                 e |= POLLIN;
     475                 :            :         else {
     476                 :            :                 usec_t until;
     477                 :            :                 /* Caller wants to process if there is something to
     478                 :            :                  * process, but doesn't care otherwise */
     479                 :            : 
     480                 :         36 :                 r = sd_netlink_get_timeout(rtnl, &until);
     481         [ -  + ]:         36 :                 if (r < 0)
     482                 :          0 :                         return r;
     483         [ +  - ]:         36 :                 if (r > 0) {
     484                 :            :                         usec_t nw;
     485                 :         36 :                         nw = now(CLOCK_MONOTONIC);
     486         [ +  - ]:         36 :                         m = until > nw ? until - nw : 0;
     487                 :            :                 }
     488                 :            :         }
     489                 :            : 
     490   [ +  +  +  +  :         76 :         if (timeout_usec != (uint64_t) -1 && (m == (uint64_t) -1 || timeout_usec < m))
                   +  + ]
     491                 :         60 :                 m = timeout_usec;
     492                 :            : 
     493                 :         76 :         p[0].fd = rtnl->fd;
     494                 :         76 :         p[0].events = e;
     495                 :            : 
     496         [ +  + ]:         76 :         r = ppoll(p, 1, m == (uint64_t) -1 ? NULL : timespec_store(&ts, m), NULL);
     497         [ -  + ]:         76 :         if (r < 0)
     498                 :          0 :                 return -errno;
     499                 :            : 
     500                 :         76 :         return r > 0 ? 1 : 0;
     501                 :            : }
     502                 :            : 
     503                 :         36 : int sd_netlink_wait(sd_netlink *nl, uint64_t timeout_usec) {
     504   [ -  +  -  + ]:         36 :         assert_return(nl, -EINVAL);
     505   [ -  +  -  + ]:         36 :         assert_return(!rtnl_pid_changed(nl), -ECHILD);
     506                 :            : 
     507         [ -  + ]:         36 :         if (nl->rqueue_size > 0)
     508                 :          0 :                 return 0;
     509                 :            : 
     510                 :         36 :         return rtnl_poll(nl, false, timeout_usec);
     511                 :            : }
     512                 :            : 
     513                 :         28 : static int timeout_compare(const void *a, const void *b) {
     514                 :         28 :         const struct reply_callback *x = a, *y = b;
     515                 :            : 
     516   [ +  -  -  + ]:         28 :         if (x->timeout != 0 && y->timeout == 0)
     517                 :          0 :                 return -1;
     518                 :            : 
     519   [ -  +  #  # ]:         28 :         if (x->timeout == 0 && y->timeout != 0)
     520                 :          0 :                 return 1;
     521                 :            : 
     522         [ -  + ]:         28 :         return CMP(x->timeout, y->timeout);
     523                 :            : }
     524                 :            : 
     525                 :         72 : int sd_netlink_call_async(
     526                 :            :                 sd_netlink *nl,
     527                 :            :                 sd_netlink_slot **ret_slot,
     528                 :            :                 sd_netlink_message *m,
     529                 :            :                 sd_netlink_message_handler_t callback,
     530                 :            :                 sd_netlink_destroy_t destroy_callback,
     531                 :            :                 void *userdata,
     532                 :            :                 uint64_t usec,
     533                 :            :                 const char *description) {
     534                 :         72 :         _cleanup_free_ sd_netlink_slot *slot = NULL;
     535                 :            :         uint32_t s;
     536                 :            :         int r, k;
     537                 :            : 
     538   [ -  +  -  + ]:         72 :         assert_return(nl, -EINVAL);
     539   [ -  +  -  + ]:         72 :         assert_return(m, -EINVAL);
     540   [ -  +  -  + ]:         72 :         assert_return(callback, -EINVAL);
     541   [ -  +  -  + ]:         72 :         assert_return(!rtnl_pid_changed(nl), -ECHILD);
     542                 :            : 
     543                 :         72 :         r = hashmap_ensure_allocated(&nl->reply_callbacks, &uint64_hash_ops);
     544         [ -  + ]:         72 :         if (r < 0)
     545                 :          0 :                 return r;
     546                 :            : 
     547         [ +  + ]:         72 :         if (usec != (uint64_t) -1) {
     548                 :         64 :                 r = prioq_ensure_allocated(&nl->reply_callbacks_prioq, timeout_compare);
     549         [ -  + ]:         64 :                 if (r < 0)
     550                 :          0 :                         return r;
     551                 :            :         }
     552                 :            : 
     553                 :         72 :         r = netlink_slot_allocate(nl, !ret_slot, NETLINK_REPLY_CALLBACK, sizeof(struct reply_callback), userdata, description, &slot);
     554         [ -  + ]:         72 :         if (r < 0)
     555                 :          0 :                 return r;
     556                 :            : 
     557                 :         72 :         slot->reply_callback.callback = callback;
     558                 :         72 :         slot->reply_callback.timeout = calc_elapse(usec);
     559                 :            : 
     560                 :         72 :         k = sd_netlink_send(nl, m, &s);
     561         [ -  + ]:         72 :         if (k < 0)
     562                 :          0 :                 return k;
     563                 :            : 
     564                 :         72 :         slot->reply_callback.serial = s;
     565                 :            : 
     566                 :         72 :         r = hashmap_put(nl->reply_callbacks, &slot->reply_callback.serial, &slot->reply_callback);
     567         [ -  + ]:         72 :         if (r < 0)
     568                 :          0 :                 return r;
     569                 :            : 
     570         [ +  + ]:         72 :         if (slot->reply_callback.timeout != 0) {
     571                 :         64 :                 r = prioq_put(nl->reply_callbacks_prioq, &slot->reply_callback, &slot->reply_callback.prioq_idx);
     572         [ -  + ]:         64 :                 if (r < 0) {
     573                 :          0 :                         (void) hashmap_remove(nl->reply_callbacks, &slot->reply_callback.serial);
     574                 :          0 :                         return r;
     575                 :            :                 }
     576                 :            :         }
     577                 :            : 
     578                 :            :         /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
     579                 :         72 :         slot->destroy_callback = destroy_callback;
     580                 :            : 
     581         [ +  + ]:         72 :         if (ret_slot)
     582                 :         12 :                 *ret_slot = slot;
     583                 :            : 
     584                 :         72 :         TAKE_PTR(slot);
     585                 :            : 
     586                 :         72 :         return k;
     587                 :            : }
     588                 :            : 
     589                 :         40 : int sd_netlink_call(sd_netlink *rtnl,
     590                 :            :                 sd_netlink_message *message,
     591                 :            :                 uint64_t usec,
     592                 :            :                 sd_netlink_message **ret) {
     593                 :            :         usec_t timeout;
     594                 :            :         uint32_t serial;
     595                 :            :         int r;
     596                 :            : 
     597   [ -  +  -  + ]:         40 :         assert_return(rtnl, -EINVAL);
     598   [ -  +  -  + ]:         40 :         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
     599   [ -  +  -  + ]:         40 :         assert_return(message, -EINVAL);
     600                 :            : 
     601                 :         40 :         r = sd_netlink_send(rtnl, message, &serial);
     602         [ +  + ]:         40 :         if (r < 0)
     603                 :          4 :                 return r;
     604                 :            : 
     605                 :         36 :         timeout = calc_elapse(usec);
     606                 :            : 
     607                 :         88 :         for (;;) {
     608                 :            :                 usec_t left;
     609                 :            :                 unsigned i;
     610                 :            : 
     611         [ +  + ]:        212 :                 for (i = 0; i < rtnl->rqueue_size; i++) {
     612                 :            :                         uint32_t received_serial;
     613                 :            : 
     614                 :        124 :                         received_serial = rtnl_message_get_serial(rtnl->rqueue[i]);
     615                 :            : 
     616         [ +  + ]:        124 :                         if (received_serial == serial) {
     617                 :         36 :                                 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *incoming = NULL;
     618                 :            :                                 uint16_t type;
     619                 :            : 
     620                 :         36 :                                 incoming = rtnl->rqueue[i];
     621                 :            : 
     622                 :            :                                 /* found a match, remove from rqueue and return it */
     623                 :         36 :                                 memmove(rtnl->rqueue + i,rtnl->rqueue + i + 1,
     624                 :         36 :                                         sizeof(sd_netlink_message*) * (rtnl->rqueue_size - i - 1));
     625                 :         36 :                                 rtnl->rqueue_size--;
     626                 :            : 
     627                 :         36 :                                 r = sd_netlink_message_get_errno(incoming);
     628         [ -  + ]:         36 :                                 if (r < 0)
     629                 :          0 :                                         return r;
     630                 :            : 
     631                 :         36 :                                 r = sd_netlink_message_get_type(incoming, &type);
     632         [ -  + ]:         36 :                                 if (r < 0)
     633                 :          0 :                                         return r;
     634                 :            : 
     635         [ -  + ]:         36 :                                 if (type == NLMSG_DONE) {
     636                 :          0 :                                         *ret = NULL;
     637                 :          0 :                                         return 0;
     638                 :            :                                 }
     639                 :            : 
     640         [ +  + ]:         36 :                                 if (ret)
     641                 :         32 :                                         *ret = TAKE_PTR(incoming);
     642                 :            : 
     643                 :         36 :                                 return 1;
     644                 :            :                         }
     645                 :            :                 }
     646                 :            : 
     647                 :         88 :                 r = socket_read_message(rtnl);
     648         [ -  + ]:         88 :                 if (r < 0)
     649                 :          0 :                         return r;
     650         [ +  + ]:         88 :                 if (r > 0)
     651                 :            :                         /* received message, so try to process straight away */
     652                 :         48 :                         continue;
     653                 :            : 
     654         [ +  + ]:         40 :                 if (timeout > 0) {
     655                 :            :                         usec_t n;
     656                 :            : 
     657                 :         36 :                         n = now(CLOCK_MONOTONIC);
     658         [ -  + ]:         36 :                         if (n >= timeout)
     659                 :          0 :                                 return -ETIMEDOUT;
     660                 :            : 
     661                 :         36 :                         left = timeout - n;
     662                 :            :                 } else
     663                 :          4 :                         left = (uint64_t) -1;
     664                 :            : 
     665                 :         40 :                 r = rtnl_poll(rtnl, true, left);
     666         [ -  + ]:         40 :                 if (r < 0)
     667                 :          0 :                         return r;
     668         [ -  + ]:         40 :                 else if (r == 0)
     669                 :          0 :                         return -ETIMEDOUT;
     670                 :            :         }
     671                 :            : }
     672                 :            : 
     673                 :         80 : int sd_netlink_get_events(sd_netlink *rtnl) {
     674   [ -  +  -  + ]:         80 :         assert_return(rtnl, -EINVAL);
     675   [ -  +  -  + ]:         80 :         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
     676                 :            : 
     677         [ +  + ]:         80 :         if (rtnl->rqueue_size == 0)
     678                 :         68 :                 return POLLIN;
     679                 :            :         else
     680                 :         12 :                 return 0;
     681                 :            : }
     682                 :            : 
     683                 :         40 : int sd_netlink_get_timeout(sd_netlink *rtnl, uint64_t *timeout_usec) {
     684                 :            :         struct reply_callback *c;
     685                 :            : 
     686   [ -  +  -  + ]:         40 :         assert_return(rtnl, -EINVAL);
     687   [ -  +  -  + ]:         40 :         assert_return(timeout_usec, -EINVAL);
     688   [ -  +  -  + ]:         40 :         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
     689                 :            : 
     690         [ -  + ]:         40 :         if (rtnl->rqueue_size > 0) {
     691                 :          0 :                 *timeout_usec = 0;
     692                 :          0 :                 return 1;
     693                 :            :         }
     694                 :            : 
     695                 :         40 :         c = prioq_peek(rtnl->reply_callbacks_prioq);
     696         [ -  + ]:         40 :         if (!c) {
     697                 :          0 :                 *timeout_usec = (uint64_t) -1;
     698                 :          0 :                 return 0;
     699                 :            :         }
     700                 :            : 
     701                 :         40 :         *timeout_usec = c->timeout;
     702                 :            : 
     703                 :         40 :         return 1;
     704                 :            : }
     705                 :            : 
     706                 :          4 : static int io_callback(sd_event_source *s, int fd, uint32_t revents, void *userdata) {
     707                 :          4 :         sd_netlink *rtnl = userdata;
     708                 :            :         int r;
     709                 :            : 
     710         [ -  + ]:          4 :         assert(rtnl);
     711                 :            : 
     712                 :          4 :         r = sd_netlink_process(rtnl, NULL);
     713         [ -  + ]:          4 :         if (r < 0)
     714                 :          0 :                 return r;
     715                 :            : 
     716                 :          4 :         return 1;
     717                 :            : }
     718                 :            : 
     719                 :          0 : static int time_callback(sd_event_source *s, uint64_t usec, void *userdata) {
     720                 :          0 :         sd_netlink *rtnl = userdata;
     721                 :            :         int r;
     722                 :            : 
     723         [ #  # ]:          0 :         assert(rtnl);
     724                 :            : 
     725                 :          0 :         r = sd_netlink_process(rtnl, NULL);
     726         [ #  # ]:          0 :         if (r < 0)
     727                 :          0 :                 return r;
     728                 :            : 
     729                 :          0 :         return 1;
     730                 :            : }
     731                 :            : 
     732                 :          4 : static int prepare_callback(sd_event_source *s, void *userdata) {
     733                 :          4 :         sd_netlink *rtnl = userdata;
     734                 :            :         int r, e;
     735                 :            :         usec_t until;
     736                 :            : 
     737         [ -  + ]:          4 :         assert(s);
     738         [ -  + ]:          4 :         assert(rtnl);
     739                 :            : 
     740                 :          4 :         e = sd_netlink_get_events(rtnl);
     741         [ -  + ]:          4 :         if (e < 0)
     742                 :          0 :                 return e;
     743                 :            : 
     744                 :          4 :         r = sd_event_source_set_io_events(rtnl->io_event_source, e);
     745         [ -  + ]:          4 :         if (r < 0)
     746                 :          0 :                 return r;
     747                 :            : 
     748                 :          4 :         r = sd_netlink_get_timeout(rtnl, &until);
     749         [ -  + ]:          4 :         if (r < 0)
     750                 :          0 :                 return r;
     751         [ +  - ]:          4 :         if (r > 0) {
     752                 :            :                 int j;
     753                 :            : 
     754                 :          4 :                 j = sd_event_source_set_time(rtnl->time_event_source, until);
     755         [ -  + ]:          4 :                 if (j < 0)
     756                 :          0 :                         return j;
     757                 :            :         }
     758                 :            : 
     759                 :          4 :         r = sd_event_source_set_enabled(rtnl->time_event_source, r > 0);
     760         [ -  + ]:          4 :         if (r < 0)
     761                 :          0 :                 return r;
     762                 :            : 
     763                 :          4 :         return 1;
     764                 :            : }
     765                 :            : 
     766                 :         12 : int sd_netlink_attach_event(sd_netlink *rtnl, sd_event *event, int64_t priority) {
     767                 :            :         int r;
     768                 :            : 
     769   [ -  +  -  + ]:         12 :         assert_return(rtnl, -EINVAL);
     770   [ -  +  -  + ]:         12 :         assert_return(!rtnl->event, -EBUSY);
     771                 :            : 
     772         [ -  + ]:         12 :         assert(!rtnl->io_event_source);
     773         [ -  + ]:         12 :         assert(!rtnl->time_event_source);
     774                 :            : 
     775         [ +  - ]:         12 :         if (event)
     776                 :         12 :                 rtnl->event = sd_event_ref(event);
     777                 :            :         else {
     778                 :          0 :                 r = sd_event_default(&rtnl->event);
     779         [ #  # ]:          0 :                 if (r < 0)
     780                 :          0 :                         return r;
     781                 :            :         }
     782                 :            : 
     783                 :         12 :         r = sd_event_add_io(rtnl->event, &rtnl->io_event_source, rtnl->fd, 0, io_callback, rtnl);
     784         [ -  + ]:         12 :         if (r < 0)
     785                 :          0 :                 goto fail;
     786                 :            : 
     787                 :         12 :         r = sd_event_source_set_priority(rtnl->io_event_source, priority);
     788         [ -  + ]:         12 :         if (r < 0)
     789                 :          0 :                 goto fail;
     790                 :            : 
     791                 :         12 :         r = sd_event_source_set_description(rtnl->io_event_source, "rtnl-receive-message");
     792         [ -  + ]:         12 :         if (r < 0)
     793                 :          0 :                 goto fail;
     794                 :            : 
     795                 :         12 :         r = sd_event_source_set_prepare(rtnl->io_event_source, prepare_callback);
     796         [ -  + ]:         12 :         if (r < 0)
     797                 :          0 :                 goto fail;
     798                 :            : 
     799                 :         12 :         r = sd_event_add_time(rtnl->event, &rtnl->time_event_source, CLOCK_MONOTONIC, 0, 0, time_callback, rtnl);
     800         [ -  + ]:         12 :         if (r < 0)
     801                 :          0 :                 goto fail;
     802                 :            : 
     803                 :         12 :         r = sd_event_source_set_priority(rtnl->time_event_source, priority);
     804         [ -  + ]:         12 :         if (r < 0)
     805                 :          0 :                 goto fail;
     806                 :            : 
     807                 :         12 :         r = sd_event_source_set_description(rtnl->time_event_source, "rtnl-timer");
     808         [ -  + ]:         12 :         if (r < 0)
     809                 :          0 :                 goto fail;
     810                 :            : 
     811                 :         12 :         return 0;
     812                 :            : 
     813                 :          0 : fail:
     814                 :          0 :         sd_netlink_detach_event(rtnl);
     815                 :          0 :         return r;
     816                 :            : }
     817                 :            : 
     818                 :          4 : int sd_netlink_detach_event(sd_netlink *rtnl) {
     819   [ -  +  -  + ]:          4 :         assert_return(rtnl, -EINVAL);
     820   [ -  +  -  + ]:          4 :         assert_return(rtnl->event, -ENXIO);
     821                 :            : 
     822                 :          4 :         rtnl->io_event_source = sd_event_source_unref(rtnl->io_event_source);
     823                 :            : 
     824                 :          4 :         rtnl->time_event_source = sd_event_source_unref(rtnl->time_event_source);
     825                 :            : 
     826                 :          4 :         rtnl->event = sd_event_unref(rtnl->event);
     827                 :            : 
     828                 :          4 :         return 0;
     829                 :            : }
     830                 :            : 
     831                 :         52 : int sd_netlink_add_match(
     832                 :            :                 sd_netlink *rtnl,
     833                 :            :                 sd_netlink_slot **ret_slot,
     834                 :            :                 uint16_t type,
     835                 :            :                 sd_netlink_message_handler_t callback,
     836                 :            :                 sd_netlink_destroy_t destroy_callback,
     837                 :            :                 void *userdata,
     838                 :            :                 const char *description) {
     839                 :         52 :         _cleanup_free_ sd_netlink_slot *slot = NULL;
     840                 :            :         int r;
     841                 :            : 
     842   [ -  +  -  + ]:         52 :         assert_return(rtnl, -EINVAL);
     843   [ -  +  -  + ]:         52 :         assert_return(callback, -EINVAL);
     844   [ -  +  -  + ]:         52 :         assert_return(!rtnl_pid_changed(rtnl), -ECHILD);
     845                 :            : 
     846                 :         52 :         r = netlink_slot_allocate(rtnl, !ret_slot, NETLINK_MATCH_CALLBACK, sizeof(struct match_callback), userdata, description, &slot);
     847         [ -  + ]:         52 :         if (r < 0)
     848                 :          0 :                 return r;
     849                 :            : 
     850                 :         52 :         slot->match_callback.callback = callback;
     851                 :         52 :         slot->match_callback.type = type;
     852                 :            : 
     853   [ +  +  +  +  :         52 :         switch (type) {
                   +  - ]
     854                 :         20 :                 case RTM_NEWLINK:
     855                 :            :                 case RTM_DELLINK:
     856                 :         20 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_LINK);
     857         [ -  + ]:         20 :                         if (r < 0)
     858                 :          0 :                                 return r;
     859                 :            : 
     860                 :         20 :                         break;
     861                 :          8 :                 case RTM_NEWADDR:
     862                 :            :                 case RTM_DELADDR:
     863                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_IFADDR);
     864         [ -  + ]:          8 :                         if (r < 0)
     865                 :          0 :                                 return r;
     866                 :            : 
     867                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_IFADDR);
     868         [ -  + ]:          8 :                         if (r < 0)
     869                 :          0 :                                 return r;
     870                 :            : 
     871                 :          8 :                         break;
     872                 :          8 :                 case RTM_NEWNEIGH:
     873                 :            :                 case RTM_DELNEIGH:
     874                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_NEIGH);
     875         [ -  + ]:          8 :                         if (r < 0)
     876                 :          0 :                                 return r;
     877                 :            : 
     878                 :          8 :                         break;
     879                 :          8 :                 case RTM_NEWROUTE:
     880                 :            :                 case RTM_DELROUTE:
     881                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_ROUTE);
     882         [ -  + ]:          8 :                         if (r < 0)
     883                 :          0 :                                 return r;
     884                 :            : 
     885                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_ROUTE);
     886         [ -  + ]:          8 :                         if (r < 0)
     887                 :          0 :                                 return r;
     888                 :          8 :                         break;
     889                 :          8 :                 case RTM_NEWRULE:
     890                 :            :                 case RTM_DELRULE:
     891                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV4_RULE);
     892         [ -  + ]:          8 :                         if (r < 0)
     893                 :          0 :                                 return r;
     894                 :            : 
     895                 :          8 :                         r = socket_broadcast_group_ref(rtnl, RTNLGRP_IPV6_RULE);
     896         [ -  + ]:          8 :                         if (r < 0)
     897                 :          0 :                                 return r;
     898                 :          8 :                         break;
     899                 :          0 :                 default:
     900                 :          0 :                         return -EOPNOTSUPP;
     901                 :            :         }
     902                 :            : 
     903   [ -  +  +  + ]:         52 :         LIST_PREPEND(match_callbacks, rtnl->match_callbacks, &slot->match_callback);
     904                 :            : 
     905                 :            :         /* Set this at last. Otherwise, some failures in above call the destroy callback but some do not. */
     906                 :         52 :         slot->destroy_callback = destroy_callback;
     907                 :            : 
     908         [ +  + ]:         52 :         if (ret_slot)
     909                 :          8 :                 *ret_slot = slot;
     910                 :            : 
     911                 :         52 :         TAKE_PTR(slot);
     912                 :            : 
     913                 :         52 :         return 0;
     914                 :            : }

Generated by: LCOV version 1.14