]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.sbin/pkg/config.c
Remove spurious newline
[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/sbuf.h>
36 #include <sys/utsname.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
46 #include "config.h"
47
48 #define roundup2(x, y)  (((x)+((y)-1))&(~((y)-1))) /* if y is powers of two */
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         struct sbuf *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         newval = sbuf_new_auto();
184         sbuf_bcat(newval, oldval, variable_string - oldval);
185         sbuf_cat(newval, abi);
186         sbuf_cat(newval, variable_string + strlen("${ABI}"));
187         sbuf_finish(newval);
188
189         free(c[PACKAGESITE].value);
190         c[PACKAGESITE].value = strdup(sbuf_data(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)
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                 config_parse(cur, CONFFILE_REPO);
341         }
342 }
343
344
345 static int
346 read_conf_file(const char *confpath, pkg_conf_file_t conftype)
347 {
348         struct ucl_parser *p;
349         ucl_object_t *obj = NULL;
350
351         p = ucl_parser_new(0);
352
353         if (!ucl_parser_add_file(p, confpath)) {
354                 if (errno != ENOENT)
355                         errx(EXIT_FAILURE, "Unable to parse configuration "
356                             "file %s: %s", confpath, ucl_parser_get_error(p));
357                 ucl_parser_free(p);
358                 /* no configuration present */
359                 return (1);
360         }
361
362         obj = ucl_parser_get_object(p);
363         if (obj->type != UCL_OBJECT) 
364                 warnx("Invalid configuration format, ignoring the "
365                     "configuration file %s", confpath);
366         else {
367                 if (conftype == CONFFILE_PKG)
368                         config_parse(obj, conftype);
369                 else if (conftype == CONFFILE_REPO)
370                         parse_repo_file(obj);
371         }
372
373         ucl_object_unref(obj);
374         ucl_parser_free(p);
375
376         return (0);
377 }
378
379 static int
380 load_repositories(const char *repodir)
381 {
382         struct dirent *ent;
383         DIR *d;
384         char *p;
385         size_t n;
386         char path[MAXPATHLEN];
387         int ret;
388
389         ret = 0;
390
391         if ((d = opendir(repodir)) == NULL)
392                 return (1);
393
394         while ((ent = readdir(d))) {
395                 /* Trim out 'repos'. */
396                 if ((n = strlen(ent->d_name)) <= 5)
397                         continue;
398                 p = &ent->d_name[n - 5];
399                 if (strcmp(p, ".conf") == 0) {
400                         snprintf(path, sizeof(path), "%s%s%s",
401                             repodir,
402                             repodir[strlen(repodir) - 1] == '/' ? "" : "/",
403                             ent->d_name);
404                         if (access(path, F_OK) == 0 &&
405                             read_conf_file(path, CONFFILE_REPO)) {
406                                 ret = 1;
407                                 goto cleanup;
408                         }
409                 }
410         }
411
412 cleanup:
413         closedir(d);
414
415         return (ret);
416 }
417
418 int
419 config_init(void)
420 {
421         char *val;
422         int i;
423         const char *localbase;
424         char *env_list_item;
425         char confpath[MAXPATHLEN];
426         struct config_value *cv;
427         char abi[BUFSIZ];
428
429         for (i = 0; i < CONFIG_SIZE; i++) {
430                 val = getenv(c[i].key);
431                 if (val != NULL) {
432                         c[i].envset = true;
433                         switch (c[i].type) {
434                         case PKG_CONFIG_LIST:
435                                 /* Split up comma-separated items from env. */
436                                 c[i].list = malloc(sizeof(*c[i].list));
437                                 STAILQ_INIT(c[i].list);
438                                 for (env_list_item = strtok(val, ",");
439                                     env_list_item != NULL;
440                                     env_list_item = strtok(NULL, ",")) {
441                                         cv =
442                                             malloc(sizeof(struct config_value));
443                                         cv->value =
444                                             strdup(env_list_item);
445                                         STAILQ_INSERT_TAIL(c[i].list, cv,
446                                             next);
447                                 }
448                                 break;
449                         default:
450                                 c[i].val = val;
451                                 break;
452                         }
453                 }
454         }
455
456         /* Read LOCALBASE/etc/pkg.conf first. */
457         localbase = getenv("LOCALBASE") ? getenv("LOCALBASE") : _LOCALBASE;
458         snprintf(confpath, sizeof(confpath), "%s/etc/pkg.conf",
459             localbase);
460
461         if (access(confpath, F_OK) == 0 && read_conf_file(confpath,
462             CONFFILE_PKG))
463                 goto finalize;
464
465         /* Then read in all repos from REPOS_DIR list of directories. */
466         if (c[REPOS_DIR].list == NULL) {
467                 c[REPOS_DIR].list = malloc(sizeof(*c[REPOS_DIR].list));
468                 STAILQ_INIT(c[REPOS_DIR].list);
469                 cv = malloc(sizeof(struct config_value));
470                 cv->value = strdup("/etc/pkg");
471                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
472                 cv = malloc(sizeof(struct config_value));
473                 if (asprintf(&cv->value, "%s/etc/pkg/repos", localbase) < 0)
474                         goto finalize;
475                 STAILQ_INSERT_TAIL(c[REPOS_DIR].list, cv, next);
476         }
477
478         STAILQ_FOREACH(cv, c[REPOS_DIR].list, next)
479                 if (load_repositories(cv->value))
480                         goto finalize;
481
482 finalize:
483         if (c[ABI].val == NULL && c[ABI].value == NULL) {
484                 if (pkg_get_myabi(abi, BUFSIZ) != 0)
485                         errx(EXIT_FAILURE, "Failed to determine the system "
486                             "ABI");
487                 c[ABI].val = abi;
488         }
489
490         subst_packagesite(c[ABI].value != NULL ? c[ABI].value : c[ABI].val);
491
492         return (0);
493 }
494
495 int
496 config_string(pkg_config_key k, const char **val)
497 {
498         if (c[k].type != PKG_CONFIG_STRING)
499                 return (-1);
500
501         if (c[k].value != NULL)
502                 *val = c[k].value;
503         else
504                 *val = c[k].val;
505
506         return (0);
507 }
508
509 int
510 config_bool(pkg_config_key k, bool *val)
511 {
512         const char *value;
513
514         if (c[k].type != PKG_CONFIG_BOOL)
515                 return (-1);
516
517         *val = false;
518
519         if (c[k].value != NULL)
520                 value = c[k].value;
521         else
522                 value = c[k].val;
523
524         if (boolstr_to_bool(value))
525                 *val = true;
526
527         return (0);
528 }
529
530 void
531 config_finish(void) {
532         int i;
533
534         for (i = 0; i < CONFIG_SIZE; i++)
535                 free(c[i].value);
536 }