diff options
| author | Remi Collet <remi@remirepo.net> | 2018-01-25 13:30:20 +0100 | 
|---|---|---|
| committer | Remi Collet <remi@remirepo.net> | 2018-01-25 13:30:20 +0100 | 
| commit | 7adc0cf86d301028e268f1803ee117bdd74586d5 (patch) | |
| tree | 72a553f8a633e7ce498fe7579e49db972720ee04 | |
| parent | 4daae0625ceaf2aa7aba7bf0a7230044d346e256 (diff) | |
New function: array rpminfo(string path [, bool full ]);
| -rw-r--r-- | README.md | 3 | ||||
| -rw-r--r-- | REFLECTION | 20 | ||||
| -rw-r--r-- | examples/repomanage.php | 21 | ||||
| -rw-r--r-- | php_rpminfo.h | 10 | ||||
| -rw-r--r-- | rpminfo.c | 164 | ||||
| -rw-r--r-- | tests/003.phpt | 270 | ||||
| -rw-r--r-- | tests/bidon-src.rpm | bin | 0 -> 6257 bytes | |||
| -rw-r--r-- | tests/bidon.rpm | bin | 0 -> 6808 bytes | 
8 files changed, 459 insertions, 29 deletions
@@ -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 [ <persistent> extension #15 rpminfo version 0.1.0 ] { + +  - Functions { +    Function [ <internal:rpminfo> function rpminfo ] { + +      - Parameters [2] { +        Parameter #0 [ <required> $path ] +        Parameter #1 [ <optional> $full ] +      } +    } +    Function [ <internal:rpminfo> function rpmvercmp ] { + +      - Parameters [2] { +        Parameter #0 [ <required> $evr1 ] +        Parameter #1 [ <required> $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 @@ -23,16 +23,17 @@  #include "php.h"  #include "php_ini.h"  #include "ext/standard/info.h" -#include "php_rpminfo.h" +#include <rpm/rpmio.h>  #include <rpm/rpmlib.h> +#include <rpm/rpmts.h> + +#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-- +<?php if (!extension_loaded("rpminfo")) print "skip"; ?> +--FILE-- +<?php  +var_dump(rpminfo(__DIR__ . "/bidon.rpm")); +var_dump(rpminfo(__DIR__ . "/bidon.rpm", true)); +var_dump(rpminfo(__DIR__ . "/bidon-src.rpm", true)); +// Errors +var_dump(rpminfo(__DIR__ . "/missing.rpm")); +var_dump(rpminfo(__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 <remi@fedoraproject.org>" +  ["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 <remi@fedoraproject.org> - 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 <remi@fedoraproject.org>" +  ["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 <remi@fedoraproject.org> - 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 Binary files differnew file mode 100644 index 0000000..c1c05cf --- /dev/null +++ b/tests/bidon-src.rpm diff --git a/tests/bidon.rpm b/tests/bidon.rpm Binary files differnew file mode 100644 index 0000000..259ddf6 --- /dev/null +++ b/tests/bidon.rpm  | 
