our ($verbose, $mail_logs, $list_order, $list_state,
$curr_date, $op_mode, $user, $real_user, $distribution,
- $fail_reason, $opt_override, $import_from, $opt_create_db,
+ $fail_reason, $opt_override, $import_from, $export_to, $opt_create_db,
%prioval, %sectval,
$info_all_dists, $arch,
$category, %catval, %short_category,
"merge-sources" => { mode => "merge-sources" },
"pretend-avail" => { short => "p", mode => "pretend-avail" },
"merge-all" => { mode => "merge-all" },
+ "merge-all-overlay" => { mode => "merge-all-overlay" },
info => { short => "i", mode => "info" },
'binNMU' => { mode => 'set-binary-nmu', arg => \$binNMUver,
code => sub { die "Invalid binNMU version: $binNMUver\n"
$list_min_age *= -1;
} },
# special actions
+ export => { arg => \$export_to, mode => "export" },
import => { arg => \$import_from, mode => "import" },
"manual-edit" => { mode => "manual-edit" },
);
print "wanna-build $version for $distribution on $arch\n";
}
-if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import
+if (!@ARGV && !isin( $op_mode, qw(list merge-quinn merge-partial-quinn import export
merge-packages manual-edit
merge-sources))) {
warn "No packages given.\n";
die "This operation is restricted to admin users\n"
if (defined @conf::admin_users and
!isin( $real_user, @conf::admin_users));
+ lock_table();
parse_quinn_diff(1);
last SWITCH;
};
die "This operation is restricted to admin users\n"
if (defined @conf::admin_users and
!isin( $real_user, @conf::admin_users));
+ lock_table();
parse_quinn_diff(0);
last SWITCH;
};
die "This operation is restricted to admin users\n"
if (defined @conf::admin_users and
!isin( $real_user, @conf::admin_users));
+ lock_table();
parse_packages();
last SWITCH;
};
die "This operation is restricted to admin users\n"
if (defined @conf::admin_users and
!isin( $real_user, @conf::admin_users));
+ lock_table();
parse_sources(0);
last SWITCH;
};
pretend_avail( @ARGV );
last SWITCH;
};
- /^merge-all/ && do {
+ /^merge-all$/ && do {
die "This operation is restricted to admin users\n"
if (defined @conf::admin_users and
!isin( $real_user, @conf::admin_users));
+ lock_table();
my @ARGS = @ARGV;
@ARGV = ( $ARGS[0] );
my $pkgs = parse_packages();
call_edos_depcheck( $ARGS[0], $srcs );
last SWITCH;
};
+ /^merge-all-overlay/ && do {
+ die "This operation is restricted to admin users\n"
+ if (defined @conf::admin_users and
+ !isin( $real_user, @conf::admin_users));
+ lock_table();
+ my @ARGS = @ARGV;
+ @ARGV = ( $ARGS[0] );
+ my $pkgs = parse_packages();
+ @ARGV = ( $ARGS[1] );
+ parse_quinn_diff(0);
+ @ARGV = ( $ARGS[2] );
+ my $srcs = parse_sources(1);
+ call_edos_depcheck( $ARGS[3], $srcs );
+ last SWITCH;
+ };
/^import/ && do {
die "This operation is restricted to admin users\n"
if (defined @conf::admin_users and
read_db( $import_from );
last SWITCH;
};
+ /^export/ && do {
+ export_db( $export_to );
+ last SWITCH;
+ };
die "Unexpected operation mode $op_mode\n";
}
return;
}
- change_state( \$pkg, 'BD-Uninstallable' );
- $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
+ if ($distribution eq "unstable") {
+ change_state( \$pkg, 'BD-Uninstallable' );
+ $pkg->{'bd_problem'} = "Installability of build dependencies not tested yet";
+ }
+ else
+ {
+ change_state( \$pkg, 'Needs-Build' );
+ }
delete $pkg->{'builder'};
delete $pkg->{'depends'};
$pkg->{'binary_nmu_version'} = $binNMUver;
my $name;
my $full = shift;
+ my $db = get_all_source_info();
+
local($/) = ""; # read in paragraph mode
while( <> ) {
my( $version, $arch, $section, $priority, $builddep, $buildconf, $binaries );
$pkgs{$name}{'bin'} = $binaries;
$pkgs{$name}{'dep'} = $builddep;
$pkgs{$name}{'conf'} = $buildconf;
- my $pkg = get_source_info($name);
+ my $pkg = $db->{$name};
if (defined $pkg) {
my $change = 0;
"from database, because now Arch: all\n"
if $verbose;
del_source_info($name);
+ delete $db->{$name};
next;
}
}
# remove installed packages that no longer have source available
# or binaries installed
- my $db = get_all_source_info();
foreach $name (keys %$db) {
next if $name =~ /^_/;
my $pkg = $db->{$name};
"not in Sources anymore\n"
if $verbose;
del_source_info($name);
+ delete $db->{$name};
} else {
next if !isin( $pkg->{'state'}, qw(Installed) );
if ($full && not defined $merge_srcvers{$name}) {
"binaries don't exist anymore\n"
if $verbose;
del_source_info($name);
+ delete $db->{$name};
} elsif ($full && version_less( $merge_srcvers{$name}, $pkg->{'version'})) {
print "$name ($pkg->{'version'}): ".
"package is Installed but binaries are from ".
sub parse_packages {
my $installed;
+ my $pkgs = get_all_source_info();
local($/) = ""; # read in paragraph mode
while( <> ) {
my( $name, $version, $depends, $source, $sourcev, $architecture, $provides, $binaryv, $binnmu );
next if defined($merge_srcvers{$name}) and $merge_srcvers{$name} eq $version;
$merge_srcvers{$name} = $version;
- my $pkg = get_source_info($name);
+ my $pkg = $pkgs->{$name};
if (defined $pkg) {
if (isin( $pkg->{'state'}, qw(Not-For-Us)) ||
my %quinn_pkgs;
my $dubious = "";
+ my $pkgs = get_all_source_info();
+
while( <> ) {
my $change = 0;
next if !m,^([-\w\d/]*)/ # section
$priority = "unknown" if $priority eq "-";
$priority = "standard" if ($name eq "debian-installer");
- my $pkg = get_source_info($name);
+ my $pkg = $pkgs->{$name};
# Always update section and priority.
if (defined($pkg)) {
) );
}
+sub export_db {
+ my $file = shift;
+ my($name,$pkg,$key);
+
+ print "Writing ASCII database to $file..." if $verbose >= 1;
+ open( F, ">$file" ) or
+ die "Can't open export $file: $!\n";
+
+ my $db = get_all_source_info();
+ foreach $name (keys %$db) {
+ next if $name =~ /^_/;
+ my $pkg = $db->{$name};
+ foreach $key (keys %{$pkg}) {
+ my $val = $pkg->{$key};
+ next if !defined($val);
+ $val =~ s/\n*$//;
+ $val =~ s/^/ /mg;
+ $val =~ s/^ +$/ ./mg;
+ print F "$key: $val\n";
+ }
+ print F "\n";
+ }
+ close( F );
+ print "done\n" if $verbose >= 1;
+}
+
sub change_state {
my $pkgr = shift;
my $pkg = $$pkgr;
"changed from $prevstate to $pkg->{'state'} ".
"by $real_user as $user";
- my $transactlog = db_transactlog();
- if (!open( LOG, ">>$transactlog" )) {
- warn "Can't open log file $transactlog: $!\n";
- return;
- }
- print LOG "$curr_date: $str\n";
- close( LOG );
+ $dbh->do('INSERT INTO ' . transactions_table_name() .
+ ' (package, distribution, version, action, ' .
+ ' prevstate, state, real_user, set_user, time) ' .
+ ' values (?, ?, ?, ?, ?, ?, ?, ?, ?)',
+ undef, $pkg->{'package'}, $distribution,
+ $pkg->{'version'}, $action, $prevstate, $pkg->{'state'},
+ $real_user, $user, 'now()') or die $dbh->errstr;
if (!($prevstate eq 'Failed' && $pkg->{'state'} eq 'Failed')) {
$str .= " (with --override)"
close( PIPE );
}
-sub db_transactlog {
- return "$conf::basedir/$arch/$conf::transactlog";
-}
-
# for parsing input to dep-wait
sub parse_deplist {
my $deps = shift;
return \%result;
}
-# for parsing Build-Depends from Sources
-sub parse_srcdeplist {
- my $pkg = shift;
- my $deps = shift;
- my $arch = shift;
- my $dep;
- my @results;
-
- foreach $dep (split( /\s*,\s*/, $deps )) {
- my @alts = split( /\s*\|\s*/, $dep );
- # Anything with an | is ignored, as it can be configured on a
- # per-buildd basis what will be installed
- next if $#alts != 0;
- $_ = shift @alts;
- if (!/^([^\s([]+)\s*(\(\s*([<=>]+)\s*(\S+)\s*\))?(\s*\[([^]]+)\])?/) {
- warn( "parse_srcdeplist: bad dependency $_\n" );
- next;
- }
- my($dep, $rel, $relv, $archlist) = ($1, $3, $4, $6);
- if ($archlist) {
- $archlist =~ s/^\s*(.*)\s*$/$1/;
- my @archs = split( /\s+/, $archlist );
- my ($use_it, $ignore_it, $include) = (0, 0, 0);
- foreach (@archs) {
- if (/^!/) {
- $ignore_it = 1 if substr($_, 1) eq $arch;
- } else {
- $use_it = 1 if $_ eq $arch;
- $include = 1;
- }
- }
- warn "Warning: inconsistent arch restriction on ",
- "$pkg: $dep depedency\n"
- if $ignore_it && $use_it;
- next if $ignore_it || ($include && !$use_it);
- }
- my $neg = 0;
- if ($dep =~ /^!/) {
- $dep =~ s/^!\s*//;
- $neg = 1;
- }
- my $result;
- $result->{'package'} = $dep;
- $result->{'Neg'} = $neg;
- if ($rel && $relv) {
- $result->{'rel'} = $rel;
- $result->{'version'} = $relv;
- }
- push @results, $result;
- }
- return \@results;
-}
-
sub build_deplist {
my $list = shift;
my($key, $result);
return $result;
}
-sub get_unsatisfied_dep {
- my $bd = shift;
- my $pkgs = shift;
- my $dep = shift;
- my $savedep = shift;
-
- my $pkgname = $dep->{'package'};
-
- if (defined $pkgs->{$pkgname}{'Provider'}) {
- # provides. leave them for buildd/sbuild.
- return "";
- }
-
- # check cache
- return $pkgs->{$pkgname}{'Unsatisfied'} if $savedep and defined($pkgs->{$pkgname}{'Unsatisfied'});
-
- # Return unsatisfied deps to a higher caller to process
- if ((!defined($pkgs->{$pkgname})) or
- (defined($dep->{'rel'}) and !version_compare( $pkgs->{$pkgname}{'version'}, $dep->{'rel'}, $dep->{'Version'} ) ) ) {
- my %deplist;
- $deplist{$pkgname} = $dep;
- my $deps = build_deplist(\%deplist);
- $pkgs->{$pkgname}{'Unsatisfied'} = $deps if $savedep;
- return $deps;
- }
-
- # set cache to "" to avoid infinite recursion
- $pkgs->{$pkgname}{'Unsatisfied'} = "" if $savedep;
-
- if (defined $pkgs->{$dep->{'package'}}{'depends'}) {
- my $deps = parse_deplist( $pkgs->{$dep->{'package'}}{'depends'} );
- foreach (keys %$deps) {
- $dep = $$deps{$_};
- # recur on dep.
- my $ret = get_unsatisfied_dep($bd,$pkgs,$dep,1);
- if ($ret ne "") {
- my $retdep = parse_deplist( $ret );
- foreach (keys %$retdep) {
- $dep = $$retdep{$_};
-
- $dep->{'rel'} = '>=' if defined($dep->{'rel'}) and $dep->{'rel'} =~ '^=';
-
- if (defined($dep->{'rel'}) and $dep->{'rel'} =~ '^>' and defined ($pkgs->{$dep->{'package'}}) and
- version_compare($bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'ver'},'>>',$pkgs->{$dep->{'package'}}{'Sourcev'})) {
- if (not defined($merge_binsrc{$dep->{'package'}})) {
- # the uninstallable package doesn't exist in the new source; look for something else that does.
- delete $$retdep{$dep->{'package'}};
- foreach (sort (split( /\s*,\s*/, $bd->{$pkgs->{$dep->{'package'}}{'Source'}}{'bin'}))) {
- next if ($pkgs->{$_}{'all'} or not defined $pkgs->{$_}{'version'});
- $dep->{'package'} = $_;
- $dep->{'rel'} = '>>';
- $dep->{'version'} = $pkgs->{$_}{'Version'};
- $$retdep{$_} = $dep;
- last;
- }
- }
- } else {
- # sanity check to make sure the depending binary still exists, and the depended binary exists and dep-wait on a new version of it
- if ( defined($merge_binsrc{$pkgname}) and defined($pkgs->{$dep->{'package'}}{'version'}) ) {
- delete $$retdep{$dep->{'package'}};
- $dep->{'package'} = $pkgname;
- $dep->{'rel'} = '>>';
- $dep->{'version'} = $pkgs->{$pkgname}{'Version'};
- $$retdep{$pkgname} = $dep;
- }
- delete $$retdep{$dep->{'package'}} if (defined ($dep->{'rel'}) and $dep->{'rel'} =~ '^>');
- }
- }
- $ret = build_deplist($retdep);
- $pkgs->{$pkgname}{'Unsatisfied'} = $ret if $savedep;
- return $ret;
- }
- }
- }
- return "";
-}
-
sub call_edos_depcheck {
my $packagesfile = shift;
my $srcs = shift;
open SOURCES, '>', $tmpfile or die "Could not open temporary file $tmpfile\n";
for my $key (keys %interesting_packages) {
- my $pkg = get_source_info($key);
+ my $pkg = $db->{$key};
print SOURCES "Package: $key\n";
print SOURCES "Version: $pkg->{'version'}\n";
print SOURCES "Build-Depends: $srcs->{$key}{'dep'}\n" if $srcs->{$key}{'dep'};
unlink( $tmpfile );
for my $key (keys %interesting_packages) {
- my $pkg = get_source_info($key);
+ my $pkg = $db->{$key};
my $change =
(defined $interesting_packages{$key} and $pkg->{'state'} eq 'Needs-Build') ||
(not defined $interesting_packages{$key} and $pkg->{'state'} eq 'BD-Uninstallable');
if -l is missing, set user name to be entered in db; usually
automatically choosen
--import FILE: Import database from a ASCII file FILE
+ --export FILE: Export database to a ASCII file FILE
The remaining arguments (depending on operation) usually start with
"name_version", the trailer is ignored. This allows to pass the names
return '"' . $arch . $schema_suffix . '".users';
}
+sub transactions_table_name {
+ return '"' . $arch . $schema_suffix . '".transactions';
+}
+
sub get_readonly_source_info {
my $name = shift;
my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' .
my $name = shift;
my $pkg = $dbh->selectrow_hashref('SELECT * FROM ' .
table_name() . ' WHERE package = ? AND distribution = ?' .
- 'FOR UPDATE',
+ ' FOR UPDATE',
undef, $name, $distribution);
return $pkg;
}
my $q = 'SELECT * FROM ' . table_name()
. ' WHERE distribution = ? ';
my @args = ($distribution);
- if (uc($options{state}) ne "ALL") {
+ if ($options{state} && uc($options{state}) ne "ALL") {
$q .= ' AND upper(state) = ? ';
push @args, uc($options{state});
}
}
if ($options{list_min_age} < 0) {
- $q .= ' AND age(state_change::timestamp) < ? days ';
+ $q .= ' AND age(state_change::timestamp) < ? ';
push @args, -$options{list_min_age} . " days";
}
or die $dbh->errstr;
}
+sub lock_table()
+{
+ $dbh->do('LOCK TABLE ' . table_name() .
+ ' IN EXCLUSIVE MODE', undef) or die $dbh->errstr;
+}
+