]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - usr.sbin/pkg/pkg.c
Add pkg bootstrapping, configuration and public keys. [EN-14:03]
[FreeBSD/releng/9.2.git] / usr.sbin / pkg / pkg.c
1 /*-
2  * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
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 AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29
30 #include <sys/param.h>
31 #include <sys/elf_common.h>
32 #include <sys/endian.h>
33 #include <sys/wait.h>
34
35 #include <archive.h>
36 #include <archive_entry.h>
37 #include <ctype.h>
38 #include <err.h>
39 #include <errno.h>
40 #include <fcntl.h>
41 #include <fetch.h>
42 #include <gelf.h>
43 #include <paths.h>
44 #include <stdlib.h>
45 #include <stdio.h>
46 #include <string.h>
47 #include <time.h>
48 #include <unistd.h>
49
50 #include "elf_tables.h"
51 #include "dns_utils.h"
52
53 #define _LOCALBASE "/usr/local"
54 #define _PKGS_URL "http://pkg.FreeBSD.org"
55
56 static const char *
57 elf_corres_to_string(struct _elf_corres *m, int e)
58 {
59         int i;
60
61         for (i = 0; m[i].string != NULL; i++)
62                 if (m[i].elf_nb == e)
63                         return (m[i].string);
64
65         return ("unknown");
66 }
67
68 static int
69 pkg_get_myabi(char *dest, size_t sz)
70 {
71         Elf *elf;
72         Elf_Data *data;
73         Elf_Note note;
74         Elf_Scn *scn;
75         char *src, *osname;
76         const char *abi;
77         GElf_Ehdr elfhdr;
78         GElf_Shdr shdr;
79         int fd, i, ret;
80         uint32_t version;
81
82         version = 0;
83         ret = -1;
84         scn = NULL;
85         abi = NULL;
86
87         if (elf_version(EV_CURRENT) == EV_NONE) {
88                 warnx("ELF library initialization failed: %s",
89                     elf_errmsg(-1));
90                 return (-1);
91         }
92
93         if ((fd = open("/bin/sh", O_RDONLY)) < 0) {
94                 warn("open()");
95                 return (-1);
96         }
97
98         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
99                 ret = -1;
100                 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
101                 goto cleanup;
102         }
103
104         if (gelf_getehdr(elf, &elfhdr) == NULL) {
105                 ret = -1;
106                 warn("getehdr() failed: %s.", elf_errmsg(-1));
107                 goto cleanup;
108         }
109
110         while ((scn = elf_nextscn(elf, scn)) != NULL) {
111                 if (gelf_getshdr(scn, &shdr) != &shdr) {
112                         ret = -1;
113                         warn("getshdr() failed: %s.", elf_errmsg(-1));
114                         goto cleanup;
115                 }
116
117                 if (shdr.sh_type == SHT_NOTE)
118                         break;
119         }
120
121         if (scn == NULL) {
122                 ret = -1;
123                 warn("failed to get the note section");
124                 goto cleanup;
125         }
126
127         data = elf_getdata(scn, NULL);
128         src = data->d_buf;
129         for (;;) {
130                 memcpy(&note, src, sizeof(Elf_Note));
131                 src += sizeof(Elf_Note);
132                 if (note.n_type == NT_VERSION)
133                         break;
134                 src += note.n_namesz + note.n_descsz;
135         }
136         osname = src;
137         src += note.n_namesz;
138         if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
139                 version = be32dec(src);
140         else
141                 version = le32dec(src);
142
143         for (i = 0; osname[i] != '\0'; i++)
144                 osname[i] = (char)tolower(osname[i]);
145
146         snprintf(dest, sz, "%s:%d:%s:%s",
147             osname, version / 100000,
148             elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
149             elf_corres_to_string(wordsize_corres,
150             (int)elfhdr.e_ident[EI_CLASS]));
151
152         ret = 0;
153
154         switch (elfhdr.e_machine) {
155         case EM_ARM:
156                 snprintf(dest + strlen(dest), sz - strlen(dest),
157                     ":%s:%s:%s", elf_corres_to_string(endian_corres,
158                     (int)elfhdr.e_ident[EI_DATA]),
159                     (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
160                     "eabi" : "oabi",
161                     (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
162                     "softfp" : "vfp");
163                 break;
164         case EM_MIPS:
165                 /*
166                  * this is taken from binutils sources:
167                  * include/elf/mips.h
168                  * mapping is figured out from binutils:
169                  * gas/config/tc-mips.c
170                  */
171                 switch (elfhdr.e_flags & EF_MIPS_ABI) {
172                 case E_MIPS_ABI_O32:
173                         abi = "o32";
174                         break;
175                 case E_MIPS_ABI_N32:
176                         abi = "n32";
177                         break;
178                 default:
179                         if (elfhdr.e_ident[EI_DATA] ==
180                             ELFCLASS32)
181                                 abi = "o32";
182                         else if (elfhdr.e_ident[EI_DATA] ==
183                             ELFCLASS64)
184                                 abi = "n64";
185                         break;
186                 }
187                 snprintf(dest + strlen(dest), sz - strlen(dest),
188                     ":%s:%s", elf_corres_to_string(endian_corres,
189                     (int)elfhdr.e_ident[EI_DATA]), abi);
190                 break;
191         }
192
193 cleanup:
194         if (elf != NULL)
195                 elf_end(elf);
196
197         close(fd);
198         return (ret);
199 }
200
201 static int
202 extract_pkg_static(int fd, char *p, int sz)
203 {
204         struct archive *a;
205         struct archive_entry *ae;
206         char *end;
207         int ret, r;
208
209         ret = -1;
210         a = archive_read_new();
211         if (a == NULL) {
212                 warn("archive_read_new");
213                 return (ret);
214         }
215         archive_read_support_compression_all(a);
216         archive_read_support_format_tar(a);
217
218         if (lseek(fd, 0, 0) == -1) {
219                 warn("lseek");
220                 goto cleanup;
221         }
222
223         if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
224                 warnx("archive_read_open_fd: %s", archive_error_string(a));
225                 goto cleanup;
226         }
227
228         ae = NULL;
229         while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
230                 end = strrchr(archive_entry_pathname(ae), '/');
231                 if (end == NULL)
232                         continue;
233
234                 if (strcmp(end, "/pkg-static") == 0) {
235                         r = archive_read_extract(a, ae,
236                             ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
237                             ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL |
238                             ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR);
239                         strlcpy(p, archive_entry_pathname(ae), sz);
240                         break;
241                 }
242         }
243
244         if (r == ARCHIVE_OK)
245                 ret = 0;
246         else
247                 warnx("fail to extract pkg-static");
248
249 cleanup:
250         archive_read_finish(a);
251         return (ret);
252
253 }
254
255 static int
256 install_pkg_static(char *path, char *pkgpath)
257 {
258         int pstat;
259         pid_t pid;
260
261         switch ((pid = fork())) {
262         case -1:
263                 return (-1);
264         case 0:
265                 execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
266                 _exit(1);
267         default:
268                 break;
269         }
270
271         while (waitpid(pid, &pstat, 0) == -1)
272                 if (errno != EINTR)
273                         return (-1);
274
275         if (WEXITSTATUS(pstat))
276                 return (WEXITSTATUS(pstat));
277         else if (WIFSIGNALED(pstat))
278                 return (128 & (WTERMSIG(pstat)));
279         return (pstat);
280 }
281
282 static int
283 bootstrap_pkg(void)
284 {
285         struct url *u;
286         FILE *remote;
287         struct dns_srvinfo *mirrors, *current;
288         /* To store _https._tcp. + hostname + \0 */
289         char zone[MAXHOSTNAMELEN + 13];
290         char url[MAXPATHLEN];
291         char abi[BUFSIZ];
292         char tmppkg[MAXPATHLEN];
293         char buf[10240];
294         char pkgstatic[MAXPATHLEN];
295         int fd, retry, ret, max_retry;
296         struct url_stat st;
297         off_t done, r;
298         time_t now;
299         time_t last;
300
301         done = 0;
302         last = 0;
303         max_retry = 3;
304         ret = -1;
305         remote = NULL;
306         current = mirrors = NULL;
307
308         printf("Bootstrapping pkg please wait\n");
309
310         if (pkg_get_myabi(abi, MAXPATHLEN) != 0) {
311                 warnx("failed to determine the system ABI");
312                 return (-1);
313         }
314
315         if (getenv("PACKAGESITE") != NULL)
316                 snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE"));
317         else
318                 snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz",
319                     getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL,
320                     getenv("ABI") ? getenv("ABI") : abi);
321
322         snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
323             getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
324
325         if ((fd = mkstemp(tmppkg)) == -1) {
326                 warn("mkstemp()");
327                 return (-1);
328         }
329
330         retry = max_retry;
331
332         u = fetchParseURL(url);
333         while (remote == NULL) {
334                 if (retry == max_retry) {
335                         if (strcmp(u->scheme, "file") != 0) {
336                                 snprintf(zone, sizeof(zone),
337                                     "_%s._tcp.%s", u->scheme, u->host);
338                                 printf("%s\n", zone);
339                                 mirrors = dns_getsrvinfo(zone);
340                                 current = mirrors;
341                         }
342                 }
343
344                 if (mirrors != NULL)
345                         strlcpy(u->host, current->host, sizeof(u->host));
346
347                 remote = fetchXGet(u, &st, "");
348                 if (remote == NULL) {
349                         --retry;
350                         if (retry <= 0)
351                                 goto fetchfail;
352                         if (mirrors == NULL) {
353                                 sleep(1);
354                         } else {
355                                 current = current->next;
356                                 if (current == NULL)
357                                         current = mirrors;
358                         }
359                 }
360         }
361
362         if (remote == NULL)
363                 goto fetchfail;
364
365         while (done < st.size) {
366                 if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
367                         break;
368
369                 if (write(fd, buf, r) != r) {
370                         warn("write()");
371                         goto cleanup;
372                 }
373
374                 done += r;
375                 now = time(NULL);
376                 if (now > last || done == st.size)
377                         last = now;
378         }
379
380         if (ferror(remote))
381                 goto fetchfail;
382
383         if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
384                 ret = install_pkg_static(pkgstatic, tmppkg);
385
386         goto cleanup;
387
388 fetchfail:
389         warnx("Error fetching %s: %s", url, fetchLastErrString);
390
391 cleanup:
392         if (remote != NULL)
393                 fclose(remote);
394         close(fd);
395         unlink(tmppkg);
396
397         return (ret);
398 }
399
400 static const char confirmation_message[] =
401 "The package management tool is not yet installed on your system.\n"
402 "The mechanism for doing this is not secure on FreeBSD 9.2. To securely install\n"
403 "pkg(8), use ports from a portsnap checkout:\n"
404 "  # portsnap fetch extract\n"
405 "  # make -C /usr/ports/ports-mgmt/pkg install clean\n"
406 "Do you still want to fetch and install it now? [y/N]: ";
407
408 static int
409 pkg_query_yes_no(void)
410 {
411         int ret, c;
412
413         c = getchar();
414
415         if (c == 'y' || c == 'Y')
416                 ret = 1;
417         else
418                 ret = 0;
419
420         while (c != '\n' && c != EOF)
421                 c = getchar();
422
423         return (ret);
424 }
425
426 int
427 main(__unused int argc, char *argv[])
428 {
429         char pkgpath[MAXPATHLEN];
430
431         snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
432             getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
433
434         if (access(pkgpath, X_OK) == -1) {
435                 /* 
436                  * To allow 'pkg -N' to be used as a reliable test for whether
437                  * a system is configured to use pkg, don't bootstrap pkg
438                  * when that argument is given as argv[1].
439                  */
440                 if (argv[1] != NULL && strcmp(argv[1], "-N") == 0)
441                         errx(EXIT_FAILURE, "pkg is not installed");
442
443                 /*
444                  * Do not ask for confirmation if either of stdin or stdout is
445                  * not tty. Check the environment to see if user has answer
446                  * tucked in there already.
447                  */
448                 if (getenv("ASSUME_ALWAYS_YES") == NULL) {
449                         printf("%s", confirmation_message);
450                         if (!isatty(fileno(stdin)))
451                                 exit(EXIT_FAILURE);
452
453                         if (pkg_query_yes_no() == 0)
454                                 exit(EXIT_FAILURE);
455                 }
456                 if (bootstrap_pkg() != 0)
457                         exit(EXIT_FAILURE);
458         }
459
460         execv(pkgpath, argv);
461
462         /* NOT REACHED */
463         return (EXIT_FAILURE);
464 }