├── Makefile ├── README.md ├── bigram.c ├── bigram.h ├── configure.c ├── mkpath.c ├── pkg-provides.8 ├── poudriere ├── README.md ├── pkgrepo.sh └── provides-db-gen.rb ├── progressbar.c └── provides.c /Makefile: -------------------------------------------------------------------------------- 1 | .include 2 | 3 | PREFIX?= /usr/local 4 | LIBDIR= ${PREFIX}/lib/pkg 5 | SHLIB_DIR?= ${LIBDIR}/ 6 | SHLIB_NAME?= ${PLUGIN_NAME}.so 7 | 8 | PLUGIN_NAME= provides 9 | SRCS= provides.c progressbar.c mkpath.c configure.c bigram.c 10 | 11 | CFLAGS+= -I /usr/local/include 12 | LDFLAGS+= -L /usr/lib -L /usr/local/lib -lc -lfetch -lpcre2-8 -lutil 13 | 14 | .include 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://pkg-provides.osorio.me/pkg-provides-logo.png) 2 | 3 | pkg-provides is a pkg plugin for querying which package will provide you a particular file 4 | 5 | http://pkg-provides.osorio.me 6 | -------------------------------------------------------------------------------- /bigram.c: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-4-Clause 3 | * 4 | * Copyright (c) 1995 Wolfram Schneider . Berlin. 5 | * Copyright (c) 1989, 1993 6 | * The Regents of the University of California. All rights reserved. 7 | * 8 | * This code is derived from software contributed to Berkeley by 9 | * James A. Woods. 10 | * 11 | * Redistribution and use in source and binary forms, with or without 12 | * modification, are permitted provided that the following conditions 13 | * are met: 14 | * 1. Redistributions of source code must retain the above copyright 15 | * notice, this list of conditions and the following disclaimer. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 3. All advertising materials mentioning features or use of this software 20 | * must display the following acknowledgement: 21 | * This product includes software developed by the University of 22 | * California, Berkeley and its contributors. 23 | * 4. Neither the name of the University nor the names of its contributors 24 | * may be used to endorse or promote products derived from this software 25 | * without specific prior written permission. 26 | * 27 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 28 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 29 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 30 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 31 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 32 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 33 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 34 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 35 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 36 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 37 | * SUCH DAMAGE. 38 | * 39 | * $FreeBSD$ 40 | */ 41 | 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | #include 50 | 51 | #include "bigram.h" 52 | 53 | char separator='\n'; /* line separator */ 54 | 55 | /* 56 | * Validate bigram chars. If the test failed the database is corrupt 57 | * or the database is obviously not a locate database. 58 | */ 59 | int 60 | check_bigram_char(int ch) 61 | { 62 | /* legal bigram: 0, ASCII_MIN ... ASCII_MAX */ 63 | if (ch == 0 || 64 | (ch >= ASCII_MIN && ch <= ASCII_MAX)) 65 | return(ch); 66 | 67 | fprintf(stderr, "Provides database corrupted, perform a forced update to correct it.\n"); 68 | exit (1); 69 | } 70 | 71 | /* 72 | * Read integer from mmap pointer. 73 | * Essentially a simple ``return *(int *)p'' but avoids sigbus 74 | * for integer alignment (SunOS 4.x, 5.x). 75 | * 76 | * Convert network byte order to host byte order if necessary. 77 | * So we can read a locate database on FreeBSD/i386 (little endian) 78 | * which was built on SunOS/sparc (big endian). 79 | */ 80 | 81 | int 82 | getwm(caddr_t p) 83 | { 84 | union { 85 | char buf[INTSIZE]; 86 | int i; 87 | } u; 88 | register int i, hi; 89 | 90 | for (i = 0; i < (int)INTSIZE; i++) 91 | u.buf[i] = *p++; 92 | 93 | i = u.i; 94 | 95 | if (i > MAXPATHLEN || i < -(MAXPATHLEN)) { 96 | hi = ntohl(i); 97 | if (hi > MAXPATHLEN || hi < -(MAXPATHLEN)) { 98 | fprintf(stderr, "integer out of +-MAXPATHLEN (%d): %u", 99 | MAXPATHLEN, abs(i) < abs(hi) ? i : hi); 100 | exit (1); 101 | } 102 | return(hi); 103 | } 104 | return(i); 105 | } 106 | 107 | /* 108 | * Read integer from stream. 109 | * 110 | * Convert network byte order to host byte order if necessary. 111 | * So we can read on FreeBSD/i386 (little endian) a locate database 112 | * which was built on SunOS/sparc (big endian). 113 | */ 114 | 115 | int 116 | getwf(FILE *fp) 117 | { 118 | register int word, hword; 119 | 120 | word = getw(fp); 121 | 122 | if (word > MAXPATHLEN || word < -(MAXPATHLEN)) { 123 | hword = ntohl(word); 124 | if (hword > MAXPATHLEN || hword < -(MAXPATHLEN)) 125 | errx(1, "integer out of +-MAXPATHLEN (%d): %u", 126 | MAXPATHLEN, abs(word) < abs(hword) ? word : hword); 127 | return(hword); 128 | } 129 | return(word); 130 | } 131 | 132 | int 133 | bigram_expand(FILE *fp, int (*match_cb)(char *,void *), void * extra) 134 | { 135 | register u_char *p, *s; 136 | register int c; 137 | int count, found, globflag; 138 | u_char *cutoff; 139 | u_char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN]; 140 | 141 | /* use a lookup table for case insensitive search */ 142 | u_char table[UCHAR_MAX + 1]; 143 | 144 | /* init bigram table */ 145 | for (c = 0, p = bigram1, s = bigram2; c < NBG; c++) { 146 | p[c] = check_bigram_char(getc(fp)); 147 | s[c] = check_bigram_char(getc(fp)); 148 | } 149 | 150 | /* main loop */ 151 | found = count = 0; 152 | 153 | c = getc(fp); 154 | for (; c != EOF; ) { 155 | 156 | /* go forward or backward */ 157 | if (c == SWITCH) { /* big step, an integer */ 158 | count += getwf(fp) - OFFSET; 159 | } else { /* slow step, =< 14 chars */ 160 | count += c - OFFSET; 161 | } 162 | 163 | if (count < 0 || count > MAXPATHLEN) 164 | return (-1); 165 | /* overlay old path */ 166 | p = path + count; 167 | 168 | for (;;) { 169 | c = getc(fp); 170 | /* 171 | * == UMLAUT: 8 bit char followed 172 | * <= SWITCH: offset 173 | * >= PARITY: bigram 174 | * rest: single ascii char 175 | * 176 | * offset < SWITCH < UMLAUT < ascii < PARITY < bigram 177 | */ 178 | if (c < PARITY) { 179 | if (c <= UMLAUT) { 180 | if (c == UMLAUT) { 181 | c = getc(fp); 182 | } else 183 | break; /* SWITCH */ 184 | } 185 | *p++ = c; 186 | } 187 | else { 188 | /* bigrams are parity-marked */ 189 | TO7BIT(c); 190 | 191 | *p++ = bigram1[c]; 192 | *p++ = bigram2[c]; 193 | } 194 | } 195 | *p-- = '\0'; 196 | match_cb((char *)path, extra); 197 | } 198 | 199 | return (0); 200 | } 201 | -------------------------------------------------------------------------------- /bigram.h: -------------------------------------------------------------------------------- 1 | /* 2 | * SPDX-License-Identifier: BSD-4-Clause 3 | * 4 | * Copyright (c) 1995 Wolfram Schneider . Berlin. 5 | * Copyright (c) 1989, 1993 6 | * The Regents of the University of California. 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 | * 2. Redistributions in binary form must reproduce the above copyright 14 | * notice, this list of conditions and the following disclaimer in the 15 | * documentation and/or other materials provided with the distribution. 16 | * 3. All advertising materials mentioning features or use of this software 17 | * must display the following acknowledgement: 18 | * This product includes software developed by the University of 19 | * California, Berkeley and its contributors. 20 | * 4. Neither the name of the University nor the names of its contributors 21 | * may be used to endorse or promote products derived from this software 22 | * without specific prior written permission. 23 | * 24 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 25 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 28 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 30 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 31 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 32 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 33 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 34 | * SUCH DAMAGE. 35 | * 36 | * @(#)locate.h 8.1 (Berkeley) 6/6/93 37 | * $FreeBSD$ 38 | */ 39 | 40 | /* Symbolic constants shared by locate.c and code.c */ 41 | 42 | #define NBG 128 /* number of bigrams considered */ 43 | #define OFFSET 14 /* abs value of max likely diff */ 44 | #define PARITY 0200 /* parity bit */ 45 | #define SWITCH 30 /* switch code */ 46 | #define UMLAUT 31 /* an 8 bit char followed */ 47 | 48 | /* 0-28 likeliest differential counts + offset to make nonnegative */ 49 | #define LDC_MIN 0 50 | #define LDC_MAX 28 51 | 52 | /* 128-255 bigram codes (128 most common, as determined by 'updatedb') */ 53 | #define BIGRAM_MIN (UCHAR_MAX - SCHAR_MAX) 54 | #define BIGRAM_MAX UCHAR_MAX 55 | 56 | /* 32-127 single character (printable) ascii residue (ie, literal) */ 57 | #define ASCII_MIN 32 58 | #define ASCII_MAX SCHAR_MAX 59 | 60 | /* #define TO7BIT(x) (x = ( ((u_char)x) & SCHAR_MAX )) */ 61 | #define TO7BIT(x) (x = x & SCHAR_MAX ) 62 | 63 | 64 | #if UCHAR_MAX >= 4096 65 | define TOLOWER(ch) tolower(ch) 66 | #else 67 | 68 | u_char myctype[UCHAR_MAX + 1]; 69 | #define TOLOWER(ch) (myctype[ch]) 70 | #endif 71 | 72 | #define INTSIZE (sizeof(int)) 73 | -------------------------------------------------------------------------------- /configure.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2017 Rodrigo Osorio 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer 10 | * in this position and unchanged. 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(S) ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define PKG_DB_URL "https://pkg-provides.osorio.me" 33 | 34 | static char * url = NULL; 35 | static char * filepath = NULL; 36 | 37 | int 38 | config_fetch_on_update() 39 | { 40 | const char * str = getenv("PROVIDES_FETCH_ON_UPDATE"); 41 | if (str != NULL && strcasecmp(str,"no") == 0) { 42 | return (0); 43 | } 44 | 45 | return (1); 46 | } 47 | 48 | char * 49 | config_get_remote_srv() 50 | { 51 | const char * env; 52 | 53 | if (url == NULL) { 54 | env = getenv("PROVIDES_SRV"); 55 | if (env == NULL) { 56 | /* DEPRECATED, must be removed */ 57 | env = getenv("PROVIDES_URL"); 58 | if (env) { 59 | fprintf(stderr, "Warning: PROVIDES_URL environment variable is deprecated, use PROVIDES_SRV instead\n"); 60 | } 61 | } 62 | url = strdup((env != NULL) ? env : PKG_DB_URL); 63 | if (url == NULL) { 64 | exit(ENOMEM); 65 | } 66 | } 67 | return (url); 68 | } 69 | 70 | 71 | char * 72 | config_get_filepath() 73 | { 74 | const char * env; 75 | if (filepath == NULL) { 76 | env = getenv("PROVIDES_FILEPATH"); 77 | filepath = env ? strdup(env) : NULL; 78 | } 79 | return filepath; 80 | } 81 | -------------------------------------------------------------------------------- /mkpath.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 1983, 1992, 1993 3 | * The Regents of the University of California. All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer. 10 | * 2. Redistributions in binary form must reproduce the above copyright 11 | * notice, this list of conditions and the following disclaimer in the 12 | * documentation and/or other materials provided with the distribution. 13 | * 3. Neither the name of the University nor the names of its contributors 14 | * may be used to endorse or promote products derived from this software 15 | * without specific prior written permission. 16 | * 17 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | * SUCH DAMAGE. 28 | * 29 | * $OpenBSD: mkpath.c,v 1.2 2005/06/20 07:14:06 otto Exp $ 30 | * $FreeBSD: stable/11/usr.bin/patch/mkpath.c 246091 2013-01-29 20:05:16Z delphij $ 31 | */ 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | 39 | int mkpath(char *); 40 | 41 | /* Code taken directly from mkdir(1). 42 | 43 | * mkpath -- create directories. 44 | * path - path 45 | */ 46 | int 47 | mkpath(char *path) 48 | { 49 | struct stat sb; 50 | char *slash; 51 | int done = 0; 52 | 53 | slash = path; 54 | 55 | while (!done) { 56 | slash += strspn(slash, "/"); 57 | slash += strcspn(slash, "/"); 58 | 59 | done = (*slash == '\0'); 60 | *slash = '\0'; 61 | 62 | if (stat(path, &sb)) { 63 | if (errno != ENOENT || (mkdir(path, 0755) && 64 | errno != EEXIST)) { 65 | warn("%s", path); 66 | return (-1); 67 | } 68 | } else if (!S_ISDIR(sb.st_mode)) { 69 | warnx("%s: %s", path, strerror(ENOTDIR)); 70 | return (-1); 71 | } 72 | 73 | *slash = '/'; 74 | } 75 | 76 | return (0); 77 | } 78 | -------------------------------------------------------------------------------- /pkg-provides.8: -------------------------------------------------------------------------------- 1 | .\" 2 | .\" Copyright (c) 2018 Rodrigo Osorio 3 | .\" 4 | .\" Permission to use, copy, modify, and distribute this software for any 5 | .\" purpose with or without fee is hereby granted, provided that the above 6 | .\" copyright notice and this permission notice appear in all copies. 7 | .\" 8 | .\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 | .\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 | .\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 | .\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 | .\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 | .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 | .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 | .\" 16 | .Dd February 9, 2020 17 | .Dt PKG-PROVIDES 8 18 | .Os 19 | .Sh NAME 20 | .Nm "pkg provides" 21 | .Nd query which package provides a file matching a particular pattern 22 | .Sh SYNOPSIS 23 | .Nm 24 | .Op Fl u 25 | .Op Fl f 26 | .Nm 27 | .Op Fl r Ar repo 28 | .Ar pattern 29 | .Sh DESCRIPTION 30 | .Nm 31 | is used to query which package in your pkg catalog provides a particular 32 | file given a pattern. 33 | .Pp 34 | The search pattern can be any perl compatible regular expression (PCRE). 35 | .Sh OPTIONS 36 | The following options are supported by 37 | .Nm : 38 | .Bl -tag -width repository 39 | .It Fl u 40 | Check if a new database file is available and then perform the update. 41 | .It Fl f 42 | Force the update. 43 | .It Fl r Ar repo 44 | Restrict search results to a specific repository. 45 | .It Sy pattern 46 | Can be any perl compatible regular expression (PCRE). The search is not case sensitive. 47 | .El 48 | .Sh ENVIRONMENT 49 | .Bl -tag -width "PROVIDES_FETCH_ON_UPDATE" 50 | .It PROVIDES_FETCH_ON_UPDATE 51 | If set to "NO", it disables the default behaviour and doesn't perform a 52 | .Nm 53 | database update after updating pkg. 54 | .It PROVIDES_SRV 55 | When set, overrides the default 56 | .Nm 57 | server address. 58 | The default value is "https://pkg-provides.osorio.me". 59 | .It PROVIDES_FILEPATH 60 | When set, overrides the default database file location in the remote server. 61 | The filepath format is the following : v3/{osname}/{osver}:{arch}. 62 | The default value is "v3/FreeBSD/12:amd64" for a FreeBSD 12 and "v3/DragonFly/6.2:x86:64" 63 | for a DragonFly 6.2. 64 | .It PROVIDES_URL 65 | This environment variable is \fBdeprecated\fP. Use \fBPROVIDES_SRV\fP instead. 66 | .El 67 | .Sh EXIT STATUS 68 | .Ex -std 69 | .Sh EXAMPLES 70 | Update the provides database: 71 | .Pp 72 | .Dl $ pkg provides -u 73 | .Pp 74 | Search for a package that provides bin/firefox 75 | .Pp 76 | .Dl $ pkg provides bin/firefox$ 77 | .Pp 78 | Search for packages that provide a file with the pattern libbz2.so.* 79 | .Pp 80 | .Dl $ pkg provides ^libbz2.so. 81 | .Pp 82 | Look for bin/firefox but only in the 83 | .Fx 84 | repository 85 | .Pp 86 | .Dl $ pkg provides -r FreeBSD bin/firefox$ 87 | .Sh AUTHORS 88 | .An -nosplit 89 | .Nm 90 | .An was written by Rodrigo Osorio . 91 | -------------------------------------------------------------------------------- /poudriere/README.md: -------------------------------------------------------------------------------- 1 | 2 | # How to generate your pkg-provides database using poudriere hooks 3 | 4 | This directory contains: 5 | * The pkgrepo.sh hook script to generate the pkg-provides database before signing the repo 6 | * A ruby script who parses your packages to extract the plist and generate a pkg-provides database 7 | 8 | ## requirements 9 | 10 | List of packages required to generate the pkg-provides database 11 | * rubygem-zstd-ruby 12 | * rubygem-ruby-xz 13 | 14 | ## Benchmarking 15 | 16 | CPU: Intel(R) Core(TM) i5-6500 CPU @ 3.20GHz (3200.00-MHz K8-class CPU) 17 | hw.physmem: 34185838592 18 | hw.usermem: 22954500096 19 | hw.realmem: 34359738368 20 | 21 | For a subset of 245 packages it takes about 66 seconds to generate the pkg-provides database with 4 threads. 22 | 23 | The script uses the poudriere PARALLEL_JOBS environment variable to parallelize the process. 24 | 25 | ## How to make it works 26 | 27 | 1. cp pkgrepo.sh provides-db-gen.rb /usr/local/etc/poudriere.d/hooks/ 28 | 2. chmod +x /usr/local/etc/poudriere.d/hooks/pkgrepo.sh /usr/local/etc/poudriere.d/hooks/provides-db-gen.rb 29 | 3. run a bulk build with poudriere! 30 | 4. the provides.db.xz file will be stored in the package direcory at the same place as packagesite.pkg 31 | 32 | ## Integrate with the pkg-provides plugin 33 | 34 | The pkg-plugin uses a set of environment variables to overload some default variables: 35 | 36 | |Variable | Default value | Meaning | 37 | |-------- | ------------- | ------- | 38 | | PROVIDES_SRV | https://pkg-provides.osorio.me | server where the pkg-provides database is stored | 39 | | PROVIDES_FILEPATH | v3/FreeBSD/12:amd64 (for a FreeBSD 12 amd64) | location of the provides.db.xz file | 40 | 41 | 42 | If the following command works, you are fine. 43 | fetch ${PROVIDES_SRV}/${PROVIDES_FILEPATH}/provides.db.xz 44 | -------------------------------------------------------------------------------- /poudriere/pkgrepo.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright (c) 2010-2013 Baptiste Daroussin 4 | # Copyright (c) 2010-2011 Julien Laffaye 5 | # Copyright (c) 2012-2017 Bryan Drewery 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 | # 2. Redistributions in binary form must reproduce the above copyright 14 | # notice, this list of conditions and the following disclaimer in the 15 | # documentation and/or other materials provided with the distribution. 16 | # 17 | # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 18 | # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 21 | # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23 | # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24 | # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25 | # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26 | # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27 | # SUCH DAMAGE. 28 | 29 | PKGPATH=$2 30 | 31 | . ${SCRIPTPREFIX}/common.sh 32 | 33 | mkdir ${POUDRIERE_DATA}/cache/${MASTERNAME}/provides_db 34 | 35 | msg "pkg-provides: start building the database using ${PARALLEL_JOBS} threads" 36 | 37 | msg "pkg-provides: extracting file list form packages" 38 | /usr/local/etc/poudriere.d/hooks/provides-db-gen.rb ${PKGPATH}/All ${PARALLEL_JOBS} > ${POUDRIERE_DATA}/cache/${MASTERNAME}/provides_db/provides.data 39 | 40 | msg "pkg-provides: sorting and packaging the database" 41 | sort ${POUDRIERE_DATA}/cache/${MASTERNAME}/provides_db/provides.data | /usr/libexec/locate.mklocatedb | xz > ${PKGPATH}/provides.db.xz 42 | 43 | msg "pkg-provides: database generation complete" 44 | exit 0 45 | -------------------------------------------------------------------------------- /poudriere/provides-db-gen.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'zstd-ruby' 3 | require 'xz' 4 | require 'json' 5 | require 'rubygems/package' 6 | 7 | $wlock = Mutex.new 8 | $count = 0 9 | 10 | def extract_manifest_file(path) 11 | comp = File.open(path, "rb"){|f| f.read} 12 | begin 13 | uncompressed = StringIO.new(Zstd.decompress(comp)) 14 | rescue 15 | uncompressed = StringIO.new(XZ.decompress(comp)) 16 | end 17 | Gem::Package::TarReader.new(uncompressed) do |tar| 18 | tar.each do |entry| 19 | if entry.full_name == "+MANIFEST" 20 | StringIO.new(entry.read).each do |line| 21 | return line 22 | end 23 | end 24 | end 25 | end 26 | end 27 | 28 | def process_file(path) 29 | dump = extract_manifest_file(path) 30 | json = JSON.parse(dump) 31 | if json.key?("files") 32 | json["files"].keys.each do |file| 33 | puts json["name"] + "*" + file 34 | end 35 | end 36 | $wlock.synchronize { 37 | $count -= 1 38 | } 39 | end 40 | 41 | 42 | $threads = [] 43 | $maxproc = ARGV[1].to_i 44 | 45 | Dir[ARGV[0] + '/*.pkg'].each {|f| 46 | sleep(1) until ($count < $maxproc) 47 | $wlock.synchronize { 48 | if ($count < $maxproc) 49 | puts "Star proc" 50 | $count += 1 51 | $threads << Thread.new { process_file(f) } 52 | end 53 | } 54 | } 55 | 56 | $threads.each { |thr| thr.join } 57 | -------------------------------------------------------------------------------- /progressbar.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2011-2014 Baptiste Daroussin 3 | * Copyright (c) 2011-2012 Julien Laffaye 4 | * Copyright (c) 2011 Will Andrews 5 | * Copyright (c) 2011-2012 Marin Atanasov Nikolov 6 | * Copyright (c) 2014 Vsevolod Stakhov 7 | * Copyright (c) 2015 Matthew Seaman 8 | * All rights reserved. 9 | * 10 | * Redistribution and use in source and binary forms, with or without 11 | * modification, are permitted provided that the following conditions 12 | * are met: 13 | * 1. Redistributions of source code must retain the above copyright 14 | * notice, this list of conditions and the following disclaimer 15 | * in this position and unchanged. 16 | * 2. Redistributions in binary form must reproduce the above copyright 17 | * notice, this list of conditions and the following disclaimer in the 18 | * documentation and/or other materials provided with the distribution. 19 | * 20 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 21 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 22 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 23 | * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 24 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 25 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 29 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | */ 31 | 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | 40 | #define STALL_TIME 5 41 | 42 | static char *progress_message = NULL; 43 | static UT_string *msg_buf = NULL; 44 | static int last_progress_percent = -1; 45 | static bool progress_started = false; 46 | static bool progress_interrupted = false; 47 | static bool progress_debit = false; 48 | static int64_t last_tick = 0; 49 | static int64_t stalled; 50 | static int64_t bytes_per_second; 51 | static time_t last_update; 52 | static time_t begin = 0; 53 | static int add_deps_depth; 54 | static bool signal_handler_installed = false; 55 | static bool quiet = false; 56 | 57 | /* units for format_size */ 58 | static const char *unit_SI[] = { " ", "k", "M", "G", "T", }; 59 | 60 | static void 61 | format_rate_SI(char *buf, int size, off_t bytes) 62 | { 63 | int i; 64 | 65 | bytes *= 100; 66 | for (i = 0; bytes >= 100*1000 && unit_SI[i][0] != 'T'; i++) 67 | bytes = (bytes + 500) / 1000; 68 | if (i == 0) { 69 | i++; 70 | bytes = (bytes + 500) / 1000; 71 | } 72 | snprintf(buf, size, "%3lld.%1lld%s%s", 73 | (long long) (bytes + 5) / 100, 74 | (long long) (bytes + 5) / 10 % 10, 75 | unit_SI[i], 76 | i ? "B" : " "); 77 | } 78 | 79 | void 80 | provides_progressbar_stop(void) 81 | { 82 | if (progress_started) { 83 | if (!isatty(STDOUT_FILENO)) 84 | printf(" done"); 85 | putchar('\n'); 86 | } 87 | last_progress_percent = -1; 88 | progress_started = false; 89 | progress_interrupted = false; 90 | } 91 | 92 | void 93 | provides_progressbar_start(const char *pmsg) { 94 | free(progress_message); 95 | progress_message = NULL; 96 | progress_debit = true; 97 | 98 | if (quiet) 99 | return; 100 | if (pmsg != NULL) 101 | progress_message = strdup(pmsg); 102 | else { 103 | progress_message = strdup(utstring_body(msg_buf)); 104 | } 105 | last_progress_percent = -1; 106 | last_tick = 0; 107 | begin = last_update = time(NULL); 108 | bytes_per_second = 0; 109 | stalled = 0; 110 | 111 | progress_started = true; 112 | progress_interrupted = false; 113 | if (!isatty(STDOUT_FILENO)) 114 | printf("%s: ", progress_message); 115 | else 116 | printf("%s: 0%%", progress_message); 117 | } 118 | 119 | static void 120 | draw_progressbar(int64_t current, int64_t total) 121 | { 122 | int percent; 123 | int64_t transferred; 124 | time_t elapsed = 0, now = 0; 125 | char buf[8]; 126 | int64_t bytes_left; 127 | int cur_speed; 128 | int hours, minutes, seconds; 129 | float age_factor; 130 | 131 | if (!progress_started) { 132 | provides_progressbar_stop(); 133 | return; 134 | } 135 | 136 | if (progress_debit) { 137 | now = time(NULL); 138 | elapsed = (now >= last_update) ? now - last_update : 0; 139 | } 140 | 141 | percent = (total != 0) ? (current * 100. / total) : 100; 142 | 143 | /** 144 | * Wait for interval for debit bars to keep calc per second. 145 | * If not debit, show on every % change, or if ticking after 146 | * an interruption (which removed our progressbar output). 147 | */ 148 | if (current >= total || (progress_debit && elapsed >= 1) || 149 | (!progress_debit && 150 | (percent != last_progress_percent || progress_interrupted))) { 151 | last_progress_percent = percent; 152 | 153 | printf("\r%s: %3d%%", progress_message, percent); 154 | if (progress_debit) { 155 | transferred = current - last_tick; 156 | last_tick = current; 157 | bytes_left = total - current; 158 | if (bytes_left <= 0) { 159 | elapsed = now - begin; 160 | /* Always show at least 1 second at end. */ 161 | if (elapsed == 0) 162 | elapsed = 1; 163 | /* Calculate true total speed when done */ 164 | transferred = total; 165 | bytes_per_second = 0; 166 | } 167 | 168 | if (elapsed != 0) 169 | cur_speed = (transferred / elapsed); 170 | else 171 | cur_speed = transferred; 172 | 173 | #define AGE_FACTOR_SLOW_START 3 174 | if (now - begin <= AGE_FACTOR_SLOW_START) 175 | age_factor = 0.4; 176 | else 177 | age_factor = 0.9; 178 | 179 | if (bytes_per_second != 0) { 180 | bytes_per_second = 181 | (bytes_per_second * age_factor) + 182 | (cur_speed * (1.0 - age_factor)); 183 | } else 184 | bytes_per_second = cur_speed; 185 | 186 | humanize_number(buf, sizeof(buf), 187 | current,"B", HN_AUTOSCALE, HN_IEC_PREFIXES); 188 | printf(" %*s", (int)sizeof(buf), buf); 189 | 190 | if (bytes_left > 0) 191 | format_rate_SI(buf, sizeof(buf), transferred); 192 | else /* Show overall speed when done */ 193 | format_rate_SI(buf, sizeof(buf), 194 | bytes_per_second); 195 | printf(" %s/s ", buf); 196 | 197 | if (!transferred) 198 | stalled += elapsed; 199 | else 200 | stalled = 0; 201 | 202 | if (stalled >= STALL_TIME) 203 | printf(" - stalled -"); 204 | else if (bytes_per_second == 0 && bytes_left > 0) 205 | printf(" --:-- ETA"); 206 | else { 207 | if (bytes_left > 0) 208 | seconds = bytes_left / bytes_per_second; 209 | else 210 | seconds = elapsed; 211 | 212 | hours = seconds / 3600; 213 | seconds -= hours * 3600; 214 | minutes = seconds / 60; 215 | seconds -= minutes * 60; 216 | 217 | if (hours != 0) 218 | printf("%02d:%02d:%02d", hours, 219 | minutes, seconds); 220 | else 221 | printf(" %02d:%02d", minutes, seconds); 222 | 223 | if (bytes_left > 0) 224 | printf(" ETA"); 225 | else 226 | printf(" "); 227 | last_update = now; 228 | } 229 | } 230 | fflush(stdout); 231 | } 232 | if (current >= total) 233 | return provides_progressbar_stop(); 234 | } 235 | 236 | void 237 | provides_progressbar_tick(int64_t current, int64_t total) 238 | { 239 | int percent; 240 | 241 | if (!quiet && progress_started) { 242 | if (isatty(STDOUT_FILENO)) 243 | draw_progressbar(current, total); 244 | else { 245 | if (progress_interrupted) { 246 | printf("%s...", progress_message); 247 | } else if (!getenv("NO_TICK")){ 248 | percent = (total != 0) ? (current * 100. / total) : 100; 249 | if (last_progress_percent / 10 < percent / 10) { 250 | last_progress_percent = percent; 251 | printf("."); 252 | fflush(stdout); 253 | } 254 | } 255 | if (current >= total) 256 | provides_progressbar_stop(); 257 | } 258 | } 259 | progress_interrupted = false; 260 | } 261 | -------------------------------------------------------------------------------- /provides.c: -------------------------------------------------------------------------------- 1 | /*- 2 | * Copyright (c) 2017 Rodrigo Osorio 3 | * All rights reserved. 4 | * 5 | * Redistribution and use in source and binary forms, with or without 6 | * modification, are permitted provided that the following conditions 7 | * are met: 8 | * 1. Redistributions of source code must retain the above copyright 9 | * notice, this list of conditions and the following disclaimer 10 | * in this position and unchanged. 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(S) ``AS IS'' AND ANY EXPRESS OR 16 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 17 | * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 18 | * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 19 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 20 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 21 | * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 22 | * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 23 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 24 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 | */ 26 | 27 | #define PCRE2_CODE_UNIT_WIDTH 8 28 | 29 | #include 30 | #include 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | 44 | static char myname[] = "provides"; 45 | static char myversion[] = "0.7.4"; 46 | static char dbversion[] = "v3"; 47 | static char mydescription[] = "A plugin for querying which package provides a particular file"; 48 | static struct pkg_plugin *self; 49 | bool force_flag = false; 50 | 51 | bool fetch_on_update = true; 52 | 53 | typedef struct file_t { 54 | char *name; 55 | SLIST_ENTRY (file_t) next; 56 | } file_t; 57 | SLIST_HEAD (file_head_t, file_t); 58 | 59 | typedef struct fpkg_t { 60 | char *pkg_name; 61 | struct file_head_t files; 62 | SLIST_ENTRY (fpkg_t) next; 63 | } fpkg_t; 64 | SLIST_HEAD (pkg_head_t, fpkg_t); 65 | 66 | struct search_t { 67 | struct pkg_head_t head; 68 | pcre2_code *regex; 69 | fpkg_t *pnode; 70 | char * pattern; 71 | pcre2_match_data *match_data; 72 | }; 73 | 74 | void provides_progressbar_start(const char *pmsg); 75 | void provides_progressbar_stop(void); 76 | void provides_progressbar_tick(int64_t current, int64_t total); 77 | int mkpath(char *path); 78 | int bigram_expand(FILE *fp, void (*match_cb)(const char *,struct search_t *), void *extra); 79 | 80 | int config_fetch_on_update(); 81 | char *config_get_remote_srv(); 82 | char *config_get_filepath(); 83 | 84 | #define BUFLEN 4096 85 | #define MAX_FN_SIZE 255 86 | #define PKG_DB_PATH "/var/db/pkg/provides/" 87 | 88 | int 89 | pkg_plugin_shutdown(struct pkg_plugin *p __unused) 90 | { 91 | /* nothing to be done here */ 92 | return (EPKG_OK); 93 | } 94 | 95 | void 96 | plugin_provides_usage(void) 97 | { 98 | fprintf(stderr, "usage: pkg %s [-uf] [-r repo] pattern\n\n", myname); 99 | fprintf(stderr, "%s\n", mydescription); 100 | } 101 | 102 | int get_filepath(char *filename, size_t size) 103 | { 104 | static char osver[1024]; 105 | static char arch[1024]; 106 | static char osname[1024]; 107 | int mib_arch[] = { (CTL_HW), (HW_MACHINE_ARCH) }; 108 | int mib_ver[] = { (KERN_OSTYPE), (KERN_OSRELEASE) }; 109 | int mib_os[] = { (KERN_OSTYPE), (KERN_OSTYPE) }; 110 | size_t len; 111 | char * ptr; 112 | #ifdef __FreeBSD__ 113 | char sep[] = "."; 114 | #else 115 | char sep[] = "-"; 116 | #endif 117 | 118 | ptr = config_get_filepath(); 119 | if (ptr != NULL) { 120 | strncpy(filename, ptr, size); 121 | return 0; 122 | } 123 | 124 | len = sizeof osname; 125 | if (sysctl(mib_os, 2, &osname, &len, NULL, 0) == -1) { 126 | return -1; 127 | } 128 | 129 | len = sizeof osver; 130 | if (sysctl(mib_ver, 2, &osver, &len, NULL, 0) == -1) { 131 | return (-1); 132 | } 133 | 134 | ptr = strstr(osver, sep); 135 | if (ptr == NULL) { 136 | return (-1); 137 | } else { 138 | ptr[0] = '\0'; 139 | } 140 | 141 | len = sizeof arch; 142 | if (sysctl(mib_arch, 2, &arch, &len, NULL, 0) == -1) { 143 | return -1; 144 | } 145 | #ifndef __FreeBSD__ 146 | if ((ptr = strstr(arch, "_")) != NULL) { 147 | ptr[0] = ':'; 148 | } 149 | #endif 150 | 151 | if(snprintf(filename, size, "%s/%s/%s:%s", dbversion, osname, osver, arch) < 0) { 152 | return -1; 153 | } 154 | 155 | return (0); 156 | } 157 | 158 | int 159 | plugin_archive_extract(int fd, const char *out) 160 | { 161 | 162 | struct archive_entry *ae = NULL; 163 | struct archive *ar = NULL; 164 | int fd_out = -1; 165 | 166 | fd_out = open(out, O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 167 | if ( fd_out < 0) { 168 | return -1; 169 | } 170 | 171 | ar = archive_read_new(); 172 | if (ar == NULL) { 173 | goto error; 174 | } 175 | 176 | archive_read_support_filter_all(ar); 177 | archive_read_support_format_raw(ar); 178 | 179 | archive_read_open_fd(ar, fd, BUFLEN); 180 | 181 | if (archive_read_next_header(ar, &ae) == ARCHIVE_OK) { 182 | if (archive_read_data_into_fd(ar, fd_out) != 0) { 183 | fprintf(stderr,"archive_read_data failed\n"); 184 | goto error; 185 | } 186 | } 187 | archive_read_free(ar); 188 | close(fd_out); 189 | return (0); 190 | 191 | error: 192 | if (fd_out) { 193 | close(fd); 194 | unlink(out); 195 | } 196 | return -1; 197 | } 198 | 199 | int 200 | plugin_fetch_file(void) 201 | { 202 | char buffer[BUFLEN]; 203 | FILE *fi; 204 | int fo, ft; 205 | int count; 206 | struct url_stat us; 207 | int64_t size = 0; 208 | char tmpfile[] = "/var/tmp/pkg-provides-XXXX"; 209 | struct stat sb; 210 | char path[] =PKG_DB_PATH; 211 | char filepath[MAX_FN_SIZE + 1]; 212 | char url[MAXPATHLEN + 1]; 213 | 214 | if(get_filepath(filepath, MAX_FN_SIZE) != 0) { 215 | fprintf(stderr,"Can't get the OS ABI\n"); 216 | return (-1); 217 | } 218 | 219 | snprintf(url, MAXPATHLEN , "%s/%s/provides.db.xz", config_get_remote_srv(), filepath); 220 | ft = open( PKG_DB_PATH "provides.db", O_WRONLY); 221 | if (ft < 0) { 222 | if (errno == ENOENT) { 223 | if (mkpath(path) == 0) { 224 | ft = open(PKG_DB_PATH "provides.db", O_RDWR | O_CREAT); 225 | } 226 | } 227 | if (ft < 0) { 228 | fprintf(stderr,"Insufficient privileges to update the provides database.\n"); 229 | return (-1); 230 | } 231 | unlink(PKG_DB_PATH "provides.db"); 232 | } else { 233 | if(fstat(ft, &sb) < 0) { 234 | fprintf(stderr,"fstat error\n"); 235 | close(ft); 236 | return (-1); 237 | } 238 | close(ft); 239 | if(fetchStatURL(url, &us, "") != 0) { 240 | fprintf(stderr,"fetchStatURL error : %s\n",url); 241 | return -1; 242 | } 243 | if(us.mtime < sb.st_mtim.tv_sec && (!force_flag)) { 244 | printf("The provides database is up-to-date.\n"); 245 | return (0); 246 | } 247 | } 248 | close(ft); 249 | 250 | fo = mkstemp(tmpfile); 251 | if(fo < 0) { 252 | fprintf(stderr, "mkstemp failed\n"); 253 | goto error; 254 | } 255 | 256 | unlink(tmpfile); 257 | 258 | fi = fetchXGetURL(url, &us, ""); 259 | if (fi == NULL) { 260 | fprintf(stderr, "fetchXGetURL error: %s\n", url); 261 | goto error; 262 | } 263 | 264 | provides_progressbar_start("Fetching provides database"); 265 | provides_progressbar_tick(size,us.size); 266 | while ((count = fread(buffer, 1, BUFLEN, fi)) > 0) { 267 | if(write(fo, buffer, count) != count) { 268 | fprintf(stderr, "Could not write to temporary file.\n"); 269 | goto error; 270 | } 271 | size += count; 272 | provides_progressbar_tick(size,us.size); 273 | } 274 | 275 | if (!feof(fi)) { 276 | fprintf(stderr, "Error reading from %s\n", url); 277 | goto error; 278 | } 279 | 280 | printf("Extracting database...."); 281 | fflush(stdout); 282 | 283 | lseek(fo, SEEK_SET, 0); 284 | if (plugin_archive_extract(fo, PKG_DB_PATH "provides.db") != 0 ) { 285 | printf("fail\n"); 286 | goto error; 287 | } 288 | lchmod(PKG_DB_PATH "provides.db",S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH); 289 | printf("success\n"); 290 | 291 | fclose(fi); 292 | close(fo); 293 | 294 | return EPKG_OK; 295 | 296 | error: 297 | if (fi != NULL) { 298 | fclose(fi); 299 | provides_progressbar_stop(); 300 | } 301 | 302 | if (fo >= 0) { 303 | close(fo); 304 | } 305 | 306 | return (-1); 307 | } 308 | 309 | static void 310 | free_list(struct pkg_head_t *head) { 311 | fpkg_t *pnode, *prev_n; 312 | file_t *pfile, *prev_f; 313 | 314 | prev_n = NULL; 315 | SLIST_FOREACH(pnode,head, next) { 316 | if (prev_n) { 317 | free(prev_n); 318 | } 319 | prev_f = NULL; 320 | SLIST_FOREACH(pfile,&(pnode->files), next) { 321 | if (prev_f) { 322 | free(prev_f); 323 | } 324 | free(pfile->name); 325 | prev_f = pfile; 326 | } 327 | free(pnode->pkg_name); 328 | prev_n = pnode; 329 | } 330 | 331 | } 332 | 333 | static int 334 | display_per_repo(char *repo_name, struct pkg_head_t *head) 335 | { 336 | struct pkgdb_it *it; 337 | struct pkg *pkg = NULL; 338 | struct pkg_repo *r = NULL; 339 | struct pkgdb *db = NULL; 340 | fpkg_t *pnode; 341 | file_t *pfile; 342 | 343 | int ret ; 344 | 345 | if (pkgdb_open_all(&db, PKGDB_REMOTE,repo_name) != EPKG_OK) { 346 | fprintf(stderr, "Can't open %s database\n",repo_name); 347 | return -1; 348 | } 349 | 350 | SLIST_FOREACH(pnode,head, next) { 351 | it = pkgdb_repo_query(db,pnode->pkg_name,MATCH_EXACT,repo_name); 352 | if (it == NULL) { 353 | continue; 354 | } 355 | 356 | if ((ret = pkgdb_it_next(it, &pkg, PKG_LOAD_BASIC)) != EPKG_OK) { 357 | pkgdb_it_free(it); 358 | continue; 359 | } 360 | 361 | printf("%-8s: ", "Name"); 362 | pkg_printf("%n-", pkg); 363 | pkg_printf("%v\n", pkg); 364 | printf("%-8s: ", "Comment"); 365 | pkg_printf("%c\n", pkg); 366 | printf("%-8s: ", "Repo"); 367 | printf("%s\n", repo_name); 368 | 369 | SLIST_FOREACH(pfile,&(pnode->files), next) { 370 | if(SLIST_FIRST(&(pnode->files)) == pfile) { 371 | printf("Filename: %s\n",pfile->name); 372 | } else { 373 | printf(" %s\n",pfile->name); 374 | } 375 | } 376 | if(SLIST_NEXT(pnode, next) != NULL) { 377 | printf("\n"); 378 | } 379 | pkgdb_it_free(it); 380 | } 381 | pkgdb_close(db); 382 | 383 | return (0); 384 | } 385 | 386 | void 387 | match_cb(const char * line, struct search_t *search) 388 | { 389 | file_t *pfile; 390 | fpkg_t *pnode; 391 | 392 | char *separator = strstr(line,"*"); 393 | if(separator != NULL) { 394 | char *exp; 395 | char * fullpath = separator + 1; 396 | 397 | if(strstr(search->pattern,"/")) { 398 | exp = fullpath; 399 | } else { 400 | exp = basename(fullpath); 401 | } 402 | 403 | if (pcre2_match(search->regex, (PCRE2_SPTR)exp, strlen(exp), 0, 0, search->match_data, NULL) > 0) { 404 | int found = 0; 405 | char * name = strndup(line, (separator - line + 1)); 406 | name[separator - line] = '\0'; 407 | SLIST_FOREACH(pnode,&(search->head), next) { 408 | if(strcmp(pnode->pkg_name, name)==0) { 409 | found = 1; 410 | break; 411 | } 412 | } 413 | 414 | if (found == 0) { 415 | pnode = malloc (sizeof(struct fpkg_t)); 416 | if (pnode == NULL) { 417 | exit(ENOMEM); 418 | } else { 419 | pnode->pkg_name = name; 420 | if(pnode->pkg_name == NULL) { 421 | exit(ENOMEM); 422 | } 423 | SLIST_INIT (&(pnode->files)); 424 | } 425 | SLIST_INSERT_HEAD(&(search->head),pnode,next); 426 | } else { 427 | free(name); 428 | } 429 | pfile = malloc(sizeof(struct file_t)); 430 | if(pfile == NULL) { 431 | exit(ENOMEM); 432 | } 433 | pfile->name = strdup(fullpath + 1); 434 | if(pfile->name == NULL) { 435 | exit(ENOMEM); 436 | } 437 | SLIST_INSERT_HEAD(&pnode->files,pfile,next); 438 | } 439 | } 440 | } 441 | 442 | int 443 | plugin_provides_search(char *repo, char *pattern) 444 | { 445 | FILE *fh; 446 | PCRE2_SIZE pcreErrorOffset; 447 | int pcreErrorNumber; 448 | char *repo_name; 449 | struct pkg_repo *r = NULL; 450 | 451 | struct search_t search; 452 | 453 | memset(&search, 0, sizeof(search)); 454 | 455 | search.pattern = pattern; 456 | 457 | SLIST_INIT (&search.head); 458 | 459 | fh = fopen(PKG_DB_PATH "provides.db","r"); 460 | if (fh == NULL) { 461 | fprintf(stderr, "Provides database not found, please update first.\n"); 462 | return (-1); 463 | } 464 | 465 | search.regex = pcre2_compile((PCRE2_SPTR)pattern, PCRE2_ZERO_TERMINATED, PCRE2_CASELESS, &pcreErrorNumber, &pcreErrorOffset, NULL); 466 | 467 | if(search.regex == NULL) { 468 | fprintf(stderr, "Invalid search pattern\n"); 469 | goto error_pcre; 470 | } 471 | 472 | search.match_data = pcre2_match_data_create_from_pattern(search.regex, NULL); 473 | 474 | if (bigram_expand(fh, &match_cb, &search) == -1) { 475 | fprintf(stderr, "Corrupted database\n"); 476 | } 477 | 478 | while (pkg_repos(&r) == EPKG_OK) { 479 | if (pkg_repo_enabled(r)) { 480 | repo_name = (char *)pkg_repo_name(r); 481 | if (repo == NULL || strcmp(repo, repo_name) == 0) { 482 | display_per_repo(repo_name, &search.head); 483 | } 484 | } 485 | } 486 | 487 | pcre2_match_data_free(search.match_data); 488 | 489 | fclose(fh); 490 | pcre2_code_free(search.regex); 491 | free_list(&search.head); 492 | return (0); 493 | 494 | error_pcre: 495 | fclose(fh); 496 | if (search.regex != NULL) { 497 | pcre2_code_free(search.regex); 498 | } 499 | return (-1); 500 | } 501 | 502 | int cb_event(void *data, struct pkgdb *db) { 503 | struct pkg_event *ev = data; 504 | if (ev->type == PKG_EVENT_INCREMENTAL_UPDATE && fetch_on_update) { 505 | plugin_fetch_file(); 506 | } 507 | return (EPKG_OK); 508 | } 509 | 510 | int 511 | plugin_provides_callback(int argc, char **argv) 512 | { 513 | int ch; 514 | bool do_update = false; 515 | char *repo = NULL; 516 | 517 | while ((ch = getopt(argc, argv, "ufr:")) != -1) { 518 | switch (ch) { 519 | case 'u': 520 | do_update = true; 521 | break; 522 | case 'f': 523 | force_flag = true; 524 | break; 525 | case 'r': 526 | repo = optarg; 527 | break; 528 | default: 529 | plugin_provides_usage(); 530 | return (EX_USAGE); 531 | break; 532 | } 533 | } 534 | 535 | if (do_update) { 536 | return plugin_fetch_file(); 537 | } 538 | 539 | argc -= optind; 540 | argv += optind; 541 | 542 | if (argc <= 0) { 543 | plugin_provides_usage(); 544 | return (EX_USAGE); 545 | } 546 | 547 | plugin_provides_search(repo, argv[0]); 548 | 549 | return (EPKG_OK); 550 | } 551 | 552 | int 553 | pkg_plugin_init(struct pkg_plugin *p) 554 | { 555 | self = p; 556 | 557 | pkg_plugin_set(p, PKG_PLUGIN_NAME, myname); 558 | pkg_plugin_set(p, PKG_PLUGIN_VERSION, myversion); 559 | pkg_plugin_set(p, PKG_PLUGIN_DESC, mydescription); 560 | 561 | pkg_plugin_hook_register(p, PKG_PLUGIN_HOOK_EVENT, cb_event); 562 | 563 | fetch_on_update = config_fetch_on_update() ? true : false; 564 | 565 | return (EPKG_OK); 566 | } 567 | 568 | 569 | int 570 | pkg_register_cmd(int idx, const char **name, const char **desc, int (**exec)(int argc, char **argv)) 571 | { 572 | *name = myname; 573 | *desc = mydescription; 574 | *exec = plugin_provides_callback; 575 | 576 | return (EPKG_OK); 577 | } 578 | 579 | int 580 | pkg_register_cmd_count (void) 581 | { 582 | return 1; 583 | } 584 | --------------------------------------------------------------------------------