xref: /freebsd/usr.sbin/pkg/config.c (revision f05cddf9)
1 /*-
2  * Copyright (c) 2013 Baptiste Daroussin <bapt@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  */
26 
27 #include <sys/cdefs.h>
28 __FBSDID("$FreeBSD$");
29 
30 #include <sys/param.h>
31 #include <sys/sbuf.h>
32 #include <sys/elf_common.h>
33 #include <sys/endian.h>
34 
35 #include <bsdyml.h>
36 #include <ctype.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <gelf.h>
41 #include <inttypes.h>
42 #include <paths.h>
43 #include <stdbool.h>
44 #include <string.h>
45 #include <unistd.h>
46 
47 #include "elf_tables.h"
48 #include "config.h"
49 
50 #define roundup2(x, y)	(((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
51 
52 struct config_entry {
53 	uint8_t type;
54 	const char *key;
55 	const char *val;
56 	char *value;
57 	bool envset;
58 };
59 
60 static struct config_entry c[] = {
61 	[PACKAGESITE] = {
62 		PKG_CONFIG_STRING,
63 		"PACKAGESITE",
64 		"http://pkg.FreeBSD.org/${ABI}/latest",
65 		NULL,
66 		false,
67 	},
68 	[ABI] = {
69 		PKG_CONFIG_STRING,
70 		"ABI",
71 		NULL,
72 		NULL,
73 		false,
74 	},
75 	[MIRROR_TYPE] = {
76 		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 
91 static const char *
92 elf_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 
103 static int
104 pkg_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 
227 cleanup:
228 	if (elf != NULL)
229 		elf_end(elf);
230 
231 	close(fd);
232 	return (ret);
233 }
234 
235 static void
236 subst_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 
260 static void
261 config_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 
323 int
324 config_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 
372 finalize:
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].value != NULL ? c[ABI].value : c[ABI].val);
380 
381 	return (0);
382 }
383 
384 int
385 config_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 
398 int
399 config_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 
422 void
423 config_finish(void) {
424 	int i;
425 
426 	for (i = 0; i < CONFIG_SIZE; i++)
427 		free(c[i].value);
428 }
429