"Fossies" - the Fresh Open Source Software Archive

Member "hydra-3.3.2/utils/sock/sock.c" (12 Nov 2019, 16001 Bytes) of package /linux/misc/hydra-3.3.2.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "sock.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 3.3.1_vs_3.3.2.

    1 /* -*- Mode: C; c-basic-offset:4 ; indent-tabs-mode:nil ; -*- */
    2 /*
    3  *  (C) 2008 by Argonne National Laboratory.
    4  *      See COPYRIGHT in top-level directory.
    5  */
    6 
    7 #include "hydra.h"
    8 #include "demux.h"
    9 #include "mpl.h"
   10 
   11 struct fwd_hash {
   12     int in;
   13     int out;
   14 
   15     char buf[HYD_TMPBUF_SIZE];
   16     int buf_offset;
   17     int buf_count;
   18 
   19     struct fwd_hash *next;
   20 };
   21 
   22 /* FIXME: redudant code with PMIServGetPort. Can we call PMIServGetPort here? */
   23 HYD_status HYDU_sock_listen(int *listen_fd, char *port_range, uint16_t * port)
   24 {
   25     int ret;
   26     int one = 1;
   27     uint16_t low_port, high_port;
   28     char *port_str;
   29     uint16_t i;
   30     HYD_status status = HYD_SUCCESS;
   31 
   32     HYDU_FUNC_ENTER();
   33 
   34     low_port = 0;
   35     high_port = 0;
   36     if (port_range) {
   37         /* If port range is set, we always pick from there */
   38         *port = 0;
   39 
   40         port_str = strtok(port_range, ":");
   41         if (port_str == NULL)
   42             HYDU_ERR_SETANDJUMP(status, HYD_INTERNAL_ERROR, "error parsing port range\n");
   43         low_port = atoi(port_str);
   44 
   45         port_str = strtok(NULL, ":");
   46         if (port_str == NULL)
   47             HYDU_ERR_SETANDJUMP(status, HYD_INTERNAL_ERROR, "error parsing port range\n");
   48         high_port = atoi(port_str);
   49 
   50         if (high_port < low_port)
   51             HYDU_ERR_SETANDJUMP(status, HYD_INTERNAL_ERROR, "high port < low port\n");
   52     } else {
   53         /* If port range is NULL, if a port is already provided, we
   54          * pick that. Otherwise, we search for an available port. */
   55         low_port = *port;
   56         high_port = *port;
   57     }
   58 
   59   setup_socket:
   60     *listen_fd = MPL_socket();
   61     /* FIXME: duplicate with pm/hydra2/libhydra/sock/hydra_sock.c */
   62     if (*listen_fd < 0)
   63         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "cannot open socket (%s)\n",
   64                             MPL_strerror(errno));
   65 
   66     if (setsockopt(*listen_fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(int)) < 0)
   67         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "cannot set TCP_NODELAY\n");
   68 
   69     /* The sockets standard does not guarantee that a successful
   70      * return here means that this is set. However, REUSEADDR not
   71      * being set is not a fatal error, so we ignore that
   72      * case. However, we do check for error cases, which means that
   73      * something bad has happened. */
   74     if (setsockopt(*listen_fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int)) < 0)
   75         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "cannot set SO_REUSEADDR\n");
   76 
   77     if (low_port == 0) {
   78         ret = MPL_listen_anyport(*listen_fd, port);
   79         if (ret)
   80             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "failed to bind to any port\n");
   81     } else {
   82         ret = MPL_listen_portrange(*listen_fd, port, low_port, high_port);
   83         if (ret == -2) {
   84             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "no port to bind\n");
   85         } else if (ret < 0) {
   86             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "listen error (%s)\n", MPL_strerror(errno));
   87         }
   88         /* We got a port */
   89     }
   90 
   91     /* FIXME: the original code checks EADDRINUSE in both bind and listen. Is there a case EADDRINUSE slip through bind? If there is, we need somehow restore the original code logic.  */
   92 
   93   fn_exit:
   94     HYDU_FUNC_EXIT();
   95     return status;
   96 
   97   fn_fail:
   98     goto fn_exit;
   99 }
  100 
  101 
  102 HYD_status HYDU_sock_connect(const char *host, uint16_t port, int *fd, int retries,
  103                              unsigned long delay)
  104 {
  105     MPL_sockaddr_t addr;
  106     int one = 1, ret, retry_count;
  107     HYD_status status = HYD_SUCCESS;
  108 
  109     HYDU_FUNC_ENTER();
  110 
  111     ret = MPL_get_sockaddr(host, &addr);
  112     if (ret)
  113         HYDU_ERR_SETANDJUMP(status, HYD_INVALID_PARAM, "unable to get host address for %s\n", host);
  114 
  115     /* Create a socket and set the required options */
  116     *fd = MPL_socket();
  117     if (*fd < 0)
  118         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "cannot open socket (%s)\n",
  119                             MPL_strerror(errno));
  120 
  121     /* Not being able to connect is not an error in all cases. So we
  122      * return an error, but only print a warning message. The upper
  123      * layer can decide what to do with the return status. */
  124     retry_count = 0;
  125     do {
  126         ret = MPL_connect(*fd, &addr, port);
  127         if (ret < 0 && (errno == ECONNREFUSED || errno == ETIMEDOUT)) {
  128             /* connection error; increase retry count and delay */
  129             retry_count++;
  130             if (retry_count > retries)
  131                 break;
  132             HYDU_delay(delay);
  133         } else
  134             break;
  135     } while (1);
  136 
  137     if (ret < 0) {
  138         char localhost[MAX_HOSTNAME_LEN] = { 0 };
  139 
  140         if (gethostname(localhost, MAX_HOSTNAME_LEN) < 0)
  141             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "unable to get local hostname\n");
  142 
  143         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR,
  144                             "unable to connect from \"%s\" to \"%s\" (%s)\n",
  145                             localhost, host, MPL_strerror(errno));
  146     }
  147 
  148     /* Disable nagle */
  149     if (setsockopt(*fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(int)) < 0)
  150         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "cannot set TCP_NODELAY\n");
  151 
  152   fn_exit:
  153     HYDU_FUNC_EXIT();
  154     return status;
  155 
  156   fn_fail:
  157     goto fn_exit;
  158 }
  159 
  160 
  161 HYD_status HYDU_sock_accept(int listen_fd, int *fd)
  162 {
  163     int one = 1;
  164     HYD_status status = HYD_SUCCESS;
  165 
  166     HYDU_FUNC_ENTER();
  167 
  168     *fd = accept(listen_fd, 0, 0);
  169     if (*fd < 0)
  170         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "accept error (%s)\n", MPL_strerror(errno));
  171 
  172     /* Disable nagle */
  173     if (setsockopt(*fd, IPPROTO_TCP, TCP_NODELAY, &one, sizeof(int)) < 0)
  174         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "cannot set TCP_NODELAY\n");
  175 
  176   fn_exit:
  177     HYDU_FUNC_EXIT();
  178     return status;
  179 
  180   fn_fail:
  181     goto fn_exit;
  182 }
  183 
  184 HYD_status HYDU_sock_read(int fd, void *buf, int maxlen, int *recvd, int *closed,
  185                           enum HYDU_sock_comm_flag flag)
  186 {
  187     int tmp;
  188     HYD_status status = HYD_SUCCESS;
  189 
  190     HYDU_FUNC_ENTER();
  191 
  192     HYDU_ASSERT(maxlen, status);
  193 
  194     *recvd = 0;
  195     *closed = 0;
  196     while (1) {
  197         do {
  198             tmp = read(fd, (char *) buf + *recvd, maxlen - *recvd);
  199             if (tmp < 0) {
  200                 if (errno == ECONNRESET || fd == STDIN_FILENO) {
  201                     /* If the remote end closed the socket or if we
  202                      * get an EINTR on stdin, set the socket to be
  203                      * closed and jump out */
  204                     *closed = 1;
  205                     status = HYD_SUCCESS;
  206                     goto fn_exit;
  207                 }
  208             }
  209         } while (tmp < 0 && errno == EINTR);
  210 
  211         if (tmp < 0) {
  212             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "read error (%s)\n", MPL_strerror(errno));
  213         } else if (tmp == 0) {
  214             *closed = 1;
  215             goto fn_exit;
  216         } else {
  217             *recvd += tmp;
  218         }
  219 
  220         if (flag == HYDU_SOCK_COMM_NONE || *recvd == maxlen)
  221             break;
  222     }
  223 
  224   fn_exit:
  225     HYDU_FUNC_EXIT();
  226     return status;
  227 
  228   fn_fail:
  229     goto fn_exit;
  230 }
  231 
  232 HYD_status HYDU_sock_write(int fd, const void *buf, int maxlen, int *sent, int *closed,
  233                            enum HYDU_sock_comm_flag flag)
  234 {
  235     int tmp;
  236     HYD_status status = HYD_SUCCESS;
  237 
  238     HYDU_FUNC_ENTER();
  239 
  240     HYDU_ASSERT(maxlen, status);
  241 
  242     *sent = 0;
  243     *closed = 0;
  244     while (1) {
  245         tmp = write(fd, (char *) buf + *sent, maxlen - *sent);
  246         if (tmp <= 0) {
  247             if (errno == EAGAIN) {
  248                 if (flag == HYDU_SOCK_COMM_NONE)
  249                     goto fn_exit;
  250                 else
  251                     continue;
  252             } else if (errno == ECONNRESET) {
  253                 *closed = 1;
  254                 goto fn_exit;
  255             }
  256             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "write error (%s)\n", MPL_strerror(errno));
  257         } else {
  258             *sent += tmp;
  259         }
  260 
  261         if (flag == HYDU_SOCK_COMM_NONE || *sent == maxlen)
  262             break;
  263     }
  264 
  265   fn_exit:
  266     HYDU_FUNC_EXIT();
  267     return status;
  268 
  269   fn_fail:
  270     goto fn_exit;
  271 }
  272 
  273 HYD_status HYDU_sock_set_nonblock(int fd)
  274 {
  275     int flags;
  276     HYD_status status = HYD_SUCCESS;
  277 
  278     HYDU_FUNC_ENTER();
  279 
  280     if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
  281         flags = 0;
  282 #if defined O_NONBLOCK
  283     if (fcntl(fd, F_SETFL, flags | O_NONBLOCK) < 0)
  284         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "fcntl failed on %d\n", fd);
  285 #endif /* O_NONBLOCK */
  286 
  287   fn_exit:
  288     HYDU_FUNC_EXIT();
  289     return status;
  290 
  291   fn_fail:
  292     goto fn_exit;
  293 }
  294 
  295 HYD_status HYDU_sock_set_block(int fd)
  296 {
  297     int flags;
  298     HYD_status status = HYD_SUCCESS;
  299 
  300     HYDU_FUNC_ENTER();
  301 
  302     if ((flags = fcntl(fd, F_GETFL, 0)) == -1)
  303         flags = 0;
  304 #if defined O_NONBLOCK
  305     if (fcntl(fd, F_SETFL, flags & ~O_NONBLOCK) < 0)
  306         HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "fcntl failed on %d\n", fd);
  307 #endif /* O_NONBLOCK */
  308 
  309   fn_exit:
  310     HYDU_FUNC_EXIT();
  311     return status;
  312 
  313   fn_fail:
  314     goto fn_exit;
  315 }
  316 
  317 static HYD_status alloc_fwd_hash(struct fwd_hash **fwd_hash, int in, int out)
  318 {
  319     HYD_status status = HYD_SUCCESS;
  320 
  321     HYDU_FUNC_ENTER();
  322 
  323     HYDU_MALLOC_OR_JUMP(*fwd_hash, struct fwd_hash *, sizeof(struct fwd_hash), status);
  324     (*fwd_hash)->in = in;
  325     (*fwd_hash)->out = out;
  326 
  327     (*fwd_hash)->buf_offset = 0;
  328     (*fwd_hash)->buf_count = 0;
  329 
  330     (*fwd_hash)->next = NULL;
  331 
  332     status = HYDU_sock_set_nonblock(in);
  333     HYDU_ERR_POP(status, "unable to set out-socket to non-blocking\n");
  334 
  335     status = HYDU_sock_set_nonblock(out);
  336     HYDU_ERR_POP(status, "unable to set out-socket to non-blocking\n");
  337 
  338   fn_exit:
  339     HYDU_FUNC_EXIT();
  340     return status;
  341 
  342   fn_fail:
  343     goto fn_exit;
  344 }
  345 
  346 static struct fwd_hash *fwd_hash_list = NULL;
  347 
  348 /* This function does not provide any flow control. We just read from
  349  * the incoming socket as much as we can and push out to the outgoing
  350  * socket as much as we can. This can result in the process calling it
  351  * polling continuously waiting for events, but that's a rare case for
  352  * stdio (which is what this function is meant to provide
  353  * functionality for). */
  354 HYD_status HYDU_sock_forward_stdio(int in, int out, int *closed)
  355 {
  356     struct fwd_hash *fwd_hash, *tmp;
  357     int count;
  358     HYD_status status = HYD_SUCCESS;
  359 
  360     HYDU_FUNC_ENTER();
  361 
  362     /* find the fwd hash */
  363     for (tmp = fwd_hash_list; tmp; tmp = tmp->next)
  364         if (out == tmp->out)
  365             break;
  366 
  367     if (tmp == NULL) {  /* No hash found; create one */
  368         status = alloc_fwd_hash(&fwd_hash, in, out);
  369         HYDU_ERR_POP(status, "unable to allocate forward hash\n");
  370         if (fwd_hash_list == NULL)
  371             fwd_hash_list = fwd_hash;
  372         else {
  373             for (tmp = fwd_hash_list; tmp->next; tmp = tmp->next);
  374             tmp->next = fwd_hash;
  375         }
  376     } else {
  377         fwd_hash = tmp;
  378     }
  379 
  380     *closed = 0;
  381     if (fwd_hash->buf_count == 0) {
  382         /* there is no data in the buffer, read something into it */
  383         status = HYDU_sock_read(in, fwd_hash->buf, HYD_TMPBUF_SIZE, &count, closed,
  384                                 HYDU_SOCK_COMM_NONE);
  385         HYDU_ERR_POP(status, "read error\n");
  386 
  387         if (!*closed) {
  388             fwd_hash->buf_offset = 0;
  389             fwd_hash->buf_count += count;
  390 
  391             /* We should never get a zero count, as the upper-layer
  392              * should have waited for an event from the demux engine
  393              * before calling us. */
  394             HYDU_ASSERT(count, status);
  395         }
  396     }
  397 
  398     if (fwd_hash->buf_count) {
  399         /* there is data in the buffer, send it out first */
  400         status =
  401             HYDU_sock_write(out, fwd_hash->buf + fwd_hash->buf_offset, fwd_hash->buf_count,
  402                             &count, closed, HYDU_SOCK_COMM_MSGWAIT);
  403         HYDU_ERR_POP(status, "write error\n");
  404 
  405         if (!*closed) {
  406             fwd_hash->buf_offset += count;
  407             fwd_hash->buf_count -= count;
  408         }
  409     }
  410 
  411     /* If the incoming socket is closed, make sure we forward out all
  412      * of the buffered data */
  413     while (*closed && fwd_hash->buf_count) {
  414         status =
  415             HYDU_sock_write(out, fwd_hash->buf + fwd_hash->buf_offset, fwd_hash->buf_count,
  416                             &count, closed, HYDU_SOCK_COMM_MSGWAIT);
  417         HYDU_ERR_POP(status, "write error\n");
  418 
  419         if (!*closed) {
  420             fwd_hash->buf_offset += count;
  421             fwd_hash->buf_count -= count;
  422         }
  423     }
  424 
  425   fn_exit:
  426     HYDU_FUNC_EXIT();
  427     return status;
  428 
  429   fn_fail:
  430     goto fn_exit;
  431 }
  432 
  433 void HYDU_sock_finalize(void)
  434 {
  435     struct fwd_hash *tmp, *fwd_hash;
  436 
  437     for (fwd_hash = fwd_hash_list; fwd_hash;) {
  438         tmp = fwd_hash->next;
  439         MPL_free(fwd_hash);
  440         fwd_hash = tmp;
  441     }
  442 }
  443 
  444 HYD_status HYDU_sock_get_iface_ip(char *iface, char **ip)
  445 {
  446     HYD_status status = HYD_SUCCESS;
  447     int ret;
  448 
  449 #if defined(HAVE_GETIFADDRS)
  450     MPL_sockaddr_t addr;
  451     ret = MPL_get_sockaddr_iface(iface, &addr);
  452     if (ret)
  453         HYDU_ERR_SETANDJUMP(status, HYD_INTERNAL_ERROR, "unable to find interface %s\n", iface);
  454 
  455     char buf[MAX_HOSTNAME_LEN];
  456     ret = MPL_sockaddr_to_str(&addr, buf, MAX_HOSTNAME_LEN);
  457     (*ip) = MPL_strdup(buf);
  458 #else
  459     /* For now just disable interface selection when getifaddrs isn't
  460      * available, such as on AIX.  In the future we can implement in MPL
  461      * something along the lines of MPIDI_GetIPInterface from tcp_getip.c in
  462      * nemesis. */
  463     HYDU_ERR_SETANDJUMP(status, HYD_INTERNAL_ERROR,
  464                         "interface selection not supported on this platform\n");
  465 #endif
  466 
  467   fn_exit:
  468     return status;
  469 
  470   fn_fail:
  471     goto fn_exit;
  472 }
  473 
  474 HYD_status
  475 HYDU_sock_create_and_listen_portstr(char *iface, char *hostname, char *port_range,
  476                                     char **port_str,
  477                                     HYD_status(*callback) (int fd, HYD_event_t events,
  478                                                            void *userp), void *userp)
  479 {
  480     int listenfd = -1;
  481     char *sport, *real_port_range, *ip = NULL;
  482     uint16_t port;
  483     HYD_status status = HYD_SUCCESS;
  484 
  485     /* Listen on a port in the port range */
  486     port = 0;
  487     real_port_range = port_range ? MPL_strdup(port_range) : NULL;
  488     status = HYDU_sock_listen(&listenfd, real_port_range, &port);
  489     HYDU_ERR_POP(status, "unable to listen on port\n");
  490 
  491     /* Register the listening socket with the demux engine */
  492     status = HYDT_dmx_register_fd(1, &listenfd, HYD_POLLIN, userp, callback);
  493     HYDU_ERR_POP(status, "unable to register fd\n");
  494 
  495     /* Create a port string for MPI processes to use to connect to */
  496     if (iface) {
  497         status = HYDU_sock_get_iface_ip(iface, &ip);
  498         HYDU_ERR_POP(status, "unable to get network interface IP\n");
  499     } else if (hostname) {
  500         ip = MPL_strdup(hostname);
  501         HYDU_ERR_CHKANDJUMP(status, NULL == ip, HYD_INTERNAL_ERROR, "%s", "");
  502     } else {
  503         char localhost[MAX_HOSTNAME_LEN] = { 0 };
  504 
  505         if (gethostname(localhost, MAX_HOSTNAME_LEN) < 0)
  506             HYDU_ERR_SETANDJUMP(status, HYD_SOCK_ERROR, "unable to get local hostname\n");
  507 
  508         ip = MPL_strdup(localhost);
  509         HYDU_ERR_CHKANDJUMP(status, NULL == ip, HYD_INTERNAL_ERROR, "%s", "");
  510     }
  511 
  512     sport = HYDU_int_to_str(port);
  513     HYDU_ERR_CHKANDJUMP(status, NULL == sport, HYD_INTERNAL_ERROR, "%s", "");
  514     HYDU_MALLOC_OR_JUMP(*port_str, char *, strlen(ip) + 1 + strlen(sport) + 1, status);
  515     MPL_snprintf(*port_str, strlen(ip) + 1 + strlen(sport) + 1, "%s:%s", ip, sport);
  516     MPL_free(sport);
  517 
  518   fn_exit:
  519     if (ip)
  520         MPL_free(ip);
  521     return status;
  522 
  523   fn_fail:
  524     if (-1 != listenfd)
  525         close(listenfd);
  526     goto fn_exit;
  527 }
  528 
  529 HYD_status HYDU_sock_cloexec(int fd)
  530 {
  531     int flags;
  532     HYD_status status = HYD_SUCCESS;
  533 
  534 #if defined HAVE_FCNTL
  535     flags = fcntl(fd, F_GETFD, 0);
  536     if (flags < 0)
  537         HYDU_ERR_POP(status, "unable to get fd flags\n");
  538     flags |= FD_CLOEXEC;
  539     if (fcntl(fd, F_SETFD, flags) < 0)
  540         HYDU_ERR_POP(status, "unable to set fd flags\n");
  541 #endif /* HAVE_FCNTL */
  542 
  543   fn_exit:
  544     return status;
  545 
  546   fn_fail:
  547     goto fn_exit;
  548 }