LCOV - code coverage report
Current view: top level - libpkg - fetch_ssh.c (source / functions) Hit Total Coverage
Test: rapport Lines: 0 203 0.0 %
Date: 2021-12-10 16:22:55 Functions: 0 6 0.0 %
Branches: 0 52 0.0 %

           Branch data     Line data    Source code
       1                 :            : /*-
       2                 :            :  * Copyright (c) 2020 Baptiste Daroussin <bapt@FreeBSD.org>
       3                 :            :  *
       4                 :            :  * Redistribution and use in source and binary forms, with or without
       5                 :            :  * modification, are permitted provided that the following conditions
       6                 :            :  * are met:
       7                 :            :  * 1. Redistributions of source code must retain the above copyright
       8                 :            :  *    notice, this list of conditions and the following disclaimer
       9                 :            :  *    in this position and unchanged.
      10                 :            :  * 2. Redistributions in binary form must reproduce the above copyright
      11                 :            :  *    notice, this list of conditions and the following disclaimer in the
      12                 :            :  *    documentation and/or other materials provided with the distribution.
      13                 :            :  *
      14                 :            :  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
      15                 :            :  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
      16                 :            :  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
      17                 :            :  * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
      18                 :            :  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
      19                 :            :  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
      20                 :            :  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
      21                 :            :  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
      22                 :            :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
      23                 :            :  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
      24                 :            :  */
      25                 :            : 
      26                 :            : #include <sys/param.h>
      27                 :            : #include <sys/wait.h>
      28                 :            : #include <sys/socket.h>
      29                 :            : #include <sys/time.h>
      30                 :            : 
      31                 :            : #include <ctype.h>
      32                 :            : #include <fcntl.h>
      33                 :            : #include <errno.h>
      34                 :            : #include <stdio.h>
      35                 :            : #include <string.h>
      36                 :            : #include <fetch.h>
      37                 :            : #include <paths.h>
      38                 :            : #include <poll.h>
      39                 :            : 
      40                 :            : #include <bsd_compat.h>
      41                 :            : 
      42                 :            : #include "pkg.h"
      43                 :            : #include "private/event.h"
      44                 :            : #include "private/pkg.h"
      45                 :            : #include "private/fetch.h"
      46                 :            : #include "private/utils.h"
      47                 :            : 
      48                 :            : static int ssh_read(void *data, char *buf, int len);
      49                 :            : static int ssh_write(void *data, const char *buf, int l);
      50                 :            : static int ssh_close(void *data);
      51                 :            : 
      52                 :            : static int
      53                 :          0 : ssh_connect(struct pkg_repo *repo, struct url *u)
      54                 :            : {
      55                 :          0 :         char *line = NULL;
      56                 :          0 :         size_t linecap = 0;
      57                 :            :         int sshin[2];
      58                 :            :         int sshout[2];
      59                 :          0 :         xstring *cmd = NULL;
      60                 :            :         char *cmdline;
      61                 :          0 :         int retcode = EPKG_FATAL;
      62                 :            :         const char *ssh_args;
      63                 :            :         const char *argv[4];
      64                 :            : 
      65                 :            :         /* Use socket pair because pipe have blocking issues */
      66                 :          0 :         if (socketpair(AF_UNIX, SOCK_STREAM, 0, sshin) <0 ||
      67                 :          0 :                         socketpair(AF_UNIX, SOCK_STREAM, 0, sshout) < 0)
      68                 :          0 :                 return(EPKG_FATAL);
      69                 :            : 
      70                 :          0 :         repo->sshio.pid = fork();
      71         [ #  # ]:          0 :         if (repo->sshio.pid == -1) {
      72                 :          0 :                 pkg_emit_errno("Cannot fork", "start_ssh");
      73                 :          0 :                 goto ssh_cleanup;
      74                 :            :         }
      75                 :            : 
      76                 :          0 :         if (repo->sshio.pid == 0) {
      77                 :            : 
      78                 :          0 :                 if (dup2(sshin[0], STDIN_FILENO) < 0 ||
      79                 :          0 :                                 close(sshin[1]) < 0 ||
      80                 :          0 :                                 close(sshout[0]) < 0 ||
      81                 :          0 :                                 dup2(sshout[1], STDOUT_FILENO) < 0) {
      82                 :          0 :                         pkg_emit_errno("Cannot prepare pipes", "start_ssh");
      83                 :          0 :                         goto ssh_cleanup;
      84                 :            :                 }
      85                 :            : 
      86                 :          0 :                 cmd = xstring_new();
      87                 :          0 :                 fputs("/usr/bin/ssh -e none -T ", cmd->fp);
      88                 :            : 
      89                 :          0 :                 ssh_args = pkg_object_string(pkg_config_get("PKG_SSH_ARGS"));
      90                 :          0 :                 if (ssh_args != NULL)
      91                 :          0 :                         fprintf(cmd->fp, "%s ", ssh_args);
      92         [ #  # ]:          0 :                 if ((repo->flags & REPO_FLAGS_USE_IPV4) == REPO_FLAGS_USE_IPV4)
      93                 :          0 :                         fputs("-4 ", cmd->fp);
      94                 :          0 :                 else if ((repo->flags & REPO_FLAGS_USE_IPV6) == REPO_FLAGS_USE_IPV6)
      95                 :          0 :                         fputs("-6 ", cmd->fp);
      96                 :          0 :                 if (u->port > 0)
      97                 :          0 :                         fprintf(cmd->fp, "-p %d ", u->port);
      98                 :          0 :                 if (u->user[0] != '\0')
      99                 :          0 :                         fprintf(cmd->fp, "%s@", u->user);
     100                 :          0 :                 fprintf(cmd->fp, "%s pkg ssh", u->host);
     101                 :          0 :                 cmdline = xstring_get(cmd);
     102                 :          0 :                 pkg_debug(1, "Fetch: running '%s'", cmdline);
     103                 :          0 :                 argv[0] = _PATH_BSHELL;
     104                 :          0 :                 argv[1] = "-c";
     105                 :          0 :                 argv[2] = cmdline;
     106                 :          0 :                 argv[3] = NULL;
     107                 :            : 
     108                 :          0 :                 if (sshin[0] != STDIN_FILENO)
     109                 :          0 :                         close(sshin[0]);
     110                 :          0 :                 if (sshout[1] != STDOUT_FILENO)
     111                 :          0 :                         close(sshout[1]);
     112                 :          0 :                 execvp(argv[0], __DECONST(char **, argv));
     113                 :            :                 /* NOT REACHED */
     114                 :          0 :         }
     115                 :            : 
     116                 :          0 :         if (close(sshout[1]) < 0 || close(sshin[0]) < 0) {
     117                 :          0 :                 pkg_emit_errno("Failed to close pipes", "start_ssh");
     118                 :          0 :                 goto ssh_cleanup;
     119                 :            :         }
     120                 :            : 
     121                 :          0 :         pkg_debug(1, "SSH> connected");
     122                 :            : 
     123                 :          0 :         repo->sshio.in = sshout[0];
     124                 :          0 :         repo->sshio.out = sshin[1];
     125                 :          0 :         set_nonblocking(repo->sshio.in);
     126                 :            : 
     127                 :          0 :         repo->ssh = funopen(repo, ssh_read, ssh_write, NULL, ssh_close);
     128         [ #  # ]:          0 :         if (repo->ssh == NULL) {
     129                 :          0 :                 pkg_emit_errno("Failed to open stream", "start_ssh");
     130                 :          0 :                 goto ssh_cleanup;
     131                 :            :         }
     132                 :            : 
     133         [ #  # ]:          0 :         if (getline(&line, &linecap, repo->ssh) > 0) {
     134         [ #  # ]:          0 :                 if (strncmp(line, "ok:", 3) != 0) {
     135                 :          0 :                         pkg_debug(1, "SSH> server rejected, got: %s", line);
     136                 :          0 :                         goto ssh_cleanup;
     137                 :            :                 }
     138                 :          0 :                 pkg_debug(1, "SSH> server is: %s", line +4);
     139                 :          0 :         } else {
     140                 :          0 :                 pkg_debug(1, "SSH> nothing to read, got: %s", line);
     141                 :          0 :                 goto ssh_cleanup;
     142                 :            :         }
     143                 :          0 :         retcode = EPKG_OK;
     144                 :            : 
     145                 :            : ssh_cleanup:
     146                 :          0 :         if (retcode == EPKG_FATAL && repo->ssh != NULL) {
     147                 :          0 :                 fclose(repo->ssh);
     148                 :          0 :                 repo->ssh = NULL;
     149                 :          0 :         }
     150                 :          0 :         free(line);
     151                 :          0 :         return (retcode);
     152                 :          0 : }
     153                 :            : 
     154                 :            : int
     155                 :          0 : ssh_open(struct pkg_repo *repo, struct url *u, off_t *sz)
     156                 :            : {
     157                 :          0 :         char *line = NULL;
     158                 :          0 :         size_t linecap = 0;
     159                 :            :         size_t linelen;
     160                 :            :         const char *errstr;
     161                 :          0 :         int retcode = EPKG_FATAL;
     162                 :            : 
     163         [ #  # ]:          0 :         if (repo->ssh == NULL)
     164                 :          0 :                 retcode = ssh_connect(repo, u);
     165                 :            :         else
     166                 :          0 :                 retcode = EPKG_OK;
     167                 :            : 
     168         [ #  # ]:          0 :         if (retcode != EPKG_OK)
     169                 :          0 :                 return (retcode);
     170                 :            : 
     171                 :          0 :         pkg_debug(1, "SSH> get %s %" PRIdMAX "", u->doc, (intmax_t)u->ims_time);
     172                 :          0 :         fprintf(repo->ssh, "get %s %" PRIdMAX "\n", u->doc, (intmax_t)u->ims_time);
     173                 :          0 :         if ((linelen = getline(&line, &linecap, repo->ssh)) > 0) {
     174                 :          0 :                 if (line[linelen -1 ] == '\n')
     175                 :          0 :                         line[linelen -1 ] = '\0';
     176                 :            : 
     177                 :          0 :                 pkg_debug(1, "SSH> recv: %s", line);
     178         [ #  # ]:          0 :                 if (strncmp(line, "ok:", 3) == 0) {
     179                 :          0 :                         *sz = strtonum(line + 4, 0, LONG_MAX, &errstr);
     180         [ #  # ]:          0 :                         if (errstr) {
     181                 :          0 :                                 goto out;
     182                 :            :                         }
     183                 :            : 
     184         [ #  # ]:          0 :                         if (*sz == 0) {
     185                 :          0 :                                 retcode = EPKG_UPTODATE;
     186                 :          0 :                                 goto out;
     187                 :            :                         }
     188                 :            : 
     189                 :          0 :                         retcode = EPKG_OK;
     190                 :          0 :                         goto out;
     191                 :            :                 }
     192                 :          0 :         }
     193                 :            : 
     194                 :            : out:
     195                 :          0 :         free(line);
     196                 :          0 :         return (retcode);
     197                 :          0 : }
     198                 :            : 
     199                 :            : static int
     200                 :          0 : ssh_close(void *data)
     201                 :            : {
     202                 :          0 :         struct pkg_repo *repo = (struct pkg_repo *)data;
     203                 :            :         int pstat;
     204                 :            : 
     205                 :          0 :         write(repo->sshio.out, "quit\n", 5);
     206                 :            : 
     207         [ #  # ]:          0 :         while (waitpid(repo->sshio.pid, &pstat, 0) == -1) {
     208         [ #  # ]:          0 :                 if (errno != EINTR)
     209                 :          0 :                         return (EPKG_FATAL);
     210                 :            :         }
     211                 :            : 
     212                 :          0 :         repo->ssh = NULL;
     213                 :            : 
     214                 :          0 :         return (WEXITSTATUS(pstat));
     215                 :          0 : }
     216                 :            : 
     217                 :            : static int
     218                 :          0 : ssh_writev(int fd, struct iovec *iov, int iovcnt)
     219                 :            : {
     220                 :            :         struct timeval now, timeout, delta;
     221                 :            :         struct pollfd pfd;
     222                 :            :         ssize_t wlen, total;
     223                 :            :         int deltams;
     224                 :            :         struct msghdr msg;
     225                 :            : 
     226                 :          0 :         memset(&pfd, 0, sizeof pfd);
     227                 :            : 
     228                 :          0 :         if (fetchTimeout) {
     229                 :          0 :                 pfd.fd = fd;
     230                 :          0 :                 pfd.events = POLLOUT | POLLERR;
     231                 :          0 :                 gettimeofday(&timeout, NULL);
     232                 :          0 :                 timeout.tv_sec += fetchTimeout;
     233                 :          0 :         }
     234                 :            : 
     235                 :          0 :         total = 0;
     236         [ #  # ]:          0 :         while (iovcnt > 0) {
     237         [ #  # ]:          0 :                 while (fetchTimeout && pfd.revents == 0) {
     238                 :          0 :                         gettimeofday(&now, NULL);
     239         [ #  # ]:          0 :                         if (!timercmp(&timeout, &now, >)) {
     240                 :          0 :                                 errno = ETIMEDOUT;
     241                 :          0 :                                 return (-1);
     242                 :            :                         }
     243                 :          0 :                         timersub(&timeout, &now, &delta);
     244                 :          0 :                         deltams = delta.tv_sec * 1000 +
     245                 :          0 :                                 delta.tv_usec / 1000;
     246                 :          0 :                         errno = 0;
     247                 :          0 :                         pfd.revents = 0;
     248         [ #  # ]:          0 :                         while (poll(&pfd, 1, deltams) == -1) {
     249         [ #  # ]:          0 :                                 if (errno == EINTR)
     250                 :          0 :                                         continue;
     251                 :            : 
     252                 :          0 :                                 return (-1);
     253                 :            :                         }
     254                 :            :                 }
     255                 :          0 :                 errno = 0;
     256                 :          0 :                 memset(&msg, 0, sizeof(msg));
     257                 :          0 :                 msg.msg_iov = iov;
     258                 :          0 :                 msg.msg_iovlen = iovcnt;
     259                 :            : 
     260                 :          0 :                 wlen = sendmsg(fd, &msg, 0);
     261         [ #  # ]:          0 :                 if (wlen == 0) {
     262                 :          0 :                         errno = ECONNRESET;
     263                 :          0 :                         return (-1);
     264                 :            :                 }
     265         [ #  # ]:          0 :                 else if (wlen < 0)
     266                 :          0 :                         return (-1);
     267                 :            : 
     268                 :          0 :                 total += wlen;
     269                 :            : 
     270         [ #  # ]:          0 :                 while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
     271                 :          0 :                         wlen -= iov->iov_len;
     272                 :          0 :                         iov++;
     273                 :          0 :                         iovcnt--;
     274                 :            :                 }
     275                 :            : 
     276                 :          0 :                 if (iovcnt > 0) {
     277                 :          0 :                         iov->iov_len -= wlen;
     278                 :          0 :                         iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
     279                 :          0 :                 }
     280                 :            :         }
     281                 :          0 :         return (total);
     282                 :          0 : }
     283                 :            : 
     284                 :            : static int
     285                 :          0 : ssh_write(void *data, const char *buf, int l)
     286                 :            : {
     287                 :          0 :         struct pkg_repo *repo = (struct pkg_repo *)data;
     288                 :            :         struct iovec iov;
     289                 :            : 
     290                 :          0 :         iov.iov_base = __DECONST(char *, buf);
     291                 :          0 :         iov.iov_len = l;
     292                 :            : 
     293                 :          0 :         pkg_debug(1, "writing data");
     294                 :            : 
     295                 :          0 :         return (ssh_writev(repo->sshio.out, &iov, 1));
     296                 :            : }
     297                 :            : 
     298                 :            : static int
     299                 :          0 : ssh_read(void *data, char *buf, int len)
     300                 :            : {
     301                 :          0 :         struct pkg_repo *repo = (struct pkg_repo *) data;
     302                 :            :         struct timeval now, timeout, delta;
     303                 :            :         struct pollfd pfd;
     304                 :            :         ssize_t rlen;
     305                 :            :         int deltams;
     306                 :            : 
     307                 :          0 :         pkg_debug(2, "ssh: start reading");
     308                 :            : 
     309                 :          0 :         if (fetchTimeout > 0) {
     310                 :          0 :                 gettimeofday(&timeout, NULL);
     311                 :          0 :                 timeout.tv_sec += fetchTimeout;
     312                 :          0 :         }
     313                 :            : 
     314                 :          0 :         deltams = -1;
     315                 :          0 :         memset(&pfd, 0, sizeof pfd);
     316                 :          0 :         pfd.fd = repo->sshio.in;
     317                 :          0 :         pfd.events = POLLIN | POLLERR;
     318                 :            : 
     319                 :          0 :         for (;;) {
     320                 :          0 :                 rlen = read(pfd.fd, buf, len);
     321                 :          0 :                 pkg_debug(2, "read %jd", (intmax_t)rlen);
     322         [ #  # ]:          0 :                 if (rlen >= 0) {
     323                 :          0 :                         break;
     324                 :          0 :                 } else if (rlen == -1) {
     325         [ #  # ]:          0 :                         if (errno == EINTR)
     326                 :          0 :                                 continue;
     327         [ #  # ]:          0 :                         if (errno != EAGAIN) {
     328                 :          0 :                                 pkg_emit_errno("timeout", "ssh");
     329                 :          0 :                                 return (-1);
     330                 :            :                         }
     331                 :          0 :                 }
     332                 :            : 
     333                 :            :                 /* only EAGAIN should get here */
     334                 :          0 :                 if (fetchTimeout > 0) {
     335                 :          0 :                         gettimeofday(&now, NULL);
     336         [ #  # ]:          0 :                         if (!timercmp(&timeout, &now, >)) {
     337                 :          0 :                                 errno = ETIMEDOUT;
     338                 :          0 :                                 return (-1);
     339                 :            :                         }
     340                 :          0 :                         timersub(&timeout, &now, &delta);
     341                 :          0 :                         deltams = delta.tv_sec * 1000 +
     342                 :          0 :                             delta.tv_usec / 1000;
     343                 :          0 :                 }
     344                 :            : 
     345                 :          0 :                 errno = 0;
     346                 :          0 :                 pfd.revents = 0;
     347                 :          0 :                 pkg_debug(1, "begin poll()");
     348         [ #  # ]:          0 :                 if (poll(&pfd, 1, deltams) < 0) {
     349         [ #  # ]:          0 :                         if (errno == EINTR)
     350                 :          0 :                                 continue;
     351                 :          0 :                         return (-1);
     352                 :            :                 }
     353                 :          0 :                 pkg_debug(1, "end poll()");
     354                 :            : 
     355                 :            : 
     356                 :            :         }
     357                 :            : 
     358                 :          0 :         pkg_debug(2, "ssh: have read %jd bytes", (intmax_t)rlen);
     359                 :            : 
     360                 :          0 :         return (rlen);
     361                 :          0 : }

Generated by: LCOV version 1.15