]> CyberLeo.Net >> Repos - FreeBSD/releng/9.1.git/blob - usr.sbin/pkg/pkg.c
Add pkg bootstrapping, configuration and public keys. [EN-14:03]
[FreeBSD/releng/9.1.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
52 #define _LOCALBASE "/usr/local"
53 #define _PKGS_URL "http://pkgbeta.FreeBSD.org"
54
55 static const char *
56 elf_corres_to_string(struct _elf_corres *m, int e)
57 {
58         int i;
59
60         for (i = 0; m[i].string != NULL; i++)
61                 if (m[i].elf_nb == e)
62                         return (m[i].string);
63
64         return ("unknown");
65 }
66
67 static int
68 pkg_get_myabi(char *dest, size_t sz)
69 {
70         Elf *elf;
71         Elf_Data *data;
72         Elf_Note note;
73         Elf_Scn *scn;
74         char *src, *osname;
75         const char *abi;
76         GElf_Ehdr elfhdr;
77         GElf_Shdr shdr;
78         int fd, i, ret;
79         uint32_t version;
80
81         version = 0;
82         ret = -1;
83         scn = NULL;
84         abi = NULL;
85
86         if (elf_version(EV_CURRENT) == EV_NONE) {
87                 warnx("ELF library initialization failed: %s",
88                     elf_errmsg(-1));
89                 return (-1);
90         }
91
92         if ((fd = open("/bin/sh", O_RDONLY)) < 0) {
93                 warn("open()");
94                 return (-1);
95         }
96
97         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
98                 ret = -1;
99                 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
100                 goto cleanup;
101         }
102
103         if (gelf_getehdr(elf, &elfhdr) == NULL) {
104                 ret = -1;
105                 warn("getehdr() failed: %s.", elf_errmsg(-1));
106                 goto cleanup;
107         }
108
109         while ((scn = elf_nextscn(elf, scn)) != NULL) {
110                 if (gelf_getshdr(scn, &shdr) != &shdr) {
111                         ret = -1;
112                         warn("getshdr() failed: %s.", elf_errmsg(-1));
113                         goto cleanup;
114                 }
115
116                 if (shdr.sh_type == SHT_NOTE)
117                         break;
118         }
119
120         if (scn == NULL) {
121                 ret = -1;
122                 warn("failed to get the note section");
123                 goto cleanup;
124         }
125
126         data = elf_getdata(scn, NULL);
127         src = data->d_buf;
128         for (;;) {
129                 memcpy(&note, src, sizeof(Elf_Note));
130                 src += sizeof(Elf_Note);
131                 if (note.n_type == NT_VERSION)
132                         break;
133                 src += note.n_namesz + note.n_descsz;
134         }
135         osname = src;
136         src += note.n_namesz;
137         if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
138                 version = be32dec(src);
139         else
140                 version = le32dec(src);
141
142         for (i = 0; osname[i] != '\0'; i++)
143                 osname[i] = (char)tolower(osname[i]);
144
145         snprintf(dest, sz, "%s:%d:%s:%s",
146             osname, version / 100000,
147             elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
148             elf_corres_to_string(wordsize_corres,
149             (int)elfhdr.e_ident[EI_CLASS]));
150
151         ret = 0;
152
153         switch (elfhdr.e_machine) {
154         case EM_ARM:
155                 snprintf(dest + strlen(dest), sz - strlen(dest),
156                     ":%s:%s:%s", elf_corres_to_string(endian_corres,
157                     (int)elfhdr.e_ident[EI_DATA]),
158                     (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
159                     "eabi" : "oabi",
160                     (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
161                     "softfp" : "vfp");
162                 break;
163         case EM_MIPS:
164                 /*
165                  * this is taken from binutils sources:
166                  * include/elf/mips.h
167                  * mapping is figured out from binutils:
168                  * gas/config/tc-mips.c
169                  */
170                 switch (elfhdr.e_flags & EF_MIPS_ABI) {
171                 case E_MIPS_ABI_O32:
172                         abi = "o32";
173                         break;
174                 case E_MIPS_ABI_N32:
175                         abi = "n32";
176                         break;
177                 default:
178                         if (elfhdr.e_ident[EI_DATA] ==
179                             ELFCLASS32)
180                                 abi = "o32";
181                         else if (elfhdr.e_ident[EI_DATA] ==
182                             ELFCLASS64)
183                                 abi = "n64";
184                         break;
185                 }
186                 snprintf(dest + strlen(dest), sz - strlen(dest),
187                     ":%s:%s", elf_corres_to_string(endian_corres,
188                     (int)elfhdr.e_ident[EI_DATA]), abi);
189                 break;
190         }
191
192 cleanup:
193         if (elf != NULL)
194                 elf_end(elf);
195
196         close(fd);
197         return (ret);
198 }
199
200 static int
201 extract_pkg_static(int fd, char *p, int sz)
202 {
203         struct archive *a;
204         struct archive_entry *ae;
205         char *end;
206         int ret, r;
207
208         ret = -1;
209         a = archive_read_new();
210         if (a == NULL) {
211                 warn("archive_read_new");
212                 return (ret);
213         }
214         archive_read_support_compression_all(a);
215         archive_read_support_format_tar(a);
216
217         if (lseek(fd, 0, 0) == -1) {
218                 warn("lseek");
219                 goto cleanup;
220         }
221
222         if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
223                 warnx("archive_read_open_fd: %s", archive_error_string(a));
224                 goto cleanup;
225         }
226
227         ae = NULL;
228         while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
229                 end = strrchr(archive_entry_pathname(ae), '/');
230                 if (end == NULL)
231                         continue;
232
233                 if (strcmp(end, "/pkg-static") == 0) {
234                         r = archive_read_extract(a, ae,
235                             ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM |
236                             ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL |
237                             ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR);
238                         strlcpy(p, archive_entry_pathname(ae), sz);
239                         break;
240                 }
241         }
242
243         if (r == ARCHIVE_OK)
244                 ret = 0;
245         else
246                 warnx("fail to extract pkg-static");
247
248 cleanup:
249         archive_read_finish(a);
250         return (ret);
251
252 }
253
254 static int
255 install_pkg_static(char *path, char *pkgpath)
256 {
257         int pstat;
258         pid_t pid;
259
260         switch ((pid = fork())) {
261         case -1:
262                 return (-1);
263         case 0:
264                 execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
265                 _exit(1);
266         default:
267                 break;
268         }
269
270         while (waitpid(pid, &pstat, 0) == -1)
271                 if (errno != EINTR)
272                         return (-1);
273
274         if (WEXITSTATUS(pstat))
275                 return (WEXITSTATUS(pstat));
276         else if (WIFSIGNALED(pstat))
277                 return (128 & (WTERMSIG(pstat)));
278         return (pstat);
279 }
280
281 static int
282 bootstrap_pkg(void)
283 {
284         FILE *remote;
285         char url[MAXPATHLEN];
286         char abi[BUFSIZ];
287         char tmppkg[MAXPATHLEN];
288         char buf[10240];
289         char pkgstatic[MAXPATHLEN];
290         int fd, retry, ret;
291         struct url_stat st;
292         off_t done, r;
293         time_t now;
294         time_t last;
295
296         done = 0;
297         last = 0;
298         ret = -1;
299         remote = NULL;
300
301         printf("Bootstrapping pkg please wait\n");
302
303         if (pkg_get_myabi(abi, MAXPATHLEN) != 0) {
304                 warnx("failed to determine the system ABI");
305                 return (-1);
306         }
307
308         if (getenv("PACKAGESITE") != NULL)
309                 snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE"));
310         else
311                 snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz",
312                     getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL,
313                     getenv("ABI") ? getenv("ABI") : abi);
314
315         snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
316             getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
317
318         if ((fd = mkstemp(tmppkg)) == -1) {
319                 warn("mkstemp()");
320                 return (-1);
321         }
322
323         retry = 3;
324         do {
325                 remote = fetchXGetURL(url, &st, "");
326                 if (remote == NULL)
327                         sleep(1);
328         } while (remote == NULL && retry-- > 0);
329
330         if (remote == NULL)
331                 goto fetchfail;
332
333         while (done < st.size) {
334                 if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
335                         break;
336
337                 if (write(fd, buf, r) != r) {
338                         warn("write()");
339                         goto cleanup;
340                 }
341
342                 done += r;
343                 now = time(NULL);
344                 if (now > last || done == st.size)
345                         last = now;
346         }
347
348         if (ferror(remote))
349                 goto fetchfail;
350
351         if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
352                 ret = install_pkg_static(pkgstatic, tmppkg);
353
354         goto cleanup;
355
356 fetchfail:
357         warnx("Error fetching %s: %s", url, fetchLastErrString);
358
359 cleanup:
360         if (remote != NULL)
361                 fclose(remote);
362         close(fd);
363         unlink(tmppkg);
364
365         return (ret);
366 }
367
368 static const char confirmation_message[] =
369 "The package management tool is not yet installed on your system.\n"
370 "The mechanism for doing this is not secure on FreeBSD 9.1. To securely install\n"
371 "pkg(8), use ports from a portsnap checkout:\n"
372 "  # portsnap fetch extract\n"
373 "  # make -C /usr/ports/ports-mgmt/pkg install clean\n"
374 "Do you still want to fetch and install it now? [y/N]: ";
375
376 static int
377 pkg_query_yes_no(void)
378 {
379         int ret, c;
380
381         c = getchar();
382
383         if (c == 'y' || c == 'Y')
384                 ret = 1;
385         else
386                 ret = 0;
387
388         while (c != '\n' && c != EOF)
389                 c = getchar();
390
391         return (ret);
392 }
393
394 int
395 main(__unused int argc, char *argv[])
396 {
397         char pkgpath[MAXPATHLEN];
398
399         snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
400             getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
401
402         if (access(pkgpath, X_OK) == -1) {
403                 /*
404                  * Do not ask for confirmation if either of stdin or stdout is
405                  * not tty. Check the environment to see if user has answer
406                  * tucked in there already.
407                  */
408                 if (getenv("ASSUME_ALWAYS_YES") == NULL) {
409                         printf("%s", confirmation_message);
410                         if (!isatty(fileno(stdin)))
411                                 exit(EXIT_FAILURE);
412
413                         if (pkg_query_yes_no() == 0)
414                                 exit(EXIT_FAILURE);
415                 }
416                 if (bootstrap_pkg() != 0)
417                         exit(EXIT_FAILURE);
418         }
419
420         execv(pkgpath, argv);
421
422         /* NOT REACHED */
423         return (EXIT_FAILURE);
424 }