Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/pkg
Path: blob/main/libpkg/fetch.c
2645 views
1
/*-
2
* Copyright (c) 2012-2023 Baptiste Daroussin <[email protected]>
3
* Copyright (c) 2011-2012 Julien Laffaye <[email protected]>
4
* Copyright (c) 2014 Vsevolod Stakhov <[email protected]>
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
#include <stdio.h>
38
#include <string.h>
39
#include <paths.h>
40
#include <poll.h>
41
42
#include <bsd_compat.h>
43
44
#include "pkg.h"
45
#include "private/event.h"
46
#include "private/pkg.h"
47
#include "private/utils.h"
48
#include "private/fetch.h"
49
50
static struct fetcher fetchers[] = {
51
{
52
.scheme = "tcp",
53
.open = tcp_open,
54
.close = NULL,
55
.cleanup = fh_close,
56
.fetch = stdio_fetch,
57
},
58
{
59
.scheme = "ssh",
60
.open = ssh_open,
61
.close = NULL,
62
.cleanup = fh_close,
63
.fetch = stdio_fetch,
64
},
65
{
66
.scheme = "pkg+https",
67
.open = curl_open,
68
.close = NULL,
69
.cleanup = curl_cleanup,
70
.fetch = curl_fetch,
71
},
72
{
73
.scheme = "pkg+http",
74
.open = curl_open,
75
.close = NULL,
76
.cleanup = curl_cleanup,
77
.fetch = curl_fetch,
78
},
79
{
80
.scheme = "https",
81
.open = curl_open,
82
.close = NULL,
83
.cleanup = curl_cleanup,
84
.fetch = curl_fetch,
85
},
86
{
87
.scheme = "http",
88
.open = curl_open,
89
.close = NULL,
90
.cleanup = curl_cleanup,
91
.fetch = curl_fetch,
92
},
93
{
94
.scheme = "file",
95
.open = file_open,
96
.close = fh_close,
97
.cleanup = NULL,
98
.fetch = stdio_fetch,
99
},
100
};
101
102
int
103
pkg_fetch_file_tmp(struct pkg_repo *repo, const char *url, char *dest,
104
time_t t)
105
{
106
int fd = -1;
107
int retcode = EPKG_FATAL;
108
struct fetch_item fi;
109
110
memset(&fi, 0, sizeof(struct fetch_item));
111
112
fd = mkstemp(dest);
113
114
if (fd == -1) {
115
pkg_emit_errno("mkstemp", dest);
116
return(EPKG_FATAL);
117
}
118
119
fi.url = url;
120
fi.mtime = t;
121
retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
122
123
if (fi.mtime != 0) {
124
struct timespec ts[2] = {
125
{
126
.tv_sec = fi.mtime,
127
.tv_nsec = 0
128
},
129
{
130
.tv_sec = fi.mtime,
131
.tv_nsec = 0
132
}
133
};
134
futimens(fd, ts);
135
}
136
137
close(fd);
138
139
/* Remove local file if fetch failed */
140
if (retcode != EPKG_OK)
141
unlink(dest);
142
143
return (retcode);
144
}
145
146
int
147
pkg_fetch_file(struct pkg_repo *repo, const char *url, char *dest, time_t t,
148
ssize_t offset, int64_t size)
149
{
150
int fd = -1;
151
int retcode = EPKG_FATAL;
152
struct fetch_item fi;
153
char *url_to_free = NULL;
154
155
fd = open(dest, O_CREAT|O_APPEND|O_WRONLY, 00644);
156
if (fd == -1) {
157
pkg_emit_errno("open", dest);
158
return(EPKG_FATAL);
159
}
160
161
if (repo != NULL) {
162
xasprintf(&url_to_free, "%s/%s", repo->url, url);
163
fi.url = url_to_free;
164
} else {
165
fi.url = url;
166
}
167
168
fi.offset = offset;
169
fi.size = size;
170
fi.mtime = t;
171
172
retcode = pkg_fetch_file_to_fd(repo, fd, &fi, false);
173
free(url_to_free);
174
175
if (t != 0) {
176
struct timespec ts[2] = {
177
{
178
.tv_sec = t,
179
.tv_nsec = 0
180
},
181
{
182
.tv_sec = t,
183
.tv_nsec = 0
184
}
185
};
186
futimens(fd, ts);
187
}
188
close(fd);
189
190
/* Remove local file if fetch failed */
191
if (retcode != EPKG_OK)
192
unlink(dest);
193
194
return (retcode);
195
}
196
197
#define URL_SCHEME_PREFIX "pkg+"
198
199
static const struct fetcher *
200
select_fetcher(const char *url)
201
{
202
struct fetcher *f;
203
size_t nsz;
204
205
for (size_t i = 0; i < NELEM(fetchers); i++) {
206
nsz = strlen(fetchers[i].scheme);
207
208
if ((strncasecmp(url, fetchers[i].scheme, nsz) == 0) &&
209
url[nsz] == ':') {
210
f = &fetchers[i];
211
f->timeout =
212
pkg_object_int(pkg_config_get("FETCH_TIMEOUT"));
213
return (f);
214
}
215
}
216
return (NULL);
217
218
}
219
int
220
pkg_fetch_file_to_fd(struct pkg_repo *repo, int dest, struct fetch_item *fi,
221
bool silent)
222
{
223
struct pkg_kv *kv;
224
kvlist_t envtorestore = vec_init();
225
c_charv_t envtounset = vec_init();
226
char *tmp;
227
int retcode = EPKG_OK;
228
struct pkg_repo *fakerepo = NULL;
229
size_t nsz;
230
231
/* A URL of the form http://host.example.com/ where
232
* host.example.com does not resolve as a simple A record is
233
* not valid according to RFC 2616 Section 3.2.2. Our usage
234
* with SRV records is incorrect. However it is encoded into
235
* /usr/sbin/pkg in various releases so we can't just drop it.
236
*
237
* Instead, introduce new pkg+http://, pkg+https://,
238
* pkg+ssh://, pkg+file:// to support the
239
* SRV-style server discovery, and also to allow eg. Firefox
240
* to run pkg-related stuff given a pkg+foo:// URL.
241
*
242
* Error if using plain http://, https:// etc with SRV
243
*/
244
245
pkg_dbg(PKG_DBG_FETCH, 1, "Request to fetch %s", fi->url);
246
if (repo == NULL) {
247
fakerepo = xcalloc(1, sizeof(struct pkg_repo));
248
fakerepo->url = xstrdup(fi->url);
249
fakerepo->mirror_type = NOMIRROR;
250
repo = fakerepo;
251
}
252
253
if (repo->fetcher == NULL)
254
repo->fetcher = select_fetcher(fi->url);
255
if (repo->fetcher == NULL) {
256
pkg_emit_error("Unknown scheme: %s", fi->url);
257
return (EPKG_FATAL);
258
}
259
260
nsz = strlen(URL_SCHEME_PREFIX);
261
if (strncasecmp(URL_SCHEME_PREFIX, fi->url, nsz) == 0) {
262
if (repo->mirror_type != SRV) {
263
pkg_emit_error("packagesite URL error for %s -- "
264
URL_SCHEME_PREFIX
265
":// implies SRV mirror type", fi->url);
266
267
/* Too early for there to be anything to cleanup */
268
return(EPKG_FATAL);
269
}
270
fi->url += nsz;
271
}
272
273
repo->silent = silent;
274
vec_foreach(repo->env, i) {
275
if ((tmp = getenv(repo->env.d[i]->key)) != NULL) {
276
kv = xcalloc(1, sizeof(*kv));
277
kv->key = xstrdup(repo->env.d[i]->key);
278
kv->value = xstrdup(tmp);
279
vec_push(&envtorestore, kv);
280
} else {
281
vec_push(&envtounset, repo->env.d[i]->key);
282
}
283
setenv(repo->env.d[i]->key, repo->env.d[i]->value, 1);
284
}
285
286
if ((retcode = repo->fetcher->open(repo, fi)) != EPKG_OK)
287
goto cleanup;
288
pkg_dbg(PKG_DBG_FETCH, 1, "Fetch: fetcher used: %s", repo->fetcher->scheme);
289
290
retcode = repo->fetcher->fetch(repo, dest, fi);
291
if (retcode == EPKG_OK)
292
pkg_emit_fetch_finished(fi->url);
293
294
cleanup:
295
vec_foreach(envtorestore, i) {
296
setenv(envtorestore.d[i]->key, envtorestore.d[i]->value, 1);
297
vec_remove_and_free(&envtorestore, i, pkg_kv_free);
298
}
299
vec_free(&envtorestore);
300
while (vec_len(&envtounset) > 0)
301
unsetenv(vec_pop(&envtounset));
302
vec_free(&envtounset);
303
304
if (repo->fetcher != NULL && repo->fetcher->close != NULL)
305
repo->fetcher->close(repo);
306
free(fakerepo);
307
308
if (retcode == EPKG_OK) {
309
struct timespec ts[2] = {
310
{
311
.tv_sec = fi->mtime,
312
.tv_nsec = 0
313
},
314
{
315
.tv_sec = fi->mtime,
316
.tv_nsec = 0
317
}
318
};
319
futimens(dest, ts);
320
}
321
322
return (retcode);
323
}
324
325