OpenPKG CVS Repository
http://cvs.openpkg.org/
____________________________________________________________________________
Server: cvs.openpkg.org Name: Ralf S. Engelschall
Root: /e/openpkg/cvs Email: [EMAIL PROTECTED]
Module: openpkg-re Date: 04-Jul-2003 19:04:45
Branch: HEAD Handle: 2003070418044400
Added files:
openpkg-re rpmlint.pl
Log:
first cut for a .rpm file lint
Summary:
Revision Changes Path
1.1 +345 -0 openpkg-re/rpmlint.pl
____________________________________________________________________________
patch -p0 <<'@@ .'
Index: openpkg-re/rpmlint.pl
============================================================================
$ cvs diff -u -r0 -r1.1 rpmlint.pl
--- /dev/null 2003-07-04 19:04:45.000000000 +0200
+++ rpmlint.pl 2003-07-04 19:04:45.000000000 +0200
@@ -0,0 +1,345 @@
+#!/bin/sh -- # -*- perl -*-
+eval 'exec perl -S $0 ${1+"$@"}'
+ if $running_under_some_shell;
+##
+## rpmlint -- OpenPKG .rpm File Checker
+## Copyright (c) 2003 The OpenPKG Project <http://www.openpkg.org/>
+## Copyright (c) 2003 Ralf S. Engelschall <[EMAIL PROTECTED]>
+## Copyright (c) 2003 Cable & Wireless <http://www.cw.com/>
+##
+## Permission to use, copy, modify, and distribute this software for
+## any purpose with or without fee is hereby granted, provided that
+## the above copyright notice and this permission notice appear in all
+## copies.
+##
+## THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+## WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+## MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+## IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+## CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+## USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+## OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+## OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+## SUCH DAMAGE.
+##
+
+require 5;
+use Getopt::Long;
+use IO;
+use strict;
+
+# program information
+my $progname = "rpmlint";
+my $progvers = "0.0.1";
+
+# parameters (defaults)
+my $version = 0;
+my $verbose = 0;
+my $help = 0;
+my $check = 'all';
+my $tmpdir = ($ENV{TMPDIR} || $ENV{TEMPDIR} || "/tmp") . "/$progname";
+my $rpm = 'rpm';
+my $rpm2cpio = 'rpm2cpio';
+
+# exception handling support
+$SIG{__DIE__} = sub {
+ my ($err) = @_;
+ $err =~ s|\s+at\s+.*||s if (not $verbose);
+ print STDERR "$progname:ERROR: $err ". ($! ? "($!)" : "") . "\n";
+ exit(1);
+};
+
+# command line parsing
+Getopt::Long::Configure("bundling");
+my $result = GetOptions(
+ 'V|version' => \$version,
+ 'v|verbose' => \$verbose,
+ 'h|help' => \$help,
+ 'c|check=s' => \$check,
+ 't|tmpdir=s' => \$tmpdir,
+ 'r|rpm=s' => \$rpm,
+) || die "option parsing failed";
+if ($help) {
+ print "Usage: $progname [options] [RPMFILE ...]\n" .
+ "Available options:\n" .
+ " -v,--verbose enable verbose run-time mode\n" .
+ " -h,--help print out this usage page\n" .
+ " -c,--check=CHECKS select checks to perform (default='all')\n" .
+ " -r,--rpm=FILE filesystem path to RPM program\n" .
+ " -t,--tmpdir=PATH filesystem path to temporary directory\n" .
+ " -V,--version print program version\n" .
+ exit(0);
+}
+if ($version) {
+ print "OpenPKG $progname $progvers\n";
+ exit(0);
+}
+
+# verbose message printing
+sub msg_verbose {
+ my ($msg) = @_;
+ print STDERR "$msg\n" if ($verbose);
+}
+
+# warning message printing
+sub msg_warning {
+ my ($msg) = @_;
+ print STDERR "$progname:WARNING: $msg\n";
+}
+
+# error message printing
+sub msg_error {
+ my ($msg) = @_;
+ print STDERR "$progname:ERROR: $msg\n";
+}
+
+# determine check list
+my @check_list = (qw(
+ layout
+ attrib
+ content
+));
+my @checks = ();
+if ($check eq 'all') {
+ @checks = @check_list;
+}
+else {
+ foreach my $c (split(/,/, $check)) {
+ if (not grep(/^$c$/, @check_list)) {
+ die "invalid check \"$c\"";
+ }
+ push(@checks, $c);
+ }
+}
+
+# global return code
+$main::GRC = 0;
+
+# environment preparation
+system("rm -rf $tmpdir");
+system("mkdir -p $tmpdir");
+
+# iterate over all .spec files
+foreach my $filename (@ARGV) {
+ die "unable to open file \"$filename\" for reading"
+ if (not -f $filename);
+ my $info = &rpm_info($filename, [EMAIL PROTECTED]);
+ foreach my $check (@checks) {
+ eval "\&check_$check(\$filename, \$info);";
+ }
+}
+
+# environment cleanup
+system("rm -rf $tmpdir");
+
+# die gracefully
+exit($main::GRC);
+
+## _________________________________________________________________
+##
+## COMMON SUBROUTINES
+## _________________________________________________________________
+##
+
+sub lint_message {
+ my ($type, $file, $msg) = @_;
+ $file =~ s|^.+?/([^/]+)$|$1|s;
+ printf(STDERR "%s:%s: %s: %s\n", $progname, $type, $file, $msg);
+}
+
+sub lint_warning {
+ my ($file, $msg) = @_;
+ &lint_message("WARNING", $file, $msg);
+ $main::GRC = 1 if ($main::GRC < 1);
+}
+
+sub lint_error {
+ my ($file, $msg) = @_;
+ &lint_message("ERROR", $file, $msg);
+ $main::GRC = 2 if ($main::GRC < 2);
+}
+
+## _________________________________________________________________
+##
+## RPM INFORMATION GATHERING
+## _________________________________________________________________
+##
+
+sub rpm_info {
+ my ($filename, $checks) = @_;
+ my $info = {};
+
+ # query prefix
+ &msg_verbose("++ querying RPM package installation prefix");
+ $info->{prefix} = `$rpm -qp --qf '%{PREFIXES}' $filename`;
+
+ # query file listing
+ &msg_verbose("++ querying RPM package file listing");
+ my @list = `$rpm -qplv $filename`;
+ my @config = `$rpm -qplc $filename`;
+
+ # process file listing
+ $info->{ls} = {};
+ foreach my $entry (@list) {
+ if ($entry =~ m|^\(contains no files\)\s*$|s) {
+ next;
+ }
+ elsif ($entry =~
m|^(\S+)\s+(\d+)\s+(\S+)\s+(\S+)\s+(\d+)\s+(.{12})\s+(.+)\s*$|s) {
+ my ($perm, $links, $owner, $group, $size, $mtime, $path) = ($1, $2, $3,
$4, $5, $6, $7);
+ my $symlink = "";
+ if ($path =~ m|^(\S+)\s+->\s+(\S+)$|) {
+ ($path, $symlink) = ($1, $2);
+ }
+ $path =~ s|\s+$||s;
+ my $config = 0;
+ if (grep(m|^$path$|, @config)) {
+ $config = 1;
+ }
+ $info->{ls}->{$path} = {
+ 'perm' => $perm,
+ 'links' => $links,
+ 'owner' => $owner,
+ 'group' => $group,
+ 'size' => $size,
+ 'time' => $mtime,
+ 'path' => $path,
+ 'symlink' => $symlink,
+ 'config' => $config,
+ };
+ }
+ else {
+ &lint_error($filename, "invalid file listing entry: \"$entry\"");
+ }
+ }
+
+ # unpacking files
+ if (grep(/^content$/, @checks)) {
+ &msg_verbose("++ unpacking RPM package files");
+ $info->{root} = "$tmpdir/root";
+ system("mkdir -p ".$info->{root});
+ system("$rpm2cpio $filename | (cd ".$info->{root}." && cpio -idmu
2>/dev/null)");
+ }
+
+ return $info;
+}
+
+## _________________________________________________________________
+##
+## CHECK "layout": file path layout
+## _________________________________________________________________
+##
+
+sub check_layout {
+ my ($rpm, $info) = @_;
+
+ # no need to check 'openpkg' package because it
+ # has a hard-coded file list!
+ return if ($rpm =~ m|^openpkg-\d+|);
+
+ # check prefix
+ if ($info->{prefix} !~ m|^/.+$|) {
+ &lint_error($rpm, "invalid installation prefix ".$info->{prefix}.
+ " (expected to match \"^/.+\$\")");
+ return;
+ }
+
+ # check top-level path (all-in-one)
+ my @topdirs = (qw(
+ bin cgi etc include info lib libexec
+ local man pub sbin share usr var
+ ));
+ my $topdirs = "{".join(",", @topdirs)."}";
+ if (not keys(%{$info->{ls}})) {
+ &lint_error($rpm, "invalid empty package (expected at least one file)");
+ return;
+ }
+ foreach my $path (keys(%{$info->{ls}})) {
+ my $ok = 0;
+ foreach my $topdir (@topdirs) {
+ my $prefix = quotemeta($info->{prefix} . "/" . $topdir);
+ if ($path =~ m/^$prefix$/ && $rpm !~ m|^openpkg-\d+|) {
+ &lint_error($rpm, "top-level directory \"$topdir\" provided" .
+ " (expected none except for 'openpkg' package)");
+ }
+ if ($path =~ m/^$prefix/) {
+ $ok = 1;
+ last;
+ }
+ }
+ if (not $ok) {
+ &lint_error($rpm, "invalid top-level directory in path \"$path\"".
+ " (expected one of $topdirs)");
+ }
+ }
+
+ # check for second-level path (all-in-one)
+ my @topdirs_subdir_no = (qw(bin cgi info sbin));
+ my @topdirs_subdir_yes = (qw(etc libexec share var));
+ foreach my $path (keys(%{$info->{ls}})) {
+ foreach my $topdir (@topdirs_subdir_yes) {
+ my $prefix = quotemeta($info->{prefix} . "/" . $topdir);
+ if ($path =~ m/^$prefix\/[^\/]+$/) {
+ if ($info->{ls}->{$path}->{perm} !~ m|^d|) {
+ &lint_error($rpm, "invalid positioned file \"$path\" under
topdir \"$topdir\" (expected directory)");
+ }
+ }
+ }
+ foreach my $topdir (@topdirs_subdir_no) {
+ my $prefix = quotemeta($info->{prefix} . "/" . $topdir);
+ if ($path =~ m/^$prefix\/[^\/]+$/) {
+ if ($info->{ls}->{$path}->{perm} =~ m|^d|) {
+ &lint_error($rpm, "invalid positioned directory \"$path\" under
topdir \"$topdir\" (expected file)");
+ }
+ }
+ }
+ }
+
+ # check "bin" and "sbin" directories
+ foreach my $path (keys(%{$info->{ls}})) {
+ foreach my $topdir (qw(bin sbin)) {
+ my $prefix = quotemeta($info->{prefix} . "/" . $topdir);
+ if ($path =~ m/^$prefix\/(.+)$/) {
+ my $file = $1;
+ if ($file =~ m|^[^/]+\.[^/.]+$|) {
+ &lint_warning($rpm, "strange executable filename \"$path\"
containing an extension (expected no extension)");
+ }
+ my $perm = $info->{ls}->{$path}->{'perm'};
+ if ($perm =~ m|^-| && $perm !~ m|^-([-r][-w][sx]){3}$|) {
+ &lint_error($rpm, "non-executable file \"$path\" under topdir
\"$topdir\" (expected to be executable)");
+ }
+ }
+ }
+ }
+}
+
+## _________________________________________________________________
+##
+## CHECK "attrib": file attributes
+## _________________________________________________________________
+##
+
+sub check_attrib {
+ my ($rpm, $info) = @_;
+
+ # permissions
+ # user/group
+ # size
+ # %config flag
+}
+
+## _________________________________________________________________
+##
+## CHECK "content": file content
+## _________________________________________________________________
+##
+
+sub check_content {
+ my ($rpm, $info) = @_;
+
+ # stripped (file)
+ # syslibs (ldd)
+}
+
@@ .
______________________________________________________________________
The OpenPKG Project www.openpkg.org
CVS Repository Commit List [EMAIL PROTECTED]