]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pkg/config.c
cdn-patch: use key from efi if it exists
[FreeBSD/FreeBSD.git] / usr.sbin / pkg / config.c
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2014 Baptiste Daroussin <bapt@FreeBSD.org>
5  * Copyright (c) 2013 Bryan Drewery <bdrewery@FreeBSD.org>
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
21  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27  * SUCH DAMAGE.
28  */
29
30 #include <sys/cdefs.h>
31 __FBSDID("$FreeBSD$");
32
33 #include <sys/param.h>
34 #include <sys/queue.h>
35 #include <sys/utsname.h>
36 #include <sys/sysctl.h>
37
38 #include <dirent.h>
39 #include <ucl.h>
40 #include <err.h>
41 #include <errno.h>
42 #include <stdbool.h>
43 #include <unistd.h>
44 #include <ctype.h>
45
46 #include "config.h"
47
48 struct config_value {
49         char *value;
50         STAILQ_ENTRY(config_value) next;
51 };
52
53 struct config_entry {
54         uint8_t type;
55         const char *key;
56         const char *val;
57         char *value;
58         STAILQ_HEAD(, config_value) *list;
59         bool envset;
60         bool main_only;                         /* Only set in pkg.conf. */
61 };
62
63 static struct config_entry c[] = {
64         [PACKAGESITE] = {
65                 PKG_CONFIG_STRING,
66                 "PACKAGESITE",
67                 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
68                 NULL,
69                 NULL,
70                 false,
71                 false,
72         },
73         [ABI] = {
74                 PKG_CONFIG_STRING,
75                 "ABI",
76                 NULL,
77                 NULL,
78                 NULL,
79                 false,
80                 true,
81         },
82         [MIRROR_TYPE] = {
83                 PKG_CONFIG_STRING,
84                 "MIRROR_TYPE",
85                 "SRV",
86                 NULL,
87                 NULL,
88                 false,
89                 false,
90         },
91         [ASSUME_ALWAYS_YES] = {
92                 PKG_CONFIG_BOOL,
93                 "ASSUME_ALWAYS_YES",
94                 "NO",
95                 NULL,
96                 NULL,
97                 false,
98                 true,
99         },
100         [SIGNATURE_TYPE] = {
101                 PKG_CONFIG_STRING,
102                 "SIGNATURE_TYPE",
103                 NULL,
104                 NULL,
105                 NULL,
106                 false,
107                 false,
108         },
109         [FINGERPRINTS] = {
110                 PKG_CONFIG_STRING,
111                 "FINGERPRINTS",
112                 NULL,
113                 NULL,
114                 NULL,
115                 false,
116                 false,
117         },
118         [REPOS_DIR] = {
119                 PKG_CONFIG_LIST,
120                 "REPOS_DIR",
121                 NULL,
122                 NULL,
123                 NULL,
124                 false,
125                 true,
126         },
127         [PUBKEY] = {
128                 PKG_CONFIG_STRING,
129                 "PUBKEY",
130                 NULL,
131                 NULL,
132                 NULL,
133                 false,
134                 false
135         },
136         [PKG_ENV] = {
137                 PKG_CONFIG_OBJECT,
138                 "PKG_ENV",
139                 NULL,
140                 NULL,
141                 NULL,
142                 false,
143                 false,
144         }
145 };
146
147 static int
148 pkg_get_myabi(char *dest, size_t sz)
149 {
150         struct utsname uts;
151         char machine_arch[255];
152         size_t len;
153         int error;
154
155         error = uname(&uts);
156         if (error)
157                 return (errno);
158
159         len = sizeof(machine_arch);
160         error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0);
161         if (error)
162                 return (errno);
163         machine_arch[len] = '\0';
164
165         /*
166          * Use __FreeBSD_version rather than kernel version (uts.release) for
167          * use in jails. This is equivalent to the value of uname -U.
168          */
169         snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000,
170             machine_arch);
171
172         return (error);
173 }
174
175 static void
176 subst_packagesite(const char *abi)
177 {
178         char *newval;
179         const char *variable_string;
180         const char *oldval;
181
182         if (c[PACKAGESITE].value != NULL)
183                 oldval = c[PACKAGESITE].value;
184         else
185                 oldval = c[PACKAGESITE].val;
186
187         if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
188                 return;
189
190         asprintf(&newval, "%.*s%s%s",
191             (int)(variable_string - oldval), oldval, abi,
192             variable_string + strlen("${ABI}"));
193         if (newval == NULL)
194                 errx(EXIT_FAILURE, "asprintf");
195
196         free(c[PACKAGESITE].value);
197         c[PACKAGESITE].value = newval;
198 }
199
200 static int
201 boolstr_to_bool(const char *str)
202 {
203         if (str != NULL && (strcasecmp(str, "true") == 0 ||
204             strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
205             str[0] == '1'))
206                 return (true);
207
208         return (false);
209 }
210
211 static void
212 config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype)
213 {
214         FILE *buffp;
215         char *buf = NULL;
216         size_t bufsz = 0;
217         const ucl_object_t *cur, *seq, *tmp;
218         ucl_object_iter_t it = NULL, itseq = NULL, it_obj = NULL;
219         struct config_entry *temp_config;
220         struct config_value *cv;
221         const char *key, *evkey;
222         int i;
223         size_t j;
224
225         /* Temporary config for configs that may be disabled. */
226         temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
227         buffp = open_memstream(&buf, &bufsz);
228         if (buffp == NULL)
229                 err(EXIT_FAILURE, "open_memstream()");
230
231         while ((cur = ucl_iterate_object(obj, &it, true))) {
232                 key = ucl_object_key(cur);
233                 if (key == NULL)
234                         continue;
235                 if (buf != NULL)
236                         memset(buf, 0, bufsz);
237                 rewind(buffp);
238
239                 if (conftype == CONFFILE_PKG) {
240                         for (j = 0; j < strlen(key); ++j)
241                                 fputc(toupper(key[j]), buffp);
242                         fflush(buffp);
243                 } else if (conftype == CONFFILE_REPO) {
244                         if (strcasecmp(key, "url") == 0)
245                                 fputs("PACKAGESITE", buffp);
246                         else if (strcasecmp(key, "mirror_type") == 0)
247                                 fputs("MIRROR_TYPE", buffp);
248                         else if (strcasecmp(key, "signature_type") == 0)
249                                 fputs("SIGNATURE_TYPE", buffp);
250                         else if (strcasecmp(key, "fingerprints") == 0)
251                                 fputs("FINGERPRINTS", buffp);
252                         else if (strcasecmp(key, "pubkey") == 0)
253                                 fputs("PUBKEY", buffp);
254                         else if (strcasecmp(key, "enabled") == 0) {
255                                 if ((cur->type != UCL_BOOLEAN) ||
256                                     !ucl_object_toboolean(cur))
257                                         goto cleanup;
258                         } else
259                                 continue;
260                         fflush(buffp);
261                 }
262
263                 for (i = 0; i < CONFIG_SIZE; i++) {
264                         if (strcmp(buf, c[i].key) == 0)
265                                 break;
266                 }
267
268                 /* Silently skip unknown keys to be future compatible. */
269                 if (i == CONFIG_SIZE)
270                         continue;
271
272                 /* env has priority over config file */
273                 if (c[i].envset)
274                         continue;
275
276                 /* Parse sequence value ["item1", "item2"] */
277                 switch (c[i].type) {
278                 case PKG_CONFIG_LIST:
279                         if (cur->type != UCL_ARRAY) {
280                                 warnx("Skipping invalid array "
281                                     "value for %s.\n", c[i].key);
282                                 continue;
283                         }
284                         temp_config[i].list =
285                             malloc(sizeof(*temp_config[i].list));
286                         STAILQ_INIT(temp_config[i].list);
287
288                         while ((seq = ucl_iterate_object(cur, &itseq, true))) {
289                                 if (seq->type != UCL_STRING)
290                                         continue;
291                                 cv = malloc(sizeof(struct config_value));
292                                 cv->value =
293                                     strdup(ucl_object_tostring(seq));
294                                 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
295                                     next);
296                         }
297                         break;
298                 case PKG_CONFIG_BOOL:
299                         temp_config[i].value =
300                             strdup(ucl_object_toboolean(cur) ? "yes" : "no");
301                         break;
302                 case PKG_CONFIG_OBJECT:
303                         if (strcmp(c[i].key, "PKG_ENV") == 0) {
304                                 while ((tmp =
305                                     ucl_iterate_object(cur, &it_obj, true))) {
306                                         evkey = ucl_object_key(tmp);
307                                         if (evkey != NULL && *evkey != '\0') {
308                                                 setenv(evkey, ucl_object_tostring_forced(tmp), 1);
309                                         }
310                                 }
311                         }
312                         break;
313                 default:
314                         /* Normal string value. */
315                         temp_config[i].value = strdup(ucl_object_tostring(cur));
316                         break;
317                 }
318         }
319
320         /* Repo is enabled, copy over all settings from temp_config. */
321         for (i = 0; i < CONFIG_SIZE; i++) {
322                 if (c[i].envset)
323                         continue;
324                 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
325                 if (conftype != CONFFILE_PKG && c[i].main_only == true)
326                         continue;
327                 switch (c[i].type) {
328                 case PKG_CONFIG_LIST:
329                         c[i].list = temp_config[i].list;
330                         break;
331                 default:
332                         c[i].value = temp_config[i].value;
333                         break;
334                 }
335         }
336
337 cleanup:
338         free(temp_config);
339         fclose(buffp);
340         free(buf);
341 }
342
343 /*-
344  * Parse new repo style configs in style:
345  * Name:
346  *   URL:
347  *   MIRROR_TYPE:
348  * etc...
349  */
350 static void
351 parse_repo_file(ucl_object_t *obj, const char *requested_repo)
352 {
353         ucl_object_iter_t it = NULL;
354         const ucl_object_t *cur;
355         const char *key;
356
357         while ((cur = ucl_iterate_object(obj, &it, true))) {
358                 key = ucl_object_key(cur);
359
360                 if (key == NULL)
361                         continue;
362
363                 if (cur->type != UCL_OBJECT)
364                         continue;
365
366                 if (requested_repo != NULL && strcmp(requested_repo, key) != 0)
367                         continue;
368
369                 config_parse(cur, CONFFILE_REPO);
370         }
371 }
372
373
374 static int
375 read_conf_file(const char *confpath, const char *requested_repo,
376     pkg_conf_file_t conftype)
377 {
378         struct ucl_parser *p;
379         ucl_object_t *obj = NULL;
380
381         p = ucl_parser_new(0);
382
383         if (!ucl_parser_add_file(p, confpath)) {
384                 if (errno != ENOENT)
385                         errx(EXIT_FAILURE, "Unable to parse configuration "
386                             "file %s: %s", confpath, ucl_parser_get_error(p));
387                 ucl_parser_free(p);
388                 /* no configuration present */
389                 return (1);
390         }
391
392         obj = ucl_parser_get_object(p);
393         if (obj->type != UCL_OBJECT) 
394                 warnx("Invalid configuration format, ignoring the "
395                     "configuration file %s", confpath);
396         else {
397                 if (conftype == CONFFILE_PKG)
398                         config_parse(obj, conftype);
399                 else if (conftype == CONFFILE_REPO)
400                         parse_repo_file(obj, requested_repo);
401         }
402
403         ucl_object_unref(obj);
404         ucl_parser_free(p);
405
406         return (0);
407 }
408
409 static int
410 load_repositories(const char *repodir, const char *requested_repo)
411 {
412         struct dirent *ent;
413         DIR *d;
414         char *p;
415         size_t n;
416         char path[MAXPATHLEN];
417         int ret;
418
419         ret = 0;
420
421         if ((d = opendir(repodir)) == NULL)
422                 return (1);
423
424         while ((ent = readdir(d))) {
425                 /* Trim out 'repos'. */
426                 if ((n = strlen(ent->d_name)) <= 5)
427                         continue;
428                 p = &ent->d_name[n - 5];
429                 if (strcmp(p, ".conf") == 0) {
430                         snprintf(path, sizeof(path), "%s%s%s",
431                             repodir,
432                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
433                             ent->d_name);
434                         if (access(path, F_OK) != 0)
435                                 continue;
436                         if (read_conf_file(path, requested_repo,
437                             CONFFILE_REPO)) {
438                                 ret = 1;
439                                 goto cleanup;
440                         }
441                 }
442         }
443
444 cleanup:
445         closedir(d);
446
447         return (ret);
448 }
449
450 int
451 config_init(const char *requested_repo)
452 {
453         char *val;
454         int i;
455         const char *localbase;
456         char *env_list_item;
457         char confpath[MAXPATHLEN];
458         struct config_value *cv;
459         char abi[BUFSIZ];
460
461         for (i = 0; i < CONFIG_SIZE; i++) {
462                 val = getenv(c[i].key);
463                 if (val != NULL) {
464                         c[i].envset = true;
465                         switch (c[i].type) {
466                         case PKG_CONFIG_LIST:
467                                 /* Split up comma-separated items from env. */
468                                 c[i].list = malloc(sizeof(*c[i].list));
469                                 STAILQ_INIT(c[i].list);
470                                 for (env_list_item = strtok(val, ",");
471                                     env_list_item != NULL;
472                                     env_list_item = strtok(NULL, ",")) {
473                                         cv =
474                                             malloc(sizeof(struct config_value));
475                                         cv->value =
476                                             strdup(env_list_item);
477                                         STAILQ_INSERT_TAIL(c[i].list, cv,
478                                             next);
479                                 }
480                                 break;
481                         default:
482                                 c[i].val = val;
483                                 break;
484                         }
485                 }
486         }
487
488         /* Read LOCALBASE/etc/pkg.conf first. */
489         localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
490         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
491             localbase);
492
493         if (access(confpath, F_OK) == 0 && read_conf_file(confpath, NULL,
494             CONFFILE_PKG))
495                 goto finalize;
496
497         /* Then read in all repos from REPOS_DIR list of directories. */
498         if (c[REPOS_DIR].list == NULL) {
499                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
500                 STAILQ_INIT(c[REPOS_DIR].list);
501                 cv = malloc(sizeof(struct config_value));
502                 cv->value = strdup("/etc/pkg");
503                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
504                 cv = malloc(sizeof(struct config_value));
505                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
506                         goto finalize;
507                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
508         }
509
510         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
511                 if (load_repositories(cv->value, requested_repo))
512                         goto finalize;
513
514 finalize:
515         if (c[ABI].val == NULL && c[ABI].value == NULL) {
516                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
517                         errx(EXIT_FAILURE, "Failed to determine the system "
518                             "ABI");
519                 c[ABI].val = abi;
520         }
521
522         subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
523
524         return (0);
525 }
526
527 int
528 config_string(pkg_config_key k, const char **val)
529 {
530         if (c[k].type != PKG_CONFIG_STRING)
531                 return (-1);
532
533         if (c[k].value != NULL)
534                 *val = c[k].value;
535         else
536                 *val = c[k].val;
537
538         return (0);
539 }
540
541 int
542 config_bool(pkg_config_key k, bool *val)
543 {
544         const char *value;
545
546         if (c[k].type != PKG_CONFIG_BOOL)
547                 return (-1);
548
549         *val = false;
550
551         if (c[k].value != NULL)
552                 value = c[k].value;
553         else
554                 value = c[k].val;
555
556         if (boolstr_to_bool(value))
557                 *val = true;
558
559         return (0);
560 }
561
562 void
563 config_finish(void) {
564         int i;
565
566         for (i = 0; i < CONFIG_SIZE; i++)
567                 free(c[i].value);
568 }