]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pkg/config.c
DTrace: print() should try to resolve function pointers
[FreeBSD/FreeBSD.git] / usr.sbin / pkg / config.c
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 }