summaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:46:56 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-14 13:46:56 +0000
commit8e79ad9f544d1c4a0476e0d96aef0496ca7fc741 (patch)
treecda1743f5820600fd8c638ac7f034f917ac8c381 /bin
parentInitial commit. (diff)
downloadsbuild-8e79ad9f544d1c4a0476e0d96aef0496ca7fc741.tar.xz
sbuild-8e79ad9f544d1c4a0476e0d96aef0496ca7fc741.zip
Adding upstream version 0.85.6.upstream/0.85.6
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'bin')
-rw-r--r--bin/Makefile.am99
-rw-r--r--bin/README.bins36
-rwxr-xr-xbin/buildd93
-rwxr-xr-xbin/buildd-mail42
-rwxr-xr-xbin/buildd-make-chroot97
-rwxr-xr-xbin/buildd-update-chroots42
-rwxr-xr-xbin/buildd-uploader51
-rwxr-xr-xbin/buildd-vlog132
-rwxr-xr-xbin/buildd-watcher42
-rwxr-xr-xbin/check-old-builds153
-rwxr-xr-xbin/dobuildlog132
-rwxr-xr-xbin/finish-build123
-rwxr-xr-xbin/sbuild395
-rwxr-xr-xbin/sbuild-abort78
-rwxr-xr-xbin/sbuild-adduser82
-rwxr-xr-xbin/sbuild-apt69
-rwxr-xr-xbin/sbuild-checkpackages80
-rwxr-xr-xbin/sbuild-clean26
-rwxr-xr-xbin/sbuild-createchroot813
-rwxr-xr-xbin/sbuild-cross-resolver77
-rwxr-xr-xbin/sbuild-debian-developer-setup77
-rw-r--r--bin/sbuild-debuild391
-rwxr-xr-xbin/sbuild-destroychroot181
-rwxr-xr-xbin/sbuild-distupgrade26
-rwxr-xr-xbin/sbuild-hold58
-rwxr-xr-xbin/sbuild-qemu190
-rwxr-xr-xbin/sbuild-qemu-boot291
-rwxr-xr-xbin/sbuild-qemu-create234
-rwxr-xr-xbin/sbuild-qemu-create-modscript137
-rwxr-xr-xbin/sbuild-qemu-update282
-rwxr-xr-xbin/sbuild-shell49
-rwxr-xr-xbin/sbuild-unhold58
-rwxr-xr-xbin/sbuild-update228
-rwxr-xr-xbin/sbuild-upgrade26
-rwxr-xr-xbin/setup_system53
-rwxr-xr-xbin/wb-ssh-wrapper38
36 files changed, 4981 insertions, 0 deletions
diff --git a/bin/Makefile.am b/bin/Makefile.am
new file mode 100644
index 0000000..e4fcb69
--- /dev/null
+++ b/bin/Makefile.am
@@ -0,0 +1,99 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2008 Roger Leigh <rleigh@debian.org>
+#
+# sbuild is free software: you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# sbuild is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#####################################################################
+
+include $(top_srcdir)/scripts/global.mk
+
+sbuilddatadir = $(SBUILD_DATA_DIR)
+aptsolverdir = $(prefix)/lib/apt/solvers
+
+bin_SCRIPTS = \
+ sbuild \
+ sbuild-abort \
+ sbuild-apt \
+ sbuild-checkpackages \
+ sbuild-createchroot \
+ sbuild-debian-developer-setup \
+ sbuild-update \
+ sbuild-upgrade \
+ sbuild-distupgrade \
+ sbuild-clean \
+ sbuild-qemu \
+ sbuild-qemu-boot \
+ sbuild-qemu-create \
+ sbuild-qemu-create-modscript \
+ sbuild-qemu-update \
+ sbuild-shell \
+ sbuild-hold \
+ sbuild-unhold \
+ buildd \
+ buildd-mail \
+ buildd-uploader \
+ buildd-vlog \
+ buildd-update-chroots \
+ buildd-watcher
+
+sbin_SCRIPTS = \
+ sbuild-adduser \
+ sbuild-destroychroot
+
+sbuilddata_SCRIPTS = \
+ dobuildlog
+
+doc_DATA = \
+ README.bins
+
+aptsolver_SCRIPTS = \
+ sbuild-cross-resolver
+
+EXTRA_DIST = \
+ $(bin_SCRIPTS) \
+ $(sbin_SCRIPTS) \
+ $(sbuilddata_SCRIPTS) \
+ $(doc_DATA) \
+ $(aptsolver_SCRIPTS) \
+ buildd-make-chroot \
+ check-old-builds \
+ finish-build \
+ sbuild-debuild \
+ setup_system \
+ wb-ssh-wrapper
+
+install-exec-hook:
+# Additional directories
+ $(MKDIR_P) "$(DESTDIR)$(sbuilddatadir)"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/.ssh"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/build"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/build-trees"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/logs"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/mqueue"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/old-logs"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/stats/graphs"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/upload"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/buildd/upload-security"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/sbuild"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/sbuild/build"
+ $(MKDIR_P) "$(DESTDIR)$(localstatedir)/lib/sbuild/apt-keys"
+
+# Links for compatibility.
+ ln -sf "$(bindir)/buildd-mail" "$(DESTDIR)$(bindir)/buildd-mail-wrapper"
+ ln -sf "$(bindir)/sbuild-abort" "$(DESTDIR)$(bindir)/buildd-abort"
+
diff --git a/bin/README.bins b/bin/README.bins
new file mode 100644
index 0000000..eeda008
--- /dev/null
+++ b/bin/README.bins
@@ -0,0 +1,36 @@
+ dobuildlog
+
+A shell script for mutt & vi to handle the build logs that
+sbuild mails to you. The "bug" option is generally useful as it
+massages the build log a little so you can easily file a bug.
+The other options are for communicating with a build daemon.
+
+From the script:
+# craft a bug report or fail/success reply to a buildd log mail
+# using vim, mutt and optionally quintuple-agent:
+# mutt
+# 'f'orward the message
+# (may require autoedit & edit_headers .muttrc settings)
+# vim
+# map <F3> :%!~buildd/bin/dobuildlog agpg<CR>
+# map <S-F3> :%!~buildd/bin/dobuildlog gpg<CR>
+# map <F4> :%!~buildd/bin/dobuildlog bug<CR>
+
+You'll have to change these to your own settings:
+SIGNOPTS='--clearsign --default-key younie@debian.org'
+FROM="$EMAIL" # "Your Name <your@addr.ess>"
+ARCH=m68k # for the bug report log link
+
+Please see the comments in the script.
+
+ ------------------------------------------
+<PLUG>
+ppack in the searchscripts package can be useful,
+
+ppack -r unstable -O[q] # shows orphans in the unstable chroot
+ppack -r un -I[q] '*' # shows installed packages in unstable
+ppack -r un -a # shows anomalies (holds, misconfigured pkgs)
+
+ppack -r st -Lb pkg # urlified a package listing -> browser
+ - this is useful for browsing an installed packages contents. Try
+ "ppack -Lb sbuild" although it may be a little late for that now. :-)
diff --git a/bin/buildd b/bin/buildd
new file mode 100755
index 0000000..41fbf09
--- /dev/null
+++ b/bin/buildd
@@ -0,0 +1,93 @@
+#!/usr/bin/perl
+#
+# buildd: daemon to automatically build packages
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2009 Roger Leigh <rleigh@debian.org>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Buildd::Conf qw();
+use Buildd::Daemon;
+use Sbuild::OptionsBase;
+
+sub shutdown_fast ($);
+sub reread_config ($);
+sub reopen_log ($);
+
+my $conf = Buildd::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "buildd", "1");
+exit 1 if !defined($options);
+my $daemon = Buildd::Daemon->new($conf);
+exit 1 if !defined($daemon);
+
+# Global signal handling
+foreach (qw(QUIT ILL TRAP ABRT BUS FPE USR2 SEGV PIPE XCPU XFSZ)) {
+ $SIG{$_} = \&shutdown_fast;
+}
+$SIG{'HUP'} = \&reopen_log;
+$SIG{'USR1'} = \&reread_config;
+$SIG{'INT'} = \&shutdown;
+$SIG{'TERM'} = \&shutdown;
+
+exit $daemon->run();
+
+sub shutdown_fast ($) {
+ my $signame = shift;
+ $daemon->log("buildd ($$) killed by SIG$signame\n")
+ if defined($daemon);
+ unlink( $conf->get('PIDFILE') );
+ exit 1;
+}
+
+sub shutdown ($) {
+ my $signame = shift;
+
+ if ($daemon) {
+ $daemon->shutdown($signame);
+ }
+ exit 1;
+}
+
+sub reread_config ($) {
+ my $signame = shift;
+
+ $daemon->log("buildd ($$) received SIG$signame -- rereading configuration\n")
+ if defined($daemon);
+
+ $Buildd::Conf::reread_config = 1;
+}
+
+sub reopen_log ($) {
+ my $signame = shift;
+
+ $daemon->log("buildd ($$) received SIG$signame -- reopening logfile\n")
+ if defined($daemon);
+
+ $daemon->reopen_log();
+}
+
+END {
+ unlink( $conf->get('PIDFILE') )
+ if (defined($conf) &&
+ defined($daemon) &&
+ $daemon->get('Daemon'));
+}
diff --git a/bin/buildd-mail b/bin/buildd-mail
new file mode 100755
index 0000000..a45424e
--- /dev/null
+++ b/bin/buildd-mail
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+#
+# buildd-mail: mail answer processor for buildd
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2009 Roger Leigh <rleigh@debian.org>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Buildd::Conf qw();
+use Buildd::Mail;
+use Sbuild::OptionsBase;
+
+my $conf = Buildd::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "buildd-mail", "1");
+exit 1 if !defined($options);
+my $mail = Buildd::Mail->new($conf);
+exit 1 if !defined($mail);
+
+my $status = $mail->run();
+
+$mail->close_log();
+
+exit $status;
diff --git a/bin/buildd-make-chroot b/bin/buildd-make-chroot
new file mode 100755
index 0000000..6062968
--- /dev/null
+++ b/bin/buildd-make-chroot
@@ -0,0 +1,97 @@
+#!/bin/sh -e
+#
+# Script that uses debootstrap 0.3.2+ to build a build-essential
+# chroot for buildd use.
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+# user suite target <mirror>
+if [ "$#" -lt "3" ]; then
+ echo "usage: buildd-make-chroot user suite target <mirror>"
+ exit 1
+fi
+
+if [ "$#" -gt "4" ]; then
+ echo "usage: buildd-make-chroot user suite target <mirror>"
+ exit 1
+fi
+
+USER=$1
+SUITE=$2
+if echo "$3" | grep -Eq '^/'; then
+ TARGET="$3"
+else
+ TARGET="`pwd`/$3"
+fi
+if [ "$#" -gt "3" ]; then
+ MIRROR=$4
+else
+ MIRROR=http://incoming.debian.org/debian
+fi
+debootstrap --variant=buildd --include=sudo,fakeroot,build-essential $SUITE $TARGET $MIRROR
+hostname=`hostname`
+echo 127.0.0.1 $hostname localhost > $TARGET/etc/hosts
+echo "# put any local/close mirrors at the top of the file" > $TARGET/etc/apt/sources.list
+if [ "$#" -gt "3" ]; then
+ echo "deb $4 $SUITE main contrib" >> $TARGET/etc/apt/sources.list
+fi
+echo "deb http://incoming.debian.org/debian-debian buildd-$SUITE main contrib" >> $TARGET/etc/apt/sources.list
+echo "deb-src http://incoming.debian.org/debian-debian buildd-$SUITE main contrib" >> $TARGET/etc/apt/sources.list
+case "$2" in
+ sid)
+ ;;
+ woody)
+ echo "deb http://non-us.debian.org/debian-non-US $SUITE/non-US main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://non-us.debian.org/debian-non-US $SUITE/non-US main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb http://$hostname:PASSWORD@security-master.debian.org/debian-security $SUITE/updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://$hostname:PASSWORD@security-master.debian.org/debian-security $SUITE/updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb http://$hostname:PASSWORD@security-master.debian.org/buildd $SUITE/" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://$hostname:PASSWORD@security-master.debian.org/buildd $SUITE/" >> $TARGET/etc/apt/sources.list
+ echo "deb http://incoming.debian.org/debian $SUITE-proposed-updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://incoming.debian.org/debian $SUITE-proposed-updates main contrib" >> $TARGET/etc/apt/sources.list
+ ;;
+ sarge)
+ echo "deb http://incoming.debian.org/debian $SUITE-proposed-updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://incoming.debian.org/debian $SUITE-proposed-updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb http://$hostname:PASSWORD@security-master.debian.org/debian-security $SUITE/updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://$hostname:PASSWORD@security-master.debian.org/debian-security $SUITE/updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb http://$hostname:PASSWORD@security-master.debian.org/buildd $SUITE/" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://$hostname:PASSWORD@security-master.debian.org/buildd $SUITE/" >> $TARGET/etc/apt/sources.list
+ ;;
+ etch)
+ echo "deb http://incoming.debian.org/debian $SUITE-proposed-updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://incoming.debian.org/debian $SUITE-proposed-updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb http://$hostname:PASSWORD@security-master.debian.org/debian-security $SUITE/updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://$hostname:PASSWORD@security-master.debian.org/debian-security $SUITE/updates main contrib" >> $TARGET/etc/apt/sources.list
+ echo "deb http://$hostname:PASSWORD@security-master.debian.org/buildd $SUITE/" >> $TARGET/etc/apt/sources.list
+ echo "deb-src http://$hostname:PASSWORD@security-master.debian.org/buildd $SUITE/" >> $TARGET/etc/apt/sources.list
+ ;;
+esac
+getent passwd $USER | sed -re 's/^([^:]+):x/\1:*/' -e 's/:[^:]+:([^:]+)$/:\/nonexistent:\1/' >> $TARGET/etc/passwd
+getent group $USER | sed -re 's/^([^:]+):x/\1:*/' >> $TARGET/etc/group
+echo $USER ALL=NOPASSWD: ALL >> $TARGET/etc/sudoers
+mkdir -p $TARGET/var/lib/sbuild//srcdep-lock $TARGET/build/$USER
+chown -R $USER:$USER $TARGET/var/lib/sbuild $TARGET/build/$USER
+chmod -R 02775 $TARGET/var/lib/sbuild
+echo include /etc/ld.so.conf.d/*.conf >> $TARGET/etc/ld.so.conf
+(cd $TARGET/dev ; ./MAKEDEV fd)
+sudo chroot $TARGET dpkg -P debconf-i18n debconf liblocale-gettext-perl libtext-charwidth-perl libtext-iconv-perl libtext-wrapi18n-perl procps makedev
+echo "Successfully setup chroot for a buildd"
+echo Possible commands to append to fstab:
+echo echo $SUITE-proc $TARGET/proc proc defaults 0 0 \>\> /etc/fstab
+echo echo $SUITE-devpts $TARGET/dev/pts devpts defaults,gid=5,mode=600 0 0 \>\> /etc/fstab
diff --git a/bin/buildd-update-chroots b/bin/buildd-update-chroots
new file mode 100755
index 0000000..b8d0166
--- /dev/null
+++ b/bin/buildd-update-chroots
@@ -0,0 +1,42 @@
+#!/bin/sh
+#
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2009 Thibaut VARÈNE <varenet@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+cleanup() {
+ rm -f ~/NO-DAEMON-PLEASE
+}
+
+touch ~/NO-DAEMON-PLEASE
+trap cleanup 0
+
+touch ~/EXIT-DAEMON-PLEASE
+echo -n Waiting for sbuild and buildd to exit...
+while [ -f ~/EXIT-DAEMON-PLEASE ]; do
+ sleep 10
+done
+echo .
+
+schroot -a -u root -d /root -- apt-get update
+echo Upgrading chroots:
+schroot -a -u root -d /root -- apt-get dist-upgrade -y
+echo Cleaning chroots:
+schroot -a -u root -d /root -- apt-get autoremove -y
+schroot -a -u root -d /root -- debfoster -f
+
diff --git a/bin/buildd-uploader b/bin/buildd-uploader
new file mode 100755
index 0000000..de172f0
--- /dev/null
+++ b/bin/buildd-uploader
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+#
+# buildd-uploader: upload finished packages for buildd
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2009 Roger Leigh <rleigh@debian.org>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Buildd qw(unlock_file);
+use Buildd::Conf qw();
+use Buildd::Uploader;
+use Sbuild::OptionsBase;
+
+my $conf = Buildd::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "buildd-uploader", "1");
+exit 1 if !defined($options);
+my $uploader = Buildd::Uploader->new($conf);
+exit 1 if !defined($uploader);
+
+my $status = $uploader->run();
+
+$uploader->close_log();
+
+exit $status;
+
+END {
+ unlock_file($conf->get('HOME') . "/buildd-uploader")
+ if (defined($conf) &&
+ defined($uploader) &&
+ defined($uploader->get('Uploader Lock')) &&
+ $uploader->get('Uploader Lock'));
+}
diff --git a/bin/buildd-vlog b/bin/buildd-vlog
new file mode 100755
index 0000000..3b83efa
--- /dev/null
+++ b/bin/buildd-vlog
@@ -0,0 +1,132 @@
+#!/usr/bin/perl
+#
+# buildd-vlog: little utility to watch the logs written by sbuild
+# Copyright © 1999 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2009 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+use POSIX;
+
+use Buildd;
+use Buildd::Conf qw();
+use Sbuild::OptionsBase;
+
+my $conf = Buildd::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "buildd-vlog", "1");
+exit 1 if !defined($options);
+
+my $logpath = $conf->get('HOME') . '/logs';
+
+sub read_progress ();
+sub newest_log ($);
+sub tail ($);
+
+while( 1 ) {
+
+ my $curr_pkg = read_progress();
+ if (!$curr_pkg) {
+ print "No build-progress -- waiting\n";
+ do {
+ sleep 5;
+ } while (!($curr_pkg = read_progress()));
+ }
+
+ print("package '$curr_pkg'\n");
+
+ my $logf = newest_log( "$logpath/${curr_pkg}*" );
+
+ if ($logf eq "") {
+ sleep(1);
+ next;
+ }
+
+ print "\n\n", "="x78, "\n$logf:\n\n";
+
+ tail( $logf );
+}
+
+sub read_progress () {
+ my $f = $conf->get('HOME') . '/build/build-progress';
+ my $p = "";
+ my $state = "";
+
+ open( F, "<$f" ) || return "";
+ while( <F> ) {
+ s/_[0-9]+:/_/;
+ ($p,$state) = ($1,$2) if /^(\S+): (\S+)$/;
+ }
+ return ""
+ if ($state ne "building");
+ close( F );
+ return $p;
+}
+
+sub newest_log ($) {
+ my $pattern = shift;
+
+ my @f = glob( $pattern );
+ my $maxtime = 0;
+ my $f = "";
+ my @s;
+
+ foreach (@f) {
+ @s = stat( $_ );
+ warn "Cannot stat $_: $!", next if !@s;
+ if ($s[9] > $maxtime) {
+ $maxtime = $s[9];
+ $f = $_;
+ }
+ }
+ return $f;
+}
+
+sub tail ($) {
+ my $f = shift;
+
+ my @s = stat( $f );
+ if (!@s) {
+ warn "Cannot stat $f: $!\n";
+ return;
+ }
+ my $size = $s[7];
+
+ if (!open( F, "<$f" )) {
+ warn "Cannot open $f: $!\n";
+ return;
+ }
+ if ($size > 3*1024) {
+ seek( F, -3*1024, SEEK_END );
+ my $junk = <F>; # throw away first incomplete line
+ print $size+3*1024, " bytes skipped...\n";
+ }
+
+ while( 1 ) {
+ while( <F> ) {
+ print $_;
+ if (/^Build needed \d\d:\d\d:\d\d/) {
+ close( F );
+ sleep( 1 );
+ return;
+ }
+ }
+ sleep( 2 );
+ }
+}
diff --git a/bin/buildd-watcher b/bin/buildd-watcher
new file mode 100755
index 0000000..5d6a110
--- /dev/null
+++ b/bin/buildd-watcher
@@ -0,0 +1,42 @@
+#!/usr/bin/perl
+#
+# buildd-watcher:
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2009 Roger Leigh <rleigh@debian.org>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Buildd::Conf qw();
+use Buildd::Watcher;
+use Sbuild::OptionsBase;
+
+my $conf = Buildd::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "buildd-watcher", "1");
+exit 1 if !defined($options);
+my $watcher = Buildd::Watcher->new($conf);
+exit 1 if !defined($watcher);
+
+my $status = $watcher->run();
+
+$watcher->close_log();
+
+exit $status;
diff --git a/bin/check-old-builds b/bin/check-old-builds
new file mode 100755
index 0000000..12306f7
--- /dev/null
+++ b/bin/check-old-builds
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+#
+# check-old-build: check for packages which are in Building for extended time
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+use Time::Local;
+
+my $HOME = $ENV{'HOME'}
+or die "HOME not defined in environment!\n";
+
+sub check (@);
+sub notify_mail (@);
+sub parse_date ($);
+
+my $reported_file = "$HOME/lib/reported-old-builds";
+my $list_cmd = "wanna-build --list=building -v";
+my $report_days = 10;
+my $mailprog = "/usr/sbin/sendmail";
+chomp( my $mailname = `cat /etc/mailname` || `hostname` );
+my $sender = $ENV{'LOGNAME'} || (getpwuid($<))[0];
+
+my ($pkg, $builder, $date);
+my %reported;
+my %seen;
+my $now = time;
+my $report_time = $report_days * 24*60*60;
+
+my %monname = ('jan', 0,
+ 'feb', 1,
+ 'mar', 2,
+ 'apr', 3,
+ 'may', 4,
+ 'jun', 5,
+ 'jul', 6,
+ 'aug', 7,
+ 'sep', 8,
+ 'oct', 9,
+ 'nov', 10,
+ 'dec', 11 );
+
+if (open( F, "<$reported_file" )) {
+ while( <F> ) {
+ next if !/^(\S+)\s+(\S+)\s+(\d+)$/;
+ $reported{$2}->{$1} = $3;
+ }
+ close( F );
+}
+
+my $dist;
+foreach $dist (qw(stable frozen unstable)) {
+ open( PIPE, "$list_cmd --dist=$dist 2>&1 |" )
+ or die "Cannot spawn $list_cmd: $!\n";
+ while( <PIPE> ) {
+ next if /^wanna-build Revision/ || /^Total \d+ package/;
+ if (/^Database for \S+ doesn't exist/i) {
+ last;
+ }
+ elsif (m,^\S*/(\S+) by (\S+) \[.*\]$,) {
+ ($pkg, $builder) = ($1, $2);
+ $seen{$dist}->{$pkg} = 1;
+ }
+ elsif (/^\s+Previous state was \S+ until (.*)$/) {
+ $date = parse_date($1);
+ check( $dist, $pkg, $builder, $date );
+ }
+ elsif (/^Database locked by \S+ -- please wait/ || /^\s/) {
+ # ignore
+ }
+ else {
+ warn "Unexpected output from $list_cmd line $.:\n$_";
+ }
+ }
+ close( PIPE );
+}
+
+open( F, ">$reported_file" )
+ or die "Cannot open $reported_file for writing: $!\n";
+foreach $dist (qw(stable frozen unstable)) {
+ foreach (keys %{$reported{$dist}}) {
+ print F "$_ $dist $reported{$dist}->{$_}\n"
+ if $seen{$dist}->{$_};
+ }
+}
+close( F );
+
+exit 0;
+
+sub check (@) {
+ my ($dist, $pkg, $builder, $bdate) = @_;
+ my $date = (exists $reported{$dist}->{$pkg}) ?
+ $reported{$dist}->{$pkg} : $bdate;
+
+ if ($now - $date > $report_time) {
+ notify_mail( $dist, $pkg, $builder, $bdate );
+ $reported{$dist}->{$pkg} = $now;
+ }
+}
+
+sub notify_mail (@) {
+ my ($dist, $pkg, $to, $_date) = @_;
+ my $date = localtime($date);
+ local( *MAIL );
+
+ local $SIG{'PIPE'} = 'IGNORE';
+ open( MAIL, "| $mailprog -oem $to\@$mailname" )
+ or die "Can't open pipe to $mailprog: $!\n";
+ print MAIL <<"EOF";
+From: $sender\@$mailname
+To: $to\@$mailname
+Subject: Old build of $pkg (dist=$dist)
+
+The package $pkg has been taken by you for
+building in distribution $dist at $date.
+This is some time ago now, so it could be you have forgotten the build.
+Can you please check this and --if this is the case-- give back the package
+or finish it?
+If you did not call wanna-build --uploaded, it might also be the case
+that the package is not yet installed in the archive.
+
+(This is an automated message.)
+EOF
+ close( MAIL );
+}
+
+sub parse_date ($) {
+ my $text = shift;
+
+ die "Cannot parse date: $text\n"
+ if $text !~ /^(\d{4}) (\w{3}) (\d+) (\d{2}):(\d{2}):(\d{2})$/;
+ my ($year, $mon, $day, $hour, $min, $sec) = ($1, $2, $3, $4, $5, $6);
+ $mon =~ y/A-Z/a-z/;
+ die "Invalid month name $mon" if !exists $monname{$mon};
+ $mon = $monname{$mon};
+ return timelocal($sec, $min, $hour, $day, $mon, $year);
+}
diff --git a/bin/dobuildlog b/bin/dobuildlog
new file mode 100755
index 0000000..793daf2
--- /dev/null
+++ b/bin/dobuildlog
@@ -0,0 +1,132 @@
+#!/bin/bash -e
+#
+# Copyright © 2002 Rick Younie <rick@def.debian.net>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+#
+# craft a bug report or fail/success reply to a buildd log mail
+# using vim, mutt and optionally quintuple-agent:
+# mutt
+# 'f'orward the message
+# (may require autoedit & edit_headers .muttrc settings)
+# vim
+# map <F3> :%!~buildd/bin/dobuildlog agpg<CR>
+# map <S-F3> :%!~buildd/bin/dobuildlog gpg<CR>
+# map <F4> :%!~buildd/bin/dobuildlog bug<CR>
+
+# these require setting by the user
+SIGNOPTS='--clearsign --default-key younie@debian.org'
+FROM="$EMAIL" # "Your Name <your@addr.ess>"
+ARCH=m68k # for the bug report log link
+
+print_header () {
+ echo "From: $FROM"
+ sed -n '
+ /^-----/,/^Automatic/ {
+ s/From: /To: /p
+ s/^Subject: Log/Subject: Re: Log/p
+ }'
+ echo
+}
+
+fail_options () {
+ cat << EOF
+failed
+ this one takes a comment,
+ multi-line, indenting optional
+dep-wait
+ - usage: dep-wait some-package (>= version), another-package (>> version)
+giveback
+manual
+newvers
+not-for-us
+purge
+ - purges the source tree from the chroot
+retry
+upload-rem
+
+
+EOF
+}
+
+success_fail () {
+ STATUS=$(sed -n '/^-----/,/^Automatic/ s/^Subject: Log for \([^ ]*\) build .*/\1/p')
+
+ case "$STATUS" in
+ successful )
+ print_header
+ sed -n '/\.changes:$/,$ {
+ /^Format: /,/^$/p
+ }' |$SIGNPRG 2>/dev/null
+ ;;
+ failed )
+ print_header
+ fail_options
+ sed -n '/^Automatic build of/,$p'
+ ;;
+ * )
+ echo "..this doesn't appear to be a buildd success/fail message"
+ exit 1
+ ;;
+ esac
+}
+
+bug_report () {
+ PKG=$1
+ VERS=$2
+
+ cat << EOF
+From: $FROM
+To: submit@bugs.debian.org
+Subject: $PKG_VERS: fails to build
+
+Package: $PKG
+Version: $VERS
+Severity: serious
+
+Hi,
+
+
+EOF
+
+ sed -n '/^Automatic build of/,/^Build needed/ s/^/| /p'
+ cat <<EOF
+
+
+The $ARCH build logs for $PKG can be found at
+ http://buildd.debian.org/build.php?arch=$ARCH&pkg=$PKG
+
+
+EOF
+}
+
+case "$1" in
+ gpg | agpg )
+ SIGNPRG="$1 $SIGNOPTS"
+ success_fail
+ exit 0
+ ;;
+ bug )
+ PKG_VERS=$(sed -n '/^-----/,/^Automatic/ s/^Subject: Log for \([^ ]*\) build of \([^ ]*\) .*/\2/p')
+ bug_report $(echo "$PKG_VERS" |sed 's/_/ /')
+ ;;
+ * )
+ echo "Usage: $(basename $0) gpg|agpg|bug"
+ exit 1
+ ;;
+esac
diff --git a/bin/finish-build b/bin/finish-build
new file mode 100755
index 0000000..1c63d72
--- /dev/null
+++ b/bin/finish-build
@@ -0,0 +1,123 @@
+#!/bin/sh
+#
+# finish-build: finishes a manually fixed build by running binary-arch
+# if necessary and generating a .changes file
+# Copyright © 1999 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+set -e
+
+# TODO: Convert to perl and read configuration directly.
+
+if [ ! -f debian/rules ]; then
+ echo "This directory doesn't seem to contain a Debian source tree" 1>&2
+ exit 1
+fi
+
+logpath=$(cat /etc/sbuild.conf /etc/sbuild.conf.local $HOME/.sbuildrc | \
+ sed -n '/^\$log_dir/s/.*"\(.*\)".*/\1/p' | tail -1)
+logpath=`eval echo $logpath`
+if [ -z "$logpath" ]; then
+ logpath=$HOME/logs
+fi
+
+maintname=$(cat /etc/sbuild.conf /etc/sbuild.conf.local $HOME/.sbuildrc | \
+ sed -n '/^\$maintainer_name/s/.*"\(.*\)".*/\1/p' | tail -1)
+maintname=$(echo $maintname | sed 's/\\@/@/g')
+if [ -z "$maintname" ]; then
+ echo "Can't extract \$maintainer_name variable from sbuild config" 1>&2
+ exit 1
+fi
+
+mailto=$(cat /etc/sbuild.conf /etc/sbuild.conf.local $HOME/.sbuildrc | \
+ sed -n '/^\$mailto/s/.*"\(.*\)".*/\1/p' | tail -1)
+mailto=$(echo $mailto | sed 's/\\@/@/g')
+if [ -z "$mailto" ]; then
+ echo "Can't extract \$mailto variable from sbuild config" 1>&2
+ exit 1
+fi
+
+setvar () {
+ if [ "x$2" = x ]; then
+ echo "$0: unable to determine $3"
+ exit 1
+ else
+ eval "$1='$2'"
+ fi
+}
+
+opt_b=0
+while [ $# -ge 1 ]; do
+ case "$1" in
+ -b) opt_b=1;;
+ *) echo "Unknown option $1" 1>&2; exit 1;;
+ esac
+ shift
+done
+
+tmpf=/tmp/finish-build.$$
+dpkg-parsechangelog > $tmpf
+setvar package "`sed -n 's/^Source: //p' $tmpf`" "source package"
+setvar version "`sed -n 's/^Version: //p' $tmpf`" "source version"
+setvar arch "`dpkg --print-architecture`" "build architecture"
+rm -f $tmpf
+sversion=`echo "$version" | perl -pe 's/^\d+://'`
+changes=${package}_${sversion}_${arch}.changes
+logpat=${package}_${version}
+
+lastlog=`(cd $logpath; ls -1t ${logpat}_* | head -1) 2>/dev/null`
+if [ -z "$lastlog" ]; then
+ echo "No log file found (pattern ${logpat}_*)" 1>&2
+ exit 1
+else
+ echo " Log file is $lastlog"
+fi
+
+do_binarch=0
+if [ ! -f debian/files ]; then
+ echo " debian/files missing -- running binary-arch"
+ do_binarch=1
+elif [ $opt_b = 1 ]; then
+ do_binarch=1
+fi
+
+if [ $do_binarch = 1 ]; then
+ echo " sudo debian/rules binary-arch"
+ sudo debian/rules binary-arch 2>&1 | tee -a $logpath/$lastlog
+fi
+
+if [ ! -s ../$changes ]; then
+ echo " Generating .changes file:"
+ dpkg-genchanges -B -m"$maintname" > ../$changes
+fi
+
+if [ ! -f debian/files ]; then
+ echo "debian/files not found" 1>&2
+ exit 1
+fi
+files="`cut -d' ' -f1 debian/files`"
+if [ -z "$files" ]; then
+ echo "No files list" 1>&2
+ exit 1
+fi
+
+(cat $logpath/$lastlog;
+ for i in $files; do echo; echo "$i:"; dpkg --info ../$i; done;
+ for i in $files; do echo; echo "$i:"; dpkg --contents ../$i; done;
+ echo; echo "$changes:"; cat ../$changes
+ ) | mail -s "Log for successful build of $logpat (dist=unstable)" $mailto
diff --git a/bin/sbuild b/bin/sbuild
new file mode 100755
index 0000000..4f0db29
--- /dev/null
+++ b/bin/sbuild
@@ -0,0 +1,395 @@
+#!/usr/bin/perl
+#
+# sbuild: build packages, obeying source dependencies
+# Copyright © 1998-2000 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2009 Roger Leigh <rleigh@debian.org
+# Copyright © 2008 Timothy G Abbott <tabbott@mit.edu>
+# Copyright © 2008 Simon McVittie <smcv@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+package main;
+
+use strict;
+use warnings;
+
+use Cwd qw(:DEFAULT abs_path);
+use File::Basename qw(basename dirname);
+use File::Spec;
+use POSIX;
+use Data::Dumper;
+use Dpkg::Control;
+use Sbuild qw(isin check_group_membership $debug_level dsc_files debug);
+use Sbuild::Conf qw();
+use Sbuild::Sysconfig qw(%programs);
+use Sbuild::Options;
+use Sbuild::Build;
+use Sbuild::Exception;
+use Sbuild::Utility qw(check_url download);
+
+sub main ();
+sub create_source_package ($);
+sub download_source_package ($);
+sub write_jobs_file ();
+sub status_trigger ($$);
+sub shutdown ($);
+sub dump_main_state ();
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::Options->new($conf, "sbuild", "1");
+exit 1 if !defined($options);
+check_group_membership() if $conf->get('CHROOT_MODE') eq 'schroot';
+
+if (!$conf->get('MAINTAINER_NAME') &&
+ ($conf->get('BIN_NMU') || $conf->get('APPEND_TO_VERSION'))) {
+ die "A maintainer name, uploader name or key ID must be specified in .sbuildrc,\nor use -m, -e or -k, when performing a binNMU or appending a version suffix\n";
+}
+
+# default umask for Debian
+# see dpkg source: scripts/Dpkg/Vendor/Debian.pm
+umask(0022);
+
+# Job state
+my $job = undef;
+
+main();
+
+sub main () {
+ $SIG{'INT'} = \&main::shutdown;
+ $SIG{'TERM'} = \&main::shutdown;
+ $SIG{'ALRM'} = \&main::shutdown;
+ $SIG{'PIPE'} = \&main::shutdown;
+
+ # If no arguments are supplied, assume we want to process the current dir.
+ push @ARGV, '.' unless (@ARGV);
+
+ die "Only one build is permitted\n"
+ if (@ARGV != 1);
+
+ # Create and run job
+ my $status = eval {
+ my $jobname = $ARGV[0];
+ my $source_dir = 0;
+
+ if (-e $jobname) {
+ # $jobname should be an absolute path, so that the %SBUILD_DSC
+ # escape also is absolute. This is important for `dgit sbuild`.
+ # See Debian bug #801436 for details. On the other hand, the
+ # last component of the path must not have any possible symlinks
+ # resolved so that a symlink ending in .dsc is not turned
+ # into a path that does not end in .dsc. See Debian bug #1012856
+ # for details. Thus, we call File::Spec->rel2abs instead of
+ # Cwd::abs_path because the latter behaves like `realpath` and
+ # resolves symlinks while the former does not.
+ $jobname = File::Spec->rel2abs($jobname);
+ }
+
+ if (-d $jobname) {
+ $jobname = create_source_package($jobname);
+ if ($jobname eq '.') {
+ chdir('..') or Sbuild::Exception::Build->throw(
+ error => "Failed to change directory",
+ failstage => "change-build-dir");
+ $conf->_set_default('BUILD_DIR', cwd());
+ }
+ $source_dir = 1;
+ } elsif (($jobname =~ m/\.dsc$/) && # Use apt to download
+ check_url($jobname)) {
+ # Valid URL
+ $jobname = download_source_package($jobname);
+ }
+
+
+ # Check after source package build (which might set dist)
+ my $dist = $conf->get('DISTRIBUTION');
+ if (!defined($dist) || !$dist) {
+ print STDERR "No distribution defined\n";
+ exit(1);
+ }
+
+ print "Selected distribution " . $conf->get('DISTRIBUTION') . "\n"
+ if $conf->get('DEBUG');
+ print "Selected chroot " . $conf->get('CHROOT') . "\n"
+ if $conf->get('DEBUG') and defined $conf->get('CHROOT');
+ print "Selected host architecture " . $conf->get('HOST_ARCH') . "\n"
+ if $conf->get('DEBUG' && defined($conf->get('HOST_ARCH')));
+ print "Selected build architecture " . $conf->get('BUILD_ARCH') . "\n"
+ if $conf->get('DEBUG' && defined($conf->get('BUILD_ARCH')));
+ print "Selected build profiles " . $conf->get('BUILD_PROFILES') . "\n"
+ if $conf->get('DEBUG' && defined($conf->get('BUILD_PROFILES')));
+
+ $job = Sbuild::Build->new($jobname, $conf);
+ $job->set('Pkg Status Trigger', \&status_trigger);
+ write_jobs_file(); # Will now update on trigger.
+
+ # Run job.
+ $job->run();
+
+ dump_main_state() if $conf->get('DEBUG');
+ };
+
+ my $e;
+ if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
+ print STDERR "E: $e\n";
+ print STDERR "I: " . $e->info . "\n"
+ if ($e->info);
+ if ($debug_level) {
+ #dump_main_state();
+ #print STDERR $e->trace->as_string, "\n";
+ }
+ } elsif (!defined($e)) {
+ print STDERR "E: $@\n" if $@;
+ }
+
+ unlink($conf->get('JOB_FILE'))
+ if $conf->get('BATCH_MODE');
+
+ # Until buildd parses status info from sbuild output, skipped must
+ # be treated as a failure.
+ if (defined($job)) {
+ if ($job->get_status() eq "successful" ||
+ ($conf->get('SBUILD_MODE') ne "buildd" &&
+ $job->get_status() eq "skipped")) {
+ exit 0;
+ } elsif ($job->get_status() eq "attempted") {
+ exit 2;
+ } elsif ($job->get_status() eq "given-back") {
+ #Probably needs a give back:
+ exit 3;
+ }
+ # Unknown status - probably needs a give back, but needs to be
+ # reported to the admin as failure:
+ exit 1;
+ }
+ debug("Error main(): $@") if $@;
+ exit 1;
+}
+
+sub create_source_package ($) {
+ my $dsc = shift;
+
+ open(my $pipe, '-|', 'dpkg-parsechangelog',
+ '-l' . $dsc . '/debian/changelog')
+ or Sbuild::Exception::Build->throw(
+ error => "Could not parse $dsc/debian/changelog: $!",
+ failstage => "pack-source");
+
+ my $pclog = Dpkg::Control->new(type => CTRL_CHANGELOG);
+ if (!$pclog->parse($pipe, 'dpkg-parsechangelog')) {
+ Sbuild::Exception::Build->throw(
+ error => "Could not parse $dsc/debian/changelog: $!",
+ failstage => "pack-source");
+ }
+
+ $pipe->close or Sbuild::Exception::Build->throw(
+ error => "dpkg-parsechangelog failed (exit status $?)",
+ failstage => "pack-source");
+
+ my $package = $pclog->{'Source'};
+ my $version = $pclog->{'Version'};
+
+ if (!defined($package) || !defined($version)) {
+ Sbuild::Exception::Build->throw(
+ error => "Missing Source or Version in $dsc/debian/changelog",
+ failstage => "pack-source");
+ }
+
+ my $dist = $pclog->{'Distribution'};
+ my $pver = Dpkg::Version->new($version, check => 1);
+ unless (defined $pver) {
+ Sbuild::Exception::Build->throw(
+ error => "Bad version $version in $dsc/debian/changelog",
+ failstage => "pack-source");
+ }
+
+ my ($uversion, $dversion);
+ $uversion = $pver->version();
+ $dversion = "-" . $pver->revision();
+ $dversion = "" if $pver->{'no_revision'};
+
+ if (!defined($conf->get('DISTRIBUTION')) ||
+ !$conf->get('DISTRIBUTION')) {
+ $conf->set('DISTRIBUTION', $dist);
+ }
+
+ my $dir = getcwd();
+ my $origdir=$dir;
+ my $origdsc=$dsc;
+ # Note: need to support cases when invoked from a subdirectory
+ # of the build directory, i.e. $dsc/foo -> $dsc/.. in addition
+ # to $dsc -> $dsc/.. as below.
+ # We won't attempt to build the source package from the source
+ # directory so the source package files will go to the parent dir.
+ my $dscdir = abs_path("$dsc/..");
+ if (index($dir, $dsc, 0) == 0) {
+ $conf->_set_default('BUILD_DIR', $dscdir);
+ }
+
+ $dsc = "${dscdir}/${package}_${uversion}${dversion}.dsc";
+
+ $dir = $origdsc;
+
+ chdir($dir) or Sbuild::Exception::Build->throw(
+ error => "Failed to change directory",
+ failstage => "pack-source");
+ my @dpkg_source_before = ($conf->get('DPKG_SOURCE'), '--before-build');
+ push @dpkg_source_before, @{$conf->get('DPKG_SOURCE_OPTIONS')}
+ if ($conf->get('DPKG_SOURCE_OPTIONS'));
+ push @dpkg_source_before, '.';
+ system(@dpkg_source_before);
+ if ($?) {
+ Sbuild::Exception::Build->throw(
+ error => "Failed to run dpkg-source --before-build " . getcwd(),
+ failstage => "pack-source");
+ }
+ if ($conf->get('CLEAN_SOURCE')) {
+ system($conf->get('FAKEROOT'), 'debian/rules', 'clean');
+ if ($?) {
+ Sbuild::Exception::Build->throw(
+ error => "Failed to clean source directory $dir ($dsc)",
+ failstage => "pack-source");
+ }
+ }
+ my @dpkg_source_command = ($conf->get('DPKG_SOURCE'), '-b');
+ push @dpkg_source_command, @{$conf->get('DPKG_SOURCE_OPTIONS')}
+ if ($conf->get('DPKG_SOURCE_OPTIONS'));
+ push @dpkg_source_command, '.';
+ system(@dpkg_source_command);
+ if ($?) {
+ Sbuild::Exception::Build->throw(
+ error => "Failed to package source directory " . getcwd(),
+ failstage => "pack-source");
+ }
+ my @dpkg_source_after = ($conf->get('DPKG_SOURCE'), '--after-build');
+ push @dpkg_source_after, @{$conf->get('DPKG_SOURCE_OPTIONS')}
+ if ($conf->get('DPKG_SOURCE_OPTIONS'));
+ push @dpkg_source_after, '.';
+ system(@dpkg_source_after);
+ if ($?) {
+ Sbuild::Exception::Build->throw(
+ error => "Failed to run dpkg-source --after-build " . getcwd(),
+ failstage => "pack-source");
+ }
+ chdir($origdir) or Sbuild::Exception::Build->throw(
+ error => "Failed to change directory",
+ failstage => "pack-source");
+
+ return $dsc;
+}
+
+sub download_source_package ($) {
+ my $dsc = shift;
+
+ my $srcdir = dirname($dsc);
+ my $dscbase = basename($dsc);
+
+ my @fetched;
+
+ # Work with a .dsc file.
+ # $file is the name of the downloaded dsc file written in a tempfile.
+ my $file;
+ $file = download($dsc, $dscbase) or
+ Sbuild::Exception::Build->throw(
+ error => "Could not download $dsc",
+ failstage => "download-source");
+ push(@fetched, $dscbase);
+
+ my @cwd_files = dsc_files($file);
+
+ foreach (@cwd_files) {
+ my $subfile = download("$srcdir/$_", $_);
+ if (!$subfile) {
+ # Remove downloaded sources
+ foreach my $rm (@fetched) {
+ unlink($rm);
+ }
+ Sbuild::Exception::Build->throw(
+ error => "Could not download $srcdir/$_",
+ failstage => "download-source");
+ }
+ push(@fetched, $_);
+ }
+
+ return $file;
+}
+
+# only called from main loop, but depends on job state.
+sub write_jobs_file () {
+ if ($conf->get('BATCH_MODE')) {
+
+ my $file = $conf->get('JOB_FILE');
+ local( *F );
+
+ return if !open( F, ">$file" );
+ if (defined($job)) {
+ print F $job->get('Package_OVersion') . ": " .
+ $job->get_status() . "\n";
+ }
+ close( F );
+ }
+}
+
+sub status_trigger ($$) {
+ my $build = shift;
+ my $status = shift;
+
+ write_jobs_file();
+
+ # Rewrite status if we need to give back or mark attempted
+ # following failure. Note that this must follow the above
+ # function calls because set_status will recursively trigger.
+ if ($status eq "failed" &&
+ isin($build->get('Pkg Fail Stage'),
+ qw(fetch-src install-core install-essential install-deps
+ unpack check-unpacked-version check-space hack-binNMU
+ install-deps-env apt-get-clean apt-get-update
+ apt-get-upgrade apt-get-distupgrade))) {
+ $build->set_status('given-back');
+ } elsif ($status eq "failed" &&
+ isin ($build->get('Pkg Fail Stage'),
+ qw(build arch-check))) {
+ $build->set_status('attempted');
+ }
+}
+
+sub shutdown ($) {
+ my $signame = shift;
+
+ $SIG{'INT'} = 'IGNORE';
+ $SIG{'QUIT'} = 'IGNORE';
+ $SIG{'TERM'} = 'IGNORE';
+ $SIG{'ALRM'} = 'IGNORE';
+ $SIG{'PIPE'} = 'IGNORE';
+
+ if (defined($job)) {
+ $job->request_abort("Received $signame signal");
+ } else {
+ exit(1);
+ }
+
+ $SIG{'INT'} = \&main::shutdown;
+ $SIG{'TERM'} = \&main::shutdown;
+ $SIG{'ALRM'} = \&main::shutdown;
+ $SIG{'PIPE'} = \&main::shutdown;
+}
+
+sub dump_main_state () {
+ print STDERR Data::Dumper->Dump([$job],
+ [qw($job)] );
+}
diff --git a/bin/sbuild-abort b/bin/sbuild-abort
new file mode 100755
index 0000000..556188e
--- /dev/null
+++ b/bin/sbuild-abort
@@ -0,0 +1,78 @@
+#!/usr/bin/perl
+#
+# Abort the current build.
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2003 Ryan Murray <rmurray@debian.org>
+# Copyright © 2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+use Sbuild::Conf qw();
+use Sbuild::OptionsBase;
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "sbuild-abort", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+my $buildcount = 0;
+my $linecount = 0;
+my $header = "";
+my @detail = ();
+
+open (PIPE, "/bin/ps xjww |") or die "Can't run /bin/ps: $!\n";
+while(<PIPE>) {
+ chomp;
+ if ($linecount == 0) {
+ $header = $_;
+ } elsif (m/\/usr\/bin\/perl \/usr\/bin\/dpkg-buildpackage/) {
+ push @detail, $_;
+ $buildcount++;
+ }
+ $linecount++;
+}
+close (PIPE) or die "Can't close /bin/ps pipe: $!\n";
+
+if ($buildcount == 0) {
+ print STDERR "E: No dpkg-buildpackage process found\n";
+ exit 1;
+} elsif ($buildcount > 1) {
+ print STDERR "E: More than one dpkg-buildpackage process found:\n";
+
+ print "I: $header\n";
+ foreach (@detail) {
+ print "I: $_\n";
+ }
+ exit 1;
+}
+
+# Get PGID from saved ps output.
+my @fields = split(/[[:space:]]+/, $detail[0]);
+die "Error parsing /bin/ps output" if (@fields < 1);
+my $pgid = $fields[2];
+
+# Kill process group.
+print "I: Killing process group $pgid\n";
+kill("TERM", -$pgid) or die "Error killing PGID $pgid: $!\n";
+
+exit 0;
diff --git a/bin/sbuild-adduser b/bin/sbuild-adduser
new file mode 100755
index 0000000..033a4ed
--- /dev/null
+++ b/bin/sbuild-adduser
@@ -0,0 +1,82 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+package main;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error);
+use Sbuild::Conf qw();
+use Sbuild::OptionsBase;
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "sbuild-adduser", "8");
+exit 1 if !defined($options);
+
+usage_error("sbuild-adduser", "Incorrect number of options") if (@ARGV < 1);
+
+my $status = 0;
+
+foreach (@ARGV) {
+ my $user = getpwnam($_);
+
+ if (defined $user) {
+ $status += system(qw(/usr/sbin/adduser --), $_, 'sbuild');
+ } else {
+ print STDERR "W: User \"$_\" does not exist\n";
+ $status++;
+ }
+}
+
+if ($status == 0) {
+ print STDOUT <<EOF;
+
+# Setup tasks for sudo users:
+
+# BUILD
+# HOME directory in chroot, user:sbuild, 0770 perms, from
+# passwd/group copying to chroot, filtered
+# Maybe source 50sbuild, or move into common location.
+
+Next, copy the example sbuildrc file to the home directory of each user and
+set the variables for your system:
+
+EOF
+
+ foreach (@ARGV) {
+ my $home = (getpwnam($_))[7];
+ print STDERR " cp /usr/share/doc/sbuild/examples/example.sbuildrc $home/.sbuildrc\n";
+ }
+ print STDOUT <<EOF;
+
+Now try a build:
+
+ cd /path/to/source
+ sbuild-update -ud <distribution>
+ (or "sbuild-apt <distribution> apt-get -f install"
+ first if the chroot is broken)
+ sbuild -d <distribution> <package>_<version>
+EOF
+}
+
+exit ($status ? 1:0);
diff --git a/bin/sbuild-apt b/bin/sbuild-apt
new file mode 100755
index 0000000..879f438
--- /dev/null
+++ b/bin/sbuild-apt
@@ -0,0 +1,69 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+use Sbuild::Utility qw(setup cleanup);
+use Sbuild::Conf qw();
+use Sbuild::OptionsBase;
+use Sbuild::AptResolver;
+use Sbuild::ChrootRoot;
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "sbuild-apt", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+usage_error("sbuild-apt", "Incorrect number of options") if (@ARGV < 2);
+
+my $chroot = shift @ARGV;
+my $command = shift @ARGV;
+
+if ($command eq "apt-get") {
+ $command = $conf->get('APT_GET');
+} elsif ($command eq "apt-cache") {
+ $command = $conf->get('APT_CACHE');
+} else {
+ usage_error("sbuild-apt",
+ "Bad command $command. Allowed commands: apt-get or apt-cache\n");
+}
+
+my $session = setup('source', $chroot, $conf) or die "Chroot setup failed";
+my $host = Sbuild::ChrootRoot->new($conf);
+$host->begin_session() or die "Chroot setup (host) failed";
+
+my $resolver = Sbuild::AptResolver->new($conf, $session, $host);
+$resolver->setup();
+
+$resolver->run_apt_command(
+ { COMMAND => [$command, '-oAPT::Get::Assume-Yes=true', @ARGV],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ PRIORITY => 1,
+ DIR => '/' });
+my $status = $? >> 8;
+
+cleanup($conf);
+
+exit $status;
diff --git a/bin/sbuild-checkpackages b/bin/sbuild-checkpackages
new file mode 100755
index 0000000..68c696c
--- /dev/null
+++ b/bin/sbuild-checkpackages
@@ -0,0 +1,80 @@
+#!/usr/bin/perl -w
+# check the package list in a chroot against a reference list.
+#
+# Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+our $mode = undef;
+
+package Options;
+
+use Sbuild::OptionsBase;
+use Sbuild::Conf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::OptionsBase);
+
+ @EXPORT = qw();
+}
+
+sub set_options {
+ my $self = shift;
+
+ $self->add_options(
+ "l|list" => sub { $mode = "list"; },
+ "s|set" => sub { $mode = "set"; });
+}
+
+package main;
+
+use locale;
+use POSIX qw(locale_h);
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_packages check_group_membership);
+use Sbuild::Conf qw();
+use Sbuild::Utility qw(setup cleanup shutdown);
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Options->new($conf, "sbuild-checkpackages", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+usage_error("sbuild-checkpackages", "--list or --set must be specified")
+ if (!defined($mode));
+
+usage_error("sbuild-checkpackages", "A chroot must be specified")
+ if (@ARGV != 1);
+
+my $chroot = $ARGV[0];
+
+setlocale(LC_COLLATE, "POSIX");
+$ENV{'LC_COLLATE'} = "POSIX";
+
+my $session = setup('source', $chroot, $conf) or die "Chroot setup failed";
+
+check_packages($session, $mode);
+
+cleanup($conf);
+exit 0;
diff --git a/bin/sbuild-clean b/bin/sbuild-clean
new file mode 100755
index 0000000..b69346b
--- /dev/null
+++ b/bin/sbuild-clean
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+print "$0 is deprecated. sbuild-clean functionality has been merged with ";
+print "sbuild-update. Use sbuild-update instead.\n";
+exec("sbuild-update", "--clean", @ARGV) or die "Can't run sbuild-update: $!";
diff --git a/bin/sbuild-createchroot b/bin/sbuild-createchroot
new file mode 100755
index 0000000..07e5912
--- /dev/null
+++ b/bin/sbuild-createchroot
@@ -0,0 +1,813 @@
+#!/usr/bin/perl
+#
+# Run debootstrap and add a few other files needed to create a working
+# sbuild chroot.
+# Copyright © 2004 Francesco P. Lovergine <frankie@debian.org>.
+# Copyright © 2007-2010 Roger Leigh <rleigh@debian.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+umask 0022;
+
+use English;
+
+use Sbuild::AptResolver;
+
+package Conf;
+
+sub setup {
+ my $conf = shift;
+
+ my $keyring = '';
+ $keyring = '/etc/apt/trusted.gpg'
+ if -f '/etc/apt/trusted.gpg';
+
+ my %createchroot_keys = (
+ 'CHROOT_PREFIX' => {
+ DEFAULT => undef
+ },
+ 'CHROOT_SUFFIX' => {
+ DEFAULT => '-sbuild'
+ },
+ 'FOREIGN' => {
+ DEFAULT => 0
+ },
+ 'INCLUDE' => {
+ DEFAULT => ''
+ },
+ 'EXCLUDE' => {
+ DEFAULT => ''
+ },
+ 'COMPONENTS' => {
+ DEFAULT => 'main'
+ },
+ 'RESOLVE_DEPS' => {
+ DEFAULT => 1
+ },
+ 'KEEP_DEBOOTSTRAP_DIR' => {
+ DEFAULT => 0
+ },
+ 'DEBOOTSTRAP' => {
+ DEFAULT => 'debootstrap'
+ },
+ 'KEYRING' => {
+ DEFAULT => undef
+ },
+ 'SETUP_ONLY' => {
+ DEFAULT => 0
+ },
+ 'MAKE_SBUILD_TARBALL' => {
+ DEFAULT => ''
+ },
+ 'KEEP_SBUILD_CHROOT_DIR' => {
+ DEFAULT => 0
+ },
+ 'DEB_SRC' => {
+ DEFAULT => 1
+ },
+ 'ALIASES' => {
+ DEFAULT => []
+ },
+ 'EXTRA_REPOSITORIES' => {
+ DEFAULT => []
+ },
+ 'COMMAND_PREFIX' => {
+ DEFAULT => ''
+ },
+ 'CHROOT_MODE' => {
+ DEFAULT => 'schroot'
+ },
+ 'MERGED_USR' => {
+ DEFAULT => 'auto'
+ },
+ );
+
+ $conf->set_allowed_keys(\%createchroot_keys);
+}
+
+package Options;
+
+use Sbuild::OptionsBase;
+use Sbuild::Conf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::OptionsBase);
+
+ @EXPORT = qw();
+}
+
+sub set_options {
+ my $self = shift;
+
+ $self->add_options(
+ "chroot-mode=s" => sub {
+ $self->set_conf('CHROOT_MODE', $_[1]);
+ },
+ "chroot-prefix=s" => sub {
+ $self->set_conf('CHROOT_PREFIX', $_[1]);
+ },
+ "chroot-suffix=s" => sub {
+ $self->set_conf('CHROOT_SUFFIX', $_[1]);
+ },
+ "arch=s" => sub {
+ $self->set_conf('BUILD_ARCH', $_[1]);
+ },
+ "foreign" => sub {
+ $self->set_conf('FOREIGN', 1);
+ },
+ "resolve-deps" => sub {
+ $self->set_conf('RESOLVE_DEPS', 1)
+ },
+ "no-resolve-deps" => sub {
+ $self->set_conf('RESOLVE_DEPS', 0)
+ },
+ "keep-debootstrap-dir" => sub {
+ $self->set_conf('KEEP_DEBOOTSTRAP_DIR', 1)
+ },
+ "debootstrap=s" => sub {
+ $self->set_conf('DEBOOTSTRAP', $_[1])
+ },
+ "exclude=s" => sub {
+ $self->set_conf('EXCLUDE', $_[1]);
+ },
+ "include=s" => sub {
+ $self->set_conf('INCLUDE', $_[1]);
+ },
+ "components=s" => sub {
+ $self->set_conf('COMPONENTS', $_[1]);
+ },
+ "keyring=s" => sub {
+ $self->set_conf('KEYRING', $_[1]);
+ },
+ "setup-only" => sub {
+ $self->set_conf('SETUP_ONLY', 1);
+ },
+ "make-sbuild-tarball=s" => sub {
+ $self->set_conf('MAKE_SBUILD_TARBALL', $_[1]);
+ },
+ "keep-sbuild-chroot-dir" => sub {
+ $self->set_conf('KEEP_SBUILD_CHROOT_DIR', 1);
+ },
+ "no-deb-src" => sub {
+ $self->set_conf('DEB_SRC', 0);
+ },
+ "alias=s" => sub {
+ push @{$self->get_conf('ALIASES')}, $_[1];
+ },
+ "extra-repository=s" => sub {
+ push @{$self->get_conf('EXTRA_REPOSITORIES')}, $_[1];
+ },
+ "command-prefix=s" => sub {
+ $self->set_conf('COMMAND_PREFIX', $_[1]);
+ },
+ "merged-usr" => sub {
+ $self->set_conf('MERGED_USR', 1)
+ },
+ "auto-merged-usr" => sub {
+ $self->set_conf('MERGED_USR', 'auto')
+ },
+ "no-merged-usr" => sub {
+ $self->set_conf('MERGED_USR', 0)
+ });
+}
+
+package main;
+
+use POSIX;
+use Getopt::Long qw(:config no_ignore_case auto_abbrev gnu_getopt);
+use Sbuild qw(dump_file help_text version_text usage_error check_packages);
+use Sbuild::ChrootPlain;
+use Sbuild::ChrootUnshare;
+use Sbuild::ChrootRoot;
+use Sbuild::Sysconfig;
+use Sbuild::Conf qw();
+use Sbuild::Utility;
+use File::Basename qw(dirname);
+use File::Path qw(mkpath rmtree);
+use File::Temp qw(tempfile);
+use File::Copy;
+use Cwd qw(abs_path);
+use IPC::Open3;
+use File::Spec;
+
+sub add_items ($@);
+sub makedir ($$);
+
+my %personalities = (
+ 'armel:arm64' => 'linux32',
+ 'armhf:arm64' => 'linux32',
+ 'i386:amd64' => 'linux32',
+ 'mipsel:mips64el' => 'linux32',
+ 'powerpc:ppc64' => 'linux32',
+);
+
+my $conf = Sbuild::Conf::new();
+Conf::setup($conf);
+exit 1 if !defined($conf);
+my $options = Options->new($conf, "sbuild-createchroot", "8");
+exit 1 if !defined($options);
+
+
+usage_error("sbuild-createchroot",
+ "Incorrect number of options") if (@ARGV <2 || @ARGV >4);
+
+if ($conf->get('CHROOT_MODE') eq 'unshare' and !$conf->get('MAKE_SBUILD_TARBALL')) {
+ usage_error("sbuild-createchroot",
+ "--chroot-mode=unshare requires --make-sbuild-tarball to be set");
+}
+
+if ($conf->get('CHROOT_MODE') eq 'unshare' and $conf->get('SETUP_ONLY')) {
+ usage_error("sbuild-createchroot",
+ "--chroot-mode=unshare is incompatible with --setup-only")
+}
+
+if ($conf->get('CHROOT_MODE') eq 'unshare' and scalar @{$conf->get('ALIASES')} > 0) {
+ usage_error("sbuild-createchroot",
+ "--chroot-mode=unshare is incompatible with --alias")
+}
+
+if ($conf->get('CHROOT_MODE') ne 'schroot' and $conf->get('COMMAND_PREFIX')) {
+ usage_error("sbuild-createchroot",
+ "--command-prefix requires --chroot-mode=schroot")
+}
+
+if ($conf->get('MAKE_SBUILD_TARBALL') and -e $conf->get('MAKE_SBUILD_TARBALL')) {
+ print STDERR "E: tarball already exists: ". $conf->get('MAKE_SBUILD_TARBALL') . "\n";
+ exit 1;
+}
+
+# Make sure fakeroot and build-essential are installed
+$conf->set('INCLUDE', add_items($conf->get('INCLUDE'),
+ "fakeroot",
+ "build-essential"));
+
+# Deal with SUITE-VARIANT
+my $suite = $ARGV[0];
+
+# check if schroot name is already in use
+
+my $chrootname;
+if (defined $conf->get('CHROOT_PREFIX') && $conf->get('CHROOT_PREFIX') ne "") {
+ $chrootname = $conf->get('CHROOT_PREFIX')
+} else {
+ $chrootname = $suite
+}
+$chrootname .= "-" . $conf->get('BUILD_ARCH') . $conf->get('CHROOT_SUFFIX');
+
+if ($conf->get('CHROOT_MODE') eq 'schroot') {
+ # We redirect stderr to /dev/null because otherwise schroot might print
+ # warnings on stderr which throws off autopkgtest
+ open(NULL, ">", File::Spec->devnull);
+ my $pid = open3(my $in = '', \*PH, \*NULL, 'schroot', '-l', '--all-source-chroots');
+ while (my $line = <PH>) {
+ $line ne "source:$chrootname\n" or die "chroot with name $chrootname already exists";
+ }
+ waitpid($pid, 0);
+ if (($? >> 8) != 0) {
+ die "schroot exited with non-zero exit status";
+ }
+}
+
+my $target = $ARGV[1];
+if (-e $target) {
+ if (!-d $target) {
+ die "$target exists and is not a directory";
+ }
+ chmod 0755, $target or die "cannot chmod $target";
+ # only check if the directory is empty if the --setup-only option is not
+ # given because that option needs an already populated directory
+ if (!$conf->get('SETUP_ONLY')) {
+ # check if the directory is empty or contains nothing more than an
+ # empty lost+found directory. The latter exists on freshly created
+ # ext3 and ext4 partitions.
+ # rationale for requiring an empty directory: https://bugs.debian.org/833525
+ opendir(my $dh, $target) or die "Can't opendir($target): $!";
+ while (my $entry = readdir $dh) {
+ # skip the "." and ".." entries
+ next if $entry eq ".";
+ next if $entry eq "..";
+ # if the entry is a directory named "lost+found" then skip it
+ # if it's empty
+ if ($entry eq "lost+found" and -d "$target/$entry") {
+ opendir(my $dh2, "$target/$entry");
+ # Attempt reading the directory thrice. If the third time
+ # succeeds, then it has more entries than just "." and ".."
+ # and must thus not be empty.
+ readdir $dh2;
+ readdir $dh2;
+ # rationale for requiring an empty directory:
+ # https://bugs.debian.org/833525
+ if (readdir $dh2) {
+ die "$target contains a non-empty lost+found directory";
+ }
+ closedir($dh2);
+ } else {
+ die "$target is not empty";
+ }
+ }
+ closedir($dh);
+ }
+} else {
+ # Create the target directory in advance so abs_path (which is buggy)
+ # won't fail. Remove if abs_path is replaced by something better.
+ makedir($target, 0755);
+}
+$target = abs_path($target);
+my $script = undef;
+my $mirror = "http://deb.debian.org/debian";
+
+$mirror = $ARGV[2] if $#ARGV >= 2;
+$script = $ARGV[3] if $#ARGV == 3;
+
+if ($conf->get('VERBOSE')) {
+ print "I: SUITE: $suite\n";
+ print "I: TARGET: $target\n";
+ print "I: MIRROR: $mirror\n";
+ print "I: SCRIPT: $script\n" if (defined($script));
+}
+
+my @args = ("--arch=" . $conf->get('BUILD_ARCH'),
+ "--variant=buildd");
+push @args, "--verbose" if $conf->get('VERBOSE');
+push @args, "--foreign" if $conf->get('FOREIGN');
+push @args, "--keep-debootstrap-dir" if $conf->get('KEEP_DEBOOTSTRAP_DIR');
+push @args, "--include=" . $conf->get('INCLUDE') if $conf->get('INCLUDE');
+push @args, "--exclude=" . $conf->get('EXCLUDE') if $conf->get('EXCLUDE');
+push @args, "--components=" . $conf->get('COMPONENTS')
+ if $conf->get('COMPONENTS');
+push @args, "--keyring=" . $conf->get('KEYRING') if $conf->get('KEYRING');
+push @args, "--no-check-gpg" if defined $conf->get('KEYRING') && $conf->get('KEYRING') eq "";
+push @args, $conf->get('RESOLVE_DEPS') ?
+ "--resolve-deps" : "--no-resolve-deps";
+if ($conf->get('MERGED_USR') ne 'auto') {
+ push @args, $conf->get('MERGED_USR') ?
+ "--merged-usr" : "--no-merged-usr";
+}
+push @args, "$suite", "$target", "$mirror";
+push @args, "$script" if $script;
+
+# Set the path to debootstrap
+my $debootstrap = $conf->get('DEBOOTSTRAP');
+
+# Get the name of the debootstrap binary
+my $debootstrap_bin = $debootstrap;
+$debootstrap_bin =~ s/^.*\///s;
+
+if ($conf->get('VERBOSE')) {
+ print "I: Running $debootstrap_bin " . join(' ',@args) . "\n";
+}
+
+my @idmap;
+if ($conf->get('CHROOT_MODE') eq 'unshare') {
+ @idmap = read_subuid_subgid;
+ # sanity check
+ if (scalar(@idmap) != 2 || $idmap[0][0] ne 'u' || $idmap[1][0] ne 'g') {
+ printf STDERR "invalid idmap\n";
+ return 0;
+ }
+}
+
+# Run debootstrap with specified options.
+if (!$conf->get('SETUP_ONLY')) {
+ if ($conf->get('CHROOT_MODE') eq 'unshare') {
+ if(!test_unshare) {
+ print STDERR "E: unable to to unshare\n";
+ exit 1;
+ }
+
+ makedir($target, 0755);
+
+ my $outer_gid = $REAL_GROUP_ID+0;
+ system(get_unshare_cmd({
+ IDMAP => [['u', '0', $REAL_USER_ID, '1'],
+ ['g', '0', $outer_gid, '1'],
+ ['u', '1', $idmap[0][2], '1'],
+ ['g', '1', $idmap[1][2], '1'],
+ ]
+ }), 'chown', '1:1', $target);
+ my @cmd = ('env', 'PATH=/usr/sbin:/usr/bin:/sbin:/bin',
+ get_unshare_cmd({UNSHARE_FLAGS => CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC, FORK => 1, IDMAP => \@idmap}), 'sh', '-c', "
+ rootdir=\"\$1\"; shift;
+ hostname sbuild;
+ mkdir -p \"\$rootdir/dev\";
+ for f in null zero full random urandom tty; do
+ touch \"\$rootdir/dev/\$f\";
+ chmod -rwx \"\$rootdir/dev/\$f\";
+ mount -o bind \"/dev/\$f\" \"\$rootdir/dev/\$f\";
+ done;
+ mkdir -p \"\$rootdir/sys\";
+ mount -o rbind /sys \"\$rootdir/sys\";
+ mkdir -p \"\$rootdir/proc\";
+ mount -t proc proc \"\$rootdir/proc\";
+ mkdir -p \"\$rootdir/fakebin\";
+ ln -sf /bin/true \"\$rootdir/fakebin/mknod\";
+ ln -sf /bin/true \"\$rootdir/fakebin/mount\";
+ export PATH=\"\$rootdir/fakebin:/fakebin:\$PATH\"
+ \"\$@\";
+ exit_status=\$?;
+ rm \"\$rootdir/fakebin/mknod\" \"\$rootdir/fakebin/mount\";
+ rm -d \"\$rootdir/fakebin\";
+ exit \$exit_status;
+ ", '--', $target, $debootstrap, @args
+ );
+ !system(@cmd) or die "E: Error running @cmd";
+ } else {
+ if ($REAL_USER_ID == 0) {
+ chown(0, 0, $target) or die "cannot chown $target";
+ }
+ !system($debootstrap, @args) or die "E: Error running $debootstrap_bin";
+ }
+}
+
+if (!($conf->get('SETUP_ONLY') && $conf->get('MAKE_SBUILD_TARBALL'))) {
+ my $sources_list = "";
+
+ # Add deb-src to /etc/apt/sources.list.
+ if ($conf->get('DEB_SRC')) {
+ my $comps = join(' ',split(/,/,$conf->get('COMPONENTS')));
+ $sources_list .= "deb-src $mirror $suite $comps\n";
+ }
+
+ # Add extra repositories to /etc/apt/sources.list
+ for my $repo (@{$conf->get('EXTRA_REPOSITORIES')}) {
+ $sources_list .= "$repo\n";
+ }
+
+ my $passwd_sbuild = `getent passwd sbuild`;
+ my $group_sbuild = `getent group sbuild`;
+
+ my $setup_script = <<"EOF";
+open (my \$passwd_fd, ">>", "\$target/etc/passwd") or die "cannot open /etc/passwd";
+print \$passwd_fd \$passwd_sbuild;
+close(\$passwd_fd);
+open (my \$group_fd, ">>", "\$target/etc/group") or die "cannot open /etc/group";
+print \$group_fd \$group_sbuild;
+close(\$group_fd);
+
+
+# Set up minimal /etc/hosts if it didn't exist yet. Normally, the package
+# netbase would create the file.
+my \$hosts = "\${target}/etc/hosts";
+if (! -e \$hosts) {
+ open(HOSTS, ">\$hosts")
+ or die "Can't open \$hosts for writing";
+ # write the default content that would be created by the netbase package
+ print HOSTS <<"EOF2";
+127.0.0.1 localhost
+::1 localhost ip6-localhost ip6-loopback
+ff02::1 ip6-allnodes
+ff02::2 ip6-allrouters
+
+EOF2
+ close HOSTS or die "Can't close \$hosts";
+
+ # Display /etc/hosts.
+ print "I: Configured /etc/hosts:\n";
+ dump_file("\$hosts");
+}
+
+# Set up minimal /usr/sbin/policy-rc.d.
+my \$policy_rc_d = "\${target}/usr/sbin/policy-rc.d";
+open(POLICY_RC_D, ">\$policy_rc_d")
+ or die "Can't open \$policy_rc_d for writing";
+print POLICY_RC_D <<"EOF2";
+#!/bin/sh
+echo "All runlevel operations denied by policy" >&2
+exit 101
+EOF2
+
+close POLICY_RC_D or die "Can't close \$policy_rc_d";
+
+my (undef, undef, \$uid, undef) = getpwnam('root');
+chown(\$uid, -1, \$policy_rc_d) == 1
+ or die "E: Failed to set root: ownership on \$policy_rc_d";
+chmod(0775, \$policy_rc_d) == 1
+ or die "E: Failed to set 0755 permissions on \$policy_rc_d";
+
+# Display /usr/sbin/policy-rc.d.
+print "I: Configured /usr/sbin/policy-rc.d:\n";
+dump_file("\$policy_rc_d");
+EOF
+
+ if ($conf->get('DEB_SRC') || scalar @{$conf->get('EXTRA_REPOSITORIES')} > 0) {
+ $setup_script .= <<"EOF";
+my \$sources = "\${target}/etc/apt/sources.list";
+open(SOURCES, ">>\$sources")
+ or die "E: Can't open \$sources for writing";
+
+print SOURCES \$sources_list;
+close SOURCES or die "E: Can't close \$sources";
+EOF
+ }
+
+ $setup_script .= <<"EOF";
+# Display /etc/apt/sources.list.
+print "I: Configured APT /etc/apt/sources.list:\n";
+dump_file("\${target}/etc/apt/sources.list");
+print "I: Please add any additional APT sources to \${target}/etc/apt/sources.list\n";
+EOF
+ if ($conf->get('CHROOT_MODE') eq 'unshare') {
+ my $group_sbuild = `getent group sbuild`;
+ $setup_script = <<"EOF";
+use strict;
+use warnings;
+use Sbuild qw(dump_file);
+my \$target = \$ARGV[0];
+my \$passwd_sbuild = \$ARGV[1];
+my \$group_sbuild = \$ARGV[2];
+my \$sources_list = \$ARGV[3];
+$setup_script
+EOF
+ !system(get_unshare_cmd(
+ {IDMAP => \@idmap}), 'perl', '-e', $setup_script, $target, $passwd_sbuild, $group_sbuild, $sources_list
+ ) or die "E: failed running setup script";
+ } else {
+ eval $setup_script;
+ if ($@) {
+ die "E: failed running setup script: $@\n";
+ }
+ }
+}
+
+if ($conf->get('CHROOT_MODE') eq 'schroot') {
+ # Write out schroot chroot configuration.
+
+ my $arch = $conf->get('BUILD_ARCH');
+ my $config_entry = <<"EOF";
+[$chrootname]
+description=Debian $suite/$arch autobuilder
+groups=root,sbuild
+root-groups=root,sbuild
+profile=sbuild
+EOF
+
+ # Determine the schroot chroot configuration to use.
+ if ($conf->get('MAKE_SBUILD_TARBALL')) {
+ my $tarball = $conf->get('MAKE_SBUILD_TARBALL');
+
+ # Default to using tar gzip compression if unable to determine compression
+ # mode via file extension.
+ if ($tarball !~ /\.(tgz|tbz|tlz|txz|tar(\.(gz|bz2|lz|xz))?)$/) {
+ print "I: Renaming sbuild tarball '$tarball' to '$tarball.tar.gz'\n";
+ $tarball .= ".tar.gz";
+ $conf->set('MAKE_SBUILD_TARBALL', $tarball);
+ }
+
+ $config_entry .= <<"EOF";
+type=file
+file=$tarball
+EOF
+ } else {
+ # Determine whether system has overlayfs capability
+ my $uniontype = "none";
+ if (lc("$^O") =~ /linux/ && -e '/sbin/modprobe') {
+ my $ret = system(qw(/sbin/modprobe overlay));
+ if ($ret == 0 && open(FILE, "/proc/filesystems")) {
+ if (grep {/\soverlay$/} <FILE>) {
+ $uniontype = "overlay";
+ }
+ close(FILE);
+ }
+ }
+
+ $config_entry .= <<"EOF";
+type=directory
+directory=$target
+union-type=$uniontype
+EOF
+ }
+
+ if (scalar @{$conf->get('ALIASES')} > 0) {
+ my $aliases = join ',', @{$conf->get('ALIASES')};
+ $config_entry .= "aliases=$aliases\n";
+ }
+
+ if ($conf->get('COMMAND_PREFIX') ne '') {
+ $config_entry .= "command-prefix=" . $conf->get('COMMAND_PREFIX') . "\n";
+ }
+
+ if (-d "/etc/schroot/chroot.d") {
+ # TODO: Don't hardcode path
+ my $SCHROOT_CONF =
+ new File::Temp( TEMPLATE => "$chrootname-XXXXXX",
+ DIR => "/etc/schroot/chroot.d",
+ UNLINK => 0)
+ or die "Can't open schroot configuration file: $!\n";
+
+ print $SCHROOT_CONF "$config_entry";
+
+ my ($personality, $personality_message);
+ # Detect whether personality might be needed.
+ if ($conf->get('ARCH') ne $conf->get('BUILD_ARCH')) {
+ # Take care of the known case(s).
+ my $key = $conf->get('BUILD_ARCH') . ':' . $conf->get('ARCH');
+ if (exists $personalities{$key}) {
+ $personality = $personalities{$key};
+ $personality_message =
+ "I: Added personality=$personality automatically " .
+ "(" . $conf->get('BUILD_ARCH') . " on " . $conf->get('ARCH') . ").\n";
+ } else {
+ $personality_message =
+ "W: The selected architecture and the current architecture do not match\n" .
+ "W: (" . $conf->get('BUILD_ARCH') . " versus " . $conf->get('ARCH') . ").\n" .
+ "I: You probably need to add a personality option (see schroot(1)).\n" .
+ "I: You may want to report your use case to the sbuild developers so that\n" .
+ "I: the appropriate option gets automatically added in the future.\n\n";
+ }
+ }
+
+ # Add personality if detected.
+ print $SCHROOT_CONF "personality=$personality\n" if $personality;
+
+ # Needed to display file below.
+ $SCHROOT_CONF->flush();
+
+ # Display schroot configuration.
+ print "I: schroot chroot configuration written to $SCHROOT_CONF.\n";
+ chmod 0644, "$SCHROOT_CONF";
+ dump_file("$SCHROOT_CONF");
+ print "I: Please rename and modify this file as required.\n";
+ print $personality_message if $personality_message;
+ }
+}
+
+if ($conf->get('CHROOT_MODE') eq 'schroot' || $conf->get('CHROOT_MODE') eq 'sudo') {
+ if (! -d "$Sbuild::Sysconfig::paths{'SBUILD_SYSCONF_DIR'}/chroot") {
+ makedir("$Sbuild::Sysconfig::paths{'SBUILD_SYSCONF_DIR'}/chroot", 0775);
+ }
+
+ # Populate /etc/sbuild/chroot with a symlink to be able to use the chroot in
+ # sudo mode for directory based chroots
+ my $chrootlink = "$Sbuild::Sysconfig::paths{'SBUILD_SYSCONF_DIR'}/chroot/$chrootname";
+ if ((defined $chrootlink) && (! $conf->get('MAKE_SBUILD_TARBALL'))) {
+ if (! -e $chrootlink) {
+ if (symlink($target, $chrootlink)) {
+ print "I: sudo chroot configuration linked as $Sbuild::Sysconfig::paths{'SBUILD_SYSCONF_DIR'}/chroot/$chrootname.\n";
+ } else {
+ print STDERR "E: Failed to symlink $target to $chrootlink: $!\n";
+ }
+ } else {
+ print "W: Not creating symlink $target to $chrootlink: file already exists\n";
+
+ }
+ }
+}
+
+if (!$conf->get('SETUP_ONLY') || !$conf->get('MAKE_SBUILD_TARBALL')) {
+ # FIXME: also update packages with the unshare backend
+ if ($conf->get('ARCH') eq $conf->get('HOST_ARCH') && $conf->get('CHROOT_MODE') ne 'unshare') {
+ my $session = Sbuild::ChrootPlain->new($conf, $target);
+ my $host = Sbuild::ChrootRoot->new($conf);
+ if (defined($session)) {
+ $session->set('Log Stream', \*STDOUT);
+
+ if (!$session->begin_session() || !$host->begin_session()) {
+ print STDERR "E: Error creating chroot session: skipping apt update\n";
+ } else {
+ my $resolver = Sbuild::AptResolver->new($conf, $session, $host);
+ $resolver->setup();
+
+ print "I: Setting reference package list.\n";
+ check_packages($session, "set");
+
+ print "I: Updating chroot.\n";
+ my $status = $resolver->update();
+ print "W: Failed to update APT package lists\n"
+ if ($status);
+
+ $status = $resolver->distupgrade();
+ print "W: Failed to upgrade chroot\n"
+ if ($status);
+
+ $status = $resolver->clean();
+ print "W: Failed to clean up downloaded packages\n"
+ if ($status);
+
+ $resolver->cleanup();
+ $session->end_session();
+ $session = undef;
+ }
+ }
+ } elsif ($conf->get('ARCH') ne $conf->get('HOST_ARCH')) {
+ print "W: The selected architecture and the current architecture do not match\n";
+ print "W: (" . $conf->get('BUILD_ARCH') . " versus " . $conf->get('ARCH') . ").\n";
+ print "W: Not automatically updating APT package lists.\n";
+ print "I: Run \"apt-get update\" and \"apt-get dist-upgrade\" prior to use.\n";
+ print "I: Run \"sbuild-checkpackages --set\" to set reference package list.\n";
+ }
+}
+
+# This block makes the tarball chroot if one has been requested and delete
+# the sbuild chroot directory created, unless it's been requested to keep the
+# directory.
+if ($conf->get('MAKE_SBUILD_TARBALL') && !$conf->get('SETUP_ONLY')) {
+ my ($tmpfh, $tmpfile) = tempfile("XXXXXX");
+ my @program_list = ("/bin/tar", "-c", "-C", $target);
+ push @program_list, get_tar_compress_options($conf->get('MAKE_SBUILD_TARBALL'));
+ if ($conf->get('CHROOT_MODE') ne 'unshare') {
+ push @program_list, '-f', $tmpfile;
+ }
+ push @program_list, './';
+
+ print "I: Creating tarball...\n";
+ if ($conf->get('CHROOT_MODE') eq 'unshare') {
+ open(my $in, '-|', get_unshare_cmd(
+ {IDMAP => \@idmap}), @program_list
+ ) // die "could not exec tar";
+ if (copy($in, $tmpfile) != 1 ) {
+ die "unable to copy: $!\n";
+ }
+ close($in) or die "Could not create chroot tarball: $?\n";
+ } else {
+ system(@program_list) == 0 or die "Could not create chroot tarball: $?\n";
+ }
+
+ makedir(dirname($conf->get('MAKE_SBUILD_TARBALL')), 0755);
+ move("$tmpfile", $conf->get('MAKE_SBUILD_TARBALL')) or die "cannot mv to $conf->get('MAKE_SBUILD_TARBALL'): $!";
+ chmod 0644, $conf->get('MAKE_SBUILD_TARBALL');
+
+ print "I: Done creating " . $conf->get('MAKE_SBUILD_TARBALL') . "\n";
+
+ if (! $conf->get('KEEP_SBUILD_CHROOT_DIR')) {
+ if ($conf->get('CHROOT_MODE') eq 'unshare') {
+ # this looks like a recipe for disaster, but since we execute "rm -rf" with
+ # lxc-usernsexec, we only have permission to delete the files that were
+ # created with the fake root user
+ system(get_unshare_cmd({IDMAP => \@idmap}), 'rm', '-rf', $target);
+ die "Unable to remove $target" if -e $target;
+ } else {
+ rmtree("$target");
+ }
+ print "I: chroot $target has been removed.\n";
+ } else {
+ print "I: chroot $target has been kept.\n";
+ }
+}
+
+print "I: Successfully set up $suite chroot.\n";
+if ($conf->get('CHROOT_MODE') eq 'schroot') {
+ print "I: Run \"sbuild-adduser\" to add new sbuild users.\n";
+}
+
+exit 0;
+
+# Add items to the start of a comma-separated list, and remove the
+# items from later in the list if they were already in the list.
+sub add_items ($@) {
+ my $items = shift;
+ my @add = @_;
+
+ my $ret = '';
+ my %values;
+
+ foreach (@_) {
+ $values{$_} = '';
+ $ret .= "$_,"
+ }
+
+ # Only add if not already used, to eliminate duplicates.
+ foreach (split (/,/,$items)) {
+ $ret .= "$_," if (!defined($values{$_}));
+ }
+
+ # Remove trailing comma.
+ $ret =~ s/,$//;
+
+ return $ret;
+}
+
+sub makedir ($$) {
+ my $dir = shift;
+ my $perms = shift;
+
+ mkpath($dir,
+ { mode => $perms,
+ verbose => 1,
+ error => \my $error
+ });
+
+ for my $diag (@$error) {
+ my ($file, $message) = each %$diag;
+ print "E: Can't make directory $file: $message\n";
+ }
+}
diff --git a/bin/sbuild-cross-resolver b/bin/sbuild-cross-resolver
new file mode 100755
index 0000000..110fd53
--- /dev/null
+++ b/bin/sbuild-cross-resolver
@@ -0,0 +1,77 @@
+#!/usr/bin/perl
+#
+# This script is in the public domain
+#
+# Author: Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
+#
+# Thin layer around /usr/lib/apt/solvers/apt which removes M-A:foreign and
+# Essential:yes packages that are not arch:all and not arch:native from the
+# EDSP before handing it to the apt solver. This is useful for resolving cross
+# build dependencies as it makes sure that M-A:foreign packages and
+# Essential:yes packages in the solution must come from the build architecture.
+
+use strict;
+use warnings;
+
+if (! -e '/usr/lib/apt/solvers/apt') {
+ printf STDOUT 'Error: ERR_NO_SOLVER\n';
+ printf STDOUT 'Message: The external apt solver doesn\'t exist. You must install the apt-utils package.\n';
+ exit 1;
+}
+
+my $buffer = '';
+my $architecture = undef;
+my $essential = 0;
+my $multiarch = 'no';
+my $build_arch;
+sub keep {
+ if ( $multiarch ne 'foreign' and !$essential ) {
+ return 1;
+ }
+ if ( !defined $architecture ) {
+ print STDOUT 'Error: ERR_NO_ARCH\n';
+ print STDOUT 'Message: package without architecture\n';
+ exit 1;
+ }
+ if ( $architecture eq 'all' or $architecture eq $build_arch ) {
+ return 1;
+ }
+ return 0;
+}
+open my $fh, '|-', '/usr/lib/apt/solvers/apt';
+my $first_stanza = 1;
+while ( my $line = <STDIN> ) {
+ $buffer .= $line;
+ if ( $line eq "\n" ) {
+ if ($first_stanza) {
+ if (! defined $architecture) {
+ print STDOUT 'ERROR: ERR_NO_ARCH';
+ print STDOUT 'Message: no Architecture field in first stanza';
+ exit 1;
+ }
+ $build_arch = $architecture;
+ $first_stanza = 0;
+ }
+ if (keep) {
+ print $fh $buffer;
+ }
+ $buffer = '';
+ $architecture = undef;
+ $essential = 0;
+ $multiarch = 'no';
+ next;
+ }
+ if ( $line =~ /^Essential: yes\n$/ ) {
+ $essential = 1;
+ }
+ if ( $line =~ /^Multi-Arch: (.*)\n$/ ) {
+ $multiarch = $1;
+ }
+ if ( $line =~ /^Architecture: (.*)\n$/ ) {
+ $architecture = $1;
+ }
+}
+if (keep) {
+ print $fh $buffer;
+}
+close $fh;
diff --git a/bin/sbuild-debian-developer-setup b/bin/sbuild-debian-developer-setup
new file mode 100755
index 0000000..fa9fb51
--- /dev/null
+++ b/bin/sbuild-debian-developer-setup
@@ -0,0 +1,77 @@
+#!/usr/bin/perl
+#
+# Set up sbuild so that packages for Debian unstable can be built and
+# maintenance is done automatically via a daily update cronjob.
+# Copyright © 2017 Michael Stapelberg <stapelberg@debian.org>.
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+use v5.10;
+use Getopt::Long;
+use Sbuild qw(help_text);
+
+my $dist = "debian";
+my $suite = "unstable";
+chomp(my $arch = `dpkg --print-architecture`);
+
+my @options = (
+ 'distribution=s' => \$dist,
+ 'suite=s' => \$suite,
+ 'arch=s' => \$arch,
+ 'help' => sub { help_text(1, "sbuild-debian-developer-setup") },
+ );
+GetOptions(@options);
+
+if (!defined($ENV{SUDO_USER})) {
+ die "Please run sudo $0";
+}
+
+system("adduser", "--", $ENV{SUDO_USER}, "sbuild") == 0
+ or die "adduser failed: $?";
+
+sub chroot_exists {
+ system("schroot -i -c chroot:$suite-$arch-sbuild >/dev/null 2>&1") == 0
+}
+
+if (!chroot_exists()) {
+ my @aliases = ();
+ if ( $suite eq "unstable" || $suite eq "sid" ) {
+ @aliases = ( "--alias=UNRELEASED", "--alias=UNRELEASED-$arch-sbuild" );
+ if ( $suite eq "unstable" ) {
+ push @aliases, "--alias=sid";
+ }
+ if ( $suite eq "sid" ) {
+ push @aliases, "--alias=unstable";
+ }
+ }
+ system("sbuild-createchroot",
+ "--command-prefix=eatmydata",
+ "--include=eatmydata",
+ @aliases,
+ "$suite",
+ "/srv/chroot/$suite-$arch-sbuild",
+ "http://localhost:3142/deb.debian.org/debian") == 0
+ or die "sbuild-createchroot failed: $!";
+} else {
+ say "chroot $suite-$arch-sbuild already exists";
+}
+
+say "Your current user is now part of the sbuild group (no need to run sbuild-adduser) and a chroot environment exists in /srv/chroot/$suite-$arch-sbuild";
+
+say "Now run `newgrp sbuild', or log out and log in again.";
diff --git a/bin/sbuild-debuild b/bin/sbuild-debuild
new file mode 100644
index 0000000..ca15a77
--- /dev/null
+++ b/bin/sbuild-debuild
@@ -0,0 +1,391 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
+# Copyright © 2009 Andres Mejia <mcitadel@gmail.com>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+package Conf;
+
+sub setup {
+ my $conf = shift;
+
+ my %update_keys = (
+ 'DPKG_BUILDPACKAGE_OPTS' => {
+ DEFAULT => ['-S',
+ '-us',
+ '-uc',
+ ]
+ },
+ 'DPKG_BUILDPACKAGE_EXTRA_OPTS' => {
+ DEFAULT => []
+ },
+ 'SBUILD_OPTS' => {
+ DEFAULT => []
+ },
+ 'SBUILD_EXTRA_OPTS' => {
+ DEFAULT => []
+ },
+ 'LINTIAN_OPTS' => {
+ DEFAULT => []
+ },
+ 'LINTIAN_EXTRA_OPTS' => {
+ DEFAULT => []
+ },
+ 'NO_LINTIAN' => {
+ DEFAULT => 0
+ },
+ 'PRE_DPKG_BUILDPACKAGE_COMMANDS' => {
+ DEFAULT => []
+ },
+ 'PRE_SBUILD_COMMANDS' => {
+ DEFAULT => []
+ },
+ 'PRE_LINTIAN_COMMANDS' => {
+ DEFAULT => []
+ },
+ 'PRE_EXIT_COMMANDS' => {
+ DEFAULT => []
+ },
+ );
+
+ $conf->set_allowed_keys(\%update_keys);
+}
+
+# This subroutine is used to read a different set of config files for
+# sbuild-debuild
+sub read_sbuild_debuild_config {
+ my $self = shift;
+
+ # Our HOME environment variable.
+ my $HOME = $self->get('HOME');
+
+ # Variables are undefined, so config will default to DEFAULT if unset.
+ my $dpkg_buildpackage_opts = undef;
+ my $dpkg_buildpackage_extra_opts = undef;
+ my $sbuild_opts = undef;
+ my $sbuild_extra_opts = undef;
+ my $lintian_opts = undef;
+ my $lintian_extra_opts = undef;
+ my $no_lintian = undef;
+ my $pre_dpkg_buildpackage_commands = undef;
+ my $pre_sbuild_commands = undef;
+ my $pre_lintian_commands = undef;
+ my $pre_exit_commands = undef;
+
+ foreach ("/etc/sbuild/sbuild-debuild.conf", "$HOME/.sbuild-debuildrc") {
+ if (-r $_) {
+ my $e = eval `cat "$_"`;
+ if (!defined($e)) {
+ print STDERR "E: $_: Errors found in configuration file:\n$@";
+ exit(1);
+ }
+ }
+ }
+
+ $self->set('DPKG_BUILDPACKAGE_OPTS', $dpkg_buildpackage_opts)
+ if ($dpkg_buildpackage_opts);
+ push(@{$self->get('DPKG_BUILDPACKAGE_OPTS')},
+ @{$dpkg_buildpackage_extra_opts}) if ($dpkg_buildpackage_extra_opts);
+ $self->set('SBUILD_OPTS', $sbuild_opts)
+ if ($sbuild_opts);
+ push(@{$self->get('SBUILD_OPTS')}, @{$sbuild_extra_opts})
+ if ($sbuild_extra_opts);
+ $self->set('LINTIAN_OPTS', $lintian_opts)
+ if ($lintian_opts);
+ push(@{$self->get('LINTIAN_OPTS')}, @{$lintian_extra_opts})
+ if ($lintian_extra_opts);
+ $self->set('NO_LINTIAN', $no_lintian)
+ if ($no_lintian);
+ $self->set('PRE_DPKG_BUILDPACKAGE_COMMANDS',
+ $pre_dpkg_buildpackage_commands) if ($pre_dpkg_buildpackage_commands);
+ $self->set('PRE_SBUILD_COMMANDS', $pre_sbuild_commands)
+ if ($pre_sbuild_commands);
+ $self->set('PRE_LINTIAN_COMMANDS', $pre_lintian_commands)
+ if ($pre_lintian_commands);
+ $self->set('PRE_EXIT_COMMANDS', $pre_exit_commands)
+ if ($pre_exit_commands);
+}
+
+package Options;
+
+use Sbuild::OptionsBase;
+use Sbuild::Conf;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::OptionsBase);
+
+ @EXPORT = qw();
+}
+
+sub set_options {
+ my $self = shift;
+
+ $self->add_options(
+ "dpkg-buildpackage-opts=s" => sub {
+ my @opt_values = split(/\s+/,$_[1]);
+ $self->set_conf('DPKG_BUILDPACKAGE_OPTS', \@opt_values);
+ },
+ "dpkg-buildpackage-extra-opts=s" => sub {
+ my @opt_values = split(/\s+/,$_[1]);
+ push(@{$self->get_conf('DPKG_BUILDPACKAGE_OPTS')},
+ @opt_values);
+ },
+ "sbuild-opts=s" => sub {
+ my @opt_values = split(/\s+/,$_[1]);
+ $self->set_conf('SBUILD_OPTS', \@opt_values);
+ },
+ "sbuild-extra-opts=s" => sub {
+ my @opt_values = split(/\s+/,$_[1]);
+ push(@{$self->get_conf('SBUILD_OPTS')},
+ @opt_values);
+ },
+ "lintian-opts=s" => sub {
+ my @opt_values = split(/\s+/,$_[1]);
+ $self->set_conf('LINTIAN_OPTS', \@opt_values);
+ },
+ "lintian-extra-opts=s" => sub {
+ my @opt_values = split(/\s+/,$_[1]);
+ push(@{$self->get_conf('LINTIAN_OPTS')},
+ @opt_values);
+ },
+ "no-lintian" => sub {
+ $self->set_conf('NO_LINTIAN', 1);
+ },
+ "pre-dpkg-buildpackage-commands=s" => sub {
+ push(@{$self->get_conf('PRE_DPKG_BUILDPACKAGE_COMMANDS')}, $_[1]);
+ },
+ "pre-sbuild-commands=s" => sub {
+ push(@{$self->get_conf('PRE_SBUILD_COMMANDS')}, $_[1]);
+ },
+ "pre-lintian-commands=s" => sub {
+ push(@{$self->get_conf('PRE_LINTIAN_COMMANDS')}, $_[1]);
+ },
+ "pre-exit-commands=s" => sub {
+ push(@{$self->get_conf('PRE_EXIT_COMMANDS')}, $_[1]);
+ },
+ );
+}
+
+package main;
+
+use Getopt::Long;
+use Cwd;
+use File::Basename;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+
+my $conf = Sbuild::Conf::new();
+Conf::setup($conf);
+exit 1 if !defined($conf);
+$conf->read_sbuild_debuild_config();
+my $options = Options->new($conf, "sbuild-debuild", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+# This subroutine determines the architecture we're building for.
+sub detect_arch {
+ # Detect if we're building for another architecture
+ my @values = grep {/--arch=.+/} @{$conf->get('SBUILD_OPTS')};
+ my $arch_opt = $values[0];
+ $arch_opt =~ s/--arch=// if ($arch_opt);
+
+ # Determine the arch using dpkg-architecture
+ my $dpkg_arch_command = '/usr/bin/dpkg-architecture -qDEB_HOST_ARCH';
+ $dpkg_arch_command .= " -a$arch_opt" if ($arch_opt);
+
+ # Grab the architecture and return it. We discard output from STDERR
+ # to suppress the "Specified GNU system ... does not match" warning that
+ # may occur from dpkg-architecture
+ my $arch = qx($dpkg_arch_command 2>/dev/null);
+ chomp $arch;
+ return $arch;
+}
+
+# This subroutine determines the package and version we're building for
+sub detect_package_and_version {
+ my ($build_input) = @_;
+
+ # chdir into package directories, but not dsc files
+ chdir($build_input) unless ($build_input =~ /.*\.dsc$/);
+
+ my $output;
+ if ($build_input =~ m/.*\.dsc$/) {
+ # Read the dsc file directly.
+ open($output, '<', $build_input);
+ } else {
+ # Grab the output from dpkg-parsechangelog
+ my $dpkg_parsechangelog = '/usr/bin/dpkg-parsechangelog';
+ open($output, '-|', $dpkg_parsechangelog);
+ }
+
+ # Grab the package and version info
+ my ($package, $version);
+ while (<$output>) {
+ $package = $1 if ($_ =~ /^Source: (.*)$/);
+ $version = $1 if ($_ =~ /^Version: (.*)$/);
+ last if (($package) and ($version));
+ }
+ return ($package, $version);
+}
+
+# This subroutine strips the epoch from a Debian package version.
+sub strip_epoch {
+ my ($version) = @_;
+ $version =~ s/^[^:]*?://;
+ return $version;
+}
+
+# This subroutine processes the array external ommands to run at the various
+# stages of sbuild-debuild's run
+sub process_commands {
+ my ($commands, $dsc, $source_changes, $bin_changes) = @_;
+
+ # Determine which set of commands to run based on the string $commands
+ my @commands;
+ if ($commands eq "pre_dpkg_buildpackage_commands") {
+ @commands = @{$conf->get('PRE_DPKG_BUILDPACKAGE_COMMANDS')};
+ } elsif ($commands eq "pre_sbuild_commands") {
+ @commands = @{$conf->get('PRE_SBUILD_COMMANDS')};
+ } elsif ($commands eq "pre_lintian_commands") {
+ @commands = @{$conf->get('PRE_LINTIAN_COMMANDS')};
+ } elsif ($commands eq "pre_exit_commands") {
+ @commands = @{$conf->get('PRE_EXIT_COMMANDS')};
+ }
+
+ # Run each command, substituting the various @SBUILD_DEBUILD_*@ options from
+ # the commands to run with the appropriate subsitutions.
+ my $returnval = 1;
+ foreach my $command (@commands) {
+ $command =~ s/\@SBUILD_DEBUILD_DSC@/$dsc/;
+ $command =~ s/\@SBUILD_DEBUILD_SOURCE_CHANGES@/$source_changes/;
+ $command =~ s/\@SBUILD_DEBUILD_BIN_CHANGES@/$bin_changes/;
+ my @args = split(/\s+/, $command);
+ print "Running $command\n";
+ system(@args);
+ if (($? >> 8) != 0) {
+ print "Failed to run command ($command): $?";
+ $returnval = 0;
+ }
+ }
+ return $returnval;
+}
+
+# Subroutine to determine various files we'll be working with.
+sub detect_files {
+ my ($build_input) = @_;
+
+ # Determine the dsc and changes files that we'll use
+ my ($package, $version) = detect_package_and_version($build_input);
+ $version = strip_epoch($version);
+ my $arch = detect_arch();
+ my ($dsc, $dirname);
+ if ($build_input =~ /.*\.dsc$/) {
+ $dsc = Cwd::abs_path("$build_input");
+ $dirname = Cwd::abs_path(dirname($build_input));
+ } else {
+ $dirname = Cwd::abs_path("$build_input/..");
+ $dsc = "$dirname/" . $package . "_" . "$version.dsc";
+ }
+ my $source_changes = "$dirname/" . $package . "_$version" .
+ "_source.changes";
+ my $bin_changes = "$dirname/" . $package . "_$version" . "_$arch.changes";
+
+ return ($dsc, $source_changes, $bin_changes);
+}
+
+# Process a debianized package directory or .dsc file.
+sub process_package {
+ my ($build_input) = @_;
+
+ # We use this to determine if there was a problem processing the external
+ # commands.
+ my $returnval = 1;
+
+ # Save the current directory we're in.
+ my $currentdir = getcwd();
+
+ # Determine the dsc and changes files that we'll use
+ my ($dsc, $source_changes, $bin_changes) = detect_files($build_input);
+
+ print "Processing pre dpkg-buildpackage commands.\n";
+ $returnval = 0 unless process_commands("pre_dpkg_buildpackage_commands",
+ $dsc, $source_changes, $bin_changes);
+
+ if ($build_input !~ /.*\.dsc$/) {
+ chdir($build_input);
+ print "Running dpkg-buildpackage.\n";
+ system('/usr/bin/dpkg-buildpackage',
+ @{$conf->get('DPKG_BUILDPACKAGE_OPTS')});
+ if (($? >> 8) != 0) {
+ print "Running dpkg-buildpckage failed: $?";
+ chdir($currentdir);
+ return 0;
+ }
+ }
+
+ print "Processing pre sbuild commands.\n";
+ $returnval = 0 unless process_commands("pre_sbuild_commands", $dsc,
+ $source_changes, $bin_changes);
+
+ chdir(dirname($dsc));
+ print "Running sbuild.\n";
+ system('/usr/bin/sbuild', @{$conf->get('SBUILD_OPTS')}, $dsc);
+ if (($? >> 8) != 0) {
+ print "Running sbuild failed: $?";
+ chdir($currentdir);
+ return 0;
+ }
+
+ print "Processing pre lintian commands.\n";
+ $returnval = 0 unless process_commands("pre_lintian_commands", $dsc,
+ $source_changes, $bin_changes);
+
+ if ((!$conf->get('NO_LINTIAN')) && (-x '/usr/bin/lintian')) {
+ print "Running lintian.\n";
+ system('/usr/bin/lintian', @{$conf->get('LINTIAN_OPTS')}, '--', $bin_changes);
+ if (($? >> 8) != 0) {
+ print "Running lintian failed: $?";
+ chdir($currentdir);
+ return 0;
+ }
+ }
+
+ print "Processing commands run before exiting.\n";
+ $returnval = 0 unless process_commands("pre_exit_commands", $dsc,
+ $source_changes, $bin_changes);
+
+ # Go back to the directory we were in earlier
+ chdir($currentdir);
+ return $returnval;
+}
+
+# Process each package directory and .dsc file.
+my $status = 0;
+if (!@ARGV) {
+ $status = 1 unless process_package(getcwd());
+} else {
+ foreach my $arg (@ARGV) {
+ $status = 1 unless process_package($arg);
+ }
+}
+exit $status;
diff --git a/bin/sbuild-destroychroot b/bin/sbuild-destroychroot
new file mode 100755
index 0000000..264c2ba
--- /dev/null
+++ b/bin/sbuild-destroychroot
@@ -0,0 +1,181 @@
+#!/usr/bin/perl
+#
+# Destroy a chroot created by sbuild-createchroot
+#
+# Copyright © 2016 Johannes Schauer Marin Rodrigues <josch@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+package Conf;
+
+sub setup {
+ my $conf = shift;
+
+ my %destroychroot_keys = (
+ 'CHROOT_SUFFIX' => {
+ DEFAULT => '-sbuild'
+ },
+ );
+
+ $conf->set_allowed_keys(\%destroychroot_keys);
+}
+
+
+package Options;
+
+use Sbuild::OptionsBase;
+use Sbuild::Conf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::OptionsBase);
+
+ @EXPORT = qw();
+}
+
+sub set_options {
+ my $self = shift;
+
+ $self->add_options(
+ "chroot-suffix=s" => sub {
+ $self->set_conf('CHROOT_SUFFIX', $_[1]);
+ },
+ "arch=s" => sub {
+ $self->set_conf('BUILD_ARCH', $_[1]);
+ },
+ );
+}
+
+package main;
+
+use POSIX;
+use Getopt::Long qw(:config no_ignore_case auto_abbrev gnu_getopt);
+use Sbuild qw(usage_error);
+use Sbuild::Utility;
+use Sbuild::ChrootInfoSchroot;
+
+my $conf = Sbuild::Conf::new();
+Conf::setup($conf);
+exit 1 if !defined($conf);
+my $options = Options->new($conf, "sbuild-destroychroot", "8");
+exit 1 if !defined($options);
+
+usage_error("sbuild-destroychroot",
+ "Incorrect number of options") if (scalar @ARGV != 1);
+
+my $chroot = Sbuild::Utility::get_dist($ARGV[0]);
+
+my $chroot_info = Sbuild::ChrootInfoSchroot->new($conf);
+my $session = $chroot_info->create("source",
+ $chroot,
+ undef, # TODO: Add --chroot option
+ $conf->get('BUILD_ARCH'));
+
+if (!defined $session) {
+ die "Error creating chroot info\n";
+}
+
+my $chroot_id = $session->get('Chroot ID');
+
+$chroot_id =~ s/^source://;
+
+opendir my $dir, "/etc/schroot/chroot.d" or die "Cannot open /etc/schroot/chroot.d: $!";
+my @files = readdir $dir;
+closedir $dir;
+
+my $config_path;
+foreach my $file (@files) {
+ my $ininame = "/etc/schroot/chroot.d/$file";
+ -f $ininame || next;
+ open F, $ininame or die "cannot open $ininame\n";
+ my $firstline = <F>;
+ chomp $firstline;
+ close F;
+ $firstline eq "[$chroot_id]" || next;
+ $config_path = $ininame;
+ last;
+}
+
+if (! defined $config_path) {
+ die "Cannot find configuration file for $chroot_id in /etc/schroot/chroot.d\n";
+}
+
+my $chroot_type;
+my $chroot_path;
+open F, $config_path or die "cannot open $config_path\n";
+while (<F>) {
+ if (/^type=(.*)/) {
+ $chroot_type = $1;
+ }
+ if (/^directory=(.*)/) {
+ $chroot_path = $1;
+ }
+ if (/^file=(.*)/) {
+ $chroot_path = $1;
+ }
+}
+close F;
+
+if (! defined $chroot_type) {
+ die "type key missing from config\n";
+}
+
+if (! defined $chroot_path) {
+ die "directory or file key missing from config\n";
+}
+
+if ($chroot_type ne "file" && $chroot_type ne "directory") {
+ die "unknown chroot type: $chroot_type\n";
+}
+
+print "Before deleting the chroot, make sure that it is not in use anymore.\n";
+print "Specifically, make sure that no open schroot session is using it\n";
+print "anymore by running:\n";
+print "\n";
+print " schroot --all-sessions --list\n";
+print "\n";
+if ($chroot_type eq "directory") {
+ print "Make sure that no other process is using the chroot directory anymore, \n";
+ print "for example by running:\n";
+ print "\n";
+ print " lsof $chroot_path\n";
+ print "\n";
+ print "Delete the chroot, for example by running:\n";
+ print "\n";
+ print " rm --recursive --one-file-system $chroot_path\n";
+ print "\n";
+ if (-e "$Sbuild::Sysconfig::paths{'SBUILD_SYSCONF_DIR'}/chroot/$chroot") {
+ print "Delete the chroot link, for example by running:\n";
+ print "\n";
+ print " rm $Sbuild::Sysconfig::paths{'SBUILD_SYSCONF_DIR'}/chroot/$chroot\n";
+ print "\n";
+ }
+} else {
+ print "Delete the tarball, for example by running:\n";
+ print "\n";
+ print " rm $chroot_path\n";
+ print "\n";
+}
+print "Finally, delete the schroot configuration file, for example by running:\n";
+print "\n";
+print " rm $config_path\n";
+print "\n";
diff --git a/bin/sbuild-distupgrade b/bin/sbuild-distupgrade
new file mode 100755
index 0000000..4eef909
--- /dev/null
+++ b/bin/sbuild-distupgrade
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+print "$0 is deprecated. Use sbuild-update --dist-upgrade directly instead.\n";
+exec("sbuild-update", "--dist-upgrade", @ARGV) or
+ die "Can't run sbuild-update: $!";
diff --git a/bin/sbuild-hold b/bin/sbuild-hold
new file mode 100755
index 0000000..1d14a04
--- /dev/null
+++ b/bin/sbuild-hold
@@ -0,0 +1,58 @@
+#!/usr/bin/perl -w
+# changes the dpkg status of a package in a chroot to "hold"
+#
+# Copyright © 2006,2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+use Sbuild::Utility qw(setup cleanup shutdown);
+use Sbuild::ChrootSetup qw(hold_packages list_packages);
+use Sbuild::Conf qw();
+use Sbuild::OptionsBase;
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "sbuild-hold", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+usage_error("sbuild-hold", "Incorrect number of options") if (@ARGV < 2);
+
+my $chroot = Sbuild::Utility::get_dist($ARGV[0]);
+
+my $session = setup('source', $ARGV[0], $conf) or die "Chroot setup failed for $chroot chroot";
+
+print STDOUT "Holding packages in $chroot chroot:";
+shift @ARGV;
+foreach (@ARGV) {
+ print STDOUT " $_";
+}
+print STDOUT ".\n\n";
+
+my $status = hold_packages($session, $conf, @ARGV);
+$status >>= 8;
+
+list_packages($session, $conf, @ARGV);
+
+cleanup($conf);
+
+exit $status;
diff --git a/bin/sbuild-qemu b/bin/sbuild-qemu
new file mode 100755
index 0000000..52ab6ff
--- /dev/null
+++ b/bin/sbuild-qemu
@@ -0,0 +1,190 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright © 2020-2024 Christian Kastner <ckk@debian.org>
+# 2024 Johannes Schauer Marin Rodrigues <josch@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+
+import argparse
+import os
+import subprocess
+import sys
+
+
+DEB_ARCH_TO_QEMU = {
+ 'amd64': 'x86_64',
+ 'arm64': 'aarch64',
+ 'armhf': 'arm',
+ 'i386': 'i386',
+ 'ppc64el': 'ppc64le',
+}
+
+IMAGEDIR = os.environ.get(
+ 'IMAGEDIR',
+ os.path.join(os.path.expanduser('~'), '.cache', 'sbuild'),
+)
+
+DEFAULT_ARCH = subprocess.check_output(
+ ['dpkg', '--print-architecture'],
+ text=True,
+).strip()
+
+
+def main():
+ # init options
+ parser = argparse.ArgumentParser(
+ description="Build Debian packages with sbuild(1) using QEMU images",
+ epilog="All options other than the ones described below are passed on "
+ "through to sbuild(1), though the options --dist, --arch, and "
+ "--build are peeked at when looking for images. The image will "
+ "be started in snapshot mode, so the image is never modified. "
+ "Multiple processes can use the same image concurrently. The "
+ "architectures currently supported by sbuild-qemu are: "
+ f"{', '.join(DEB_ARCH_TO_QEMU.keys())}.",
+ )
+ parser.add_argument(
+ '--image',
+ action='store',
+ help="QEMU image file to use for building. If not specified, "
+ "sbuild-qemu will look for an image with the name "
+ "DIST-autopkgtest-ARCH.img, where DIST is taken from --dist "
+ "if present, and ARCH is taken from --arch or --build if "
+ "present. Otherwise, DIST defaults to 'unstable', and ARCH to "
+ "the host architecture. sbuild-qemu will first look in the "
+ "current directory for such an image, and then in the directory "
+ "$IMAGEDIR. A suitable image can be created with "
+ "qemu-sbuild-create(1).",
+ )
+ parser.add_argument(
+ '--ram-size',
+ metavar='MiB',
+ action='store',
+ default=2048,
+ help=f"VM memory size in MB. Default: 2048",
+ )
+ parser.add_argument(
+ '--cpus',
+ metavar='CPUs',
+ action='store',
+ default=2,
+ help="VM CPU count. Default: 2",
+ )
+ parser.add_argument(
+ '--overlay-dir',
+ action='store',
+ default='/tmp',
+ help="Directory for the temporary image overlay instead of "
+ "autopkgtest's default of /tmp (or $TMPDIR).",
+ )
+ parser.add_argument(
+ '--noexec',
+ action='store_true',
+ help="Don't actually do anything. Just print the sbuild(1) command "
+ "string that would be executed, and then exit.",
+ )
+ parser.add_argument(
+ '--autopkgtest-debug',
+ action='store_true',
+ help="Enable debug output for the autopkgtest-virt-qemu(1) driver.",
+ )
+ parser.add_argument(
+ '--boot',
+ choices=['auto', 'bios', 'efi', 'ieee1275', 'none'],
+ default='auto',
+ help="How to boot the image. Default is BIOS on amd64 and i386, EFI "
+ "on arm64 and armhf, and IEEE1275 on ppc64el.",
+ )
+ parsed_args, unparsed_args = parser.parse_known_args()
+
+ # These aren't options for us specifically, but we use them for guessing
+ # image locations
+ peeker = argparse.ArgumentParser()
+ peeker.add_argument(
+ '--dist',
+ action='store',
+ default='unstable',
+ )
+ peeker.add_argument(
+ '--arch',
+ action='store',
+ default=DEFAULT_ARCH,
+ )
+ peeker.add_argument(
+ '--build',
+ action='store',
+ )
+ peeked_args, _ = peeker.parse_known_args(unparsed_args)
+
+ build_arch = peeked_args.build or peeked_args.arch
+ try:
+ qemu_arch = DEB_ARCH_TO_QEMU[build_arch]
+ except KeyError:
+ print(f"Unsupported architecture: {build_arch}", file=sys.stderr)
+ print("Supported architectures are: ", file=sys.stderr, end="")
+ print(f"{', '.join(DEB_ARCH_TO_QEMU.keys())}", file=sys.stderr)
+ sys.exit(1)
+
+ if parsed_args.image:
+ if os.path.exists(os.path.abspath(parsed_args.image)):
+ image = parsed_args.image
+ else:
+ image = os.path.join(IMAGEDIR, parsed_args.image)
+ else:
+ guessed_name = f'{peeked_args.dist}-autopkgtest-{build_arch}.img'
+ if os.path.exists(os.path.abspath(guessed_name)):
+ images = os.path.abspath(guessed_name)
+ else:
+ image = os.path.join(
+ IMAGEDIR,
+ f'{peeked_args.dist}-autopkgtest-{build_arch}.img',
+ )
+
+ if not os.path.exists(image):
+ print(f"File {image} does not exist.", file=sys.stderr)
+ sys.exit(1)
+
+ args = [
+ 'sbuild',
+ '--dist', peeked_args.dist,
+ '--purge-build=never',
+ '--purge-deps=never',
+ '--chroot-mode=autopkgtest',
+ '--autopkgtest-virt-server=qemu',
+ '--autopkgtest-virt-server-opt', f'--overlay-dir={parsed_args.overlay_dir}',
+ '--autopkgtest-virt-server-opt', f'--qemu-architecture={qemu_arch}',
+ '--autopkgtest-virt-server-opt', f'--ram-size={parsed_args.ram_size}',
+ '--autopkgtest-virt-server-opt', f'--cpus={parsed_args.cpus}',
+ '--autopkgtest-virt-server-opt', f'--boot={parsed_args.boot}',
+ '--autopkgtest-virt-server-opt', image,
+ # Worarkound -- dose can hang stuff in a qemu VM
+ '--bd-uninstallable-explainer', 'apt',
+ ]
+ if parsed_args.autopkgtest_debug:
+ args += ['--autopkgtest-virt-server-opt', '--debug']
+
+ # Pass on the remaining (before peeking) arguments to sbuild
+ args += unparsed_args
+
+ print(' '.join(str(a) for a in args))
+ if not parsed_args.noexec:
+ os.execvp(args[0], args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/sbuild-qemu-boot b/bin/sbuild-qemu-boot
new file mode 100755
index 0000000..ae2ef1d
--- /dev/null
+++ b/bin/sbuild-qemu-boot
@@ -0,0 +1,291 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright © 2020-2024 Christian Kastner <ckk@debian.org>
+# 2021 Simon McVittie <smcv@debian.org>
+# 2024 Johannes Schauer Marin Rodrigues <josch@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+
+# Note that there is significant overlap between this program and
+# sbuild-qemu-update. Both are in their developmental stages and I'd prefer to
+# wait and see where this goes before refactoring them. --ckk
+
+
+import argparse
+import datetime
+import os
+import subprocess
+import sys
+
+
+SUPPORTED_ARCHS = [
+ 'amd64',
+ 'arm64',
+ 'armhf',
+ 'i386',
+ 'ppc64el',
+]
+
+IMAGEDIR = os.environ.get(
+ 'IMAGEDIR',
+ os.path.join(os.path.expanduser('~'), '.cache', 'sbuild'),
+)
+
+
+def make_snapshot(image):
+ iso_stamp = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')
+ run = subprocess.run(
+ ['qemu-img', 'snapshot', '-l', image],
+ capture_output=True
+ )
+ tags = [t.split()[1].decode('utf-8') for t in run.stdout.splitlines()[2:]]
+
+ if iso_stamp in tags:
+ print(
+ f"Error: snapshot for {iso_stamp} already exists.",
+ file=sys.stderr
+ )
+ return False
+
+ run = subprocess.run(['qemu-img', 'snapshot', '-c', iso_stamp, image])
+ return True if run.returncode == 0 else False
+
+
+def get_qemu_base_args(image, guest_arch=None, boot="auto"):
+ host_arch = subprocess.check_output(
+ ['dpkg', '--print-architecture'],
+ text=True,
+ ).strip()
+
+ if not guest_arch:
+ # This assumes that images are named foo-bar-ARCH.img
+ root, _ = os.path.splitext(os.path.basename(image))
+ components = root.split('-')
+ for c in reversed(components):
+ if c in SUPPORTED_ARCHS:
+ guest_arch = c
+ break
+ if not guest_arch:
+ print(
+ f"Could not guess guest architecture, please use --arch",
+ file=sys.stderr,
+ )
+ return
+ else:
+ if not guest_arch in SUPPORTED_ARCHS:
+ print(f"Unsupported architecture: {guest_arch}", file=sys.stderr)
+ print("Supported architectures are: ", file=sys.stderr, end="")
+ print(f"{', '.join(SUPPORTED_ARCHS)}", file=sys.stderr)
+ return
+
+ if guest_arch == 'amd64' :
+ argv = ['qemu-system-x86_64']
+ if host_arch == 'amd64':
+ argv.append('-enable-kvm')
+ elif guest_arch == 'i386':
+ argv = ['qemu-system-i386', '-machine', 'q35']
+ if host_arch in ['amd64', 'i386']:
+ argv.append('-enable-kvm')
+ elif guest_arch == 'ppc64el':
+ argv = ['qemu-system-ppc64le']
+ if host_arch == 'ppc64el':
+ argv.append('-enable-kvm')
+ elif guest_arch == 'arm64':
+ argv = [
+ 'qemu-system-aarch64',
+ '-machine', 'virt',
+ '-drive', 'if=pflash,format=raw,unit=0,read-only=on,'
+ 'file=/usr/share/AAVMF/AAVMF_CODE.fd',
+ ]
+ if host_arch == 'arm64':
+ argv.extend(['-cpu', 'host', '-enable-kvm'])
+ else:
+ argv.extend(['-cpu', 'cortex-a53'])
+ elif guest_arch == 'armhf':
+ if host_arch == 'arm64':
+ argv = [
+ 'qemu-system-aarch64',
+ '-cpu', 'host,aarch64=off',
+ '-enable-kvm'
+ ]
+ else:
+ argv = ['qemu-system-arm']
+ argv.extend([
+ '-machine', 'virt',
+ '-drive', 'if=pflash,format=raw,unit=0,read-only=on,'
+ 'file=/usr/share/AAVMF/AAVMF32_CODE.fd',
+ ])
+
+ if boot == "auto":
+ match guest_arch:
+ case 'amd64'|'i386':
+ boot = "bios"
+ case 'arm64'|'armhf':
+ boot = "efi"
+ case 'ppc64el':
+ boot = "ieee1275"
+
+ eficode = None
+ match boot:
+ case "bios"|"none":
+ pass
+ case "efi":
+ match guest_arch:
+ case 'amd64':
+ eficode = "/usr/share/OVMF/OVMF_CODE.fd"
+ case 'i386':
+ eficode = "/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd"
+ case 'arm64':
+ eficode = "/usr/share/AAVMF/AAVMF_CODE.fd"
+ case 'armhf':
+ eficode = "/usr/share/AAVMF/AAVMF32_CODE.fd"
+ case 'ppc64el':
+ print("efi not supported on ppc64el")
+ if eficode:
+ argv.extend(["-drive", f"if=pflash,format=raw,unit=0,read-only=on,file={eficode}"])
+
+ return argv
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Boot a VM using a QEMU image.',
+ )
+ parser.add_argument('--read-write',
+ action='store_true',
+ help="Write changes back to the image, instead of using the image "
+ "read-only.",
+ )
+ parser.add_argument(
+ '--snapshot',
+ action='store_true',
+ help="Create a snapshot of the image before changing it. Useful for "
+ "reproducibility purposes. Ignored if the image is not booted in "
+ "read-write mode, which is the default.",
+ )
+ parser.add_argument(
+ '--shared-dir',
+ help="Share this directory on the host with the guest. This will only "
+ "work when the image was created with sbuild-qemu-create(1).",
+ )
+ parser.add_argument(
+ '--arch',
+ help="Architecture to use (instead of attempting to auto-guess based "
+ "on the image name).",
+ )
+ parser.add_argument(
+ '--ram-size',
+ metavar='MiB',
+ action='store',
+ default=2048,
+ help=f"VM memory size in MB. Default: 2048",
+ )
+ parser.add_argument(
+ '--cpus',
+ metavar='CPUs',
+ action='store',
+ default=2,
+ help="VM CPU count. Default: 2",
+ )
+ parser.add_argument(
+ '--ssh-port',
+ metavar='PORT',
+ action='store',
+ help="Forward local port PORT to port 22 within the guest. Package "
+ "'openssh-server' must be installed within the guest for this "
+ "to be useful.",
+ )
+ parser.add_argument(
+ '--noexec',
+ action='store_true',
+ help="Don't actually do anything. Just print the command string that "
+ "would be executed, and then exit.",
+ )
+ parser.add_argument(
+ '--boot',
+ choices=['auto', 'bios', 'efi', 'ieee1275', 'none'],
+ default='auto',
+ help="How to boot the image. Default is BIOS on amd64 and i386, EFI "
+ "on arm64 and armhf, and IEEE1275 on ppc64el.",
+ )
+ parser.add_argument(
+ 'image',
+ help="Image. Will first be interpreted as a path. If no suitable "
+ "image exists at that location, then $IMAGEDIR\<image> is tried.",
+ )
+ parsed_args = parser.parse_args()
+
+ if os.path.exists(parsed_args.image):
+ image = parsed_args.image
+ elif os.path.exists(os.path.join(IMAGEDIR, parsed_args.image)):
+ image = os.path.join(IMAGEDIR, parsed_args.image)
+ else:
+ print("Image does not exist", file=sys.stderr)
+ sys.exit(1)
+
+ nic = 'user,model=virtio'
+ if parsed_args.ssh_port:
+ nic += f',hostfwd=tcp:127.0.0.1:{parsed_args.ssh_port}-:22'
+
+ args = get_qemu_base_args(parsed_args.image, parsed_args.arch, parsed_args.boot)
+ if not args:
+ sys.exit(1)
+
+ args.extend([
+ '-object', 'rng-random,filename=/dev/urandom,id=rng0',
+ '-device', 'virtio-rng-pci,rng=rng0,id=rng-device0',
+ '-device', 'virtio-serial',
+ '-nic', nic,
+ '-m', str(parsed_args.ram_size),
+ '-smp', str(parsed_args.cpus),
+ '-nographic',
+ ])
+
+ if parsed_args.shared_dir:
+ args.extend([
+ '-virtfs', f'local,path={parsed_args.shared_dir},id=sbuild-qemu,'
+ 'mount_tag=sbuild-qemu,security_model=none',
+ ])
+
+ # Pass on host terminal rows/columns to guest
+ # FIXME: qemu-system-pp64le doesn't support fw_cfg?
+ if args[0] not in ['qemu-system-ppc64le']:
+ termsize = os.get_terminal_size()
+ args.extend([
+ '-fw_cfg', f'name=opt/sbuild-qemu/tty-rows,string={termsize.lines}',
+ '-fw_cfg', f'name=opt/sbuild-qemu/tty-cols,string={termsize.columns}',
+ ])
+
+ args.append(image)
+
+ print(' '.join(str(a) for a in args))
+ if parsed_args.noexec:
+ return
+
+ if parsed_args.read_write:
+ if parsed_args.snapshot and not make_snapshot(image):
+ return
+ else:
+ args.append('-snapshot')
+
+ os.execvp(args[0], args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/sbuild-qemu-create b/bin/sbuild-qemu-create
new file mode 100755
index 0000000..ca59404
--- /dev/null
+++ b/bin/sbuild-qemu-create
@@ -0,0 +1,234 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright © 2020-2024 Christian Kastner <ckk@debian.org>
+# 2024 Johannes Schauer Marin Rodrigues <josch@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+
+import argparse
+import os
+import subprocess
+import sys
+import textwrap
+
+
+SUPPORTED_ARCHS = [
+ 'amd64',
+ 'arm64',
+ 'armhf',
+ 'i386',
+ 'ppc64el',
+]
+
+DEFAULT_ARCH = subprocess.check_output(
+ ['dpkg', '--print-architecture'],
+ text=True,
+).strip()
+
+
+def gen_sourceslist(mirror, dist, components, with_bpo=False):
+ """Generate a sources.list file for the VM.
+
+ If distribution ends with '-backports', then its base distribution will
+ automatically be added.
+
+ If distribution is 'experimental', then the 'unstable' distribution will
+ automatically be added.
+ """
+ sl = textwrap.dedent(
+ f"""\
+ deb {mirror} {dist} {' '.join(components)}
+ deb-src {mirror} {dist} {' '.join(components)}
+ """)
+ if dist == 'experimental':
+ sl += textwrap.dedent(
+ f"""
+ deb {mirror} unstable {' '.join(components)}
+ deb-src {mirror} unstable {' '.join(components)}
+ """)
+ elif dist.endswith('-backports'):
+ sl += textwrap.dedent(
+ f"""
+ deb {mirror} {dist[:-10]} {' '.join(components)}
+ deb-src {mirror} {dist[:-10]} {' '.join(components)}
+ """)
+ return sl
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="Builds images for use with qemu-sbuild and autopkgtest.",
+ epilog="Note that qemu-sbuild-create is just a simple wrapper around "
+ "autopkgtest-build-qemu(1) that automates a few additional "
+ "steps commonly performed with package-building images.",
+ )
+ parser.add_argument(
+ '--arch',
+ action='store',
+ default=DEFAULT_ARCH,
+ help="Architecture to use. Default is the host architecture. "
+ "Currently supported architectures are: "
+ f"{', '.join(SUPPORTED_ARCHS)}.",
+ )
+ parser.add_argument(
+ '--install-packages',
+ action='store',
+ help="Comma-separated list of additional packages to install in the "
+ "image using 'apt-get install' from within the image.",
+ )
+ parser.add_argument(
+ '--extra-deb',
+ action='append',
+ help="Package file (.deb) from the local filesystem to install. Can "
+ "be specified more than once.",
+ )
+ parser.add_argument(
+ '--components',
+ action='store',
+ default='main',
+ help="Comma-separated list of components to use with sources.list "
+ "entries. Default: main.",
+ )
+ # Not yet merged into autopkgtest, see #973457
+ # parser.add_argument('--variant', action='store')
+ parser.add_argument(
+ '--skel',
+ type=str,
+ action='store',
+ help="Skeleton directory to use for /root.",
+ )
+ parser.add_argument(
+ '--authorized-keys',
+ metavar='FILE',
+ action='store',
+ help="Install this file as /root/.ssh/authorized_keys within the "
+ "guest. This will automatically install the 'openssh-server' "
+ "package. This supersedes any copying of this file by the "
+ "--skel option.",
+ )
+ parser.add_argument(
+ '--size',
+ type=str,
+ action='store',
+ default='10G',
+ help="Image size to use. Note that the images are in qcow2 format, so "
+ "they won't consume that space right away. Default: 10G.",
+ )
+ parser.add_argument(
+ '-o', '--out-file',
+ action='store',
+ help="Output filename. If not supplied, then "
+ "DIST-autopkgtest-ARCH.img will be used.",
+ )
+ parser.add_argument(
+ '--noexec',
+ action='store_true',
+ help="Don't actually do anything. Just print the autopkgtest-build-"
+ "qemu(1) command string that would be executed, and then exit.",
+ )
+ parser.add_argument(
+ '--boot',
+ choices=['auto', 'bios', 'efi', 'ieee1275', 'none'],
+ default='auto',
+ help="How the image should boot. Default is BIOS on amd64 and i386, "
+ "EFI on arm64 and armhf, and IEEE1275 on ppc64el.",
+ )
+ parser.add_argument(
+ 'distribution',
+ action='store',
+ help="The distribution to debootstrap.",
+ )
+ parser.add_argument(
+ 'mirror',
+ action='store',
+ help="The mirror to use for the installation. Note that the mirror "
+ "will also be used for the sources.list file in the VM.",
+ )
+ parsed = parser.parse_args()
+
+ # Internal args
+ if parsed.arch not in SUPPORTED_ARCHS:
+ print(
+ f"Unsupported architecture: {parsed.arch}",
+ file=sys.stderr,
+ )
+ sys.exit(1)
+ if parsed.out_file:
+ out_file = parsed.out_file
+ else:
+ out_file = f"{parsed.distribution}-autopkgtest-{parsed.arch}.img"
+ components = parsed.components.split(',')
+
+ # We can only pass arguments to the other tools via the environment
+ #
+ # Args consumed by the modscript
+ if parsed.skel:
+ os.environ['SQC_SKEL'] = parsed.skel
+ print('export SQC_SKEL=' + os.environ['SQC_SKEL'])
+ if parsed.authorized_keys:
+ os.environ['SQC_AUTH_KEYS'] = parsed.authorized_keys
+ print('export SQC_AUTH_KEYS=' + os.environ['SQC_AUTH_KEYS'])
+ if parsed.extra_deb:
+ extra_debs = ' '.join(parsed.extra_deb)
+ os.environ['SQC_EXTRA_DEBS'] = extra_debs
+ print('export SQC_EXTRA_DEBS=' + extra_debs)
+ if parsed.install_packages:
+ install_packages = parsed.install_packages.replace(',', ' ')
+ os.environ['SQC_INSTALL_PACKAGES'] = install_packages
+ print('export SQC_INSTALL_PACKAGES=' + install_packages)
+ # Args consumed by autopkgtest-build-qemu
+ os.environ['AUTOPKGTEST_APT_SOURCES'] = gen_sourceslist(
+ parsed.mirror,
+ parsed.distribution,
+ components,
+ )
+ print('sources.list (via export AUTOPKGTEST_APT_SOURCES)\n------------')
+ print(os.environ['AUTOPKGTEST_APT_SOURCES'])
+
+ #if parsed.variant:
+ # args += ['--variant', parsed.variant]
+ dist = parsed.distribution
+ if dist.endswith('-backports'):
+ dist = dist[:-len('-backports')]
+ elif dist == 'experimental':
+ dist = 'unstable'
+
+ args = [
+ 'autopkgtest-build-qemu',
+ '--architecture', parsed.arch,
+ '--mirror', parsed.mirror,
+ '--size', parsed.size,
+ '--script', '/usr/share/sbuild/sbuild-qemu-create-modscript',
+ '--boot', parsed.boot,
+ dist,
+ out_file,
+ ]
+
+ if os.getuid() != 0:
+ print('Must be root to use this.', file=sys.stderr)
+ sys.exit(1)
+ os.umask(22)
+
+ print(' '.join(str(a) for a in args))
+ if not parsed.noexec:
+ os.execvp(args[0], args)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/sbuild-qemu-create-modscript b/bin/sbuild-qemu-create-modscript
new file mode 100755
index 0000000..0f139b5
--- /dev/null
+++ b/bin/sbuild-qemu-create-modscript
@@ -0,0 +1,137 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright © 2020 Christian Kastner <ckk@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+
+set -e
+umask 0022
+
+
+VMROOT="$1"
+if [ -z "$VMROOT" ]
+then
+ echo "$0 expects the mounted root of the VM as first argument." >&2
+ exit 1
+elif ! mountpoint -q "$VMROOT"
+then
+ echo "$VMROOT is not a mountpoint." >&2
+ exit 1
+fi
+
+
+echo "### Customizing base image ###"
+
+if [ -n "$SQC_SKEL" ]
+then
+ echo "Copying contents of $SQC_SKEL"
+ if [ ! -d "$SQC_SKEL" ]
+ then
+ echo "$SQC_SKEL is not a directory." >&2
+ exit 1
+ fi
+ cp -pr "$SQC_SKEL/." "$VMROOT/root"
+fi
+
+if [ -n "$SQC_AUTH_KEYS" ]
+then
+ echo "Copying $SQC_AUTH_KEYS to /root/.ssh/"
+ if [ ! -f "$SQC_AUTH_KEYS" ]
+ then
+ echo "$SQC_AUTH_KEYS is not a regular file." >&2
+ exit 1
+ fi
+
+ TARGET_KEYS="$VMROOT/root/.ssh/authorized_keys"
+ if [ ! -d "$VMROOT/root/.ssh" ]
+ then
+ mkdir --mode=0700 "$VMROOT/root/.ssh"
+ fi
+ cp "$SQC_AUTH_KEYS" "$VMROOT/root/.ssh/authorized_keys"
+ chroot "$VMROOT" chmod 0600 /root/.ssh/authorized_keys
+ chroot "$VMROOT" chown root:root /root/.ssh/authorized_keys
+ chroot "$VMROOT" apt-get install --quiet --assume-yes openssh-server
+fi
+
+if [ -n "$SQC_INSTALL_PACKAGES" ]
+then
+ echo "Installing additional packages"
+ chroot "$VMROOT" apt-get install --quiet --assume-yes $SQC_INSTALL_PACKAGES
+fi
+
+if [ -n "$SQC_EXTRA_DEBS" ]
+then
+ echo "Installing extra .debs"
+ VMTMP=`mktemp -d -p "$VMROOT"`
+ cp -t "$VMTMP" $SQC_EXTRA_DEBS
+ chroot "$VMROOT" dpkg --recursive -i `basename "$VMTMP"`
+ chroot "$VMROOT" apt-get update
+ rm -rf "$VMTMP"
+fi
+
+# Mount point for a shared folder, if the VM is launched with one
+echo "Adding 9p to initramfs"
+echo -e "9p\n9pnet\n9pnet_virtio" >> "$VMROOT/etc/initramfs-tools/modules"
+chroot "$VMROOT" update-initramfs -u
+echo "Adding shared folder to fstab"
+mkdir -m 755 "$VMROOT/shared"
+echo "sbuild-qemu /shared 9p trans=virtio,version=9p2000.L,auto,nofail 0 0" >> "$VMROOT/etc/fstab"
+
+echo "Updating GRUB menu"
+echo "GRUB_TIMEOUT=1" >> "$VMROOT/etc/default/grub"
+chroot "$VMROOT" update-grub
+
+# Enable automatically setting terminal rows/columns if the host passes us the
+# params using -fw_cfg
+echo "Creating script in /etc/profile.d/ to set terminal geometry to host"
+cat > "$VMROOT/etc/profile.d/sbuild-qemu-terminal-settings.sh" <<"EOF"
+#!/bin/sh
+# Set VM tty rows/columns to host rows/columns
+#
+# This only works if the guest kernel was compiled with CONFIG_FW_CFG_FSYS, and
+# the host rows/columns were passed on through QEMU using -fw_cfg. Regular
+# users will also need permission to read this file (see the udev rule).
+
+ROWSFILE="/sys/firmware/qemu_fw_cfg/by_name/opt/sbuild-qemu/tty-rows/raw"
+COLSFILE="/sys/firmware/qemu_fw_cfg/by_name/opt/sbuild-qemu/tty-cols/raw"
+
+DEB_HOST_ARCH="`dpkg-architecture -qDEB_HOST_ARCH`"
+if [ "$DEB_HOST_ARCH" = "armhf" ] || [ "$DEB_HOST_ARCH" = "arm64" ]
+then
+ TTY=/dev/ttyAMA0
+else
+ TTY=/dev/ttyS0
+fi
+
+if [ -f "$ROWSFILE" ]
+then
+ stty -F "$TTY" rows `cat "$ROWSFILE"`
+fi
+
+if [ -f "$COLSFILE" ]
+then
+ stty -F "$TTY" cols `cat "$COLSFILE"`
+fi
+EOF
+
+# Makes the image significantly smaller
+chroot "$VMROOT" apt-get --option Dir::Etc::SourceList=/dev/null --option Dir::Etc::SourceParts=/dev/null update
+chroot "$VMROOT" apt-get clean
+
+echo "### Customization of base image complete. ###"
diff --git a/bin/sbuild-qemu-update b/bin/sbuild-qemu-update
new file mode 100755
index 0000000..af5c31c
--- /dev/null
+++ b/bin/sbuild-qemu-update
@@ -0,0 +1,282 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright © 2020-2024 Christian Kastner <ckk@debian.org>
+# 2021 Simon McVittie <smcv@debian.org>
+# 2024 Johannes Schauer Marin Rodrigues <josch@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+
+# Note that there is significant overlap between this program and
+# sbuild-qemu-boot. Both are in their developmental stages and I'd prefer to
+# wait and see where this goes before refactoring them. --ckk
+
+
+import argparse
+import datetime
+import os
+import subprocess
+import sys
+
+import pexpect
+
+
+SUPPORTED_ARCHS = [
+ 'amd64',
+ 'arm64',
+ 'armhf',
+ 'i386',
+ 'ppc64el',
+]
+
+IMAGEDIR = os.environ.get(
+ 'IMAGEDIR',
+ os.path.join(os.path.expanduser('~'), '.cache', 'sbuild'),
+)
+
+
+def make_snapshot(image):
+ iso_stamp = datetime.datetime.now().strftime('%Y-%m-%d_%H%M%S')
+ run = subprocess.run(
+ ['qemu-img', 'snapshot', '-l', image],
+ capture_output=True
+ )
+ tags = [t.split()[1].decode('utf-8') for t in run.stdout.splitlines()[2:]]
+
+ if iso_stamp in tags:
+ print(
+ f"Error: snapshot for {iso_stamp} already exists.",
+ file=sys.stderr
+ )
+ return False
+
+ run = subprocess.run(['qemu-img', 'snapshot', '-c', iso_stamp, image])
+ return True if run.returncode == 0 else False
+
+
+def get_qemu_base_args(image, guest_arch=None, boot="auto"):
+ host_arch = subprocess.check_output(
+ ['dpkg', '--print-architecture'],
+ text=True,
+ ).strip()
+
+ if not guest_arch:
+ # This assumes that images are named foo-bar-ARCH.img
+ root, _ = os.path.splitext(os.path.basename(image))
+ components = root.split('-')
+ for c in reversed(components):
+ if c in SUPPORTED_ARCHS:
+ guest_arch = c
+ break
+ if not guest_arch:
+ print(
+ f"Could not guess guest architecture, please use --arch",
+ file=sys.stderr,
+ )
+ return
+ else:
+ if not guest_arch in SUPPORTED_ARCHS:
+ print(f"Unsupported architecture: {guest_arch}", file=sys.stderr)
+ print("Supported architectures are: ", file=sys.stderr, end="")
+ print(f"{', '.join(SUPPORTED_ARCHS)}", file=sys.stderr)
+ return
+
+ if guest_arch == 'amd64' :
+ argv = ['qemu-system-x86_64']
+ if host_arch == 'amd64':
+ argv.append('-enable-kvm')
+ elif guest_arch == 'i386':
+ argv = ['qemu-system-i386', '-machine', 'q35']
+ if host_arch in ['amd64', 'i386']:
+ argv.append('-enable-kvm')
+ elif guest_arch == 'ppc64el':
+ argv = ['qemu-system-ppc64le']
+ if host_arch == 'ppc64el':
+ argv.append('-enable-kvm')
+ elif guest_arch == 'arm64':
+ argv = [
+ 'qemu-system-aarch64',
+ '-machine', 'virt',
+ ]
+ if host_arch == 'arm64':
+ argv.extend(['-cpu', 'host', '-enable-kvm'])
+ else:
+ argv.extend(['-cpu', 'cortex-a53'])
+ elif guest_arch == 'armhf':
+ if host_arch == 'arm64':
+ argv = [
+ 'qemu-system-aarch64',
+ '-cpu', 'host,aarch64=off',
+ '-enable-kvm'
+ ]
+ else:
+ argv = ['qemu-system-arm']
+ argv.extend([
+ '-machine', 'virt',
+ ])
+
+ if boot == "auto":
+ match guest_arch:
+ case 'amd64'|'i386':
+ boot = "bios"
+ case 'arm64'|'armhf':
+ boot = "efi"
+ case 'ppc64el':
+ boot = "ieee1275"
+
+ eficode = None
+ match boot:
+ case "bios"|"none":
+ pass
+ case "efi":
+ match guest_arch:
+ case 'amd64':
+ eficode = "/usr/share/OVMF/OVMF_CODE.fd"
+ case 'i386':
+ eficode = "/usr/share/OVMF/OVMF32_CODE_4M.secboot.fd"
+ case 'arm64':
+ eficode = "/usr/share/AAVMF/AAVMF_CODE.fd"
+ case 'armhf':
+ eficode = "/usr/share/AAVMF/AAVMF32_CODE.fd"
+ case 'ppc64el':
+ print("efi not supported on ppc64el")
+ if eficode:
+ argv.extend(["-drive", f"if=pflash,format=raw,unit=0,read-only=on,file={eficode}"])
+
+
+ return argv
+
+
+def update_interaction(child, quiet):
+ child.expect('host login: ')
+ child.sendline('root')
+ if not quiet:
+ child.logfile = sys.stdout
+ child.expect('root@host:~# ')
+ child.sendline('DEBIAN_FRONTEND=noninteractive apt-get --quiet update')
+ child.expect('root@host:~# ')
+ child.sendline('DEBIAN_FRONTEND=noninteractive apt-get --quiet --assume-yes dist-upgrade')
+ child.expect('root@host:~# ')
+ child.sendline('DEBIAN_FRONTEND=noninteractive apt-get --quiet --assume-yes clean')
+ child.expect('root@host:~# ')
+ child.sendline('DEBIAN_FRONTEND=noninteractive apt-get --quiet --assume-yes autoremove')
+ child.expect('root@host:~# ')
+ child.sendline('sync')
+ child.expect('root@host:~# ')
+ # Don't recall what issue this solves, but it solves it
+ child.sendline('sleep 1')
+ child.expect('root@host:~# ')
+ child.sendline('shutdown -h now')
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='sbuild-update analog for QEMU images.',
+ )
+ parser.add_argument(
+ '--snapshot',
+ action='store_true',
+ help="Create a snapshot of the image before changing it. Useful for "
+ "reproducibility purposes.",
+ )
+ parser.add_argument(
+ '--arch',
+ help="Architecture to use (instead of attempting to auto-guess based "
+ "on the image name).",
+ )
+ parser.add_argument(
+ '--timeout',
+ type=int,
+ default=600,
+ metavar='SECONDS',
+ action='store',
+ help="Maximum time to wait for command to finish with expected "
+ "result. Mostly relevant for foreign architectures, where "
+ "`apt-get update` can take quite a while. Default: 600s."
+ )
+ parser.add_argument(
+ '--noexec',
+ action='store_true',
+ help="Don't actually do anything. Just print the command string that "
+ "would be executed, and then exit.",
+ )
+ parser.add_argument(
+ '--boot',
+ choices=['auto', 'bios', 'efi', 'ieee1275', 'none'],
+ default='auto',
+ help="How to boot the image. Default is BIOS on amd64 and i386, EFI "
+ "on arm64 and armhf, and IEEE1275 on ppc64el.",
+ )
+ parser.add_argument(
+ 'image',
+ help="Image. Will first be interpreted as a path. If no suitable "
+ "image exists at that location, then $IMAGEDIR\<image> is tried.",
+ )
+ dbglvlgroup = parser.add_mutually_exclusive_group()
+ dbglvlgroup.add_argument("-v", "--verbose", action="store_true")
+ dbglvlgroup.add_argument("-q", "--quiet", action="store_true")
+
+ parser.add_argument("extra_args", nargs='*')
+
+ parsed_args = parser.parse_args()
+
+ if os.path.exists(parsed_args.image):
+ image = parsed_args.image
+ elif os.path.exists(os.path.join(IMAGEDIR, parsed_args.image)):
+ image = os.path.join(IMAGEDIR, parsed_args.image)
+ else:
+ print("Image does not exist", file=sys.stderr)
+ sys.exit(1)
+
+ args = get_qemu_base_args(parsed_args.image, parsed_args.arch, parsed_args.boot)
+ if not args:
+ sys.exit(1)
+
+ args.extend([
+ '-object', 'rng-random,filename=/dev/urandom,id=rng0',
+ '-device', 'virtio-rng-pci,rng=rng0,id=rng-device0',
+ '-device', 'virtio-serial',
+ '-nic', 'user,model=virtio',
+ '-m', '1024',
+ '-smp', '1',
+ '-nographic',
+ image,
+ ])
+ if parsed_args.extra_args:
+ args.extend(parsed_args.extra_args)
+
+ print(' '.join(str(a) for a in args))
+ if parsed_args.noexec:
+ return
+ elif parsed_args.snapshot and not make_snapshot(image):
+ return
+
+ logfile = None
+ if parsed_args.verbose:
+ logfile = sys.stdout
+ child = pexpect.spawn(args[0], args[1:], logfile=logfile, encoding="utf-8")
+ child.timeout = parsed_args.timeout
+ try:
+ update_interaction(child, parsed_args.quiet)
+ except pexpect.TIMEOUT:
+ print("Update timed out. Consider using --timeout.", file=sys.stderr)
+ child.terminate()
+
+
+if __name__ == '__main__':
+ main()
diff --git a/bin/sbuild-shell b/bin/sbuild-shell
new file mode 100755
index 0000000..90083f2
--- /dev/null
+++ b/bin/sbuild-shell
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+#
+# Run a shell in a chroot.
+# Copyright © 2006 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+use Sbuild::Utility qw(setup cleanup);
+use Sbuild::ChrootSetup qw(shell);
+use Sbuild::Sysconfig;
+use Sbuild::Conf qw();
+use Sbuild::OptionsBase;
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "sbuild-shell", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+usage_error("sbuild-shell", "Incorrect number of options") if (@ARGV != 1);
+
+
+my $session = setup('source', $ARGV[0], $conf) or die "Chroot setup failed";
+
+my $status = shell($session, $conf);
+$status >>= 8;
+
+cleanup($conf);
+
+exit $status;
diff --git a/bin/sbuild-unhold b/bin/sbuild-unhold
new file mode 100755
index 0000000..12b1266
--- /dev/null
+++ b/bin/sbuild-unhold
@@ -0,0 +1,58 @@
+#!/usr/bin/perl -w
+# changes the dpkg status of a package in a chroot to "installed"
+#
+# Copyright © 2006,2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+use Sbuild::Utility qw(setup cleanup shutdown);
+use Sbuild::ChrootSetup qw(unhold_packages list_packages);
+use Sbuild::Conf qw();
+use Sbuild::OptionsBase;
+
+my $conf = Sbuild::Conf::new();
+exit 1 if !defined($conf);
+my $options = Sbuild::OptionsBase->new($conf, "sbuild-unhold", "1");
+exit 1 if !defined($options);
+check_group_membership();
+
+usage_error("sbuild-unhold", "Incorrect number of options") if (@ARGV < 2);
+
+my $chroot = Sbuild::Utility::get_dist($ARGV[0]);
+
+my $session = setup('source', $ARGV[0], $conf) or die "Chroot setup failed for $chroot chroot";
+
+print STDOUT "Unholding packages in $chroot chroot:";
+shift @ARGV;
+foreach (@ARGV) {
+ print STDOUT " $_";
+}
+print STDOUT ".\n\n";
+
+my $status = unhold_packages($session, $conf, @ARGV);
+$status >>= 8;
+
+list_packages($session, $conf, @ARGV);
+
+cleanup($conf);
+
+exit $status;
diff --git a/bin/sbuild-update b/bin/sbuild-update
new file mode 100755
index 0000000..396b526
--- /dev/null
+++ b/bin/sbuild-update
@@ -0,0 +1,228 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Sbuild::ChrootRoot;
+use Sbuild::Resolver qw(get_resolver);
+
+package Conf;
+
+sub setup {
+ my $conf = shift;
+
+ my %update_keys = (
+ 'COMPAT' => {
+ DEFAULT => 1
+ },
+ 'UPDATE' => {
+ DEFAULT => 0
+ },
+ 'UPGRADE' => {
+ DEFAULT => 0
+ },
+ 'DISTUPGRADE' => {
+ DEFAULT => 0
+ },
+ 'CLEAN' => {
+ DEFAULT => 0
+ },
+ 'AUTOCLEAN' => {
+ DEFAULT => 0
+ },
+ 'AUTOREMOVE' => {
+ DEFAULT => 0
+ },
+ 'CHROOT_MODE' => {
+ DEFAULT => 'schroot'
+ },
+ );
+
+ $conf->set_allowed_keys(\%update_keys);
+}
+
+package Options;
+
+use Sbuild::OptionsBase;
+use Sbuild::Conf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::OptionsBase);
+
+ @EXPORT = qw();
+}
+
+sub set_options {
+ my $self = shift;
+
+ $self->add_options(
+ "chroot-mode=s" => sub {
+ $self->set_conf('CHROOT_MODE', $_[1]);
+ },
+ "arch=s" => sub {
+ $self->set_conf('ARCH', $_[1]);
+ $self->set_conf('HOST_ARCH', $_[1]);
+ $self->set_conf('BUILD_ARCH', $_[1]);
+ },
+ "update|u" => sub {
+ $self->set_conf('UPDATE', 1);
+ $self->set_conf('COMPAT', 0);
+ },
+ "upgrade|g" => sub {
+ $self->set_conf('UPGRADE', 1);
+ $self->set_conf('COMPAT', 0);
+ },
+ "dist-upgrade|d" => sub {
+ $self->set_conf('DISTUPGRADE', 1);
+ $self->set_conf('COMPAT', 0);
+ },
+ "clean|c" => sub {
+ $self->set_conf('CLEAN', 1);
+ $self->set_conf('COMPAT', 0);
+ },
+ "autoclean|a" => sub {
+ $self->set_conf('AUTOCLEAN', 1);
+ $self->set_conf('COMPAT', 0);
+ },
+ "autoremove|r" => sub {
+ $self->set_conf('AUTOREMOVE', 1);
+ $self->set_conf('COMPAT', 0);
+ });
+}
+
+package main;
+
+use Getopt::Long;
+use Sbuild qw(help_text version_text usage_error check_group_membership);
+use Sbuild::Utility qw(setup cleanup);
+
+my $conf = Sbuild::Conf::new();
+Conf::setup($conf);
+exit 1 if !defined($conf);
+my $options = Options->new($conf, "sbuild-update", "1");
+exit 1 if !defined($options);
+check_group_membership() if $conf->get('CHROOT_MODE') eq 'schroot';
+
+if ($conf->get('COMPAT')) {
+ my $msg = "$0 will perform apt-get command 'update' now, however this ";
+ $msg .= "may change at a later revision.\n";
+ print "$msg";
+ $conf->set('UPDATE', 1);
+}
+
+if (@ARGV < 1) {
+ usage_error("sbuild-update", "No chroot was specified");
+}
+
+my $status = 0;
+
+my $host = Sbuild::ChrootRoot->new($conf);
+
+foreach (@ARGV) {
+ my $distribution = Sbuild::Utility::get_dist($_);
+
+ my $session = setup('source', $distribution, $conf) or die "Chroot setup failed";
+ if (!$host->begin_session()) {
+ die "Chroot setup failed";
+ }
+ my $resolver = get_resolver($conf, $session, $host);
+
+ if (!$session->lock_chroot('SBUILD_UPDATE', $$, $conf->get('USERNAME'))) {
+ goto cleanup_unlocked;
+ }
+
+ $resolver->setup();
+
+ if ($conf->get('UPDATE')) {
+ print "$distribution: Performing update.\n";
+ $status = $resolver->update($session, $conf);
+ $status >>= 8;
+ if ($status) {
+ print STDERR "Exiting from update with status $status.\n";
+ goto cleanup;
+ }
+ }
+
+ if ($conf->get('UPGRADE')) {
+ print "$distribution: Performing upgrade.\n";
+ my $status = $resolver->upgrade($session, $conf);
+ $status >>= 8;
+ if ($status) {
+ print STDERR "Exiting from upgrade with status $status.\n";
+ goto cleanup;
+ }
+ }
+
+ if ($conf->get('DISTUPGRADE')) {
+ print "$distribution: Performing dist-upgrade.\n";
+ my $status = $resolver->distupgrade($session, $conf);
+ $status >>= 8;
+ if ($status) {
+ print STDERR "Exiting from distupgrade with status $status.\n";
+ goto cleanup;
+ }
+ }
+
+ if ($conf->get('CLEAN')) {
+ print "$distribution: Performing clean.\n";
+ my $status = $resolver->clean($session, $conf);
+ $status >>= 8;
+ if ($status) {
+ print STDERR "Exiting from update with status $status.\n";
+ goto cleanup;
+ }
+ }
+
+ if ($conf->get('AUTOCLEAN')) {
+ print "$distribution: Performing autoclean.\n";
+ my $status = $resolver->autoclean($session, $conf);
+ $status >>= 8;
+ if ($status) {
+ print STDERR "Exiting from autoclean with status $status.\n";
+ goto cleanup;
+ }
+ }
+
+ if ($conf->get('AUTOREMOVE')) {
+ print "$distribution: Performing autoremove.\n";
+ my $status = $resolver->autoremove($session, $conf);
+ $status >>= 8;
+ if ($status) {
+ print STDERR "Exiting from autoremove with status $status.\n";
+ goto cleanup;
+ }
+ }
+
+cleanup:
+ $resolver->cleanup();
+ # Unlock chroot now it's cleaned up and ready for other users.
+ $session->unlock_chroot();
+
+cleanup_unlocked:
+ cleanup($conf);
+
+ last if $status;
+}
+
+exit($status ? 1 : 0);
diff --git a/bin/sbuild-upgrade b/bin/sbuild-upgrade
new file mode 100755
index 0000000..e338632
--- /dev/null
+++ b/bin/sbuild-upgrade
@@ -0,0 +1,26 @@
+#!/usr/bin/perl -w
+#
+# Copyright © 2006-2008 Roger Leigh <rleigh@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+print "$0 is deprecated. Use sbuild-update --upgrade directly instead.\n";
+exec("sbuild-update", "--upgrade", @ARGV) or
+ die "Can't run sbuild-update: $!";
diff --git a/bin/setup_system b/bin/setup_system
new file mode 100755
index 0000000..d99939e
--- /dev/null
+++ b/bin/setup_system
@@ -0,0 +1,53 @@
+#!/bin/sh
+#
+# Copyright © 2005-2006 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+
+# Needed sudoers entries:
+#buildd ALL=NOPASSWD: ALL
+#Defaults:buildd env_keep+="APT_CONFIG DEBIAN_FRONTEND"
+#
+# parts to be run as root.
+# ons of these, depending on whether you have a buildd group or not
+#sudo adduser --system --shell /bin/sh --uid 60000 --gecos 'Build Daemon' --ingroup buildd --disabled-password buildd
+sudo adduser --system --shell /bin/sh --uid 60000 --gecos 'Build Daemon' --group --disabled-password buildd
+sudo chown -R buildd:buildd /var/lib/wanna-build
+sudo chmod -R 2775 /var/lib/wanna-build
+# parts to be done as buildd.
+cd ~buildd
+zcat /usr/share/doc/buildd/examples/buildd.conf.gz > buildd.conf
+zcat /usr/share/doc/sbuild/examples/sbuildrc.gz > .sbuildrc
+mkdir -p .ssh build logs mqueue old-logs stats/graphs upload upload-security
+chmod o= .ssh upload-security old-logs mqueue logs build
+echo "|/usr/bin/buildd-mail-wrapper" > .forward
+ssh-keygen -b 2048 -t rsa -f .ssh/id_rsa -N ''
+echo I: setup .forward-porters with where you want buildd mail to go.
+echo I: chroot creation commands:
+echo buildd-make-chroot buildd sid build/chroot-unstable http://ftp.debian.org/debian
+echo buildd-make-chroot buildd sarge build/chroot-sarge http://ftp.debian.org/debian
+echo buildd-make-chroot buildd woody build/chroot-woody http://ftp.debian.org/debian
+echo buildd-make-chroot buildd etch build/chroot-etch http://ftp.debian.org/debian
+echo I: Link commands for the chroots:
+echo ln -s chroot-woody chroot-oldstable-security
+echo ln -s chroot-sarge chroot-stable
+echo ln -s chroot-sarge chroot-stable-security
+echo ln -s chroot-etch chroot-testing
+echo ln -s chroot-etch chroot-testing-security
+echo I: Done.
+exit 0
diff --git a/bin/wb-ssh-wrapper b/bin/wb-ssh-wrapper
new file mode 100755
index 0000000..85a6cb7
--- /dev/null
+++ b/bin/wb-ssh-wrapper
@@ -0,0 +1,38 @@
+#!/bin/bash
+#
+# A wrapper script to point ssh at in an authorized_keys file to only allow
+# access to wanna-build
+# Copyright © 2006 Ryan Murray <rmurray@debian.org>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see
+# <http://www.gnu.org/licenses/>.
+#
+#######################################################################
+
+bin=/usr/bin/wanna-build
+
+[ -n "$SSH_ORIGINAL_COMMAND" ] || exit 1
+
+set -- $SSH_ORIGINAL_COMMAND
+
+bn=$(basename "$1")
+if [ "$bn" != "wanna-build" ]; then
+ exit 1
+fi
+
+shift
+
+[ -f "$bin" -a -x "$bin" ] || exit 1
+
+exec $bin $@