Fix multi-repository support by properly respecting 'enabled' flag.

This will read the REPOS_DIR env/config setting (default is /etc/pkg
and /usr/local/etc/pkg/repos) and use the last enabled repository.

This can be changed in the environment using a comma-separated list,
or in /usr/local/etc/pkg.conf with JSON array syntax of:
    REPOS_DIR: ["/etc/pkg", "/usr/local/etc/pkg/repos"]

Approved by:	bapt
MFC after:	1 week
This commit is contained in:
Bryan Drewery 2013-12-12 17:59:09 +00:00
parent 369f2ddcb8
commit eb31a57474
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=259266
3 changed files with 186 additions and 15 deletions

View File

@ -32,8 +32,10 @@ __FBSDID("$FreeBSD$");
#include <sys/sbuf.h> #include <sys/sbuf.h>
#include <sys/elf_common.h> #include <sys/elf_common.h>
#include <sys/endian.h> #include <sys/endian.h>
#include <sys/types.h>
#include <assert.h> #include <assert.h>
#include <dirent.h>
#include <yaml.h> #include <yaml.h>
#include <ctype.h> #include <ctype.h>
#include <err.h> #include <err.h>
@ -51,11 +53,17 @@ __FBSDID("$FreeBSD$");
#define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */ #define roundup2(x, y) (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
struct config_value {
char *value;
STAILQ_ENTRY(config_value) next;
};
struct config_entry { struct config_entry {
uint8_t type; uint8_t type;
const char *key; const char *key;
const char *val; const char *val;
char *value; char *value;
STAILQ_HEAD(, config_value) *list;
bool envset; bool envset;
}; };
@ -65,6 +73,7 @@ static struct config_entry c[] = {
"PACKAGESITE", "PACKAGESITE",
URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest", URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
NULL, NULL,
NULL,
false, false,
}, },
[ABI] = { [ABI] = {
@ -72,6 +81,7 @@ static struct config_entry c[] = {
"ABI", "ABI",
NULL, NULL,
NULL, NULL,
NULL,
false, false,
}, },
[MIRROR_TYPE] = { [MIRROR_TYPE] = {
@ -79,6 +89,7 @@ static struct config_entry c[] = {
"MIRROR_TYPE", "MIRROR_TYPE",
"SRV", "SRV",
NULL, NULL,
NULL,
false, false,
}, },
[ASSUME_ALWAYS_YES] = { [ASSUME_ALWAYS_YES] = {
@ -86,6 +97,7 @@ static struct config_entry c[] = {
"ASSUME_ALWAYS_YES", "ASSUME_ALWAYS_YES",
"NO", "NO",
NULL, NULL,
NULL,
false, false,
}, },
[SIGNATURE_TYPE] = { [SIGNATURE_TYPE] = {
@ -93,6 +105,7 @@ static struct config_entry c[] = {
"SIGNATURE_TYPE", "SIGNATURE_TYPE",
NULL, NULL,
NULL, NULL,
NULL,
false, false,
}, },
[FINGERPRINTS] = { [FINGERPRINTS] = {
@ -100,6 +113,15 @@ static struct config_entry c[] = {
"FINGERPRINTS", "FINGERPRINTS",
NULL, NULL,
NULL, NULL,
NULL,
false,
},
[REPOS_DIR] = {
PKG_CONFIG_LIST,
"REPOS_DIR",
NULL,
NULL,
NULL,
false, false,
}, },
}; };
@ -474,17 +496,34 @@ subst_packagesite(const char *abi)
c[PACKAGESITE].value = strdup(sbuf_data(newval)); c[PACKAGESITE].value = strdup(sbuf_data(newval));
} }
static int
boolstr_to_bool(const char *str)
{
if (str != NULL && (strcasecmp(str, "true") == 0 ||
strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
str[0] == '1'))
return (true);
return (false);
}
static void static void
config_parse(yaml_document_t *doc, yaml_node_t *node, pkg_conf_file_t conftype) config_parse(yaml_document_t *doc, yaml_node_t *node, pkg_conf_file_t conftype)
{ {
yaml_node_item_t *item;
yaml_node_pair_t *pair; yaml_node_pair_t *pair;
yaml_node_t *key, *val; yaml_node_t *key, *val, *item_val;
struct sbuf *buf = sbuf_new_auto(); struct sbuf *buf = sbuf_new_auto();
struct config_entry *temp_config;
struct config_value *cv;
int i; int i;
size_t j; size_t j;
pair = node->data.mapping.pairs.start; pair = node->data.mapping.pairs.start;
/* Temporary config for configs that may be disabled. */
temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
while (pair < node->data.mapping.pairs.top) { while (pair < node->data.mapping.pairs.top) {
key = yaml_document_get_node(doc, pair->key); key = yaml_document_get_node(doc, pair->key);
val = yaml_document_get_node(doc, pair->value); val = yaml_document_get_node(doc, pair->value);
@ -530,7 +569,12 @@ config_parse(yaml_document_t *doc, yaml_node_t *node, pkg_conf_file_t conftype)
else if (strcasecmp(key->data.scalar.value, else if (strcasecmp(key->data.scalar.value,
"fingerprints") == 0) "fingerprints") == 0)
sbuf_cpy(buf, "FINGERPRINTS"); sbuf_cpy(buf, "FINGERPRINTS");
else { /* Skip unknown entries for future use. */ else if (strcasecmp(key->data.scalar.value,
"enabled") == 0) {
/* Skip disabled repos. */
if (!boolstr_to_bool(val->data.scalar.value))
goto cleanup;
} else { /* Skip unknown entries for future use. */
++pair; ++pair;
continue; continue;
} }
@ -554,10 +598,58 @@ config_parse(yaml_document_t *doc, yaml_node_t *node, pkg_conf_file_t conftype)
continue; continue;
} }
c[i].value = strdup(val->data.scalar.value); /* Parse sequence value ["item1", "item2"] */
switch (c[i].type) {
case PKG_CONFIG_LIST:
if (val->type != YAML_SEQUENCE_NODE) {
fprintf(stderr, "Skipping invalid array "
"value for %s.\n", c[i].key);
++pair;
continue;
}
item = val->data.sequence.items.start;
temp_config[i].list =
malloc(sizeof(*temp_config[i].list));
STAILQ_INIT(temp_config[i].list);
while (item < val->data.sequence.items.top) {
item_val = yaml_document_get_node(doc, *item);
if (item_val->type != YAML_SCALAR_NODE) {
++item;
continue;
}
cv = malloc(sizeof(struct config_value));
cv->value =
strdup(item_val->data.scalar.value);
STAILQ_INSERT_TAIL(temp_config[i].list, cv,
next);
++item;
}
break;
default:
/* Normal string value. */
temp_config[i].value = strdup(val->data.scalar.value);
break;
}
++pair; ++pair;
} }
/* Repo is enabled, copy over all settings from temp_config. */
for (i = 0; i < CONFIG_SIZE; i++) {
if (c[i].envset)
continue;
switch (c[i].type) {
case PKG_CONFIG_LIST:
c[i].list = temp_config[i].list;
break;
default:
c[i].value = temp_config[i].value;
break;
}
}
cleanup:
free(temp_config);
sbuf_delete(buf); sbuf_delete(buf);
} }
@ -632,23 +724,84 @@ read_conf_file(const char *confpath, pkg_conf_file_t conftype)
return (0); return (0);
} }
static int
load_repositories(const char *repodir)
{
struct dirent *ent;
DIR *d;
char *p;
size_t n;
char path[MAXPATHLEN];
int ret;
ret = 0;
if ((d = opendir(repodir)) == NULL)
return (1);
while ((ent = readdir(d))) {
/* Trim out 'repos'. */
if ((n = strlen(ent->d_name)) <= 5)
continue;
p = &ent->d_name[n - 5];
if (strcmp(p, ".conf") == 0) {
snprintf(path, sizeof(path), "%s%s%s",
repodir,
repodir[strlen(repodir) - 1] == '/' ? "" : "/",
ent->d_name);
if (access(path, F_OK) == 0 &&
read_conf_file(path, CONFFILE_REPO)) {
ret = 1;
goto cleanup;
}
}
}
cleanup:
closedir(d);
return (ret);
}
int int
config_init(void) config_init(void)
{ {
const char *val; char *val;
int i; int i;
const char *localbase; const char *localbase;
char *env_list_item;
char confpath[MAXPATHLEN]; char confpath[MAXPATHLEN];
struct config_value *cv;
char abi[BUFSIZ]; char abi[BUFSIZ];
for (i = 0; i < CONFIG_SIZE; i++) { for (i = 0; i < CONFIG_SIZE; i++) {
val = getenv(c[i].key); val = getenv(c[i].key);
if (val != NULL) { if (val != NULL) {
c[i].val = val;
c[i].envset = true; c[i].envset = true;
switch (c[i].type) {
case PKG_CONFIG_LIST:
/* Split up comma-separated items from env. */
c[i].list = malloc(sizeof(*c[i].list));
STAILQ_INIT(c[i].list);
for (env_list_item = strtok(val, ",");
env_list_item != NULL;
env_list_item = strtok(NULL, ",")) {
cv =
malloc(sizeof(struct config_value));
cv->value =
strdup(env_list_item);
STAILQ_INSERT_TAIL(c[i].list, cv,
next);
}
break;
default:
c[i].val = val;
break;
}
} }
} }
/* Read LOCALBASE/etc/pkg.conf first. */
localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE; localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
localbase); localbase);
@ -657,10 +810,22 @@ config_init(void)
CONFFILE_PKG)) CONFFILE_PKG))
goto finalize; goto finalize;
snprintf(confpath, sizeof(confpath), "/etc/pkg/FreeBSD.conf"); /* Then read in all repos from REPOS_DIR list of directories. */
if (access(confpath, F_OK) == 0 && read_conf_file(confpath, if (c[REPOS_DIR].list == NULL) {
CONFFILE_REPO)) c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
goto finalize; STAILQ_INIT(c[REPOS_DIR].list);
cv = malloc(sizeof(struct config_value));
cv->value = strdup("/etc/pkg");
STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
cv = malloc(sizeof(struct config_value));
if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
goto finalize;
STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
}
STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
if (load_repositories(cv->value))
goto finalize;
finalize: finalize:
if (c[ABI].val == NULL && c[ABI].value == NULL) { if (c[ABI].val == NULL && c[ABI].value == NULL) {
@ -704,10 +869,7 @@ config_bool(pkg_config_key k, bool *val)
else else
value = c[k].val; value = c[k].val;
if (strcasecmp(value, "true") == 0 || if (boolstr_to_bool(value))
strcasecmp(value, "yes") == 0 ||
strcasecmp(value, "on") == 0 ||
*value == '1')
*val = true; *val = true;
return (0); return (0);

View File

@ -39,12 +39,14 @@ typedef enum {
ASSUME_ALWAYS_YES, ASSUME_ALWAYS_YES,
SIGNATURE_TYPE, SIGNATURE_TYPE,
FINGERPRINTS, FINGERPRINTS,
REPOS_DIR,
CONFIG_SIZE CONFIG_SIZE
} pkg_config_key; } pkg_config_key;
typedef enum { typedef enum {
PKG_CONFIG_STRING=0, PKG_CONFIG_STRING=0,
PKG_CONFIG_BOOL, PKG_CONFIG_BOOL,
PKG_CONFIG_LIST,
} pkg_config_t; } pkg_config_t;
typedef enum { typedef enum {

View File

@ -24,7 +24,7 @@
.\" .\"
.\" $FreeBSD$ .\" $FreeBSD$
.\" .\"
.Dd November 19, 2013 .Dd December 12, 2013
.Dt PKG 7 .Dt PKG 7
.Os .Os
.Sh NAME .Sh NAME
@ -152,6 +152,7 @@ MIRROR_TYPE: "srv",
SIGNATURE_TYPE: "none", SIGNATURE_TYPE: "none",
FINGERPRINTS: "/usr/share/keys/pkg", FINGERPRINTS: "/usr/share/keys/pkg",
ASSUME_ALWAYS_YES: "yes" ASSUME_ALWAYS_YES: "yes"
REPOS_DIR: ["/etc/pkg", "/usr/local/etc/pkg/repos"]
.Ed .Ed
.Pp .Pp
Reference Reference
@ -194,14 +195,20 @@ The URL that
.Xr pkg 8 .Xr pkg 8
and other packages and other packages
will be fetched from. will be fetched from.
.It Ev REPOS_DIR
Comma-separated list of directories that should be searched for repository
configuration files.
.El .El
.Sh FILES .Sh FILES
Configuration is read from the files in the listed order. Configuration is read from the files in the listed order.
The first enabled repository is the one used for bootstrapping This path can be changed by setting
.Sy REPOS_DIR .
The last enabled repository is the one used for bootstrapping
.Xr pkg 8 . .Xr pkg 8 .
.Bl -tag -width "/usr/local/etc/pkg/repos/*.conf" .Bl -tag -width "/usr/local/etc/pkg/repos/*.conf"
.It Pa /usr/local/etc/pkg.conf .It Pa /usr/local/etc/pkg.conf
.It Pa /etc/pkg/FreeBSD.conf .It Pa /etc/pkg/FreeBSD.conf
.It Pa /usr/local/etc/pkg/repos/*.conf
.El .El
.Sh EXAMPLES .Sh EXAMPLES
Some examples are listed here. Some examples are listed here.