2 * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
30 #include <sys/param.h>
31 #include <sys/elf_common.h>
32 #include <sys/endian.h>
36 #include <archive_entry.h>
50 #include "elf_tables.h"
51 #include "dns_utils.h"
53 #define _LOCALBASE "/usr/local"
54 #define _PKGS_URL "http://pkg.FreeBSD.org"
57 elf_corres_to_string(struct _elf_corres *m, int e)
61 for (i = 0; m[i].string != NULL; i++)
69 pkg_get_myabi(char *dest, size_t sz)
87 if (elf_version(EV_CURRENT) == EV_NONE) {
88 warnx("ELF library initialization failed: %s",
93 if ((fd = open("/bin/sh", O_RDONLY)) < 0) {
98 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
100 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
104 if (gelf_getehdr(elf, &elfhdr) == NULL) {
106 warn("getehdr() failed: %s.", elf_errmsg(-1));
110 while ((scn = elf_nextscn(elf, scn)) != NULL) {
111 if (gelf_getshdr(scn, &shdr) != &shdr) {
113 warn("getshdr() failed: %s.", elf_errmsg(-1));
117 if (shdr.sh_type == SHT_NOTE)
123 warn("failed to get the note section");
127 data = elf_getdata(scn, NULL);
130 memcpy(¬e, src, sizeof(Elf_Note));
131 src += sizeof(Elf_Note);
132 if (note.n_type == NT_VERSION)
134 src += note.n_namesz + note.n_descsz;
137 src += note.n_namesz;
138 if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
139 version = be32dec(src);
141 version = le32dec(src);
143 for (i = 0; osname[i] != '\0'; i++)
144 osname[i] = (char)tolower(osname[i]);
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]));
154 switch (elfhdr.e_machine) {
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 ?
161 (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
166 * this is taken from binutils sources:
168 * mapping is figured out from binutils:
169 * gas/config/tc-mips.c
171 switch (elfhdr.e_flags & EF_MIPS_ABI) {
179 if (elfhdr.e_ident[EI_DATA] ==
182 else if (elfhdr.e_ident[EI_DATA] ==
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);
202 extract_pkg_static(int fd, char *p, int sz)
205 struct archive_entry *ae;
210 a = archive_read_new();
212 warn("archive_read_new");
215 archive_read_support_compression_all(a);
216 archive_read_support_format_tar(a);
218 if (lseek(fd, 0, 0) == -1) {
223 if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) {
224 warnx("archive_read_open_fd: %s", archive_error_string(a));
229 while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) {
230 end = strrchr(archive_entry_pathname(ae), '/');
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);
247 warnx("fail to extract pkg-static");
250 archive_read_finish(a);
256 install_pkg_static(char *path, char *pkgpath)
261 switch ((pid = fork())) {
265 execl(path, "pkg-static", "add", pkgpath, (char *)NULL);
271 while (waitpid(pid, &pstat, 0) == -1)
275 if (WEXITSTATUS(pstat))
276 return (WEXITSTATUS(pstat));
277 else if (WIFSIGNALED(pstat))
278 return (128 & (WTERMSIG(pstat)));
287 struct dns_srvinfo *mirrors, *current;
288 /* To store _https._tcp. + hostname + \0 */
289 char zone[MAXHOSTNAMELEN + 13];
290 char url[MAXPATHLEN];
292 char tmppkg[MAXPATHLEN];
294 char pkgstatic[MAXPATHLEN];
295 int fd, retry, ret, max_retry;
306 current = mirrors = NULL;
308 printf("Bootstrapping pkg please wait\n");
310 if (pkg_get_myabi(abi, MAXPATHLEN) != 0) {
311 warnx("failed to determine the system ABI");
315 if (getenv("PACKAGESITE") != NULL)
316 snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE"));
318 snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz",
319 getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL,
320 getenv("ABI") ? getenv("ABI") : abi);
322 snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX",
323 getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP);
325 if ((fd = mkstemp(tmppkg)) == -1) {
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);
345 strlcpy(u->host, current->host, sizeof(u->host));
347 remote = fetchXGet(u, &st, "");
348 if (remote == NULL) {
352 if (mirrors == NULL) {
355 current = current->next;
365 while (done < st.size) {
366 if ((r = fread(buf, 1, sizeof(buf), remote)) < 1)
369 if (write(fd, buf, r) != r) {
376 if (now > last || done == st.size)
383 if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0)
384 ret = install_pkg_static(pkgstatic, tmppkg);
389 warnx("Error fetching %s: %s", url, fetchLastErrString);
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]: ";
409 pkg_query_yes_no(void)
415 if (c == 'y' || c == 'Y')
420 while (c != '\n' && c != EOF)
427 main(__unused int argc, char *argv[])
429 char pkgpath[MAXPATHLEN];
431 snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg",
432 getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE);
434 if (access(pkgpath, X_OK) == -1) {
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].
440 if (argv[1] != NULL && strcmp(argv[1], "-N") == 0)
441 errx(EXIT_FAILURE, "pkg is not installed");
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.
448 if (getenv("ASSUME_ALWAYS_YES") == NULL) {
449 printf("%s", confirmation_message);
450 if (!isatty(fileno(stdin)))
453 if (pkg_query_yes_no() == 0)
456 if (bootstrap_pkg() != 0)
460 execv(pkgpath, argv);
463 return (EXIT_FAILURE);