pkg.c revision 244553
1234313Sbapt/*- 2234313Sbapt * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org> 3234313Sbapt * All rights reserved. 4234313Sbapt * 5234313Sbapt * Redistribution and use in source and binary forms, with or without 6234313Sbapt * modification, are permitted provided that the following conditions 7234313Sbapt * are met: 8234313Sbapt * 1. Redistributions of source code must retain the above copyright 9234313Sbapt * notice, this list of conditions and the following disclaimer. 10234313Sbapt * 2. Redistributions in binary form must reproduce the above copyright 11234313Sbapt * notice, this list of conditions and the following disclaimer in the 12234313Sbapt * documentation and/or other materials provided with the distribution. 13234313Sbapt * 14234313Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15234313Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16234313Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17234313Sbapt * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18234313Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19234313Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20234313Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21234313Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22234313Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23234313Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24234313Sbapt * SUCH DAMAGE. 25234313Sbapt */ 26234313Sbapt 27234313Sbapt#include <sys/cdefs.h> 28234313Sbapt__FBSDID("$FreeBSD: head/usr.sbin/pkg/pkg.c 244553 2012-12-21 20:01:13Z matthew $"); 29234313Sbapt 30234313Sbapt#include <sys/param.h> 31234313Sbapt#include <sys/elf_common.h> 32234313Sbapt#include <sys/endian.h> 33234322Sbapt#include <sys/wait.h> 34234313Sbapt 35234313Sbapt#include <archive.h> 36234313Sbapt#include <archive_entry.h> 37234313Sbapt#include <ctype.h> 38234313Sbapt#include <err.h> 39234313Sbapt#include <errno.h> 40234313Sbapt#include <fcntl.h> 41234322Sbapt#include <fetch.h> 42234313Sbapt#include <gelf.h> 43234351Sbapt#include <paths.h> 44234313Sbapt#include <stdlib.h> 45234313Sbapt#include <stdio.h> 46234313Sbapt#include <string.h> 47234313Sbapt#include <time.h> 48234313Sbapt#include <unistd.h> 49234313Sbapt 50234313Sbapt#include "elf_tables.h" 51243883Sbapt#include "dns_utils.h" 52234313Sbapt 53234313Sbapt#define _LOCALBASE "/usr/local" 54243883Sbapt#define _PKGS_URL "http://pkg.FreeBSD.org" 55234313Sbapt 56234313Sbaptstatic const char * 57234322Sbaptelf_corres_to_string(struct _elf_corres *m, int e) 58234313Sbapt{ 59234315Sbapt int i; 60234313Sbapt 61234313Sbapt for (i = 0; m[i].string != NULL; i++) 62234313Sbapt if (m[i].elf_nb == e) 63234313Sbapt return (m[i].string); 64234313Sbapt 65234313Sbapt return ("unknown"); 66234313Sbapt} 67234313Sbapt 68234313Sbaptstatic int 69234313Sbaptpkg_get_myabi(char *dest, size_t sz) 70234313Sbapt{ 71234313Sbapt Elf *elf; 72234313Sbapt Elf_Data *data; 73234313Sbapt Elf_Note note; 74234313Sbapt Elf_Scn *scn; 75234313Sbapt char *src, *osname; 76234313Sbapt const char *abi; 77234351Sbapt GElf_Ehdr elfhdr; 78234351Sbapt GElf_Shdr shdr; 79234313Sbapt int fd, i, ret; 80234313Sbapt uint32_t version; 81234313Sbapt 82234313Sbapt version = 0; 83234351Sbapt ret = -1; 84234313Sbapt scn = NULL; 85234313Sbapt abi = NULL; 86234313Sbapt 87234313Sbapt if (elf_version(EV_CURRENT) == EV_NONE) { 88234322Sbapt warnx("ELF library initialization failed: %s", 89234322Sbapt elf_errmsg(-1)); 90234322Sbapt return (-1); 91234313Sbapt } 92234313Sbapt 93234313Sbapt if ((fd = open("/bin/sh", O_RDONLY)) < 0) { 94234313Sbapt warn("open()"); 95234322Sbapt return (-1); 96234313Sbapt } 97234313Sbapt 98234313Sbapt if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 99234313Sbapt ret = -1; 100234313Sbapt warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 101234313Sbapt goto cleanup; 102234313Sbapt } 103234313Sbapt 104234313Sbapt if (gelf_getehdr(elf, &elfhdr) == NULL) { 105234313Sbapt ret = -1; 106234313Sbapt warn("getehdr() failed: %s.", elf_errmsg(-1)); 107234313Sbapt goto cleanup; 108234313Sbapt } 109234313Sbapt 110234313Sbapt while ((scn = elf_nextscn(elf, scn)) != NULL) { 111234313Sbapt if (gelf_getshdr(scn, &shdr) != &shdr) { 112234313Sbapt ret = -1; 113234313Sbapt warn("getshdr() failed: %s.", elf_errmsg(-1)); 114234313Sbapt goto cleanup; 115234313Sbapt } 116234313Sbapt 117234313Sbapt if (shdr.sh_type == SHT_NOTE) 118234313Sbapt break; 119234313Sbapt } 120234313Sbapt 121234313Sbapt if (scn == NULL) { 122234313Sbapt ret = -1; 123234351Sbapt warn("failed to get the note section"); 124234313Sbapt goto cleanup; 125234313Sbapt } 126234313Sbapt 127234313Sbapt data = elf_getdata(scn, NULL); 128234313Sbapt src = data->d_buf; 129234315Sbapt for (;;) { 130234313Sbapt memcpy(¬e, src, sizeof(Elf_Note)); 131234313Sbapt src += sizeof(Elf_Note); 132234313Sbapt if (note.n_type == NT_VERSION) 133234313Sbapt break; 134234313Sbapt src += note.n_namesz + note.n_descsz; 135234313Sbapt } 136234313Sbapt osname = src; 137234313Sbapt src += note.n_namesz; 138234313Sbapt if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 139234313Sbapt version = be32dec(src); 140234313Sbapt else 141234313Sbapt version = le32dec(src); 142234313Sbapt 143234313Sbapt for (i = 0; osname[i] != '\0'; i++) 144234313Sbapt osname[i] = (char)tolower(osname[i]); 145234313Sbapt 146234313Sbapt snprintf(dest, sz, "%s:%d:%s:%s", 147234322Sbapt osname, version / 100000, 148234322Sbapt elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 149234313Sbapt elf_corres_to_string(wordsize_corres, 150234322Sbapt (int)elfhdr.e_ident[EI_CLASS])); 151234313Sbapt 152234351Sbapt ret = 0; 153234351Sbapt 154234313Sbapt switch (elfhdr.e_machine) { 155234351Sbapt case EM_ARM: 156234351Sbapt snprintf(dest + strlen(dest), sz - strlen(dest), 157234351Sbapt ":%s:%s:%s", elf_corres_to_string(endian_corres, 158234351Sbapt (int)elfhdr.e_ident[EI_DATA]), 159234351Sbapt (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 160234351Sbapt "eabi" : "oabi", 161234351Sbapt (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 162234351Sbapt "softfp" : "vfp"); 163234351Sbapt break; 164234351Sbapt case EM_MIPS: 165234351Sbapt /* 166234351Sbapt * this is taken from binutils sources: 167234351Sbapt * include/elf/mips.h 168234351Sbapt * mapping is figured out from binutils: 169234351Sbapt * gas/config/tc-mips.c 170234351Sbapt */ 171234351Sbapt switch (elfhdr.e_flags & EF_MIPS_ABI) { 172234351Sbapt case E_MIPS_ABI_O32: 173234351Sbapt abi = "o32"; 174234313Sbapt break; 175234351Sbapt case E_MIPS_ABI_N32: 176234351Sbapt abi = "n32"; 177234313Sbapt break; 178234351Sbapt default: 179234351Sbapt if (elfhdr.e_ident[EI_DATA] == 180234351Sbapt ELFCLASS32) 181234351Sbapt abi = "o32"; 182234351Sbapt else if (elfhdr.e_ident[EI_DATA] == 183234351Sbapt ELFCLASS64) 184234351Sbapt abi = "n64"; 185234351Sbapt break; 186234351Sbapt } 187234351Sbapt snprintf(dest + strlen(dest), sz - strlen(dest), 188234351Sbapt ":%s:%s", elf_corres_to_string(endian_corres, 189234351Sbapt (int)elfhdr.e_ident[EI_DATA]), abi); 190234351Sbapt break; 191234313Sbapt } 192234313Sbapt 193234313Sbaptcleanup: 194234313Sbapt if (elf != NULL) 195234313Sbapt elf_end(elf); 196234313Sbapt 197234313Sbapt close(fd); 198234313Sbapt return (ret); 199234313Sbapt} 200234313Sbapt 201234313Sbaptstatic int 202234313Sbaptextract_pkg_static(int fd, char *p, int sz) 203234313Sbapt{ 204234313Sbapt struct archive *a; 205234313Sbapt struct archive_entry *ae; 206234313Sbapt char *end; 207234313Sbapt int ret, r; 208234313Sbapt 209234351Sbapt ret = -1; 210234313Sbapt a = archive_read_new(); 211234351Sbapt if (a == NULL) { 212234351Sbapt warn("archive_read_new"); 213234351Sbapt return (ret); 214234351Sbapt } 215234313Sbapt archive_read_support_compression_all(a); 216234313Sbapt archive_read_support_format_tar(a); 217234313Sbapt 218234351Sbapt if (lseek(fd, 0, 0) == -1) { 219234351Sbapt warn("lseek"); 220234351Sbapt goto cleanup; 221234351Sbapt } 222234313Sbapt 223234313Sbapt if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { 224234351Sbapt warnx("archive_read_open_fd: %s", archive_error_string(a)); 225234313Sbapt goto cleanup; 226234313Sbapt } 227234313Sbapt 228234313Sbapt ae = NULL; 229234313Sbapt while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { 230234313Sbapt end = strrchr(archive_entry_pathname(ae), '/'); 231234313Sbapt if (end == NULL) 232234313Sbapt continue; 233234313Sbapt 234234313Sbapt if (strcmp(end, "/pkg-static") == 0) { 235234313Sbapt r = archive_read_extract(a, ae, 236234322Sbapt ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | 237234322Sbapt ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | 238234322Sbapt ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); 239234351Sbapt strlcpy(p, archive_entry_pathname(ae), sz); 240234313Sbapt break; 241234313Sbapt } 242234313Sbapt } 243234313Sbapt 244234351Sbapt if (r == ARCHIVE_OK) 245234351Sbapt ret = 0; 246234351Sbapt else 247234313Sbapt warnx("fail to extract pkg-static"); 248234313Sbapt 249234313Sbaptcleanup: 250234313Sbapt archive_read_finish(a); 251234322Sbapt return (ret); 252234313Sbapt 253234313Sbapt} 254234313Sbapt 255234313Sbaptstatic int 256234313Sbaptinstall_pkg_static(char *path, char *pkgpath) 257234313Sbapt{ 258234313Sbapt int pstat; 259234313Sbapt pid_t pid; 260234313Sbapt 261234313Sbapt switch ((pid = fork())) { 262234351Sbapt case -1: 263234351Sbapt return (-1); 264234351Sbapt case 0: 265234351Sbapt execl(path, "pkg-static", "add", pkgpath, (char *)NULL); 266234351Sbapt _exit(1); 267234351Sbapt default: 268234351Sbapt break; 269234313Sbapt } 270234313Sbapt 271234351Sbapt while (waitpid(pid, &pstat, 0) == -1) 272234313Sbapt if (errno != EINTR) 273234313Sbapt return (-1); 274234313Sbapt 275234351Sbapt if (WEXITSTATUS(pstat)) 276234351Sbapt return (WEXITSTATUS(pstat)); 277234351Sbapt else if (WIFSIGNALED(pstat)) 278234351Sbapt return (128 & (WTERMSIG(pstat))); 279234351Sbapt return (pstat); 280234313Sbapt} 281234313Sbapt 282234313Sbaptstatic int 283234313Sbaptbootstrap_pkg(void) 284234313Sbapt{ 285243883Sbapt struct url *u; 286234313Sbapt FILE *remote; 287234870Sbapt FILE *config; 288234870Sbapt char *site; 289243883Sbapt struct dns_srvinfo *mirrors, *current; 290243883Sbapt /* To store _https._tcp. + hostname + \0 */ 291243883Sbapt char zone[MAXHOSTNAMELEN + 13]; 292234313Sbapt char url[MAXPATHLEN]; 293234870Sbapt char conf[MAXPATHLEN]; 294234313Sbapt char abi[BUFSIZ]; 295234313Sbapt char tmppkg[MAXPATHLEN]; 296234313Sbapt char buf[10240]; 297234313Sbapt char pkgstatic[MAXPATHLEN]; 298243883Sbapt int fd, retry, ret, max_retry; 299234351Sbapt struct url_stat st; 300234313Sbapt off_t done, r; 301234351Sbapt time_t now; 302234351Sbapt time_t last; 303234313Sbapt 304234313Sbapt done = 0; 305234351Sbapt last = 0; 306243883Sbapt max_retry = 3; 307234351Sbapt ret = -1; 308234313Sbapt remote = NULL; 309234870Sbapt config = NULL; 310243883Sbapt current = mirrors = NULL; 311234313Sbapt 312234351Sbapt printf("Bootstrapping pkg please wait\n"); 313234313Sbapt 314234313Sbapt if (pkg_get_myabi(abi, MAXPATHLEN) != 0) { 315234351Sbapt warnx("failed to determine the system ABI"); 316234322Sbapt return (-1); 317234313Sbapt } 318234313Sbapt 319234351Sbapt if (getenv("PACKAGESITE") != NULL) 320234870Sbapt snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE")); 321234351Sbapt else 322234313Sbapt snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz", 323234313Sbapt getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL, 324234313Sbapt getenv("ABI") ? getenv("ABI") : abi); 325234313Sbapt 326234313Sbapt snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", 327234351Sbapt getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 328234313Sbapt 329234313Sbapt if ((fd = mkstemp(tmppkg)) == -1) { 330234313Sbapt warn("mkstemp()"); 331234322Sbapt return (-1); 332234313Sbapt } 333234313Sbapt 334243883Sbapt retry = max_retry; 335234313Sbapt 336243883Sbapt u = fetchParseURL(url); 337243883Sbapt while (remote == NULL) { 338243883Sbapt if (retry == max_retry) { 339243883Sbapt if (strcmp(u->scheme, "file") != 0) { 340243883Sbapt snprintf(zone, sizeof(zone), 341243883Sbapt "_%s._tcp.%s", u->scheme, u->host); 342243883Sbapt printf("%s\n", zone); 343243883Sbapt mirrors = dns_getsrvinfo(zone); 344243883Sbapt current = mirrors; 345243883Sbapt } 346243883Sbapt } 347243883Sbapt 348243883Sbapt if (mirrors != NULL) 349243883Sbapt strlcpy(u->host, current->host, sizeof(u->host)); 350243883Sbapt 351243883Sbapt remote = fetchXGet(u, &st, ""); 352243883Sbapt if (remote == NULL) { 353243883Sbapt --retry; 354243883Sbapt if (retry <= 0) 355243883Sbapt goto fetchfail; 356243883Sbapt if (mirrors == NULL) { 357243883Sbapt sleep(1); 358243883Sbapt } else { 359243883Sbapt current = current->next; 360243883Sbapt if (current == NULL) 361243883Sbapt current = mirrors; 362243883Sbapt } 363243883Sbapt } 364243883Sbapt } 365243883Sbapt 366234351Sbapt if (remote == NULL) 367234351Sbapt goto fetchfail; 368234351Sbapt 369234313Sbapt while (done < st.size) { 370234313Sbapt if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) 371234313Sbapt break; 372234313Sbapt 373234313Sbapt if (write(fd, buf, r) != r) { 374234313Sbapt warn("write()"); 375234313Sbapt goto cleanup; 376234313Sbapt } 377234313Sbapt 378234313Sbapt done += r; 379234313Sbapt now = time(NULL); 380234351Sbapt if (now > last || done == st.size) 381234313Sbapt last = now; 382234313Sbapt } 383234313Sbapt 384234351Sbapt if (ferror(remote)) 385234351Sbapt goto fetchfail; 386234313Sbapt 387234313Sbapt if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) 388234313Sbapt ret = install_pkg_static(pkgstatic, tmppkg); 389234313Sbapt 390234870Sbapt snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf", 391234870Sbapt getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 392234870Sbapt 393234870Sbapt if (access(conf, R_OK) == -1) { 394234870Sbapt site = strrchr(url, '/'); 395234870Sbapt if (site == NULL) 396234870Sbapt goto cleanup; 397234870Sbapt site[0] = '\0'; 398234870Sbapt site = strrchr(url, '/'); 399234870Sbapt if (site == NULL) 400234870Sbapt goto cleanup; 401234870Sbapt site[0] = '\0'; 402234870Sbapt 403234870Sbapt config = fopen(conf, "w+"); 404234870Sbapt if (config == NULL) 405234870Sbapt goto cleanup; 406235726Sbapt fprintf(config, "packagesite: %s\n", url); 407234870Sbapt fclose(config); 408234870Sbapt } 409234870Sbapt 410234351Sbapt goto cleanup; 411234351Sbapt 412234351Sbaptfetchfail: 413234351Sbapt warnx("Error fetching %s: %s", url, fetchLastErrString); 414234351Sbapt 415234313Sbaptcleanup: 416234870Sbapt if (remote != NULL) 417234870Sbapt fclose(remote); 418234313Sbapt close(fd); 419234313Sbapt unlink(tmppkg); 420234313Sbapt 421234351Sbapt return (ret); 422234313Sbapt} 423234313Sbapt 424238461Skanstatic const char confirmation_message[] = 425238461Skan"The package management tool is not yet installed on your system.\n" 426238461Skan"Do you want to fetch and install it now? [y/N]: "; 427238461Skan 428238461Skanstatic int 429238461Skanpkg_query_yes_no(void) 430238461Skan{ 431238461Skan int ret, c; 432238461Skan 433238461Skan c = getchar(); 434238461Skan 435238461Skan if (c == 'y' || c == 'Y') 436238461Skan ret = 1; 437238461Skan else 438238461Skan ret = 0; 439238461Skan 440238461Skan while (c != '\n' && c != EOF) 441238461Skan c = getchar(); 442238461Skan 443238461Skan return (ret); 444238461Skan} 445238461Skan 446234313Sbaptint 447234322Sbaptmain(__unused int argc, char *argv[]) 448234313Sbapt{ 449234313Sbapt char pkgpath[MAXPATHLEN]; 450234313Sbapt 451234313Sbapt snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", 452234322Sbapt getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 453234313Sbapt 454238461Skan if (access(pkgpath, X_OK) == -1) { 455244553Smatthew /* 456244553Smatthew * To allow 'pkg -n' to be used as a reliable test for whether 457244553Smatthew * a system is configured to use pkg, don't bootstrap pkg 458244553Smatthew * when that argument is given as argv[1]. 459244553Smatthew */ 460244553Smatthew if ( argv[1] != NULL && strcmp(argv[1], "-n") == 0) { 461244553Smatthew printf("%s", "pkg is not installed\n"); 462244553Smatthew exit(EXIT_FAILURE); 463244553Smatthew } 464244553Smatthew 465238461Skan /* 466238461Skan * Do not ask for confirmation if either of stdin or stdout is 467238461Skan * not tty. Check the environment to see if user has answer 468238461Skan * tucked in there already. 469238461Skan */ 470239663Sbapt if (getenv("ASSUME_ALWAYS_YES") == NULL) { 471238461Skan printf("%s", confirmation_message); 472239664Sbapt if (!isatty(fileno(stdin))) 473238461Skan exit(EXIT_FAILURE); 474239664Sbapt 475239664Sbapt if (pkg_query_yes_no() == 0) 476239663Sbapt exit(EXIT_FAILURE); 477238461Skan } 478234351Sbapt if (bootstrap_pkg() != 0) 479234351Sbapt exit(EXIT_FAILURE); 480238461Skan } 481234313Sbapt 482234313Sbapt execv(pkgpath, argv); 483234313Sbapt 484234351Sbapt /* NOT REACHED */ 485234322Sbapt return (EXIT_FAILURE); 486234313Sbapt} 487