1 /*-
2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3 *
4 * Copyright (c) 2014 Baptiste Daroussin <[email protected]>
5 * Copyright (c) 2013 Bryan Drewery <[email protected]>
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
pkg_get_myabi(char * dest,size_t sz)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
subst_packagesite(const char * abi)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
boolstr_to_bool(const char * str)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
config_parse(const ucl_object_t * obj,pkg_conf_file_t conftype)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
parse_repo_file(ucl_object_t * obj)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
read_conf_file(const char * confpath,pkg_conf_file_t conftype)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
load_repositories(const char * repodir)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
config_init(void)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
config_string(pkg_config_key k,const char ** val)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
config_bool(pkg_config_key k,bool * val)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
config_finish(void)531 config_finish(void) {
532 int i;
533
534 for (i = 0; i < CONFIG_SIZE; i++)
535 free(c[i].value);
536 }
537