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