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