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