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