LCOV - code coverage report
Current view: top level - shared - nscd-flush.c (source / functions) Hit Total Coverage
Test: systemd_full.info Lines: 0 60 0.0 %
Date: 2019-08-23 13:36:53 Functions: 0 2 0.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 0 76 0.0 %

           Branch data     Line data    Source code
       1                 :            : /* SPDX-License-Identifier: LGPL-2.1+ */
       2                 :            : #include <sys/poll.h>
       3                 :            : 
       4                 :            : #include "fd-util.h"
       5                 :            : #include "io-util.h"
       6                 :            : #include "nscd-flush.h"
       7                 :            : #include "socket-util.h"
       8                 :            : #include "strv.h"
       9                 :            : #include "time-util.h"
      10                 :            : 
      11                 :            : #define NSCD_FLUSH_CACHE_TIMEOUT_USEC (5*USEC_PER_SEC)
      12                 :            : 
      13                 :            : struct nscdInvalidateRequest {
      14                 :            :         int32_t version;
      15                 :            :         int32_t type; /* in glibc this is an enum. We don't replicate this here 1:1. Also, wtf, how unportable is that
      16                 :            :                        * even? */
      17                 :            :         int32_t key_len;
      18                 :            :         char dbname[];
      19                 :            : };
      20                 :            : 
      21                 :            : static const union sockaddr_union nscd_sa = {
      22                 :            :         .un.sun_family = AF_UNIX,
      23                 :            :         .un.sun_path = "/run/nscd/socket",
      24                 :            : };
      25                 :            : 
      26                 :          0 : static int nscd_flush_cache_one(const char *database, usec_t end) {
      27                 :          0 :         size_t req_size, has_written = 0, has_read = 0, l;
      28                 :            :         struct nscdInvalidateRequest *req;
      29                 :          0 :         _cleanup_close_ int fd = -1;
      30                 :            :         int32_t resp;
      31                 :            :         int events;
      32                 :            : 
      33         [ #  # ]:          0 :         assert(database);
      34                 :            : 
      35                 :          0 :         l = strlen(database);
      36                 :          0 :         req_size = offsetof(struct nscdInvalidateRequest, dbname) + l + 1;
      37                 :            : 
      38                 :          0 :         req = alloca(req_size);
      39                 :          0 :         *req = (struct nscdInvalidateRequest) {
      40                 :            :                 .version = 2,
      41                 :            :                 .type = 10,
      42                 :          0 :                 .key_len = l + 1,
      43                 :            :         };
      44                 :            : 
      45                 :          0 :         strcpy(req->dbname, database);
      46                 :            : 
      47                 :          0 :         fd = socket(AF_UNIX, SOCK_STREAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0);
      48         [ #  # ]:          0 :         if (fd < 0)
      49         [ #  # ]:          0 :                 return log_debug_errno(errno, "Failed to allocate nscd socket: %m");
      50                 :            : 
      51                 :            :         /* Note: connect() returns EINPROGRESS if O_NONBLOCK is set and establishing a connection takes time. The
      52                 :            :          * kernel lets us know this way that the connection is now being established, and we should watch with poll()
      53                 :            :          * to learn when it is fully established. That said, AF_UNIX on Linux never triggers this IRL (connect() is
      54                 :            :          * always instant on AF_UNIX), hence handling this is mostly just an exercise in defensive, protocol-agnostic
      55                 :            :          * programming.
      56                 :            :          *
      57                 :            :          * connect() returns EAGAIN if the socket's backlog limit has been reached. When we see this we give up right
      58                 :            :          * away, after all this entire function here is written in a defensive style so that a non-responding nscd
      59                 :            :          * doesn't stall us for good. (Even if we wanted to handle this better: the Linux kernel doesn't really have a
      60                 :            :          * nice way to connect() to a server synchronously with a time limit that would also cover dealing with the
      61                 :            :          * backlog limit. After all SO_RCVTIMEO and SR_SNDTIMEO don't apply to connect(), and alarm() is frickin' ugly
      62                 :            :          * and not really reasonably usable from threads-aware code.) */
      63   [ #  #  #  #  :          0 :         if (connect(fd, &nscd_sa.sa, SOCKADDR_UN_LEN(nscd_sa.un)) < 0) {
                   #  # ]
      64         [ #  # ]:          0 :                 if (errno == EAGAIN)
      65         [ #  # ]:          0 :                         return log_debug_errno(errno, "nscd is overloaded (backlog limit reached) and refuses to take further connections: %m");
      66         [ #  # ]:          0 :                 if (errno != EINPROGRESS)
      67         [ #  # ]:          0 :                         return log_debug_errno(errno, "Failed to connect to nscd socket: %m");
      68                 :            : 
      69                 :            :                 /* Continue in case of EINPROGRESS, but don't bother with send() or recv() until being notified that
      70                 :            :                  * establishing the connection is complete. */
      71                 :          0 :                 events = 0;
      72                 :            :         } else
      73                 :          0 :                 events = POLLIN|POLLOUT; /* Let's assume initially that we can write and read to the fd, to suppress
      74                 :            :                                           * one poll() invocation */
      75                 :          0 :         for (;;) {
      76                 :            :                 usec_t p;
      77                 :            : 
      78         [ #  # ]:          0 :                 if (events & POLLOUT) {
      79                 :            :                         ssize_t m;
      80                 :            : 
      81         [ #  # ]:          0 :                         assert(has_written < req_size);
      82                 :            : 
      83                 :          0 :                         m = send(fd, (uint8_t*) req + has_written, req_size - has_written, MSG_NOSIGNAL);
      84         [ #  # ]:          0 :                         if (m < 0) {
      85         [ #  # ]:          0 :                                 if (errno != EAGAIN) /* Note that EAGAIN is returned by the kernel whenever it can't
      86                 :            :                                                       * take the data right now, and that includes if the connect() is
      87                 :            :                                                       * asynchronous and we saw EINPROGRESS on it, and it hasn't
      88                 :            :                                                       * completed yet. */
      89         [ #  # ]:          0 :                                         return log_debug_errno(errno, "Failed to write to nscd socket: %m");
      90                 :            :                         } else
      91                 :          0 :                                 has_written += m;
      92                 :            :                 }
      93                 :            : 
      94         [ #  # ]:          0 :                 if (events & (POLLIN|POLLERR|POLLHUP)) {
      95                 :            :                         ssize_t m;
      96                 :            : 
      97         [ #  # ]:          0 :                         if (has_read >= sizeof(resp))
      98         [ #  # ]:          0 :                                 return log_debug_errno(SYNTHETIC_ERRNO(EIO), "Response from nscd longer than expected: %m");
      99                 :            : 
     100                 :          0 :                         m = recv(fd, (uint8_t*) &resp + has_read, sizeof(resp) - has_read, 0);
     101         [ #  # ]:          0 :                         if (m < 0) {
     102         [ #  # ]:          0 :                                 if (errno != EAGAIN)
     103         [ #  # ]:          0 :                                         return log_debug_errno(errno, "Failed to read from nscd socket: %m");
     104         [ #  # ]:          0 :                         } else if (m == 0) { /* EOF */
     105   [ #  #  #  # ]:          0 :                                 if (has_read == 0 && has_written >= req_size) /* Older nscd immediately terminated the
     106                 :            :                                                                                * connection, accept that as OK */
     107                 :          0 :                                         return 1;
     108                 :            : 
     109         [ #  # ]:          0 :                                 return log_debug_errno(SYNTHETIC_ERRNO(EIO), "nscd prematurely ended connection.");
     110                 :            :                         } else
     111                 :          0 :                                 has_read += m;
     112                 :            :                 }
     113                 :            : 
     114   [ #  #  #  # ]:          0 :                 if (has_written >= req_size && has_read >= sizeof(resp)) { /* done? */
     115         [ #  # ]:          0 :                         if (resp < 0)
     116         [ #  # ]:          0 :                                 return log_debug_errno(SYNTHETIC_ERRNO(EBADMSG), "nscd sent us a negative error number: %i", resp);
     117         [ #  # ]:          0 :                         if (resp > 0)
     118         [ #  # ]:          0 :                                 return log_debug_errno(resp, "nscd return failure code on invalidating '%s'.", database);
     119                 :          0 :                         return 1;
     120                 :            :                 }
     121                 :            : 
     122                 :          0 :                 p = now(CLOCK_MONOTONIC);
     123         [ #  # ]:          0 :                 if (p >= end)
     124                 :          0 :                         return -ETIMEDOUT;
     125                 :            : 
     126         [ #  # ]:          0 :                 events = fd_wait_for_event(fd, POLLIN | (has_written < req_size ? POLLOUT : 0), end - p);
     127         [ #  # ]:          0 :                 if (events < 0)
     128                 :          0 :                         return events;
     129                 :            :         }
     130                 :            : }
     131                 :            : 
     132                 :          0 : int nscd_flush_cache(char **databases) {
     133                 :            :         usec_t end;
     134                 :          0 :         int r = 0;
     135                 :            :         char **i;
     136                 :            : 
     137                 :            :         /* Tries to invalidate the specified database in nscd. We do this carefully, with a 5s time-out, so that we
     138                 :            :          * don't block indefinitely on another service. */
     139                 :            : 
     140                 :          0 :         end = usec_add(now(CLOCK_MONOTONIC), NSCD_FLUSH_CACHE_TIMEOUT_USEC);
     141                 :            : 
     142   [ #  #  #  # ]:          0 :         STRV_FOREACH(i, databases) {
     143                 :            :                 int k;
     144                 :            : 
     145                 :          0 :                 k = nscd_flush_cache_one(*i, end);
     146   [ #  #  #  # ]:          0 :                 if (k < 0 && r >= 0)
     147                 :          0 :                         r = k;
     148                 :            :         }
     149                 :            : 
     150                 :          0 :         return r;
     151                 :            : }

Generated by: LCOV version 1.14