pkg.c revision 239663
1235537Sgber/*- 2235537Sgber * Copyright (c) 2012 Baptiste Daroussin <bapt@FreeBSD.org> 3235537Sgber * All rights reserved. 4235537Sgber * 5235537Sgber * Redistribution and use in source and binary forms, with or without 6235537Sgber * modification, are permitted provided that the following conditions 7235537Sgber * are met: 8235537Sgber * 1. Redistributions of source code must retain the above copyright 9235537Sgber * notice, this list of conditions and the following disclaimer. 10235537Sgber * 2. Redistributions in binary form must reproduce the above copyright 11235537Sgber * notice, this list of conditions and the following disclaimer in the 12235537Sgber * documentation and/or other materials provided with the distribution. 13235537Sgber * 14235537Sgber * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15235537Sgber * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16235537Sgber * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17235537Sgber * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18235537Sgber * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19235537Sgber * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20235537Sgber * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21235537Sgber * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22235537Sgber * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23235537Sgber * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24235537Sgber * SUCH DAMAGE. 25235537Sgber */ 26235537Sgber 27235537Sgber#include <sys/cdefs.h> 28235537Sgber__FBSDID("$FreeBSD: head/usr.sbin/pkg/pkg.c 239663 2012-08-24 21:08:56Z bapt $"); 29235537Sgber 30235537Sgber#include <sys/param.h> 31235537Sgber#include <sys/elf_common.h> 32235537Sgber#include <sys/endian.h> 33235537Sgber#include <sys/wait.h> 34235537Sgber 35235537Sgber#include <archive.h> 36235537Sgber#include <archive_entry.h> 37235537Sgber#include <ctype.h> 38235537Sgber#include <err.h> 39235537Sgber#include <errno.h> 40235537Sgber#include <fcntl.h> 41235537Sgber#include <fetch.h> 42235537Sgber#include <gelf.h> 43235537Sgber#include <paths.h> 44235537Sgber#include <stdlib.h> 45235537Sgber#include <stdio.h> 46235537Sgber#include <string.h> 47235537Sgber#include <time.h> 48235537Sgber#include <unistd.h> 49235537Sgber 50235537Sgber#include "elf_tables.h" 51235537Sgber 52235537Sgber#define _LOCALBASE "/usr/local" 53235537Sgber#define _PKGS_URL "http://pkgbeta.FreeBSD.org" 54235537Sgber 55235537Sgberstatic const char * 56235537Sgberelf_corres_to_string(struct _elf_corres *m, int e) 57235537Sgber{ 58235537Sgber int i; 59235537Sgber 60235537Sgber for (i = 0; m[i].string != NULL; i++) 61235537Sgber if (m[i].elf_nb == e) 62235537Sgber return (m[i].string); 63235537Sgber 64235537Sgber return ("unknown"); 65235537Sgber} 66235537Sgber 67235537Sgberstatic int 68235537Sgberpkg_get_myabi(char *dest, size_t sz) 69235537Sgber{ 70235537Sgber Elf *elf; 71235537Sgber Elf_Data *data; 72235537Sgber Elf_Note note; 73235537Sgber Elf_Scn *scn; 74235537Sgber char *src, *osname; 75235537Sgber const char *abi; 76235537Sgber GElf_Ehdr elfhdr; 77235537Sgber GElf_Shdr shdr; 78235537Sgber int fd, i, ret; 79235537Sgber uint32_t version; 80235537Sgber 81235537Sgber version = 0; 82235537Sgber ret = -1; 83235537Sgber scn = NULL; 84235537Sgber abi = NULL; 85235537Sgber 86235537Sgber if (elf_version(EV_CURRENT) == EV_NONE) { 87235537Sgber warnx("ELF library initialization failed: %s", 88235537Sgber elf_errmsg(-1)); 89235537Sgber return (-1); 90235537Sgber } 91235537Sgber 92235537Sgber if ((fd = open("/bin/sh", O_RDONLY)) < 0) { 93235537Sgber warn("open()"); 94235537Sgber return (-1); 95235537Sgber } 96235537Sgber 97235537Sgber if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 98235537Sgber ret = -1; 99235537Sgber warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 100235537Sgber goto cleanup; 101235537Sgber } 102235537Sgber 103235537Sgber if (gelf_getehdr(elf, &elfhdr) == NULL) { 104235537Sgber ret = -1; 105235537Sgber warn("getehdr() failed: %s.", elf_errmsg(-1)); 106235537Sgber goto cleanup; 107235537Sgber } 108235537Sgber 109235537Sgber while ((scn = elf_nextscn(elf, scn)) != NULL) { 110235537Sgber if (gelf_getshdr(scn, &shdr) != &shdr) { 111235537Sgber ret = -1; 112235537Sgber warn("getshdr() failed: %s.", elf_errmsg(-1)); 113235537Sgber goto cleanup; 114235537Sgber } 115235537Sgber 116235537Sgber if (shdr.sh_type == SHT_NOTE) 117235537Sgber break; 118235537Sgber } 119235537Sgber 120235537Sgber if (scn == NULL) { 121235537Sgber ret = -1; 122235537Sgber warn("failed to get the note section"); 123235537Sgber goto cleanup; 124235537Sgber } 125235537Sgber 126235537Sgber data = elf_getdata(scn, NULL); 127235537Sgber src = data->d_buf; 128235537Sgber for (;;) { 129235537Sgber memcpy(¬e, src, sizeof(Elf_Note)); 130235537Sgber src += sizeof(Elf_Note); 131235537Sgber if (note.n_type == NT_VERSION) 132235537Sgber break; 133235537Sgber src += note.n_namesz + note.n_descsz; 134235537Sgber } 135235537Sgber osname = src; 136235537Sgber src += note.n_namesz; 137235537Sgber if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 138235537Sgber version = be32dec(src); 139235537Sgber else 140235537Sgber version = le32dec(src); 141235537Sgber 142235537Sgber for (i = 0; osname[i] != '\0'; i++) 143235537Sgber osname[i] = (char)tolower(osname[i]); 144235537Sgber 145235537Sgber snprintf(dest, sz, "%s:%d:%s:%s", 146235537Sgber osname, version / 100000, 147235537Sgber elf_corres_to_string(mach_corres, (int)elfhdr.e_machine), 148235537Sgber elf_corres_to_string(wordsize_corres, 149235537Sgber (int)elfhdr.e_ident[EI_CLASS])); 150235537Sgber 151235537Sgber ret = 0; 152235537Sgber 153235537Sgber switch (elfhdr.e_machine) { 154235537Sgber case EM_ARM: 155235537Sgber snprintf(dest + strlen(dest), sz - strlen(dest), 156235537Sgber ":%s:%s:%s", elf_corres_to_string(endian_corres, 157235537Sgber (int)elfhdr.e_ident[EI_DATA]), 158235537Sgber (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ? 159235537Sgber "eabi" : "oabi", 160235537Sgber (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 161235537Sgber "softfp" : "vfp"); 162235537Sgber break; 163235537Sgber case EM_MIPS: 164235537Sgber /* 165235537Sgber * this is taken from binutils sources: 166235537Sgber * include/elf/mips.h 167235537Sgber * mapping is figured out from binutils: 168235537Sgber * gas/config/tc-mips.c 169235537Sgber */ 170235537Sgber switch (elfhdr.e_flags & EF_MIPS_ABI) { 171235537Sgber case E_MIPS_ABI_O32: 172235537Sgber abi = "o32"; 173235537Sgber break; 174235537Sgber 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 192cleanup: 193 if (elf != NULL) 194 elf_end(elf); 195 196 close(fd); 197 return (ret); 198} 199 200static int 201extract_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 248cleanup: 249 archive_read_finish(a); 250 return (ret); 251 252} 253 254static int 255install_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 281static int 282bootstrap_pkg(void) 283{ 284 FILE *remote; 285 FILE *config; 286 char *site; 287 char url[MAXPATHLEN]; 288 char conf[MAXPATHLEN]; 289 char abi[BUFSIZ]; 290 char tmppkg[MAXPATHLEN]; 291 char buf[10240]; 292 char pkgstatic[MAXPATHLEN]; 293 int fd, retry, ret; 294 struct url_stat st; 295 off_t done, r; 296 time_t now; 297 time_t last; 298 299 done = 0; 300 last = 0; 301 ret = -1; 302 remote = NULL; 303 config = NULL; 304 305 printf("Bootstrapping pkg please wait\n"); 306 307 if (pkg_get_myabi(abi, MAXPATHLEN) != 0) { 308 warnx("failed to determine the system ABI"); 309 return (-1); 310 } 311 312 if (getenv("PACKAGESITE") != NULL) 313 snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE")); 314 else 315 snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz", 316 getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL, 317 getenv("ABI") ? getenv("ABI") : abi); 318 319 snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", 320 getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 321 322 if ((fd = mkstemp(tmppkg)) == -1) { 323 warn("mkstemp()"); 324 return (-1); 325 } 326 327 retry = 3; 328 do { 329 remote = fetchXGetURL(url, &st, ""); 330 if (remote == NULL) 331 sleep(1); 332 } while (remote == NULL && retry-- > 0); 333 334 if (remote == NULL) 335 goto fetchfail; 336 337 while (done < st.size) { 338 if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) 339 break; 340 341 if (write(fd, buf, r) != r) { 342 warn("write()"); 343 goto cleanup; 344 } 345 346 done += r; 347 now = time(NULL); 348 if (now > last || done == st.size) 349 last = now; 350 } 351 352 if (ferror(remote)) 353 goto fetchfail; 354 355 if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) 356 ret = install_pkg_static(pkgstatic, tmppkg); 357 358 snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf", 359 getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 360 361 if (access(conf, R_OK) == -1) { 362 site = strrchr(url, '/'); 363 if (site == NULL) 364 goto cleanup; 365 site[0] = '\0'; 366 site = strrchr(url, '/'); 367 if (site == NULL) 368 goto cleanup; 369 site[0] = '\0'; 370 371 config = fopen(conf, "w+"); 372 if (config == NULL) 373 goto cleanup; 374 fprintf(config, "packagesite: %s\n", url); 375 fclose(config); 376 } 377 378 goto cleanup; 379 380fetchfail: 381 warnx("Error fetching %s: %s", url, fetchLastErrString); 382 383cleanup: 384 if (remote != NULL) 385 fclose(remote); 386 close(fd); 387 unlink(tmppkg); 388 389 return (ret); 390} 391 392static const char confirmation_message[] = 393"The package management tool is not yet installed on your system.\n" 394"Do you want to fetch and install it now? [y/N]: "; 395 396static int 397pkg_query_yes_no(void) 398{ 399 int ret, c; 400 401 c = getchar(); 402 403 if (c == 'y' || c == 'Y') 404 ret = 1; 405 else 406 ret = 0; 407 408 while (c != '\n' && c != EOF) 409 c = getchar(); 410 411 return (ret); 412} 413 414int 415main(__unused int argc, char *argv[]) 416{ 417 char pkgpath[MAXPATHLEN]; 418 419 snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", 420 getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 421 422 if (access(pkgpath, X_OK) == -1) { 423 /* 424 * Do not ask for confirmation if either of stdin or stdout is 425 * not tty. Check the environment to see if user has answer 426 * tucked in there already. 427 */ 428 if (getenv("ASSUME_ALWAYS_YES") == NULL) { 429 printf("%s", confirmation_message); 430 if (isatty(fileno(stdin)) && 431 pkg_query_yes_no() == 0) 432 exit(EXIT_FAILURE); 433 else 434 exit(EXIT_FAILURE); 435 } 436 if (bootstrap_pkg() != 0) 437 exit(EXIT_FAILURE); 438 } 439 440 execv(pkgpath, argv); 441 442 /* NOT REACHED */ 443 return (EXIT_FAILURE); 444} 445