LCOV - code coverage report
Current view: top level - lib/net - alertsock.c (source / functions) Hit Total Coverage
Test: lcov.info Lines: 94 118 79.7 %
Date: 2021-11-24 03:28:48 Functions: 11 12 91.7 %

          Line data    Source code
       1             : /* Copyright (c) 2003-2004, Roger Dingledine
       2             :  * Copyright (c) 2004-2006, Roger Dingledine, Nick Mathewson.
       3             :  * Copyright (c) 2007-2021, The Tor Project, Inc. */
       4             : /* See LICENSE for licensing information */
       5             : 
       6             : /**
       7             :  * \file alertsock.c
       8             :  *
       9             :  * \brief Use a socket to alert the main thread from a worker thread.
      10             :  *
      11             :  * Because our main loop spends all of its time in select, epoll, kqueue, or
      12             :  * etc, we need a way to wake up the main loop from another thread.  This code
      13             :  * tries to provide the fastest reasonable way to do that, depending on our
      14             :  * platform.
      15             :  **/
      16             : 
      17             : #include "orconfig.h"
      18             : #include "lib/net/alertsock.h"
      19             : #include "lib/net/socket.h"
      20             : #include "lib/log/util_bug.h"
      21             : 
      22             : #ifdef HAVE_SYS_EVENTFD_H
      23             : #include <sys/eventfd.h>
      24             : #endif
      25             : #ifdef HAVE_FCNTL_H
      26             : #include <fcntl.h>
      27             : #endif
      28             : #ifdef HAVE_UNISTD_H
      29             : #include <unistd.h>
      30             : #endif
      31             : #ifdef HAVE_SYS_SOCKET_H
      32             : #include <sys/socket.h>
      33             : #endif
      34             : #ifdef _WIN32
      35             : #include <winsock2.h>
      36             : #endif
      37             : 
      38             : #if defined(HAVE_EVENTFD) || defined(HAVE_PIPE)
      39             : /* As write(), but retry on EINTR, and return the negative error code on
      40             :  * error. */
      41             : static int
      42       16236 : write_ni(int fd, const void *buf, size_t n)
      43             : {
      44       16236 :   int r;
      45       16236 :  again:
      46       16236 :   r = (int) write(fd, buf, n);
      47       16236 :   if (r < 0) {
      48           0 :     if (errno == EINTR)
      49           0 :       goto again;
      50             :     else
      51           0 :       return -errno;
      52             :   }
      53             :   return r;
      54             : }
      55             : /* As read(), but retry on EINTR, and return the negative error code on error.
      56             :  */
      57             : static int
      58       23744 : read_ni(int fd, void *buf, size_t n)
      59             : {
      60       23744 :   int r;
      61       23744 :  again:
      62       23744 :   r = (int) read(fd, buf, n);
      63       23744 :   if (r < 0) {
      64        7541 :     if (errno == EINTR)
      65           0 :       goto again;
      66             :     else
      67        7541 :       return -errno;
      68             :   }
      69             :   return r;
      70             : }
      71             : #endif /* defined(HAVE_EVENTFD) || defined(HAVE_PIPE) */
      72             : 
      73             : /** As send(), but retry on EINTR, and return the negative error code on
      74             :  * error. */
      75             : static int
      76        2814 : send_ni(int fd, const void *buf, size_t n, int flags)
      77             : {
      78        2814 :   int r;
      79        2814 :  again:
      80        2814 :   r = (int) send(fd, buf, n, flags);
      81        2814 :   if (r < 0) {
      82           0 :     int error = tor_socket_errno(fd);
      83           0 :     if (ERRNO_IS_EINTR(error))
      84           0 :       goto again;
      85             :     else
      86           0 :       return -error;
      87             :   }
      88             :   return r;
      89             : }
      90             : 
      91             : /** As recv(), but retry on EINTR, and return the negative error code on
      92             :  * error. */
      93             : static int
      94        5597 : recv_ni(int fd, void *buf, size_t n, int flags)
      95             : {
      96        5597 :   int r;
      97        5597 :  again:
      98        5597 :   r = (int) recv(fd, buf, n, flags);
      99        5597 :   if (r < 0) {
     100        2797 :     int error = tor_socket_errno(fd);
     101        2797 :     if (ERRNO_IS_EINTR(error))
     102           0 :       goto again;
     103             :     else
     104        2797 :       return -error;
     105             :   }
     106             :   return r;
     107             : }
     108             : 
     109             : #ifdef HAVE_EVENTFD
     110             : /* Increment the event count on an eventfd <b>fd</b> */
     111             : static int
     112        8675 : eventfd_alert(int fd)
     113             : {
     114        8675 :   uint64_t u = 1;
     115        8675 :   int r = write_ni(fd, (void*)&u, sizeof(u));
     116        8675 :   if (r < 0 && -r != EAGAIN)
     117           0 :     return -1;
     118             :   return 0;
     119             : }
     120             : 
     121             : /* Drain all events from an eventfd <b>fd</b>. */
     122             : static int
     123        8657 : eventfd_drain(int fd)
     124             : {
     125        8657 :   uint64_t u = 0;
     126        8657 :   int r = read_ni(fd, (void*)&u, sizeof(u));
     127        8657 :   if (r < 0 && -r != EAGAIN)
     128           0 :     return r;
     129             :   return 0;
     130             : }
     131             : #endif /* defined(HAVE_EVENTFD) */
     132             : 
     133             : #ifdef HAVE_PIPE
     134             : /** Send a byte over a pipe. Return 0 on success or EAGAIN; -1 on error */
     135             : static int
     136        7561 : pipe_alert(int fd)
     137             : {
     138        7561 :   ssize_t r = write_ni(fd, "x", 1);
     139        7561 :   if (r < 0 && -r != EAGAIN)
     140           0 :     return (int)r;
     141             :   return 0;
     142             : }
     143             : 
     144             : /** Drain all input from a pipe <b>fd</b> and ignore it.  Return 0 on
     145             :  * success, -1 on error. */
     146             : static int
     147        7541 : pipe_drain(int fd)
     148             : {
     149       15087 :   char buf[32];
     150       15087 :   ssize_t r;
     151       15087 :   do {
     152       15087 :     r = read_ni(fd, buf, sizeof(buf));
     153       15087 :   } while (r > 0);
     154        7541 :   if (r < 0 && errno != EAGAIN)
     155           0 :     return -errno;
     156             :   /* A value of r = 0 means EOF on the fd so successfully drained. */
     157             :   return 0;
     158             : }
     159             : #endif /* defined(HAVE_PIPE) */
     160             : 
     161             : /** Send a byte on socket <b>fd</b>t.  Return 0 on success or EAGAIN,
     162             :  * -1 on error. */
     163             : static int
     164        2814 : sock_alert(tor_socket_t fd)
     165             : {
     166        2814 :   ssize_t r = send_ni(fd, "x", 1, 0);
     167        2814 :   if (r < 0 && !ERRNO_IS_EAGAIN(-r))
     168           0 :     return (int)r;
     169             :   return 0;
     170             : }
     171             : 
     172             : /** Drain all the input from a socket <b>fd</b>, and ignore it.  Return 0 on
     173             :  * success, -errno on error. */
     174             : static int
     175        2797 : sock_drain(tor_socket_t fd)
     176             : {
     177        5597 :   char buf[32];
     178        5597 :   ssize_t r;
     179        5597 :   do {
     180        5597 :     r = recv_ni(fd, buf, sizeof(buf), 0);
     181        5597 :   } while (r > 0);
     182        2797 :   if (r < 0 && !ERRNO_IS_EAGAIN(-r))
     183           0 :     return (int)r;
     184             :   /* A value of r = 0 means EOF on the fd so successfully drained. */
     185             :   return 0;
     186             : }
     187             : 
     188             : /** Allocate a new set of alert sockets, and set the appropriate function
     189             :  * pointers, in <b>socks_out</b>. */
     190             : int
     191           7 : alert_sockets_create(alert_sockets_t *socks_out, uint32_t flags)
     192             : {
     193           7 :   tor_socket_t socks[2] = { TOR_INVALID_SOCKET, TOR_INVALID_SOCKET };
     194             : 
     195             : #ifdef HAVE_EVENTFD
     196             :   /* First, we try the Linux eventfd() syscall.  This gives a 64-bit counter
     197             :    * associated with a single file descriptor. */
     198             : #if defined(EFD_CLOEXEC) && defined(EFD_NONBLOCK)
     199           7 :   if (!(flags & ASOCKS_NOEVENTFD2))
     200           3 :     socks[0] = eventfd(0, EFD_CLOEXEC|EFD_NONBLOCK);
     201             : #endif
     202           7 :   if (socks[0] < 0 && !(flags & ASOCKS_NOEVENTFD)) {
     203           1 :     socks[0] = eventfd(0,0);
     204           1 :     if (socks[0] >= 0) {
     205           2 :       if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 ||
     206           1 :           set_socket_nonblocking(socks[0]) < 0) {
     207             :         // LCOV_EXCL_START -- if eventfd succeeds, fcntl will.
     208             :         tor_assert_nonfatal_unreached();
     209             :         close(socks[0]);
     210             :         return -1;
     211             :         // LCOV_EXCL_STOP
     212             :       }
     213             :     }
     214             :   }
     215           7 :   if (socks[0] >= 0) {
     216           4 :     socks_out->read_fd = socks_out->write_fd = socks[0];
     217           4 :     socks_out->alert_fn = eventfd_alert;
     218           4 :     socks_out->drain_fn = eventfd_drain;
     219           4 :     return 0;
     220             :   }
     221             : #endif /* defined(HAVE_EVENTFD) */
     222             : 
     223             : #ifdef HAVE_PIPE2
     224             :   /* Now we're going to try pipes. First type the pipe2() syscall, if we
     225             :    * have it, so we can save some calls... */
     226           4 :   if (!(flags & ASOCKS_NOPIPE2) &&
     227           1 :       pipe2(socks, O_NONBLOCK|O_CLOEXEC) == 0) {
     228           1 :     socks_out->read_fd = socks[0];
     229           1 :     socks_out->write_fd = socks[1];
     230           1 :     socks_out->alert_fn = pipe_alert;
     231           1 :     socks_out->drain_fn = pipe_drain;
     232           1 :     return 0;
     233             :   }
     234             : #endif /* defined(HAVE_PIPE2) */
     235             : 
     236             : #ifdef HAVE_PIPE
     237             :   /* Now try the regular pipe() syscall.  Pipes have a bit lower overhead than
     238             :    * socketpairs, fwict. */
     239           3 :   if (!(flags & ASOCKS_NOPIPE) &&
     240           1 :       pipe(socks) == 0) {
     241           2 :     if (fcntl(socks[0], F_SETFD, FD_CLOEXEC) < 0 ||
     242           2 :         fcntl(socks[1], F_SETFD, FD_CLOEXEC) < 0 ||
     243           2 :         set_socket_nonblocking(socks[0]) < 0 ||
     244           1 :         set_socket_nonblocking(socks[1]) < 0) {
     245             :       // LCOV_EXCL_START -- if pipe succeeds, you can fcntl the output
     246             :       tor_assert_nonfatal_unreached();
     247             :       close(socks[0]);
     248             :       close(socks[1]);
     249             :       return -1;
     250             :       // LCOV_EXCL_STOP
     251             :     }
     252           1 :     socks_out->read_fd = socks[0];
     253           1 :     socks_out->write_fd = socks[1];
     254           1 :     socks_out->alert_fn = pipe_alert;
     255           1 :     socks_out->drain_fn = pipe_drain;
     256           1 :     return 0;
     257             :   }
     258             : #endif /* defined(HAVE_PIPE) */
     259             : 
     260             :   /* If nothing else worked, fall back on socketpair(). */
     261           2 :   if (!(flags & ASOCKS_NOSOCKETPAIR) &&
     262           1 :       tor_socketpair(AF_UNIX, SOCK_STREAM, 0, socks) == 0) {
     263           2 :     if (set_socket_nonblocking(socks[0]) < 0 ||
     264           1 :         set_socket_nonblocking(socks[1])) {
     265             :       // LCOV_EXCL_START -- if socketpair worked, you can make it nonblocking.
     266             :       tor_assert_nonfatal_unreached();
     267             :       tor_close_socket(socks[0]);
     268             :       tor_close_socket(socks[1]);
     269             :       return -1;
     270             :       // LCOV_EXCL_STOP
     271             :     }
     272           1 :     socks_out->read_fd = socks[0];
     273           1 :     socks_out->write_fd = socks[1];
     274           1 :     socks_out->alert_fn = sock_alert;
     275           1 :     socks_out->drain_fn = sock_drain;
     276           1 :     return 0;
     277             :   }
     278             :   return -1;
     279             : }
     280             : 
     281             : /** Close the sockets in <b>socks</b>. */
     282             : void
     283           0 : alert_sockets_close(alert_sockets_t *socks)
     284             : {
     285           0 :   if (socks->alert_fn == sock_alert) {
     286             :     /* they are sockets. */
     287           0 :     tor_close_socket(socks->read_fd);
     288           0 :     tor_close_socket(socks->write_fd);
     289             :   } else {
     290           0 :     close(socks->read_fd);
     291           0 :     if (socks->write_fd != socks->read_fd)
     292           0 :       close(socks->write_fd);
     293             :   }
     294           0 :   socks->read_fd = socks->write_fd = -1;
     295           0 : }

Generated by: LCOV version 1.14