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