]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pkg/config.c
zfs: merge OpenZFS master-891568c99
[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/sbuf.h>
37 #include <sys/sysctl.h>
38
39 #include <dirent.h>
40 #include <ucl.h>
41 #include <err.h>
42 #include <errno.h>
43 #include <libutil.h>
44 #include <paths.h>
45 #include <stdbool.h>
46 #include <unistd.h>
47
48 #include "config.h"
49
50 struct config_value {
51        char *value;
52        STAILQ_ENTRY(config_value) next;
53 };
54
55 struct config_entry {
56         uint8_t type;
57         const char *key;
58         const char *val;
59         char *value;
60         STAILQ_HEAD(, config_value) *list;
61         bool envset;
62         bool main_only;                         /* Only set in pkg.conf. */
63 };
64
65 static struct config_entry c[] = {
66         [PACKAGESITE] = {
67                 PKG_CONFIG_STRING,
68                 "PACKAGESITE",
69                 URL_SCHEME_PREFIX "http://pkg.FreeBSD.org/${ABI}/latest",
70                 NULL,
71                 NULL,
72                 false,
73                 false,
74         },
75         [ABI] = {
76                 PKG_CONFIG_STRING,
77                 "ABI",
78                 NULL,
79                 NULL,
80                 NULL,
81                 false,
82                 true,
83         },
84         [MIRROR_TYPE] = {
85                 PKG_CONFIG_STRING,
86                 "MIRROR_TYPE",
87                 "SRV",
88                 NULL,
89                 NULL,
90                 false,
91                 false,
92         },
93         [ASSUME_ALWAYS_YES] = {
94                 PKG_CONFIG_BOOL,
95                 "ASSUME_ALWAYS_YES",
96                 "NO",
97                 NULL,
98                 NULL,
99                 false,
100                 true,
101         },
102         [SIGNATURE_TYPE] = {
103                 PKG_CONFIG_STRING,
104                 "SIGNATURE_TYPE",
105                 NULL,
106                 NULL,
107                 NULL,
108                 false,
109                 false,
110         },
111         [FINGERPRINTS] = {
112                 PKG_CONFIG_STRING,
113                 "FINGERPRINTS",
114                 NULL,
115                 NULL,
116                 NULL,
117                 false,
118                 false,
119         },
120         [REPOS_DIR] = {
121                 PKG_CONFIG_LIST,
122                 "REPOS_DIR",
123                 NULL,
124                 NULL,
125                 NULL,
126                 false,
127                 true,
128         },
129         [PUBKEY] = {
130                 PKG_CONFIG_STRING,
131                 "PUBKEY",
132                 NULL,
133                 NULL,
134                 NULL,
135                 false,
136                 false
137         }
138 };
139
140 static int
141 pkg_get_myabi(char *dest, size_t sz)
142 {
143         struct utsname uts;
144         char machine_arch[255];
145         size_t len;
146         int error;
147
148         error = uname(&uts);
149         if (error)
150                 return (errno);
151
152         len = sizeof(machine_arch);
153         error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0);
154         if (error)
155                 return (errno);
156         machine_arch[len] = '\0';
157
158         /*
159          * Use __FreeBSD_version rather than kernel version (uts.release) for
160          * use in jails. This is equivalent to the value of uname -U.
161          */
162         snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000,
163             machine_arch);
164
165         return (error);
166 }
167
168 static void
169 subst_packagesite(const char *abi)
170 {
171         char *newval;
172         const char *variable_string;
173         const char *oldval;
174
175         if (c[PACKAGESITE].value != NULL)
176                 oldval = c[PACKAGESITE].value;
177         else
178                 oldval = c[PACKAGESITE].val;
179
180         if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
181                 return;
182
183         asprintf(&newval, "%.*s%s%s",
184             (int)(variable_string - oldval), oldval, abi,
185             variable_string + strlen("${ABI}"));
186         if (newval == NULL)
187                 errx(EXIT_FAILURE, "asprintf");
188
189         free(c[PACKAGESITE].value);
190         c[PACKAGESITE].value = newval;
191 }
192
193 static int
194 boolstr_to_bool(const char *str)
195 {
196         if (str != NULL && (strcasecmp(str, "true") == 0 ||
197             strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
198             str[0] == '1'))
199                 return (true);
200
201         return (false);
202 }
203
204 static void
205 config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype)
206 {
207         struct sbuf *buf = sbuf_new_auto();
208         const ucl_object_t *cur, *seq;
209         ucl_object_iter_t it = NULL, itseq = NULL;
210         struct config_entry *temp_config;
211         struct config_value *cv;
212         const char *key;
213         int i;
214         size_t j;
215
216         /* Temporary config for configs that may be disabled. */
217         temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
218
219         while ((cur = ucl_iterate_object(obj, &it, true))) {
220                 key = ucl_object_key(cur);
221                 if (key == NULL)
222                         continue;
223                 sbuf_clear(buf);
224
225                 if (conftype == CONFFILE_PKG) {
226                         for (j = 0; j < strlen(key); ++j)
227                                 sbuf_putc(buf, key[j]);
228                         sbuf_finish(buf);
229                 } else if (conftype == CONFFILE_REPO) {
230                         if (strcasecmp(key, "url") == 0)
231                                 sbuf_cpy(buf, "PACKAGESITE");
232                         else if (strcasecmp(key, "mirror_type") == 0)
233                                 sbuf_cpy(buf, "MIRROR_TYPE");
234                         else if (strcasecmp(key, "signature_type") == 0)
235                                 sbuf_cpy(buf, "SIGNATURE_TYPE");
236                         else if (strcasecmp(key, "fingerprints") == 0)
237                                 sbuf_cpy(buf, "FINGERPRINTS");
238                         else if (strcasecmp(key, "pubkey") == 0)
239                                 sbuf_cpy(buf, "PUBKEY");
240                         else if (strcasecmp(key, "enabled") == 0) {
241                                 if ((cur->type != UCL_BOOLEAN) ||
242                                     !ucl_object_toboolean(cur))
243                                         goto cleanup;
244                         } else
245                                 continue;
246                         sbuf_finish(buf);
247                 }
248
249                 for (i = 0; i < CONFIG_SIZE; i++) {
250                         if (strcmp(sbuf_data(buf), c[i].key) == 0)
251                                 break;
252                 }
253
254                 /* Silently skip unknown keys to be future compatible. */
255                 if (i == CONFIG_SIZE)
256                         continue;
257
258                 /* env has priority over config file */
259                 if (c[i].envset)
260                         continue;
261
262                 /* Parse sequence value ["item1", "item2"] */
263                 switch (c[i].type) {
264                 case PKG_CONFIG_LIST:
265                         if (cur->type != UCL_ARRAY) {
266                                 warnx("Skipping invalid array "
267                                     "value for %s.\n", c[i].key);
268                                 continue;
269                         }
270                         temp_config[i].list =
271                             malloc(sizeof(*temp_config[i].list));
272                         STAILQ_INIT(temp_config[i].list);
273
274                         while ((seq = ucl_iterate_object(cur, &itseq, true))) {
275                                 if (seq->type != UCL_STRING)
276                                         continue;
277                                 cv = malloc(sizeof(struct config_value));
278                                 cv->value =
279                                     strdup(ucl_object_tostring(seq));
280                                 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
281                                     next);
282                         }
283                         break;
284                 case PKG_CONFIG_BOOL:
285                         temp_config[i].value =
286                             strdup(ucl_object_toboolean(cur) ? "yes" : "no");
287                         break;
288                 default:
289                         /* Normal string value. */
290                         temp_config[i].value = strdup(ucl_object_tostring(cur));
291                         break;
292                 }
293         }
294
295         /* Repo is enabled, copy over all settings from temp_config. */
296         for (i = 0; i < CONFIG_SIZE; i++) {
297                 if (c[i].envset)
298                         continue;
299                 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
300                 if (conftype != CONFFILE_PKG && c[i].main_only == true)
301                         continue;
302                 switch (c[i].type) {
303                 case PKG_CONFIG_LIST:
304                         c[i].list = temp_config[i].list;
305                         break;
306                 default:
307                         c[i].value = temp_config[i].value;
308                         break;
309                 }
310         }
311
312 cleanup:
313         free(temp_config);
314         sbuf_delete(buf);
315 }
316
317 /*-
318  * Parse new repo style configs in style:
319  * Name:
320  *   URL:
321  *   MIRROR_TYPE:
322  * etc...
323  */
324 static void
325 parse_repo_file(ucl_object_t *obj, const char *requested_repo)
326 {
327         ucl_object_iter_t it = NULL;
328         const ucl_object_t *cur;
329         const char *key;
330
331         while ((cur = ucl_iterate_object(obj, &it, true))) {
332                 key = ucl_object_key(cur);
333
334                 if (key == NULL)
335                         continue;
336
337                 if (cur->type != UCL_OBJECT)
338                         continue;
339
340                 if (requested_repo != NULL && strcmp(requested_repo, key) != 0)
341                         continue;
342
343                 config_parse(cur, CONFFILE_REPO);
344         }
345 }
346
347
348 static int
349 read_conf_file(const char *confpath, const char *requested_repo,
350     pkg_conf_file_t conftype)
351 {
352         struct ucl_parser *p;
353         ucl_object_t *obj = NULL;
354
355         p = ucl_parser_new(0);
356
357         if (!ucl_parser_add_file(p, confpath)) {
358                 if (errno != ENOENT)
359                         errx(EXIT_FAILURE, "Unable to parse configuration "
360                             "file %s: %s", confpath, ucl_parser_get_error(p));
361                 ucl_parser_free(p);
362                 /* no configuration present */
363                 return (1);
364         }
365
366         obj = ucl_parser_get_object(p);
367         if (obj->type != UCL_OBJECT) 
368                 warnx("Invalid configuration format, ignoring the "
369                     "configuration file %s", confpath);
370         else {
371                 if (conftype == CONFFILE_PKG)
372                         config_parse(obj, conftype);
373                 else if (conftype == CONFFILE_REPO)
374                         parse_repo_file(obj, requested_repo);
375         }
376
377         ucl_object_unref(obj);
378         ucl_parser_free(p);
379
380         return (0);
381 }
382
383 static int
384 load_repositories(const char *repodir, const char *requested_repo)
385 {
386         struct dirent *ent;
387         DIR *d;
388         char *p;
389         size_t n;
390         char path[MAXPATHLEN];
391         int ret;
392
393         ret = 0;
394
395         if ((d = opendir(repodir)) == NULL)
396                 return (1);
397
398         while ((ent = readdir(d))) {
399                 /* Trim out 'repos'. */
400                 if ((n = strlen(ent->d_name)) <= 5)
401                         continue;
402                 p = &ent->d_name[n - 5];
403                 if (strcmp(p, ".conf") == 0) {
404                         snprintf(path, sizeof(path), "%s%s%s",
405                             repodir,
406                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
407                             ent->d_name);
408                         if (access(path, F_OK) != 0)
409                                 continue;
410                         if (read_conf_file(path, requested_repo,
411                             CONFFILE_REPO)) {
412                                 ret = 1;
413                                 goto cleanup;
414                         }
415                 }
416         }
417
418 cleanup:
419         closedir(d);
420
421         return (ret);
422 }
423
424 int
425 config_init(const char *requested_repo)
426 {
427         char *val;
428         int i;
429         const char *localbase;
430         char *env_list_item;
431         char confpath[MAXPATHLEN];
432         struct config_value *cv;
433         char abi[BUFSIZ];
434
435         for (i = 0; i < CONFIG_SIZE; i++) {
436                 val = getenv(c[i].key);
437                 if (val != NULL) {
438                         c[i].envset = true;
439                         switch (c[i].type) {
440                         case PKG_CONFIG_LIST:
441                                 /* Split up comma-separated items from env. */
442                                 c[i].list = malloc(sizeof(*c[i].list));
443                                 STAILQ_INIT(c[i].list);
444                                 for (env_list_item = strtok(val, ",");
445                                     env_list_item != NULL;
446                                     env_list_item = strtok(NULL, ",")) {
447                                         cv =
448                                             malloc(sizeof(struct config_value));
449                                         cv->value =
450                                             strdup(env_list_item);
451                                         STAILQ_INSERT_TAIL(c[i].list, cv,
452                                             next);
453                                 }
454                                 break;
455                         default:
456                                 c[i].val = val;
457                                 break;
458                         }
459                 }
460         }
461
462         /* Read LOCALBASE/etc/pkg.conf first. */
463         localbase = getlocalbase();
464         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
465
466         if (access(confpath, F_OK) == 0 && read_conf_file(confpath, NULL,
467             CONFFILE_PKG))
468                 goto finalize;
469
470         /* Then read in all repos from REPOS_DIR list of directories. */
471         if (c[REPOS_DIR].list == NULL) {
472                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
473                 STAILQ_INIT(c[REPOS_DIR].list);
474                 cv = malloc(sizeof(struct config_value));
475                 cv->value = strdup("/etc/pkg");
476                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
477                 cv = malloc(sizeof(struct config_value));
478                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
479                         goto finalize;
480                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
481         }
482
483         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
484                 if (load_repositories(cv->value, requested_repo))
485                         goto finalize;
486
487 finalize:
488         if (c[ABI].val == NULL && c[ABI].value == NULL) {
489                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
490                         errx(EXIT_FAILURE, "Failed to determine the system "
491                             "ABI");
492                 c[ABI].val = abi;
493         }
494
495         subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
496
497         return (0);
498 }
499
500 int
501 config_string(pkg_config_key k, const char **val)
502 {
503         if (c[k].type != PKG_CONFIG_STRING)
504                 return (-1);
505
506         if (c[k].value != NULL)
507                 *val = c[k].value;
508         else
509                 *val = c[k].val;
510
511         return (0);
512 }
513
514 int
515 config_bool(pkg_config_key k, bool *val)
516 {
517         const char *value;
518
519         if (c[k].type != PKG_CONFIG_BOOL)
520                 return (-1);
521
522         *val = false;
523
524         if (c[k].value != NULL)
525                 value = c[k].value;
526         else
527                 value = c[k].val;
528
529         if (boolstr_to_bool(value))
530                 *val = true;
531
532         return (0);
533 }
534
535 void
536 config_finish(void) {
537         int i;
538
539         for (i = 0; i < CONFIG_SIZE; i++)
540                 free(c[i].value);
541 }