config.c revision 247843
177218Sphk/*-
277218Sphk * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org>
377218Sphk * All rights reserved.
477218Sphk *
577218Sphk * Redistribution and use in source and binary forms, with or without
677218Sphk * modification, are permitted provided that the following conditions
777218Sphk * are met:
877218Sphk * 1. Redistributions of source code must retain the above copyright
977218Sphk *    notice, this list of conditions and the following disclaimer.
1077218Sphk * 2. Redistributions in binary form must reproduce the above copyright
1177218Sphk *    notice, this list of conditions and the following disclaimer in the
1291454Sbrooks *    documentation and/or other materials provided with the distribution.
1391454Sbrooks *
1477218Sphk * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1577218Sphk * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1677218Sphk * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1777218Sphk * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1877218Sphk * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
1977218Sphk * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2077218Sphk * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2177218Sphk * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2277218Sphk * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2377218Sphk * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2477218Sphk * SUCH DAMAGE.
2577218Sphk */
2677218Sphk
2777218Sphk#include <sys/cdefs.h>
2877218Sphk__FBSDID("$FreeBSD: head/usr.sbin/pkg/config.c 247843 2013-03-05 14:08:36Z bapt $");
2977218Sphk
3077218Sphk#include <sys/param.h>
3177218Sphk#include <sys/sbuf.h>
3277218Sphk#include <sys/elf_common.h>
3377218Sphk#include <sys/endian.h>
3477218Sphk
3577218Sphk#include <bsdyml.h>
3677218Sphk#include <ctype.h>
3777218Sphk#include <err.h>
3877218Sphk#include <errno.h>
3977218Sphk#include <fcntl.h>
4077218Sphk#include <gelf.h>
4177218Sphk#include <inttypes.h>
4277218Sphk#include <paths.h>
4377218Sphk#include <stdbool.h>
4477218Sphk#include <string.h>
4577218Sphk#include <unistd.h>
4677218Sphk
4777218Sphk#include "elf_tables.h"
4877218Sphk#include "config.h"
4977218Sphk
5077218Sphk#define roundup2(x, y)	(((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
5177218Sphk
5277218Sphkstruct config_entry {
5377218Sphk	uint8_t type;
5477218Sphk	const char *key;
5577218Sphk	const char *val;
5677218Sphk	char *value;
5777218Sphk	bool envset;
5877218Sphk};
5977218Sphk
6077218Sphkstatic struct config_entry c[] = {
6177218Sphk	[PACKAGESITE] = {
6277218Sphk		PKG_CONFIG_STRING,
6377218Sphk		"PACKAGESITE",
6477218Sphk		"http://pkg.FreeBSD.org/${ABI}/latest",
6577218Sphk		NULL,
6677218Sphk		false,
6777218Sphk	},
6877218Sphk	[ABI] = {
6977218Sphk		PKG_CONFIG_STRING,
7077218Sphk		"ABI",
7177218Sphk		NULL,
7277218Sphk		NULL,
7377218Sphk		false,
7477218Sphk	},
7577218Sphk	[MIRROR_TYPE] = {
7677218Sphk		PKG_CONFIG_STRING,
7777218Sphk		"MIRROR_TYPE",
78116957Ssam		"SRV",
79120178Ssam		NULL,
80116957Ssam		false,
8177218Sphk	},
8277218Sphk	[ASSUME_ALWAYS_YES] = {
8377218Sphk		PKG_CONFIG_BOOL,
8477218Sphk		"ASSUME_ALWAYS_YES",
8577218Sphk		"NO",
8677218Sphk		NULL,
8777218Sphk		false,
8877218Sphk	}
8977218Sphk};
9077218Sphk
9177218Sphkstatic const char *
9277218Sphkelf_corres_to_string(struct _elf_corres *m, int e)
9377218Sphk{
9477218Sphk	int i;
9577218Sphk
9677218Sphk	for (i = 0; m[i].string != NULL; i++)
9777218Sphk		if (m[i].elf_nb == e)
9877218Sphk			return (m[i].string);
9977218Sphk
10077218Sphk	return ("unknown");
10177218Sphk}
10277218Sphk
10377218Sphkstatic int
10477218Sphkpkg_get_myabi(char *dest, size_t sz)
10577218Sphk{
10688748Sambrisko	Elf *elf;
10788748Sambrisko	Elf_Data *data;
10888748Sambrisko	Elf_Note note;
10988748Sambrisko	Elf_Scn *scn;
11088748Sambrisko	char *src, *osname;
11177218Sphk	const char *abi;
11277218Sphk	GElf_Ehdr elfhdr;
11377218Sphk	GElf_Shdr shdr;
11477218Sphk	int fd, i, ret;
11577218Sphk	uint32_t version;
11677218Sphk
11777218Sphk	version = 0;
11877218Sphk	ret = -1;
11977218Sphk	scn = NULL;
12077218Sphk	abi = NULL;
12177218Sphk
12277218Sphk	if (elf_version(EV_CURRENT) == EV_NONE) {
12377218Sphk		warnx("ELF library initialization failed: %s",
12477218Sphk		    elf_errmsg(-1));
12577218Sphk		return (-1);
12677218Sphk	}
12777218Sphk
12877218Sphk	if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
12977218Sphk		warn("open()");
13077218Sphk		return (-1);
13177218Sphk	}
13277218Sphk
13377218Sphk	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
13477218Sphk		ret = -1;
135116957Ssam		warnx("elf_begin() failed: %s.", elf_errmsg(-1));
136116957Ssam		goto cleanup;
137116957Ssam	}
138116957Ssam
13977218Sphk	if (gelf_getehdr(elf, &elfhdr) == NULL) {
14077218Sphk		ret = -1;
14177218Sphk		warn("getehdr() failed: %s.", elf_errmsg(-1));
14277218Sphk		goto cleanup;
14377218Sphk	}
14477218Sphk	while ((scn = elf_nextscn(elf, scn)) != NULL) {
14577218Sphk		if (gelf_getshdr(scn, &shdr) != &shdr) {
14691454Sbrooks			ret = -1;
14777218Sphk			warn("getshdr() failed: %s.", elf_errmsg(-1));
14891454Sbrooks			goto cleanup;
14977218Sphk		}
15091454Sbrooks
15177218Sphk		if (shdr.sh_type == SHT_NOTE)
15277218Sphk			break;
15377218Sphk	}
15477218Sphk
15577218Sphk	if (scn == NULL) {
15677218Sphk		ret = -1;
15777218Sphk		warn("failed to get the note section");
15877218Sphk		goto cleanup;
15977218Sphk	}
16077218Sphk
16177218Sphk	data = elf_getdata(scn, NULL);
16277218Sphk	src = data->d_buf;
16377218Sphk	for (;;) {
16491454Sbrooks		memcpy(&note, src, sizeof(Elf_Note));
16577218Sphk		src += sizeof(Elf_Note);
16691454Sbrooks		if (note.n_type == NT_VERSION)
16777218Sphk			break;
16891454Sbrooks		src += note.n_namesz + note.n_descsz;
16977218Sphk	}
17091454Sbrooks	osname = src;
17177218Sphk	src += roundup2(note.n_namesz, 4);
17291454Sbrooks	if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
17377218Sphk		version = be32dec(src);
17477218Sphk	else
17577218Sphk		version = le32dec(src);
17677218Sphk
17777218Sphk	for (i = 0; osname[i] != '\0'; i++)
17877218Sphk		osname[i] = (char)tolower(osname[i]);
17977218Sphk
18077218Sphk	snprintf(dest, sz, "%s:%d:%s:%s",
18177218Sphk	    osname, version / 100000,
18277218Sphk	    elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
18377218Sphk	    elf_corres_to_string(wordsize_corres,
18477218Sphk	    (int)elfhdr.e_ident[EI_CLASS]));
18577218Sphk
18677218Sphk	ret = 0;
18777218Sphk
18877218Sphk	switch (elfhdr.e_machine) {
18977218Sphk	case EM_ARM:
19077218Sphk		snprintf(dest + strlen(dest), sz - strlen(dest),
19177218Sphk		    ":%s:%s:%s", elf_corres_to_string(endian_corres,
19277218Sphk		    (int)elfhdr.e_ident[EI_DATA]),
19377218Sphk		    (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
19477218Sphk		    "eabi" : "oabi",
19577218Sphk		    (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
19677218Sphk		    "softfp" : "vfp");
19777218Sphk		break;
19877218Sphk	case EM_MIPS:
19977218Sphk		/*
20077218Sphk		 * this is taken from binutils sources:
20177218Sphk		 * include/elf/mips.h
20277218Sphk		 * mapping is figured out from binutils:
20391454Sbrooks		 * gas/config/tc-mips.c
20477218Sphk		 */
20591454Sbrooks		switch (elfhdr.e_flags & EF_MIPS_ABI) {
20677218Sphk		case E_MIPS_ABI_O32:
20791454Sbrooks			abi = "o32";
20877218Sphk			break;
20977218Sphk		case E_MIPS_ABI_N32:
21077218Sphk			abi = "n32";
21177218Sphk			break;
21277218Sphk		default:
21377218Sphk			if (elfhdr.e_ident[EI_DATA] ==
21477218Sphk			    ELFCLASS32)
21577218Sphk				abi = "o32";
21677218Sphk			else if (elfhdr.e_ident[EI_DATA] ==
21777218Sphk			    ELFCLASS64)
21877218Sphk				abi = "n64";
21977218Sphk			break;
22077218Sphk		}
22177218Sphk		snprintf(dest + strlen(dest), sz - strlen(dest),
22277218Sphk		    ":%s:%s", elf_corres_to_string(endian_corres,
22377218Sphk		    (int)elfhdr.e_ident[EI_DATA]), abi);
22477218Sphk		break;
22577218Sphk	}
22677218Sphk
22777218Sphkcleanup:
22877218Sphk	if (elf != NULL)
22977218Sphk		elf_end(elf);
23077218Sphk
23177218Sphk	close(fd);
23277218Sphk	return (ret);
233120178Ssam}
23477218Sphk
23591454Sbrooksstatic void
23677218Sphksubst_packagesite(const char *abi)
23777218Sphk{
23877218Sphk	struct sbuf *newval;
23977218Sphk	const char *variable_string;
24077218Sphk	const char *oldval;
24177218Sphk
24277218Sphk	if (c[PACKAGESITE].value != NULL)
24377218Sphk		oldval = c[PACKAGESITE].value;
24477218Sphk	else
24577218Sphk		oldval = c[PACKAGESITE].val;
24677218Sphk
24777218Sphk	if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
24877218Sphk		return;
24977218Sphk
25077218Sphk	newval = sbuf_new_auto();
25177218Sphk	sbuf_bcat(newval, oldval, variable_string - oldval);
25277218Sphk	sbuf_cat(newval, abi);
25377218Sphk	sbuf_cat(newval, variable_string + strlen("${ABI}"));
25477218Sphk	sbuf_finish(newval);
25577218Sphk
25677218Sphk	free(c[PACKAGESITE].value);
257120178Ssam	c[PACKAGESITE].value = strdup(sbuf_data(newval));
25877218Sphk}
25977218Sphk
26077218Sphkstatic void
26191454Sbrooksconfig_parse(yaml_document_t *doc, yaml_node_t *node)
26277218Sphk{
26377218Sphk	yaml_node_pair_t *pair;
26477218Sphk	yaml_node_t *key, *val;
26591454Sbrooks	struct sbuf *buf = sbuf_new_auto();
26677218Sphk	int i;
26777218Sphk	size_t j;
26877218Sphk
26977218Sphk	pair = node->data.mapping.pairs.start;
27077218Sphk
27177218Sphk	while (pair < node->data.mapping.pairs.top) {
27277218Sphk		key = yaml_document_get_node(doc, pair->key);
27377218Sphk		val = yaml_document_get_node(doc, pair->value);
27477218Sphk
27577218Sphk		/*
27677218Sphk		 * ignoring silently empty keys can be empty lines
27777218Sphk		 * or user mistakes
27877218Sphk		 */
27977218Sphk		if (key->data.scalar.length <= 0) {
28077218Sphk			++pair;
28191454Sbrooks			continue;
28277218Sphk		}
28377218Sphk
28477218Sphk		/*
28577218Sphk		 * silently skip on purpose to allow user to leave
28677218Sphk		 * empty lines without complaining
28777218Sphk		 */
28877218Sphk		if (val->type == YAML_NO_NODE ||
28995005Simp		    (val->type == YAML_SCALAR_NODE &&
29077218Sphk		     val->data.scalar.length <= 0)) {
29177218Sphk			++pair;
29277218Sphk			continue;
29377218Sphk		}
29477218Sphk
29577218Sphk		sbuf_clear(buf);
29677218Sphk		for (j = 0; j < strlen(key->data.scalar.value); ++j)
29777218Sphk			sbuf_putc(buf, toupper(key->data.scalar.value[j]));
29877218Sphk
29977218Sphk		sbuf_finish(buf);
30077218Sphk		for (i = 0; i < CONFIG_SIZE; i++) {
30177218Sphk			if (strcmp(sbuf_data(buf), c[i].key) == 0)
30277218Sphk				break;
30377218Sphk		}
30477218Sphk
30577218Sphk		if (i == CONFIG_SIZE) {
30677218Sphk			++pair;
30777218Sphk			continue;
30877218Sphk		}
30988748Sambrisko
31088748Sambrisko		/* env has priority over config file */
31188748Sambrisko		if (c[i].envset) {
31288748Sambrisko			++pair;
31388748Sambrisko			continue;
31488748Sambrisko		}
31588748Sambrisko
31688748Sambrisko		c[i].value = strdup(val->data.scalar.value);
31788748Sambrisko		++pair;
31888748Sambrisko	}
31988748Sambrisko
32088748Sambrisko	sbuf_delete(buf);
32177218Sphk}
32277218Sphk
32377218Sphkint
32477218Sphkconfig_init(void)
32577218Sphk{
32677218Sphk	FILE *fp;
32777218Sphk	yaml_parser_t parser;
32877218Sphk	yaml_document_t doc;
32977218Sphk	yaml_node_t *node;
33077218Sphk	const char *val;
33177218Sphk	int i;
33277218Sphk	const char *localbase;
33377218Sphk	char confpath[MAXPATHLEN];
33477218Sphk	char abi[BUFSIZ];
33577218Sphk
33677218Sphk	for (i = 0; i < CONFIG_SIZE; i++) {
33777218Sphk		val = getenv(c[i].key);
33877218Sphk		if (val != NULL) {
33977218Sphk			c[i].val = val;
34077218Sphk			c[i].envset = true;
34177218Sphk		}
34277218Sphk	}
34377218Sphk
34477218Sphk	localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
34577218Sphk	snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
34677218Sphk
34777218Sphk	if ((fp = fopen(confpath, "r")) == NULL) {
34877218Sphk		if (errno != ENOENT)
34977218Sphk			err(EXIT_FAILURE, "Unable to open configuration file %s", confpath);
35077218Sphk		/* no configuration present */
35177218Sphk		goto finalize;
35277218Sphk	}
35377218Sphk
35477218Sphk	yaml_parser_initialize(&parser);
35577218Sphk	yaml_parser_set_input_file(&parser, fp);
35677218Sphk	yaml_parser_load(&parser, &doc);
35777218Sphk
35877218Sphk	node = yaml_document_get_root_node(&doc);
35977218Sphk
36077218Sphk	if (node != NULL) {
36177218Sphk		if (node->type != YAML_MAPPING_NODE)
36277218Sphk			warnx("Invalid configuration format, ignoring the configuration file");
36377218Sphk		else
36477218Sphk			config_parse(&doc, node);
36577218Sphk	} else {
36677218Sphk		warnx("Invalid configuration format, ignoring the configuration file");
36777218Sphk	}
36877218Sphk
36977218Sphk	yaml_document_delete(&doc);
37077218Sphk	yaml_parser_delete(&parser);
37177218Sphk
37277218Sphkfinalize:
37377218Sphk	if (c[ABI].val == NULL && c[ABI].value == NULL) {
37477218Sphk		if (pkg_get_myabi(abi, BUFSIZ) != 0)
37577218Sphk			errx(EXIT_FAILURE, "Failed to determine the system ABI");
37691454Sbrooks		c[ABI].val = abi;
37777218Sphk	}
37877218Sphk
37977218Sphk	subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
38077218Sphk
38177218Sphk	return (0);
38277218Sphk}
38377218Sphk
38480315Sbrooksint
38580315Sbrooksconfig_string(pkg_config_key k, const char **val)
38677218Sphk{
38777218Sphk	if (c[k].type != PKG_CONFIG_STRING)
38877218Sphk		return (-1);
38977218Sphk
39077218Sphk	if (c[k].value != NULL)
39177218Sphk		*val = c[k].value;
39277218Sphk	else
39377218Sphk		*val = c[k].val;
39477218Sphk
39577218Sphk	return (0);
39677218Sphk}
39777218Sphk
39877218Sphkint
39977218Sphkconfig_bool(pkg_config_key k, bool *val)
40077218Sphk{
40177218Sphk	const char *value;
40277218Sphk
40377218Sphk	if (c[k].type != PKG_CONFIG_BOOL)
40477218Sphk		return (-1);
40591454Sbrooks
40677218Sphk	*val = false;
40777218Sphk
40877218Sphk	if (c[k].value != NULL)
40977218Sphk		value = c[k].value;
41077218Sphk	else
41177218Sphk		value = c[k].val;
41277218Sphk
41377218Sphk	if (strcasecmp(value, "true") == 0 ||
41477218Sphk	    strcasecmp(value, "yes") == 0 ||
41577218Sphk	    strcasecmp(value, "on") == 0 ||
41677218Sphk	    *value == '1')
41777218Sphk		*val = true;
41877218Sphk
41977218Sphk	return (0);
42077218Sphk}
42177218Sphk
42277218Sphkvoid
42377218Sphkconfig_finish(void) {
42477218Sphk	int i;
42591454Sbrooks
42677218Sphk	for (i = 0; i < CONFIG_SIZE; i++)
42777218Sphk		free(c[i].value);
42877218Sphk}
42977218Sphk