config.c revision 247841
19751Smdoerr/*-
29751Smdoerr * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org>
310049Sgoetz * All rights reserved.
49751Smdoerr *
59751Smdoerr * Redistribution and use in source and binary forms, with or without
69751Smdoerr * modification, are permitted provided that the following conditions
79751Smdoerr * are met:
89751Smdoerr * 1. Redistributions of source code must retain the above copyright
99751Smdoerr *    notice, this list of conditions and the following disclaimer.
109751Smdoerr * 2. Redistributions in binary form must reproduce the above copyright
119751Smdoerr *    notice, this list of conditions and the following disclaimer in the
129751Smdoerr *    documentation and/or other materials provided with the distribution.
139751Smdoerr *
149751Smdoerr * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
159751Smdoerr * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
169751Smdoerr * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
179751Smdoerr * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
189751Smdoerr * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
199751Smdoerr * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
209751Smdoerr * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
219751Smdoerr * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
229751Smdoerr * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
239751Smdoerr * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
249751Smdoerr * SUCH DAMAGE.
259751Smdoerr */
269751Smdoerr
279751Smdoerr#include <sys/cdefs.h>
289751Smdoerr__FBSDID("$FreeBSD: head/usr.sbin/pkg/config.c 247841 2013-03-05 13:31:06Z bapt $");
299751Smdoerr
309751Smdoerr#include <sys/param.h>
319751Smdoerr#include <sys/sbuf.h>
329751Smdoerr#include <sys/elf_common.h>
339751Smdoerr#include <sys/endian.h>
349751Smdoerr
359751Smdoerr#include <bsdyml.h>
369751Smdoerr#include <ctype.h>
379751Smdoerr#include <err.h>
389751Smdoerr#include <errno.h>
399751Smdoerr#include <fcntl.h>
409751Smdoerr#include <gelf.h>
419751Smdoerr#include <inttypes.h>
429751Smdoerr#include <paths.h>
439751Smdoerr#include <stdbool.h>
449751Smdoerr#include <string.h>
459751Smdoerr#include <unistd.h>
469751Smdoerr
479751Smdoerr#include "elf_tables.h"
489751Smdoerr#include "config.h"
499751Smdoerr
509751Smdoerr#define roundup2(x, y)	(((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
519751Smdoerr
529751Smdoerrstruct config_entry {
539751Smdoerr	uint8_t type;
549751Smdoerr	const char *key;
559751Smdoerr	const char *val;
569751Smdoerr	char *value;
579751Smdoerr	bool envset;
589751Smdoerr};
599751Smdoerr
609751Smdoerrstatic struct config_entry c[] = {
619751Smdoerr	[PACKAGESITE] = {
629751Smdoerr		PKG_CONFIG_STRING,
639751Smdoerr		"PACKAGESITE",
649751Smdoerr		"http://pkg.FreeBSD.org/${ABI}/latest",
659751Smdoerr		NULL,
669751Smdoerr		false,
679751Smdoerr	},
689751Smdoerr	[ABI] = {
699751Smdoerr		PKG_CONFIG_STRING,
709751Smdoerr		"ABI",
719751Smdoerr		NULL,
729751Smdoerr		NULL,
739751Smdoerr		false,
749751Smdoerr	},
759751Smdoerr	[MIRROR_TYPE] = {
769751Smdoerr		PKG_CONFIG_STRING,
77		"MIRROR_TYPE",
78		"SRV",
79		NULL,
80		false,
81	},
82	[ASSUME_ALWAYS_YES] = {
83		PKG_CONFIG_BOOL,
84		"ASSUME_ALWAYS_YES",
85		"NO",
86		NULL,
87		false,
88	}
89};
90
91static const char *
92elf_corres_to_string(struct _elf_corres *m, int e)
93{
94	int i;
95
96	for (i = 0; m[i].string != NULL; i++)
97		if (m[i].elf_nb == e)
98			return (m[i].string);
99
100	return ("unknown");
101}
102
103static int
104pkg_get_myabi(char *dest, size_t sz)
105{
106	Elf *elf;
107	Elf_Data *data;
108	Elf_Note note;
109	Elf_Scn *scn;
110	char *src, *osname;
111	const char *abi;
112	GElf_Ehdr elfhdr;
113	GElf_Shdr shdr;
114	int fd, i, ret;
115	uint32_t version;
116
117	version = 0;
118	ret = -1;
119	scn = NULL;
120	abi = NULL;
121
122	if (elf_version(EV_CURRENT) == EV_NONE) {
123		warnx("ELF library initialization failed: %s",
124		    elf_errmsg(-1));
125		return (-1);
126	}
127
128	if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
129		warn("open()");
130		return (-1);
131	}
132
133	if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
134		ret = -1;
135		warnx("elf_begin() failed: %s.", elf_errmsg(-1));
136		goto cleanup;
137	}
138
139	if (gelf_getehdr(elf, &elfhdr) == NULL) {
140		ret = -1;
141		warn("getehdr() failed: %s.", elf_errmsg(-1));
142		goto cleanup;
143	}
144	while ((scn = elf_nextscn(elf, scn)) != NULL) {
145		if (gelf_getshdr(scn, &shdr) != &shdr) {
146			ret = -1;
147			warn("getshdr() failed: %s.", elf_errmsg(-1));
148			goto cleanup;
149		}
150
151		if (shdr.sh_type == SHT_NOTE)
152			break;
153	}
154
155	if (scn == NULL) {
156		ret = -1;
157		warn("failed to get the note section");
158		goto cleanup;
159	}
160
161	data = elf_getdata(scn, NULL);
162	src = data->d_buf;
163	for (;;) {
164		memcpy(&note, src, sizeof(Elf_Note));
165		src += sizeof(Elf_Note);
166		if (note.n_type == NT_VERSION)
167			break;
168		src += note.n_namesz + note.n_descsz;
169	}
170	osname = src;
171	src += roundup2(note.n_namesz, 4);
172	if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
173		version = be32dec(src);
174	else
175		version = le32dec(src);
176
177	for (i = 0; osname[i] != '\0'; i++)
178		osname[i] = (char)tolower(osname[i]);
179
180	snprintf(dest, sz, "%s:%d:%s:%s",
181	    osname, version / 100000,
182	    elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
183	    elf_corres_to_string(wordsize_corres,
184	    (int)elfhdr.e_ident[EI_CLASS]));
185
186	ret = 0;
187
188	switch (elfhdr.e_machine) {
189	case EM_ARM:
190		snprintf(dest + strlen(dest), sz - strlen(dest),
191		    ":%s:%s:%s", elf_corres_to_string(endian_corres,
192		    (int)elfhdr.e_ident[EI_DATA]),
193		    (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
194		    "eabi" : "oabi",
195		    (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
196		    "softfp" : "vfp");
197		break;
198	case EM_MIPS:
199		/*
200		 * this is taken from binutils sources:
201		 * include/elf/mips.h
202		 * mapping is figured out from binutils:
203		 * gas/config/tc-mips.c
204		 */
205		switch (elfhdr.e_flags & EF_MIPS_ABI) {
206		case E_MIPS_ABI_O32:
207			abi = "o32";
208			break;
209		case E_MIPS_ABI_N32:
210			abi = "n32";
211			break;
212		default:
213			if (elfhdr.e_ident[EI_DATA] ==
214			    ELFCLASS32)
215				abi = "o32";
216			else if (elfhdr.e_ident[EI_DATA] ==
217			    ELFCLASS64)
218				abi = "n64";
219			break;
220		}
221		snprintf(dest + strlen(dest), sz - strlen(dest),
222		    ":%s:%s", elf_corres_to_string(endian_corres,
223		    (int)elfhdr.e_ident[EI_DATA]), abi);
224		break;
225	}
226
227cleanup:
228	if (elf != NULL)
229		elf_end(elf);
230
231	close(fd);
232	return (ret);
233}
234
235static void
236subst_packagesite(const char *abi)
237{
238	struct sbuf *newval;
239	const char *variable_string;
240	const char *oldval;
241
242	if (c[PACKAGESITE].value != NULL)
243		oldval = c[PACKAGESITE].value;
244	else
245		oldval = c[PACKAGESITE].val;
246
247	if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
248		return;
249
250	newval = sbuf_new_auto();
251	sbuf_bcat(newval, oldval, variable_string - oldval);
252	sbuf_cat(newval, abi);
253	sbuf_cat(newval, variable_string + strlen("${ABI}"));
254	sbuf_finish(newval);
255
256	free(c[PACKAGESITE].value);
257	c[PACKAGESITE].value = strdup(sbuf_data(newval));
258}
259
260static void
261config_parse(yaml_document_t *doc, yaml_node_t *node)
262{
263	yaml_node_pair_t *pair;
264	yaml_node_t *key, *val;
265	struct sbuf *buf = sbuf_new_auto();
266	int i;
267	size_t j;
268
269	pair = node->data.mapping.pairs.start;
270
271	while (pair < node->data.mapping.pairs.top) {
272		key = yaml_document_get_node(doc, pair->key);
273		val = yaml_document_get_node(doc, pair->value);
274
275		/*
276		 * ignoring silently empty keys can be empty lines
277		 * or user mistakes
278		 */
279		if (key->data.scalar.length <= 0) {
280			++pair;
281			continue;
282		}
283
284		/*
285		 * silently skip on purpose to allow user to leave
286		 * empty lines without complaining
287		 */
288		if (val->type == YAML_NO_NODE ||
289		    (val->type == YAML_SCALAR_NODE &&
290		     val->data.scalar.length <= 0)) {
291			++pair;
292			continue;
293		}
294
295		sbuf_clear(buf);
296		for (j = 0; j < strlen(key->data.scalar.value); ++j)
297			sbuf_putc(buf, toupper(key->data.scalar.value[j]));
298
299		sbuf_finish(buf);
300		for (i = 0; i < CONFIG_SIZE; i++) {
301			if (strcmp(sbuf_data(buf), c[i].key) == 0)
302				break;
303		}
304
305		if (i == CONFIG_SIZE) {
306			++pair;
307			continue;
308		}
309
310		/* env has priority over config file */
311		if (c[i].envset) {
312			++pair;
313			continue;
314		}
315
316		c[i].value = strdup(val->data.scalar.value);
317		++pair;
318	}
319
320	sbuf_delete(buf);
321}
322
323int
324config_init(void)
325{
326	FILE *fp;
327	yaml_parser_t parser;
328	yaml_document_t doc;
329	yaml_node_t *node;
330	const char *val;
331	int i;
332	const char *localbase;
333	char confpath[MAXPATHLEN];
334	char abi[BUFSIZ];
335
336	for (i = 0; i < CONFIG_SIZE; i++) {
337		val = getenv(c[i].key);
338		if (val != NULL) {
339			c[i].val = val;
340			c[i].envset = true;
341		}
342	}
343
344	localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
345	snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
346
347	if ((fp = fopen(confpath, "r")) == NULL) {
348		if (errno != ENOENT)
349			err(EXIT_FAILURE, "Unable to open configuration file %s", confpath);
350		/* no configuration present */
351		goto finalize;
352	}
353
354	yaml_parser_initialize(&parser);
355	yaml_parser_set_input_file(&parser, fp);
356	yaml_parser_load(&parser, &doc);
357
358	node = yaml_document_get_root_node(&doc);
359
360	if (node != NULL) {
361		if (node->type != YAML_MAPPING_NODE)
362			warnx("Invalid configuration format, ignoring the configuration file");
363		else
364			config_parse(&doc, node);
365	} else {
366		warnx("Invalid configuration format, ignoring the configuration file");
367	}
368
369	yaml_document_delete(&doc);
370	yaml_parser_delete(&parser);
371
372finalize:
373	if (c[ABI].val == NULL && c[ABI].value == NULL) {
374		if (pkg_get_myabi(abi, BUFSIZ) != 0)
375			errx(EXIT_FAILURE, "Failed to determine the system ABI");
376		c[ABI].val = abi;
377	}
378
379	subst_packagesite(c[ABI].val);
380
381	return (0);
382}
383
384int
385config_string(pkg_config_key k, const char **val)
386{
387	if (c[k].type != PKG_CONFIG_STRING)
388		return (-1);
389
390	if (c[k].value != NULL)
391		*val = c[k].value;
392	else
393		*val = c[k].val;
394
395	return (0);
396}
397
398int
399config_bool(pkg_config_key k, bool *val)
400{
401	const char *value;
402
403	if (c[k].type != PKG_CONFIG_BOOL)
404		return (-1);
405
406	*val = false;
407
408	if (c[k].value != NULL)
409		value = c[k].value;
410	else
411		value = c[k].val;
412
413	if (strcasecmp(value, "true") == 0 ||
414	    strcasecmp(value, "yes") == 0 ||
415	    strcasecmp(value, "on") == 0 ||
416	    *value == '1')
417		*val = true;
418
419	return (0);
420}
421
422void
423config_finish(void) {
424	int i;
425
426	for (i = 0; i < CONFIG_SIZE; i++)
427		free(c[i].value);
428}
429