From 7adc0cf86d301028e268f1803ee117bdd74586d5 Mon Sep 17 00:00:00 2001 From: Remi Collet Date: Thu, 25 Jan 2018 13:30:20 +0100 Subject: New function: array rpminfo(string path [, bool full ]); --- README.md | 3 +- REFLECTION | 20 ++++ examples/repomanage.php | 21 ++-- php_rpminfo.h | 10 +- rpminfo.c | 164 +++++++++++++++++++++++++++-- tests/003.phpt | 270 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/bidon-src.rpm | Bin 0 -> 6257 bytes tests/bidon.rpm | Bin 0 -> 6808 bytes 8 files changed, 459 insertions(+), 29 deletions(-) create mode 100644 REFLECTION create mode 100644 tests/003.phpt create mode 100644 tests/bidon-src.rpm create mode 100644 tests/bidon.rpm diff --git a/README.md b/README.md index 7703d5b..1584173 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Experimental wrapper for librpm For now, only expose int rpmvercmp(string evr1, string evr2); + array rpminfo(string path [, bool full ]); Mostly a PoC build for fun because of @@ -38,5 +39,5 @@ Some benchmark results (find 15 old RPMs among 5000) $ time php repomanage.php --old --keep 5 . ... - real 0m0,634s + real 0m0,135s diff --git a/REFLECTION b/REFLECTION new file mode 100644 index 0000000..630606c --- /dev/null +++ b/REFLECTION @@ -0,0 +1,20 @@ +Extension [ extension #15 rpminfo version 0.1.0 ] { + + - Functions { + Function [ function rpminfo ] { + + - Parameters [2] { + Parameter #0 [ $path ] + Parameter #1 [ $full ] + } + } + Function [ function rpmvercmp ] { + + - Parameters [2] { + Parameter #0 [ $evr1 ] + Parameter #1 [ $evr2 ] + } + } + } +} + diff --git a/examples/repomanage.php b/examples/repomanage.php index 8428118..7682c3d 100644 --- a/examples/repomanage.php +++ b/examples/repomanage.php @@ -44,19 +44,20 @@ if (is_dir($rpms)) { } $tree = []; -$handle = popen('rpm -qp --qf "%{NAME} %{EPOCHNUM} %{VERSION} %{RELEASE} %{ARCH} %{NEVR}\n" ' . $rpms . "/*.rpm", "r"); -while ($line = fgets($handle)) { - $tab = explode(' ', trim($line)); - if (count($tab) == 6) { - $tree[$tab[4]][$tab[0]][] = [ - 'name' => $tab[0], - 'path' => $tab[5], - 'evr' => "${tab[1]}:${tab[2]}-${tab[3]}", - ]; +foreach(glob("$rpms/*.rpm") as $rpm) { + $info = rpminfo($rpm); + if (is_array($info)) { + $info['path'] = $rpm; + $info['evr'] = $info['Version'] . '-' . $info['Release']; + if (isset($info['Epoch'])) { + $info['evr'] = $info['Epoch'] . ":" . $info['evr']; + } + $tree[$info['Name']][$info['Arch']][] = $info; } else { - echo "Ignore $line\n"; + echo "Skip $rpm\n"; } } + foreach($tree as $arch => $subtree) { foreach ($subtree as $name => $versions) { if (count($versions) > $keep) { diff --git a/php_rpminfo.h b/php_rpminfo.h index 9c8319c..d90e9e1 100644 --- a/php_rpminfo.h +++ b/php_rpminfo.h @@ -22,7 +22,7 @@ extern zend_module_entry rpminfo_module_entry; #define phpext_rpminfo_ptr &rpminfo_module_entry -#define PHP_RPMINFO_VERSION "0.1.0" /* Replace with version number for your extension */ +#define PHP_RPMINFO_VERSION "0.1.0-dev" /* Replace with version number for your extension */ #ifdef PHP_WIN32 # define PHP_RPMINFO_API __declspec(dllexport) @@ -36,15 +36,9 @@ extern zend_module_entry rpminfo_module_entry; #include "TSRM.h" #endif -/* - Declare any global variables you may need between the BEGIN - and END macros here: - ZEND_BEGIN_MODULE_GLOBALS(rpminfo) - zend_long global_value; - char *global_string; + rpmts ts; ZEND_END_MODULE_GLOBALS(rpminfo) -*/ /* Always refer to the globals in your function as RPMINFO_G(variable). You are encouraged to rename these macros something shorter, see diff --git a/rpminfo.c b/rpminfo.c index b08e05b..30c003c 100644 --- a/rpminfo.c +++ b/rpminfo.c @@ -23,16 +23,17 @@ #include "php.h" #include "php_ini.h" #include "ext/standard/info.h" -#include "php_rpminfo.h" +#include #include +#include + +#include "php_rpminfo.h" -/* If you declare any globals in php_rpminfo.h uncomment this: ZEND_DECLARE_MODULE_GLOBALS(rpminfo) -*/ /* True global resources - no need for thread safety here */ -static int le_rpminfo; +// static int le_rpminfo; /* {{{ PHP_INI */ @@ -44,15 +45,130 @@ PHP_INI_END() */ /* }}} */ +static rpmts rpminfo_getts(rpmVSFlags flags) { + if (!RPMINFO_G(ts)) { + RPMINFO_G(ts) = rpmtsCreate(); + } + if (RPMINFO_G(ts)) { + (void)rpmtsSetVSFlags(RPMINFO_G(ts), flags); + } + return RPMINFO_G(ts); +} + +static void rpminfo_freets(void) { + if (RPMINFO_G(ts)) { + rpmtsFree(RPMINFO_G(ts)); + RPMINFO_G(ts) = NULL; + } +} + +ZEND_BEGIN_ARG_INFO_EX(arginfo_rpminfo, 0, 0, 1) + ZEND_ARG_INFO(0, path) + ZEND_ARG_INFO(0, full) +ZEND_END_ARG_INFO() + +/* {{{ proto array rpminfo(string path [, bool full]) + Retrieve information from a RPM file */ +PHP_FUNCTION(rpminfo) +{ + char *path, *msg=NULL, *val; + size_t len; + zend_bool full = 0; + FD_t f; + int rc; + Header h; + HeaderIterator hi; + rpmTagVal tag; + rpmTagType type; + rpmts ts = rpminfo_getts(_RPMVSF_NODIGESTS | _RPMVSF_NOSIGNATURES | RPMVSF_NOHDRCHK); + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "p|b", &path, &len, &full) == FAILURE) { + return; + } + + f = Fopen(path, "r"); + if (f) { + rc = rpmReadPackageFile(ts, f, "rpminfo", &h); + if (rc == RPMRC_OK || rc == RPMRC_NOKEY || rc == RPMRC_NOTTRUSTED) { + + array_init(return_value); + hi = headerInitIterator(h); + while ((tag=headerNextTag(hi)) != RPMTAG_NOT_FOUND) { + switch (tag) { + case RPMTAG_NAME: + case RPMTAG_VERSION: + case RPMTAG_RELEASE: + case RPMTAG_EPOCH: + case RPMTAG_ARCH: + break; + default: + if (!full) { + continue; + } + } + + type = rpmTagGetTagType(tag); + switch (type) { + case RPM_STRING_TYPE: + case RPM_I18NSTRING_TYPE: + val = headerGetString(h, tag); + if (val) { + add_assoc_string(return_value, rpmTagGetName(tag), headerGetAsString(h, tag)); + } else { + add_assoc_null(return_value, rpmTagGetName(tag)); + } + break; + case RPM_CHAR_TYPE: + case RPM_INT8_TYPE: + case RPM_INT16_TYPE: + case RPM_INT32_TYPE: + case RPM_INT64_TYPE: + add_assoc_long(return_value, rpmTagGetName(tag), (zend_long)headerGetNumber(h, tag)); + break; + default: + val = headerGetAsString(h, tag); + if (val) { + add_assoc_string(return_value, rpmTagGetName(tag), headerGetAsString(h, tag)); + } else { + add_assoc_null(return_value, rpmTagGetName(tag)); + } + } + } + if (full) { + add_assoc_bool(return_value, "IsSource", headerIsSource(h)); + } + if (h) { + headerFree(h); + } + Fclose(f); + return; + + } else if (rc == RPMRC_NOTFOUND) { + php_error_docref(NULL, E_WARNING, "Can't read '%s': Argument is not a RPM file", path); + + } else if (rc == RPMRC_NOTFOUND) { + php_error_docref(NULL, E_WARNING, "Can't read '%s': Error reading header from package", path); + + } else { + php_error_docref(NULL, E_WARNING, "Can't read '%s': Unkown error", path); + } + + Fclose(f); + } else { + php_error_docref(NULL, E_WARNING, "Can't open '%s': %s", path, Fstrerror(f)); + } + + RETURN_FALSE; +} +/* }}} */ ZEND_BEGIN_ARG_INFO_EX(arginfo_rpmvercmp, 0, 0, 2) ZEND_ARG_INFO(0, evr1) ZEND_ARG_INFO(0, evr2) ZEND_END_ARG_INFO() -/* Every user-visible function in PHP should document itself in the source */ -/* {{{ proto string rpmcmpver(string evr1, string evr2) - Return a string to confirm that the module is compiled in */ +/* {{{ proto int rpmcmpver(string evr1, string evr2) + Compare 2 RPM evr (epoch:version-release) strings */ PHP_FUNCTION(rpmvercmp) { char *evr1, *evr2; @@ -64,6 +180,7 @@ PHP_FUNCTION(rpmvercmp) RETURN_LONG(rpmvercmp(evr1, evr2)); } +/* }}} */ /* {{{ php_rpminfo_init_globals */ @@ -135,12 +252,33 @@ PHP_MINFO_FUNCTION(rpminfo) } /* }}} */ +/* {{{ PHP_GINIT_FUNCTION + */ +static PHP_GINIT_FUNCTION(rpminfo) /* {{{ */ +{ +#if defined(COMPILE_DL_SESSION) && defined(ZTS) + ZEND_TSRMLS_CACHE_UPDATE(); +#endif + + rpminfo_globals->ts = NULL; +} +/* }}} */ + +/* {{{ PHP_GSHUTDOWN_FUNCTION +*/ +PHP_GSHUTDOWN_FUNCTION(rpminfo) +{ + rpminfo_freets(); +} +/* }}} */ + /* {{{ rpminfo_functions[] * * Every user visible function must have an entry in rpminfo_functions[]. */ const zend_function_entry rpminfo_functions[] = { - PHP_FE(rpmvercmp, arginfo_rpmvercmp) + PHP_FE(rpminfo, arginfo_rpminfo) + PHP_FE(rpmvercmp, arginfo_rpmvercmp) PHP_FE_END }; /* }}} */ @@ -148,7 +286,9 @@ const zend_function_entry rpminfo_functions[] = { /* {{{ rpminfo_module_entry */ zend_module_entry rpminfo_module_entry = { - STANDARD_MODULE_HEADER, + STANDARD_MODULE_HEADER_EX, + NULL, + NULL, "rpminfo", rpminfo_functions, PHP_MINIT(rpminfo), @@ -157,7 +297,11 @@ zend_module_entry rpminfo_module_entry = { PHP_RSHUTDOWN(rpminfo), /* Replace with NULL if there's nothing to do at request end */ PHP_MINFO(rpminfo), PHP_RPMINFO_VERSION, - STANDARD_MODULE_PROPERTIES + PHP_MODULE_GLOBALS(rpminfo), + PHP_GINIT(rpminfo), + PHP_GSHUTDOWN(rpminfo), + NULL, + STANDARD_MODULE_PROPERTIES_EX }; /* }}} */ diff --git a/tests/003.phpt b/tests/003.phpt new file mode 100644 index 0000000..5a46dac --- /dev/null +++ b/tests/003.phpt @@ -0,0 +1,270 @@ +--TEST-- +Check for rpminfo function +--SKIPIF-- + +--FILE-- + +Done +--EXPECTF-- +array(4) { + ["Name"]=> + string(5) "bidon" + ["Version"]=> + string(1) "1" + ["Release"]=> + string(11) "1.fc25.remi" + ["Arch"]=> + string(6) "x86_64" +} +array(63) { + ["Headeri18ntable"]=> + string(1) "C" + ["Sigsize"]=> + int(2304) + ["Sigmd5"]=> + string(32) "644819c3566819b1e10a5c97943de094" + ["Sha1header"]=> + string(40) "0a86742fe53973ac9ab4611187a83ffb44f1de5a" + ["Sha256header"]=> + string(64) "9aab7242a80212ad1fe4fdd3b250c0c4f176c0b3fb1355c0d62ff094fc3f7da0" + ["Name"]=> + string(5) "bidon" + ["Version"]=> + string(1) "1" + ["Release"]=> + string(11) "1.fc25.remi" + ["Summary"]=> + string(5) "Bidon" + ["Description"]=> + string(15) "A dummy package" + ["Buildtime"]=> + int(1516882146) + ["Buildhost"]=> + string(20) "builder.remirepo.net" + ["Size"]=> + int(29) + ["Vendor"]=> + string(11) "Remi Collet" + ["License"]=> + string(13) "Public Domain" + ["Packager"]=> + string(36) "Remi Collet " + ["Group"]=> + string(11) "Unspecified" + ["Url"]=> + string(30) "http://blog.famillecollet.com/" + ["Os"]=> + string(5) "linux" + ["Arch"]=> + string(6) "x86_64" + ["Filesizes"]=> + int(0) + ["Filemodes"]=> + int(0) + ["Filerdevs"]=> + int(0) + ["Filemtimes"]=> + int(0) + ["Filedigests"]=> + NULL + ["Filelinktos"]=> + NULL + ["Fileflags"]=> + int(0) + ["Fileusername"]=> + NULL + ["Filegroupname"]=> + NULL + ["Sourcerpm"]=> + string(27) "bidon-1-1.fc25.remi.src.rpm" + ["Fileverifyflags"]=> + int(0) + ["Archivesize"]=> + int(428) + ["Providename"]=> + NULL + ["Requireflags"]=> + int(0) + ["Requirename"]=> + NULL + ["Requireversion"]=> + NULL + ["Rpmversion"]=> + string(6) "4.14.0" + ["Changelogtime"]=> + int(1419422400) + ["Changelogname"]=> + string(42) "Remi Collet - 1-1" + ["Changelogtext"]=> + string(8) "- create" + ["Cookie"]=> + string(31) "builder.remirepo.net 1516882146" + ["Filedevices"]=> + int(0) + ["Fileinodes"]=> + int(0) + ["Filelangs"]=> + NULL + ["Provideflags"]=> + int(0) + ["Provideversion"]=> + NULL + ["Dirindexes"]=> + int(0) + ["Basenames"]=> + NULL + ["Dirnames"]=> + NULL + ["Optflags"]=> + string(219) "-O2 -g -pipe -Wall -Werror=format-security -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic" + ["Payloadformat"]=> + string(4) "cpio" + ["Payloadcompressor"]=> + string(2) "xz" + ["Payloadflags"]=> + string(1) "2" + ["Platform"]=> + string(23) "x86_64-redhat-linux-gnu" + ["Filecolors"]=> + int(0) + ["Fileclass"]=> + int(0) + ["Classdict"]=> + NULL + ["Sourcepkgid"]=> + string(32) "188da2a3966f4a5f0dd48e784be76846" + ["Filedigestalgo"]=> + int(8) + ["Encoding"]=> + string(5) "utf-8" + ["Payloaddigest"]=> + string(64) "ace77d50077cb8088d9bf224c9a9e89343a2aa40fe596b3e60ef10a9a200a3bd" + ["Payloaddigestalgo"]=> + int(8) + ["IsSource"]=> + bool(false) +} +array(54) { + ["Headeri18ntable"]=> + string(1) "C" + ["Sigsize"]=> + int(1753) + ["Sigmd5"]=> + string(32) "188da2a3966f4a5f0dd48e784be76846" + ["Sha1header"]=> + string(40) "994275fb4366d82043c791c50682cbe46e1c96d6" + ["Sha256header"]=> + string(64) "4c2f1cba929cc05ce58d4a9184d4652f2f7d7bdf05ba1dc92966ce9e9cefe93c" + ["Name"]=> + string(5) "bidon" + ["Version"]=> + string(1) "1" + ["Release"]=> + string(11) "1.fc25.remi" + ["Summary"]=> + string(5) "Bidon" + ["Description"]=> + string(15) "A dummy package" + ["Buildtime"]=> + int(1516882146) + ["Buildhost"]=> + string(20) "builder.remirepo.net" + ["Size"]=> + int(360) + ["Vendor"]=> + string(11) "Remi Collet" + ["License"]=> + string(13) "Public Domain" + ["Packager"]=> + string(36) "Remi Collet " + ["Group"]=> + string(11) "Unspecified" + ["Url"]=> + string(30) "http://blog.famillecollet.com/" + ["Os"]=> + string(5) "linux" + ["Arch"]=> + string(6) "x86_64" + ["Filesizes"]=> + int(360) + ["Filemodes"]=> + int(33188) + ["Filerdevs"]=> + int(0) + ["Filemtimes"]=> + int(1516882140) + ["Filedigests"]=> + string(64) "195d7dd3ca9518024a1554e68b3f63fa7e2bdaa4efac59f06c1ab231283e6067" + ["Filelinktos"]=> + string(0) "" + ["Fileflags"]=> + int(32) + ["Fileusername"]=> + string(6) "extras" + ["Filegroupname"]=> + string(4) "remi" + ["Fileverifyflags"]=> + int(4294967295) + ["Archivesize"]=> + int(608) + ["Requireflags"]=> + int(0) + ["Requirename"]=> + NULL + ["Requireversion"]=> + NULL + ["Rpmversion"]=> + string(6) "4.14.0" + ["Changelogtime"]=> + int(1419422400) + ["Changelogname"]=> + string(42) "Remi Collet - 1-1" + ["Changelogtext"]=> + string(8) "- create" + ["Cookie"]=> + string(31) "builder.remirepo.net 1516882146" + ["Filedevices"]=> + int(1) + ["Fileinodes"]=> + int(1) + ["Filelangs"]=> + string(0) "" + ["Sourcepackage"]=> + int(1) + ["Dirindexes"]=> + int(0) + ["Basenames"]=> + string(10) "bidon.spec" + ["Dirnames"]=> + string(0) "" + ["Payloadformat"]=> + string(4) "cpio" + ["Payloadcompressor"]=> + string(4) "gzip" + ["Payloadflags"]=> + string(1) "9" + ["Filedigestalgo"]=> + int(8) + ["Encoding"]=> + string(5) "utf-8" + ["Payloaddigest"]=> + string(64) "b104f6e80a0b761ca05b0c478c5a5e3f5fe57cf079cfca53d360351806c23951" + ["Payloaddigestalgo"]=> + int(8) + ["IsSource"]=> + bool(true) +} + +Warning: rpminfo(): Can't open '%s/tests/missing.rpm': No such file or directory in %s/003.php on line 6 +bool(false) + +Warning: rpminfo(): Can't read '%s/tests/003.php': Argument is not a RPM file in %s/003.php on line 7 +bool(false) +Done diff --git a/tests/bidon-src.rpm b/tests/bidon-src.rpm new file mode 100644 index 0000000..c1c05cf Binary files /dev/null and b/tests/bidon-src.rpm differ diff --git a/tests/bidon.rpm b/tests/bidon.rpm new file mode 100644 index 0000000..259ddf6 Binary files /dev/null and b/tests/bidon.rpm differ -- cgit