Line data Source code
1 : /*-
2 : * Copyright (c) 2012-2013 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
5 : * All rights reserved.
6 : *
7 : * Redistribution and use in source and binary forms, with or without
8 : * modification, are permitted provided that the following conditions
9 : * are met:
10 : * 1. Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer
12 : * in this position and unchanged.
13 : * 2. Redistributions in binary form must reproduce the above copyright
14 : * notice, this list of conditions and the following disclaimer in the
15 : * documentation and/or other materials provided with the distribution.
16 : *
17 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
18 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
21 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 : */
28 :
29 : #include <sys/param.h>
30 : #include <sys/wait.h>
31 : #include <sys/socket.h>
32 : #include <sys/time.h>
33 :
34 : #include <ctype.h>
35 : #include <fcntl.h>
36 : #include <errno.h>
37 : #define _WITH_GETLINE
38 : #include <stdio.h>
39 : #include <string.h>
40 : #include <fetch.h>
41 : #include <paths.h>
42 : #include <poll.h>
43 :
44 : #include <bsd_compat.h>
45 :
46 : #include "pkg.h"
47 : #include "private/event.h"
48 : #include "private/pkg.h"
49 : #include "private/utils.h"
50 :
51 : static void
52 0 : gethttpmirrors(struct pkg_repo *repo, const char *url) {
53 : FILE *f;
54 0 : char *line = NULL;
55 0 : size_t linecap = 0;
56 : ssize_t linelen;
57 : struct http_mirror *m;
58 : struct url *u;
59 :
60 0 : if ((f = fetchGetURL(url, "")) == NULL)
61 0 : return;
62 :
63 0 : while ((linelen = getline(&line, &linecap, f)) > 0) {
64 0 : if (strncmp(line, "URL:", 4) == 0) {
65 : /* trim '\n' */
66 0 : if (line[linelen - 1] == '\n')
67 0 : line[linelen - 1 ] = '\0';
68 :
69 0 : line += 4;
70 0 : while (isspace(*line)) {
71 0 : line++;
72 : }
73 0 : if (*line == '\0')
74 0 : continue;
75 :
76 0 : if ((u = fetchParseURL(line)) != NULL) {
77 0 : m = malloc(sizeof(struct http_mirror));
78 0 : m->url = u;
79 0 : LL_APPEND(repo->http, m);
80 : }
81 : }
82 : }
83 0 : fclose(f);
84 0 : return;
85 : }
86 :
87 : int
88 0 : pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
89 : time_t t)
90 : {
91 0 : int fd = -1;
92 0 : int retcode = EPKG_FATAL;
93 :
94 0 : fd = mkstemp(dest);
95 :
96 0 : if (fd == -1) {
97 0 : pkg_emit_errno("mkstemp", dest);
98 0 : return(EPKG_FATAL);
99 : }
100 :
101 0 : retcode = pkg_fetch_file_to_fd(repo, url, fd, &t, 0, -1);
102 :
103 0 : if (t != 0) {
104 0 : struct timeval ftimes[2] = {
105 : {
106 : .tv_sec = t,
107 : .tv_usec = 0
108 : },
109 : {
110 : .tv_sec = t,
111 : .tv_usec = 0
112 : }
113 : };
114 0 : futimes(fd, ftimes);
115 : }
116 :
117 0 : close(fd);
118 :
119 : /* Remove local file if fetch failed */
120 0 : if (retcode != EPKG_OK)
121 0 : unlink(dest);
122 :
123 0 : return (retcode);
124 : }
125 :
126 : int
127 0 : pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
128 : ssize_t offset, int64_t size)
129 : {
130 0 : int fd = -1;
131 0 : int retcode = EPKG_FATAL;
132 :
133 0 : fd = open(dest, O_CREAT|O_APPEND|O_WRONLY, 00644);
134 0 : if (fd == -1) {
135 0 : pkg_emit_errno("open", dest);
136 0 : return(EPKG_FATAL);
137 : }
138 :
139 0 : retcode = pkg_fetch_file_to_fd(repo, url, fd, &t, offset, size);
140 :
141 0 : if (t != 0) {
142 0 : struct timeval ftimes[2] = {
143 : {
144 : .tv_sec = t,
145 : .tv_usec = 0
146 : },
147 : {
148 : .tv_sec = t,
149 : .tv_usec = 0
150 : }
151 : };
152 0 : futimes(fd, ftimes);
153 : }
154 :
155 0 : close(fd);
156 :
157 : /* Remove local file if fetch failed */
158 0 : if (retcode != EPKG_OK)
159 0 : unlink(dest);
160 :
161 0 : return (retcode);
162 : }
163 :
164 : static int
165 0 : ssh_read(void *data, char *buf, int len)
166 : {
167 0 : struct pkg_repo *repo = (struct pkg_repo *) data;
168 : struct timeval now, timeout, delta;
169 : struct pollfd pfd;
170 : ssize_t rlen;
171 : int deltams;
172 :
173 0 : pkg_debug(2, "ssh: start reading %d");
174 :
175 0 : if (fetchTimeout > 0) {
176 0 : gettimeofday(&timeout, NULL);
177 0 : timeout.tv_sec += fetchTimeout;
178 : }
179 :
180 0 : deltams = -1;
181 0 : memset(&pfd, 0, sizeof pfd);
182 0 : pfd.fd = repo->sshio.in;
183 0 : pfd.events = POLLIN | POLLERR;
184 :
185 : for (;;) {
186 0 : rlen = read(pfd.fd, buf, len);
187 0 : pkg_debug(2, "read %d", rlen);
188 0 : if (rlen >= 0) {
189 0 : break;
190 0 : } else if (rlen == -1) {
191 0 : if (errno == EINTR)
192 0 : continue;
193 0 : if (errno != EAGAIN) {
194 0 : pkg_emit_errno("timeout", "ssh");
195 0 : return (-1);
196 : }
197 : }
198 :
199 : /* only EAGAIN should get here */
200 0 : if (fetchTimeout > 0) {
201 0 : gettimeofday(&now, NULL);
202 0 : if (!timercmp(&timeout, &now, >)) {
203 0 : errno = ETIMEDOUT;
204 0 : return (-1);
205 : }
206 0 : timersub(&timeout, &now, &delta);
207 0 : deltams = delta.tv_sec * 1000 +
208 0 : delta.tv_usec / 1000;
209 : }
210 :
211 0 : errno = 0;
212 0 : pfd.revents = 0;
213 0 : pkg_debug(1, "begin poll()");
214 0 : if (poll(&pfd, 1, deltams) < 0) {
215 0 : if (errno == EINTR)
216 0 : continue;
217 0 : return (-1);
218 : }
219 0 : pkg_debug(1, "end poll()");
220 :
221 :
222 0 : }
223 :
224 0 : pkg_debug(2, "ssh: have read %d bytes", rlen);
225 :
226 0 : return (rlen);
227 : }
228 :
229 : static int
230 0 : ssh_writev(int fd, struct iovec *iov, int iovcnt)
231 : {
232 : struct timeval now, timeout, delta;
233 : struct pollfd pfd;
234 : ssize_t wlen, total;
235 : int deltams;
236 : struct msghdr msg;
237 :
238 0 : memset(&pfd, 0, sizeof pfd);
239 :
240 0 : if (fetchTimeout) {
241 0 : pfd.fd = fd;
242 0 : pfd.events = POLLOUT | POLLERR;
243 0 : gettimeofday(&timeout, NULL);
244 0 : timeout.tv_sec += fetchTimeout;
245 : }
246 :
247 0 : total = 0;
248 0 : while (iovcnt > 0) {
249 0 : while (fetchTimeout && pfd.revents == 0) {
250 0 : gettimeofday(&now, NULL);
251 0 : if (!timercmp(&timeout, &now, >)) {
252 0 : errno = ETIMEDOUT;
253 0 : return (-1);
254 : }
255 0 : timersub(&timeout, &now, &delta);
256 0 : deltams = delta.tv_sec * 1000 +
257 0 : delta.tv_usec / 1000;
258 0 : errno = 0;
259 0 : pfd.revents = 0;
260 0 : while (poll(&pfd, 1, deltams) == -1) {
261 0 : if (errno == EINTR)
262 0 : continue;
263 :
264 0 : return (-1);
265 : }
266 : }
267 0 : errno = 0;
268 0 : memset(&msg, 0, sizeof(msg));
269 0 : msg.msg_iov = iov;
270 0 : msg.msg_iovlen = iovcnt;
271 :
272 0 : wlen = sendmsg(fd, &msg, 0);
273 0 : if (wlen == 0) {
274 0 : errno = ECONNRESET;
275 0 : return (-1);
276 : }
277 0 : else if (wlen < 0)
278 0 : return (-1);
279 :
280 0 : total += wlen;
281 :
282 0 : while (iovcnt > 0 && wlen >= (ssize_t)iov->iov_len) {
283 0 : wlen -= iov->iov_len;
284 0 : iov++;
285 0 : iovcnt--;
286 : }
287 :
288 0 : if (iovcnt > 0) {
289 0 : iov->iov_len -= wlen;
290 0 : iov->iov_base = __DECONST(char *, iov->iov_base) + wlen;
291 : }
292 : }
293 0 : return (total);
294 : }
295 :
296 : static int
297 0 : ssh_write(void *data, const char *buf, int l)
298 : {
299 0 : struct pkg_repo *repo = (struct pkg_repo *)data;
300 : struct iovec iov;
301 :
302 0 : iov.iov_base = __DECONST(char *, buf);
303 0 : iov.iov_len = l;
304 :
305 0 : pkg_debug(1, "writing data");
306 :
307 0 : return (ssh_writev(repo->sshio.out, &iov, 1));
308 : }
309 :
310 : static int
311 0 : ssh_close(void *data)
312 : {
313 0 : struct pkg_repo *repo = (struct pkg_repo *)data;
314 : int pstat;
315 :
316 0 : write(repo->sshio.out, "quit\n", 5);
317 :
318 0 : while (waitpid(repo->sshio.pid, &pstat, 0) == -1) {
319 0 : if (errno != EINTR)
320 0 : return (EPKG_FATAL);
321 : }
322 :
323 0 : repo->ssh = NULL;
324 :
325 0 : return (WEXITSTATUS(pstat));
326 : }
327 :
328 : static int
329 0 : start_ssh(struct pkg_repo *repo, struct url *u, off_t *sz)
330 : {
331 0 : char *line = NULL;
332 0 : size_t linecap = 0;
333 : size_t linelen;
334 0 : struct sbuf *cmd = NULL;
335 : const char *errstr;
336 : const char *ssh_args;
337 : int sshin[2];
338 : int sshout[2];
339 : const char *argv[4];
340 :
341 0 : ssh_args = pkg_object_string(pkg_config_get("PKG_SSH_ARGS"));
342 :
343 0 : if (repo->ssh == NULL) {
344 : /* Use socket pair because pipe have blocking issues */
345 0 : if (socketpair(AF_UNIX, SOCK_STREAM, 0, sshin) <0 ||
346 0 : socketpair(AF_UNIX, SOCK_STREAM, 0, sshout) < 0)
347 0 : return(EPKG_FATAL);
348 :
349 0 : repo->sshio.pid = fork();
350 0 : if (repo->sshio.pid == -1) {
351 0 : pkg_emit_errno("Cannot fork", "start_ssh");
352 0 : return (EPKG_FATAL);
353 : }
354 :
355 0 : if (repo->sshio.pid == 0) {
356 0 : if (dup2(sshin[0], STDIN_FILENO) < 0 ||
357 0 : close(sshin[1]) < 0 ||
358 0 : close(sshout[0]) < 0 ||
359 0 : dup2(sshout[1], STDOUT_FILENO) < 0) {
360 0 : pkg_emit_errno("Cannot prepare pipes", "start_ssh");
361 0 : return (EPKG_FATAL);
362 : }
363 :
364 0 : cmd = sbuf_new_auto();
365 0 : sbuf_cat(cmd, "/usr/bin/ssh -e none -T ");
366 0 : if (ssh_args != NULL)
367 0 : sbuf_printf(cmd, "%s ", ssh_args);
368 0 : if ((repo->flags & REPO_FLAGS_USE_IPV4) == REPO_FLAGS_USE_IPV4)
369 0 : sbuf_cat(cmd, "-4 ");
370 0 : else if ((repo->flags & REPO_FLAGS_USE_IPV6) == REPO_FLAGS_USE_IPV6)
371 0 : sbuf_cat(cmd, "-6 ");
372 0 : if (u->port > 0)
373 0 : sbuf_printf(cmd, "-p %d ", u->port);
374 0 : if (u->user[0] != '\0')
375 0 : sbuf_printf(cmd, "%s@", u->user);
376 0 : sbuf_cat(cmd, u->host);
377 0 : sbuf_printf(cmd, " pkg ssh");
378 0 : sbuf_finish(cmd);
379 0 : pkg_debug(1, "Fetch: running '%s'", sbuf_data(cmd));
380 0 : argv[0] = _PATH_BSHELL;
381 0 : argv[1] = "-c";
382 0 : argv[2] = sbuf_data(cmd);
383 0 : argv[3] = NULL;
384 :
385 0 : if (sshin[0] != STDIN_FILENO)
386 0 : close(sshin[0]);
387 0 : if (sshout[1] != STDOUT_FILENO)
388 0 : close(sshout[1]);
389 0 : execvp(argv[0], __DECONST(char **, argv));
390 : /* NOT REACHED */
391 : }
392 :
393 0 : if (close(sshout[1]) < 0 || close(sshin[0]) < 0) {
394 0 : pkg_emit_errno("Failed to close pipes", "start_ssh");
395 0 : return (EPKG_FATAL);
396 : }
397 :
398 0 : pkg_debug(1, "SSH> connected");
399 :
400 0 : repo->sshio.in = sshout[0];
401 0 : repo->sshio.out = sshin[1];
402 0 : set_nonblocking(repo->sshio.in);
403 :
404 0 : repo->ssh = funopen(repo, ssh_read, ssh_write, NULL, ssh_close);
405 0 : if (repo->ssh == NULL) {
406 0 : pkg_emit_errno("Failed to open stream", "start_ssh");
407 0 : return (EPKG_FATAL);
408 : }
409 :
410 0 : if (getline(&line, &linecap, repo->ssh) > 0) {
411 0 : if (strncmp(line, "ok:", 3) != 0) {
412 0 : pkg_debug(1, "SSH> server rejected, got: %s", line);
413 0 : fclose(repo->ssh);
414 0 : free(line);
415 0 : return (EPKG_FATAL);
416 : }
417 0 : pkg_debug(1, "SSH> server is: %s", line +4);
418 : } else {
419 0 : pkg_debug(1, "SSH> nothing to read, got: %s", line);
420 0 : fclose(repo->ssh);
421 0 : return (EPKG_FATAL);
422 : }
423 : }
424 0 : pkg_debug(1, "SSH> get %s %" PRIdMAX "", u->doc, (intmax_t)u->ims_time);
425 0 : fprintf(repo->ssh, "get %s %" PRIdMAX "\n", u->doc, (intmax_t)u->ims_time);
426 0 : if ((linelen = getline(&line, &linecap, repo->ssh)) > 0) {
427 0 : if (line[linelen -1 ] == '\n')
428 0 : line[linelen -1 ] = '\0';
429 :
430 0 : pkg_debug(1, "SSH> recv: %s", line);
431 0 : if (strncmp(line, "ok:", 3) == 0) {
432 0 : *sz = strtonum(line + 4, 0, LONG_MAX, &errstr);
433 0 : if (errstr) {
434 0 : free(line);
435 0 : return (EPKG_FATAL);
436 : }
437 :
438 0 : if (*sz == 0) {
439 0 : free(line);
440 0 : return (EPKG_UPTODATE);
441 : }
442 :
443 0 : free(line);
444 0 : return (EPKG_OK);
445 : }
446 :
447 : }
448 :
449 0 : free(line);
450 0 : return (EPKG_FATAL);
451 : }
452 :
453 : #define URL_SCHEME_PREFIX "pkg+"
454 :
455 : int
456 16 : pkg_fetch_file_to_fd(struct pkg_repo *repo, const char *url, int dest,
457 : time_t *t, ssize_t offset, int64_t size)
458 : {
459 16 : FILE *remote = NULL;
460 16 : struct url *u = NULL;
461 : struct url_stat st;
462 16 : off_t done = 0;
463 : off_t r;
464 : int64_t max_retry, retry;
465 : int64_t fetch_timeout;
466 : char buf[10240];
467 16 : char *doc = NULL;
468 : char docpath[MAXPATHLEN];
469 16 : int retcode = EPKG_OK;
470 : char zone[MAXHOSTNAMELEN + 13];
471 16 : struct dns_srvinfo *srv_current = NULL;
472 16 : struct http_mirror *http_current = NULL;
473 16 : off_t sz = 0;
474 16 : size_t buflen = 0;
475 16 : size_t left = 0;
476 16 : bool pkg_url_scheme = false;
477 16 : struct sbuf *fetchOpts = NULL;
478 :
479 16 : max_retry = pkg_object_int(pkg_config_get("FETCH_RETRY"));
480 16 : fetch_timeout = pkg_object_int(pkg_config_get("FETCH_TIMEOUT"));
481 :
482 16 : fetchTimeout = (int) fetch_timeout;
483 :
484 16 : retry = max_retry;
485 :
486 : /* A URL of the form http://host.example.com/ where
487 : * host.example.com does not resolve as a simple A record is
488 : * not valid according to RFC 2616 Section 3.2.2. Our usage
489 : * with SRV records is incorrect. However it is encoded into
490 : * /usr/sbin/pkg in various releases so we can't just drop it.
491 : *
492 : * Instead, introduce new pkg+http://, pkg+https://,
493 : * pkg+ssh://, pkg+ftp://, pkg+file:// to support the
494 : * SRV-style server discovery, and also to allow eg. Firefox
495 : * to run pkg-related stuff given a pkg+foo:// URL.
496 : *
497 : * Error if using plain http://, https:// etc with SRV
498 : */
499 :
500 32 : if (repo != NULL &&
501 16 : strncmp(URL_SCHEME_PREFIX, url, strlen(URL_SCHEME_PREFIX)) == 0) {
502 0 : if (repo->mirror_type != SRV) {
503 0 : pkg_emit_error("packagesite URL error for %s -- "
504 : URL_SCHEME_PREFIX
505 : ":// implies SRV mirror type", url);
506 :
507 : /* Too early for there to be anything to cleanup */
508 0 : return(EPKG_FATAL);
509 : }
510 :
511 0 : url += strlen(URL_SCHEME_PREFIX);
512 0 : pkg_url_scheme = true;
513 : }
514 :
515 16 : u = fetchParseURL(url);
516 16 : if (t != NULL)
517 16 : u->ims_time = *t;
518 :
519 16 : if (repo != NULL && strcmp(u->scheme, "ssh") == 0) {
520 0 : if ((retcode = start_ssh(repo, u, &sz)) != EPKG_OK)
521 0 : goto cleanup;
522 0 : remote = repo->ssh;
523 : }
524 :
525 16 : doc = u->doc;
526 48 : while (remote == NULL) {
527 16 : if (retry == max_retry) {
528 16 : if (repo != NULL && repo->mirror_type == SRV &&
529 0 : (strncmp(u->scheme, "http", 4) == 0
530 0 : || strcmp(u->scheme, "ftp") == 0)) {
531 :
532 0 : if (!pkg_url_scheme)
533 0 : pkg_emit_notice(
534 : "Warning: use of %s:// URL scheme with SRV records is deprecated: "
535 0 : "switch to pkg+%s://", u->scheme, u->scheme);
536 :
537 0 : snprintf(zone, sizeof(zone),
538 0 : "_%s._tcp.%s", u->scheme, u->host);
539 0 : if (repo->srv == NULL)
540 0 : repo->srv = dns_getsrvinfo(zone);
541 0 : srv_current = repo->srv;
542 16 : } else if (repo != NULL && repo->mirror_type == HTTP &&
543 0 : strncmp(u->scheme, "http", 4) == 0) {
544 0 : snprintf(zone, sizeof(zone),
545 0 : "%s://%s", u->scheme, u->host);
546 0 : if (repo->http == NULL)
547 0 : gethttpmirrors(repo, zone);
548 0 : http_current = repo->http;
549 : }
550 : }
551 :
552 16 : if (repo != NULL && repo->mirror_type == SRV && repo->srv != NULL) {
553 0 : strlcpy(u->host, srv_current->host, sizeof(u->host));
554 0 : u->port = srv_current->port;
555 : }
556 16 : else if (repo != NULL && repo->mirror_type == HTTP && repo->http != NULL) {
557 0 : strlcpy(u->scheme, http_current->url->scheme, sizeof(u->scheme));
558 0 : strlcpy(u->host, http_current->url->host, sizeof(u->host));
559 0 : snprintf(docpath, sizeof(docpath), "%s%s", http_current->url->doc, doc);
560 0 : u->doc = docpath;
561 0 : u->port = http_current->url->port;
562 : }
563 :
564 16 : fetchOpts = sbuf_new_auto();
565 16 : sbuf_cat(fetchOpts, "i");
566 16 : if (repo != NULL) {
567 16 : if ((repo->flags & REPO_FLAGS_USE_IPV4) ==
568 : REPO_FLAGS_USE_IPV4)
569 0 : sbuf_cat(fetchOpts, "4");
570 16 : else if ((repo->flags & REPO_FLAGS_USE_IPV6) ==
571 : REPO_FLAGS_USE_IPV6)
572 0 : sbuf_cat(fetchOpts, "6");
573 : }
574 :
575 48 : pkg_debug(1,"Fetch: fetching from: %s://%s%s%s%s with opts \"%s\"",
576 16 : u->scheme,
577 16 : u->user,
578 16 : u->user[0] != '\0' ? "@" : "",
579 16 : u->host,
580 : u->doc,
581 : sbuf_data(fetchOpts));
582 :
583 16 : sbuf_finish(fetchOpts);
584 :
585 16 : if (offset > 0)
586 0 : u->offset = offset;
587 16 : remote = fetchXGet(u, &st, sbuf_data(fetchOpts));
588 16 : if (remote == NULL) {
589 0 : if (fetchLastErrCode == FETCH_OK) {
590 0 : retcode = EPKG_UPTODATE;
591 0 : goto cleanup;
592 : }
593 0 : --retry;
594 0 : if (retry <= 0 || fetchLastErrCode == FETCH_UNAVAIL) {
595 0 : pkg_emit_error("%s: %s", url,
596 : fetchLastErrString);
597 0 : retcode = EPKG_FATAL;
598 0 : goto cleanup;
599 : }
600 0 : if (repo != NULL && repo->mirror_type == SRV && repo->srv != NULL) {
601 0 : srv_current = srv_current->next;
602 0 : if (srv_current == NULL)
603 0 : srv_current = repo->srv;
604 0 : } else if (repo != NULL && repo->mirror_type == HTTP && repo->http != NULL) {
605 0 : http_current = repo->http->next;
606 0 : if (http_current == NULL)
607 0 : http_current = repo->http;
608 : } else {
609 0 : sleep(1);
610 : }
611 : }
612 : }
613 :
614 16 : if (strcmp(u->scheme, "ssh") != 0) {
615 16 : if (t != NULL && st.mtime != 0) {
616 16 : if (st.mtime <= *t) {
617 2 : retcode = EPKG_UPTODATE;
618 2 : goto cleanup;
619 : } else
620 14 : *t = st.mtime;
621 : }
622 14 : sz = st.size;
623 : }
624 :
625 14 : if (sz <= 0 && size > 0)
626 0 : sz = size;
627 :
628 14 : pkg_emit_fetch_begin(url);
629 14 : pkg_emit_progress_start(NULL);
630 14 : if (offset > 0)
631 0 : done += offset;
632 14 : buflen = sizeof(buf);
633 14 : left = sizeof(buf);
634 14 : if (sz > 0)
635 14 : left = sz - done;
636 42 : while ((r = fread(buf, 1, left < buflen ? left : buflen, remote)) > 0) {
637 14 : if (write(dest, buf, r) != r) {
638 0 : pkg_emit_errno("write", "");
639 0 : retcode = EPKG_FATAL;
640 0 : goto cleanup;
641 : }
642 14 : done += r;
643 14 : if (sz > 0) {
644 14 : left -= r;
645 14 : pkg_debug(1, "Read status: %d over %d", done, sz);
646 : } else
647 0 : pkg_debug(1, "Read status: %d", done);
648 14 : if (sz > 0)
649 14 : pkg_emit_progress_tick(done, sz);
650 : }
651 :
652 14 : if (r != 0) {
653 0 : pkg_emit_error("An error occurred while fetching package");
654 0 : retcode = EPKG_FATAL;
655 0 : goto cleanup;
656 : } else {
657 14 : pkg_emit_progress_tick(done, done);
658 : }
659 14 : pkg_emit_fetch_finished(url);
660 :
661 14 : if (strcmp(u->scheme, "ssh") != 0 && ferror(remote)) {
662 0 : pkg_emit_error("%s: %s", url, fetchLastErrString);
663 0 : retcode = EPKG_FATAL;
664 0 : goto cleanup;
665 : }
666 :
667 : cleanup:
668 16 : if (u != NULL) {
669 16 : if (remote != NULL && repo != NULL && remote != repo->ssh)
670 16 : fclose(remote);
671 : }
672 :
673 : /* restore original doc */
674 16 : u->doc = doc;
675 16 : if (fetchOpts != NULL)
676 16 : sbuf_delete(fetchOpts);
677 :
678 16 : fetchFreeURL(u);
679 :
680 16 : return (retcode);
681 : }
|