Index: head/usr.sbin/pkg/config.c =================================================================== --- head/usr.sbin/pkg/config.c (revision 257050) +++ head/usr.sbin/pkg/config.c (revision 257051) @@ -1,630 +1,630 @@ /*- * Copyright (c) 2013 Baptiste Daroussin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "elf_tables.h" #include "config.h" #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ struct config_entry { uint8_t type; const char *key; const char *val; char *value; bool envset; }; static struct config_entry c[] = { [PACKAGESITE] = { PKG_CONFIG_STRING, "PACKAGESITE", - "http://pkg.FreeBSD.org/${ABI}/latest", + URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest", NULL, false, }, [ABI] = { PKG_CONFIG_STRING, "ABI", NULL, NULL, false, }, [MIRROR_TYPE] = { PKG_CONFIG_STRING, "MIRROR_TYPE", "SRV", NULL, false, }, [ASSUME_ALWAYS_YES] = { PKG_CONFIG_BOOL, "ASSUME_ALWAYS_YES", "NO", NULL, false, } }; static const char * elf_corres_to_string(struct _elf_corres *m, int e) { int i; for (i = 0; m[i].string != NULL; i++) if (m[i].elf_nb == e) return (m[i].string); return ("unknown"); } static const char * aeabi_parse_arm_attributes(void *data, size_t length) { uint32_t sect_len; uint8_t *section = data; #define MOVE(len) do { \ assert(length >= (len)); \ section += (len); \ length -= (len); \ } while (0) if (length == 0 || *section != 'A') return (NULL); MOVE(1); /* Read the section length */ if (length < sizeof(sect_len)) return (NULL); memcpy(§_len, section, sizeof(sect_len)); /* * The section length should be no longer than the section it is within */ if (sect_len > length) return (NULL); MOVE(sizeof(sect_len)); /* Skip the vendor name */ while (length != 0) { if (*section == '\0') break; MOVE(1); } if (length == 0) return (NULL); MOVE(1); while (length != 0) { uint32_t tag_length; switch(*section) { case 1: /* Tag_File */ MOVE(1); if (length < sizeof(tag_length)) return (NULL); memcpy(&tag_length, section, sizeof(tag_length)); break; case 2: /* Tag_Section */ case 3: /* Tag_Symbol */ default: return (NULL); } /* At least space for the tag and size */ if (tag_length <= 5) return (NULL); tag_length--; /* Check the tag fits */ if (tag_length > length) return (NULL); #define MOVE_TAG(len) do { \ assert(tag_length >= (len)); \ MOVE(len); \ tag_length -= (len); \ } while(0) MOVE(sizeof(tag_length)); tag_length -= sizeof(tag_length); while (tag_length != 0) { uint8_t tag; assert(tag_length >= length); tag = *section; MOVE_TAG(1); /* * These tag values come from: * * Addenda to, and Errata in, the ABI for the * ARM Architecture. Release 2.08, section 2.3. */ if (tag == 6) { /* == Tag_CPU_arch */ uint8_t val; val = *section; /* * We don't support values that require * more than one byte. */ if (val & (1 << 7)) return (NULL); /* We have an ARMv4 or ARMv5 */ if (val <= 5) return ("arm"); else /* We have an ARMv6+ */ return ("armv6"); } else if (tag == 4 || tag == 5 || tag == 32 || tag == 65 || tag == 67) { while (*section != '\0' && length != 0) MOVE_TAG(1); if (tag_length == 0) return (NULL); /* Skip the last byte */ MOVE_TAG(1); } else if ((tag >= 7 && tag <= 31) || tag == 34 || tag == 36 || tag == 38 || tag == 42 || tag == 44 || tag == 64 || tag == 66 || tag == 68 || tag == 70) { /* Skip the uleb128 data */ while (*section & (1 << 7) && length != 0) MOVE_TAG(1); if (tag_length == 0) return (NULL); /* Skip the last byte */ MOVE_TAG(1); } else return (NULL); #undef MOVE_TAG } break; } return (NULL); #undef MOVE } static int pkg_get_myabi(char *dest, size_t sz) { Elf *elf; Elf_Data *data; Elf_Note note; Elf_Scn *scn; char *src, *osname; const char *arch, *abi, *fpu, *endian_corres_str; const char *wordsize_corres_str; GElf_Ehdr elfhdr; GElf_Shdr shdr; int fd, i, ret; uint32_t version; version = 0; ret = -1; scn = NULL; abi = NULL; if (elf_version(EV_CURRENT) == EV_NONE) { warnx("ELF library initialization failed: %s", elf_errmsg(-1)); return (-1); } if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) { warn("open()"); return (-1); } if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { ret = -1; warnx("elf_begin() failed: %s.", elf_errmsg(-1)); goto cleanup; } if (gelf_getehdr(elf, &elfhdr) == NULL) { ret = -1; warn("getehdr() failed: %s.", elf_errmsg(-1)); goto cleanup; } while ((scn = elf_nextscn(elf, scn)) != NULL) { if (gelf_getshdr(scn, &shdr) != &shdr) { ret = -1; warn("getshdr() failed: %s.", elf_errmsg(-1)); goto cleanup; } if (shdr.sh_type == SHT_NOTE) break; } if (scn == NULL) { ret = -1; warn("failed to get the note section"); goto cleanup; } data = elf_getdata(scn, NULL); src = data->d_buf; for (;;) { memcpy(¬e, src, sizeof(Elf_Note)); src += sizeof(Elf_Note); if (note.n_type == NT_VERSION) break; src += note.n_namesz + note.n_descsz; } osname = src; src += roundup2(note.n_namesz, 4); if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB) version = be32dec(src); else version = le32dec(src); for (i = 0; osname[i] != '\0'; i++) osname[i] = (char)tolower(osname[i]); wordsize_corres_str = elf_corres_to_string(wordsize_corres, (int)elfhdr.e_ident[EI_CLASS]); arch = elf_corres_to_string(mach_corres, (int) elfhdr.e_machine); snprintf(dest, sz, "%s:%d", osname, version / 100000); ret = 0; switch (elfhdr.e_machine) { case EM_ARM: endian_corres_str = elf_corres_to_string(endian_corres, (int)elfhdr.e_ident[EI_DATA]); /* FreeBSD doesn't support the hard-float ABI yet */ fpu = "softfp"; if ((elfhdr.e_flags & 0xFF000000) != 0) { const char *sh_name = NULL; size_t shstrndx; /* This is an EABI file, the conformance level is set */ abi = "eabi"; /* Find which TARGET_ARCH we are building for. */ elf_getshdrstrndx(elf, &shstrndx); while ((scn = elf_nextscn(elf, scn)) != NULL) { sh_name = NULL; if (gelf_getshdr(scn, &shdr) != &shdr) { scn = NULL; break; } sh_name = elf_strptr(elf, shstrndx, shdr.sh_name); if (sh_name == NULL) continue; if (strcmp(".ARM.attributes", sh_name) == 0) break; } if (scn != NULL && sh_name != NULL) { data = elf_getdata(scn, NULL); /* * Prior to FreeBSD 10.0 libelf would return * NULL from elf_getdata on the .ARM.attributes * section. As this was the first release to * get armv6 support assume a NULL value means * arm. * * This assumption can be removed when 9.x * is unsupported. */ if (data != NULL) { arch = aeabi_parse_arm_attributes( data->d_buf, data->d_size); if (arch == NULL) { ret = 1; warn("unknown ARM ARCH"); goto cleanup; } } } else { ret = 1; warn("Unable to find the .ARM.attributes " "section"); goto cleanup; } } else if (elfhdr.e_ident[EI_OSABI] != ELFOSABI_NONE) { /* * EABI executables all have this field set to * ELFOSABI_NONE, therefore it must be an oabi file. */ abi = "oabi"; } else { ret = 1; warn("unknown ARM ABI"); goto cleanup; } snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s:%s:%s:%s", arch, wordsize_corres_str, endian_corres_str, abi, fpu); break; case EM_MIPS: /* * this is taken from binutils sources: * include/elf/mips.h * mapping is figured out from binutils: * gas/config/tc-mips.c */ switch (elfhdr.e_flags & EF_MIPS_ABI) { case E_MIPS_ABI_O32: abi = "o32"; break; case E_MIPS_ABI_N32: abi = "n32"; break; default: if (elfhdr.e_ident[EI_DATA] == ELFCLASS32) abi = "o32"; else if (elfhdr.e_ident[EI_DATA] == ELFCLASS64) abi = "n64"; break; } endian_corres_str = elf_corres_to_string(endian_corres, (int)elfhdr.e_ident[EI_DATA]); snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s:%s:%s", arch, wordsize_corres_str, endian_corres_str, abi); break; default: snprintf(dest + strlen(dest), sz - strlen(dest), ":%s:%s", arch, wordsize_corres_str); } cleanup: if (elf != NULL) elf_end(elf); close(fd); return (ret); } static void subst_packagesite(const char *abi) { struct sbuf *newval; const char *variable_string; const char *oldval; if (c[PACKAGESITE].value != NULL) oldval = c[PACKAGESITE].value; else oldval = c[PACKAGESITE].val; if ((variable_string = strstr(oldval, "${ABI}")) == NULL) return; newval = sbuf_new_auto(); sbuf_bcat(newval, oldval, variable_string - oldval); sbuf_cat(newval, abi); sbuf_cat(newval, variable_string + strlen("${ABI}")); sbuf_finish(newval); free(c[PACKAGESITE].value); c[PACKAGESITE].value = strdup(sbuf_data(newval)); } static void config_parse(yaml_document_t *doc, yaml_node_t *node) { yaml_node_pair_t *pair; yaml_node_t *key, *val; struct sbuf *buf = sbuf_new_auto(); int i; size_t j; pair = node->data.mapping.pairs.start; while (pair < node->data.mapping.pairs.top) { key = yaml_document_get_node(doc, pair->key); val = yaml_document_get_node(doc, pair->value); /* * ignoring silently empty keys can be empty lines * or user mistakes */ if (key->data.scalar.length <= 0) { ++pair; continue; } /* * silently skip on purpose to allow user to leave * empty lines without complaining */ if (val->type == YAML_NO_NODE || (val->type == YAML_SCALAR_NODE && val->data.scalar.length <= 0)) { ++pair; continue; } sbuf_clear(buf); for (j = 0; j < strlen(key->data.scalar.value); ++j) sbuf_putc(buf, toupper(key->data.scalar.value[j])); sbuf_finish(buf); for (i = 0; i < CONFIG_SIZE; i++) { if (strcmp(sbuf_data(buf), c[i].key) == 0) break; } if (i == CONFIG_SIZE) { ++pair; continue; } /* env has priority over config file */ if (c[i].envset) { ++pair; continue; } c[i].value = strdup(val->data.scalar.value); ++pair; } sbuf_delete(buf); } int config_init(void) { FILE *fp; yaml_parser_t parser; yaml_document_t doc; yaml_node_t *node; const char *val; int i; const char *localbase; char confpath[MAXPATHLEN]; char abi[BUFSIZ]; for (i = 0; i < CONFIG_SIZE; i++) { val = getenv(c[i].key); if (val != NULL) { c[i].val = val; c[i].envset = true; } } localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase); if ((fp = fopen(confpath, "r")) == NULL) { if (errno != ENOENT) err(EXIT_FAILURE, "Unable to open configuration file %s", confpath); /* no configuration present */ goto finalize; } yaml_parser_initialize(&parser); yaml_parser_set_input_file(&parser, fp); yaml_parser_load(&parser, &doc); node = yaml_document_get_root_node(&doc); if (node != NULL) { if (node->type != YAML_MAPPING_NODE) warnx("Invalid configuration format, ignoring the configuration file"); else config_parse(&doc, node); } else { warnx("Invalid configuration format, ignoring the configuration file"); } yaml_document_delete(&doc); yaml_parser_delete(&parser); finalize: if (c[ABI].val == NULL && c[ABI].value == NULL) { if (pkg_get_myabi(abi, BUFSIZ) != 0) errx(EXIT_FAILURE, "Failed to determine the system ABI"); c[ABI].val = abi; } subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val); return (0); } int config_string(pkg_config_key k, const char **val) { if (c[k].type != PKG_CONFIG_STRING) return (-1); if (c[k].value != NULL) *val = c[k].value; else *val = c[k].val; return (0); } int config_bool(pkg_config_key k, bool *val) { const char *value; if (c[k].type != PKG_CONFIG_BOOL) return (-1); *val = false; if (c[k].value != NULL) value = c[k].value; else value = c[k].val; if (strcasecmp(value, "true") == 0 || strcasecmp(value, "yes") == 0 || strcasecmp(value, "on") == 0 || *value == '1') *val = true; return (0); } void config_finish(void) { int i; for (i = 0; i < CONFIG_SIZE; i++) free(c[i].value); } Index: head/usr.sbin/pkg/config.h =================================================================== --- head/usr.sbin/pkg/config.h (revision 257050) +++ head/usr.sbin/pkg/config.h (revision 257051) @@ -1,52 +1,53 @@ /*- * Copyright (c) 2013 Baptiste Daroussin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _PKG_CONFIG_H #define _PKG_CONFIG_H #define _LOCALBASE "/usr/local" +#define URL_SCHEME_PREFIX "pkg+" typedef enum { PACKAGESITE = 0, ABI, MIRROR_TYPE, ASSUME_ALWAYS_YES, CONFIG_SIZE } pkg_config_key; typedef enum { PKG_CONFIG_STRING=0, PKG_CONFIG_BOOL, } pkg_config_t; int config_init(void); void config_finish(void); int config_string(pkg_config_key, const char **); int config_bool(pkg_config_key, bool *); #endif Index: head/usr.sbin/pkg/pkg.c =================================================================== --- head/usr.sbin/pkg/pkg.c (revision 257050) +++ head/usr.sbin/pkg/pkg.c (revision 257051) @@ -1,354 +1,361 @@ /*- * Copyright (c) 2012-2013 Baptiste Daroussin * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "dns_utils.h" #include "config.h" static int extract_pkg_static(int fd, char *p, int sz) { struct archive *a; struct archive_entry *ae; char *end; int ret, r; ret = -1; a = archive_read_new(); if (a == NULL) { warn("archive_read_new"); return (ret); } archive_read_support_filter_all(a); archive_read_support_format_tar(a); if (lseek(fd, 0, 0) == -1) { warn("lseek"); goto cleanup; } if (archive_read_open_fd(a, fd, 4096) != ARCHIVE_OK) { warnx("archive_read_open_fd: %s", archive_error_string(a)); goto cleanup; } ae = NULL; while ((r = archive_read_next_header(a, &ae)) == ARCHIVE_OK) { end = strrchr(archive_entry_pathname(ae), '/'); if (end == NULL) continue; if (strcmp(end, "/pkg-static") == 0) { r = archive_read_extract(a, ae, ARCHIVE_EXTRACT_OWNER | ARCHIVE_EXTRACT_PERM | ARCHIVE_EXTRACT_TIME | ARCHIVE_EXTRACT_ACL | ARCHIVE_EXTRACT_FFLAGS | ARCHIVE_EXTRACT_XATTR); strlcpy(p, archive_entry_pathname(ae), sz); break; } } if (r == ARCHIVE_OK) ret = 0; else warnx("fail to extract pkg-static"); cleanup: archive_read_free(a); return (ret); } static int install_pkg_static(char *path, char *pkgpath) { int pstat; pid_t pid; switch ((pid = fork())) { case -1: return (-1); case 0: execl(path, "pkg-static", "add", pkgpath, (char *)NULL); _exit(1); default: break; } while (waitpid(pid, &pstat, 0) == -1) if (errno != EINTR) return (-1); if (WEXITSTATUS(pstat)) return (WEXITSTATUS(pstat)); else if (WIFSIGNALED(pstat)) return (128 & (WTERMSIG(pstat))); return (pstat); } static int bootstrap_pkg(void) { struct url *u; FILE *remote; FILE *config; char *site; struct dns_srvinfo *mirrors, *current; /* To store _https._tcp. + hostname + \0 */ char zone[MAXHOSTNAMELEN + 13]; char url[MAXPATHLEN]; char conf[MAXPATHLEN]; char tmppkg[MAXPATHLEN]; const char *packagesite, *mirror_type; char buf[10240]; char pkgstatic[MAXPATHLEN]; int fd, retry, ret, max_retry; struct url_stat st; off_t done, r; time_t now; time_t last; done = 0; last = 0; max_retry = 3; ret = -1; remote = NULL; config = NULL; current = mirrors = NULL; printf("Bootstrapping pkg, please wait...\n"); if (config_string(PACKAGESITE, &packagesite) != 0) { warnx("No PACKAGESITE defined"); return (-1); } if (config_string(MIRROR_TYPE, &mirror_type) != 0) { warnx("No MIRROR_TYPE defined"); return (-1); } + + /* Support pkg+http:// for PACKAGESITE which is the new format + in 1.2 to avoid confusion on why http://pkg.FreeBSD.org has + no A record. */ + if (strncmp(URL_SCHEME_PREFIX, packagesite, + strlen(URL_SCHEME_PREFIX)) == 0) + packagesite += strlen(URL_SCHEME_PREFIX); snprintf(url, MAXPATHLEN, "%s/Latest/pkg.txz", packagesite); snprintf(tmppkg, MAXPATHLEN, "%s/pkg.txz.XXXXXX", getenv("TMPDIR") ? getenv("TMPDIR") : _PATH_TMP); if ((fd = mkstemp(tmppkg)) == -1) { warn("mkstemp()"); return (-1); } retry = max_retry; u = fetchParseURL(url); while (remote == NULL) { if (retry == max_retry) { if (strcmp(u->scheme, "file") != 0 && strcasecmp(mirror_type, "srv") == 0) { snprintf(zone, sizeof(zone), "_%s._tcp.%s", u->scheme, u->host); mirrors = dns_getsrvinfo(zone); current = mirrors; } } if (mirrors != NULL) { strlcpy(u->host, current->host, sizeof(u->host)); u->port = current->port; } remote = fetchXGet(u, &st, ""); if (remote == NULL) { --retry; if (retry <= 0) goto fetchfail; if (mirrors == NULL) { sleep(1); } else { current = current->next; if (current == NULL) current = mirrors; } } } if (remote == NULL) goto fetchfail; while (done < st.size) { if ((r = fread(buf, 1, sizeof(buf), remote)) < 1) break; if (write(fd, buf, r) != r) { warn("write()"); goto cleanup; } done += r; now = time(NULL); if (now > last || done == st.size) last = now; } if (ferror(remote)) goto fetchfail; if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, tmppkg); snprintf(conf, MAXPATHLEN, "%s/etc/pkg.conf", getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); if (access(conf, R_OK) == -1) { site = strrchr(url, '/'); if (site == NULL) goto cleanup; site[0] = '\0'; site = strrchr(url, '/'); if (site == NULL) goto cleanup; site[0] = '\0'; config = fopen(conf, "w+"); if (config == NULL) goto cleanup; fprintf(config, "packagesite: %s\n", url); fclose(config); } goto cleanup; fetchfail: warnx("Error fetching %s: %s", url, fetchLastErrString); fprintf(stderr, "A pre-built version of pkg could not be found for your system.\n"); fprintf(stderr, "Consider changing PACKAGESITE or installing it from ports: 'ports-mgmt/pkg'.\n"); cleanup: if (remote != NULL) fclose(remote); close(fd); unlink(tmppkg); return (ret); } static const char confirmation_message[] = "The package management tool is not yet installed on your system.\n" "Do you want to fetch and install it now? [y/N]: "; static int pkg_query_yes_no(void) { int ret, c; c = getchar(); if (c == 'y' || c == 'Y') ret = 1; else ret = 0; while (c != '\n' && c != EOF) c = getchar(); return (ret); } int main(__unused int argc, char *argv[]) { char pkgpath[MAXPATHLEN]; char pkgstatic[MAXPATHLEN]; bool yes = false; int fd, ret; snprintf(pkgpath, MAXPATHLEN, "%s/sbin/pkg", getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE); if (access(pkgpath, X_OK) == -1) { /* * To allow 'pkg -N' to be used as a reliable test for whether * a system is configured to use pkg, don't bootstrap pkg * when that argument is given as argv[1]. */ if (argv[1] != NULL && strcmp(argv[1], "-N") == 0) errx(EXIT_FAILURE, "pkg is not installed"); if (argc > 2 && strcmp(argv[1], "add") == 0 && access(argv[2], R_OK) == 0) { fd = open(argv[2], O_RDONLY); if (fd == -1) err(EXIT_FAILURE, "Unable to open %s", argv[2]); if ((ret = extract_pkg_static(fd, pkgstatic, MAXPATHLEN)) == 0) ret = install_pkg_static(pkgstatic, argv[2]); close(fd); if (ret != 0) exit(EXIT_FAILURE); exit(EXIT_SUCCESS); } /* * Do not ask for confirmation if either of stdin or stdout is * not tty. Check the environment to see if user has answer * tucked in there already. */ config_init(); config_bool(ASSUME_ALWAYS_YES, &yes); if (!yes) { printf("%s", confirmation_message); if (!isatty(fileno(stdin))) exit(EXIT_FAILURE); if (pkg_query_yes_no() == 0) exit(EXIT_FAILURE); } if (bootstrap_pkg() != 0) exit(EXIT_FAILURE); config_finish(); } execv(pkgpath, argv); /* NOT REACHED */ return (EXIT_FAILURE); }