pkg.c revision 234315
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: head/usr.sbin/pkg/pkg.c 234315 2012-04-15 16:00:32Z bapt $"); 29 30#include <sys/types.h> 31#include <sys/wait.h> 32#include <sys/param.h> 33#include <sys/elf_common.h> 34#include <sys/endian.h> 35 36#include <archive.h> 37#include <archive_entry.h> 38#include <ctype.h> 39#include <err.h> 40#include <errno.h> 41#include <fcntl.h> 42#include <gelf.h> 43#include <stdlib.h> 44#include <stdio.h> 45#include <string.h> 46#include <time.h> 47#include <unistd.h> 48#include <fetch.h> 49 50#include "elf_tables.h" 51 52#define _LOCALBASE "/usr/local" 53#define _PKGS_URL "http://pkgbeta.FreeBSD.org" 54#define _DEFAULT_TMP "/tmp" 55 56static const char * 57elf_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 68static int 69pkg_get_myabi(char *dest, size_t sz) 70{ 71 Elf *elf; 72 GElf_Ehdr elfhdr; 73 GElf_Shdr shdr; 74 Elf_Data *data; 75 Elf_Note note; 76 Elf_Scn *scn; 77 char *src, *osname; 78 const char *abi; 79 int fd, i, ret; 80 uint32_t version; 81 82 version = 0; 83 ret = 0; 84 scn = NULL; 85 abi = NULL; 86 87 if (elf_version(EV_CURRENT) == EV_NONE) { 88 warnx("ELF library initialization failed: %s", 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("fail 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(¬e, 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, 147 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 switch (elfhdr.e_machine) { 153 case EM_ARM: 154 snprintf(dest + strlen(dest), sz - strlen(dest), 155 ":%s:%s:%s", 156 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", 188 elf_corres_to_string(endian_corres, 189 (int) elfhdr.e_ident[EI_DATA]), 190 abi); 191 break; 192 } 193 194cleanup: 195 if (elf != NULL) 196 elf_end(elf); 197 198 close(fd); 199 return (ret); 200} 201 202static int 203extract_pkg_static(int fd, char *p, int sz) 204{ 205 struct archive *a; 206 struct archive_entry *ae; 207 char *end; 208 int ret, r; 209 210 ret = 0; 211 a = archive_read_new(); 212 archive_read_support_compression_all(a); 213 archive_read_support_format_tar(a); 214 215 lseek(fd, 0, 0); 216 217 if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { 218 warnx("archive_read_open_fd: %s", 219 archive_error_string(a)); 220 ret = -1; 221 goto cleanup; 222 } 223 224 ae = NULL; 225 while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { 226 end = strrchr(archive_entry_pathname(ae), '/'); 227 if (end == NULL) 228 continue; 229 230 if (strcmp(end, "/pkg-static") == 0) { 231 r = archive_read_extract(a, ae, 232 ARCHIVE_EXTRACT_OWNER |ARCHIVE_EXTRACT_PERM| 233 ARCHIVE_EXTRACT_TIME |ARCHIVE_EXTRACT_ACL | 234 ARCHIVE_EXTRACT_FFLAGS|ARCHIVE_EXTRACT_XATTR); 235 snprintf(p, sz, archive_entry_pathname(ae)); 236 break; 237 } 238 } 239 240 if (r != ARCHIVE_OK) { 241 warnx("fail to extract pkg-static"); 242 ret = -1; 243 } 244 245cleanup: 246 archive_read_finish(a); 247 return ret; 248 249} 250 251static int 252install_pkg_static(char *path, char *pkgpath) 253{ 254 int pstat; 255 pid_t pid; 256 257 switch ((pid = fork())) { 258 case -1: 259 return (-1); 260 case 0: 261 execl(path, "pkg-static", "add", pkgpath, (char *)NULL); 262 _exit(1); /* NOT REACHED */ 263 default: 264 break; 265 } 266 267 while (waitpid(pid, &pstat, 0) == -1) { 268 if (errno != EINTR) 269 return (-1); 270 } 271 272 return (WEXITSTATUS(pstat)); 273} 274 275static int 276bootstrap_pkg(void) 277{ 278 struct url_stat st; 279 FILE *remote; 280 time_t begin_dl; 281 time_t now; 282 time_t last = 0; 283 char url[MAXPATHLEN]; 284 char abi[BUFSIZ]; 285 char tmppkg[MAXPATHLEN]; 286 char buf[10240]; 287 char pkgstatic[MAXPATHLEN]; 288 int fd, retry, ret; 289 off_t done, r; 290 291 done = 0; 292 ret = 0; 293 retry = 3; 294 remote = NULL; 295 296 printf("Bootstraping pkg please wait\n"); 297 298 if (pkg_get_myabi(abi, MAXPATHLEN) != 0) { 299 warnx("fail to determine my abi"); 300 return -1; 301 } 302 303 if (getenv("PACKAGESITE") != NULL) { 304 snprintf(url, MAXPATHLEN, "%s/pkg.txz", 305 getenv("PACKAGESITE")); 306 } else { 307 snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz", 308 getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL, 309 getenv("ABI") ? getenv("ABI") : abi); 310 } 311 312 snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", 313 getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp"); 314 315 if ((fd = mkstemp(tmppkg)) == -1) { 316 warn("mkstemp()"); 317 return -1; 318 } 319 320 while (remote == NULL) { 321 remote = fetchXGetURL(url, &st, ""); 322 if (remote == NULL) { 323 --retry; 324 if (retry == 0) { 325 warnx("Error fetching %s: %s", url, 326 fetchLastErrString); 327 ret = 1; 328 goto cleanup; 329 } 330 sleep(1); 331 } 332 } 333 334 begin_dl = time(NULL); 335 while (done < st.size) { 336 if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) 337 break; 338 339 if (write(fd, buf, r) != r) { 340 warn("write()"); 341 ret = -1; 342 goto cleanup; 343 } 344 345 done += r; 346 now = time(NULL); 347 if (now > last || done == st.size) { 348 last = now; 349 } 350 } 351 352 if (ferror(remote)) { 353 warnx("Error fetching %s: %s", url, 354 fetchLastErrString); 355 ret = 1; 356 goto cleanup; 357 } 358 359 if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) 360 ret = install_pkg_static(pkgstatic, tmppkg); 361 362cleanup: 363 close(fd); 364 unlink(tmppkg); 365 366 return 0; 367} 368 369int 370main(__unused int argc, char * argv[]) 371{ 372 char pkgpath[MAXPATHLEN]; 373 374 snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", 375 getenv("LOCALBASE") ? getenv("LOCALBASE"): _LOCALBASE); 376 377 if (access(pkgpath, X_OK) == -1) 378 bootstrap_pkg(); 379 380 execv(pkgpath, argv); 381 382 return (EXIT_SUCCESS); 383} 384