config.c revision 247841
1247841Sbapt/*-
2247841Sbapt * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org>
3247841Sbapt * All rights reserved.
4247841Sbapt *
5247841Sbapt * Redistribution and use in source and binary forms, with or without
6247841Sbapt * modification, are permitted provided that the following conditions
7247841Sbapt * are met:
8247841Sbapt * 1. Redistributions of source code must retain the above copyright
9247841Sbapt *    notice, this list of conditions and the following disclaimer.
10247841Sbapt * 2. Redistributions in binary form must reproduce the above copyright
11247841Sbapt *    notice, this list of conditions and the following disclaimer in the
12247841Sbapt *    documentation and/or other materials provided with the distribution.
13247841Sbapt *
14247841Sbapt * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15247841Sbapt * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16247841Sbapt * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17247841Sbapt * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18247841Sbapt * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19247841Sbapt * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20247841Sbapt * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21247841Sbapt * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22247841Sbapt * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23247841Sbapt * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24247841Sbapt * SUCH DAMAGE.
25247841Sbapt */
26247841Sbapt
27247841Sbapt#include <sys/cdefs.h>
28247841Sbapt__FBSDID("$FreeBSD: head/usr.sbin/pkg/config.c 247841 2013-03-05 13:31:06Z bapt $");
29247841Sbapt
30247841Sbapt#include <sys/param.h>
31247841Sbapt#include <sys/sbuf.h>
32247841Sbapt#include <sys/elf_common.h>
33247841Sbapt#include <sys/endian.h>
34247841Sbapt
35247841Sbapt#include <bsdyml.h>
36247841Sbapt#include <ctype.h>
37247841Sbapt#include <err.h>
38247841Sbapt#include <errno.h>
39247841Sbapt#include <fcntl.h>
40247841Sbapt#include <gelf.h>
41247841Sbapt#include <inttypes.h>
42247841Sbapt#include <paths.h>
43247841Sbapt#include <stdbool.h>
44247841Sbapt#include <string.h>
45247841Sbapt#include <unistd.h>
46247841Sbapt
47247841Sbapt#include "elf_tables.h"
48247841Sbapt#include "config.h"
49247841Sbapt
50247841Sbapt#define roundup2(x, y)	(((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
51247841Sbapt
52247841Sbaptstruct config_entry {
53247841Sbapt	uint8_t type;
54247841Sbapt	const char *key;
55247841Sbapt	const char *val;
56247841Sbapt	char *value;
57247841Sbapt	bool envset;
58247841Sbapt};
59247841Sbapt
60247841Sbaptstatic struct config_entry c[] = {
61247841Sbapt	[PACKAGESITE] = {
62247841Sbapt		PKG_CONFIG_STRING,
63247841Sbapt		"PACKAGESITE",
64247841Sbapt		"http://pkg.FreeBSD.org/${ABI}/latest",
65247841Sbapt		NULL,
66247841Sbapt		false,
67247841Sbapt	},
68247841Sbapt	[ABI] = {
69247841Sbapt		PKG_CONFIG_STRING,
70247841Sbapt		"ABI",
71247841Sbapt		NULL,
72247841Sbapt		NULL,
73247841Sbapt		false,
74247841Sbapt	},
75247841Sbapt	[MIRROR_TYPE] = {
76247841Sbapt		PKG_CONFIG_STRING,
77247841Sbapt		"MIRROR_TYPE",
78247841Sbapt		"SRV",
79247841Sbapt		NULL,
80247841Sbapt		false,
81247841Sbapt	},
82247841Sbapt	[ASSUME_ALWAYS_YES] = {
83247841Sbapt		PKG_CONFIG_BOOL,
84247841Sbapt		"ASSUME_ALWAYS_YES",
85247841Sbapt		"NO",
86247841Sbapt		NULL,
87247841Sbapt		false,
88247841Sbapt	}
89247841Sbapt};
90247841Sbapt
91247841Sbaptstatic const char *
92247841Sbaptelf_corres_to_string(struct _elf_corres *m, int e)
93247841Sbapt{
94247841Sbapt	int i;
95247841Sbapt
96247841Sbapt	for (i = 0; m[i].string != NULL; i++)
97247841Sbapt		if (m[i].elf_nb == e)
98247841Sbapt			return (m[i].string);
99247841Sbapt
100247841Sbapt	return ("unknown");
101247841Sbapt}
102247841Sbapt
103247841Sbaptstatic int
104247841Sbaptpkg_get_myabi(char *dest, size_t sz)
105247841Sbapt{
106247841Sbapt	Elf *elf;
107247841Sbapt	Elf_Data *data;
108247841Sbapt	Elf_Note note;
109247841Sbapt	Elf_Scn *scn;
110247841Sbapt	char *src, *osname;
111247841Sbapt	const char *abi;
112247841Sbapt	GElf_Ehdr elfhdr;
113247841Sbapt	GElf_Shdr shdr;
114247841Sbapt	int fd, i, ret;
115247841Sbapt	uint32_t version;
116247841Sbapt
117247841Sbapt	version = 0;
118247841Sbapt	ret = -1;
119247841Sbapt	scn = NULL;
120247841Sbapt	abi = NULL;
121247841Sbapt
122247841Sbapt	if (elf_version(EV_CURRENT) == EV_NONE) {
123247841Sbapt		warnx("ELF library initialization failed: %s",
124247841Sbapt		    elf_errmsg(-1));
125247841Sbapt		return (-1);
126247841Sbapt	}
127247841Sbapt
128247841Sbapt	if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
129247841Sbapt		warn("open()");
130247841Sbapt		return (-1);
131247841Sbapt	}
132247841Sbapt
133247841Sbapt	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
134247841Sbapt		ret = -1;
135247841Sbapt		warnx("elf_begin() failed: %s.", elf_errmsg(-1));
136247841Sbapt		goto cleanup;
137247841Sbapt	}
138247841Sbapt
139247841Sbapt	if (gelf_getehdr(elf, &elfhdr) == NULL) {
140247841Sbapt		ret = -1;
141247841Sbapt		warn("getehdr() failed: %s.", elf_errmsg(-1));
142247841Sbapt		goto cleanup;
143247841Sbapt	}
144247841Sbapt	while ((scn = elf_nextscn(elf, scn)) != NULL) {
145247841Sbapt		if (gelf_getshdr(scn, &shdr) != &shdr) {
146247841Sbapt			ret = -1;
147247841Sbapt			warn("getshdr() failed: %s.", elf_errmsg(-1));
148247841Sbapt			goto cleanup;
149247841Sbapt		}
150247841Sbapt
151247841Sbapt		if (shdr.sh_type == SHT_NOTE)
152247841Sbapt			break;
153247841Sbapt	}
154247841Sbapt
155247841Sbapt	if (scn == NULL) {
156247841Sbapt		ret = -1;
157247841Sbapt		warn("failed to get the note section");
158247841Sbapt		goto cleanup;
159247841Sbapt	}
160247841Sbapt
161247841Sbapt	data = elf_getdata(scn, NULL);
162247841Sbapt	src = data->d_buf;
163247841Sbapt	for (;;) {
164247841Sbapt		memcpy(&note, src, sizeof(Elf_Note));
165247841Sbapt		src += sizeof(Elf_Note);
166247841Sbapt		if (note.n_type == NT_VERSION)
167247841Sbapt			break;
168247841Sbapt		src += note.n_namesz + note.n_descsz;
169247841Sbapt	}
170247841Sbapt	osname = src;
171247841Sbapt	src += roundup2(note.n_namesz, 4);
172247841Sbapt	if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
173247841Sbapt		version = be32dec(src);
174247841Sbapt	else
175247841Sbapt		version = le32dec(src);
176247841Sbapt
177247841Sbapt	for (i = 0; osname[i] != '\0'; i++)
178247841Sbapt		osname[i] = (char)tolower(osname[i]);
179247841Sbapt
180247841Sbapt	snprintf(dest, sz, "%s:%d:%s:%s",
181247841Sbapt	    osname, version / 100000,
182247841Sbapt	    elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
183247841Sbapt	    elf_corres_to_string(wordsize_corres,
184247841Sbapt	    (int)elfhdr.e_ident[EI_CLASS]));
185247841Sbapt
186247841Sbapt	ret = 0;
187247841Sbapt
188247841Sbapt	switch (elfhdr.e_machine) {
189247841Sbapt	case EM_ARM:
190247841Sbapt		snprintf(dest + strlen(dest), sz - strlen(dest),
191247841Sbapt		    ":%s:%s:%s", elf_corres_to_string(endian_corres,
192247841Sbapt		    (int)elfhdr.e_ident[EI_DATA]),
193247841Sbapt		    (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
194247841Sbapt		    "eabi" : "oabi",
195247841Sbapt		    (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
196247841Sbapt		    "softfp" : "vfp");
197247841Sbapt		break;
198247841Sbapt	case EM_MIPS:
199247841Sbapt		/*
200247841Sbapt		 * this is taken from binutils sources:
201247841Sbapt		 * include/elf/mips.h
202247841Sbapt		 * mapping is figured out from binutils:
203247841Sbapt		 * gas/config/tc-mips.c
204247841Sbapt		 */
205247841Sbapt		switch (elfhdr.e_flags & EF_MIPS_ABI) {
206247841Sbapt		case E_MIPS_ABI_O32:
207247841Sbapt			abi = "o32";
208247841Sbapt			break;
209247841Sbapt		case E_MIPS_ABI_N32:
210247841Sbapt			abi = "n32";
211247841Sbapt			break;
212247841Sbapt		default:
213247841Sbapt			if (elfhdr.e_ident[EI_DATA] ==
214247841Sbapt			    ELFCLASS32)
215247841Sbapt				abi = "o32";
216247841Sbapt			else if (elfhdr.e_ident[EI_DATA] ==
217247841Sbapt			    ELFCLASS64)
218247841Sbapt				abi = "n64";
219247841Sbapt			break;
220247841Sbapt		}
221247841Sbapt		snprintf(dest + strlen(dest), sz - strlen(dest),
222247841Sbapt		    ":%s:%s", elf_corres_to_string(endian_corres,
223247841Sbapt		    (int)elfhdr.e_ident[EI_DATA]), abi);
224247841Sbapt		break;
225247841Sbapt	}
226247841Sbapt
227247841Sbaptcleanup:
228247841Sbapt	if (elf != NULL)
229247841Sbapt		elf_end(elf);
230247841Sbapt
231247841Sbapt	close(fd);
232247841Sbapt	return (ret);
233247841Sbapt}
234247841Sbapt
235247841Sbaptstatic void
236247841Sbaptsubst_packagesite(const char *abi)
237247841Sbapt{
238247841Sbapt	struct sbuf *newval;
239247841Sbapt	const char *variable_string;
240247841Sbapt	const char *oldval;
241247841Sbapt
242247841Sbapt	if (c[PACKAGESITE].value != NULL)
243247841Sbapt		oldval = c[PACKAGESITE].value;
244247841Sbapt	else
245247841Sbapt		oldval = c[PACKAGESITE].val;
246247841Sbapt
247247841Sbapt	if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
248247841Sbapt		return;
249247841Sbapt
250247841Sbapt	newval = sbuf_new_auto();
251247841Sbapt	sbuf_bcat(newval, oldval, variable_string - oldval);
252247841Sbapt	sbuf_cat(newval, abi);
253247841Sbapt	sbuf_cat(newval, variable_string + strlen("${ABI}"));
254247841Sbapt	sbuf_finish(newval);
255247841Sbapt
256247841Sbapt	free(c[PACKAGESITE].value);
257247841Sbapt	c[PACKAGESITE].value = strdup(sbuf_data(newval));
258247841Sbapt}
259247841Sbapt
260247841Sbaptstatic void
261247841Sbaptconfig_parse(yaml_document_t *doc, yaml_node_t *node)
262247841Sbapt{
263247841Sbapt	yaml_node_pair_t *pair;
264247841Sbapt	yaml_node_t *key, *val;
265247841Sbapt	struct sbuf *buf = sbuf_new_auto();
266247841Sbapt	int i;
267247841Sbapt	size_t j;
268247841Sbapt
269247841Sbapt	pair = node->data.mapping.pairs.start;
270247841Sbapt
271247841Sbapt	while (pair < node->data.mapping.pairs.top) {
272247841Sbapt		key = yaml_document_get_node(doc, pair->key);
273247841Sbapt		val = yaml_document_get_node(doc, pair->value);
274247841Sbapt
275247841Sbapt		/*
276247841Sbapt		 * ignoring silently empty keys can be empty lines
277247841Sbapt		 * or user mistakes
278247841Sbapt		 */
279247841Sbapt		if (key->data.scalar.length <= 0) {
280247841Sbapt			++pair;
281247841Sbapt			continue;
282247841Sbapt		}
283247841Sbapt
284247841Sbapt		/*
285247841Sbapt		 * silently skip on purpose to allow user to leave
286247841Sbapt		 * empty lines without complaining
287247841Sbapt		 */
288247841Sbapt		if (val->type == YAML_NO_NODE ||
289247841Sbapt		    (val->type == YAML_SCALAR_NODE &&
290247841Sbapt		     val->data.scalar.length <= 0)) {
291247841Sbapt			++pair;
292247841Sbapt			continue;
293247841Sbapt		}
294247841Sbapt
295247841Sbapt		sbuf_clear(buf);
296247841Sbapt		for (j = 0; j < strlen(key->data.scalar.value); ++j)
297247841Sbapt			sbuf_putc(buf, toupper(key->data.scalar.value[j]));
298247841Sbapt
299247841Sbapt		sbuf_finish(buf);
300247841Sbapt		for (i = 0; i < CONFIG_SIZE; i++) {
301247841Sbapt			if (strcmp(sbuf_data(buf), c[i].key) == 0)
302247841Sbapt				break;
303247841Sbapt		}
304247841Sbapt
305247841Sbapt		if (i == CONFIG_SIZE) {
306247841Sbapt			++pair;
307247841Sbapt			continue;
308247841Sbapt		}
309247841Sbapt
310247841Sbapt		/* env has priority over config file */
311247841Sbapt		if (c[i].envset) {
312247841Sbapt			++pair;
313247841Sbapt			continue;
314247841Sbapt		}
315247841Sbapt
316247841Sbapt		c[i].value = strdup(val->data.scalar.value);
317247841Sbapt		++pair;
318247841Sbapt	}
319247841Sbapt
320247841Sbapt	sbuf_delete(buf);
321247841Sbapt}
322247841Sbapt
323247841Sbaptint
324247841Sbaptconfig_init(void)
325247841Sbapt{
326247841Sbapt	FILE *fp;
327247841Sbapt	yaml_parser_t parser;
328247841Sbapt	yaml_document_t doc;
329247841Sbapt	yaml_node_t *node;
330247841Sbapt	const char *val;
331247841Sbapt	int i;
332247841Sbapt	const char *localbase;
333247841Sbapt	char confpath[MAXPATHLEN];
334247841Sbapt	char abi[BUFSIZ];
335247841Sbapt
336247841Sbapt	for (i = 0; i < CONFIG_SIZE; i++) {
337247841Sbapt		val = getenv(c[i].key);
338247841Sbapt		if (val != NULL) {
339247841Sbapt			c[i].val = val;
340247841Sbapt			c[i].envset = true;
341247841Sbapt		}
342247841Sbapt	}
343247841Sbapt
344247841Sbapt	localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
345247841Sbapt	snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
346247841Sbapt
347247841Sbapt	if ((fp = fopen(confpath, "r")) == NULL) {
348247841Sbapt		if (errno != ENOENT)
349247841Sbapt			err(EXIT_FAILURE, "Unable to open configuration file %s", confpath);
350247841Sbapt		/* no configuration present */
351247841Sbapt		goto finalize;
352247841Sbapt	}
353247841Sbapt
354247841Sbapt	yaml_parser_initialize(&parser);
355247841Sbapt	yaml_parser_set_input_file(&parser, fp);
356247841Sbapt	yaml_parser_load(&parser, &doc);
357247841Sbapt
358247841Sbapt	node = yaml_document_get_root_node(&doc);
359247841Sbapt
360247841Sbapt	if (node != NULL) {
361247841Sbapt		if (node->type != YAML_MAPPING_NODE)
362247841Sbapt			warnx("Invalid configuration format, ignoring the configuration file");
363247841Sbapt		else
364247841Sbapt			config_parse(&doc, node);
365247841Sbapt	} else {
366247841Sbapt		warnx("Invalid configuration format, ignoring the configuration file");
367247841Sbapt	}
368247841Sbapt
369247841Sbapt	yaml_document_delete(&doc);
370247841Sbapt	yaml_parser_delete(&parser);
371247841Sbapt
372247841Sbaptfinalize:
373247841Sbapt	if (c[ABI].val == NULL && c[ABI].value == NULL) {
374247841Sbapt		if (pkg_get_myabi(abi, BUFSIZ) != 0)
375247841Sbapt			errx(EXIT_FAILURE, "Failed to determine the system ABI");
376247841Sbapt		c[ABI].val = abi;
377247841Sbapt	}
378247841Sbapt
379247841Sbapt	subst_packagesite(c[ABI].val);
380247841Sbapt
381247841Sbapt	return (0);
382247841Sbapt}
383247841Sbapt
384247841Sbaptint
385247841Sbaptconfig_string(pkg_config_key k, const char **val)
386247841Sbapt{
387247841Sbapt	if (c[k].type != PKG_CONFIG_STRING)
388247841Sbapt		return (-1);
389247841Sbapt
390247841Sbapt	if (c[k].value != NULL)
391247841Sbapt		*val = c[k].value;
392247841Sbapt	else
393247841Sbapt		*val = c[k].val;
394247841Sbapt
395247841Sbapt	return (0);
396247841Sbapt}
397247841Sbapt
398247841Sbaptint
399247841Sbaptconfig_bool(pkg_config_key k, bool *val)
400247841Sbapt{
401247841Sbapt	const char *value;
402247841Sbapt
403247841Sbapt	if (c[k].type != PKG_CONFIG_BOOL)
404247841Sbapt		return (-1);
405247841Sbapt
406247841Sbapt	*val = false;
407247841Sbapt
408247841Sbapt	if (c[k].value != NULL)
409247841Sbapt		value = c[k].value;
410247841Sbapt	else
411247841Sbapt		value = c[k].val;
412247841Sbapt
413247841Sbapt	if (strcasecmp(value, "true") == 0 ||
414247841Sbapt	    strcasecmp(value, "yes") == 0 ||
415247841Sbapt	    strcasecmp(value, "on") == 0 ||
416247841Sbapt	    *value == '1')
417247841Sbapt		*val = true;
418247841Sbapt
419247841Sbapt	return (0);
420247841Sbapt}
421247841Sbapt
422247841Sbaptvoid
423247841Sbaptconfig_finish(void) {
424247841Sbapt	int i;
425247841Sbapt
426247841Sbapt	for (i = 0; i < CONFIG_SIZE; i++)
427247841Sbapt		free(c[i].value);
428247841Sbapt}
429