| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- ##
- ## rpmx -- RPM eXtension (Perl program)
- ## Copyright (c) 2001 Ralf S. Engelschall <rse@engelschall.com>
- ##
- ## list all files in the hierarchy which are not listed in RPM database
- ## $ rpm --stowaway
- ##
- ## generate repository information (on server only)
- ## $ rpm --makeindex <pkgdir>
- ##
- ## update local database with latest repository information
- ## $ rpm --update
- ##
- ## update the package sources by downloading missing files
- ## $ rpm --fetch <spec-file>
- ##
- ## query repository information
- ## $ rpm -rqa
- ## $ rpm -rqai
- ## $ rpm -rqal
- ## $ rpm -rqalv
- ## $ rpm -rqi package
- ## $ rpm -rql package
- ## $ rpm -rqlv package
- ##
- ## update already installed packages
- ## $ rpm -Uvhs '*'
- ##
- ## install/update a particular package and its dependencies
- ## $ rpm -Uvhs file-pattern
- ##
- ## erase a particular package and its dependencies
- ## $ rpm -es file-pattern
- ##
- require 5.000;
- use IO::File;
- use IO::Handle;
- use Data::Dumper;
- $|++;
- ## ______________________________________________________________________
- ##
- ## Determine Configuration and Run-Time Information
- ## ______________________________________________________________________
- ##
- my $CFG = {
- 'PRG' => {},
- 'RC' => {},
- 'OPT' => [],
- 'OPT_OPT' => {},
- 'OPT_ARG' => {},
- 'ARG' => [],
- };
- # determine path to executables
- my $prefix = shift(@ARGV);
- $CFG->{PRG}->{"rpm"} = $prefix."/bin/rpm";
- $CFG->{PRG}->{"curl"} = $prefix."/lib/openpkg/curl";
- $CFG->{PRG}->{"bzip2"} = $prefix."/lib/openpkg/bzip2";
- $CFG->{PRG}->{"gzip"} = $prefix."/lib/openpkg/gzip";
- # determine a few RPM rc-file configuration variables
- my $var;
- my $vars = '';
- foreach $var (qw(
- _dbpath _rpmdir _srcrpmdir _tmppath
- _target
- l_prefix
- l_repo_cache
- l_repo_0 l_repo_1 l_repo_2 l_repo_3 l_repo_4
- l_repo_5 l_repo_6 l_repo_7 l_repo_8 l_repo_9
- )) {
- $vars .= "${var}=\%{${var}};";
- }
- my @assign = split(/;/, `$CFG->{PRG}->{"rpm"} --eval '$vars'`);
- foreach $assign (@assign) {
- if ($assign =~ m|^(\S+)=(.*)$|s) {
- $CFG->{RC}->{$1} = $2;
- }
- }
- # parse argument line
- #foreach my $arg (@ARGV) {
- # print "<$arg>";
- #}
- #print "\n";
- my $op = 'pass';
- my $isopt = 1;
- my $optname = '';
- my $arg;
- foreach $arg (@ARGV) {
- if ($arg =~ m/^--(stowaway|makeindex|update|fetch|repo|smart)$/) {
- $op = $1;
- next;
- }
- if ($arg eq '--') {
- $isopt = 0;
- next;
- }
- if ($isopt) {
- push(@{$CFG->{OPT}}, $arg);
- if ($arg =~ m|^-.|) {
- $optname = $arg;
- $CFG->{OPT_OPT}->{$optname} = 1;
- $CFG->{OPT_ARG}->{$optname} = '--';
- }
- else {
- $CFG->{OPT_ARG}->{$optname} = $arg;
- }
- }
- else {
- push(@{$CFG->{ARG}}, $arg);
- }
- }
- #print Data::Dumper->Dump([$CFG]);
- if ($op eq 'pass') {
- # exec $CFG->{PRG}->{"rpm"} (@{$CFG->{OPT}}, @{$CFG->{ARG}});
- }
- else {
- my $rc;
- #eval "\$rc = \&op_${op}(\$CFG);";
- if ($op eq 'stowaway') {
- $rc = &op_stowaway($CFG);
- }
- elsif ($op eq 'makeindex') {
- $rc = &op_makeindex($CFG);
- }
- elsif ($op eq 'update') {
- $rc = &op_update($CFG);
- }
- elsif ($op eq 'fetch') {
- $rc = &op_fetch($CFG);
- }
- elsif ($op eq 'repo') {
- $rc = &op_repo($CFG);
- }
- elsif ($op eq 'smart') {
- $rc = &op_smart($CFG);
- }
- exit($rc);
- }
- ## ______________________________________________________________________
- ##
- ## Database Handling
- ## ______________________________________________________________________
- ##
- # read database file from disk
- sub db_load {
- my ($dbfile) = @_;
- if (not -f $dbfile) {
- my $DB = {};
- return $DB;
- }
- my $fp = new IO::File ("<$dbfile") || die "cannot read from Database file `$dbfile'";
- my $db = '';
- my $buf;
- $db .= $buf while ($fp->read($buf, 128*1024));
- $fp->close;
- my $DB = {};
- $db =~ s|<rpm\s+(\S+)>\n(.+?)</rpm>|&load_rpm($DB, $2, $1), ''|isge;
- sub load_rpm {
- my ($DB, $db, $rpm) = @_;
- $DB->{$rpm} = {};
- $db =~ s/<(repos|info|files|requires)>\n(.+?)<\/\1>/$DB->{$rpm}->{uc($1)} = $2, ''/isge;
- }
- return $DB;
- }
- # write database file to disk
- sub db_store {
- my ($dbfile, $DB) = @_;
- my $fp = new IO::File (">$dbfile") || die "cannot store to Database file `$dbfile'";
- my $rpm;
- foreach $rpm (sort(keys(%{$DB}))) {
- print $fp "<rpm $rpm>\n" .
- "<repos>\n" .
- $DB->{$rpm}->{REPOS} .
- "</repos>\n" .
- "<requires>\n" .
- $DB->{$rpm}->{REQUIRES} .
- "</requires>\n" .
- "<info>\n" .
- $DB->{$rpm}->{INFO} .
- "</info>\n" .
- "<files>\n" .
- $DB->{$rpm}->{FILES} .
- "</files>\n" .
- "</rpm>\n";
- }
- $fp->close;
- return;
- }
- ## ______________________________________________________________________
- ##
- ## Fetch an URL
- ## ______________________________________________________________________
- ##
- sub fetch_url {
- my ($CFG, $src, $dst) = @_;
- # make sure file URLs have a fully-qualified scheme.
- if ($src =~ m|^/.+|) {
- $src = "file://$src"
- }
- # make sure only schemes curl(1) supports are used.
- if ($src !~ m;^(file|http|ftp)://.+;) {
- return "invalid URL - only file, http and ftp schemes supported";
- }
- # try to fetch the URL
- unlink("$dst.err");
- unlink("$dst.hdr");
- $rc = system($CFG->{PRG}->{"curl"}.
- " --location" .
- " --max-time 120" .
- " --stderr $dst.err" .
- " --dump-header $dst.hdr" .
- " --output $dst '$src'");
- # check whether command failed
- if ($rc != 0) {
- open(FP, "<$dst.err");
- my $err = <FP>;
- close(FP);
- $err =~ s|\n$||;
- return $err;
- }
- # check whether remote sites failed
- if (-s "$dst.hdr") {
- open(FP, "<$dst.hdr");
- my $response = <FP>;
- close(FP);
- if ($response =~ m|^HTTP/[\d.]+\s+(\d+)|) {
- if ($1 ne 200) {
- $response =~ s|\n$||;
- return $response;
- }
- }
- }
-
- # cleanup
- unlink("$dst.err");
- unlink("$dst.hdr");
- return '';
- }
- ## ______________________________________________________________________
- ##
- ## List all files which are not known to RPM
- ## ______________________________________________________________________
- ##
- sub op_stowaway {
- my ($CFG) = @_;
- my $file;
- my $prefix = $CFG->{RC}->{"l_prefix"};
- my $rpm = $CFG->{PRG}->{"rpm"};
- print "OpenPKG Hierarchy $prefix\n";
- my @known = `$rpm -qla`;
- my %known = ();
- foreach $file (@known) {
- $file =~ s|\n$||s;
- $known{$file} = 1;
- }
- my @exist = `cd $prefix && find . -print 2>/dev/null`;
- foreach $file (sort(@exist)) {
- $file =~ s|^\./|$prefix/|s;
- $file =~ s|\n$||s;
- next if ($file eq '.');
- if (not $known{$file}) {
- next if ($file =~ m|^$prefix/RPM/?|);
- print "$file\n";
- }
- }
- return 0;
- }
- ## ______________________________________________________________________
- ##
- ## Generate Repository Index
- ## ______________________________________________________________________
- ##
- sub op_makeindex {
- my ($CFG) = @_;
- if ($#{$CFG->{ARG}} ne 0) {
- print STDERR "rpm: option --makeindex requires an argument\n";
- return 1;
- }
- my $dir = $CFG->{ARG}->[0];
- if (not -d $dir) {
- print STDERR "rpm: --makeindex argument `$dir' is not a directory\n";
- return 1;
- }
- my $index = '';
- my $file;
- my $rpm = $CFG->{PRG}->{"rpm"};
- foreach $file (sort(glob("$dir/*.rpm"))) {
- $file =~ s|^$dir/*||;
- $index .= "<rpm $file>\n";
- $index .= "<requires>\n";
- $index .= `$rpm -qp --qf '[%{REQUIRENAME} %{REQUIREFLAGS:depflags} %{REQUIREVERSION}\n]' $dir/$file`;
- $index .= "</requires>\n";
- $index .= "<info>\n";
- $index .= `$rpm -qpi $dir/$file`;
- $index .= "</info>\n";
- $index .= "<files>\n";
- $index .= `$rpm -qplv $dir/$file`;
- $index .= "</files>\n";
- $index .= "</rpm>\n";
- }
- open(FP, "|".$CFG->{PRG}->{"bzip2"}.">$dir/INDEX.bz2");
- print FP $index;
- close(FP);
- return 0;
- }
- ## ______________________________________________________________________
- ##
- ## Fetch Repository Indices
- ## ______________________________________________________________________
- ##
- sub op_update {
- my ($CFG) = @_;
- my $DB = {};
- my $tmpfile = $CFG->{RC}->{_tmppath}."/rpm.update.tmp.".$$;
- for ($i = 9; $i >= 0; $i--) {
- my $repo = $CFG->{RC}->{"l_repo_$i"};
- next if (not defined($repo) or $repo eq '' or $repo eq '-');
- $repo =~ s|([^/])$|$1/|;
- my $src = $repo."INDEX.bz2";
- my $prefix = $src;
- $prefix = substr($prefix, 0, 37)."..." if (length($prefix) > 40);
- printf(STDOUT "%-40s ", $prefix);
- if (($err = &fetch_url($CFG, $src, $tmpfile))) {
- $err = substr($err, 0, 37)."..." if (length($err) > 40);
- print STDOUT "FAILED: $err\n";
- next;
- }
- my $size = (stat($tmpfile))[7];
- print STDOUT "OK: $size bytes\n";
- my $fh = new IO::Handle;
- open($fh, $CFG->{PRG}->{"bzip2"}." -d -c $tmpfile|");
- my $db = '';
- my $buf;
- $db .= $buf while ($fh->read($buf, 128*1024));
- $fh->close;
- unlink($tmpfile);
- $db =~ s|<rpm\s+(\S+)>\n(.+?)</rpm>|&load_rpm2($DB, $repo, $2, $1), ''|isge;
- sub load_rpm2 {
- my ($DB, $repo, $db, $rpm) = @_;
- my $target = $CFG->{RC}->{_target};
- return if ($rpm !~ m|\.src\.rpm$| and $rpm !~ m|\.$target\.rpm$|);
- my $repos = '';
- if (defined($DB->{$rpm})) {
- $repos = $DB->{$rpm}->{REPOS};
- }
- $repos = $repo."\n".$repos;
- $DB->{$rpm} = {};
- $DB->{$rpm}->{REPOS} = $repos;
- $db =~ s/<(info|files|requires)>\n(.+?)<\/\1>/$DB->{$rpm}->{uc($1)} = $2, ''/isge;
- }
- }
- my $dbfile = $CFG->{RC}->{_dbpath}."/Repository";
- &db_store($dbfile, $DB);
- return 0;
- }
- ## ______________________________________________________________________
- ##
- ## Repository Queries
- ## ______________________________________________________________________
- ##
- sub op_repo {
- my ($CFG) = @_;
- # argument line consistency check
- if (not defined($CFG->{OPT_OPT}->{"-q"})) {
- print STDERR "rpm: option --repo always requires option -q\n";
- return 1;
- }
- my @optok = (qw(--rcfile -q --all -i --list --verbose));
- my $opt;
- my $check;
- foreach $opt (keys(%{$CFG->{OPT_OPT}})) {
- my $ok = 0;
- foreach $check (@optok) {
- if ($check eq $opt) {
- $ok = 1;
- last;
- }
- }
- if (not $ok) {
- print STDERR "rpm: option $opt not supported in conjunction with option --repo\n";
- return 1;
- }
- }
- # load database
- my $dbfile = $CFG->{RC}->{_dbpath}."/Repository";
- if (not -f $dbfile) {
- print STDERR "rpm: option --repo requires local repository index.\n";
- print STDERR "rpm: run \"rpm --update\", first.\n";
- return 1;
- }
- my $DB = &db_load($dbfile);
- # perform query operation
- my $target = $CFG->{RC}->{_target};
- my $rpm;
- my $rpms = {};
- foreach $rpm (sort(keys(%{$DB}))) {
- my $name = $rpm;
- $name =~ s|\.src\.rpm$||;
- $name =~ s|\.${target}\.rpm$||;
- next if (defined($rpms->{$name}));
- $rpms->{$name} = 1;
- if ((defined($CFG->{ARG}->[0]) and $rpm =~ m|^$CFG->{ARG}->[0]|)
- or defined($CFG->{OPT_OPT}->{"--all"})) {
- if (not defined($CFG->{OPT_OPT}->{"--list"}) and not defined($CFG->{OPT_OPT}->{"-i"})) {
- print "$name\n";
- }
- if (defined($CFG->{OPT_OPT}->{"-i"})) {
- print $DB->{$rpm}->{INFO};
- }
- if (defined($CFG->{OPT_OPT}->{"--list"})) {
- my $files = $DB->{$rpm}->{FILES};
- if (not defined($CFG->{OPT_OPT}->{"--verbose"})) {
- if (not ($files =~ s|^.*\s+(\S+\s+->\s+\S+)\s*$|$1|mg)) {
- $files =~ s|^.*\s+(\S+)\s*$|$1|mg;
- }
- }
- print "$files";
- }
- }
- }
- }
- ## ______________________________________________________________________
- ##
- ## Fetch Operation
- ## ______________________________________________________________________
- ##
- sub op_fetch {
- my ($CFG) = @_;
-
- if ($#{$CFG->{ARG}} ne 0) {
- print STDERR "rpm: option --fetch requires an argument\n";
- return 1;
- }
- my $spec = $CFG->{ARG}->[0];
- if (not -f $spec) {
- print STDERR "rpm: spec file `$spec' not found\n";
- return 1;
- }
- # determine package name and source directory
- my $name = $spec;
- $name =~ s|\.[^.]+$||;
- $name =~ s|^.+/([^/]+)$|$1|;
- my $srcdir = `$CFG->{PRG}->{"rpm"} --define 'name $name' --eval '%_sourcedir'`;
- $srcdir =~ s|\n+$||s;
- # parse spec file
- my $DEF = {};
- my $SRC = {};
- open(SPEC, "<$spec");
- while (<SPEC>) {
- s|\n+$||s;
- if (m/^([a-zA-Z_][a-zA-Z0-9_]*):\s*(.+?)\s*$/) {
- $DEF->{lc($1)} = $2;
- }
- if (m/^%define\s+([a-zA-Z_][a-zA-Z0-9_]*)\s+(.+?)\s*$/) {
- $DEF->{lc($1)} = $2;
- }
- if (m/^((Source|Patch)[0-9]+?):\s*(.+)\s*$/i) {
- my ($srcid, $srcurl) = ($1, $3);
- 1 while ($srcurl =~ s|%{?([a-zA-Z_][a-zA-Z0-9_]*)}?|$DEF->{lc($1)}|sge);
- my $srcfile = $srcurl;
- $srcfile =~ s|^.*/([^/]+)$|$1|;
- my $file = $srcfile;
- $file = substr($file, 0, 40) if (length($file) > 40);
- printf(STDERR "%-9s %-40s ", "$srcid:", $file);
- if (-f "$srcdir/$srcfile") {
- my $size = (stat("$srcdir/$srcfile"))[7];
- if ($size > 1024*1024) {
- $size = sprintf("%.1fMB", $size / (1024*1024));
- }
- elsif ($size > 1024) {
- $size = sprintf("%.0fKB", $size / 1024);
- }
- else {
- $size = sprintf("%d", $size);
- }
- print STDERR "...OK [$size]\n";
- }
- else {
- print STDERR "...MISSING\n";
- print STDERR "rpm: Fetching $srcurl\n";
- my $cmd = $CFG->{PRG}->{"curl"}.
- " --location" .
- " --output $srcdir/$srcfile" .
- " $srcurl";
- my $rc = system($cmd);
- if ($rc != 0) {
- print STDERR "rpm: Failed to fetch source file `$srcfile'\n";
- return 1;
- }
- }
- }
- }
- close(SPEC);
- return 0;
- }
- ## ______________________________________________________________________
- ##
- ## Smart Operations
- ## ______________________________________________________________________
- ##
- sub op_smart {
- my ($CFG) = @_;
- if ($#{$CFG->{ARG}} ne 0) {
- print STDERR "rpm: option --smart requires one argument\n";
- return 1;
- }
- my $package = $CFG->{ARG}->[0];
- # load database
- my $dbfile = $CFG->{RC}->{_dbpath}."/Repository";
- if (not -f $dbfile) {
- print STDERR "rpm: option --smart requires local repository index.\n";
- print STDERR "rpm: run \"rpm --update\", first.\n";
- return 1;
- }
- my $DB = &db_load($dbfile);
- my $deps = [];
- &find_deps($CFG, $DB, $deps, $package, '=', 'ANY');
- sub find_deps {
- my ($CFG, $DB, $deps, $pkg, $op, $ver) = @_;
- push(@{$deps}, "$pkg $op $ver");
- my $rpm;
- foreach $rpm (keys(%{$DB})) {
- if (&match_rpm($CFG, $rpm, $pkg, $op, $ver)) {
- my $req;
- foreach $req (split(/\n/, $DB->{$rpm}->{REQUIRES})) {
- $req =~ s|^\s+||sg;
- $req =~ s|\s+$||sg;
- if ($req =~ m|^OpenPKG$| or
- $req =~ m|^rpmlib\(.+\)|) {
- next;
- }
- if ($req =~ m|^(\S+)$|) {
- $req .= " = ANY";
- }
- if ($req =~ m|^(\S+)\s+([=><]+)\s+(\S+)$|) {
- &find_deps($CFG, $DB, $deps, $1, $2, $3);
- }
- }
- }
- }
- }
- sub match_rpm {
- my ($CFG, $rpm, $name, $op, $ver) = @_;
- my $rc = 0;
- my $target = $CFG->{RC}->{_target};
- $rpm =~ s|\.src\.rpm$||;
- $rpm =~ s|\.${target}\.rpm$||;
- if ($rpm =~ m|^(\S+)-([^-]+-[^-]+)$|) {
- my ($tname, $tver) = ($1, $2);
- if ($name eq $tname and &match_version($ver, $op, $tver)) {
- $rc = 1;
- }
- }
- return $rc;
- }
- sub match_version {
- my ($ver, $op, $tver) = @_;
- return 1;
- }
- my $dep;
- foreach $dep (@{$deps}) {
- print "$dep\n";
- }
- }
|