openpkg/lint-rc.pl

Thu, 04 Oct 2012 20:30:05 +0200

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 04 Oct 2012 20:30:05 +0200
changeset 715
c10fb90893b9
permissions
-rw-r--r--

Correct out of date build configuration, porting to Solaris 11 network
link infrastructure and new libpcap logic. This additionally allows for
device drivers in subdirectories of /dev. Correct packaged nmap
personalities and signatures to work out of the box. Finally, hack
arpd logic to properly close sockets and quit on TERM by repeating
signaling in the run command script. Sadly, all this fails to correct
the run time behaviour of honeyd which fails to bind to the IP layer.

     1 ##
     2 ##  lint-rc.pl -- OpenPKG rc.* File Checker
     3 ##  Copyright (c) 2000-2012 OpenPKG GmbH <http://openpkg.com/>
     4 ##
     5 ##  This software is property of the OpenPKG GmbH, DE MUC HRB 160208.
     6 ##  All rights reserved. Licenses which grant limited permission to use,
     7 ##  copy, modify and distribute this software are available from the
     8 ##  OpenPKG GmbH.
     9 ##
    10 ##  THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESSED OR IMPLIED
    11 ##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
    12 ##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
    13 ##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
    14 ##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    15 ##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    16 ##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
    17 ##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
    18 ##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
    19 ##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
    20 ##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
    21 ##  SUCH DAMAGE.
    22 ##
    24 #   Perl run-time requirement
    25 require 5;
    26 BEGIN {
    27     eval "use Getopt::Long; use IO;";
    28     if ($@) {
    29         print STDERR
    30             "lint-rc: ERROR: This command requires a full-size Perl installation!\n" .
    31             "lint-rc: HINT:  Install OpenPKG \"perl\" package to use this command.\n";
    32         exit(1);
    33     }
    34 }
    36 #   OpenPKG instance prefix
    37 my $my_prefix = $ENV{'OPENPKG_PREFIX'};
    38 delete $ENV{'OPENPKG_PREFIX'};
    40 #   program information
    41 my $progname = "lint-rc";
    42 my $progvers = "1.0.0";
    44 #   parameters (defaults)
    45 my $version  = 0;
    46 my $verbose  = 0;
    47 my $help     = 0;
    48 my $check    = 'all';
    49 my $tmpdir   = ($ENV{TMPDIR} || $ENV{TEMPDIR} || "/tmp") . "/$progname";
    50 my $rpm      = "$my_prefix/bin/openpkg rpm";
    52 #   exception handling support
    53 $SIG{__DIE__} = sub {
    54     my ($err) = @_;
    55     $err =~ s|\s+at\s+.*||s if (not $verbose);
    56     print STDERR "$progname:ERROR: $err ". ($! ? "($!)" : "") . "\n";
    57     exit(1);
    58 };
    60 #   command line parsing
    61 Getopt::Long::Configure("bundling");
    62 my $result = GetOptions(
    63     'V|version'     => \$version,
    64     'v|verbose'     => \$verbose,
    65     'h|help'        => \$help,
    66     'c|check=s'     => \$check,
    67     't|tmpdir=s'    => \$tmpdir,
    68     'r|rpm=s'       => \$rpm,
    69 ) || die "option parsing failed";
    70 if ($help) {
    71     print "Usage: $progname [options] [RPMFILE ...]\n" .
    72           "Available options:\n" .
    73           " -v,--verbose       enable verbose run-time mode\n" .
    74           " -h,--help          print out this usage page\n" .
    75           " -c,--check=CHECKS  select checks to perform (default='all')\n" .
    76           " -r,--rpm=FILE      filesystem path to RPM program\n" .
    77           " -t,--tmpdir=PATH   filesystem path to temporary directory\n" .
    78           " -V,--version       print program version\n";
    79     exit(0);
    80 }
    81 if ($version) {
    82     print "OpenPKG $progname $progvers\n";
    83     exit(0);
    84 }
    86 #   verbose message printing
    87 sub msg_verbose {
    88     my ($msg) = @_;
    89     print STDERR "$msg\n" if ($verbose);
    90 }
    92 #   warning message printing
    93 sub msg_warning {
    94     my ($msg) = @_;
    95     print STDERR "$progname:WARNING: $msg\n";
    96 }
    98 #   error message printing
    99 sub msg_error {
   100     my ($msg) = @_;
   101     print STDERR "$progname:ERROR: $msg\n";
   102 }
   104 #   determine check list
   105 my @check_list = (qw(
   106     blank
   107     comment
   108     section
   109     script
   110     global
   111 ));
   112 my @checks = ();
   113 if ($check eq 'all') {
   114     @checks = @check_list;
   115 }
   116 else {
   117     foreach my $c (split(/,/, $check)) {
   118         if (not grep(/^$c$/, @check_list)) {
   119             die "invalid check \"$c\"";
   120         }
   121         push(@checks, $c);
   122     }
   123 }
   125 #   global return code
   126 $main::GRC = 0;
   128 #   environment preparation
   129 system("rm -rf $tmpdir");
   130 system("mkdir -p $tmpdir");
   132 #   iterate over all rc. files
   133 foreach my $filename (@ARGV) {
   134     my $io = new IO::File "<$filename"
   135         or die "unable to open file \"$filename\" for reading";
   136     my $spec; { local $/ = undef; $spec = <$io>; }
   137     $io->close;
   138     foreach my $check (@checks) {
   139         &msg_verbose("$check in $filename");
   140         eval "\&check_$check(\$filename, \$spec);";
   141     }
   142 }
   144 #   environment cleanup
   145 system("rm -rf $tmpdir");
   147 #   die gracefully
   148 exit($main::GRC);
   150 ##  _________________________________________________________________
   151 ##
   152 ##  COMMON SUBROUTINES
   153 ##  _________________________________________________________________
   154 ##
   156 sub lines {
   157     my ($txt) = @_;
   158     my $l = 0;
   159     $txt =~ s|\n|$l++, ''|sge;
   160     return $l;
   161 }
   163 sub lint_message {
   164     my ($type, $file, $done, $this, $msg) = @_;
   165     if (defined($done) and defined($this)) {
   166         my $start = &lines($done) + 1;
   167         my $end = $start + &lines($this);
   168         my $pos = $start;
   169         $pos .= "-". $end if ($end > $start);
   170         printf("%s:%s: %s:%s: %s\n", $progname, $type, $file, $pos, $msg);
   171     }
   172     else {
   173         printf("%s:%s: %s: %s\n", $progname, $type, $file, $msg);
   174     }
   175 }
   177 sub lint_warning {
   178     my ($file, $done, $this, $msg) = @_;
   179     &lint_message("WARNING", $file, $done, $this, $msg);
   180     $main::GRC = 1 if ($main::GRC < 1);
   181 }
   183 sub lint_error {
   184     my ($file, $done, $this, $msg) = @_;
   185     &lint_message("ERROR", $file, $done, $this, $msg);
   186     $main::GRC = 2 if ($main::GRC < 2);
   187 }
   189 ##  _________________________________________________________________
   190 ##
   191 ##  CHECK "blank": whitespace and blank lines
   192 ##  _________________________________________________________________
   193 ##
   195 sub check_blank {
   196     my ($file, $spec) = @_;
   198     #   check for CR-LF combination
   199     my $done = ''; my $this = ''; my $todo = $spec;
   200     while ($todo =~ m/\r\n/s) {
   201         $done .= $`; $this = $&; $todo = $';
   202         &lint_warning($file, $done, $this, "carriage-return (CR, 0x0d) line-feed (NL, 0x0a) combination (expected just line-feed)");
   203         $done .= $this;
   204     }
   206     #   check for multiple blank lines
   207     $done = ''; $this = ''; $todo = $spec;
   208     while ($todo =~ m/(\r?\n[ \t]*){3,}/s) {
   209         $done .= $`; $this = $&; $todo = $';
   210         &lint_warning($file, $done, $this, "multiple subsequent blank lines (expected single blank line)");
   211         $done .= $this;
   212     }
   214     #   check for trailing whitespaces
   215     $done = ''; $this = ''; $todo = $spec;
   216     while ($todo =~ m/[ \t]+\r?\n/s) {
   217         $done .= $`; $this = $&; $todo = $';
   218         if ($done eq '' or $done =~ m|\n$|s) {
   219             &lint_warning($file, $done, $this, "whitespace on empty line (expected none)");
   220         }
   221         else {
   222             &lint_warning($file, $done, $this, "trailing whitespace (expected none)");
   223         }
   224         $done .= $this;
   225     }
   227     #   check for bogus line continuations
   228     $done = ''; $this = ''; $todo = $spec;
   229     while ($todo =~ m/\\[ \t]*\r?\n(?=[ \t]*\r?\n)/s) {
   230         $done .= $`; $this = $&; $todo = $';
   231         &lint_warning($file, $done, $this, "bogus line continuation for following empty line (expect no line continuation)");
   232         $done .= $this;
   233     }
   235     #   check for leading whitespaces before line continuations
   236     $done = ''; $this = ''; $todo = $spec;
   237     while ($todo =~ m/[ \t]{2,}\\[ \t]*\r?\n/s) {
   238         $done .= $`; $this = $&; $todo = $';
   239         &lint_warning($file, $done, $this, "multiple leading whitespace before line continuation (expected just a single space)");
   240         $done .= $this;
   241     }
   243     #   check for leading tabs
   244     $done = ''; $this = ''; $todo = $spec;
   245     while ($todo =~ m/^ *\t+ *[^ \t]/m) {
   246         $done .= $`; $this = $&; $todo = $';
   247         &lint_warning($file, $done, $this, "leading tabs (expected spaces)");
   248         $done .= $this;
   249     }
   251     #   check for mandatory/wished trailing blank line
   252     if ($spec !~ m|\n\n$|s) {
   253         &lint_warning($file, $done, "", "mandatory/wished trailing blank line at end of file missing (expected one)");
   254     }
   255 }
   257 ##  _________________________________________________________________
   258 ##
   259 ##  CHECK "comment": sharp-comments
   260 ##  _________________________________________________________________
   261 ##
   263 sub check_comment {
   264     my ($file, $spec) = @_;
   265     my ($pkg);
   267     #   determine package name
   268     $pkg = $file;
   269     $pkg =~ s|^.+/||;
   270     $pkg =~ s|^rc\.||;
   272     #   check "shebang" header
   273     my $re = "";
   274     $re .= "#!\@l_prefix\@/bin/openpkg rc\\n";
   275     if ($spec !~ m|^$re|s) {
   276         &lint_warning($file, "", "", "invalid shebang header (expected $re)");
   277     }
   279     #   check comment header
   280     my $re = "";
   281     $re .= ".*?\\n##\\n##  rc.$pkg -- Run-Commands\\n##\\n\\n";
   282     if ($pkg ne "openpkg" and $spec !~ m|^$re|s) {
   283         &lint_warning($file, "", "", "invalid comment header (expected $re)");
   284     }
   286     #   check for comment indentation
   287     my $done .= $`; my $this = $&; my $todo = $';
   288     while ($todo =~ m/^([ \t]*)(#+)([ \t]*)(.*?)$/m) {
   289         $done .= $`; $this = $&; $todo = $';
   290         my ($lead, $sharp, $pad, $text) = ($1, $2, $3, $4);
   291         if (length($lead) % 2 != 0) {
   292             &lint_warning($file, $done, $this, "incorrect comment indentation (expected a multiple of 2 spaces)");
   293         }
   294         if (length($lead) > 1 && length($sharp) > 1) {
   295             &lint_warning($file, $done, $this, "indented comment has introduced with multiple sharps (expected single sharp character)");
   296         }
   297         if (length($pad.$text) > 0 && length($sharp.$pad) % 4 != 0) {
   298             &lint_warning($file, $done, $this, "incorrect comment text padding (expected a multiple of 4 sharps or spaces)");
   299         }
   300         if (length($pad) == 0 && length($text) > 0) {
   301             &lint_warning($file, $done, $this, "missing leading space before comment text (expected padding spaces)");
   302         }
   303         if (length($pad) > 0 && length($text) == 0) {
   304             &lint_warning($file, $done, $this, "empty comment text (expected a reasonable text)");
   305         }
   306         $done .= $this;
   307     }
   308 }
   310 ##  _________________________________________________________________
   311 ##
   312 ##  CHECK "section": run command sections
   313 ##  _________________________________________________________________
   314 ##
   316 sub check_section {
   317     my ($file, $spec) = @_;
   319     my $require = qq{
   320         (%config,)?
   321         (%common,)?
   322         (%status,)?
   323         (%info,)?
   324         (%start,)?
   325         (%stop,)?
   326         (%restart,)?
   327         (%reload,)?
   328         (%quarterly,)?
   329         (%hourly,)?
   330         (%daily,)?
   331         (%weekly,)?
   332         (%monthly,)?
   333         (%env,)?
   334     };
   336     #   check for order of headers
   337     my $sections = "";
   338     my $done = ''; my $this = ''; my $todo = $spec;
   339     while ($todo =~ m/^(\S+:|%\S+).*$/m) {
   340         $done .= $`; $this = $&; $todo = $';
   341         my $section = $1;
   342         $sections .= "$section,";
   343         $done .= $this;
   344     }
   345     my $regex = $require;
   346     $regex =~ s|\s+||sg;
   347     if ($sections !~ m/^$regex$/s) {
   348         $regex =~ s|,| |sg;
   349         &lint_error($file, undef, undef, "invalid run command section order (expected \"$regex\")");
   350     }
   351 }
   353 ##  _________________________________________________________________
   354 ##
   355 ##  CHECK "script": shell scripts
   356 ##  _________________________________________________________________
   357 ##
   359 sub check_script {
   360     my ($file, $spec) = @_;
   362     my $done = ''; my $this = ''; my $todo = $spec;
   363     while ($todo =~ m/(\%(?:config|info|common|status|start|stop|restart|reload|quarterly|hourly|daily|weekly|env))([^\n]*)\n(.*?\n)(?=\%(?:config|info|common|status|start|stop|restart|reload|quarterly|hourly|daily|weekly|env)|$)/s) {
   364         $done .= $`; $this = $&; $todo = $';
   365         my ($section, $args, $script) = ($1, $2, $3);
   367         #   perform checks for a single script section
   368         &check_script_section($file, $done, $this, $section, $args, $script);
   370         $done .= $this;
   371     }
   372 }
   374 sub check_script_section {
   375     my ($file, $outer_done, $outer_this, $section, $args, $script) = @_;
   376     my ($done, $this, $todo);
   377     my ($pkg, $pkgu);
   379     #   determine package name
   380     $pkg = $file;
   381     $pkg =~ s|^.+/||;
   382     $pkg =~ s|^rc\.||;
   384     #   determine package name, dash becomes underscore
   385     $pkgu = $pkg;
   386     $pkgu =~ s|-|_|;
   388     #   remove comment contents
   389     $outer_this =~ s|^[ \t]*#[^\n]*\n||mg;
   391     #   check config
   392     if ($section =~ m/^%(config)$/) {
   394         #   check for badly prefixed variables
   395         $done = $outer_done; $this = ''; $todo = $outer_this;
   396         while ($todo =~ m/    [^=]+=[^\n]+/s) {
   397             $done .= $`; $this = $&; $todo = $';
   398             if ($this !~ m/    ([A-Z]+|$pkgu)_[a-z_][a-z0-9_]*=/) {
   399                 &lint_warning($file, $done, $this, "section $section: badly prefixed variable");
   400             }
   401             $done .= $this;
   402         }
   404         #   enforce _enable to default to openpkg_rc_def
   405         $done = $outer_done; $this = ''; $todo = $outer_this;
   406         if ( $todo =~ m/    [^=]+_enable=[^\n]+/s and $todo !~ m/    [^=]+_enable="\$openpkg_rc_def"\n+/s) {
   407             &lint_warning($file, $done, $this, "section $section: wrong default for ${pkgu}_enable");
   408         }
   410         if ($pkg eq "openpkg") {
   411             #   openpkg_rc before _enable, if used, must be the first variable
   412             $done = $outer_done; $this = ''; $todo = $outer_this;
   413             if ( $todo !~ m/%config\n(    [A-Z]+_[a-z_]+=[^\n]*\n)*    openpkg_rc_def=[^\n]+?\n    openpkg_rc_all=[^\n]+?\n    [^=]+_enable=[^\n]+/s) {
   414                 &lint_warning($file, $done, $this, "section $section: openpkg_rc_def, openpkg_rc_all and ${pkgu}_enable must be the first lowercase variable");
   415             }
   416         }
   417         else {
   418             #   _enable, if used, must be the first variable
   419             $done = $outer_done; $this = ''; $todo = $outer_this;
   420             if ( $todo =~ m/    [^=]+_enable=[^\n]+/s and $todo !~ m/%config\n(    [A-Z]+_[a-z_]+=[^\n]*\n)*    [^=]+_enable=[^\n]+/s) {
   421                 &lint_warning($file, $done, $this, "section $section: ${pkgu}_enable must be the first lowercase variable");
   422             }
   423         }
   424     }
   426     if ($section =~ m/^%(config|info|status)$/) {
   427         #   check illegal use of return/exit
   428         $done = $outer_done; $this = ''; $todo = $outer_this;
   429         if ( $todo =~ m/[^a-zA-Z0-9_](return|exit)\s/s ) {
   430             &lint_warning($file, $done, $this, "section $section: return or exit not allowed here");
   431         }
   432         return;
   433     }
   435     #   check rcService only used for enable|usable|active PR#232
   436     $done = $outer_done; $this = ''; $todo = $outer_this;
   437     while ( $todo =~ m/rcService\s+\w+\s+(\w+)/s )  {
   438         $done .= $`; $this = $&; $todo = $';
   439         if ( $1 !~ m/^(enable|usable|active)$/ ) {
   440             &lint_warning($file, $done, $this, "section $section: rcService must check for (enable|usable|active) only, found check for \"$1\"");
   441         }
   442         $done .= $this;
   443     }
   445     #   check rcService short circuit
   446     if ($section !~ m/^%(config|common|info)$/) {
   447         $done = $outer_done; $this = ''; $todo = $outer_this;
   448         if ( $todo !~ m/^[^\n]+\n    rcService $pkg enable yes \|\| exit 0\n/s ) {
   449             &lint_warning($file, $done, $this, "section $section: \"rcService ... enable yes\" short circuit missing");
   450         }
   451         else {
   452             #   check rcService package reference
   453             $done = $outer_done; $this = ''; $todo = $outer_this;
   454             if ( $todo !~ m/\brcService\s+$pkg\s+/s ) {
   455                 &lint_warning($file, $done, $this, "section $section: rcService referencing wrong package");
   456             }
   457         }
   458     }
   460     #   check shell redirections
   461     $done = $outer_done; $this = ''; $todo = $outer_this;
   462     while (   $todo =~ m/[ \t]+(\d+)?[><][ \t]+\S+/s
   463            or $todo =~ m/[ \t]+[><](\&\d+)?[ \t]+\S+/s) {
   464         $done .= $`; $this = $&; $todo = $';
   465         &lint_warning($file, $done, $this, "section $section: whitespace after shell redirection (expected none)");
   466         $done .= $this;
   467     }
   468 }
   470 ##  _________________________________________________________________
   471 ##
   472 ##  CHECK "global": globals
   473 ##  _________________________________________________________________
   474 ##
   476 sub check_global {
   477     my ($file, $spec) = @_;
   479     #   utility function: extract a single shell command
   480     sub command_extract {
   481         my ($script) = @_;
   482         my $cmd = '';
   483         while ($script ne '') {
   484             $script =~ s/^([ \t]*'[^']*')/         $cmd .= $1, ''/se && next;
   485             $script =~ s/^([ \t]*"[^"]*")/         $cmd .= $1, ''/se && next;
   486             $script =~ s/^([ \t]*[^ \t;\)\\\r\n]+)/$cmd .= $1, ''/se && next;
   487             $script =~ s/^([ \t]*\\[ \t]*\r?\n)/   $cmd .= $1, ''/se && next;
   488             last;
   489         }
   490         return ($cmd, $script);
   491     }
   493     #   check for deprecated use of opServiceEnabled function
   494     my $done = ''; my $this = ''; my $todo = $spec;
   495     while ($todo =~ m/\bopServiceEnabled\b/s) {
   496         $done .= $`; $this = $&; $todo = $';
   497         &lint_warning($file, $done, $this, "deprecated usage of opServiceEnabled macro (expected rcService ... enable yes)");
   498         $done .= $this;
   499     }
   501     my $done = ''; my $this = ''; my $todo = $spec;
   502     while ($todo =~ m/shtool\s+/s) {
   503         $done .= $`; $this = $&; $todo = $';
   504         ($this, $todo) = &command_extract($this . $todo);
   506         #   check for shtool options with no space before argument
   507         my $subthis = $this;
   508         $subthis =~ s/%{[^}]*?}//sg;
   509         $subthis =~ s/'[^']*'//sg;
   510         $subthis =~ s/"[^"]*"//sg;
   511         $subthis =~ s/[;|&].*$//s; # catch command termination by semicolon, pipe, or, and;
   512         if ($subthis =~ m/\s-[a-zA-Z]\S/) {
   513             &lint_warning($file, $done, $this, "found use of shtool option with space omitted before argument");
   514         }
   515         $done .= $this;
   516     }
   517 }

mercurial