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