3 # Maemian -- Maemo package checker
4 # Copyright (C) Jeremiah C. Foster 2009, based on:
6 # Maemian -- Debian package checker
7 # Copyright (C) 1998 Christian Schwarz and Richard Braakman
9 # This program is free software. It is distributed under the terms of
10 # the GNU General Public License as published by the Free Software
11 # Foundation; either version 2 of the License, or (at your option) any
14 # This program is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with this program. If not, you can find it on the World Wide
21 # Web at http://www.gnu.org/copyleft/gpl.html, or write to the Free
22 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
27 maemian - Maemo package checker
31 Maemian is the maemo version of lintian - a policy checker designed to
32 assure the quality of a package uploaded into the maemo.org repositories.
33 The goal of maemian is to improve quality by checking that the maemo
34 packaging policy is followed. In order to do that it reads files in the
35 uploaded deb. Currently maemian only looks at the .dsc file and tries to
36 ascertain who uploaded it, and if they used the correct email address.
40 use strict; # Warnings turned on via -w
44 my $MAEMIAN_VERSION = "0.2"; # External Version number (Where is the canonical version?)
45 my $BANNER = "Maemian v$MAEMIAN_VERSION"; # Version Banner - text form
46 my $LAB_FORMAT = 9; # Lab format Version Number
47 # increased whenever incompatible
48 # changes are done to the lab
49 # so that all packages are re-unpacked
51 # Variables used to record commandline options
52 # Commented out variables have "defined" checks somewhere to determine if
53 # they were set via commandline or environment variables
54 my $pkg_mode = 'a'; # auto -- automatically search for
55 # binary and source pkgs
56 my $verbose = 0; #flag for -v|--verbose switch
57 my $quiet = 0; #flag for -q|--quiet switch
59 my $check_everything = 0; #flag for -a|--all switch
60 my $maemian_info = 0; #flag for -i|--info switch
61 our $display_experimentaltags = 0; #flag for -E|--display-experimental switch
62 our $display_pedantictags = 0; #flag for --pedantic switch
63 my $unpack_level = undef; #flag for -l|--unpack-level switch
64 our $no_override = 0; #flag for -o|--no-override switch
65 our $show_overrides = 0; #flag for --show-overrides switch
66 my $color = 'never'; #flag for --color switch
67 my $check_checksums = 0; #flag for -m|--md5sums|--checksums switch
68 my $allow_root = 0; #flag for --allow-root switch
69 my $fail_on_warnings = 0; #flag for --fail-on-warnings switch
70 my $keep_lab = 0; #flag for --keep-lab switch
71 my $packages_file = 0; #string for the -p option
72 our $OPT_MAEMIAN_LAB = ""; #string for the --lab option
73 our $OPT_MAEMIAN_ARCHIVEDIR = "";#string for the --archivedir option
74 our $OPT_MAEMIAN_DIST = ""; #string for the --dist option
75 our $OPT_MAEMIAN_AREA = ""; #string for the --area option
76 # These options can also be used via default or environment variables
77 our $MAEMIAN_CFG = ""; #config file to use
78 our $MAEMIAN_ROOT = "/home/jeremiah/maemian/"; #location of the maemian modules
79 my $MAEMIAN_ARCH = "any";
81 my $experimental_output_opts = undef;
83 my @severities = qw(wishlist minor normal important serious);
84 my @certainties = qw(wild-guess possible certain);
85 my %display_level = ();
86 my %display_source = ();
101 my %already_scheduled;
107 # reset configuration variables
108 our $MAEMIAN_LAB = undef;
109 our $MAEMIAN_ARCHIVEDIR = undef;
110 our $MAEMIAN_DIST = undef;
111 our $MAEMIAN_UNPACK_LEVEL = undef;
112 our $MAEMIAN_SECTION = undef;
113 our $MAEMIAN_AREA = undef;
115 #turn off file buffering
118 # reset locale definition (necessary for tar)
119 $ENV{'LC_ALL'} = 'C';
120 # reset timezone definition (also for tar)
125 # {{{ Process Command Line
127 #######################################
128 # Subroutines called by various options
129 # in the options hash below. These are
130 # invoked to process the commandline
132 #######################################
133 # Display Command Syntax
137 print <<"EOT-EOT-EOT";
138 Syntax: maemian [action] [options] [--] [packages] ...
140 -S, --setup-lab set up static lab
141 -R, --remove-lab remove static lab
142 -c, --check check packages (default action)
143 -C X, --check-part X check only certain aspects
144 -X X, --dont-check-part X don\'t check certain aspects
145 -T X, --tags X only run checks needed for requested tags
146 --tags-from-file X like --tags, but read list from file
147 -u, --unpack only unpack packages in the lab
148 -r, --remove remove package from the lab
150 -h, --help display short help text
151 -v, --verbose verbose messages
152 -V, --version display Maemian version and exit
153 --print-version print unadorned version number and exit
154 -d, --debug turn Maemian\'s debug messages ON
155 -q, --quiet suppress all informational messages
157 -i, --info give detailed info about tags
158 -I, --display-info display "I:" tags (normally suppressed)
159 -E, --display-experimental display "X:" tags (normally suppressed)
160 --pedantic display "P:" tags (normally suppressed)
161 -L, --display-level display tags with the specified level
162 --display-source X restrict displayed tags by source
163 -l X, --unpack-level X set default unpack level to X
164 -o, --no-override ignore overrides
165 --show-overrides output tags that have been overriden
166 --color never/always/auto disable, enable, or enable color for TTY
167 -U X, --unpack-info X specify which info should be collected
168 -m, --md5sums, --checksums check checksums when processing a .changes file
169 --allow-root suppress maemian\'s warning when run as root
170 --fail-on-warnings return a non-zero exit status if warnings found
171 --keep-lab keep lab after run, even if temporary
172 Configuration options:
173 --cfg CONFIGFILE read CONFIGFILE for configuration
174 --lab LABDIR use LABDIR as permanent laboratory
175 --archivedir ARCHIVEDIR location of Debian archive to scan for packages
176 --dist DIST scan packages in this distribution (e.g. sid)
177 --area AREA scan packages in this archive area (e.g. main)
178 --arch ARCH scan packages with architecture ARCH
179 --root ROOTDIR use ROOTDIR instead of /usr/share/maemian
180 Package selection options:
181 -a, --all process all packages in distribution
182 -b, --binary process only binary packages
183 -s, --source process only source packages
184 --udeb process only udeb packages
185 -p X, --packages-file X process all files in file (special syntax!)
191 # Display Version Banner
192 # Options: -V|--version, --print-version
194 if ($_[0] eq 'print-version') {
195 print "$MAEMIAN_VERSION\n";
202 # Record action requested
203 # Options: -S, -R, -c, -u, -r
206 die("too many actions specified: $_[0]");
211 # Record Parts requested for checking
212 # Options: -C|--check-part
213 sub record_check_part {
214 if (defined $action and $action eq 'check' and $checks) {
215 die("multiple -C or --check-part options not allowed");
218 die("both -C or --check-part and -X or --dont-check-part options not allowed");
221 die("too many actions specified: $_[0]");
227 # Record Parts requested for checking
229 sub record_check_tags {
230 if (defined $action and $action eq 'check' and $check_tags) {
231 die("multiple -T or --tags options not allowed");
234 die("both -T or --tags and -C or --check-part options not allowed");
237 die("both -T or --tags and -X or --dont-check-part options not allowed");
240 die("too many actions specified: $_[0]");
243 $check_tags = "$_[1]";
246 # Record Parts requested for checking
247 # Options: --tags-from-file
248 sub record_check_tags_from_file {
249 open my $file, '<', $_[1]
250 or fail("failed to open $_[1]: $!");
251 my $tags = join(',', map { chomp($_); $_ } <$file>);
254 record_check_tags($_[0], $tags);
258 # Record Parts requested not to check
259 # Options: -X|--dont-check-part X
260 sub record_dont_check_part {
261 if (defined $action and $action eq 'check' and $dont_check) {
262 die("multiple -X or --dont-check-part options not allowed");
265 die("both -C or --check-part and -X or --dont-check-part options not allowed");
268 die("too many actions specified: $_[0]");
271 $dont_check = "$_[1]";
275 # Process for -U|--unpack-info flag
276 sub record_unpack_info {
278 die("multiple -U or --unpack-info options not allowed");
280 $unpack_info = "$_[1]";
283 # Record what type of data is specified
284 # Options: -b|--binary, -s|--source, --udeb
286 $pkg_mode = 'b' if $_[0] eq 'binary';
287 $pkg_mode = 's' if $_[0] eq 'source';
288 $pkg_mode = 'u' if $_[0] eq 'udeb';
291 # Process -L|--display-level flag
292 sub record_display_level {
294 if ($level =~ m/^\+(.+)/) {
295 set_display_level($1, 1);
296 } elsif ($level =~ m/^\-(.+)/) {
297 set_display_level($1, 0);
298 } elsif ($level =~ m/^\=?(.+)/) {
299 reset_display_level();
300 set_display_level($1, 1);
302 die "invalid argument to --display-level: $level\n";
306 # Process -I|--display-info flag
307 sub display_infotags {
308 foreach my $s (@severities) {
309 set_display_level($s, 1);
313 # Process --display-source flag
314 sub record_display_source {
315 $display_source{$_[1]} = 1;
318 # Clears current display level information, disabling all severities and
320 sub reset_display_level {
321 foreach my $s (@severities) {
322 foreach my $c (@certainties) {
323 $display_level{$s}{$c} = 0;
328 sub set_display_level_multi {
329 my ($op, $level, $val) = @_;
331 my @inc_severities = @severities;
332 my @inc_certainties = @certainties;
333 my $inc_border = ($op =~ /^[<>]=$/) ? 1 : 0;
335 @inc_severities = reverse @inc_severities;
336 @inc_certainties = reverse @inc_certainties;
338 my $severity = join("|", @severities);
339 my $certainty = join("|", @certainties);
340 if ($level =~ m/^($severity)$/) {
341 foreach my $s (cut_list($level, $inc_border, @inc_severities)) {
342 map { $display_level{$s}{$_} = $val } @certainties;
344 } elsif ($level =~ m/^($certainty)$/) {
345 foreach my $c (cut_list($level, $inc_border, @inc_certainties)) {
346 map { $display_level{$_}{$c} = $val } @severities;
348 } elsif ($level =~ m/^($severity)\/($certainty)$/) {
349 foreach my $s (cut_list($1, $inc_border, @inc_severities)) {
350 foreach my $c (cut_list($2, $inc_border, @inc_certainties)) {
351 $display_level{$s}{$c} = $val;
355 die "invalid argument to --display-level: $level\n";
361 my ($border, $inc_border, @list) = @_;
363 my (@newlist, $found);
366 push @newlist, $_ if $inc_border;
373 die "internal error: cut_list did not find border $border\n"
375 if (!$inc_border and !@newlist
376 and $border eq $list[0]) {
377 warn "warning: display level $border specified with > (or <) is empty set, assuming >= (or <=)\n";
378 push @newlist, $list[0];
384 # Parse input display level to enable (val 1) or disable (val 0) it
386 sub set_display_level {
387 my ($level, $val) = @_;
388 if ($level =~ m/^([<>]=?)(.+)/) {
389 set_display_level_multi($1, $2, $val);
393 my $severity = join("|", @severities);
394 my $certainty = join("|", @certainties);
395 if ($level =~ m/^($severity)$/) {
396 map { $display_level{$1}{$_} = $val } @certainties;
397 } elsif ($level =~ m/^($certainty)$/) {
398 map { $display_level{$_}{$1} = $val } @severities;
399 } elsif ($level =~ m/^($severity)\/($certainty)$/) {
400 $display_level{$1}{$2} = $val;
402 die "invalid argument to --display-level: $level\n";
406 # Hash used to process commandline options
408 # ------------------ actions
409 "setup-lab|S" => \&record_action,
410 "remove-lab|R" => \&record_action,
411 "check|c" => \&record_action,
412 "check-part|C=s" => \&record_check_part,
413 "tags|T=s" => \&record_check_tags,
414 "tags-from-file=s" => \&record_check_tags_from_file,
415 "dont-check-part|X=s" => \&record_dont_check_part,
416 "unpack|u" => \&record_action,
417 "remove|r" => \&record_action,
419 # ------------------ general options
420 "help|h" => \&syntax,
421 "version|V" => \&banner,
422 "print-version" => \&banner,
424 "verbose|v" => \$verbose,
425 "debug|d" => \@debug, # Count the -d flags
426 "quiet|q" => \$quiet,
428 # ------------------ behaviour options
429 "info|i" => \$maemian_info,
430 "display-info|I" => \&display_infotags,
431 "display-experimental|E" => \$display_experimentaltags,
432 "pedantic" => \$display_pedantictags,
433 "display-level|L=s" => \&record_display_level,
434 "display-source=s" => \&record_display_source,
435 "unpack-level|l=i" => \$unpack_level,
436 "no-override|o" => \$no_override,
437 "show-overrides" => \$show_overrides,
438 "color=s" => \$color,
439 "unpack-info|U=s" => \&record_unpack_info,
440 "checksums|md5sums|m" => \$check_checksums,
441 "allow-root" => \$allow_root,
442 "fail-on-warnings" => \$fail_on_warnings,
443 "keep-lab" => \$keep_lab,
444 # Note: Ubuntu has (and other derivatives might gain) a
445 # -D/--debian option to make maemian behave like in Debian, that
446 # is, to revert distribution-specific changes
448 # ------------------ configuration options
449 "cfg=s" => \$MAEMIAN_CFG,
450 "lab=s" => \$OPT_MAEMIAN_LAB,
451 "archivedir=s" => \$OPT_MAEMIAN_ARCHIVEDIR,
452 "dist=s" => \$OPT_MAEMIAN_DIST,
453 "area=s" => \$OPT_MAEMIAN_AREA,
454 "section=s" => \$OPT_MAEMIAN_AREA,
455 "root=s" => \$MAEMIAN_ROOT,
457 # ------------------ package selection options
458 "all|a" => \$check_everything,
459 "binary|b" => \&record_pkgmode,
460 "source|s" => \&record_pkgmode,
461 "udeb" => \&record_pkgmode,
462 "packages-file|p=s" => \$packages_file,
464 # ------------------ experimental
465 "exp-output:s" => \$experimental_output_opts,
468 # init display level settings
469 reset_display_level();
470 set_display_level_multi('>=', 'important', 1);
471 set_display_level_multi('>=', 'normal/possible', 1);
472 set_display_level('minor/certain', 1);
474 # init commandline parser
475 Getopt::Long::config('bundling', 'no_getopt_compat', 'no_auto_abbrev');
477 # process commandline options
479 or die("error parsing options\n");
481 # determine current working directory--we'll need this later
484 # determine MAEMIAN_ROOT if it was not set with --root.
485 $MAEMIAN_ROOT = $MAEMIAN_ROOT || $ENV{'MAEMIAN_ROOT'};
486 if (defined $MAEMIAN_ROOT) {
487 unless ($MAEMIAN_ROOT =~ m,^/,) {
488 $MAEMIAN_ROOT = "$cwd/$MAEMIAN_ROOT";
491 $MAEMIAN_ROOT = '/usr/share/maemian';
494 # keep-lab implies unpack-level=2 unless explicetly
496 if ($keep_lab and not defined $unpack_level) {
500 # option --all and packages specified at the same time?
501 if (($check_everything or $packages_file) and $#ARGV+1 > 0) {
502 print STDERR "warning: options -a or -p can't be mixed with package parameters!\n";
503 print STDERR "(will ignore -a or -p option)\n";
504 undef $check_everything;
505 undef $packages_file;
508 # check permitted values for --color
509 if ($color and $color !~ /^(never|always|auto|html)$/) {
510 die "invalid argument to --color: $color\n";
513 # check specified action
514 $action = 'check' unless $action;
516 # check for arguments
517 if ($action =~ /^(check|unpack|remove)$/ and $#ARGV == -1 and not $check_everything and not $packages_file) {
523 # {{{ Setup Configuration
526 # check if effective UID is 0
527 if ($> == 0 and not $allow_root) {
528 print STDERR "warning: maemian's authors do not recommend running it with root privileges!\n";
531 # search for configuration file if it was not set with --cfg
532 # do not search the default locations if it was set.
534 } elsif (exists $ENV{'MAEMIAN_CFG'} &&
535 -f ($MAEMIAN_CFG = $ENV{'MAEMIAN_CFG'})) {
536 } elsif (-f ($MAEMIAN_CFG = $MAEMIAN_ROOT . '/maemianrc')) {
537 } elsif (exists $ENV{'HOME'} &&
538 -f ($MAEMIAN_CFG = $ENV{'HOME'} . '/.maemianrc')) {
539 } elsif (-f ($MAEMIAN_CFG = '/etc/maemianrc')) {
544 use constant VARS => qw(LAB ARCHIVEDIR DIST UNPACK_LEVEL SECTION AREA ARCH);
545 # read configuration file
547 open(CFG, '<', $MAEMIAN_CFG)
548 or die("cannot open configuration file $MAEMIAN_CFG for reading: $!");
555 # substitute some special variables
556 s,\$HOME/,$ENV{'HOME'}/,go;
557 s,\~/,$ENV{'HOME'}/,go;
560 foreach my $var (VARS) {
562 $var = "MAEMIAN_$var";
563 if (m/^\s*$var\s*=\s*(.*\S)\s*$/i) {
570 die "syntax error in configuration file: $_\n";
576 # environment variables overwrite settings in conf file:
579 my $var = "MAEMIAN_$_";
580 my $opt_var = "OPT_$var";
581 $$var = $ENV{$var} if $ENV{$var};
582 $$var = $$opt_var if $$opt_var;
585 # MAEMIAN_SECTION is deprecated in favour of MAEMIAN_AREA
586 if (defined $MAEMIAN_SECTION) {
587 print STDERR "warning: MAEMIAN_SECTION has been deprecated in favour of MAEMIAN_AREA.\n";
588 if (defined $MAEMIAN_AREA) {
589 print STDERR "Using MAEMIAN_AREA as both were defined.\n";
591 print STDERR "Both are currently accepted, but MAEMIAN_SECTION may be removed\n";
592 print STDERR "in a future Maemian release.\n";
593 $MAEMIAN_AREA = $MAEMIAN_SECTION;
597 # determine requested unpack level
598 if (defined($unpack_level)) {
599 # specified through command line
600 } elsif (defined($MAEMIAN_UNPACK_LEVEL)) {
601 # specified via configuration file or env variable
602 $unpack_level = $MAEMIAN_UNPACK_LEVEL;
604 # determine by action
605 if (($action eq 'unpack') or ($action eq 'check')) {
611 unless (($unpack_level == 0) or ($unpack_level == 1) or ($unpack_level == 2)) {
612 die("bad unpack level $unpack_level specified");
615 $MAEMIAN_UNPACK_LEVEL = $unpack_level;
617 # export current settings for our helper scripts
618 foreach (('ROOT', 'CFG', VARS)) {
620 my $var = "MAEMIAN_$_";
629 my $debug = $#debug + 1;
630 $verbose = 1 if $debug;
631 $ENV{'MAEMIAN_DEBUG'} = $debug;
633 # Loading maeian's own libraries (now that MAEMIAN_ROOT is known)
634 unshift @INC, "$MAEMIAN_ROOT/lib";
639 require Read_pkglists;
646 require Maemian::Data;
647 require Maemian::Schedule;
648 require Maemian::Output;
649 import Maemian::Output qw(:messages);
650 require Maemian::Command;
651 import Maemian::Command qw(spawn reap);
652 require Maemian::Check;
653 import Maemian::Check qw(check_maintainer);
656 if (defined $experimental_output_opts) {
657 my %opts = map { split(/=/) } split( /,/, $experimental_output_opts );
658 foreach (keys %opts) {
659 if ($_ eq 'format') {
660 if ($opts{$_} eq 'colons') {
661 require Maemian::Output::ColonSeparated;
662 $Maemian::Output::GLOBAL = new Maemian::Output::ColonSeparated;
663 } elsif ($opts{$_} eq 'letterqualifier') {
664 require Maemian::Output::LetterQualifier;
665 $Maemian::Output::GLOBAL = new Maemian::Output::LetterQualifier;
666 } elsif ($opts{$_} eq 'xml') {
667 require Maemian::Output::XML;
668 $Maemian::Output::GLOBAL = new Maemian::Output::XML;
672 ${"Tags::$_"} = $opts{$_};
676 $Maemian::Output::GLOBAL->verbose($verbose);
677 $Maemian::Output::GLOBAL->debug($debug);
678 $Maemian::Output::GLOBAL->quiet($quiet);
679 $Maemian::Output::GLOBAL->color($color);
680 $Maemian::Output::GLOBAL->showdescription($maemian_info);
682 # Print Debug banner, now that we're finished determining
683 # the values and have Maemian::Output available
686 "Maemian root directory: $MAEMIAN_ROOT",
687 "Configuration file: $MAEMIAN_CFG",
688 "Laboratory: $MAEMIAN_LAB",
689 "Archive directory: $MAEMIAN_ARCHIVEDIR",
690 "Distribution: $MAEMIAN_DIST",
691 "Default unpack level: $MAEMIAN_UNPACK_LEVEL",
692 "Architecture: $MAEMIAN_ARCH",
696 my @l_secs = read_dpkg_control("$MAEMIAN_ROOT/checks/maemian.desc");
698 map { $_->{'script'} = 'maemian'; Tags::add_tag($_) } @l_secs;
700 $Tags::show_experimental = $display_experimentaltags;
701 $Tags::show_pedantic = $display_pedantictags;
702 $Tags::show_overrides = $show_overrides;
703 %Tags::display_level = %display_level;
704 %Tags::display_source = %display_source;
705 %Tags::only_issue_tags = map { $_ => 1 } (split(/,/, $check_tags))
707 if defined $check_tags;
709 use vars qw(%source_info %binary_info %udeb_info); # from the above
711 # Set up clean-up handlers.
712 undef $cleanup_filename;
713 $SIG{'INT'} = \&interrupted;
714 $SIG{'QUIT'} = \&interrupted;
718 # {{{ Create/Maintain Lab and add any specified Debian Archives (*.debs)
720 $LAB = new Lab( $MAEMIAN_LAB, $MAEMIAN_DIST );
722 #######################################
724 if ($action eq 'setup-lab') {
725 if ($#ARGV+1 > 0) { # Cannot define Lab on the command line.
726 warning("ignoring additional command line arguments");
730 or fail("There was an error while setting up the static lab.");
734 #######################################
736 } elsif ($action eq 'remove-lab') {
738 warning("ignoring additional command line arguments");
741 $LAB->delete_static()
742 or fail("There was an error while removing the static lab.");
746 #######################################
747 # Check for non deb specific actions
748 } elsif (not (($action eq 'unpack') or ($action eq 'check')
749 or ($action eq 'remove'))) {
750 fail("bad action $action specified");
754 fail("maemian lab has not been set up correctly (perhaps you forgot to run maemian --setup-lab?)")
755 unless $LAB->is_lab();
757 #XXX: There has to be a cleaner way to do this
758 $MAEMIAN_LAB = $LAB->{dir};
762 # {{{ Compile list of files to process
764 $schedule = new Maemian::Schedule(verbose => $verbose);
765 # process package/file arguments
766 while (my $arg = shift) {
769 # $arg contains absolute dir spec?
770 unless ($arg =~ m,^/,) {
775 if ($arg =~ /\.deb$/) {
776 $schedule->add_deb('b', $arg)
777 or warning("$arg is a zero-byte file, skipping");
780 elsif ($arg =~ /\.udeb$/) {
781 $schedule->add_deb('u', $arg)
782 or warning("$arg is a zero-byte file, skipping");
785 elsif ($arg =~ /\.dsc$/) {
786 $schedule->add_dsc($arg)
787 or warning("$arg is a zero-byte file, skipping");
790 elsif ($arg =~ /\.changes$/) {
791 # get directory and filename part of $arg
792 my ($arg_dir, $arg_name) = $arg =~ m,(.*)/([^/]+)$,;
794 v_msg("Processing changes file $arg_name ...");
796 my ($data) = read_dpkg_control($arg);
797 if (not defined $data) {
798 warning("$arg is a zero-byte file, skipping");
801 Tags::set_pkg( $arg, $arg_name, "", "", 'binary' );
803 # If we don't have a Format key, something went seriously wrong.
804 # Tag the file and skip remaining processing.
805 if (!$data->{'format'}) {
806 tag('malformed-changes-file');
810 # Description is mandated by dak, but only makes sense if binary
811 # packages are included. Don't tag pure source uploads.
812 if (!$data->{'description'} && $data->{'architecture'} ne 'source') {
813 tag("no-description-in-changes-file");
816 # check distribution field
817 if (defined $data->{distribution}) {
818 my $ubuntu_dists = Maemian::Data->new ('changelog-file/ubuntu-dists');
819 my $ubuntu_regex = join('|', $ubuntu_dists->all);
820 my @distributions = split /\s+/o, $data->{distribution};
821 for my $distribution (@distributions) {
822 if ($distribution eq 'UNRELEASED') {
824 } elsif ($data->{version} =~ /ubuntu|$ubuntu_regex/
825 or $distribution =~ /$ubuntu_regex/) {
826 if ($distribution !~ /^($ubuntu_regex)(-(proposed|updates|backports|security))?$/ ) {
827 tag("bad-ubuntu-distribution-in-changes-file",
830 } elsif (! (($distribution eq 'oldstable')
831 or ($distribution eq 'stable')
832 or ($distribution eq 'testing')
833 or ($distribution eq 'unstable')
834 or ($distribution eq 'experimental')
835 or ($distribution =~ /^\w+-backports$/)
836 or ($distribution =~ /^\w+-proposed-updates$/)
837 or ($distribution =~ /^\w+-security$/))
839 # bad distribution entry
840 tag("bad-distribution-in-changes-file",
845 if ($#distributions > 0) {
846 # Currently disabled until dak stops accepting the syntax
847 # tag("multiple-distributions-in-changes-file",
848 # $data->{'distribution'});
852 # Urgency is only recommended by Policy.
853 if (!$data->{'urgency'}) {
854 tag("no-urgency-in-changes-file");
856 my $urgency = lc $data->{'urgency'};
858 unless ($urgency =~ /^(low|medium|high|critical|emergency)$/i) {
859 tag("bad-urgency-in-changes-file", $data->{'urgency'});
863 # Changed-By is optional in Policy, but if set, must be
864 # syntactically correct. It's also used by dak.
865 if ($data->{'changed-by'}) {
866 check_maintainer($data->{'changed-by'}, 'changed-by');
869 # process all listed `files:'
872 my $file_list = $data->{files} || '';
873 for ( split /\n/, $file_list ) {
878 my ($md5sum,$size,$section,$priority,$file) = split(/\s+/o, $_);
879 $files{$file}{md5} = $md5sum;
880 $files{$file}{size} = $size;
883 if (($section eq 'non-free') or ($section eq 'contrib')) {
884 tag( "bad-section-in-changes-file", $file, $section );
889 foreach my $alg (qw(sha1 sha256)) {
890 my $list = $data->{"checksums-$alg"} || '';
891 for ( split /\n/, $list ) {
896 my ($checksum,$size,$file) = split(/\s+/o, $_);
897 $files{$file}{$alg} = $checksum;
898 if ($files{$file}{size} != $size) {
899 tag( "file-size-mismatch-in-changes-file", $file,
900 "$files{$file}{size} != $size" );
906 foreach my $file (keys %files) {
907 my $filename = $arg_dir . '/' . $file;
910 if (not -f $filename) {
911 warning("$file does not exist, exiting");
915 if ($size ne $files{$file}{size}) {
916 tag( "file-size-mismatch-in-changes-file", $file,
917 "$files{$file}{size} != $size");
921 if ($check_checksums or $file =~ /\.dsc$/) {
922 foreach my $alg (qw(md5 sha1 sha256)) {
923 next unless exists $files{$file}{$alg};
925 my $real_checksum = get_file_checksum($alg, $filename);
927 if ($real_checksum ne $files{$file}{$alg}) {
928 tag( "checksum-mismatch-in-changes-file", $alg, $file );
934 if ($file =~ /\.dsc$/) {
935 $schedule->add_dsc($filename);
936 } elsif ($file =~ /\.deb$/) {
937 $schedule->add_deb('b', $filename);
938 } elsif ($file =~ /\.udeb$/) {
939 $schedule->add_deb('u', $filename);
943 unless ($exit_code) {
944 my $stats = Tags::get_stats( $arg );
945 if ($stats->{types}{E}) {
947 } elsif ($fail_on_warnings && $stats->{types}{W}) {
953 fail("bad package file name $arg (neither .deb, .udeb or .dsc file)");
956 # parameter is a package name--so look it up
957 # search the distribution first, then the lab
958 # special case: search only in lab if action is `remove'
961 if ($action eq 'remove') {
962 # search only in lab--see below
965 # search in dist, then in lab
966 $search = 'dist or lab';
971 read_src_list("$MAEMIAN_LAB/info/source-packages", 0);
972 read_bin_list("$MAEMIAN_LAB/info/binary-packages", 0);
973 read_udeb_list("$MAEMIAN_LAB/info/udeb-packages", 0);
975 if (($pkg_mode eq 'b') or ($pkg_mode eq 'a')) {
976 if ($binary_info{$arg}) {
977 $schedule->add_file('b', "$MAEMIAN_ARCHIVEDIR/$binary_info{$arg}->{'file'}",
978 %{$binary_info{$arg}});
982 if (($pkg_mode eq 'u') or ($pkg_mode eq 'a')) {
983 if ($udeb_info{$arg}) {
984 $schedule->add_file('u', "$MAEMIAN_ARCHIVEDIR/$udeb_info{$arg}->{'file'}",
985 %{$udeb_info{$arg}});
989 if (($pkg_mode eq 's') or ($pkg_mode eq 'a')) {
990 if ($source_info{$arg}) {
991 $schedule->add_file('s', "$MAEMIAN_ARCHIVEDIR/$source_info{$arg}->{'file'}",
992 %{$source_info{$arg}});
1000 # nothing found so far, so search the lab
1002 my $b = "$MAEMIAN_LAB/binary/$arg";
1003 my $s = "$MAEMIAN_LAB/source/$arg";
1004 my $u = "$MAEMIAN_LAB/udeb/$arg";
1006 if ($pkg_mode eq 'b') {
1008 warn "error: cannot find binary package $arg in $search (skipping)\n";
1012 } elsif ($pkg_mode eq 's') {
1014 warning("cannot find source package $arg in $search (skipping)");
1018 } elsif ($pkg_mode eq 'u') {
1020 warning("cannot find udeb package $arg in $search (skipping)");
1026 unless (-d $b or -d $s or -d $u) {
1027 warning("cannot find binary, udeb or source package $arg in $search (skipping)");
1033 if (($pkg_mode eq 'b') or (($pkg_mode eq 'a') and (-d $b))) {
1034 $schedule->add_file('b', get_bin_info_from_lab($b));
1036 if (($pkg_mode eq 's') or (($pkg_mode eq 'a') and (-d $s))) {
1037 $schedule->add_file('s', get_src_info_from_lab($s));
1039 if (($pkg_mode eq 'u') or (($pkg_mode eq 'a') and (-d $u))) {
1040 $schedule->add_file('u', get_bin_info_from_lab($u));
1045 if (not $check_everything and not $packages_file and not $schedule->count) {
1046 v_msg("No packages selected.");
1050 # Check to make sure there are packages to check.
1052 my ($f,$target,$field,$source,$required) = @_;
1054 # print map { Dump($_) } @_;
1055 if ($required and not $source->{$field}) {
1056 print Dump($f)."\n";
1057 fail("description file $f does not define required tag $field");
1059 $target->{$field} = $source->{$field};
1060 delete $source->{$field};
1063 opendir(COLLDIR, "$MAEMIAN_ROOT/collection")
1064 or fail("cannot read directory $MAEMIAN_ROOT/collection");
1066 for my $f (readdir COLLDIR) {
1067 next if $f =~ /^\./;
1068 next unless $f =~ /\.desc$/;
1070 debug_msg(2, "Reading collector description file $f ...");
1071 my @secs = read_dpkg_control("$MAEMIAN_ROOT/collection/$f");
1074 or fail("syntax error in description file $f: too many sections");
1076 ($script = $secs[0]->{'collector-script'})
1077 or fail("error in description file $f: `Collector-Script:' not defined");
1079 delete $secs[0]->{'collector-script'};
1080 $collection_info{$script}->{'script'} = $script;
1081 my $p = $collection_info{$script};
1083 set_value($f, $p,'type',$secs[0],1);
1085 my ($b,$s,$u) = ( "", "", "" );;
1086 for (split(/\s*,\s*/o,$p->{'type'})) {
1087 if ($_ eq 'binary') {
1089 } elsif ($_ eq 'source') {
1091 } elsif ($_ eq 'udeb') {
1094 fail("unknown type $_ specified in description file $f");
1097 $p->{'type'} = "$s$b$u";
1099 set_value($f,$p,'unpack-level',$secs[0],1);
1100 set_value($f,$p,'order',$secs[0],1);
1101 set_value($f,$p,'version',$secs[0],1);
1103 if (exists $secs[0]->{'needs-info'} && defined $secs[0]->{'needs-info'}) {
1104 for (split(/\s*,\s*/o,$secs[0]->{'needs-info'})) {
1107 delete $secs[0]->{'needs-info'};
1110 # ignore Info: and other fields for now
1111 delete $secs[0]->{'info'};
1112 delete $secs[0]->{'author'};
1114 for (keys %{$secs[0]}) {
1115 warning("unused tag $_ in description file $f");
1118 debug_msg(2, map( { "$_: $p->{$_}" } sort keys %$p ));
1124 # {{{ Now we're ready to load info about checks & tags
1126 # load information about checker scripts
1127 opendir(CHECKDIR, "$MAEMIAN_ROOT/checks")
1128 or fail("cannot read directory $MAEMIAN_ROOT/checks");
1130 for my $f (readdir CHECKDIR) {
1131 next if $f =~ /^\./;
1132 next unless $f =~ /\.desc$/;
1133 debug_msg(2, "Reading checker description file $f ...");
1135 my @secs = read_dpkg_control("$MAEMIAN_ROOT/checks/$f");
1137 ($script = $secs[0]->{'check-script'})
1138 or fail("error in description file $f: `Check-Script:' not defined");
1140 # ignore check `maemian' (this check is a special case and contains the
1141 # tag info for the maemian frontend--this script here)
1142 next if $script eq 'maemian';
1144 delete $secs[0]->{'check-script'};
1145 $check_info{$script}->{'script'} = $script;
1146 my $p = $check_info{$script};
1148 set_value($f,$p,'type',$secs[0],1);
1150 my ($b,$s,$u) = ( "", "", "" );
1151 for (split(/\s*,\s*/o,$p->{'type'})) {
1152 if ($_ eq 'binary') {
1154 } elsif ($_ eq 'source') {
1156 } elsif ($_ eq 'udeb') {
1159 fail("unknown type $_ specified in description file $f");
1162 $p->{'type'} = "$s$b$u";
1164 set_value($f,$p,'unpack-level',$secs[0],1);
1165 set_value($f,$p,'abbrev',$secs[0],1);
1167 if (exists $secs[0]->{'needs-info'} && defined $secs[0]->{'needs-info'}) {
1168 for (split(/\s*,\s*/o,$secs[0]->{'needs-info'})) {
1171 delete $secs[0]->{'needs-info'};
1174 # ignore Info: and other fields for now...
1175 delete $secs[0]->{'info'};
1176 delete $secs[0]->{'standards-version'};
1177 delete $secs[0]->{'author'};
1179 for (keys %{$secs[0]}) {
1180 warning("unused tag $_ in description file $f");
1183 debug_msg(2, map( { "$_: $p->{$_}" } sort keys %$p ));
1186 $p->{'requested-tags'} = 0;
1187 foreach my $tag (@secs) {
1188 $tag->{'script'} = $script;
1189 Tags::add_tag($tag);
1190 $p->{'requested-tags'}++ if Tags::display_tag($tag);
1198 # {{{ Again some lone code the author just dumped where his cursor just happened to be
1200 # determine which info has been requested
1201 for my $i (split(/,/,$unpack_info)) {
1202 unless ($collection_info{$i}) {
1203 fail("unknown info specified: $i");
1205 $unpack_infos{$i} = 1;
1209 # create check_abbrev hash
1210 for my $c (keys %check_info) {
1211 $check_abbrev{$check_info{$c}->{'abbrev'}} = $c;
1216 # {{{ determine which checks have been requested
1217 if ($action eq 'check') {
1219 foreach my $t (split(/,/, $check_tags)) {
1220 my $info = Tags::get_tag_info($t);
1222 fail("unknown tag specified: $t") unless defined($info);
1223 my $script = $info->{'script'};
1224 next if $script eq 'maemian';
1225 if ($check_info{$script}) {
1226 $checks{$script} = 1;
1228 # should never happen
1229 fail("no info for script $script");
1233 my %dont_check = map { $_ => 1 } (split m/,/, ($dont_check || ""));
1234 $checks or ($checks = join(',',keys %check_info));
1235 for my $c (split(/,/,$checks)) {
1236 if ($check_info{$c}) {
1238 || ($check_info{$c}->{'abbrev'}
1239 && $dont_check{$check_info{$c}->{'abbrev'}})) {
1240 #user requested not to run this check
1241 } elsif ($check_info{$c}->{'requested-tags'} == 0) {
1242 #no need to run this check, no tags will be issued
1246 } elsif (exists $check_abbrev{$c}) {
1247 #abbrevs only used when -C is given, so we don't need %dont_check
1248 $checks{$check_abbrev{$c}} = 1;
1250 fail("unknown check specified: $c");
1255 # determine which info is needed by the checks
1256 for my $c (keys %checks) {
1257 for my $i (keys %collection_info) {
1259 if ($check_info{$c}->{$i}) {
1260 $unpack_infos{$i} = 1;
1268 # {{{ determine which info is needed by the collection scripts
1269 for my $c (keys %unpack_infos) {
1270 for my $i (keys %collection_info) {
1272 if ($collection_info{$c}->{$i}) {
1273 $unpack_infos{$i} = 1;
1279 # {{{ process all packages in the archive?
1280 if ($check_everything) {
1281 # make sure package info is available
1282 read_src_list("$MAEMIAN_LAB/info/source-packages", 0);
1283 read_bin_list("$MAEMIAN_LAB/info/binary-packages", 0);
1284 read_udeb_list("$MAEMIAN_LAB/info/udeb-packages", 0);
1286 debug_msg(2, "pkg_mode = $pkg_mode");
1288 if (($pkg_mode eq 'a') or ($pkg_mode eq 's')) {
1289 for my $arg (sort keys %source_info) {
1290 debug_msg(1, "doing stuff with $MAEMIAN_ARCHIVEDIR/$source_info{$arg}->{'file'}");
1291 $schedule->add_file('s', "$MAEMIAN_ARCHIVEDIR/$source_info{$arg}->{'file'}",
1292 %{$source_info{$arg}});
1295 if (($pkg_mode eq 'a') or ($pkg_mode eq 'b')) {
1296 for my $arg (sort keys %binary_info) {
1297 debug_msg(1, "doing stuff with $MAEMIAN_ARCHIVEDIR/$binary_info{$arg}->{'file'}");
1298 $schedule->add_file('b', "$MAEMIAN_ARCHIVEDIR/$binary_info{$arg}->{'file'}",
1299 %{$binary_info{$arg}});
1302 if (($pkg_mode eq 'a') or ($pkg_mode eq 'u')) {
1303 for my $arg (sort keys %udeb_info) {
1304 debug_msg(1, "doing stuff with $MAEMIAN_ARCHIVEDIR/$udeb_info{$arg}->{'file'}");
1305 $schedule->add_file('u', "$MAEMIAN_ARCHIVEDIR/$udeb_info{$arg}->{'file'}",
1306 %{$udeb_info{$arg}});
1310 # package list still empty?
1311 unless ($schedule->count) {
1312 warning("no packages found in distribution directory");
1314 } elsif ($packages_file) { # process all packages listed in packages file?
1315 $schedule->add_pkg_list($packages_file);
1319 # {{{ Some silent exit
1320 unless ($schedule->count) {
1321 v_msg("No packages selected.");
1326 # {{{ Okay, now really processing the packages in one huge loop
1327 $unpack_infos{ "override-file" } = 1 unless $no_override;
1328 v_msg(sprintf("Processing %d packages...", $schedule->count));
1330 "Selected action: $action",
1331 "Requested unpack level: $unpack_level",
1332 sprintf("Requested data to collect: %s", join(',',keys %unpack_infos)),
1333 sprintf("Selected checks: %s", join(',',keys %checks)),
1337 require Maemian::Collect;
1342 foreach my $pkg_info ($schedule->get_all) {
1343 my ($type, $pkg, $ver, $arch, $file) =
1344 @$pkg_info{qw(type package version architecture file)};
1345 my $long_type = ($type eq 'b' ? 'binary' :
1346 ($type eq 's' ? 'source' : 'udeb' ));
1348 Tags::set_pkg( $file, $pkg, $ver, $arch, $long_type );
1350 # Kill pending jobs, if any
1351 Maemian::Command::kill(@pending_jobs);
1352 undef @pending_jobs;
1354 # determine base directory
1355 my $base = "$MAEMIAN_LAB/$long_type/$pkg";
1356 unless ($base =~ m,^/,) {
1357 $base = "$cwd/$base";
1359 debug_msg(1, "Base directory in lab: $base");
1361 my $act_unpack_level = 0;
1363 # unpacked package up-to-date?
1365 my $remove_basedir = 0;
1367 # there's a base dir, so we assume that at least
1368 # one level of unpacking has been done
1369 $act_unpack_level = 1;
1371 # maemian status file exists?
1372 unless (-f "$base/.maemian-status") {
1373 v_msg("No maemian status file found (removing old directory in lab)");
1374 $remove_basedir = 1;
1375 goto REMOVE_BASEDIR;
1378 # read unpack status -- catch any possible errors
1380 eval { ($data) = read_dpkg_control("$base/.maemian-status"); };
1383 $remove_basedir = 1;
1384 goto REMOVE_BASEDIR;
1387 # compatible maemian version?
1388 if (not exists $data->{'lab-format'} or ($data->{'lab-format'} < $LAB_FORMAT)) {
1389 v_msg("Lab directory was created by incompatible maemian version");
1390 $remove_basedir = 1;
1391 goto REMOVE_BASEDIR;
1394 # version up to date?
1395 if (not exists $data->{'version'} or ($data->{'version'} ne $ver)) {
1396 debug_msg(1, "Removing package in lab (newer version exists) ...");
1397 $remove_basedir = 1;
1398 goto REMOVE_BASEDIR;
1401 # unpack level defined?
1402 unless (exists $data->{'unpack-level'}) {
1403 warning("cannot determine unpack-level of package");
1404 $remove_basedir = 1;
1405 goto REMOVE_BASEDIR;
1407 $act_unpack_level = $data->{'unpack-level'};
1413 unless (@stat = stat $file) {
1414 warning("cannot stat file $file: $!");
1416 $timestamp = $stat[9];
1418 if ((not defined $timestamp) or (not exists $data->{'timestamp'}) or ($data->{'timestamp'} != $timestamp)) {
1419 debug_msg(1, "Removing package in lab (package has been changed) ...");
1420 $remove_basedir = 1;
1421 goto REMOVE_BASEDIR;
1425 if ($remove_basedir) {
1426 v_msg("Removing $pkg");
1427 unless (remove_pkg($base)) {
1428 warning("skipping $action of $long_type package $pkg");
1432 $act_unpack_level = 0;
1436 # unpack to requested unpack level
1437 $act_unpack_level = unpack_pkg($type,$base,$file,$act_unpack_level,
1439 if ($act_unpack_level == -1) {
1440 warning("could not unpack package to desired level",
1441 "skipping $action of $long_type package $pkg");
1446 if (($action eq 'unpack') or ($action eq 'check')) { # collect info
1447 my $current_order = -1;
1448 for my $coll (sort by_collection_order keys %unpack_infos) {
1449 my $ci = $collection_info{$coll};
1450 my %run_opts = ('description' => $coll);
1453 next unless ($ci->{'type'} =~ m/$type/);
1455 # If a file named .SCRIPT-VERSION already exists, we've already
1456 # collected this information and we can skip it. Otherwise,
1457 # remove any .SCRIPT-* files (which are old version information).
1458 next if (-f "$base/.${coll}-$ci->{'version'}");
1459 opendir(BASE, $base)
1460 or fail("cannot read directory $base: $!");
1461 for my $file (readdir BASE) {
1462 if ($file =~ /^\.\Q$coll-/) {
1463 unlink("$base/$file");
1468 # unpack to desired unpack level (if necessary)
1469 $act_unpack_level = unpack_pkg($type,$base,$file,$act_unpack_level,$ci->{'unpack-level'});
1470 if ($act_unpack_level == -1) {
1471 warning("could not unpack package to desired level",
1472 "skipping $action of $long_type package $pkg");
1477 # chdir to base directory
1478 unless (chdir($base)) {
1479 warning("could not chdir into directory $base: $!",
1480 "skipping $action of $long_type package $pkg");
1485 $current_order = $ci->{'order'} if ($current_order == -1);
1486 if ($current_order != $ci->{'order'}) {
1487 debug_msg(1, "Waiting for jobs from order $current_order ...");
1488 unless (reap_collect_jobs($pkg, $base, @pending_jobs)) {
1489 warning("skipping $action of $long_type package $pkg");
1493 undef @pending_jobs;
1494 $current_order = $ci->{'order'};
1498 remove_status_file($base);
1499 debug_msg(1, "Collecting info: $coll ...");
1500 my $script = "$MAEMIAN_ROOT/collection/$ci->{'script'}";
1501 unless (spawn(\%run_opts, [ $script, $pkg, $long_type, '&' ])) {
1502 warning("collect info $coll about package $pkg failed",
1503 "skipping $action of $long_type package $pkg");
1507 push @pending_jobs, \%run_opts;
1510 # wait until all the jobs finish and skip this package if any of them
1512 debug_msg(1, "Waiting for jobs from order $current_order ...");
1513 unless (reap_collect_jobs($pkg, $base, @pending_jobs)) {
1514 warning("skipping $action of $long_type package $pkg");
1518 undef @pending_jobs;
1521 if ($action eq 'check') { # read override file
1523 unless ($no_override) {
1524 Tags::add_overrides("$base/override", $pkg, $long_type)
1525 if (-f "$base/override")
1529 my $info = Maemian::Collect->new($pkg, $long_type);
1530 for my $check (keys %checks) {
1531 my $ci = $check_info{$check};
1534 next unless ($ci->{'type'} =~ m/$type/);
1536 # unpack to desired unpack level (if necessary)
1537 $act_unpack_level = unpack_pkg($type,$base,$file,$act_unpack_level,$ci->{'unpack-level'});
1538 if ($act_unpack_level == -1) {
1539 warning("could not unpack package to desired level",
1540 "skipping $action of $long_type package $pkg");
1545 # chdir to base directory
1546 unless (chdir($base)) {
1547 warning("could not chdir into directory $base: $!",
1548 "skipping $action of $long_type package $pkg");
1553 my $returnvalue = Checker::runcheck($pkg, $long_type, $info, $check);
1554 # Set exit_code correctly if there was not yet an exit code
1555 $exit_code = $returnvalue unless $exit_code;
1557 if ($returnvalue == 2) {
1558 warning("skipping $action of $long_type package $pkg");
1563 unless ($exit_code) {
1564 my $stats = Tags::get_stats( $file );
1565 if ($stats->{types}{E}) {
1567 } elsif ($fail_on_warnings && $stats->{types}{W}) {
1572 # report unused overrides
1573 if (not $no_override) {
1574 my $overrides = Tags::get_overrides( $file );
1576 for my $tag (sort keys %$overrides) {
1577 my $taginfo = Tags::get_tag_info{$tag};
1578 if (defined $taginfo) {
1579 # Did we run the check script containing the tag?
1580 next unless $checks{$taginfo->{'script'}};
1582 # If only checking specific tags, is this one of them?
1583 next unless (scalar keys %Tags::only_issue_tags == 0)
1584 or exists $Tags::only_issue_tags{$tag};
1587 for my $extra (sort keys %{$overrides->{$tag}}) {
1588 next if $overrides->{$tag}{$extra};
1590 tag( "unused-override", $tag, $extra );
1595 # Report override statistics.
1596 if (not $no_override and not $show_overrides) {
1597 my $stats = Tags::get_stats($file);
1600 my $errors = $stats->{overrides}{types}{E} || 0;
1601 my $warnings = $stats->{overrides}{types}{W} || 0;
1602 my $info = $stats->{overrides}{types}{I} || 0;
1603 $overrides{errors} += $errors;
1604 $overrides{warnings} += $warnings;
1605 $overrides{info} += $info;
1609 # chdir to maemian root directory (to unlock $base so it can be removed below)
1610 unless (chdir($MAEMIAN_ROOT)) {
1611 warning("could not chdir into directory $MAEMIAN_ROOT: $!",
1612 "skipping $action of $long_type package $pkg");
1618 if ($act_unpack_level > $unpack_level) {
1619 $act_unpack_level = clean_pkg($type,$base,$file,$act_unpack_level,$unpack_level);
1620 if ($act_unpack_level == -1) {
1621 warning("could not clean up laboratory for package $pkg: $!",
1622 "skipping clean up");
1628 # create Maemian status file
1629 if (($act_unpack_level > 0) and (not -f "$base/.maemian-status")) {
1631 unless (@stat = stat $file) {
1632 warning("cannot stat file $file: $!",
1633 "skipping creation of status file");
1637 my $timestamp = $stat[9];
1639 unless (open(STATUS, '>', "$base/.maemian-status")) {
1640 warning("could not create status file $base/.maemian-status for package $pkg: $!");
1645 print STATUS "Maemian-Version: $MAEMIAN_VERSION\n";
1646 print STATUS "Lab-Format: $LAB_FORMAT\n";
1647 print STATUS "Package: $pkg\n";
1648 print STATUS "Version: $ver\n";
1649 print STATUS "Type: $type\n";
1650 print STATUS "Unpack-Level: $act_unpack_level\n";
1651 print STATUS "Timestamp: $timestamp\n";
1656 if ($action eq 'check' and not $no_override and not $show_overrides) {
1657 my $errors = $overrides{errors} || 0;
1658 my $warnings = $overrides{warnings} || 0;
1659 my $info = $overrides{info} || 0;
1660 my $total = $errors + $warnings + $info;
1662 my $total = ($total == 1)
1663 ? "$total tag overridden"
1664 : "$total tags overridden";
1667 push (@output, ($errors == 1) ? "$errors error" : "$errors errors");
1670 push (@output, ($warnings == 1) ? "$warnings warning" : "$warnings warnings");
1673 push (@output, "$info info");
1675 msg("$total (". join (', ', @output). ")");
1683 # {{{ Some subroutines
1686 my ($type,$base,$file,$cur_level,$new_level) = @_;
1688 debug_msg(1, sprintf("Current unpack level is %d",$cur_level));
1690 return $cur_level if $cur_level == $new_level;
1692 # remove .maemian-status file
1693 remove_status_file($base);
1695 if ( ($cur_level == 0) and (-d $base) ) {
1696 # We were lied to, there's something already there - clean it up first
1697 remove_pkg($base) or return -1;
1700 if ( ($new_level >= 1) and
1701 (not defined ($cur_level) or ($cur_level < 1)) ) {
1702 # create new directory
1703 debug_msg(1, "Unpacking package to level 1 ...");
1704 if (($type eq 'b') || ($type eq 'u')) {
1705 spawn({}, ["$MAEMIAN_ROOT/unpack/unpack-binpkg-l1", $base, $file])
1708 spawn({}, ["$MAEMIAN_ROOT/unpack/unpack-srcpkg-l1", $base, $file])
1714 if ( ($new_level >= 2) and
1715 (not defined ($cur_level) or ($cur_level < 2)) ) {
1716 # unpack package contents
1717 debug_msg(1, "Unpacking package to level 2 ...");
1718 if (($type eq 'b') || ($type eq 'u')) {
1719 spawn({}, ["$MAEMIAN_ROOT/unpack/unpack-binpkg-l2", $base])
1722 debug_msg(1, "$MAEMIAN_ROOT/unpack/unpack-srcpkg-l2 $base");
1723 spawn({}, ["$MAEMIAN_ROOT/unpack/unpack-srcpkg-l2", $base])
1732 # Given a list of jobs corresponding to collect scripts, reap each of the
1733 # jobs. For each successful job, record that it was successful by creating
1734 # the corresponding version marker file in the lab. For each unsuccessful
1735 # job, warn that it was unsuccessful.
1737 # Takes the current package, base directory, and the list of pending jobs.
1738 # Return true if all jobs were successful, false otherwise.
1739 sub reap_collect_jobs {
1740 my ($pkg, $base, @pending_jobs) = @_;
1741 my $status = reap(@pending_jobs);
1742 for my $job (@pending_jobs) {
1743 my $coll = $job->{'description'};
1744 if ($job->{success}) {
1745 my $ci = $collection_info{$coll};
1746 open(VERSION, '>', "$base/.${coll}-$ci->{'version'}")
1747 or fail("cannot create $base/.${coll}-$ci->{'version'}: $!");
1748 print VERSION "Maemian-Version: $MAEMIAN_VERSION\n"
1749 . "Timestamp: " . time . "\n";
1752 warning("collect info $coll about package $pkg failed");
1758 # TODO: is this the best way to clean dirs in perl?
1759 # no, look at File::Path module
1761 my ($type,$base,$file,$cur_level,$new_level) = @_;
1763 return $cur_level if $cur_level == $new_level;
1765 if ($new_level < 1) {
1766 # remove base directory
1767 remove_pkg($base) or return -1;
1771 if ( ($new_level < 2) and defined ($cur_level) and ($cur_level >= 2) ) {
1772 # remove .maemian-status file
1773 remove_status_file($base);
1775 # remove unpacked/ directory
1776 debug_msg(1, "Decreasing unpack level to 1 (removing files) ...");
1777 if ( -l "$base/unpacked" ) {
1778 delete_dir("$base/".readlink("$base/unpacked"))
1780 delete_dir("$base/unpacked") or return -1;
1782 delete_dir("$base/unpacked") or return -1;
1791 # this function removes a package's base directory in the lab completely
1795 debug_msg(1, "Removing package in lab ...");
1796 unless (delete_dir($base)) {
1797 warning("cannot remove directory $base: $!");
1804 sub remove_status_file {
1807 # status file exists?
1808 if (not -e "$base/.maemian-status") {
1812 if (not unlink("$base/.maemian-status")) {
1813 warning("cannot remove status file $base/.maemian-status: $!");
1820 # get package name, version, and file name from the lab
1821 sub get_bin_info_from_lab {
1822 my ($base_dir) = @_;
1823 my ($pkg,$ver,$arch,$file);
1825 ($pkg = read_file("$base_dir/fields/package"))
1826 or fail("cannot read file $base_dir/fields/package: $!");
1828 ($ver = read_file("$base_dir/fields/version"))
1829 or fail("cannot read file $base_dir/fields/version: $!");
1831 ($arch = read_file("$base_dir/fields/architecture"))
1832 or fail("cannot read file $base_dir/fields/architecture: $!");
1834 ($file = readlink("$base_dir/deb"))
1835 or fail("cannot read link $base_dir/deb: $!");
1837 return ($file, package => $pkg, version => $ver, architecture => $arch);
1840 # get package name, version, and file name from the lab
1841 sub get_src_info_from_lab {
1842 my ($base_dir) = @_;
1843 my ($pkg,$ver,$file);
1845 ($pkg = read_file("$base_dir/fields/source"))
1846 or fail("cannot read file $base_dir/fields/source: $!");
1848 ($ver = read_file("$base_dir/fields/version"))
1849 or fail("cannot read file $base_dir/fields/version: $!");
1851 ($file = readlink("$base_dir/dsc"))
1852 or fail("cannot read link $base_dir/dsc: $!");
1854 return ($file, source => $pkg, version => $ver);
1857 # read first line of a file
1861 open(T, '<', $_[0]) or return;
1862 chop($first_line = <T>);
1868 # sort collection list by `order'
1869 sub by_collection_order {
1870 $collection_info{$a}->{'order'} <=> $collection_info{$b}->{'order'};
1874 # Prevent Lab::delete from affecting the exit code.
1877 $SIG{'INT'} = 'DEFAULT';
1878 $SIG{'QUIT'} = 'DEFAULT';
1880 $LAB->delete() if $LAB and not $keep_lab;
1884 $SIG{$_[0]} = 'DEFAULT';
1885 die "N: Interrupted.\n";