]> CyberLeo.Net >> Repos - FreeBSD/stable/9.git/blob - usr.sbin/pkg/config.c
MFC: r241737,r246790,r247060,r247841,r248033,r248133,r255468,r256450,r256770
[FreeBSD/stable/9.git] / usr.sbin / pkg / config.c
1 /*-
2  * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
3  * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27
28 #include <sys/cdefs.h>
29 __FBSDID("$FreeBSD$");
30
31 #include <sys/param.h>
32 #include <sys/sbuf.h>
33 #include <sys/elf_common.h>
34 #include <sys/endian.h>
35 #include <sys/types.h>
36
37 #include <dirent.h>
38 #include <ucl.h>
39 #include <ctype.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <gelf.h>
44 #include <inttypes.h>
45 #include <paths.h>
46 #include <stdbool.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 #include "elf_tables.h"
51 #include "config.h"
52
53 #define roundup2(x, y)  (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
54
55 struct config_value {
56        char *value;
57        STAILQ_ENTRY(config_value) next;
58 };
59
60 struct config_entry {
61         uint8_t type;
62         const char *key;
63         const char *val;
64         char *value;
65         STAILQ_HEAD(, config_value) *list;
66         bool envset;
67 };
68
69 static struct config_entry c[] = {
70         [PACKAGESITE] = {
71                 PKG_CONFIG_STRING,
72                 "PACKAGESITE",
73                 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
74                 NULL,
75                 NULL,
76                 false,
77         },
78         [ABI] = {
79                 PKG_CONFIG_STRING,
80                 "ABI",
81                 NULL,
82                 NULL,
83                 NULL,
84                 false,
85         },
86         [MIRROR_TYPE] = {
87                 PKG_CONFIG_STRING,
88                 "MIRROR_TYPE",
89                 "SRV",
90                 NULL,
91                 NULL,
92                 false,
93         },
94         [ASSUME_ALWAYS_YES] = {
95                 PKG_CONFIG_BOOL,
96                 "ASSUME_ALWAYS_YES",
97                 "NO",
98                 NULL,
99                 NULL,
100                 false,
101         },
102         [SIGNATURE_TYPE] = {
103                 PKG_CONFIG_STRING,
104                 "SIGNATURE_TYPE",
105                 NULL,
106                 NULL,
107                 NULL,
108                 false,
109         },
110         [FINGERPRINTS] = {
111                 PKG_CONFIG_STRING,
112                 "FINGERPRINTS",
113                 NULL,
114                 NULL,
115                 NULL,
116                 false,
117         },
118         [REPOS_DIR] = {
119                 PKG_CONFIG_LIST,
120                 "REPOS_DIR",
121                 NULL,
122                 NULL,
123                 NULL,
124                 false,
125         },
126 };
127
128 static const char *
129 elf_corres_to_string(struct _elf_corres *m, int e)
130 {
131         int i;
132
133         for (i = 0; m[i].string != NULL; i++)
134                 if (m[i].elf_nb == e)
135                         return (m[i].string);
136
137         return ("unknown");
138 }
139
140 static int
141 pkg_get_myabi(char *dest, size_t sz)
142 {
143         Elf *elf;
144         Elf_Data *data;
145         Elf_Note note;
146         Elf_Scn *scn;
147         char *src, *osname;
148         const char *abi;
149         GElf_Ehdr elfhdr;
150         GElf_Shdr shdr;
151         int fd, i, ret;
152         uint32_t version;
153
154         version = 0;
155         ret = -1;
156         scn = NULL;
157         abi = NULL;
158
159         if (elf_version(EV_CURRENT) == EV_NONE) {
160                 warnx("ELF library initialization failed: %s",
161                     elf_errmsg(-1));
162                 return (-1);
163         }
164
165         if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
166                 warn("open()");
167                 return (-1);
168         }
169
170         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
171                 ret = -1;
172                 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
173                 goto cleanup;
174         }
175
176         if (gelf_getehdr(elf, &elfhdr) == NULL) {
177                 ret = -1;
178                 warn("getehdr() failed: %s.", elf_errmsg(-1));
179                 goto cleanup;
180         }
181         while ((scn = elf_nextscn(elf, scn)) != NULL) {
182                 if (gelf_getshdr(scn, &shdr) != &shdr) {
183                         ret = -1;
184                         warn("getshdr() failed: %s.", elf_errmsg(-1));
185                         goto cleanup;
186                 }
187
188                 if (shdr.sh_type == SHT_NOTE)
189                         break;
190         }
191
192         if (scn == NULL) {
193                 ret = -1;
194                 warn("failed to get the note section");
195                 goto cleanup;
196         }
197
198         data = elf_getdata(scn, NULL);
199         src = data->d_buf;
200         for (;;) {
201                 memcpy(&note, src, sizeof(Elf_Note));
202                 src += sizeof(Elf_Note);
203                 if (note.n_type == NT_VERSION)
204                         break;
205                 src += note.n_namesz + note.n_descsz;
206         }
207         osname = src;
208         src += roundup2(note.n_namesz, 4);
209         if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
210                 version = be32dec(src);
211         else
212                 version = le32dec(src);
213
214         for (i = 0; osname[i] != '\0'; i++)
215                 osname[i] = (char)tolower(osname[i]);
216
217         snprintf(dest, sz, "%s:%d:%s:%s",
218             osname, version / 100000,
219             elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
220             elf_corres_to_string(wordsize_corres,
221             (int)elfhdr.e_ident[EI_CLASS]));
222
223         ret = 0;
224
225         switch (elfhdr.e_machine) {
226         case EM_ARM:
227                 snprintf(dest + strlen(dest), sz - strlen(dest),
228                     ":%s:%s:%s", elf_corres_to_string(endian_corres,
229                     (int)elfhdr.e_ident[EI_DATA]),
230                     (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
231                     "eabi" : "oabi",
232                     (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
233                     "softfp" : "vfp");
234                 break;
235         case EM_MIPS:
236                 /*
237                  * this is taken from binutils sources:
238                  * include/elf/mips.h
239                  * mapping is figured out from binutils:
240                  * gas/config/tc-mips.c
241                  */
242                 switch (elfhdr.e_flags & EF_MIPS_ABI) {
243                 case E_MIPS_ABI_O32:
244                         abi = "o32";
245                         break;
246                 case E_MIPS_ABI_N32:
247                         abi = "n32";
248                         break;
249                 default:
250                         if (elfhdr.e_ident[EI_DATA] ==
251                             ELFCLASS32)
252                                 abi = "o32";
253                         else if (elfhdr.e_ident[EI_DATA] ==
254                             ELFCLASS64)
255                                 abi = "n64";
256                         break;
257                 }
258                 snprintf(dest + strlen(dest), sz - strlen(dest),
259                     ":%s:%s", elf_corres_to_string(endian_corres,
260                     (int)elfhdr.e_ident[EI_DATA]), abi);
261                 break;
262         }
263
264 cleanup:
265         if (elf != NULL)
266                 elf_end(elf);
267
268         close(fd);
269         return (ret);
270 }
271
272 static void
273 subst_packagesite(const char *abi)
274 {
275         struct sbuf *newval;
276         const char *variable_string;
277         const char *oldval;
278
279         if (c[PACKAGESITE].value != NULL)
280                 oldval = c[PACKAGESITE].value;
281         else
282                 oldval = c[PACKAGESITE].val;
283
284         if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
285                 return;
286
287         newval = sbuf_new_auto();
288         sbuf_bcat(newval, oldval, variable_string - oldval);
289         sbuf_cat(newval, abi);
290         sbuf_cat(newval, variable_string + strlen("${ABI}"));
291         sbuf_finish(newval);
292
293         free(c[PACKAGESITE].value);
294         c[PACKAGESITE].value = strdup(sbuf_data(newval));
295 }
296
297 static int
298 boolstr_to_bool(const char *str)
299 {
300         if (str != NULL && (strcasecmp(str, "true") == 0 ||
301             strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
302             str[0] == '1'))
303                 return (true);
304
305         return (false);
306 }
307
308 static void
309 config_parse(ucl_object_t *obj, pkg_conf_file_t conftype)
310 {
311         struct sbuf *buf = sbuf_new_auto();
312         ucl_object_t *cur, *seq;
313         ucl_object_iter_t it = NULL, itseq = NULL;
314         struct config_entry *temp_config;
315         struct config_value *cv;
316         const char *key;
317         int i;
318         size_t j;
319
320         /* Temporary config for configs that may be disabled. */
321         temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
322
323         while ((cur = ucl_iterate_object(obj, &it, true))) {
324                 key = ucl_object_key(cur);
325                 if (key == NULL)
326                         continue;
327                 sbuf_clear(buf);
328
329                 if (conftype == CONFFILE_PKG) {
330                         for (j = 0; j < strlen(key); ++j)
331                                 sbuf_putc(buf, key[j]);
332                         sbuf_finish(buf);
333                 } else if (conftype == CONFFILE_REPO) {
334                         if (strcasecmp(key, "url") == 0)
335                                 sbuf_cpy(buf, "PACKAGESITE");
336                         else if (strcasecmp(key, "mirror_type") == 0)
337                                 sbuf_cpy(buf, "MIRROR_TYPE");
338                         else if (strcasecmp(key, "signature_type") == 0)
339                                 sbuf_cpy(buf, "SIGNATURE_TYPE");
340                         else if (strcasecmp(key, "fingerprints") == 0)
341                                 sbuf_cpy(buf, "FINGERPRINTS");
342                         else if (strcasecmp(key, "enabled") == 0) {
343                                 if ((cur->type != UCL_BOOLEAN) ||
344                                     !ucl_object_toboolean(cur))
345                                         goto cleanup;
346                         } else
347                                 continue;
348                         sbuf_finish(buf);
349                 }
350
351                 for (i = 0; i < CONFIG_SIZE; i++) {
352                         if (strcmp(sbuf_data(buf), c[i].key) == 0)
353                                 break;
354                 }
355
356                 /* Silently skip unknown keys to be future compatible. */
357                 if (i == CONFIG_SIZE)
358                         continue;
359
360                 /* env has priority over config file */
361                 if (c[i].envset)
362                         continue;
363
364                 /* Parse sequence value ["item1", "item2"] */
365                 switch (c[i].type) {
366                 case PKG_CONFIG_LIST:
367                         if (cur->type != UCL_ARRAY) {
368                                 warnx("Skipping invalid array "
369                                     "value for %s.\n", c[i].key);
370                                 continue;
371                         }
372                         temp_config[i].list =
373                             malloc(sizeof(*temp_config[i].list));
374                         STAILQ_INIT(temp_config[i].list);
375
376                         while ((seq = ucl_iterate_object(cur, &itseq, true))) {
377                                 if (seq->type != UCL_STRING)
378                                         continue;
379                                 cv = malloc(sizeof(struct config_value));
380                                 cv->value =
381                                     strdup(ucl_object_tostring(seq));
382                                 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
383                                     next);
384                         }
385                         break;
386                 default:
387                         /* Normal string value. */
388                         temp_config[i].value = strdup(ucl_object_tostring(cur));
389                         break;
390                 }
391         }
392
393         /* Repo is enabled, copy over all settings from temp_config. */
394         for (i = 0; i < CONFIG_SIZE; i++) {
395                 if (c[i].envset)
396                         continue;
397                 switch (c[i].type) {
398                 case PKG_CONFIG_LIST:
399                         c[i].list = temp_config[i].list;
400                         break;
401                 default:
402                         c[i].value = temp_config[i].value;
403                         break;
404                 }
405         }
406
407 cleanup:
408         free(temp_config);
409         sbuf_delete(buf);
410 }
411
412 /*-
413  * Parse new repo style configs in style:
414  * Name:
415  *   URL:
416  *   MIRROR_TYPE:
417  * etc...
418  */
419 static void
420 parse_repo_file(ucl_object_t *obj)
421 {
422         ucl_object_iter_t it = NULL;
423         ucl_object_t *cur;
424         const char *key;
425
426         while ((cur = ucl_iterate_object(obj, &it, true))) {
427                 key = ucl_object_key(cur);
428
429                 if (key == NULL)
430                         continue;
431
432                 if (cur->type != UCL_OBJECT)
433                         continue;
434
435                 config_parse(cur, CONFFILE_REPO);
436         }
437 }
438
439
440 static int
441 read_conf_file(const char *confpath, pkg_conf_file_t conftype)
442 {
443         struct ucl_parser *p;
444         ucl_object_t *obj = NULL;
445
446         p = ucl_parser_new(0);
447
448         if (!ucl_parser_add_file(p, confpath)) {
449                 if (errno != ENOENT)
450                         errx(EXIT_FAILURE, "Unable to parse configuration "
451                             "file %s: %s", confpath, ucl_parser_get_error(p));
452                 ucl_parser_free(p);
453                 /* no configuration present */
454                 return (1);
455         }
456
457         obj = ucl_parser_get_object(p);
458         if (obj->type != UCL_OBJECT) 
459                 warnx("Invalid configuration format, ignoring the "
460                     "configuration file %s", confpath);
461         else {
462                 if (conftype == CONFFILE_PKG)
463                         config_parse(obj, conftype);
464                 else if (conftype == CONFFILE_REPO)
465                         parse_repo_file(obj);
466         }
467
468         ucl_object_free(obj);
469         ucl_parser_free(p);
470
471         return (0);
472 }
473
474 static int
475 load_repositories(const char *repodir)
476 {
477         struct dirent *ent;
478         DIR *d;
479         char *p;
480         size_t n;
481         char path[MAXPATHLEN];
482         int ret;
483
484         ret = 0;
485
486         if ((d = opendir(repodir)) == NULL)
487                 return (1);
488
489         while ((ent = readdir(d))) {
490                 /* Trim out 'repos'. */
491                 if ((n = strlen(ent->d_name)) <= 5)
492                         continue;
493                 p = &ent->d_name[n - 5];
494                 if (strcmp(p, ".conf") == 0) {
495                         snprintf(path, sizeof(path), "%s%s%s",
496                             repodir,
497                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
498                             ent->d_name);
499                         if (access(path, F_OK) == 0 &&
500                             read_conf_file(path, CONFFILE_REPO)) {
501                                 ret = 1;
502                                 goto cleanup;
503                         }
504                 }
505         }
506
507 cleanup:
508         closedir(d);
509
510         return (ret);
511 }
512
513 int
514 config_init(void)
515 {
516         char *val;
517         int i;
518         const char *localbase;
519         char *env_list_item;
520         char confpath[MAXPATHLEN];
521         struct config_value *cv;
522         char abi[BUFSIZ];
523
524         for (i = 0; i < CONFIG_SIZE; i++) {
525                 val = getenv(c[i].key);
526                 if (val != NULL) {
527                         c[i].envset = true;
528                         switch (c[i].type) {
529                         case PKG_CONFIG_LIST:
530                                 /* Split up comma-separated items from env. */
531                                 c[i].list = malloc(sizeof(*c[i].list));
532                                 STAILQ_INIT(c[i].list);
533                                 for (env_list_item = strtok(val, ",");
534                                     env_list_item != NULL;
535                                     env_list_item = strtok(NULL, ",")) {
536                                         cv =
537                                             malloc(sizeof(struct config_value));
538                                         cv->value =
539                                             strdup(env_list_item);
540                                         STAILQ_INSERT_TAIL(c[i].list, cv,
541                                             next);
542                                 }
543                                 break;
544                         default:
545                                 c[i].val = val;
546                                 break;
547                         }
548                 }
549         }
550
551         /* Read LOCALBASE/etc/pkg.conf first. */
552         localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
553         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
554             localbase);
555
556         if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
557             CONFFILE_PKG))
558                 goto finalize;
559
560         /* Then read in all repos from REPOS_DIR list of directories. */
561         if (c[REPOS_DIR].list == NULL) {
562                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
563                 STAILQ_INIT(c[REPOS_DIR].list);
564                 cv = malloc(sizeof(struct config_value));
565                 cv->value = strdup("/etc/pkg");
566                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
567                 cv = malloc(sizeof(struct config_value));
568                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
569                         goto finalize;
570                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
571         }
572
573         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
574                 if (load_repositories(cv->value))
575                         goto finalize;
576
577 finalize:
578         if (c[ABI].val == NULL && c[ABI].value == NULL) {
579                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
580                         errx(EXIT_FAILURE, "Failed to determine the system "
581                             "ABI");
582                 c[ABI].val = abi;
583         }
584
585         subst_packagesite(c[ABI].val);
586
587         return (0);
588 }
589
590 int
591 config_string(pkg_config_key k, const char **val)
592 {
593         if (c[k].type != PKG_CONFIG_STRING)
594                 return (-1);
595
596         if (c[k].value != NULL)
597                 *val = c[k].value;
598         else
599                 *val = c[k].val;
600
601         return (0);
602 }
603
604 int
605 config_bool(pkg_config_key k, bool *val)
606 {
607         const char *value;
608
609         if (c[k].type != PKG_CONFIG_BOOL)
610                 return (-1);
611
612         *val = false;
613
614         if (c[k].value != NULL)
615                 value = c[k].value;
616         else
617                 value = c[k].val;
618
619         if (boolstr_to_bool(value))
620                 *val = true;
621
622         return (0);
623 }
624
625 void
626 config_finish(void) {
627         int i;
628
629         for (i = 0; i < CONFIG_SIZE; i++)
630                 free(c[i].value);
631 }