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