pkgbase.c
1 /*- 2 * Copyright (c) 1998 John D. Polstra 3 * Copyright (c) 2012 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 12 #include <assert.h> 13 #include <ctype.h> 14 #include <dirent.h> 15 #include <errno.h> 16 #include <string.h> 17 18 #include "pkg.h" 19 #include "pkghash.h" 20 #include "private/event.h" 21 #include "private/pkg.h" 22 #include "private/pkgdb.h" 23 #include "private/utils.h" 24 #include "xmalloc.h" 25 26 struct pkgbase { 27 charv_t system_shlibs; 28 /* 29 * unused yet but will be in the future when we will start using 30 * provides/requires in pkgbase 31 */ 32 charv_t provides; 33 bool ignore_compat32; 34 }; 35 36 static int 37 scan_dir_for_shlibs(charv_t *shlib_list, const char *dir, 38 enum pkg_shlib_flags flags) 39 { 40 DIR *dirp= opendir(dir); 41 if (dirp == NULL) { 42 if (errno == ENOENT) { 43 return (EPKG_OK); 44 } 45 pkg_errno("Failed to open '%s' to scan for shared libraries", dir); 46 return (EPKG_FATAL); 47 } 48 49 struct dirent *dp; 50 size_t cnt = 0; 51 while ((dp = readdir(dirp)) != NULL) { 52 /* Only regular files and sym-links. On some 53 filesystems d_type is not set, on these the d_type 54 field will be DT_UNKNOWN. */ 55 if (dp->d_type != DT_REG && dp->d_type != DT_LNK && 56 dp->d_type != DT_UNKNOWN) 57 continue; 58 59 int len = strlen(dp->d_name); 60 /* Name can't be shorter than "libx.so" */ 61 if (len < 7 || strncmp(dp->d_name, "lib", 3) != 0) 62 continue; 63 64 const char *vers = dp->d_name + len; 65 while (vers > dp->d_name && 66 (isdigit(*(vers-1)) || *(vers-1) == '.')) 67 vers--; 68 if (vers == dp->d_name + len) { 69 if (strncmp(vers - 3, ".so", 3) != 0) 70 continue; 71 } else if (vers < dp->d_name + 3 || 72 strncmp(vers - 3, ".so.", 4) != 0) 73 continue; 74 75 /* We have a valid shared library name. */ 76 char *full = pkg_shlib_name_with_flags(dp->d_name, flags); 77 vec_push(shlib_list, full); 78 cnt++; 79 } 80 if (cnt == 0) 81 errno = ENOENT; 82 83 closedir(dirp); 84 85 return (EPKG_OK); 86 } 87 88 static const struct { 89 const char *dir; 90 enum pkg_shlib_flags flags; 91 } system_shlib_table[] = { 92 {"/lib", PKG_SHLIB_FLAGS_NONE }, 93 {"/usr/lib", PKG_SHLIB_FLAGS_NONE }, 94 {"/usr/lib32", PKG_SHLIB_FLAGS_COMPAT_32 }, 95 }; 96 97 int 98 scan_system_shlibs(charv_t *system_shlibs, const char *rootdir) 99 { 100 int r = EPKG_OK; 101 for (size_t i = 0; i < NELEM(system_shlib_table); i++) { 102 char *dir; 103 if (rootdir != NULL) { 104 xasprintf(&dir, "%s%s", rootdir, system_shlib_table[i].dir); 105 } else { 106 dir = xstrdup(system_shlib_table[i].dir); 107 } 108 errno = 0; 109 int ret = scan_dir_for_shlibs(system_shlibs, dir, system_shlib_table[i].flags); 110 free(dir); 111 if (errno == ENOENT) 112 r = EPKG_NOCOMPAT32; 113 if (ret != EPKG_OK) { 114 return (ret); 115 } 116 } 117 if (system_shlibs->d) 118 qsort(system_shlibs->d, system_shlibs->len, sizeof(char *), char_cmp); 119 120 return (r); 121 } 122 123 struct pkgbase * 124 pkgbase_new(struct pkgdb *db) 125 { 126 struct pkgbase *pb = xcalloc(1, sizeof(*pb)); 127 128 if (!pkgdb_file_exists(db, "/usr/bin/uname")) 129 if (scan_system_shlibs(&pb->system_shlibs, ctx.pkg_rootdir) == EPKG_NOCOMPAT32) 130 pb->ignore_compat32 = true; 131 132 return (pb); 133 } 134 135 void 136 pkgbase_free(struct pkgbase *pb) 137 { 138 if (pb == NULL) 139 return; 140 vec_free_and_free(&pb->system_shlibs, free); 141 vec_free_and_free(&pb->provides, free); 142 free(pb); 143 } 144 145 bool 146 pkgbase_provide_shlib(struct pkgbase *pb, const char *shlib) 147 { 148 if (pb->ignore_compat32 && str_ends_with(shlib, ":32")) 149 return (true); 150 return (charv_search(&pb->system_shlibs, shlib) != NULL); 151 } 152 153 bool 154 pkgbase_provide(struct pkgbase *pb, const char *provide) 155 { 156 return (charv_search(&pb->provides, provide) != NULL); 157 }