pkg.c revision 234351
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 234351 2012-04-16 20:41:25Z bapt $"); 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" 51234313Sbapt 52234313Sbapt#define _LOCALBASE "/usr/local" 53234313Sbapt#define _PKGS_URL "http://pkgbeta.FreeBSD.org" 54234313Sbapt 55234313Sbaptstatic const char * 56234322Sbaptelf_corres_to_string(struct _elf_corres *m, int e) 57234313Sbapt{ 58234315Sbapt int i; 59234313Sbapt 60234313Sbapt for (i = 0; m[i].string != NULL; i++) 61234313Sbapt if (m[i].elf_nb == e) 62234313Sbapt return (m[i].string); 63234313Sbapt 64234313Sbapt return ("unknown"); 65234313Sbapt} 66234313Sbapt 67234313Sbaptstatic int 68234313Sbaptpkg_get_myabi(char *dest, size_t sz) 69234313Sbapt{ 70234313Sbapt Elf *elf; 71234313Sbapt Elf_Data *data; 72234313Sbapt Elf_Note note; 73234313Sbapt Elf_Scn *scn; 74234313Sbapt char *src, *osname; 75234313Sbapt const char *abi; 76234351Sbapt GElf_Ehdr elfhdr; 77234351Sbapt GElf_Shdr shdr; 78234313Sbapt int fd, i, ret; 79234313Sbapt uint32_t version; 80234313Sbapt 81234313Sbapt version = 0; 82234351Sbapt ret = -1; 83234313Sbapt scn = NULL; 84234313Sbapt abi = NULL; 85234313Sbapt 86234313Sbapt if (elf_version(EV_CURRENT) == EV_NONE) { 87234322Sbapt warnx("ELF library initialization failed: %s", 88234322Sbapt elf_errmsg(-1)); 89234322Sbapt return (-1); 90234313Sbapt } 91234313Sbapt 92234313Sbapt if ((fd = open("/bin/sh", O_RDONLY)) < 0) { 93234313Sbapt warn("open()"); 94234322Sbapt return (-1); 95234313Sbapt } 96234313Sbapt 97234313Sbapt if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 98234313Sbapt ret = -1; 99234313Sbapt warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 100234313Sbapt goto cleanup; 101234313Sbapt } 102234313Sbapt 103234313Sbapt if (gelf_getehdr(elf, &elfhdr) == NULL) { 104234313Sbapt ret = -1; 105234313Sbapt warn("getehdr() failed: %s.", elf_errmsg(-1)); 106234313Sbapt goto cleanup; 107234313Sbapt } 108234313Sbapt 109234313Sbapt while ((scn = elf_nextscn(elf, scn)) != NULL) { 110234313Sbapt if (gelf_getshdr(scn, &shdr) != &shdr) { 111234313Sbapt ret = -1; 112234313Sbapt warn("getshdr() failed: %s.", elf_errmsg(-1)); 113234313Sbapt goto cleanup; 114234313Sbapt } 115234313Sbapt 116234313Sbapt if (shdr.sh_type == SHT_NOTE) 117234313Sbapt break; 118234313Sbapt } 119234313Sbapt 120234313Sbapt if (scn == NULL) { 121234313Sbapt ret = -1; 122234351Sbapt warn("failed to get the note section"); 123234313Sbapt goto cleanup; 124234313Sbapt } 125234313Sbapt 126234313Sbapt data = elf_getdata(scn, NULL); 127234313Sbapt src = data->d_buf; 128234315Sbapt for (;;) { 129234313Sbapt memcpy(¬e, src, sizeof(Elf_Note)); 130234313Sbapt src += sizeof(Elf_Note); 131234313Sbapt if (note.n_type == NT_VERSION) 132234313Sbapt break; 133234313Sbapt src += note.n_namesz + note.n_descsz; 134234313Sbapt } 135234313Sbapt osname = src; 136234313Sbapt src += note.n_namesz; 137234313Sbapt if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 138234313Sbapt version = be32dec(src); 139234313Sbapt else 140234313Sbapt version = le32dec(src); 141234313Sbapt 142234313Sbapt for (i = 0; osname[i] != '\0'; i++) 143234313Sbapt osname[i] = (char)tolower(osname[i]); 144234313Sbapt 145234313Sbapt snprintf(dest, sz, "%s:%d:%s:%s", 146234322Sbapt osname, version / 100000, 147234322Sbapt elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 148234313Sbapt elf_corres_to_string(wordsize_corres, 149234322Sbapt (int)elfhdr.e_ident[EI_CLASS])); 150234313Sbapt 151234351Sbapt ret = 0; 152234351Sbapt 153234313Sbapt switch (elfhdr.e_machine) { 154234351Sbapt case EM_ARM: 155234351Sbapt snprintf(dest + strlen(dest), sz - strlen(dest), 156234351Sbapt ":%s:%s:%s", elf_corres_to_string(endian_corres, 157234351Sbapt (int)elfhdr.e_ident[EI_DATA]), 158234351Sbapt (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 159234351Sbapt "eabi" : "oabi", 160234351Sbapt (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 161234351Sbapt "softfp" : "vfp"); 162234351Sbapt break; 163234351Sbapt case EM_MIPS: 164234351Sbapt /* 165234351Sbapt * this is taken from binutils sources: 166234351Sbapt * include/elf/mips.h 167234351Sbapt * mapping is figured out from binutils: 168234351Sbapt * gas/config/tc-mips.c 169234351Sbapt */ 170234351Sbapt switch (elfhdr.e_flags & EF_MIPS_ABI) { 171234351Sbapt case E_MIPS_ABI_O32: 172234351Sbapt abi = "o32"; 173234313Sbapt break; 174234351Sbapt case E_MIPS_ABI_N32: 175234351Sbapt abi = "n32"; 176234313Sbapt break; 177234351Sbapt default: 178234351Sbapt if (elfhdr.e_ident[EI_DATA] == 179234351Sbapt ELFCLASS32) 180234351Sbapt abi = "o32"; 181234351Sbapt else if (elfhdr.e_ident[EI_DATA] == 182234351Sbapt ELFCLASS64) 183234351Sbapt abi = "n64"; 184234351Sbapt break; 185234351Sbapt } 186234351Sbapt snprintf(dest + strlen(dest), sz - strlen(dest), 187234351Sbapt ":%s:%s", elf_corres_to_string(endian_corres, 188234351Sbapt (int)elfhdr.e_ident[EI_DATA]), abi); 189234351Sbapt break; 190234313Sbapt } 191234313Sbapt 192234313Sbaptcleanup: 193234313Sbapt if (elf != NULL) 194234313Sbapt elf_end(elf); 195234313Sbapt 196234313Sbapt close(fd); 197234313Sbapt return (ret); 198234313Sbapt} 199234313Sbapt 200234313Sbaptstatic int 201234313Sbaptextract_pkg_static(int fd, char *p, int sz) 202234313Sbapt{ 203234313Sbapt struct archive *a; 204234313Sbapt struct archive_entry *ae; 205234313Sbapt char *end; 206234313Sbapt int ret, r; 207234313Sbapt 208234351Sbapt ret = -1; 209234313Sbapt a = archive_read_new(); 210234351Sbapt if (a == NULL) { 211234351Sbapt warn("archive_read_new"); 212234351Sbapt return (ret); 213234351Sbapt } 214234313Sbapt archive_read_support_compression_all(a); 215234313Sbapt archive_read_support_format_tar(a); 216234313Sbapt 217234351Sbapt if (lseek(fd, 0, 0) == -1) { 218234351Sbapt warn("lseek"); 219234351Sbapt goto cleanup; 220234351Sbapt } 221234313Sbapt 222234313Sbapt if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { 223234351Sbapt warnx("archive_read_open_fd: %s", archive_error_string(a)); 224234313Sbapt goto cleanup; 225234313Sbapt } 226234313Sbapt 227234313Sbapt ae = NULL; 228234313Sbapt while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { 229234313Sbapt end = strrchr(archive_entry_pathname(ae), '/'); 230234313Sbapt if (end == NULL) 231234313Sbapt continue; 232234313Sbapt 233234313Sbapt if (strcmp(end, "/pkg-static") == 0) { 234234313Sbapt r = archive_read_extract(a, ae, 235234322Sbapt ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | 236234322Sbapt ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | 237234322Sbapt ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); 238234351Sbapt strlcpy(p, archive_entry_pathname(ae), sz); 239234313Sbapt break; 240234313Sbapt } 241234313Sbapt } 242234313Sbapt 243234351Sbapt if (r == ARCHIVE_OK) 244234351Sbapt ret = 0; 245234351Sbapt else 246234313Sbapt warnx("fail to extract pkg-static"); 247234313Sbapt 248234313Sbaptcleanup: 249234313Sbapt archive_read_finish(a); 250234322Sbapt return (ret); 251234313Sbapt 252234313Sbapt} 253234313Sbapt 254234313Sbaptstatic int 255234313Sbaptinstall_pkg_static(char *path, char *pkgpath) 256234313Sbapt{ 257234313Sbapt int pstat; 258234313Sbapt pid_t pid; 259234313Sbapt 260234313Sbapt switch ((pid = fork())) { 261234351Sbapt case -1: 262234351Sbapt return (-1); 263234351Sbapt case 0: 264234351Sbapt execl(path, "pkg-static", "add", pkgpath, (char *)NULL); 265234351Sbapt _exit(1); 266234351Sbapt default: 267234351Sbapt break; 268234313Sbapt } 269234313Sbapt 270234351Sbapt while (waitpid(pid, &pstat, 0) == -1) 271234313Sbapt if (errno != EINTR) 272234313Sbapt return (-1); 273234313Sbapt 274234351Sbapt if (WEXITSTATUS(pstat)) 275234351Sbapt return (WEXITSTATUS(pstat)); 276234351Sbapt else if (WIFSIGNALED(pstat)) 277234351Sbapt return (128 & (WTERMSIG(pstat))); 278234351Sbapt return (pstat); 279234313Sbapt} 280234313Sbapt 281234313Sbaptstatic int 282234313Sbaptbootstrap_pkg(void) 283234313Sbapt{ 284234313Sbapt FILE *remote; 285234313Sbapt char url[MAXPATHLEN]; 286234313Sbapt char abi[BUFSIZ]; 287234313Sbapt char tmppkg[MAXPATHLEN]; 288234313Sbapt char buf[10240]; 289234313Sbapt char pkgstatic[MAXPATHLEN]; 290234313Sbapt int fd, retry, ret; 291234351Sbapt struct url_stat st; 292234313Sbapt off_t done, r; 293234351Sbapt time_t begin_dl; 294234351Sbapt time_t now; 295234351Sbapt time_t last; 296234313Sbapt 297234313Sbapt done = 0; 298234351Sbapt last = 0; 299234351Sbapt ret = -1; 300234313Sbapt remote = NULL; 301234313Sbapt 302234351Sbapt printf("Bootstrapping pkg please wait\n"); 303234313Sbapt 304234313Sbapt if (pkg_get_myabi(abi, MAXPATHLEN) != 0) { 305234351Sbapt warnx("failed to determine the system ABI"); 306234322Sbapt return (-1); 307234313Sbapt } 308234313Sbapt 309234351Sbapt if (getenv("PACKAGESITE") != NULL) 310234351Sbapt snprintf(url, MAXPATHLEN, "%s/pkg.txz", getenv("PACKAGESITE")); 311234351Sbapt else 312234313Sbapt snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz", 313234313Sbapt getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL, 314234313Sbapt getenv("ABI") ? getenv("ABI") : abi); 315234313Sbapt 316234313Sbapt snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", 317234351Sbapt getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 318234313Sbapt 319234313Sbapt if ((fd = mkstemp(tmppkg)) == -1) { 320234313Sbapt warn("mkstemp()"); 321234322Sbapt return (-1); 322234313Sbapt } 323234313Sbapt 324234351Sbapt retry = 3; 325234351Sbapt do { 326234313Sbapt remote = fetchXGetURL(url, &st, ""); 327234351Sbapt if (remote == NULL) 328234313Sbapt sleep(1); 329234351Sbapt } while (remote == NULL && retry-- > 0); 330234313Sbapt 331234351Sbapt if (remote == NULL) 332234351Sbapt goto fetchfail; 333234351Sbapt 334234313Sbapt begin_dl = time(NULL); 335234313Sbapt while (done < st.size) { 336234313Sbapt if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) 337234313Sbapt break; 338234313Sbapt 339234313Sbapt if (write(fd, buf, r) != r) { 340234313Sbapt warn("write()"); 341234313Sbapt goto cleanup; 342234313Sbapt } 343234313Sbapt 344234313Sbapt done += r; 345234313Sbapt now = time(NULL); 346234351Sbapt if (now > last || done == st.size) 347234313Sbapt last = now; 348234313Sbapt } 349234313Sbapt 350234351Sbapt if (ferror(remote)) 351234351Sbapt goto fetchfail; 352234313Sbapt 353234313Sbapt if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) 354234313Sbapt ret = install_pkg_static(pkgstatic, tmppkg); 355234313Sbapt 356234351Sbapt goto cleanup; 357234351Sbapt 358234351Sbaptfetchfail: 359234351Sbapt warnx("Error fetching %s: %s", url, fetchLastErrString); 360234351Sbapt 361234313Sbaptcleanup: 362234313Sbapt close(fd); 363234313Sbapt unlink(tmppkg); 364234313Sbapt 365234351Sbapt return (ret); 366234313Sbapt} 367234313Sbapt 368234313Sbaptint 369234322Sbaptmain(__unused int argc, char *argv[]) 370234313Sbapt{ 371234313Sbapt char pkgpath[MAXPATHLEN]; 372234313Sbapt 373234313Sbapt snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", 374234322Sbapt getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 375234313Sbapt 376234313Sbapt if (access(pkgpath, X_OK) == -1) 377234351Sbapt if (bootstrap_pkg() != 0) 378234351Sbapt exit(EXIT_FAILURE); 379234313Sbapt 380234313Sbapt execv(pkgpath, argv); 381234313Sbapt 382234351Sbapt /* NOT REACHED */ 383234322Sbapt return (EXIT_FAILURE); 384234313Sbapt} 385