pkg_abi.c
1 /*- 2 * Copyright (c) 2011-2025 Baptiste Daroussin <bapt@FreeBSD.org> 3 * Copyright (c) 2012-2013 Matthew Seaman <matthew@FreeBSD.org> 4 * Copyright (c) 2024 The FreeBSD Foundation 5 * 6 * This software was developed in part by Isaac Freund <ifreund@freebsdfoundation.org> 7 * under sponsorship from the FreeBSD Foundation. 8 * 9 * SPDX-License-Identifier: BSD-2-Clause 10 */ 11 #ifdef HAVE_CONFIG_H 12 #include "pkg_config.h" 13 #endif 14 15 #include <ctype.h> 16 #include <paths.h> 17 #include <string.h> 18 #include <unistd.h> 19 20 #include "pkg.h" 21 #include "private/pkg_abi.h" 22 #include "private/binfmt.h" 23 #include "private/event.h" 24 #include "private/pkg.h" 25 #include "private/utils.h" 26 #include "xmalloc.h" 27 28 #define _PATH_UNAME "/usr/bin/uname" 29 30 /* All possibilities on FreeBSD as of 5/26/2014 */ 31 struct arch_trans { 32 const char *elftype; 33 const char *archid; 34 }; 35 36 static const struct arch_trans machine_arch_translation[] = { { "x86:32", "i386" }, 37 { "x86:64", "amd64" }, { "powerpc:32:eb", "powerpc" }, 38 { "powerpc:64:eb", "powerpc64" }, { "powerpc:64:el", "powerpc64le" }, 39 { "sparc64:64", "sparc64" }, { "ia64:64", "ia64" }, 40 /* All the ARM stuff */ 41 { "armv6:32:el:eabi:hardfp", "armv6" }, 42 { "armv7:32:el:eabi:hardfp", "armv7" }, { "aarch64:64", "aarch64" }, 43 /* And now MIPS */ 44 { "mips:32:el:o32", "mipsel" }, { "mips:32:el:n32", "mipsn32el" }, 45 { "mips:32:eb:o32", "mips" }, { "mips:32:eb:n32", "mipsn32" }, 46 { "mips:64:el:n64", "mips64el" }, { "mips:64:eb:n64", "mips64" }, 47 /* And RISC-V */ 48 { "riscv:32:hf", "riscv32" }, { "riscv:32:sf", "riscv32sf" }, 49 { "riscv:64:hf", "riscv64" }, { "riscv:64:sf", "riscv64sf" }, 50 51 { NULL, NULL } }; 52 53 static const struct { 54 enum pkg_os os; 55 const char *string; 56 } os_string_table[] = { 57 { PKG_OS_UNKNOWN, "Unknown" }, 58 { PKG_OS_FREEBSD, "FreeBSD" }, 59 { PKG_OS_NETBSD, "NetBSD" }, 60 { PKG_OS_DRAGONFLY, "dragonfly" }, 61 { PKG_OS_LINUX, "Linux" }, 62 { PKG_OS_DARWIN, "Darwin" }, 63 { PKG_OS_ANY, "*" }, 64 { -1, NULL }, 65 }; 66 67 /* This table does not include PKG_ARCH_AMD64 as the string translation of 68 that arch is os-dependent. */ 69 static const struct { 70 enum pkg_arch arch; 71 const char *string; 72 } arch_string_table[] = { 73 { PKG_ARCH_UNKNOWN, "unknown"}, 74 { PKG_ARCH_I386, "i386"}, 75 { PKG_ARCH_ARMV6, "armv6"}, 76 { PKG_ARCH_ARMV7, "armv7"}, 77 { PKG_ARCH_AARCH64, "aarch64"}, 78 { PKG_ARCH_POWERPC, "powerpc"}, 79 { PKG_ARCH_POWERPC64, "powerpc64"}, 80 { PKG_ARCH_POWERPC64LE, "powerpc64le"}, 81 { PKG_ARCH_RISCV32, "riscv32"}, 82 { PKG_ARCH_RISCV64, "riscv64"}, 83 { PKG_ARCH_ANY, "*"}, 84 { -1, NULL }, 85 }; 86 87 const char * 88 pkg_os_to_string(enum pkg_os os) 89 { 90 for (size_t i = 0; os_string_table[i].string != NULL; i++) { 91 if (os == os_string_table[i].os) { 92 return os_string_table[i].string; 93 } 94 } 95 assert(0); 96 } 97 98 static enum pkg_os 99 pkg_os_from_string(const char *string) 100 { 101 for (size_t i = 0; os_string_table[i].string != NULL; i++) { 102 if (STREQ(string, os_string_table[i].string)) { 103 return os_string_table[i].os; 104 } 105 } 106 return (PKG_OS_UNKNOWN); 107 } 108 109 /* Returns true if the OS uses "amd64" rather than "x86_64" */ 110 static bool 111 pkg_os_uses_amd64_name(enum pkg_os os) 112 { 113 switch (os) { 114 case PKG_OS_FREEBSD: 115 return (true); 116 case PKG_OS_DARWIN: 117 case PKG_OS_NETBSD: 118 case PKG_OS_LINUX: 119 return (false); 120 case PKG_OS_DRAGONFLY: 121 case PKG_OS_UNKNOWN: 122 default: 123 assert(0); 124 } 125 } 126 127 const char * 128 pkg_arch_to_string(enum pkg_os os, enum pkg_arch arch) 129 { 130 if (arch == PKG_ARCH_AMD64) { 131 if (os == PKG_OS_DRAGONFLY) { 132 return ("x86:64"); 133 } else if (pkg_os_uses_amd64_name(os)) { 134 return ("amd64"); 135 } else { 136 return ("x86_64"); 137 } 138 } 139 140 for (size_t i = 0; arch_string_table[i].string != NULL; i++) { 141 if (arch == arch_string_table[i].arch) { 142 return arch_string_table[i].string; 143 } 144 } 145 146 assert(0); 147 } 148 149 static enum pkg_arch 150 pkg_arch_from_string(enum pkg_os os, const char *string) 151 { 152 if (os == PKG_OS_DRAGONFLY) { 153 if (STREQ(string, "x86:64")) { 154 return (PKG_ARCH_AMD64); 155 } 156 } else if (pkg_os_uses_amd64_name(os)) { 157 if (STREQ(string, "amd64")) { 158 return (PKG_ARCH_AMD64); 159 } 160 } else { 161 if (STREQ(string, "x86_64")) { 162 return (PKG_ARCH_AMD64); 163 } 164 } 165 166 for (size_t i = 0; arch_string_table[i].string != NULL; i++) { 167 if (STREQ(string, arch_string_table[i].string)) { 168 return arch_string_table[i].arch; 169 } 170 } 171 172 return (PKG_ARCH_UNKNOWN); 173 } 174 175 bool 176 pkg_abi_string_only_major_version(enum pkg_os os) 177 { 178 switch (os) { 179 case PKG_OS_FREEBSD: 180 case PKG_OS_NETBSD: 181 case PKG_OS_DARWIN: 182 return (true); 183 case PKG_OS_DRAGONFLY: 184 case PKG_OS_LINUX: 185 return (false); 186 case PKG_OS_UNKNOWN: 187 default: 188 assert (0); 189 } 190 } 191 192 char * 193 pkg_abi_to_string(const struct pkg_abi *abi) 194 { 195 char *ret; 196 if (pkg_abi_string_only_major_version(abi->os)) { 197 xasprintf(&ret, "%s:%d:%s", pkg_os_to_string(abi->os), 198 abi->major, pkg_arch_to_string(abi->os, abi->arch)); 199 } else { 200 xasprintf(&ret, "%s:%d.%d:%s", pkg_os_to_string(abi->os), 201 abi->major, abi->minor, 202 pkg_arch_to_string(abi->os, abi->arch)); 203 } 204 return (ret); 205 } 206 207 bool 208 pkg_abi_from_string(struct pkg_abi *abi, const char *string) 209 { 210 *abi = (struct pkg_abi){0}; 211 212 bool ret = false; 213 214 char *copy = xstrdup(string); 215 216 char *iter = copy; 217 char *os = strsep(&iter, ":"); 218 assert(os != NULL); 219 abi->os = pkg_os_from_string(os); 220 if (abi->os == PKG_OS_UNKNOWN) { 221 pkg_emit_error("Unknown OS '%s' in ABI string", os); 222 goto out; 223 } 224 225 char *version = strsep(&iter, ":"); 226 if (version == NULL) { 227 if (abi->os == PKG_OS_ANY) { 228 abi->major = 0; 229 abi->minor = 0; 230 abi->arch = PKG_ARCH_ANY; 231 ret = true; 232 goto out; 233 } 234 pkg_emit_error("Invalid ABI string '%s', " 235 "missing version and architecture", string); 236 goto out; 237 } 238 const char *errstr = NULL; 239 if (pkg_abi_string_only_major_version(abi->os)) { 240 abi->major = strtonum(version, 1, INT_MAX, &errstr); 241 } else { 242 /* XXX add tests for this */ 243 char *major = strsep(&version, "."); 244 char *minor = strsep(&version, "."); 245 246 assert(major != NULL); 247 if (minor == NULL) { 248 pkg_emit_error("Invalid ABI string %s, " 249 "missing minor OS version", string); 250 goto out; 251 } 252 253 abi->major = strtonum(major, 1, INT_MAX, &errstr); 254 if (errstr != NULL) { 255 abi->minor = strtonum(minor, 1, INT_MAX, &errstr); 256 } 257 } 258 if (errstr != NULL) { 259 if (STREQ(version, "*")) { 260 abi->major = 0; 261 abi->minor = 0; 262 abi->arch = PKG_ARCH_ANY; 263 ret = true; 264 goto out; 265 } 266 pkg_emit_error("Invalid version in ABI string '%s'", string); 267 goto out; 268 } 269 270 /* DragonFlyBSD continues to use the legacy/altabi format. 271 For example: dragonfly:5.10:x86:64 272 This means we can't use strsep again since that would split the arch 273 string for dragonfly. */ 274 char *arch = iter; 275 if (arch == NULL) { 276 pkg_emit_error("Invalid ABI string '%s', " 277 "missing architecture", string); 278 goto out; 279 } 280 281 abi->arch = pkg_arch_from_string(abi->os, arch); 282 if (abi->arch == PKG_ARCH_UNKNOWN) { 283 pkg_emit_error("Unknown architecture '%s' in ABI string", arch); 284 goto out; 285 } 286 287 if (abi->os == PKG_OS_DRAGONFLY && abi->arch != PKG_ARCH_AMD64) { 288 pkg_emit_error("Invalid ABI string '%s', " 289 "only x86:64 is supported on dragonfly.", string); 290 goto out; 291 } 292 293 ret = true; 294 out: 295 free(copy); 296 return (ret); 297 } 298 299 void 300 pkg_abi_set_freebsd_osversion(struct pkg_abi *abi, int osversion) 301 { 302 assert(abi->os == PKG_OS_FREEBSD); 303 304 abi->major = osversion / 100000; 305 abi->minor = (osversion / 1000) % 100; 306 abi->patch = osversion % 1000; 307 } 308 309 int 310 pkg_abi_get_freebsd_osversion(struct pkg_abi *abi) 311 { 312 assert(abi->os == PKG_OS_FREEBSD); 313 314 return (abi->major * 100000) + (abi->minor * 1000) + abi->patch; 315 } 316 317 int 318 pkg_abi_from_file(struct pkg_abi *abi) 319 { 320 char rooted_abi_file[PATH_MAX]; 321 const char *abi_files[] = { 322 getenv("ABI_FILE"), 323 _PATH_UNAME, 324 _PATH_BSHELL, 325 }; 326 char work_abi_file[PATH_MAX]; 327 char work_arch_hint[PATH_MAX]; 328 329 size_t i; 330 int fd; 331 332 /* 333 * Perhaps not yet needed, but it may be in the future that there's no 334 * need to check root under some conditions where there is a rootdir. 335 * This also helps alleviate some excessive wrapping later. 336 */ 337 bool checkroot = ctx.pkg_rootdir != NULL; 338 for (fd = -1, i = 0; i < NELEM(abi_files); i++) { 339 if (abi_files[i] == NULL) 340 continue; 341 342 const char *sep = strrchr(abi_files[i], '#'); 343 if (sep) { 344 strlcpy(work_abi_file, abi_files[i], 345 MIN(sep - abi_files[i] + 1, sizeof(work_abi_file))); 346 strlcpy(work_arch_hint, sep + 1, 347 sizeof(work_arch_hint)); 348 } else { 349 strlcpy(work_abi_file, abi_files[i], 350 sizeof(work_abi_file)); 351 work_arch_hint[0] = '\0'; 352 } 353 354 /* 355 * Try prepending rootdir and using that if it exists. If 356 * ABI_FILE is specified, assume that the consumer didn't want 357 * it mangled by rootdir. 358 */ 359 if (i > 0 && checkroot && 360 snprintf(rooted_abi_file, PATH_MAX, "%s/%s", 361 ctx.pkg_rootdir, work_abi_file) < PATH_MAX) { 362 if ((fd = open(rooted_abi_file, O_RDONLY)) >= 0) { 363 strlcpy(work_abi_file, rooted_abi_file, 364 sizeof(work_abi_file)); 365 break; 366 } 367 } 368 if ((fd = open(work_abi_file, O_RDONLY)) >= 0) { 369 break; 370 } 371 /* if the ABI_FILE was provided we only care about it */ 372 if (i == 0) 373 break; 374 } 375 if (fd == -1) { 376 pkg_emit_error( 377 "Unable to determine the ABI, none of the ABI_FILEs can be read."); 378 return EPKG_FATAL; 379 } 380 381 382 int ret = pkg_elf_abi_from_fd(fd, abi); 383 if (EPKG_OK != ret) { 384 if (-1 == lseek(fd, 0, SEEK_SET)) { 385 pkg_emit_errno("Error seeking file", work_abi_file); 386 ret = EPKG_FATAL; 387 goto close_out; 388 } 389 390 enum pkg_arch arch_hint = PKG_ARCH_UNKNOWN; 391 if (work_arch_hint[0]) { 392 arch_hint = pkg_arch_from_string(PKG_OS_DARWIN, work_arch_hint); 393 if (arch_hint == PKG_ARCH_UNKNOWN) { 394 pkg_emit_error("Invalid ABI_FILE architecture hint %s", 395 work_arch_hint); 396 ret = EPKG_FATAL; 397 goto close_out; 398 } 399 } 400 401 ret = pkg_macho_abi_from_fd(fd, abi, arch_hint); 402 if (EPKG_OK != ret) { 403 pkg_emit_error( 404 "Unable to determine ABI, %s cannot be parsed.", 405 work_abi_file); 406 ret = EPKG_FATAL; 407 goto close_out; 408 } 409 } 410 411 close_out: 412 if (close(fd)) { 413 pkg_emit_errno("Error closing file", work_abi_file); 414 ret = EPKG_FATAL; 415 } 416 return ret; 417 } 418 419 int 420 pkg_arch_to_legacy(const char *arch, char *dest, size_t sz) 421 { 422 int i = 0; 423 const struct arch_trans *arch_trans; 424 425 memset(dest, '\0', sz); 426 /* Lower case the OS */ 427 while (arch[i] != ':' && arch[i] != '\0') { 428 dest[i] = tolower(arch[i]); 429 i++; 430 } 431 if (arch[i] == '\0') 432 return (0); 433 434 dest[i++] = ':'; 435 436 /* Copy the version */ 437 while (arch[i] != ':' && arch[i] != '\0') { 438 dest[i] = arch[i]; 439 i++; 440 } 441 if (arch[i] == '\0') 442 return (0); 443 444 dest[i++] = ':'; 445 446 for (arch_trans = machine_arch_translation; arch_trans->elftype != NULL; 447 arch_trans++) { 448 if (STREQ(arch + i, arch_trans->archid)) { 449 strlcpy(dest + i, arch_trans->elftype, 450 sz - (arch + i - dest)); 451 return (0); 452 } 453 } 454 strlcpy(dest + i, arch + i, sz - (arch + i - dest)); 455 456 return (0); 457 } 458 459 void 460 pkg_cleanup_shlibs_required(struct pkg *pkg, charv_t *internal_provided) 461 { 462 struct pkg_file *file = NULL; 463 const char *lib; 464 465 vec_foreach(pkg->shlibs_required, i) { 466 char *s = pkg->shlibs_required.d[i]; 467 if (charv_search(&pkg->shlibs_provided, s) != NULL || 468 charv_search(internal_provided, s) != NULL) { 469 pkg_debug(2, 470 "remove %s from required shlibs as the " 471 "package %s provides this library itself", 472 s, pkg->name); 473 vec_remove_and_free(&pkg->shlibs_required, i, free); 474 i--; 475 continue; 476 } 477 if (charv_search(&pkg->shlibs_required_ignore, s) != NULL) { 478 pkg_debug(2, 479 "remove %s from required shlibs for package %s as it " 480 "is listed in shlibs_required_ignore.", 481 s, pkg->name); 482 vec_remove_and_free(&pkg->shlibs_required, i, free); 483 i--; 484 continue; 485 } 486 if (match_ucl_lists(s, 487 pkg_config_get("SHLIB_REQUIRE_IGNORE_GLOB"), 488 pkg_config_get("SHLIB_REQUIRE_IGNORE_REGEX"))) { 489 pkg_debug(2, 490 "remove %s from required shlibs for package %s as it " 491 "is matched by SHLIB_REQUIRE_IGNORE_GLOB/REGEX.", 492 s, pkg->name); 493 vec_remove(&pkg->shlibs_required, i); 494 if (charv_insert_sorted(&pkg->shlibs_required_ignore, s) != NULL) { 495 free(s); 496 } 497 i--; 498 continue; 499 } 500 file = NULL; 501 while (pkg_files(pkg, &file) == EPKG_OK) { 502 if ((lib = strstr(file->path, s)) != NULL && 503 strlen(lib) == strlen(s) && lib[-1] == '/') { 504 pkg_debug(2, 505 "remove %s from required shlibs as " 506 "the package %s provides this file itself", 507 s, pkg->name); 508 509 vec_remove_and_free(&pkg->shlibs_required, i, 510 free); 511 i--; 512 break; 513 } 514 } 515 } 516 } 517 518 int 519 pkg_analyse_files(struct pkgdb *db __unused, struct pkg *pkg, const char *stage) 520 { 521 struct pkg_file *file = NULL; 522 int ret = EPKG_OK; 523 char fpath[MAXPATHLEN + 1]; 524 bool failures = false; 525 526 int (*pkg_analyse_init)(const char *stage); 527 int (*pkg_analyse)(const bool developer_mode, struct pkg *pkg, 528 const char *fpath, char **provided, enum pkg_shlib_flags *flags); 529 int (*pkg_analyse_close)(void); 530 531 if (0 == strncmp(pkg->abi, "Darwin", 6)) { 532 pkg_analyse_init=pkg_analyse_init_macho; 533 pkg_analyse=pkg_analyse_macho; 534 pkg_analyse_close=pkg_analyse_close_macho; 535 } else { 536 pkg_analyse_init=pkg_analyse_init_elf; 537 pkg_analyse=pkg_analyse_elf; 538 pkg_analyse_close=pkg_analyse_close_elf; 539 } 540 541 if (vec_len(&pkg->shlibs_required) != 0) { 542 vec_free_and_free(&pkg->shlibs_required, free); 543 } 544 545 if (vec_len(&pkg->shlibs_provided) != 0) { 546 vec_free_and_free(&pkg->shlibs_provided, free); 547 } 548 549 ret = pkg_analyse_init(stage); 550 if (ret != EPKG_OK) { 551 goto cleanup; 552 } 553 554 /* Assume no architecture dependence, for contradiction */ 555 if (ctx.developer_mode) 556 pkg->flags &= ~(PKG_CONTAINS_ELF_OBJECTS | 557 PKG_CONTAINS_STATIC_LIBS | PKG_CONTAINS_LA); 558 559 /* shlibs that are provided by files in the package but not matched by 560 SHLIB_PROVIDE_PATHS_* are still used to filter the shlibs 561 required by the package */ 562 charv_t internal_provided = vec_init(); 563 /* list of shlibs that are in the path to be evaluated for provided but are symlinks */ 564 charv_t maybe_provided = vec_init(); 565 566 while (pkg_files(pkg, &file) == EPKG_OK) { 567 struct stat st; 568 if (stage != NULL) 569 snprintf(fpath, sizeof(fpath), "%s/%s", stage, 570 file->path); 571 else 572 strlcpy(fpath, file->path, sizeof(fpath)); 573 574 char *provided = NULL; 575 enum pkg_shlib_flags provided_flags = PKG_SHLIB_FLAGS_NONE; 576 577 ret = pkg_analyse(ctx.developer_mode, pkg, fpath, &provided, &provided_flags); 578 if (EPKG_WARN == ret) { 579 failures = true; 580 } 581 582 if (provided != NULL) { 583 const ucl_object_t *paths = NULL; 584 585 switch (provided_flags) { 586 case PKG_SHLIB_FLAGS_NONE: 587 paths = pkg_config_get("SHLIB_PROVIDE_PATHS_NATIVE"); 588 break; 589 case PKG_SHLIB_FLAGS_COMPAT_32: 590 paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_32"); 591 break; 592 case PKG_SHLIB_FLAGS_COMPAT_LINUX: 593 paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_LINUX"); 594 break; 595 case PKG_SHLIB_FLAGS_COMPAT_LINUX_32: 596 paths = pkg_config_get("SHLIB_PROVIDE_PATHS_COMPAT_LINUX_32"); 597 break; 598 default: 599 assert(0); 600 } 601 assert(paths != NULL); 602 603 if (lstat(fpath, &st) != 0) { 604 pkg_emit_errno("lstat() failed for", fpath); 605 free(provided); 606 continue; 607 } 608 /* If the corresponding PATHS option isn't set (i.e. an empty ucl array) 609 don't do any filtering for backwards compatibility. */ 610 if (ucl_array_size(paths) == 0 || pkg_match_paths_list(paths, file->path)) { 611 if (S_ISREG(st.st_mode)) { 612 pkg_addshlib_provided(pkg, provided, provided_flags); 613 } else { 614 vec_push(&maybe_provided, pkg_shlib_name_with_flags(provided, provided_flags)); 615 } 616 } else { 617 vec_push(&internal_provided, pkg_shlib_name_with_flags(provided, provided_flags)); 618 } 619 free(provided); 620 } 621 } 622 623 vec_foreach(maybe_provided, i) { 624 vec_foreach(internal_provided, j) { 625 if (STREQ(maybe_provided.d[i], internal_provided.d[j])) { 626 pkg_addshlib_provided(pkg, maybe_provided.d[i], PKG_SHLIB_FLAGS_NONE); 627 vec_remove_and_free(&internal_provided, j, free); 628 j--; 629 } 630 } 631 vec_remove_and_free(&maybe_provided, i, free); 632 i--; 633 } 634 vec_free(&maybe_provided); 635 /* 636 * Do not depend on libraries that a package provides itself 637 */ 638 pkg_cleanup_shlibs_required(pkg, &internal_provided); 639 while (internal_provided.len > 0) { 640 char *s = vec_pop(&internal_provided); 641 if (charv_insert_sorted(&pkg->shlibs_provided_ignore, s) != NULL) { 642 free(s); 643 } 644 } 645 646 vec_foreach(pkg->shlibs_provided, i) { 647 char *s = pkg->shlibs_provided.d[i]; 648 if (charv_search(&pkg->shlibs_provided_ignore, s) != NULL) { 649 pkg_debug(2, 650 "remove %s from provided shlibs for package %s as it " 651 "is listed in shlibs_provided_ignore.", 652 s, pkg->name); 653 vec_remove_and_free(&pkg->shlibs_provided, i, free); 654 i--; 655 continue; 656 } 657 if (match_ucl_lists(s, 658 pkg_config_get("SHLIB_PROVIDE_IGNORE_GLOB"), 659 pkg_config_get("SHLIB_PROVIDE_IGNORE_REGEX"))) { 660 pkg_debug(2, 661 "remove %s from provided shlibs for package %s as it " 662 "is matched by SHLIB_PROVIDE_IGNORE_GLOB/REGEX.", 663 s, pkg->name); 664 vec_remove(&pkg->shlibs_provided, i); 665 if (charv_insert_sorted(&pkg->shlibs_provided_ignore, s) != NULL) { 666 free(s); 667 } 668 i--; 669 continue; 670 } 671 } 672 673 /* 674 * if the package is not supposed to provide share libraries then 675 * drop the provided one 676 */ 677 if (pkg_kv_get(&pkg->annotations, "no_provide_shlib") != NULL) { 678 vec_free_and_free(&pkg->shlibs_provided, free); 679 } 680 681 if (failures) 682 goto cleanup; 683 684 ret = EPKG_OK; 685 686 cleanup: 687 ret = pkg_analyse_close(); 688 689 return (ret); 690 }