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