Line data Source code
1 : /*
2 : * Copyright (c) 2011-2015 Baptiste Daroussin <bapt@FreeBSD.org>
3 : * Copyright (c) 2011-2012 Julien Laffaye <jlaffaye@FreeBSD.org>
4 : * Copyright (c) 2014 Matthew Seaman <matthew@FreeBSD.org>
5 : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@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 : * in this position and unchanged.
14 : * 2. Redistributions in binary form must reproduce the above copyright
15 : * notice, this list of conditions and the following disclaimer in the
16 : * documentation and/or other materials provided with the distribution.
17 : *
18 : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
19 : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
20 : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
21 : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
22 : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
23 : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
27 : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 : */
29 :
30 : #include "pkg_config.h"
31 :
32 : #include <assert.h>
33 : #include <sys/socket.h>
34 : #include <sys/un.h>
35 : #include <ctype.h>
36 : #include <dirent.h>
37 : #include <dlfcn.h>
38 : #include <errno.h>
39 : #include <fcntl.h>
40 : #ifdef HAVE_OSRELDATE_H
41 : #include <osreldate.h>
42 : #endif
43 : #include <ucl.h>
44 :
45 : #include "pkg.h"
46 : #include "private/pkg.h"
47 : #include "private/event.h"
48 : #include "pkg_repos.h"
49 :
50 : #ifndef PORTSDIR
51 : #define PORTSDIR "/usr/ports"
52 : #endif
53 : #ifndef DEFAULT_VULNXML_URL
54 : #define DEFAULT_VULNXML_URL "http://vuxml.freebsd.org/freebsd/vuln.xml.bz2"
55 : #endif
56 :
57 : #ifdef OSMAJOR
58 : #define STRINGIFY(X) TEXT(X)
59 : #define TEXT(X) #X
60 : #define INDEXFILE "INDEX-" STRINGIFY(OSMAJOR)
61 : #else
62 : #define INDEXFILE "INDEX"
63 : #endif
64 :
65 : int eventpipe = -1;
66 : int64_t debug_level = 0;
67 : bool developer_mode = false;
68 : const char *pkg_rootdir = NULL;
69 :
70 : struct config_entry {
71 : uint8_t type;
72 : const char *key;
73 : const char *def;
74 : const char *desc;
75 : };
76 :
77 : static char myabi[BUFSIZ], myabi_legacy[BUFSIZ];
78 : static struct pkg_repo *repos = NULL;
79 : ucl_object_t *config = NULL;
80 :
81 : static struct config_entry c[] = {
82 : {
83 : PKG_STRING,
84 : "PKG_DBDIR",
85 : "/var/db/pkg",
86 : "Where the package databases are stored",
87 : },
88 : {
89 : PKG_STRING,
90 : "PKG_CACHEDIR",
91 : "/var/cache/pkg",
92 : "Directory containing cache of downloaded packages",
93 : },
94 : {
95 : PKG_STRING,
96 : "PORTSDIR",
97 : "/usr/ports",
98 : "Location of the ports collection",
99 : },
100 : {
101 : PKG_STRING,
102 : "INDEXDIR",
103 : NULL, /* Default to PORTSDIR unless defined */
104 : "Location of the ports INDEX",
105 : },
106 : {
107 : PKG_STRING,
108 : "INDEXFILE",
109 : INDEXFILE,
110 : "Filename of the ports INDEX",
111 : },
112 : {
113 : PKG_BOOL,
114 : "HANDLE_RC_SCRIPTS",
115 : "NO",
116 : "Automatically handle restarting services",
117 : },
118 : {
119 : PKG_BOOL,
120 : "DEFAULT_ALWAYS_YES",
121 : "NO",
122 : "Default to 'yes' for all pkg(8) questions",
123 : },
124 : {
125 : PKG_BOOL,
126 : "ASSUME_ALWAYS_YES",
127 : "NO",
128 : "Answer 'yes' to all pkg(8) questions",
129 : },
130 : {
131 : PKG_ARRAY,
132 : "REPOS_DIR",
133 : "/etc/pkg/,"PREFIX"/etc/pkg/repos/",
134 : "Location of the repository configuration files"
135 : },
136 : {
137 : PKG_STRING,
138 : "PLIST_KEYWORDS_DIR",
139 : NULL,
140 : "Directory containing definitions of plist keywords",
141 : },
142 : {
143 : PKG_BOOL,
144 : "SYSLOG",
145 : "YES",
146 : "Log pkg(8) operations via syslog(3)",
147 : },
148 : {
149 : PKG_STRING,
150 : "ABI",
151 : myabi,
152 : "Override the automatically detected ABI",
153 : },
154 : {
155 : PKG_STRING,
156 : "ALTABI",
157 : myabi_legacy,
158 : "Override the automatically detected old-form ABI",
159 : },
160 : {
161 : PKG_BOOL,
162 : "DEVELOPER_MODE",
163 : "NO",
164 : "Add extra strict, pedantic warnings as an aid to package maintainers",
165 : },
166 : {
167 : PKG_STRING,
168 : "VULNXML_SITE",
169 : DEFAULT_VULNXML_URL,
170 : "URL giving location of the vulnxml database",
171 : },
172 : {
173 : PKG_INT,
174 : "FETCH_RETRY",
175 : "3",
176 : "How many times to retry fetching files",
177 : },
178 : {
179 : PKG_STRING,
180 : "PKG_PLUGINS_DIR",
181 : PREFIX"/lib/pkg/",
182 : "Directory which pkg(8) will load plugins from",
183 : },
184 : {
185 : PKG_BOOL,
186 : "PKG_ENABLE_PLUGINS",
187 : "YES",
188 : "Activate plugin support",
189 : },
190 : {
191 : PKG_ARRAY,
192 : "PLUGINS",
193 : NULL,
194 : "List of plugins that pkg(8) should load",
195 : },
196 : {
197 : PKG_BOOL,
198 : "DEBUG_SCRIPTS",
199 : "NO",
200 : "Run shell scripts in verbose mode to facilitate debugging",
201 : },
202 : {
203 : PKG_STRING,
204 : "PLUGINS_CONF_DIR",
205 : PREFIX"/etc/pkg/",
206 : "Directory containing plugin configuration data",
207 : },
208 : {
209 : PKG_BOOL,
210 : "PERMISSIVE",
211 : "NO",
212 : "Permit package installation despite presence of conflicting packages",
213 : },
214 : {
215 : PKG_BOOL,
216 : "REPO_AUTOUPDATE",
217 : "YES",
218 : "Automatically update repository catalogues prior to package updates",
219 : },
220 : {
221 : PKG_STRING,
222 : "NAMESERVER",
223 : NULL,
224 : "Use this nameserver when looking up addresses",
225 : },
226 : {
227 : PKG_STRING,
228 : "HTTP_USER_AGENT",
229 : "pkg/"PKGVERSION,
230 : "HTTP User-Agent",
231 : },
232 : {
233 : PKG_STRING,
234 : "EVENT_PIPE",
235 : NULL,
236 : "Send all events to the specified fifo or Unix socket",
237 : },
238 : {
239 : PKG_INT,
240 : "FETCH_TIMEOUT",
241 : "30",
242 : "Number of seconds before fetch(3) times out",
243 : },
244 : {
245 : PKG_BOOL,
246 : "UNSET_TIMESTAMP",
247 : "NO",
248 : "Do not include timestamps in the package",
249 : },
250 : {
251 : PKG_STRING,
252 : "SSH_RESTRICT_DIR",
253 : NULL,
254 : "Directory the ssh subsystem will be restricted to",
255 : },
256 : {
257 : PKG_OBJECT,
258 : "PKG_ENV",
259 : NULL,
260 : "Environment variables pkg will use",
261 : },
262 : {
263 : PKG_STRING,
264 : "PKG_SSH_ARGS",
265 : NULL,
266 : "Extras arguments to pass to ssh(1)",
267 : },
268 : {
269 : PKG_INT,
270 : "DEBUG_LEVEL",
271 : "0",
272 : "Level for debug messages",
273 : },
274 : {
275 : PKG_OBJECT,
276 : "ALIAS",
277 : NULL,
278 : "Command aliases",
279 : },
280 : {
281 : PKG_STRING,
282 : "CUDF_SOLVER",
283 : NULL,
284 : "Experimental: tells pkg to use an external CUDF solver",
285 : },
286 : {
287 : PKG_STRING,
288 : "SAT_SOLVER",
289 : NULL,
290 : "Experimental: tells pkg to use an external SAT solver",
291 : },
292 : {
293 : PKG_BOOL,
294 : "RUN_SCRIPTS",
295 : "YES",
296 : "Run post/pre actions scripts",
297 : },
298 : {
299 : PKG_BOOL,
300 : "CASE_SENSITIVE_MATCH",
301 : "NO",
302 : "Match package names case sensitively",
303 : },
304 : {
305 : PKG_INT,
306 : "LOCK_WAIT",
307 : "1",
308 : "Wait time to regain a lock if it is not available"
309 : },
310 : {
311 : PKG_INT,
312 : "LOCK_RETRIES",
313 : "5",
314 : "Retries performed to obtain a lock"
315 : },
316 : {
317 : PKG_BOOL,
318 : "SQLITE_PROFILE",
319 : "NO",
320 : "Profile sqlite queries"
321 : },
322 : {
323 : PKG_INT,
324 : "WORKERS_COUNT",
325 : "0",
326 : "How many workers are used for pkg-repo (hw.ncpu if 0)"
327 : },
328 : {
329 : PKG_BOOL,
330 : "READ_LOCK",
331 : "NO",
332 : "Use read locking for query database"
333 : },
334 : {
335 : PKG_BOOL,
336 : "PLIST_ACCEPT_DIRECTORIES",
337 : "NO",
338 : "Accept directories listed like plain files in plist"
339 : },
340 : {
341 : PKG_INT,
342 : "IP_VERSION",
343 : "0",
344 : "Restrict network access to IPv4 or IPv6 only"
345 : },
346 : {
347 : PKG_BOOL,
348 : "AUTOMERGE",
349 : "YES",
350 : "Automatically merge configuration files"
351 : },
352 : {
353 : PKG_STRING,
354 : "VERSION_SOURCE",
355 : NULL,
356 : "Version source for pkg-version (I, P, R), default is auto detect"
357 : },
358 : {
359 : PKG_BOOL,
360 : "CONSERVATIVE_UPGRADE",
361 : "YES",
362 : "Prefer repos with higher priority during upgrade"
363 : },
364 : {
365 : PKG_BOOL,
366 : "PKG_CREATE_VERBOSE",
367 : "NO",
368 : "Enable verbose mode for 'pkg create'",
369 : },
370 : {
371 : PKG_BOOL,
372 : "AUTOCLEAN",
373 : "NO",
374 : "Always cleanup the cache directory after install/upgrade",
375 : },
376 : };
377 :
378 : static bool parsed = false;
379 : static size_t c_size = NELEM(c);
380 :
381 : static struct pkg_repo* pkg_repo_new(const char *name,
382 : const char *url, const char *type);
383 : static void pkg_repo_overwrite(struct pkg_repo*, const char *name,
384 : const char *url, const char *type);
385 : static void pkg_repo_free(struct pkg_repo *r);
386 :
387 : static void
388 0 : connect_evpipe(const char *evpipe) {
389 : struct stat st;
390 : struct sockaddr_un sock;
391 0 : int flag = O_WRONLY;
392 :
393 0 : if (stat(evpipe, &st) != 0) {
394 0 : pkg_emit_error("No such event pipe: %s", evpipe);
395 0 : return;
396 : }
397 :
398 0 : if (!S_ISFIFO(st.st_mode) && !S_ISSOCK(st.st_mode)) {
399 0 : pkg_emit_error("%s is not a fifo or socket", evpipe);
400 0 : return;
401 : }
402 :
403 0 : if (S_ISFIFO(st.st_mode)) {
404 0 : flag |= O_NONBLOCK;
405 0 : if ((eventpipe = open(evpipe, flag)) == -1)
406 0 : pkg_emit_errno("open event pipe", evpipe);
407 0 : return;
408 : }
409 :
410 0 : if (S_ISSOCK(st.st_mode)) {
411 0 : if ((eventpipe = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
412 0 : pkg_emit_errno("Open event pipe", evpipe);
413 0 : return;
414 : }
415 0 : memset(&sock, 0, sizeof(struct sockaddr_un));
416 0 : sock.sun_family = AF_UNIX;
417 0 : if (strlcpy(sock.sun_path, evpipe, sizeof(sock.sun_path)) >=
418 : sizeof(sock.sun_path)) {
419 0 : pkg_emit_error("Socket path too long: %s", evpipe);
420 0 : close(eventpipe);
421 0 : eventpipe = -1;
422 0 : return;
423 : }
424 :
425 0 : if (connect(eventpipe, (struct sockaddr *)&sock, SUN_LEN(&sock)) == -1) {
426 0 : pkg_emit_errno("Connect event pipe", evpipe);
427 0 : close(eventpipe);
428 0 : eventpipe = -1;
429 0 : return;
430 : }
431 : }
432 :
433 : }
434 :
435 : int
436 1 : pkg_initialized(void)
437 : {
438 1 : return (parsed);
439 : }
440 :
441 : const pkg_object *
442 5237 : pkg_config_get(const char *key) {
443 5237 : return (ucl_object_find_key(config, key));
444 : }
445 :
446 : const char *
447 2 : pkg_config_dump(void)
448 : {
449 2 : return (pkg_object_dump(config));
450 : }
451 :
452 : static void
453 200 : disable_plugins_if_static(void)
454 : {
455 : void *dlh;
456 :
457 200 : dlh = dlopen(0, 0);
458 :
459 : /* if dlh is NULL then we are in static binary */
460 200 : if (dlh == NULL)
461 0 : ucl_object_replace_key(config, ucl_object_frombool(false), "PKG_ENABLE_PLUGINS", 18, false);
462 : else
463 200 : dlclose(dlh);
464 :
465 200 : return;
466 : }
467 :
468 : static void
469 200 : add_repo(const ucl_object_t *obj, struct pkg_repo *r, const char *rname, pkg_init_flags flags)
470 : {
471 : const ucl_object_t *cur, *enabled;
472 200 : ucl_object_iter_t it = NULL;
473 200 : bool enable = true;
474 200 : const char *url = NULL, *pubkey = NULL, *mirror_type = NULL;
475 200 : const char *signature_type = NULL, *fingerprints = NULL;
476 : const char *key;
477 200 : const char *type = NULL;
478 200 : int use_ipvx = 0;
479 200 : int priority = 0;
480 :
481 200 : pkg_debug(1, "PkgConfig: parsing repository object %s", rname);
482 :
483 200 : enabled = ucl_object_find_key(obj, "enabled");
484 200 : if (enabled == NULL)
485 3 : enabled = ucl_object_find_key(obj, "ENABLED");
486 200 : if (enabled != NULL) {
487 197 : enable = ucl_object_toboolean(enabled);
488 197 : if (!enable && r == NULL) {
489 0 : pkg_debug(1, "PkgConfig: skipping disabled repo %s", rname);
490 0 : return;
491 : }
492 197 : else if (!enable && r != NULL) {
493 : /*
494 : * We basically want to remove the existing repo r and
495 : * forget all stuff parsed
496 : */
497 0 : pkg_debug(1, "PkgConfig: disabling repo %s", rname);
498 0 : HASH_DEL(repos, r);
499 0 : pkg_repo_free(r);
500 0 : return;
501 : }
502 : }
503 :
504 1364 : while ((cur = ucl_iterate_object(obj, &it, true))) {
505 964 : key = ucl_object_key(cur);
506 964 : if (key == NULL)
507 0 : continue;
508 :
509 964 : if (strcasecmp(key, "url") == 0) {
510 200 : if (cur->type != UCL_STRING) {
511 0 : pkg_emit_error("Expecting a string for the "
512 : "'%s' key of the '%s' repo",
513 : key, rname);
514 0 : return;
515 : }
516 200 : url = ucl_object_tostring(cur);
517 764 : } else if (strcasecmp(key, "pubkey") == 0) {
518 0 : if (cur->type != UCL_STRING) {
519 0 : pkg_emit_error("Expecting a string for the "
520 : "'%s' key of the '%s' repo",
521 : key, rname);
522 0 : return;
523 : }
524 0 : pubkey = ucl_object_tostring(cur);
525 764 : } else if (strcasecmp(key, "mirror_type") == 0) {
526 189 : if (cur->type != UCL_STRING) {
527 0 : pkg_emit_error("Expecting a string for the "
528 : "'%s' key of the '%s' repo",
529 : key, rname);
530 0 : return;
531 : }
532 189 : mirror_type = ucl_object_tostring(cur);
533 575 : } else if (strcasecmp(key, "signature_type") == 0) {
534 189 : if (cur->type != UCL_STRING) {
535 0 : pkg_emit_error("Expecting a string for the "
536 : "'%s' key of the '%s' repo",
537 : key, rname);
538 0 : return;
539 : }
540 189 : signature_type = ucl_object_tostring(cur);
541 386 : } else if (strcasecmp(key, "fingerprints") == 0) {
542 189 : if (cur->type != UCL_STRING) {
543 0 : pkg_emit_error("Expecting a string for the "
544 : "'%s' key of the '%s' repo",
545 : key, rname);
546 0 : return;
547 : }
548 189 : fingerprints = ucl_object_tostring(cur);
549 197 : } else if (strcasecmp(key, "type") == 0) {
550 0 : if (cur->type != UCL_STRING) {
551 0 : pkg_emit_error("Expecting a string for the "
552 : "'%s' key of the '%s' repo",
553 : key, rname);
554 0 : return;
555 : }
556 0 : type = ucl_object_tostring(cur);
557 197 : } else if (strcasecmp(key, "ip_version") == 0) {
558 0 : if (cur->type != UCL_INT) {
559 0 : pkg_emit_error("Expecting a integer for the "
560 : "'%s' key of the '%s' repo",
561 : key, rname);
562 0 : return;
563 : }
564 0 : use_ipvx = ucl_object_toint(cur);
565 0 : if (use_ipvx != 4 && use_ipvx != 6)
566 0 : use_ipvx = 0;
567 197 : } else if (strcasecmp(key, "priority") == 0) {
568 0 : if (cur->type != UCL_INT) {
569 0 : pkg_emit_error("Expecting a integer for the "
570 : "'%s' key of the '%s' repo",
571 : key, rname);
572 0 : return;
573 : }
574 0 : priority = ucl_object_toint(cur);
575 : }
576 : }
577 :
578 200 : if (r == NULL && url == NULL) {
579 0 : pkg_debug(1, "No repo and no url for %s", rname);
580 0 : return;
581 : }
582 :
583 200 : if (r == NULL)
584 200 : r = pkg_repo_new(rname, url, type);
585 : else
586 0 : pkg_repo_overwrite(r, rname, url, type);
587 :
588 200 : if (signature_type != NULL) {
589 189 : if (strcasecmp(signature_type, "pubkey") == 0)
590 0 : r->signature_type = SIG_PUBKEY;
591 189 : else if (strcasecmp(signature_type, "fingerprints") == 0)
592 189 : r->signature_type = SIG_FINGERPRINT;
593 : else
594 0 : r->signature_type = SIG_NONE;
595 : }
596 :
597 :
598 200 : if (fingerprints != NULL) {
599 189 : free(r->fingerprints);
600 189 : r->fingerprints = strdup(fingerprints);
601 : }
602 :
603 200 : if (pubkey != NULL) {
604 0 : free(r->pubkey);
605 0 : r->pubkey = strdup(pubkey);
606 : }
607 :
608 200 : r->enable = enable;
609 200 : r->priority = priority;
610 :
611 200 : if (mirror_type != NULL) {
612 189 : if (strcasecmp(mirror_type, "srv") == 0)
613 189 : r->mirror_type = SRV;
614 0 : else if (strcasecmp(mirror_type, "http") == 0)
615 0 : r->mirror_type = HTTP;
616 : else
617 0 : r->mirror_type = NOMIRROR;
618 : }
619 :
620 200 : if ((flags & PKG_INIT_FLAG_USE_IPV4) == PKG_INIT_FLAG_USE_IPV4)
621 0 : use_ipvx = 4;
622 200 : else if ((flags & PKG_INIT_FLAG_USE_IPV6) == PKG_INIT_FLAG_USE_IPV6)
623 0 : use_ipvx = 6;
624 :
625 200 : if (use_ipvx != 4 && use_ipvx != 6)
626 200 : use_ipvx = pkg_object_int(pkg_config_get("IP_VERSION"));
627 :
628 200 : if (use_ipvx == 4)
629 0 : r->flags = REPO_FLAGS_USE_IPV4;
630 200 : else if (use_ipvx == 6)
631 0 : r->flags = REPO_FLAGS_USE_IPV6;
632 : }
633 :
634 : static void
635 200 : walk_repo_obj(const ucl_object_t *obj, const char *file, pkg_init_flags flags)
636 : {
637 : const ucl_object_t *cur;
638 200 : ucl_object_iter_t it = NULL;
639 : struct pkg_repo *r;
640 : const char *key;
641 :
642 600 : while ((cur = ucl_iterate_object(obj, &it, true))) {
643 200 : key = ucl_object_key(cur);
644 200 : pkg_debug(1, "PkgConfig: parsing key '%s'", key);
645 200 : r = pkg_repo_find(key);
646 200 : if (r != NULL)
647 0 : pkg_debug(1, "PkgConfig: overwriting repository %s", key);
648 200 : if (cur->type == UCL_OBJECT)
649 200 : add_repo(cur, r, key, flags);
650 : else
651 0 : pkg_emit_error("Ignoring bad configuration entry in %s: %s",
652 : file, ucl_object_emit(cur, UCL_EMIT_YAML));
653 : }
654 200 : }
655 :
656 : static void
657 200 : load_repo_file(const char *repofile, pkg_init_flags flags)
658 : {
659 : struct ucl_parser *p;
660 200 : ucl_object_t *obj = NULL;
661 200 : const char *myarch = NULL;
662 200 : const char *myarch_legacy = NULL;
663 :
664 200 : p = ucl_parser_new(0);
665 :
666 200 : myarch = pkg_object_string(pkg_config_get("ABI"));
667 200 : ucl_parser_register_variable (p, "ABI", myarch);
668 :
669 200 : myarch_legacy = pkg_object_string(pkg_config_get("ALTABI"));
670 200 : ucl_parser_register_variable (p, "ALTABI", myarch_legacy);
671 :
672 200 : pkg_debug(1, "PKgConfig: loading %s", repofile);
673 200 : if (!ucl_parser_add_file(p, repofile)) {
674 0 : pkg_emit_error("Error parsing: %s: %s", repofile,
675 : ucl_parser_get_error(p));
676 0 : ucl_parser_free(p);
677 0 : return;
678 : }
679 :
680 200 : obj = ucl_parser_get_object(p);
681 200 : if (obj == NULL)
682 0 : return;
683 :
684 200 : if (obj->type == UCL_OBJECT)
685 200 : walk_repo_obj(obj, repofile, flags);
686 :
687 200 : ucl_object_unref(obj);
688 : }
689 :
690 : static int
691 1102 : nodots(const struct dirent *dp)
692 : {
693 1102 : return (dp->d_name[0] != '.');
694 : }
695 :
696 : static void
697 389 : load_repo_files(const char *repodir, pkg_init_flags flags)
698 : {
699 : struct dirent **ent;
700 : char *p;
701 : size_t n;
702 : int nents, i;
703 : char path[MAXPATHLEN];
704 :
705 389 : pkg_debug(1, "PkgConfig: loading repositories in %s", repodir);
706 :
707 389 : nents = scandir(repodir, &ent, nodots, alphasort);
708 717 : for (i = 0; i < nents; i++) {
709 328 : if ((n = strlen(ent[i]->d_name)) <= 5)
710 5 : continue;
711 323 : p = &ent[i]->d_name[n - 5];
712 323 : if (strcmp(p, ".conf") == 0) {
713 200 : snprintf(path, sizeof(path), "%s%s%s",
714 : repodir,
715 200 : repodir[strlen(repodir) - 1] == '/' ? "" : "/",
716 200 : ent[i]->d_name);
717 200 : load_repo_file(path, flags);
718 : }
719 323 : free(ent[i]);
720 : }
721 389 : if (nents >= 0)
722 387 : free(ent);
723 389 : }
724 :
725 : static void
726 200 : load_repositories(const char *repodir, pkg_init_flags flags)
727 : {
728 : const pkg_object *reposlist, *cur;
729 200 : pkg_iter it = NULL;
730 :
731 200 : if (repodir != NULL) {
732 2 : load_repo_files(repodir, flags);
733 202 : return;
734 : }
735 :
736 198 : reposlist = pkg_config_get( "REPOS_DIR");
737 783 : while ((cur = pkg_object_iterate(reposlist, &it)))
738 387 : load_repo_files(pkg_object_string(cur), flags);
739 : }
740 :
741 : bool
742 200 : pkg_compiled_for_same_os_major(void)
743 : {
744 : #ifdef OSMAJOR
745 : const char *myabi;
746 : int osmajor;
747 :
748 200 : myabi = pkg_object_string(pkg_config_get("ABI"));
749 200 : myabi = strchr(myabi,':');
750 200 : myabi++;
751 :
752 200 : osmajor = (int) strtol(myabi, NULL, 10);
753 :
754 200 : return (osmajor == OSMAJOR);
755 : #else
756 : return (true); /* Can't tell, so assume yes */
757 : #endif
758 : }
759 :
760 :
761 : int
762 0 : pkg_init(const char *path, const char *reposdir)
763 : {
764 0 : return (pkg_ini(path, reposdir, 0));
765 : }
766 :
767 : int
768 200 : pkg_ini(const char *path, const char *reposdir, pkg_init_flags flags)
769 : {
770 200 : struct ucl_parser *p = NULL;
771 : size_t i;
772 200 : const char *val = NULL;
773 : const char *buf, *walk, *value, *key, *k;
774 200 : const char *evkey = NULL;
775 200 : const char *nsname = NULL;
776 200 : const char *useragent = NULL;
777 200 : const char *evpipe = NULL;
778 : const ucl_object_t *cur, *object;
779 200 : ucl_object_t *obj = NULL, *o, *ncfg;
780 200 : ucl_object_iter_t it = NULL;
781 200 : struct sbuf *ukey = NULL;
782 200 : bool fatal_errors = false;
783 200 : char *rootedpath = NULL;
784 200 : char *tmp = NULL;
785 :
786 200 : k = NULL;
787 200 : o = NULL;
788 :
789 200 : pkg_get_myarch(myabi, BUFSIZ);
790 200 : pkg_get_myarch_legacy(myabi_legacy, BUFSIZ);
791 200 : if (parsed != false) {
792 0 : pkg_emit_error("pkg_init() must only be called once");
793 0 : return (EPKG_FATAL);
794 : }
795 :
796 200 : if (((flags & PKG_INIT_FLAG_USE_IPV4) == PKG_INIT_FLAG_USE_IPV4) &&
797 0 : ((flags & PKG_INIT_FLAG_USE_IPV6) == PKG_INIT_FLAG_USE_IPV6)) {
798 0 : pkg_emit_error("Invalid flags for pkg_init()");
799 0 : return (EPKG_FATAL);
800 : }
801 :
802 200 : config = ucl_object_typed_new(UCL_OBJECT);
803 :
804 10000 : for (i = 0; i < c_size; i++) {
805 9800 : switch (c[i].type) {
806 : case PKG_STRING:
807 3800 : tmp = NULL;
808 4800 : if (c[i].def != NULL && c[i].def[0] == '/' &&
809 1000 : pkg_rootdir != NULL) {
810 5 : asprintf(&tmp, "%s%s", pkg_rootdir, c[i].def);
811 : }
812 5800 : obj = ucl_object_fromstring_common(
813 5800 : c[i].def != NULL ? tmp != NULL ? tmp : c[i].def : "", 0, UCL_STRING_TRIM);
814 3800 : free(tmp);
815 3800 : ucl_object_insert_key(config, obj,
816 : c[i].key, strlen(c[i].key), false);
817 3800 : break;
818 : case PKG_INT:
819 1400 : ucl_object_insert_key(config,
820 : ucl_object_fromstring_common(c[i].def, 0, UCL_STRING_PARSE_INT),
821 : c[i].key, strlen(c[i].key), false);
822 1400 : break;
823 : case PKG_BOOL:
824 3800 : ucl_object_insert_key(config,
825 : ucl_object_fromstring_common(c[i].def, 0, UCL_STRING_PARSE_BOOLEAN),
826 : c[i].key, strlen(c[i].key), false);
827 3800 : break;
828 : case PKG_OBJECT:
829 400 : obj = ucl_object_typed_new(UCL_OBJECT);
830 400 : if (c[i].def != NULL) {
831 0 : walk = buf = c[i].def;
832 0 : while ((buf = strchr(buf, ',')) != NULL) {
833 0 : key = walk;
834 0 : value = walk;
835 0 : while (*value != ',') {
836 0 : if (*value == '=')
837 0 : break;
838 0 : value++;
839 : }
840 0 : ucl_object_insert_key(obj,
841 0 : ucl_object_fromstring_common(value + 1, buf - value - 1, UCL_STRING_TRIM),
842 0 : key, value - key, false);
843 0 : buf++;
844 0 : walk = buf;
845 : }
846 0 : key = walk;
847 0 : value = walk;
848 0 : while (*value != ',') {
849 0 : if (*value == '=')
850 0 : break;
851 0 : value++;
852 : }
853 0 : if (o == NULL)
854 0 : o = ucl_object_typed_new(UCL_OBJECT);
855 0 : ucl_object_insert_key(o,
856 : ucl_object_fromstring_common(value + 1, strlen(value + 1), UCL_STRING_TRIM),
857 0 : key, value - key, false);
858 : }
859 400 : ucl_object_insert_key(config, obj,
860 : c[i].key, strlen(c[i].key), false);
861 400 : break;
862 : case PKG_ARRAY:
863 400 : obj = ucl_object_typed_new(UCL_ARRAY);
864 400 : if (c[i].def != NULL) {
865 200 : walk = buf = c[i].def;
866 600 : while ((buf = strchr(buf, ',')) != NULL) {
867 200 : ucl_array_append(obj,
868 200 : ucl_object_fromstring_common(walk, buf - walk, UCL_STRING_TRIM));
869 200 : buf++;
870 200 : walk = buf;
871 : }
872 200 : ucl_array_append(obj,
873 : ucl_object_fromstring_common(walk, strlen(walk), UCL_STRING_TRIM));
874 : }
875 400 : ucl_object_insert_key(config, obj,
876 : c[i].key, strlen(c[i].key), false);
877 400 : break;
878 : }
879 : }
880 :
881 200 : if (path == NULL)
882 187 : path = PREFIX"/etc/pkg.conf";
883 :
884 200 : if (pkg_rootdir != NULL)
885 1 : asprintf(&rootedpath, "%s/%s", pkg_rootdir, path);
886 :
887 200 : p = ucl_parser_new(0);
888 200 : ucl_parser_register_variable (p, "ABI", myabi);
889 200 : ucl_parser_register_variable (p, "ALTABI", myabi_legacy);
890 :
891 200 : errno = 0;
892 200 : obj = NULL;
893 200 : if (!ucl_parser_add_file(p, rootedpath != NULL ? rootedpath : path)) {
894 10 : if (errno != ENOENT)
895 0 : pkg_emit_error("Invalid configuration file: %s", ucl_parser_get_error(p));
896 : } else {
897 190 : obj = ucl_parser_get_object(p);
898 :
899 : }
900 :
901 200 : ncfg = NULL;
902 590 : while (obj != NULL && (cur = ucl_iterate_object(obj, &it, true))) {
903 190 : sbuf_init(&ukey);
904 190 : key = ucl_object_key(cur);
905 1140 : for (i = 0; key[i] != '\0'; i++)
906 950 : sbuf_putc(ukey, toupper(key[i]));
907 190 : sbuf_finish(ukey);
908 190 : object = ucl_object_find_keyl(config, sbuf_data(ukey), sbuf_len(ukey));
909 :
910 190 : if (strncasecmp(sbuf_data(ukey), "PACKAGESITE", sbuf_len(ukey))
911 190 : == 0 || strncasecmp(sbuf_data(ukey), "PUBKEY",
912 380 : sbuf_len(ukey)) == 0 || strncasecmp(sbuf_data(ukey),
913 190 : "MIRROR_TYPE", sbuf_len(ukey)) == 0) {
914 0 : pkg_emit_error("%s in pkg.conf is no longer "
915 : "supported. Convert to the new repository style."
916 : " See pkg.conf(5)", sbuf_data(ukey));
917 0 : fatal_errors = true;
918 0 : continue;
919 : }
920 :
921 : /* ignore unknown keys */
922 190 : if (object == NULL)
923 0 : continue;
924 :
925 190 : if (object->type != cur->type) {
926 0 : pkg_emit_error("Malformed key %s, ignoring", key);
927 0 : continue;
928 : }
929 :
930 190 : if (ncfg == NULL)
931 190 : ncfg = ucl_object_typed_new(UCL_OBJECT);
932 190 : ucl_object_insert_key(ncfg, ucl_object_copy(cur), sbuf_data(ukey), sbuf_len(ukey), true);
933 : }
934 :
935 200 : if (fatal_errors) {
936 0 : ucl_object_unref(ncfg);
937 0 : ucl_parser_free(p);
938 0 : free(rootedpath);
939 0 : return (EPKG_FATAL);
940 : }
941 :
942 200 : if (ncfg != NULL) {
943 190 : it = NULL;
944 570 : while (( cur = ucl_iterate_object(ncfg, &it, true))) {
945 190 : key = ucl_object_key(cur);
946 190 : ucl_object_replace_key(config, ucl_object_ref(cur), key, strlen(key), true);
947 : }
948 190 : ucl_object_unref(ncfg);
949 : }
950 :
951 200 : ncfg = NULL;
952 200 : it = NULL;
953 10200 : while ((cur = ucl_iterate_object(config, &it, true))) {
954 9800 : o = NULL;
955 9800 : key = ucl_object_key(cur);
956 9800 : val = getenv(key);
957 9800 : if (val == NULL)
958 9568 : continue;
959 232 : switch (cur->type) {
960 : case UCL_STRING:
961 215 : o = ucl_object_fromstring_common(val, 0, UCL_STRING_TRIM);
962 215 : break;
963 : case UCL_INT:
964 0 : o = ucl_object_fromstring_common(val, 0, UCL_STRING_PARSE_INT);
965 0 : if (o->type != UCL_INT) {
966 0 : pkg_emit_error("Invalid type for environment "
967 : "variable %s, got %s, while expecting an integer",
968 : key, val);
969 0 : ucl_object_unref(o);
970 0 : continue;
971 : }
972 0 : break;
973 : case UCL_BOOLEAN:
974 3 : o = ucl_object_fromstring_common(val, 0, UCL_STRING_PARSE_BOOLEAN);
975 3 : if (o->type != UCL_BOOLEAN) {
976 0 : pkg_emit_error("Invalid type for environment "
977 : "variable %s, got %s, while expecting a boolean",
978 : key, val);
979 0 : ucl_object_unref(o);
980 0 : continue;
981 : }
982 3 : break;
983 : case UCL_OBJECT:
984 4 : o = ucl_object_typed_new(UCL_OBJECT);
985 4 : walk = buf = val;
986 12 : while ((buf = strchr(buf, ',')) != NULL) {
987 4 : k = walk;
988 4 : value = walk;
989 52 : while (*value != ',') {
990 48 : if (*value == '=')
991 4 : break;
992 44 : value++;
993 : }
994 8 : ucl_object_insert_key(o,
995 4 : ucl_object_fromstring_common(value + 1, buf - value - 1, UCL_STRING_TRIM),
996 4 : k, value - k, false);
997 4 : buf++;
998 4 : walk = buf;
999 : }
1000 4 : k = walk;
1001 4 : value = walk;
1002 24 : while (*value != '\0') {
1003 20 : if (*value == '=')
1004 4 : break;
1005 16 : value++;
1006 : }
1007 4 : ucl_object_insert_key(o,
1008 : ucl_object_fromstring_common(value + 1, strlen(value + 1), UCL_STRING_TRIM),
1009 4 : k, value - k, false);
1010 4 : break;
1011 : case UCL_ARRAY:
1012 10 : o = ucl_object_typed_new(UCL_ARRAY);
1013 10 : walk = buf = val;
1014 20 : while ((buf = strchr(buf, ',')) != NULL) {
1015 0 : ucl_array_append(o,
1016 0 : ucl_object_fromstring_common(walk, buf - walk, UCL_STRING_TRIM));
1017 0 : buf++;
1018 0 : walk = buf;
1019 : }
1020 10 : ucl_array_append(o,
1021 : ucl_object_fromstring_common(walk, strlen(walk), UCL_STRING_TRIM));
1022 10 : break;
1023 : default:
1024 : /* ignore other types */
1025 0 : break;
1026 : }
1027 232 : if (o != NULL) {
1028 232 : if (ncfg == NULL)
1029 198 : ncfg = ucl_object_typed_new(UCL_OBJECT);
1030 232 : ucl_object_insert_key(ncfg, o, key, strlen(key), true);
1031 : }
1032 : }
1033 :
1034 200 : if (ncfg != NULL) {
1035 198 : it = NULL;
1036 628 : while (( cur = ucl_iterate_object(ncfg, &it, true))) {
1037 232 : key = ucl_object_key(cur);
1038 232 : ucl_object_replace_key(config, ucl_object_ref(cur), key, strlen(key), true);
1039 : }
1040 198 : ucl_object_unref(ncfg);
1041 : }
1042 :
1043 200 : disable_plugins_if_static();
1044 :
1045 200 : parsed = true;
1046 200 : ucl_object_unref(obj);
1047 200 : ucl_parser_free(p);
1048 200 : free(rootedpath);
1049 :
1050 200 : if (strcmp(pkg_object_string(pkg_config_get("ABI")), "unknown") == 0) {
1051 0 : pkg_emit_error("Unable to determine ABI");
1052 0 : return (EPKG_FATAL);
1053 : }
1054 :
1055 200 : pkg_debug(1, "%s", "pkg initialized");
1056 :
1057 : /* Start the event pipe */
1058 200 : evpipe = pkg_object_string(pkg_config_get("EVENT_PIPE"));
1059 200 : if (evpipe != NULL)
1060 0 : connect_evpipe(evpipe);
1061 :
1062 200 : debug_level = pkg_object_int(pkg_config_get("DEBUG_LEVEL"));
1063 200 : developer_mode = pkg_object_bool(pkg_config_get("DEVELOPER_MODE"));
1064 :
1065 200 : it = NULL;
1066 200 : object = ucl_object_find_key(config, "PKG_ENV");
1067 400 : while ((cur = ucl_iterate_object(object, &it, true))) {
1068 0 : evkey = ucl_object_key(cur);
1069 0 : pkg_debug(1, "Setting env var: %s", evkey);
1070 0 : if (evkey != NULL && evkey[0] != '\0')
1071 0 : setenv(evkey, ucl_object_tostring_forced(cur), 1);
1072 : }
1073 :
1074 : /* Set user-agent */
1075 200 : useragent = pkg_object_string(pkg_config_get("HTTP_USER_AGENT"));
1076 200 : setenv("HTTP_USER_AGENT", useragent, 1);
1077 :
1078 : /* load the repositories */
1079 200 : load_repositories(reposdir, flags);
1080 :
1081 : /* bypass resolv.conf with specified NAMESERVER if any */
1082 200 : nsname = pkg_object_string(pkg_config_get("NAMESERVER"));
1083 200 : if (nsname != NULL)
1084 0 : set_nameserver(ucl_object_tostring_forced(o));
1085 :
1086 200 : return (EPKG_OK);
1087 : }
1088 :
1089 : static struct pkg_repo_ops*
1090 400 : pkg_repo_find_type(const char *type)
1091 : {
1092 400 : struct pkg_repo_ops *found = NULL, **cur;
1093 :
1094 : /* Default repo type */
1095 400 : if (type == NULL)
1096 200 : return (pkg_repo_find_type("binary"));
1097 :
1098 200 : cur = &repos_ops[0];
1099 600 : while (*cur != NULL) {
1100 200 : if (strcasecmp(type, (*cur)->type) == 0) {
1101 200 : found = *cur;
1102 : }
1103 200 : cur ++;
1104 : }
1105 :
1106 200 : if (found == NULL)
1107 0 : return (pkg_repo_find_type("binary"));
1108 :
1109 200 : return (found);
1110 : }
1111 :
1112 : static struct pkg_repo *
1113 200 : pkg_repo_new(const char *name, const char *url, const char *type)
1114 : {
1115 : struct pkg_repo *r;
1116 :
1117 200 : r = calloc(1, sizeof(struct pkg_repo));
1118 200 : r->ops = pkg_repo_find_type(type);
1119 200 : r->url = strdup(url);
1120 200 : r->signature_type = SIG_NONE;
1121 200 : r->mirror_type = NOMIRROR;
1122 200 : r->enable = true;
1123 200 : r->meta = pkg_repo_meta_default();
1124 200 : r->name = strdup(name);
1125 200 : HASH_ADD_KEYPTR(hh, repos, r->name, strlen(r->name), r);
1126 :
1127 200 : return (r);
1128 : }
1129 :
1130 : static void
1131 0 : pkg_repo_overwrite(struct pkg_repo *r, const char *name, const char *url,
1132 : const char *type)
1133 : {
1134 :
1135 0 : free(r->name);
1136 0 : r->name = strdup(name);
1137 0 : if (url != NULL) {
1138 0 : free(r->url);
1139 0 : r->url = strdup(url);
1140 : }
1141 0 : r->ops = pkg_repo_find_type(type);
1142 0 : HASH_DEL(repos, r);
1143 0 : HASH_ADD_KEYPTR(hh, repos, r->name, strlen(r->name), r);
1144 0 : }
1145 :
1146 : static void
1147 200 : pkg_repo_free(struct pkg_repo *r)
1148 : {
1149 200 : free(r->url);
1150 200 : free(r->name);
1151 200 : free(r->pubkey);
1152 200 : free(r->meta);
1153 200 : if (r->ssh != NULL) {
1154 0 : fprintf(r->ssh, "quit\n");
1155 0 : pclose(r->ssh);
1156 : }
1157 200 : free(r);
1158 200 : }
1159 :
1160 : void
1161 200 : pkg_shutdown(void)
1162 : {
1163 200 : if (!parsed) {
1164 0 : pkg_emit_error("pkg_shutdown() must be called after pkg_init()");
1165 0 : _exit(EX_SOFTWARE);
1166 : /* NOTREACHED */
1167 : }
1168 :
1169 200 : ucl_object_unref(config);
1170 200 : HASH_FREE(repos, pkg_repo_free);
1171 :
1172 200 : parsed = false;
1173 :
1174 200 : return;
1175 : }
1176 :
1177 : int
1178 35 : pkg_repos_total_count(void)
1179 : {
1180 :
1181 35 : return (HASH_COUNT(repos));
1182 : }
1183 :
1184 : int
1185 8 : pkg_repos_activated_count(void)
1186 : {
1187 8 : struct pkg_repo *r = NULL;
1188 8 : int count = 0;
1189 :
1190 16 : for (r = repos; r != NULL; r = r->hh.next) {
1191 8 : if (r->enable)
1192 8 : count++;
1193 : }
1194 :
1195 8 : return (count);
1196 : }
1197 :
1198 : int
1199 71 : pkg_repos(struct pkg_repo **r)
1200 : {
1201 71 : HASH_NEXT(repos, (*r));
1202 : }
1203 :
1204 : const char *
1205 106 : pkg_repo_url(struct pkg_repo *r)
1206 : {
1207 106 : return (r->url);
1208 : }
1209 :
1210 : /* The basename of the sqlite DB file and the database name */
1211 : const char *
1212 126 : pkg_repo_name(struct pkg_repo *r)
1213 : {
1214 126 : return (r->name);
1215 : }
1216 :
1217 : const char *
1218 3 : pkg_repo_key(struct pkg_repo *r)
1219 : {
1220 3 : return (r->pubkey);
1221 : }
1222 :
1223 : const char *
1224 3 : pkg_repo_fingerprints(struct pkg_repo *r)
1225 : {
1226 3 : return (r->fingerprints);
1227 : }
1228 :
1229 : signature_t
1230 55 : pkg_repo_signature_type(struct pkg_repo *r)
1231 : {
1232 55 : return (r->signature_type);
1233 : }
1234 :
1235 : bool
1236 35 : pkg_repo_enabled(struct pkg_repo *r)
1237 : {
1238 35 : return (r->enable);
1239 : }
1240 :
1241 : mirror_t
1242 6 : pkg_repo_mirror_type(struct pkg_repo *r)
1243 : {
1244 6 : return (r->mirror_type);
1245 : }
1246 :
1247 : unsigned int
1248 3 : pkg_repo_priority(struct pkg_repo *r)
1249 : {
1250 3 : return (r->priority);
1251 : }
1252 :
1253 : /* Locate the repo by the file basename / database name */
1254 : struct pkg_repo *
1255 218 : pkg_repo_find(const char *reponame)
1256 : {
1257 : struct pkg_repo *r;
1258 :
1259 218 : HASH_FIND_STR(repos, reponame, r);
1260 218 : return (r);
1261 : }
1262 :
1263 : int64_t
1264 381 : pkg_set_debug_level(int64_t new_debug_level) {
1265 381 : int64_t old_debug_level = debug_level;
1266 :
1267 381 : debug_level = new_debug_level;
1268 381 : return old_debug_level;
1269 : }
1270 :
1271 : void
1272 1 : pkg_set_rootdir(const char *rootdir) {
1273 1 : if (pkg_initialized())
1274 1 : return;
1275 :
1276 1 : pkg_rootdir = rootdir;
1277 : }
|