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