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$"); 29 30 #include <sys/param.h> 31 #include <sys/elf_common.h> 32 #include <sys/endian.h> 33 #include <sys/wait.h> 34 35 #include <archive.h> 36 #include <archive_entry.h> 37 #include <ctype.h> 38 #include <err.h> 39 #include <errno.h> 40 #include <fcntl.h> 41 #include <fetch.h> 42 #include <gelf.h> 43 #include <paths.h> 44 #include <stdlib.h> 45 #include <stdio.h> 46 #include <string.h> 47 #include <time.h> 48 #include <unistd.h> 49 50 #include "elf_tables.h" 51 #include "dns_utils.h" 52 53 #define _LOCALBASE "/usr/local" 54 #define _PKGS_URL "http://pkg.FreeBSD.org" 55 56 static const char * 57 elf_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 68 static int 69 pkg_get_myabi(char *dest, size_t sz) 70 { 71 Elf *elf; 72 Elf_Data *data; 73 Elf_Note note; 74 Elf_Scn *scn; 75 char *src, *osname; 76 const char *abi; 77 GElf_Ehdr elfhdr; 78 GElf_Shdr shdr; 79 int fd, i, ret; 80 uint32_t version; 81 82 version = 0; 83 ret = -1; 84 scn = NULL; 85 abi = NULL; 86 87 if (elf_version(EV_CURRENT) == EV_NONE) { 88 warnx("ELF library initialization failed: %s", 89 elf_errmsg(-1)); 90 return (-1); 91 } 92 93 if ((fd = open("/bin/sh", O_RDONLY)) < 0) { 94 warn("open()"); 95 return (-1); 96 } 97 98 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 99 ret = -1; 100 warnx("elf_begin() failed: %s.", elf_errmsg(-1)); 101 goto cleanup; 102 } 103 104 if (gelf_getehdr(elf, &elfhdr) == NULL) { 105 ret = -1; 106 warn("getehdr() failed: %s.", elf_errmsg(-1)); 107 goto cleanup; 108 } 109 110 while ((scn = elf_nextscn(elf, scn)) != NULL) { 111 if (gelf_getshdr(scn, &shdr) != &shdr) { 112 ret = -1; 113 warn("getshdr() failed: %s.", elf_errmsg(-1)); 114 goto cleanup; 115 } 116 117 if (shdr.sh_type == SHT_NOTE) 118 break; 119 } 120 121 if (scn == NULL) { 122 ret = -1; 123 warn("failed to get the note section"); 124 goto cleanup; 125 } 126 127 data = elf_getdata(scn, NULL); 128 src = data->d_buf; 129 for (;;) { 130 memcpy(¬e, src, sizeof(Elf_Note)); 131 src += sizeof(Elf_Note); 132 if (note.n_type == NT_VERSION) 133 break; 134 src += note.n_namesz + note.n_descsz; 135 } 136 osname = src; 137 src += note.n_namesz; 138 if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) 139 version = be32dec(src); 140 else 141 version = le32dec(src); 142 143 for (i = 0; osname[i] != '\0'; i++) 144 osname[i] = (char)tolower(osname[i]); 145 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])); 151 152 ret = 0; 153 154 switch (elfhdr.e_machine) { 155 case EM_ARM: 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 ? 160 "eabi" : "oabi", 161 (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ? 162 "softfp" : "vfp"); 163 break; 164 case EM_MIPS: 165 /* 166 * this is taken from binutils sources: 167 * include/elf/mips.h 168 * mapping is figured out from binutils: 169 * gas/config/tc-mips.c 170 */ 171 switch (elfhdr.e_flags & EF_MIPS_ABI) { 172 case E_MIPS_ABI_O32: 173 abi = "o32"; 174 break; 175 case E_MIPS_ABI_N32: 176 abi = "n32"; 177 break; 178 default: 179 if (elfhdr.e_ident[EI_DATA] == 180 ELFCLASS32) 181 abi = "o32"; 182 else if (elfhdr.e_ident[EI_DATA] == 183 ELFCLASS64) 184 abi = "n64"; 185 break; 186 } 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); 190 break; 191 } 192 193 cleanup: 194 if (elf != NULL) 195 elf_end(elf); 196 197 close(fd); 198 return (ret); 199 } 200 201 static int 202 extract_pkg_static(int fd, char *p, int sz) 203 { 204 struct archive *a; 205 struct archive_entry *ae; 206 char *end; 207 int ret, r; 208 209 ret = -1; 210 a = archive_read_new(); 211 if (a == NULL) { 212 warn("archive_read_new"); 213 return (ret); 214 } 215 archive_read_support_compression_all(a); 216 archive_read_support_format_tar(a); 217 218 if (lseek(fd, 0, 0) == -1) { 219 warn("lseek"); 220 goto cleanup; 221 } 222 223 if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { 224 warnx("archive_read_open_fd: %s", archive_error_string(a)); 225 goto cleanup; 226 } 227 228 ae = NULL; 229 while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { 230 end = strrchr(archive_entry_pathname(ae), '/'); 231 if (end == NULL) 232 continue; 233 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); 240 break; 241 } 242 } 243 244 if (r == ARCHIVE_OK) 245 ret = 0; 246 else 247 warnx("fail to extract pkg-static"); 248 249 cleanup: 250 archive_read_finish(a); 251 return (ret); 252 253 } 254 255 static int 256 install_pkg_static(char *path, char *pkgpath) 257 { 258 int pstat; 259 pid_t pid; 260 261 switch ((pid = fork())) { 262 case -1: 263 return (-1); 264 case 0: 265 execl(path, "pkg-static", "add", pkgpath, (char *)NULL); 266 _exit(1); 267 default: 268 break; 269 } 270 271 while (waitpid(pid, &pstat, 0) == -1) 272 if (errno != EINTR) 273 return (-1); 274 275 if (WEXITSTATUS(pstat)) 276 return (WEXITSTATUS(pstat)); 277 else if (WIFSIGNALED(pstat)) 278 return (128 & (WTERMSIG(pstat))); 279 return (pstat); 280 } 281 282 static int 283 bootstrap_pkg(void) 284 { 285 struct url *u; 286 FILE *remote; 287 FILE *config; 288 char *site; 289 struct dns_srvinfo *mirrors, *current; 290 /* To store _https._tcp. + hostname + \0 */ 291 char zone[MAXHOSTNAMELEN + 13]; 292 char url[MAXPATHLEN]; 293 char conf[MAXPATHLEN]; 294 char abi[BUFSIZ]; 295 char tmppkg[MAXPATHLEN]; 296 char buf[10240]; 297 char pkgstatic[MAXPATHLEN]; 298 int fd, retry, ret, max_retry; 299 struct url_stat st; 300 off_t done, r; 301 time_t now; 302 time_t last; 303 304 done = 0; 305 last = 0; 306 max_retry = 3; 307 ret = -1; 308 remote = NULL; 309 config = NULL; 310 current = mirrors = NULL; 311 312 printf("Bootstrapping pkg please wait\n"); 313 314 if (pkg_get_myabi(abi, MAXPATHLEN) != 0) { 315 warnx("failed to determine the system ABI"); 316 return (-1); 317 } 318 319 if (getenv("PACKAGESITE") != NULL) 320 snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", getenv("PACKAGESITE")); 321 else 322 snprintf(url, MAXPATHLEN, "%s/%s/latest/Latest/pkg.txz", 323 getenv("PACKAGEROOT") ? getenv("PACKAGEROOT") : _PKGS_URL, 324 getenv("ABI") ? getenv("ABI") : abi); 325 326 snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", 327 getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); 328 329 if ((fd = mkstemp(tmppkg)) == -1) { 330 warn("mkstemp()"); 331 return (-1); 332 } 333 334 retry = max_retry; 335 336 u = fetchParseURL(url); 337 while (remote == NULL) { 338 if (retry == max_retry) { 339 if (strcmp(u->scheme, "file") != 0) { 340 snprintf(zone, sizeof(zone), 341 "_%s._tcp.%s", u->scheme, u->host); 342 printf("%s\n", zone); 343 mirrors = dns_getsrvinfo(zone); 344 current = mirrors; 345 } 346 } 347 348 if (mirrors != NULL) 349 strlcpy(u->host, current->host, sizeof(u->host)); 350 351 remote = fetchXGet(u, &st, ""); 352 if (remote == NULL) { 353 --retry; 354 if (retry <= 0) 355 goto fetchfail; 356 if (mirrors == NULL) { 357 sleep(1); 358 } else { 359 current = current->next; 360 if (current == NULL) 361 current = mirrors; 362 } 363 } 364 } 365 366 if (remote == NULL) 367 goto fetchfail; 368 369 while (done < st.size) { 370 if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) 371 break; 372 373 if (write(fd, buf, r) != r) { 374 warn("write()"); 375 goto cleanup; 376 } 377 378 done += r; 379 now = time(NULL); 380 if (now > last || done == st.size) 381 last = now; 382 } 383 384 if (ferror(remote)) 385 goto fetchfail; 386 387 if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) 388 ret = install_pkg_static(pkgstatic, tmppkg); 389 390 snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf", 391 getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 392 393 if (access(conf, R_OK) == -1) { 394 site = strrchr(url, '/'); 395 if (site == NULL) 396 goto cleanup; 397 site[0] = '\0'; 398 site = strrchr(url, '/'); 399 if (site == NULL) 400 goto cleanup; 401 site[0] = '\0'; 402 403 config = fopen(conf, "w+"); 404 if (config == NULL) 405 goto cleanup; 406 fprintf(config, "packagesite: %s\n", url); 407 fclose(config); 408 } 409 410 goto cleanup; 411 412 fetchfail: 413 warnx("Error fetching %s: %s", url, fetchLastErrString); 414 415 cleanup: 416 if (remote != NULL) 417 fclose(remote); 418 close(fd); 419 unlink(tmppkg); 420 421 return (ret); 422 } 423 424 static const char confirmation_message[] = 425 "The package management tool is not yet installed on your system.\n" 426 "Do you want to fetch and install it now? [y/N]: "; 427 428 static int 429 pkg_query_yes_no(void) 430 { 431 int ret, c; 432 433 c = getchar(); 434 435 if (c == 'y' || c == 'Y') 436 ret = 1; 437 else 438 ret = 0; 439 440 while (c != '\n' && c != EOF) 441 c = getchar(); 442 443 return (ret); 444 } 445 446 int 447 main(__unused int argc, char *argv[]) 448 { 449 char pkgpath[MAXPATHLEN]; 450 451 snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", 452 getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); 453 454 if (access(pkgpath, X_OK) == -1) { 455 /* 456 * Do not ask for confirmation if either of stdin or stdout is 457 * not tty. Check the environment to see if user has answer 458 * tucked in there already. 459 */ 460 if (getenv("ASSUME_ALWAYS_YES") == NULL) { 461 printf("%s", confirmation_message); 462 if (!isatty(fileno(stdin))) 463 exit(EXIT_FAILURE); 464 465 if (pkg_query_yes_no() == 0) 466 exit(EXIT_FAILURE); 467 } 468 if (bootstrap_pkg() != 0) 469 exit(EXIT_FAILURE); 470 } 471 472 execv(pkgpath, argv); 473 474 /* NOT REACHED */ 475 return (EXIT_FAILURE); 476 } 477