LCOV - code coverage report
Current view: top level - eventdispatcher - local_dgram_server.cpp (source / functions) Hit Total Coverage
Test: coverage.info Lines: 16 71 22.5 %
Date: 2021-09-19 09:06:58 Functions: 4 6 66.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : // Copyright (c) 2012-2021  Made to Order Software Corp.  All Rights Reserved
       2             : //
       3             : // https://snapwebsites.org/project/eventdispatcher
       4             : // contact@m2osw.com
       5             : //
       6             : // This program is free software; you can redistribute it and/or modify
       7             : // it under the terms of the GNU General Public License as published by
       8             : // the Free Software Foundation; either version 2 of the License, or
       9             : // (at your option) any later version.
      10             : //
      11             : // This program is distributed in the hope that it will be useful,
      12             : // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             : // GNU General Public License for more details.
      15             : //
      16             : // You should have received a copy of the GNU General Public License
      17             : // along with this program; if not, write to the Free Software
      18             : // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
      19             : 
      20             : /** \file
      21             :  * \brief Event dispatch class.
      22             :  *
      23             :  * Class used to handle events.
      24             :  */
      25             : 
      26             : // to get the POLLRDHUP definition
      27             : #ifndef _GNU_SOURCE
      28             : #define _GNU_SOURCE
      29             : #endif
      30             : 
      31             : 
      32             : // self
      33             : //
      34             : #include    "eventdispatcher/local_dgram_server.h"
      35             : 
      36             : #include    "eventdispatcher/exception.h"
      37             : 
      38             : 
      39             : // snapwebsites lib
      40             : //
      41             : #include    <snaplogger/message.h>
      42             : 
      43             : 
      44             : // C lib
      45             : //
      46             : #include    <sys/stat.h>
      47             : #include    <poll.h>
      48             : //#include    <string.h>
      49             : 
      50             : 
      51             : // last include
      52             : //
      53             : #include    <snapdev/poison.h>
      54             : 
      55             : 
      56             : 
      57             : 
      58             : namespace ed
      59             : {
      60             : 
      61             : 
      62             : 
      63             : /** \brief Initialize a UDP server object.
      64             :  *
      65             :  * This function initializes a UDP server object making it ready to
      66             :  * receive messages.
      67             :  *
      68             :  * The server address and port are specified in the constructor so
      69             :  * if you need to receive messages from several different addresses
      70             :  * and/or port, you'll have to create a server for each.
      71             :  *
      72             :  * The address is a string and it can represent an IPv4 or IPv6
      73             :  * address.
      74             :  *
      75             :  * Note that this function calls bind() to listen to the socket
      76             :  * at the specified address. To accept data on different UDP addresses
      77             :  * and ports, multiple UDP servers must be created.
      78             :  *
      79             :  * \note
      80             :  * The socket is open in this process. If you fork() or exec() then the
      81             :  * socket will be closed by the operating system.
      82             :  *
      83             :  * \warning
      84             :  * We only make use of the first address found by getaddrinfo(). All
      85             :  * the other addresses are ignored.
      86             :  *
      87             :  * \warning
      88             :  * Remember that the multicast feature under Linux is shared by all
      89             :  * processes running on that server. Any one process can listen for
      90             :  * any and all multicast messages from any other process. Our
      91             :  * implementation limits the multicast from a specific IP. However.
      92             :  * other processes can also receive your packets and there is nothing
      93             :  * you can do to prevent that.
      94             :  *
      95             :  * \exception udp_client_server_runtime_error
      96             :  * The udp_client_server_runtime_error exception is raised when the address
      97             :  * and port combination cannot be resolved or if the socket cannot be
      98             :  * opened.
      99             :  *
     100             :  * \param[in] address  The address to connect/listen to.
     101             :  * \param[in] sequential  Whether the packets have to be 100% sequential.
     102             :  * \param[in] close_on_exec  Whether the socket has to be closed on execve().
     103             :  */
     104           2 : local_dgram_server::local_dgram_server(
     105             :               addr::unix const & address
     106             :             , bool sequential
     107             :             , bool close_on_exec
     108           2 :             , bool force_reuse_addr)
     109           2 :     : local_dgram_base(address, sequential, close_on_exec)
     110             : {
     111           2 :     if(f_address.is_unnamed())
     112             :     {
     113             :         // for an unnamed socket, we do not bind at all the user is
     114             :         // responsible for knowing where to read and where to write
     115             :         //
     116           0 :         return;
     117             :     }
     118             : 
     119           2 :     sockaddr_un un;
     120           2 :     f_address.get_un(un);
     121             : 
     122             :     // bind to the first address
     123             :     //
     124           2 :     int r(-1);
     125           2 :     if(f_address.is_file())
     126             :     {
     127             :         // TODO: this is common code to the local_stream_server_connection.cpp
     128             :         //
     129             :         // a Unix file socket must create a new socket file to prove unicity
     130             :         // if the file already exists, even if it isn't used, the bind() call
     131             :         // will fail; if the file exists and the force_reuse_addr is true this
     132             :         // this function attempts to delete the file if it is a socket and we
     133             :         // can't connect to it (i.e. "lost file")
     134             :         //
     135           2 :         struct stat st = {};
     136           2 :         if(stat(un.sun_path, &st) == 0)
     137             :         {
     138           0 :             if(!S_ISSOCK(st.st_mode))
     139             :             {
     140           0 :                 SNAP_LOG_ERROR
     141           0 :                     << "file \""
     142             :                     << un.sun_path
     143             :                     << "\" is not a socket; cannot listen on address \""
     144           0 :                     << f_address.to_uri()
     145             :                     << "\"."
     146             :                     << SNAP_LOG_SEND;
     147           0 :                 throw event_dispatcher_runtime_error("file already exists and it is not a socket, can't create an AF_UNIX server");
     148             :             }
     149             : 
     150           0 :             if(!force_reuse_addr)
     151             :             {
     152           0 :                 SNAP_LOG_ERROR
     153           0 :                     << "file socket \""
     154             :                     << un.sun_path
     155             :                     << "\" already in use (errno: "
     156           0 :                     << std::to_string(EADDRINUSE)
     157             :                     << " -- "
     158           0 :                     << strerror(EADDRINUSE)
     159             :                     << "); cannot listen on address \""
     160           0 :                     << f_address.to_uri()
     161             :                     << "\"."
     162             :                     << SNAP_LOG_SEND;
     163           0 :                 throw event_dispatcher_runtime_error("socket already exists, can't create an AF_UNIX server");
     164             :             }
     165             : 
     166           0 :             r = f_address.unlink();
     167           0 :             if(r != 0
     168           0 :             && errno != ENOENT)
     169             :             {
     170           0 :                 SNAP_LOG_ERROR
     171           0 :                     << "not able to delete file socket \""
     172             :                     << un.sun_path
     173             :                     << "\"; socket already in use (errno: "
     174           0 :                     << std::to_string(EADDRINUSE)
     175             :                     << " -- "
     176           0 :                     << strerror(EADDRINUSE)
     177             :                     << "); cannot listen on address \""
     178           0 :                     << f_address.to_uri()
     179             :                     << "\"."
     180             :                     << SNAP_LOG_SEND;
     181           0 :                 throw event_dispatcher_runtime_error("could not unlink socket to reuse it as an AF_UNIX server");
     182             :             }
     183             :         }
     184           4 :         r = bind(
     185           4 :                   f_socket.get()
     186             :                 , reinterpret_cast<sockaddr const *>(&un)
     187             :                 , sizeof(struct sockaddr_un));
     188             :     }
     189             :     else
     190             :     {
     191             :         // we want to limit the size because otherwise it would include
     192             :         // the '\0's after the specified name
     193             :         //
     194           0 :         std::size_t const size(sizeof(un.sun_family)
     195             :                                         + 1 // for the '\0' in sun_path[0]
     196           0 :                                         + strlen(un.sun_path + 1));
     197           0 :         r = bind(
     198           0 :                   f_socket.get()
     199             :                 , reinterpret_cast<sockaddr const *>(&un)
     200             :                 , size);
     201             :     }
     202             : 
     203           2 :     if(r != 0)
     204             :     {
     205           0 :         int const e(errno);
     206           0 :         SNAP_LOG_ERROR
     207           0 :                 << "the bind() function failed with errno: "
     208             :                 << e
     209             :                 << " ("
     210           0 :                 << strerror(e)
     211             :                 << "); Unix address \""
     212           0 :                 << f_address.to_uri()
     213             :                 << "\"."
     214             :                 << SNAP_LOG_SEND;
     215             :         throw event_dispatcher_runtime_error(
     216             :                 "could not bind AF_UNIX datagram socket to \""
     217           0 :                 + f_address.to_uri()
     218           0 :                 + "\"");
     219             :     }
     220             : }
     221             : 
     222             : 
     223             : /** \brief Wait on a message.
     224             :  *
     225             :  * This function waits until a message is received on this UDP server.
     226             :  * There are no means to return from this function except by receiving
     227             :  * a message. Remember that UDP does not have a connect state so whether
     228             :  * another process quits does not change the status of this UDP server
     229             :  * and thus it continues to wait forever.
     230             :  *
     231             :  * Note that you may change the type of socket by making it non-blocking
     232             :  * (use the get_socket() to retrieve the socket identifier) in which
     233             :  * case this function will not block if no message is available. Instead
     234             :  * it returns immediately.
     235             :  *
     236             :  * \param[in] msg  The buffer where the message is saved.
     237             :  * \param[in] max_size  The maximum size the message (i.e. size of the \p msg buffer.)
     238             :  *
     239             :  * \return The number of bytes read or -1 if an error occurs.
     240             :  */
     241           6 : int local_dgram_server::recv(char * msg, size_t max_size)
     242             : {
     243           6 :     return static_cast<int>(::recv(f_socket.get(), msg, max_size, 0));
     244             : }
     245             : 
     246             : 
     247             : /** \brief Wait for data to come in.
     248             :  *
     249             :  * This function waits for a given amount of time for data to come in. If
     250             :  * no data comes in after max_wait_ms, the function returns with -1 and
     251             :  * errno set to EAGAIN.
     252             :  *
     253             :  * The socket is expected to be a blocking socket (the default,) although
     254             :  * it is possible to setup the socket as non-blocking if necessary for
     255             :  * some other reason.
     256             :  *
     257             :  * This function blocks for a maximum amount of time as defined by
     258             :  * max_wait_ms. It may return sooner with an error or a message.
     259             :  *
     260             :  * \param[in] msg  The buffer where the message will be saved.
     261             :  * \param[in] max_size  The size of the \p msg buffer in bytes.
     262             :  * \param[in] max_wait_ms  The maximum number of milliseconds to wait for a message.
     263             :  *
     264             :  * \return -1 if an error occurs or the function timed out, the number of bytes received otherwise.
     265             :  */
     266           0 : int local_dgram_server::timed_recv(char * msg, size_t const max_size, int const max_wait_ms)
     267             : {
     268           0 :     pollfd fd;
     269           0 :     fd.events = POLLIN | POLLPRI | POLLRDHUP;
     270           0 :     fd.fd = f_socket.get();
     271           0 :     int const retval(poll(&fd, 1, max_wait_ms));
     272           0 :     if(retval == -1)
     273             :     {
     274             :         // poll() sets errno accordingly
     275           0 :         return -1;
     276             :     }
     277           0 :     if(retval > 0)
     278             :     {
     279             :         // our socket has data
     280           0 :         return static_cast<int>(::recv(f_socket.get(), msg, max_size, 0));
     281             :     }
     282             : 
     283             :     // our socket has no data
     284           0 :     errno = EAGAIN;
     285           0 :     return -1;
     286             : }
     287             : 
     288             : 
     289             : /** \brief Wait for data to come in, but return a std::string.
     290             :  *
     291             :  * This function waits for a given amount of time for data to come in. If
     292             :  * no data comes in after max_wait_ms, the function returns with -1 and
     293             :  * errno set to EAGAIN.
     294             :  *
     295             :  * The socket is expected to be a blocking socket (the default,) although
     296             :  * it is possible to setup the socket as non-blocking if necessary for
     297             :  * some other reason.
     298             :  *
     299             :  * This function blocks for a maximum amount of time as defined by
     300             :  * max_wait_ms. It may return sooner with an error or a message.
     301             :  *
     302             :  * \param[in] bufsize  The maximum size of the returned string in bytes.
     303             :  * \param[in] max_wait_ms  The maximum number of milliseconds to wait for a message.
     304             :  *
     305             :  * \return received string. an empty string if not data received or error.
     306             :  *
     307             :  * \sa timed_recv()
     308             :  */
     309           0 : std::string local_dgram_server::timed_recv(int const bufsize, int const max_wait_ms)
     310             : {
     311           0 :     std::vector<char> buf;
     312           0 :     buf.resize( bufsize + 1, '\0' ); // +1 for ending \0
     313           0 :     int const r(timed_recv(&buf[0], bufsize, max_wait_ms));
     314           0 :     if(r <= -1)
     315             :     {
     316             :         // Timed out, so return empty string.
     317             :         // TBD: could std::string() smash errno?
     318             :         //
     319           0 :         return std::string();
     320             :     }
     321             : 
     322             :     // Resize the buffer, then convert to std string
     323             :     //
     324           0 :     buf.resize(r + 1, '\0');
     325             : 
     326           0 :     std::string word;
     327           0 :     word.resize(r);
     328           0 :     std::copy(buf.begin(), buf.end(), word.begin());
     329             : 
     330           0 :     return word;
     331             : }
     332             : 
     333             : 
     334             : 
     335             : 
     336           6 : } // namespace ed
     337             : // vim: ts=4 sw=4 et

Generated by: LCOV version 1.13