]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pkg/config.c
Import Arm Optimized Routines v21.02
[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 <libutil.h>
43 #include <paths.h>
44 #include <stdbool.h>
45 #include <unistd.h>
46 #include <ctype.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         [PKG_ENV] = {
139                 PKG_CONFIG_OBJECT,
140                 "PKG_ENV",
141                 NULL,
142                 NULL,
143                 NULL,
144                 false,
145                 false,
146         }
147 };
148
149 static int
150 pkg_get_myabi(char *dest, size_t sz)
151 {
152         struct utsname uts;
153         char machine_arch[255];
154         size_t len;
155         int error;
156
157         error = uname(&uts);
158         if (error)
159                 return (errno);
160
161         len = sizeof(machine_arch);
162         error = sysctlbyname("hw.machine_arch", machine_arch, &len, NULL, 0);
163         if (error)
164                 return (errno);
165         machine_arch[len] = '\0';
166
167         /*
168          * Use __FreeBSD_version rather than kernel version (uts.release) for
169          * use in jails. This is equivalent to the value of uname -U.
170          */
171         snprintf(dest, sz, "%s:%d:%s", uts.sysname, __FreeBSD_version/100000,
172             machine_arch);
173
174         return (error);
175 }
176
177 static void
178 subst_packagesite(const char *abi)
179 {
180         char *newval;
181         const char *variable_string;
182         const char *oldval;
183
184         if (c[PACKAGESITE].value != NULL)
185                 oldval = c[PACKAGESITE].value;
186         else
187                 oldval = c[PACKAGESITE].val;
188
189         if ((variable_string = strstr(oldval, "${ABI}")) == NULL)
190                 return;
191
192         asprintf(&newval, "%.*s%s%s",
193             (int)(variable_string - oldval), oldval, abi,
194             variable_string + strlen("${ABI}"));
195         if (newval == NULL)
196                 errx(EXIT_FAILURE, "asprintf");
197
198         free(c[PACKAGESITE].value);
199         c[PACKAGESITE].value = newval;
200 }
201
202 static int
203 boolstr_to_bool(const char *str)
204 {
205         if (str != NULL && (strcasecmp(str, "true") == 0 ||
206             strcasecmp(str, "yes") == 0 || strcasecmp(str, "on") == 0 ||
207             str[0] == '1'))
208                 return (true);
209
210         return (false);
211 }
212
213 static void
214 config_parse(const ucl_object_t *obj, pkg_conf_file_t conftype)
215 {
216         FILE *buffp;
217         char *buf = NULL;
218         size_t bufsz = 0;
219         const ucl_object_t *cur, *seq, *tmp;
220         ucl_object_iter_t it = NULL, itseq = NULL, it_obj = NULL;
221         struct config_entry *temp_config;
222         struct config_value *cv;
223         const char *key, *evkey;
224         int i;
225         size_t j;
226
227         /* Temporary config for configs that may be disabled. */
228         temp_config = calloc(CONFIG_SIZE, sizeof(struct config_entry));
229         buffp = open_memstream(&buf, &bufsz);
230         if (buffp == NULL)
231                 err(EXIT_FAILURE, "open_memstream()");
232
233         while ((cur = ucl_iterate_object(obj, &it, true))) {
234                 key = ucl_object_key(cur);
235                 if (key == NULL)
236                         continue;
237                 if (buf != NULL)
238                         memset(buf, 0, bufsz);
239                 rewind(buffp);
240
241                 if (conftype == CONFFILE_PKG) {
242                         for (j = 0; j < strlen(key); ++j)
243                                 fputc(toupper(key[j]), buffp);
244                         fflush(buffp);
245                 } else if (conftype == CONFFILE_REPO) {
246                         if (strcasecmp(key, "url") == 0)
247                                 fputs("PACKAGESITE", buffp);
248                         else if (strcasecmp(key, "mirror_type") == 0)
249                                 fputs("MIRROR_TYPE", buffp);
250                         else if (strcasecmp(key, "signature_type") == 0)
251                                 fputs("SIGNATURE_TYPE", buffp);
252                         else if (strcasecmp(key, "fingerprints") == 0)
253                                 fputs("FINGERPRINTS", buffp);
254                         else if (strcasecmp(key, "pubkey") == 0)
255                                 fputs("PUBKEY", buffp);
256                         else if (strcasecmp(key, "enabled") == 0) {
257                                 if ((cur->type != UCL_BOOLEAN) ||
258                                     !ucl_object_toboolean(cur))
259                                         goto cleanup;
260                         } else
261                                 continue;
262                         fflush(buffp);
263                 }
264
265                 for (i = 0; i < CONFIG_SIZE; i++) {
266                         if (strcmp(buf, c[i].key) == 0)
267                                 break;
268                 }
269
270                 /* Silently skip unknown keys to be future compatible. */
271                 if (i == CONFIG_SIZE)
272                         continue;
273
274                 /* env has priority over config file */
275                 if (c[i].envset)
276                         continue;
277
278                 /* Parse sequence value ["item1", "item2"] */
279                 switch (c[i].type) {
280                 case PKG_CONFIG_LIST:
281                         if (cur->type != UCL_ARRAY) {
282                                 warnx("Skipping invalid array "
283                                     "value for %s.\n", c[i].key);
284                                 continue;
285                         }
286                         temp_config[i].list =
287                             malloc(sizeof(*temp_config[i].list));
288                         STAILQ_INIT(temp_config[i].list);
289
290                         while ((seq = ucl_iterate_object(cur, &itseq, true))) {
291                                 if (seq->type != UCL_STRING)
292                                         continue;
293                                 cv = malloc(sizeof(struct config_value));
294                                 cv->value =
295                                     strdup(ucl_object_tostring(seq));
296                                 STAILQ_INSERT_TAIL(temp_config[i].list, cv,
297                                     next);
298                         }
299                         break;
300                 case PKG_CONFIG_BOOL:
301                         temp_config[i].value =
302                             strdup(ucl_object_toboolean(cur) ? "yes" : "no");
303                         break;
304                 case PKG_CONFIG_OBJECT:
305                         if (strcmp(c[i].key, "PKG_ENV") == 0) {
306                                 while ((tmp =
307                                     ucl_iterate_object(cur, &it_obj, true))) {
308                                         evkey = ucl_object_key(tmp);
309                                         if (evkey != NULL && *evkey != '\0') {
310                                                 setenv(evkey, ucl_object_tostring_forced(tmp), 1);
311                                         }
312                                 }
313                         }
314                         break;
315                 default:
316                         /* Normal string value. */
317                         temp_config[i].value = strdup(ucl_object_tostring(cur));
318                         break;
319                 }
320         }
321
322         /* Repo is enabled, copy over all settings from temp_config. */
323         for (i = 0; i < CONFIG_SIZE; i++) {
324                 if (c[i].envset)
325                         continue;
326                 /* Prevent overriding ABI, ASSUME_ALWAYS_YES, etc. */
327                 if (conftype != CONFFILE_PKG && c[i].main_only == true)
328                         continue;
329                 switch (c[i].type) {
330                 case PKG_CONFIG_LIST:
331                         c[i].list = temp_config[i].list;
332                         break;
333                 default:
334                         c[i].value = temp_config[i].value;
335                         break;
336                 }
337         }
338
339 cleanup:
340         free(temp_config);
341         fclose(buffp);
342         free(buf);
343 }
344
345 /*-
346  * Parse new repo style configs in style:
347  * Name:
348  *   URL:
349  *   MIRROR_TYPE:
350  * etc...
351  */
352 static void
353 parse_repo_file(ucl_object_t *obj, const char *requested_repo)
354 {
355         ucl_object_iter_t it = NULL;
356         const ucl_object_t *cur;
357         const char *key;
358
359         while ((cur = ucl_iterate_object(obj, &it, true))) {
360                 key = ucl_object_key(cur);
361
362                 if (key == NULL)
363                         continue;
364
365                 if (cur->type != UCL_OBJECT)
366                         continue;
367
368                 if (requested_repo != NULL && strcmp(requested_repo, key) != 0)
369                         continue;
370
371                 config_parse(cur, CONFFILE_REPO);
372         }
373 }
374
375
376 static int
377 read_conf_file(const char *confpath, const char *requested_repo,
378     pkg_conf_file_t conftype)
379 {
380         struct ucl_parser *p;
381         ucl_object_t *obj = NULL;
382
383         p = ucl_parser_new(0);
384
385         if (!ucl_parser_add_file(p, confpath)) {
386                 if (errno != ENOENT)
387                         errx(EXIT_FAILURE, "Unable to parse configuration "
388                             "file %s: %s", confpath, ucl_parser_get_error(p));
389                 ucl_parser_free(p);
390                 /* no configuration present */
391                 return (1);
392         }
393
394         obj = ucl_parser_get_object(p);
395         if (obj->type != UCL_OBJECT) 
396                 warnx("Invalid configuration format, ignoring the "
397                     "configuration file %s", confpath);
398         else {
399                 if (conftype == CONFFILE_PKG)
400                         config_parse(obj, conftype);
401                 else if (conftype == CONFFILE_REPO)
402                         parse_repo_file(obj, requested_repo);
403         }
404
405         ucl_object_unref(obj);
406         ucl_parser_free(p);
407
408         return (0);
409 }
410
411 static int
412 load_repositories(const char *repodir, const char *requested_repo)
413 {
414         struct dirent *ent;
415         DIR *d;
416         char *p;
417         size_t n;
418         char path[MAXPATHLEN];
419         int ret;
420
421         ret = 0;
422
423         if ((d = opendir(repodir)) == NULL)
424                 return (1);
425
426         while ((ent = readdir(d))) {
427                 /* Trim out 'repos'. */
428                 if ((n = strlen(ent->d_name)) <= 5)
429                         continue;
430                 p = &ent->d_name[n - 5];
431                 if (strcmp(p, ".conf") == 0) {
432                         snprintf(path, sizeof(path), "%s%s%s",
433                             repodir,
434                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
435                             ent->d_name);
436                         if (access(path, F_OK) != 0)
437                                 continue;
438                         if (read_conf_file(path, requested_repo,
439                             CONFFILE_REPO)) {
440                                 ret = 1;
441                                 goto cleanup;
442                         }
443                 }
444         }
445
446 cleanup:
447         closedir(d);
448
449         return (ret);
450 }
451
452 int
453 config_init(const char *requested_repo)
454 {
455         char *val;
456         int i;
457         const char *localbase;
458         char *env_list_item;
459         char confpath[MAXPATHLEN];
460         struct config_value *cv;
461         char abi[BUFSIZ];
462
463         for (i = 0; i < CONFIG_SIZE; i++) {
464                 val = getenv(c[i].key);
465                 if (val != NULL) {
466                         c[i].envset = true;
467                         switch (c[i].type) {
468                         case PKG_CONFIG_LIST:
469                                 /* Split up comma-separated items from env. */
470                                 c[i].list = malloc(sizeof(*c[i].list));
471                                 STAILQ_INIT(c[i].list);
472                                 for (env_list_item = strtok(val, ",");
473                                     env_list_item != NULL;
474                                     env_list_item = strtok(NULL, ",")) {
475                                         cv =
476                                             malloc(sizeof(struct config_value));
477                                         cv->value =
478                                             strdup(env_list_item);
479                                         STAILQ_INSERT_TAIL(c[i].list, cv,
480                                             next);
481                                 }
482                                 break;
483                         default:
484                                 c[i].val = val;
485                                 break;
486                         }
487                 }
488         }
489
490         /* Read LOCALBASE/etc/pkg.conf first. */
491         localbase = getlocalbase();
492         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf", localbase);
493
494         if (access(confpath, F_OK) == 0 && read_conf_file(confpath, NULL,
495             CONFFILE_PKG))
496                 goto finalize;
497
498         /* Then read in all repos from REPOS_DIR list of directories. */
499         if (c[REPOS_DIR].list == NULL) {
500                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
501                 STAILQ_INIT(c[REPOS_DIR].list);
502                 cv = malloc(sizeof(struct config_value));
503                 cv->value = strdup("/etc/pkg");
504                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
505                 cv = malloc(sizeof(struct config_value));
506                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
507                         goto finalize;
508                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
509         }
510
511         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
512                 if (load_repositories(cv->value, requested_repo))
513                         goto finalize;
514
515 finalize:
516         if (c[ABI].val == NULL && c[ABI].value == NULL) {
517                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
518                         errx(EXIT_FAILURE, "Failed to determine the system "
519                             "ABI");
520                 c[ABI].val = abi;
521         }
522
523         subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
524
525         return (0);
526 }
527
528 int
529 config_string(pkg_config_key k, const char **val)
530 {
531         if (c[k].type != PKG_CONFIG_STRING)
532                 return (-1);
533
534         if (c[k].value != NULL)
535                 *val = c[k].value;
536         else
537                 *val = c[k].val;
538
539         return (0);
540 }
541
542 int
543 config_bool(pkg_config_key k, bool *val)
544 {
545         const char *value;
546
547         if (c[k].type != PKG_CONFIG_BOOL)
548                 return (-1);
549
550         *val = false;
551
552         if (c[k].value != NULL)
553                 value = c[k].value;
554         else
555                 value = c[k].val;
556
557         if (boolstr_to_bool(value))
558                 *val = true;
559
560         return (0);
561 }
562
563 void
564 config_finish(void) {
565         int i;
566
567         for (i = 0; i < CONFIG_SIZE; i++)
568                 free(c[i].value);
569 }