]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - usr.sbin/pkg/config.c
Raise the default for sendmail client connections to 1024-bit DH
[FreeBSD/releng/9.3.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         bool main_only;                         /* Only set in pkg.conf. */
68 };
69
70 static struct config_entry c[] = {
71         [PACKAGESITE] = {
72                 PKG_CONFIG_STRING,
73                 "PACKAGESITE",
74                 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
75                 NULL,
76                 NULL,
77                 false,
78                 false,
79         },
80         [ABI] = {
81                 PKG_CONFIG_STRING,
82                 "ABI",
83                 NULL,
84                 NULL,
85                 NULL,
86                 false,
87                 true,
88         },
89         [MIRROR_TYPE] = {
90                 PKG_CONFIG_STRING,
91                 "MIRROR_TYPE",
92                 "SRV",
93                 NULL,
94                 NULL,
95                 false,
96                 false,
97         },
98         [ASSUME_ALWAYS_YES] = {
99                 PKG_CONFIG_BOOL,
100                 "ASSUME_ALWAYS_YES",
101                 "NO",
102                 NULL,
103                 NULL,
104                 false,
105                 true,
106         },
107         [SIGNATURE_TYPE] = {
108                 PKG_CONFIG_STRING,
109                 "SIGNATURE_TYPE",
110                 NULL,
111                 NULL,
112                 NULL,
113                 false,
114                 false,
115         },
116         [FINGERPRINTS] = {
117                 PKG_CONFIG_STRING,
118                 "FINGERPRINTS",
119                 NULL,
120                 NULL,
121                 NULL,
122                 false,
123                 false,
124         },
125         [REPOS_DIR] = {
126                 PKG_CONFIG_LIST,
127                 "REPOS_DIR",
128                 NULL,
129                 NULL,
130                 NULL,
131                 false,
132                 true,
133         },
134 };
135
136 static const char *
137 elf_corres_to_string(struct _elf_corres *m, int e)
138 {
139         int i;
140
141         for (i = 0; m[i].string != NULL; i++)
142                 if (m[i].elf_nb == e)
143                         return (m[i].string);
144
145         return ("unknown");
146 }
147
148 static int
149 pkg_get_myabi(char *dest, size_t sz)
150 {
151         Elf *elf;
152         Elf_Data *data;
153         Elf_Note note;
154         Elf_Scn *scn;
155         char *src, *osname;
156         const char *abi;
157         GElf_Ehdr elfhdr;
158         GElf_Shdr shdr;
159         int fd, i, ret;
160         uint32_t version;
161
162         version = 0;
163         ret = -1;
164         scn = NULL;
165         abi = NULL;
166
167         if (elf_version(EV_CURRENT) == EV_NONE) {
168                 warnx("ELF library initialization failed: %s",
169                     elf_errmsg(-1));
170                 return (-1);
171         }
172
173         if ((fd = open(_PATH_BSHELL, O_RDONLY)) < 0) {
174                 warn("open()");
175                 return (-1);
176         }
177
178         if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
179                 ret = -1;
180                 warnx("elf_begin() failed: %s.", elf_errmsg(-1));
181                 goto cleanup;
182         }
183
184         if (gelf_getehdr(elf, &elfhdr) == NULL) {
185                 ret = -1;
186                 warn("getehdr() failed: %s.", elf_errmsg(-1));
187                 goto cleanup;
188         }
189         while ((scn = elf_nextscn(elf, scn)) != NULL) {
190                 if (gelf_getshdr(scn, &shdr) != &shdr) {
191                         ret = -1;
192                         warn("getshdr() failed: %s.", elf_errmsg(-1));
193                         goto cleanup;
194                 }
195
196                 if (shdr.sh_type == SHT_NOTE)
197                         break;
198         }
199
200         if (scn == NULL) {
201                 ret = -1;
202                 warn("failed to get the note section");
203                 goto cleanup;
204         }
205
206         data = elf_getdata(scn, NULL);
207         src = data->d_buf;
208         for (;;) {
209                 memcpy(&note, src, sizeof(Elf_Note));
210                 src += sizeof(Elf_Note);
211                 if (note.n_type == NT_VERSION)
212                         break;
213                 src += note.n_namesz + note.n_descsz;
214         }
215         osname = src;
216         src += roundup2(note.n_namesz, 4);
217         if (elfhdr.e_ident[EI_DATA] == ELFDATA2MSB)
218                 version = be32dec(src);
219         else
220                 version = le32dec(src);
221
222         for (i = 0; osname[i] != '\0'; i++)
223                 osname[i] = (char)tolower(osname[i]);
224
225         snprintf(dest, sz, "%s:%d:%s:%s",
226             osname, version / 100000,
227             elf_corres_to_string(mach_corres, (int)elfhdr.e_machine),
228             elf_corres_to_string(wordsize_corres,
229             (int)elfhdr.e_ident[EI_CLASS]));
230
231         ret = 0;
232
233         switch (elfhdr.e_machine) {
234         case EM_ARM:
235                 snprintf(dest + strlen(dest), sz - strlen(dest),
236                     ":%s:%s:%s", elf_corres_to_string(endian_corres,
237                     (int)elfhdr.e_ident[EI_DATA]),
238                     (elfhdr.e_flags & EF_ARM_NEW_ABI) > 0 ?
239                     "eabi" : "oabi",
240                     (elfhdr.e_flags & EF_ARM_VFP_FLOAT) > 0 ?
241                     "softfp" : "vfp");
242                 break;
243         case EM_MIPS:
244                 /*
245                  * this is taken from binutils sources:
246                  * include/elf/mips.h
247                  * mapping is figured out from binutils:
248                  * gas/config/tc-mips.c
249                  */
250                 switch (elfhdr.e_flags & EF_MIPS_ABI) {
251                 case E_MIPS_ABI_O32:
252                         abi = "o32";
253                         break;
254                 case E_MIPS_ABI_N32:
255                         abi = "n32";
256                         break;
257                 default:
258                         if (elfhdr.e_ident[EI_DATA] ==
259                             ELFCLASS32)
260                                 abi = "o32";
261                         else if (elfhdr.e_ident[EI_DATA] ==
262                             ELFCLASS64)
263                                 abi = "n64";
264                         break;
265                 }
266                 snprintf(dest + strlen(dest), sz - strlen(dest),
267                     ":%s:%s", elf_corres_to_string(endian_corres,
268                     (int)elfhdr.e_ident[EI_DATA]), abi);
269                 break;
270         }
271
272 cleanup:
273         if (elf != NULL)
274                 elf_end(elf);
275
276         close(fd);
277         return (ret);
278 }
279
280 static void
281 subst_packagesite(const char *abi)
282 {
283         struct sbuf *newval;
284         const char *variable_string;
285         const char *oldval;
286
287         if (c[PACKAGESITE].value != NULL)
288                 oldval = c[PACKAGESITE].value;
289         else
290                 oldval = c[PACKAGESITE].val;
291
292         if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
293                 return;
294
295         newval = sbuf_new_auto();
296         sbuf_bcat(newval, oldval, variable_string - oldval);
297         sbuf_cat(newval, abi);
298         sbuf_cat(newval, variable_string + strlen("${ABI}"));
299         sbuf_finish(newval);
300
301         free(c[PACKAGESITE].value);
302         c[PACKAGESITE].value = strdup(sbuf_data(newval));
303 }
304
305 static int
306 boolstr_to_bool(const char *str)
307 {
308         if (str != NULL && (strcasecmp(str, "true") == 0 ||
309             strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
310             str[0] == '1'))
311                 return (true);
312
313         return (false);
314 }
315
316 static void
317 config_parse(ucl_object_t *obj, pkg_conf_file_t conftype)
318 {
319         struct sbuf *buf = sbuf_new_auto();
320         ucl_object_t *cur, *seq;
321         ucl_object_iter_t it = NULL, itseq = NULL;
322         struct config_entry *temp_config;
323         struct config_value *cv;
324         const char *key;
325         int i;
326         size_t j;
327
328         /* Temporary config for configs that may be disabled. */
329         temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
330
331         while ((cur = ucl_iterate_object(obj, &it, true))) {
332                 key = ucl_object_key(cur);
333                 if (key == NULL)
334                         continue;
335                 sbuf_clear(buf);
336
337                 if (conftype == CONFFILE_PKG) {
338                         for (j = 0; j < strlen(key); ++j)
339                                 sbuf_putc(buf, key[j]);
340                         sbuf_finish(buf);
341                 } else if (conftype == CONFFILE_REPO) {
342                         if (strcasecmp(key, "url") == 0)
343                                 sbuf_cpy(buf, "PACKAGESITE");
344                         else if (strcasecmp(key, "mirror_type") == 0)
345                                 sbuf_cpy(buf, "MIRROR_TYPE");
346                         else if (strcasecmp(key, "signature_type") == 0)
347                                 sbuf_cpy(buf, "SIGNATURE_TYPE");
348                         else if (strcasecmp(key, "fingerprints") == 0)
349                                 sbuf_cpy(buf, "FINGERPRINTS");
350                         else if (strcasecmp(key, "enabled") == 0) {
351                                 if ((cur->type != UCL_BOOLEAN) ||
352                                     !ucl_object_toboolean(cur))
353                                         goto cleanup;
354                         } else
355                                 continue;
356                         sbuf_finish(buf);
357                 }
358
359                 for (i = 0; i < CONFIG_SIZE; i++) {
360                         if (strcmp(sbuf_data(buf), c[i].key) == 0)
361                                 break;
362                 }
363
364                 /* Silently skip unknown keys to be future compatible. */
365                 if (i == CONFIG_SIZE)
366                         continue;
367
368                 /* env has priority over config file */
369                 if (c[i].envset)
370                         continue;
371
372                 /* Parse sequence value ["item1", "item2"] */
373                 switch (c[i].type) {
374                 case PKG_CONFIG_LIST:
375                         if (cur->type != UCL_ARRAY) {
376                                 warnx("Skipping invalid array "
377                                     "value for %s.\n", c[i].key);
378                                 continue;
379                         }
380                         temp_config[i].list =
381                             malloc(sizeof(*temp_config[i].list));
382                         STAILQ_INIT(temp_config[i].list);
383
384                         while ((seq = ucl_iterate_object(cur, &itseq, true))) {
385                                 if (seq->type != UCL_STRING)
386                                         continue;
387                                 cv = malloc(sizeof(struct config_value));
388                                 cv->value =
389                                     strdup(ucl_object_tostring(seq));
390                                 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
391                                     next);
392                         }
393                         break;
394                 case PKG_CONFIG_BOOL:
395                         temp_config[i].value =
396                             strdup(ucl_object_toboolean(cur) ? "yes" : "no");
397                         break;
398                 default:
399                         /* Normal string value. */
400                         temp_config[i].value = strdup(ucl_object_tostring(cur));
401                         break;
402                 }
403         }
404
405         /* Repo is enabled, copy over all settings from temp_config. */
406         for (i = 0; i < CONFIG_SIZE; i++) {
407                 if (c[i].envset)
408                         continue;
409                 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
410                 if (conftype != CONFFILE_PKG && c[i].main_only == true)
411                         continue;
412                 switch (c[i].type) {
413                 case PKG_CONFIG_LIST:
414                         c[i].list = temp_config[i].list;
415                         break;
416                 default:
417                         c[i].value = temp_config[i].value;
418                         break;
419                 }
420         }
421
422 cleanup:
423         free(temp_config);
424         sbuf_delete(buf);
425 }
426
427 /*-
428  * Parse new repo style configs in style:
429  * Name:
430  *   URL:
431  *   MIRROR_TYPE:
432  * etc...
433  */
434 static void
435 parse_repo_file(ucl_object_t *obj)
436 {
437         ucl_object_iter_t it = NULL;
438         ucl_object_t *cur;
439         const char *key;
440
441         while ((cur = ucl_iterate_object(obj, &it, true))) {
442                 key = ucl_object_key(cur);
443
444                 if (key == NULL)
445                         continue;
446
447                 if (cur->type != UCL_OBJECT)
448                         continue;
449
450                 config_parse(cur, CONFFILE_REPO);
451         }
452 }
453
454
455 static int
456 read_conf_file(const char *confpath, pkg_conf_file_t conftype)
457 {
458         struct ucl_parser *p;
459         ucl_object_t *obj = NULL;
460
461         p = ucl_parser_new(0);
462
463         if (!ucl_parser_add_file(p, confpath)) {
464                 if (errno != ENOENT)
465                         errx(EXIT_FAILURE, "Unable to parse configuration "
466                             "file %s: %s", confpath, ucl_parser_get_error(p));
467                 ucl_parser_free(p);
468                 /* no configuration present */
469                 return (1);
470         }
471
472         obj = ucl_parser_get_object(p);
473         if (obj->type != UCL_OBJECT) 
474                 warnx("Invalid configuration format, ignoring the "
475                     "configuration file %s", confpath);
476         else {
477                 if (conftype == CONFFILE_PKG)
478                         config_parse(obj, conftype);
479                 else if (conftype == CONFFILE_REPO)
480                         parse_repo_file(obj);
481         }
482
483         ucl_object_free(obj);
484         ucl_parser_free(p);
485
486         return (0);
487 }
488
489 static int
490 load_repositories(const char *repodir)
491 {
492         struct dirent *ent;
493         DIR *d;
494         char *p;
495         size_t n;
496         char path[MAXPATHLEN];
497         int ret;
498
499         ret = 0;
500
501         if ((d = opendir(repodir)) == NULL)
502                 return (1);
503
504         while ((ent = readdir(d))) {
505                 /* Trim out 'repos'. */
506                 if ((n = strlen(ent->d_name)) <= 5)
507                         continue;
508                 p = &ent->d_name[n - 5];
509                 if (strcmp(p, ".conf") == 0) {
510                         snprintf(path, sizeof(path), "%s%s%s",
511                             repodir,
512                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
513                             ent->d_name);
514                         if (access(path, F_OK) == 0 &&
515                             read_conf_file(path, CONFFILE_REPO)) {
516                                 ret = 1;
517                                 goto cleanup;
518                         }
519                 }
520         }
521
522 cleanup:
523         closedir(d);
524
525         return (ret);
526 }
527
528 int
529 config_init(void)
530 {
531         char *val;
532         int i;
533         const char *localbase;
534         char *env_list_item;
535         char confpath[MAXPATHLEN];
536         struct config_value *cv;
537         char abi[BUFSIZ];
538
539         for (i = 0; i < CONFIG_SIZE; i++) {
540                 val = getenv(c[i].key);
541                 if (val != NULL) {
542                         c[i].envset = true;
543                         switch (c[i].type) {
544                         case PKG_CONFIG_LIST:
545                                 /* Split up comma-separated items from env. */
546                                 c[i].list = malloc(sizeof(*c[i].list));
547                                 STAILQ_INIT(c[i].list);
548                                 for (env_list_item = strtok(val, ",");
549                                     env_list_item != NULL;
550                                     env_list_item = strtok(NULL, ",")) {
551                                         cv =
552                                             malloc(sizeof(struct config_value));
553                                         cv->value =
554                                             strdup(env_list_item);
555                                         STAILQ_INSERT_TAIL(c[i].list, cv,
556                                             next);
557                                 }
558                                 break;
559                         default:
560                                 c[i].val = val;
561                                 break;
562                         }
563                 }
564         }
565
566         /* Read LOCALBASE/etc/pkg.conf first. */
567         localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
568         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
569             localbase);
570
571         if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
572             CONFFILE_PKG))
573                 goto finalize;
574
575         /* Then read in all repos from REPOS_DIR list of directories. */
576         if (c[REPOS_DIR].list == NULL) {
577                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
578                 STAILQ_INIT(c[REPOS_DIR].list);
579                 cv = malloc(sizeof(struct config_value));
580                 cv->value = strdup("/etc/pkg");
581                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
582                 cv = malloc(sizeof(struct config_value));
583                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
584                         goto finalize;
585                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
586         }
587
588         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
589                 if (load_repositories(cv->value))
590                         goto finalize;
591
592 finalize:
593         if (c[ABI].val == NULL && c[ABI].value == NULL) {
594                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
595                         errx(EXIT_FAILURE, "Failed to determine the system "
596                             "ABI");
597                 c[ABI].val = abi;
598         }
599
600         subst_packagesite(c[ABI].val);
601
602         return (0);
603 }
604
605 int
606 config_string(pkg_config_key k, const char **val)
607 {
608         if (c[k].type != PKG_CONFIG_STRING)
609                 return (-1);
610
611         if (c[k].value != NULL)
612                 *val = c[k].value;
613         else
614                 *val = c[k].val;
615
616         return (0);
617 }
618
619 int
620 config_bool(pkg_config_key k, bool *val)
621 {
622         const char *value;
623
624         if (c[k].type != PKG_CONFIG_BOOL)
625                 return (-1);
626
627         *val = false;
628
629         if (c[k].value != NULL)
630                 value = c[k].value;
631         else
632                 value = c[k].val;
633
634         if (boolstr_to_bool(value))
635                 *val = true;
636
637         return (0);
638 }
639
640 void
641 config_finish(void) {
642         int i;
643
644         for (i = 0; i < CONFIG_SIZE; i++)
645                 free(c[i].value);
646 }