summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitattributes8
-rw-r--r--.gitignore30
-rw-r--r--.mailmap6
-rw-r--r--AUTHORS43
-rw-r--r--COPYING340
-rw-r--r--ChangeLog.in1091
-rw-r--r--HACKING54
-rw-r--r--Makefile-buildd38
-rw-r--r--Makefile.am49
-rw-r--r--NEWS-buildd1415
-rw-r--r--README54
-rw-r--r--README.buildd-admin281
-rw-r--r--README.chroot-building93
-rw-r--r--TODO121
-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
-rw-r--r--configs/Makefile.am33
-rw-r--r--configs/README18
-rw-r--r--configs/ref-oldstable58
-rw-r--r--configs/ref-stable63
-rw-r--r--configs/ref-testing76
-rw-r--r--configs/ref-unstable80
-rw-r--r--configure.ac340
-rw-r--r--db/.gitignore1
-rw-r--r--db/Makefile.am50
-rw-r--r--db/archive-data.sql121
-rw-r--r--db/archive.sql266
-rw-r--r--db/build-data.sql41
-rw-r--r--db/build.sql245
-rw-r--r--db/db.sql.in32
-rw-r--r--db/functions.sql129
-rw-r--r--db/install.sql26
-rw-r--r--db/language.sql38
-rw-r--r--db/schema.sql27
-rw-r--r--etc/.gitignore1
-rw-r--r--etc/Makefile.am53
-rw-r--r--etc/example.sbuildrc118
-rwxr-xr-xetc/sbuild-debian-developer-setup-update-all106
-rw-r--r--etc/sbuild-debuild.conf16
-rw-r--r--lib/Buildd.pm192
-rw-r--r--lib/Buildd/Base.pm190
-rw-r--r--lib/Buildd/Client.pm135
-rw-r--r--lib/Buildd/ClientConf.pm177
-rw-r--r--lib/Buildd/Conf.pm628
-rw-r--r--lib/Buildd/Daemon.pm998
-rw-r--r--lib/Buildd/DistConf.pm159
-rw-r--r--lib/Buildd/Mail.pm1354
-rw-r--r--lib/Buildd/Makefile.am42
-rw-r--r--lib/Buildd/UploadQueueConf.pm96
-rw-r--r--lib/Buildd/Uploader.pm274
-rw-r--r--lib/Buildd/Watcher.pm528
-rw-r--r--lib/Makefile.am31
-rw-r--r--lib/Sbuild.pm454
-rw-r--r--lib/Sbuild/.gitignore1
-rw-r--r--lib/Sbuild/AptResolver.pm219
-rw-r--r--lib/Sbuild/AptitudeResolver.pm187
-rw-r--r--lib/Sbuild/AspcudResolver.pm165
-rw-r--r--lib/Sbuild/Base.pm165
-rw-r--r--lib/Sbuild/Build.pm3686
-rw-r--r--lib/Sbuild/Chroot.pm963
-rw-r--r--lib/Sbuild/ChrootAutopkgtest.pm297
-rw-r--r--lib/Sbuild/ChrootInfo.pm143
-rw-r--r--lib/Sbuild/ChrootInfoAutopkgtest.pm67
-rw-r--r--lib/Sbuild/ChrootInfoSchroot.pm186
-rw-r--r--lib/Sbuild/ChrootInfoSudo.pm120
-rw-r--r--lib/Sbuild/ChrootInfoUnshare.pm94
-rw-r--r--lib/Sbuild/ChrootPlain.pm150
-rw-r--r--lib/Sbuild/ChrootRoot.pm53
-rw-r--r--lib/Sbuild/ChrootSchroot.pm173
-rw-r--r--lib/Sbuild/ChrootSetup.pm252
-rw-r--r--lib/Sbuild/ChrootSudo.pm137
-rw-r--r--lib/Sbuild/ChrootUnshare.pm398
-rw-r--r--lib/Sbuild/Conf.pm1582
-rw-r--r--lib/Sbuild/ConfBase.pm548
-rw-r--r--lib/Sbuild/Exception.pm34
-rw-r--r--lib/Sbuild/LogBase.pm116
-rw-r--r--lib/Sbuild/Makefile.am62
-rw-r--r--lib/Sbuild/NullResolver.pm69
-rw-r--r--lib/Sbuild/Options.pm633
-rw-r--r--lib/Sbuild/OptionsBase.pm102
-rw-r--r--lib/Sbuild/Resolver.pm66
-rw-r--r--lib/Sbuild/ResolverBase.pm1685
-rw-r--r--lib/Sbuild/Sysconfig.pm.in84
-rw-r--r--lib/Sbuild/Utility.pm631
-rw-r--r--lib/Sbuild/XaptResolver.pm107
-rw-r--r--lisp/buildd-reply.el361
-rw-r--r--man/.gitignore7
-rw-r--r--man/Makefile.am102
-rw-r--r--man/buildd-mail.1.in88
-rw-r--r--man/buildd-uploader.1.in70
-rw-r--r--man/buildd-vlog.1.in72
-rw-r--r--man/buildd-watcher.1.in72
-rw-r--r--man/buildd.1.in75
-rw-r--r--man/buildd.conf.5.in75
-rw-r--r--man/defs.man.in19
-rw-r--r--man/sbuild-abort.1.in61
-rw-r--r--man/sbuild-adduser.8.in71
-rw-r--r--man/sbuild-apt.1.in84
-rw-r--r--man/sbuild-checkpackages.1.in81
-rw-r--r--man/sbuild-createchroot.8.in365
-rw-r--r--man/sbuild-debian-developer-setup.1.in78
-rw-r--r--man/sbuild-debuild.1.in196
-rw-r--r--man/sbuild-destroychroot.8.in57
-rw-r--r--man/sbuild-hold.1.in71
-rw-r--r--man/sbuild-qemu-boot.1.in98
-rw-r--r--man/sbuild-qemu-create.1.in161
-rw-r--r--man/sbuild-qemu-update.1.in77
-rw-r--r--man/sbuild-qemu.1.in117
-rw-r--r--man/sbuild-setup.7.in303
-rw-r--r--man/sbuild-shell.1.in63
-rw-r--r--man/sbuild-update.1.in134
-rw-r--r--man/sbuild.1.in1678
-rw-r--r--man/sbuild.conf.5.in74
-rw-r--r--scripts/.gitignore5
-rw-r--r--scripts/global.mk23
-rw-r--r--test/.gitignore10
-rw-r--r--test/Makefile.am49
-rw-r--r--test/logging10
-rwxr-xr-xtest/perl-syntax.in30
-rwxr-xr-xtest/sbuild-checkpackages.in28
-rw-r--r--test/sbuild-clean.in26
-rw-r--r--test/sbuild-distupgrade.in26
-rwxr-xr-xtest/sbuild-hold.in28
-rwxr-xr-xtest/sbuild-update.in28
-rwxr-xr-xtest/sbuild-upgrade.in26
-rwxr-xr-xtest/sbuild.in48
-rwxr-xr-xtest/wanna-build130
-rw-r--r--tools/Makefile.am28
-rwxr-xr-xtools/sbuild-dumpconfig191
163 files changed, 34371 insertions, 0 deletions
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..a68b868
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,8 @@
+.gitignore export-ignore
+.gitattributes export-ignore
+db/.gitignore export-ignore
+etc/.gitignore export-ignore
+lib/Sbuild/.gitignore export-ignore
+man/.gitignore export-ignore
+scripts/.gitignore export-ignore
+test/.gitignore export-ignore
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..612f56c
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+*~
+.*.swp
+debian/sbuild.substvars
+*.1
+*.5
+*.7
+*.8
+*-stamp
+Makefile
+Makefile.in
+config.h
+config.h.in
+config.log
+config.status
+stamp-h1
+aclocal.m4
+autom4te.cache
+configure
+m4
+INSTALL
+ChangeLog
+*.o
+.deps
+.libs
+*.tar.*
+\#*
+.#*
+etc/buildd.conf
+etc/sbuild.conf
+scripts/test-driver
diff --git a/.mailmap b/.mailmap
new file mode 100644
index 0000000..bd2b558
--- /dev/null
+++ b/.mailmap
@@ -0,0 +1,6 @@
+Johannes Schauer Marin Rodrigues <josch@debian.org>
+Johannes Schauer Marin Rodrigues <josch@debian.org> <josch@mister-muffin.de>
+Johannes Schauer Marin Rodrigues <josch@debian.org> <j.schauer@email.de>
+Christian Kastner <ckk@debian.org>
+Christian Kastner <ckk@debian.org> <ckk@kvr.at>
+Christian Kastner <ckk@debian.org> <debian@kvr.at>
diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..3196574
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,43 @@
+The list of contributors, in alphabetical order, is as follows. The
+principal areas that each contributed to are listed in addition.
+
+Ben Collins <bcollins@debian.org>
+ the "apt patch" for sbuild for finding source
+
+Enrico Zini <enrico@debian.org>
+ support for disabling APT package authentication
+
+Marc 'HE' Brockschmidt <he@debian.org>
+ buildd and sbuild maintenance
+
+Philipp Kern <pkern@debian.org>
+ buildd and sbuild maintenance
+
+Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+ wanna-build
+ sbuild
+
+Roger Leigh <rleigh@debian.org>
+ schroot support
+ autotools
+ maintenance
+
+Andres Mejia <mcitadel@gmail.com>
+ sbuild-createchroot improvements
+ sbuild-update and related script improvements
+ sbuild schroot namespace support
+ sbuild debuild-link functionality
+ sbuild source download improvements
+ architecture wildcard support
+ external command and lintian support
+
+Ryan Murray <rmurray@debian.org>
+ modifications for security building
+ maintenance
+
+Simon McVittie <smcv@debian.org>
+ --chroot option for sbuild
+
+Timothy G Abbott <tabbott@mit.edu>
+ support for running "apt-get update" in chroots prior to
+ fetching sources and building
diff --git a/COPYING b/COPYING
new file mode 100644
index 0000000..b7b5f53
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,340 @@
+ GNU GENERAL PUBLIC LICENSE
+ Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+ 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+ Preamble
+
+ The licenses for most software are designed to take away your
+freedom to share and change it. By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users. This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it. (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.) You can apply it to
+your programs, too.
+
+ When we speak of free software, we are referring to freedom, not
+price. Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+ To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+ For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have. You must make sure that they, too, receive or can get the
+source code. And you must show them these terms so they know their
+rights.
+
+ We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+ Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software. If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+ Finally, any free program is threatened constantly by software
+patents. We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary. To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+ The precise terms and conditions for copying, distribution and
+modification follow.
+
+ GNU GENERAL PUBLIC LICENSE
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+ 0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License. The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language. (Hereinafter, translation is included without limitation in
+the term "modification".) Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope. The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+ 1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+ 2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+ a) You must cause the modified files to carry prominent notices
+ stating that you changed the files and the date of any change.
+
+ b) You must cause any work that you distribute or publish, that in
+ whole or in part contains or is derived from the Program or any
+ part thereof, to be licensed as a whole at no charge to all third
+ parties under the terms of this License.
+
+ c) If the modified program normally reads commands interactively
+ when run, you must cause it, when started running for such
+ interactive use in the most ordinary way, to print or display an
+ announcement including an appropriate copyright notice and a
+ notice that there is no warranty (or else, saying that you provide
+ a warranty) and that users may redistribute the program under
+ these conditions, and telling the user how to view a copy of this
+ License. (Exception: if the Program itself is interactive but
+ does not normally print such an announcement, your work based on
+ the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole. If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works. But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+ 3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+ a) Accompany it with the complete corresponding machine-readable
+ source code, which must be distributed under the terms of Sections
+ 1 and 2 above on a medium customarily used for software interchange; or,
+
+ b) Accompany it with a written offer, valid for at least three
+ years, to give any third party, for a charge no more than your
+ cost of physically performing source distribution, a complete
+ machine-readable copy of the corresponding source code, to be
+ distributed under the terms of Sections 1 and 2 above on a medium
+ customarily used for software interchange; or,
+
+ c) Accompany it with the information you received as to the offer
+ to distribute corresponding source code. (This alternative is
+ allowed only for noncommercial distribution and only if you
+ received the program in object code or executable form with such
+ an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it. For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable. However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+ 4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License. Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+ 5. You are not required to accept this License, since you have not
+signed it. However, nothing else grants you permission to modify or
+distribute the Program or its derivative works. These actions are
+prohibited by law if you do not accept this License. Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+ 6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions. You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+ 7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License. If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all. For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices. Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+ 8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded. In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+ 9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time. Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number. If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation. If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+ 10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission. For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this. Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+ NO WARRANTY
+
+ 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+ 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+ END OF TERMS AND CONDITIONS
+
+ How to Apply These Terms to Your New Programs
+
+ If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+ To do so, attach the following notices to the program. It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+ <one line to give the program's name and a brief idea of what it does.>
+ Copyright (C) <year> <name of author>
+
+ 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, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+ Gnomovision version 69, Copyright (C) year name of author
+ Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+ This is free software, and you are welcome to redistribute it
+ under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License. Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary. Here is a sample; alter the names:
+
+ Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+ `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+ <signature of Ty Coon>, 1 April 1989
+ Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs. If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library. If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/ChangeLog.in b/ChangeLog.in
new file mode 100644
index 0000000..4d89ad9
--- /dev/null
+++ b/ChangeLog.in
@@ -0,0 +1,1091 @@
+ChangeLog -*- outline -*-
+---------
+
+Welcome to @PACKAGE@ @VERSION@. Please read these release notes carefully.
+
+Full installation instructions are provided in the INSTALL file. The
+README file also contains more specific notes regarding building and
+configuration.
+
+ * Major changes in 0.80.0:
+
+ 1) sbuild became a Debian native package -- consult debian/changelog or
+ /usr/share/doc/sbuild/changelog.Debian.gz instead
+
+ * Major changes in 0.79.1:
+
+ 1) buildd: add --dpkg-file-suffix option
+
+ * Major changes in 0.79.0:
+
+ 1) sbuild-createchroot: handle building armel/armhf on arm64, mipsel on
+ mips64el and powerpc on ppc64
+
+ 2) avoid using gzip --keep option, gzip in wheezy doesn't have it
+
+ 3) sbuild: add --dpkg-file-suffix option
+
+ 4) sbuild: pass the right .changes file to lintian
+
+ 5) sbuild-createchroot: use umask 0022
+
+ 6) sbuild: add APT_KEEP_DOWNLOADED_PACKAGES config variable
+
+ * Major changes in 0.78.1:
+
+ None (bugfix release)
+
+ * Major changes in 0.78.0:
+
+ 1) core: Add libc-dev and libstdc++-dev to Build-Depends when cross
+ compiling
+
+ 2) core: default to --no-arch-all when cross compiling
+
+ 3) core: Set build profiles cross and nocheck by default when cross
+ compiling
+
+ 4) core: Switch to C.UTF-8 for $LC_ALL
+
+ 5) core: allow chroots with nothing more but Essenital:yes packages, apt
+ and their hard dependencies and create missing users and groups as
+ needed
+
+ 6) sbuild: add --build-dir option
+
+ 7) sbuild: add --no-source-only-changes option
+
+ 8) sbuild: add --post-build-failed-commands hook
+
+ 9) sbuild: let --append-to-version imply --no-arch-all
+
+ 10) sbuild-createchroot: Don't usrmerge even if it is the debootstrap default
+
+ * Major changes in 0.77.1:
+
+ 1) sbuild: run dpkg-source --before-build and --after-build
+
+ 2) core: fix bd-uninstallable dose explainer for cross-building
+
+ * Major changes in 0.77.0:
+
+ 1) core: New chroot mode "unshare" which allows unprivileged package builds
+ by using Linux user namespaces. Only requires lightweight newuidmap and
+ newgidmap suid binaries. Chroots can be created and updated without
+ needing sudo and are stored in ~/.cache/sbuild or an arbitrary absolute
+ path given via the --chroot option.
+
+ 2) core: Add null resolver which does not install, upgrade or remove any
+ packages. This allows one to take full control over the package
+ installation process by using hooks.
+
+ 3) core: Remove support for signing the dummy archive because Squeeze LTS
+ support ended in February 2016: remove the --keygen option from
+ sbuild-update, remove the SBUILD_BUILD_DEPENDS_SECRET_KEY and
+ SBUILD_BUILD_DEPENDS_PUBLIC_KEY configuration variables.
+
+ 4) core: disable network during dpkg-buildpackage for backends that support
+ it (currently only the unshare backend)
+
+ 5) core: Install (cross-)build-essential, manual depends and build depends
+ with a single dummy package instead of using a multi-step process to
+ avoid having later steps change what was installed in earlier steps.
+
+ 6) core: Filter out foreign essential and multiarch foreign packages before
+ passing them to dose3 when cross building to prevent solutions that use
+ essential and multiarch foreign packages from the non-native
+ architecture.
+
+ 7) sbuild: Command line option --make-binNMU now implies --no-arch-all
+
+ 8) sbuild: only run autopkgtest if debian/tests/control exists
+
+ 9) sbuild: Add new build log filtering for /build/pkg-minimal-XXX/resolver-YYY:
+ <<RESOLVERDIR>>
+
+ 10) sbuild: Additional Lintian reasons in build log: info and warn.
+
+ 11) sbuild: Dump content of the input .dsc before extracting it.
+
+ 12) sbuild: Also print kernel version in build log
+
+ 13) sbuild-createchroot: do not attempt several setup options when the user
+ specified --setup-only together with --make-sbuild-tarball because then
+ the unpacked directory does not exist
+
+ 14) sbuild-createchroot: make the mirror argument optional as well (just as
+ it is in debootstrap)
+
+ 15) sbuild-createchroot: add --chroot-mode=unshare which allows running
+ debootstrap and creating a chroot tarball without needing sudo
+
+ 16) sbuild-createchroot: refuse to operate if output tarball or directory
+ already exist
+
+ 17) sbuild-update: add --chroot-mode option and support for unshare
+ backend
+
+ * Major changes in 0.76.0:
+
+ 1) sbuild-update-all: exit 0 when no chroots are set up.
+
+ * Major changes in 0.75.0:
+
+ 1) buildd: only build arch:any packages
+
+ 2) buildd: do not run lintian
+
+ 3) remove harmful unnecessary lintian binary check
+
+ * Major changes in 0.74.0:
+
+ 1) Install dose-distcheck and lintian for the native architecture
+
+ 2) remove DCMD config value which is not used and just uselessly making
+ libsbuild-perl depend on devscripts
+
+ 3) use dpkg-query instead of grep-status so that dependency on grep-dctrl is
+ not needed anymore
+
+ 4) suppress schroot writing to stderr in sbuild-createchroot
+
+ 5) check for modprobe in sbuild-createchroot
+
+ 6) Add missing newline character to a bunch of error messages
+
+ 7) dose3 failures no longer mask the --build-deps-failed-commands
+
+ 8) Build arch: all packages by default.
+
+ 9) Run lintian by default.
+
+ 10) sbuild-createchroot: add --command-prefix
+
+ 11) add sbuild-debian-developer-setup package
+
+ * Major changes in 0.73.0:
+
+ 1) starting-build-command and finished-build-commands are now run as root.
+
+ 2) Output .buildinfo in buildlog.
+
+ 3) The autopkgtest-virt backends are no longer supplied with the chroot
+ name.
+
+ 4) Add SBUILD_BUILD_ARCH percent escape.
+
+ 5) Use Dpkg::Build::Info::get_build_env_whitelist() to generate the default
+ of ENVIRONMENT_FILTER.
+
+ 6) For binNMUs, instead of copying the timestamp of the last changelog
+ entry, generate a new one.
+
+ 7) Add the --binNMU-timestamp command line option to allow one to pass a
+ custom timestamp for the new binNMU changelog entry.
+
+ 8) Add option --binNMU-changelog which allows one to pass a complete
+ debian/changelog entry for binary-only builds.
+
+ 9) Extra packages can now be used to upgrade already installed packages.
+
+ 10) The autopkgtest backend now runs into its own process group.
+
+ 11) Extra repositories are now unconditionally updated.
+
+ 12) Allow passing a directory to --extra-package which will add all .deb
+ inside.
+
+ * Major changes in 0.72.0:
+
+ 1) Add option --purge-extra-packages which makes sure to remove all
+ non-essential packages before the package build starts.
+
+ 2) Stop depending on newlines being produced by the build to detect timeouts
+
+ 3) Accommodate for the autopkgtest 4.0 release by renaming all adt command
+ line options and configuration variables from adt to autopkgtest because
+ the same renaming was done by the autopkgtest release.
+
+ 4) Add --bd-uninstallable-explainer which gives an explanation of why
+ the build dependencies of a source package cannot be installed.
+
+ * Major changes in 0.71.0:
+
+ 1) Undo exporting private keys in armored ASCII format as introduced by the
+ last release. It turns out, that this is not necessary to support gpg
+ cross-version compatibility. This should fix problems with gpg 2.x
+ as well as chroots not having gpg installed as apt stopped depending on
+ it.
+
+ 2) Undo the SUITE-VARIANT syntax of sbuild-createchroot as introduced by the
+ last release. It turns out that overloading the suite name argument with
+ a variant name was a bad idea because there exist distributions with a
+ minus in its name. Variant names can now be specified using the
+ --chroot-prefix option which works analogous to the --chroot-suffix
+ option.
+
+ 3) Add percent escape support to piuparts, autopkgtest and adt-virt options.
+
+ 4) Add sbuild-destroychroot.
+
+ * Major changes in 0.70.0:
+
+ 1) Fix reproducibility issue by not writing the $HOME as set during the
+ build into config files and man pages anymore
+
+ 2) Add an EXAMPLE section to the sbuild man page
+
+ 3) sbuild-createchroot now has support for SUITE-VARIANT chroot names. This
+ is useful to create variants of base suits like jessie-backports or
+ unstable-experimental.
+
+ 4) sbuild-createchroot now has the --no-deb-src option which allows one to
+ create a chroot without a deb-src entry in its sources.list. This is
+ useful to save bandwidth and disk space for chroots that will never be
+ used to download source packages as the source indices do not have to
+ downloaded, stored and later updated from the mirrors.
+
+ 5) sbuild-createchroot now has the --alias option which is useful to add a
+ shorthand alias to a chroot which will be used for chroot selection. This
+ is for example useful for a Debian unstable chroot which will be
+ automatically selected for package builds that have UNRELEASED in their
+ debian/changelog, if the UNRELEASED alias is added to the Debian unstable
+ chroot.
+
+ 6) suild-createchroot now has the --extra-repository which works like the
+ --extra-repository option for sbuild and permanently adds another line to
+ the chroot's sources.list.
+
+ 7) Private/public key pairs are now stored in armored ASCII format to
+ maintain compatibility between different gpg versions on the host and
+ within the chroot. Re-running `sbuild-update --keygen` will create the
+ new key types.
+
+ 8) Chroots can now have gpg >= 2.x installed as sbuild will properly clean
+ up any remaining gpg-agent processes and "apt-key add" is not used
+ anymore (it will leave a gpg-agent process around that cannot properly
+ be killed).
+
+ 9) Add the --source-only-changes option which will create a .changes file
+ suitable for a source-only upload alongside the normal binary .changes
+ file.
+
+ 10) Do not install debfoster by default in sbuild-createchroot to keep the
+ build chroot as minimal as possible. Users that use debfoster can use the
+ --include option of sbuild-createchroot.
+
+ 11) Drop requirement for gpg inside the chroot as external archive keys are
+ now processed without gpg and signing of the internal repository is
+ entirely optional with helpful warning and error messages in case
+ signing failed.
+
+ 12) Allow running autopkgtest after a successful build in the same manner
+ that piuparts can be run after a successful build.
+
+ * Major changes in 0.69.0:
+
+ 1) Decouple chroot from host in terms of filesystem access to allow
+ chroot backends like qemu, ssh or lxc. Deprecate external command
+ percentage escape %SBUILD_CHROOT_DIR in favour of %SBUILD_CHROOT_EXEC.
+
+ 2) Experimental adt backend. Enable with --chroot-mode=adt. Choose the
+ virtualization server with --adt-virt-server and add options with
+ --adt-virt-server-opt. This allows sbuild to use qemu, ssh and lxc
+ chroots.
+
+ 3) Allow to build packages without specifying a version. Earlier it was
+ possible to let sbuild "apt-get download" a dsc by specifying
+ "package_version" as a command line argument. The version part is not
+ necessary anymore. No manual version lookup by the user is required
+ anymore.
+
+ 4) Disallow conflicting command line options, thus making command line
+ switches position independent and remove the deprecated --arch-all-only
+ option in the process.
+
+ 5) The aspcud resolver criteria is not hardcoded anymore but can be chosen
+ with --aspcud-criteria. The default criteria is -removed,-changed,-new.
+ To minimize the number of packages from experimental, use:
+ -count(solution,APT-Release:=/a=experimental/),-removed,-changed,-new
+
+ 6) Apt doesn't download translations anymore.
+
+ 7) Dummy archive is now signed with SHA-512
+
+ 8) Cron script etc/sbuild-update-all allows one to periodically update all
+ sbuild chroots.
+
+ 9) Add another --*-failed-commands external command:
+ --chroot-update-failed-commands
+
+ 10) Documentation of configuration options in sbuild.conf(5) and in
+ /etc/sbuild/sbuild.conf now reference related command line options from
+ sbuild(1).
+
+ 11) Documentation of command line options in sbuild(1) now references the
+ respective configuration options from sbuild.conf(5).
+
+ 12) Remove configuration options which can be set on the command line from
+ the __INTERNAL group, thus exposing them in sbuild.conf as well and
+ referencing them in sbuild(1).
+
+* Major changes in 0.68.0:
+
+ 1) Dummy binary packages are now used for native building *and* cross
+ building (apt-get build-dep was used for the latter so far). For an
+ extensive rationale for this choice, read the commit message of commit
+ e227c8f3.
+
+ 2) Deprecate --arch-all-only option which was introduced two releases ago in
+ favour of --arch-all --no-arch-any. The option will be completely removed
+ in the next release to remove redundancy and for overall clarity.
+
+* Major changes in 0.67.0:
+
+ 1) add --clean-source and --no-clean-source to allow building a package from
+ an unpacked directory without calling debian/rules clean
+
+ 2) allow changing the user running dpkg-buildpackage inside the chroot
+
+ 3) sbuild-createchroot: use union-type=overlay for directory chroot
+
+ 4) Quit processing when external commands fail
+
+ 5) Add the --no-arch-any and --arch-any options which give a better control
+ over building or not building architecture specific packages than
+ --arch-all-only
+
+ 6) Set [trusted=yes] for internal dummy repository to make key generation
+ optional
+
+* Major changes in 0.66.0:
+
+ 1) add option --arch-all-only to build arch:all packages
+
+ 2) environment variable SBUILD_CONFIG allows to specify a custom
+ configuration file
+
+ 3) add option --build-path to set a deterministic build path
+
+ 4) fix crossbuild dependency resolution
+
+ 5) add option --extra-repository-key for extra apt keys
+
+ 6) add option --build-dep-resolver=aspcud for aspcud based resolver
+
+ 6) allow complex commands as sbuild hooks
+
+ 7) add now external command %SBUILD_SHELL produces an interactive shell
+
+ 8) add options --build-deps-failed-commands, --build-failed-commands and
+ --anything-failed-commands
+
+* Major changes in 0.65.2:
+
+ None.
+
+* Major changes in 0.65.1:
+
+ None.
+
+* Major changes in 0.65.0:
+
+ 1) Add support for build profiles. The specified profile is used
+ in the build if -P or --profiles is passed or the
+ DEB_BUILD_PROFILES environment variable is set.
+
+ 2) An extra build-time repository and/or individual packages can be
+ made available during the build. See the --extra-repository and
+ --extra-package options.
+
+ 3) Add more substitution variables for use in External commands:
+ host architecture (%a), chroot directory (%r), build directory inside
+ chroot (%b), and package build directory inside chroot (%p). See man
+ sbuild for details and alternate long names.
+
+* Major changes in 0.64.3:
+
+ 1) Add support for multiarch builds. Explicit build-deps on
+ foreign-arch packages enable that arch for the build.
+
+ 2) Command hook changes: chroot-setup-commands run as root. 2 new
+ user -command hooks run immediately before/after inner build
+ (--starting-build-commands/--finished-build-commands).
+
+* Major changes in 0.64.2:
+
+ 1) Allow to Define custom debootstrap variant.
+
+ 2) Allow to define custom suffixes in chroot names.
+
+* Major changes in 0.64.1:
+
+ 1) Bugfixes to work with Perl 5.18; use File::Temp and File::Spec
+ explicitly.
+
+ 2) Drop autoflush workaround added for Perl 5.10.
+
+* Major changes in 0.64.0:
+
+ 1) The number of times buildd will try to build before sleeping is
+ now configurable with MAX_SBUILD_FAILS.
+
+ 2) Lintian is now run inside the chroot rather than the host system.
+
+ 3) binNMUs now set binary-only=yes in the changelog.
+
+ 4) Cross-building improvements.
+
+* Major changes in 0.63.2:
+
+ 1) New options --no-run-lintian and --no-run-piuparts. If lintian
+ or piuparts are configured to run automatically following a
+ successful build, there options disable this behaviour for the
+ build.
+
+ 2) New option --purge-build. This behaves identically to the old
+ --purge option. --purge now purges everything (build directory,
+ build dependencies and schroot session) and is equivalent to
+ using --purge-build, --purge-deps and --purge-session together.
+
+ 3) If building from unpacked sources in the current working
+ directory, logs will be created in '..' rather than '.'.
+
+* Major changes in 0.63.1:
+
+ None.
+
+* Major changes in 0.63.0:
+
+ 1) Support for cross-compiling has been added. This includes the
+ addition of $host and $build configuration variables, with
+ corresponding --host and --build command-line options. This
+ includes the addition of a new 'xapt' dependency resolver.
+
+ 2) The deprecated 'internal' dependency resolver has been removed,
+ along with the configuration variables $apt_policy,
+ $check_depends_algorithm and $resolve_virtual, and the
+ command-line option --check-depends-algorithm. The 'apt'
+ resolver is the default replacement for 'internal'.
+
+ 3) Support for watches has been removed. The configuration
+ variables $check_watches, $ignore_watches_no_build_deps and
+ $watches (and obsolete variables @ignore_watches_no_build_deps
+ and %watches) have also been removed.
+
+ 3) sbuild-stats and support for build time and space statistics
+ recording has been removed. These statistics are recorded in
+ both the build log and are available as build metadata
+ internally. The statistics recorded in the database were not
+ particularly informative; storing the statistics in a proper
+ relational database is recommended. The configuration variables
+ $avg_time_db and $avg_space_db have been removed.
+
+* Major changes in 0.62.6:
+
+ 1) The use of alternatives in Build-Depends and Build-Depends-Indep
+ may be turned on or off by the configuration variable
+ $resolve_alternatives. It may also be enabled or disabled at
+ build time using the --resolve-alternatives and
+ --no-resolve-alternatives options, respectively.
+
+ 2) The new Build-Depends-Arch and Build-Conflicts-Arch fields in
+ debian/control are now supported. These may be manually set
+ with --add-depends-arch and --add-conflicts-arch,
+ respectively.
+
+ 3) schroot namespaces are now fully supported, meaning that
+ current 1.5.x schroot versions now work with sbuild.
+
+* Major changes in 0.62.5:
+
+ None.
+
+* Major changes in 0.62.4:
+
+ 1) The distribution in the build summary is coloured yellow if it
+ does not match the distribution in the changelog. This is to
+ indicate that there may be a potential problem, such as
+ potentially uploading a package intended for experimental to
+ unstable by building for unstable by accident. Additionally,
+ lintian test results are also coloured to highlight success and
+ failure. Log colouring may now be disabled using $log_colour.
+
+ 2) sbuild only permits building one source package at once.
+ Previously sbuild would allow building of more than one package
+ in a single invocation. Making this change means that the exit
+ status will always be the status of the build rather than the
+ last build, and it also means the logging is simplified and may
+ be started earlier.
+
+ 3) Added new directory /var/lib/sbuild/build to hold build trees for
+ sbuild when building. This will be bind mounted on /build inside
+ the chroot following an update to the schroot sbuild and buildd
+ fstab configuration for schroot. This saves significant amounts
+ of space when using LVM snapshot chroots, and it also makes it
+ easier to preserve build trees for failing builds when using
+ snapshots.
+
+ 4) Extra environment variables to be set when running
+ dpkg-buildpackage may now be set using $build_environment; these
+ will supplement the existing environment (e.g. PATH,
+ LD_LIBRARY_PATH). These variables will not be subject to
+ filtering with $environment_filter.
+
+* Major changes in 0.62.3:
+
+ 1) The apt and internal resolvers will resolve some alternatives
+ even when resolving alternative dependencies is disabled. For
+ relations involving the same package, the alternative will be
+ permitted when the package names are the same for each
+ alternative. For example,
+ foo (<< x) | foo (>= y) | bar
+ will be reduced to
+ foo (<< x) | foo (>= y)
+ while
+ foo | bar
+ will be reduced to
+ foo
+
+ 2) The sbuild-schroot wrapper has been removed. This will be moved
+ into the schroot package in its next release.
+
+ 3) The schroot "buildd" configuration profile has been moved to the
+ schroot package itself. This is due to being generated and being
+ architecture-dependent, and sbuild being an arch-all package (the
+ configuration would only be valid for the architecture sbuild was
+ built on).
+
+ 4) A new option, "-j", has been added to allow parallel builds.
+
+* Major changes in 0.62.2:
+
+ 1) New options --no-apt-clean, --no-apt-update, --no-apt-upgrade and
+ --no-apt-distupgrade have been added to allow these actions to be
+ disabled even when set as the default in the configuration.
+
+ 2) --keep-session is replaced by
+ --purge-session=(always|successful|never). This will allow the
+ session to be purged for all, successful and no builds,
+ respectively.
+
+ 3) $purge_session uses the same purge modes as $purge_build_deps and
+ $purge_build_directory, rather than a boolean value.
+
+ 4) Log filtering may be disabled using $log_filter = 0 in the
+ configuration. The replacement text is now also surrounded with
+ "«" and "»" so that strings such as CHROOT and BUILDDIR can't be
+ confused with any actual appearance of the same string in the
+ build log.
+
+ 5) Log colouring may be disabled using $log_colour = 0 in the
+ configuration.
+
+* Major changes in 0.62.1:
+
+ 1) Fixed security issue in the sbuild-schroot wrapper program which
+ did not enforce sbuild group membership in order to allow users
+ to access the chroots as user sbuild.
+
+* Major changes in 0.62.0:
+
+ 1) 'apt' is now the default build dependency resolver. Users should
+ not see any significant changes compared with the old 'internal'
+ resolver. Please note that you may need to generate a GPG key
+ for the local archive created for dependency package
+ installation, if one does not already exist; see sbuild-update
+ (--keygen) for further details.
+
+ 2) The 'internal' build dependency resolver is deprecated. It is
+ not recommended for future use, and will be removed once it is no
+ longer used by the buildd infrastructure. Please use the 'apt'
+ resolver as a drop-in replacement.
+
+ 3) The 'aptitude' build dependency resolver will, unlike 'apt' and
+ 'internal', consider alternative dependencies by default, rather
+ than only using the first alternative. This is intended to both
+ preserve backward compatibility, and make the 'aptitude' resolver
+ the preferred choice for more complex situations, such as
+ building for experimental.
+
+ 4) sbuild.conf and buildd.conf are now automatically generated from
+ the help text and defaults in the source code. This means that
+ the examples will always be syntactically correct, the help text
+ will always be current, and the defaults will always match the
+ defaults in the source code.
+
+ 5) All of the allowed values in sbuild.conf and buildd.conf are now
+ documented in the new sbuild.conf(5) and buildd.conf(5) manual
+ pages, respectively. Like sbuild.conf, this is entirely
+ generated from the source code, so will always match the defaults
+ for the same sbuild version.
+
+ 6) Non-scalar (or reference) types are deprecated in sbuild.conf.
+ This is because it is not possible to tell the difference between
+ an empty and an undefined value. Values using array or hash
+ types should use the equivalent array reference or hash
+ reference, which have been supported for some time. The old
+ style array and hash values will remain supported for now, but
+ will be removed in a future release.
+
+ 7) sbuild now performs an apt dist-upgrade at the start of each
+ build by default, rather than an upgrade. This is to reduce the
+ amount of manual administration required to keep chroots up to
+ date, and is not much more risky than upgrade in this context.
+
+ 8) A new option, --keep-session, has been added. This prevents the
+ automatic removal of session-managed snapshot chroots.
+ Previously, snapshots would not be deleted if purging of the
+ build directory or build dependencies was disabled, but this was
+ not always desirable, hence it is now configurable separately.
+
+ 9) Internally, building and other actions in the chroot are
+ performed by the 'sbuild' system user, where previously the user
+ invoking sbuild would be used instead. The aim of this change is
+ to separate privileges to increase security and reduce the chance
+ of accidental or deliberate tampering of the build environment.
+ While the latter is not addressed by these changes, this will be
+ taken care of during future architectural changes.
+
+ 10) In order to handle errors more robustly, the build code now has
+ initial support for exception handling. Normal operation will
+ not be affected, but fatal errors may be logged in a different
+ order than seen previously. Fatal errors will now be seen at the
+ end of the build log, which should make it easier to spot
+ problems.
+
+ 11) sbuild now always cleans up fully when receiving a termination
+ signal such as SIGINT or SIGTERM. Note that you may need to wait
+ while the cleanup actions are performed, or the current task is
+ completed prior to initiating cleanup. When running
+ interactively, hitting Ctrl-C will sent SIGINT to the entire
+ process group; doing this while apt-get or aptitude are running
+ will potentially leave dpkg in an inconsistent state, so aborting
+ at this point is not recommended. Sending a SIGTERM to the
+ sbuild process will always work cleanly.
+
+ 12) Long paths such as the chroot location and the build directory
+ inside the chroot are now filtered in the build log and replaced
+ with small, constant, abbreviations. This makes the build logs
+ comparable between builds with tools such as diff(1).
+
+ 13) Logging messages have been improved, and important messages are
+ now coloured when running interactively (does not affect log
+ files). Errors, warnings and informational messages are coloured
+ red, yellow and green, respectively. Build status is coloured
+ green for success and red for all failure conditions.
+
+ 14) The sbuild package build directory created inside the chroot now
+ has a reduced name length. It's now /build/packagename-XXXXXX
+ where XXXXXX are random characters. This helps reduce the chance
+ of hitting path length restrictions on some architectures,
+ particularly when using sockets.
+
+ 15) Build log mails are now compressed and mailed in MIME format by
+ default, together with a copy of the .changes file. The old
+ behaviour (plain mailing of uncompressed logs) may be restored by
+ setting $mime_build_log_mails=0 in the configuration, and
+ compression may also be disabled in the MIME mails by setting
+ $compress_build_log_mails=0. Note that it is no longer possible
+ to send compressed log mails unless MIME mailing is enabled.
+
+ 16) The wanna-build database has been removed entirely. This part of
+ the sbuild package was not used, and was not maintained for some
+ time. Users wishing to use wanna-build should investigate the
+ version in the wanna-build.git repository used by the Debian
+ autobuilding infrastructure. This version is actively maintained
+ and in continual use.
+
+
+* Major changes in 0.61.0:
+
+ 1) The apt and aptitude build dependency resolvers now use only the
+ first alternative in alternative dependencies. The other
+ alternatives are stripped out entirely. This makes the apt and
+ aptitude resolvers behave exactly like the internal resolver.
+ Note that this is configurable with the $resolve_alternatives
+ option, so alternative resolving may be re-enabled with this
+ option if desired. This is useful for e.g. backports, but not
+ for unstable where the strict consistency and reproducibility
+ offered by only using the first alternative is required.
+
+* Major changes in 0.60.9:
+
+ 1) The architecture specified with --arch (defaulting to the host
+ system architecture) must match the chroot architecture. This
+ was previously allowed to be superseded by the chroot
+ architecture. This change is to avoid any abiguity regarding the
+ host/build architecture for the package being built.
+
+* Major changes in 0.60.8:
+
+ None.
+
+* Major changes in 0.60.7:
+
+ None.
+
+* Major changes in 0.60.6:
+
+ 1) sbuild now sanitises the environment when running external
+ commands such as dpkg-buildpackage. The allowed environment
+ variables may be specified with $environment_filter in the
+ configuration. Currently, various DEB* and *FLAGS* environment
+ variables are allowed, similar to debuild's behaviour.
+
+ 2) sbuild now has a testsuite to do basic regression testing. By
+ default, only Perl syntax is checked. If configured with
+ --enable-chroot-checks, it will run the various sbuild-* helpers
+ and sbuild itself to build a small variety of different
+ packages. These are tested in an "unstable" chroot, which must
+ be present as a prerequisite for running the tests.
+
+ 3) The apt and aptitude build dependency resolvers now use a
+ temporary local apt archive for installation of the dependency
+ packages. apt-get would previously try to remove the dependency
+ package under certain conditions (apt-get -f install would force
+ removal), whereas using a real archive to install from prevents
+ this.
+
+ 4) sbuild-update has a new option, --keygen. This generated a GPG
+ key for signing local apt archives. The key will be generated
+ automatically on first use, but if entropy on the build system is
+ scarce, sbuild-update can generate it by hand at a more
+ convenient time, or on another system.
+
+ 5) sbuild no longer generates the files REDO and SBUILD-REDO-DUMPED
+ upon receiving a termination signal. These were no longer used
+ in buildd mode, and were only useful in a buildd context.
+
+* Major changes in 0.60.5:
+
+ 1) A new resolver, 'apt', has been added. Like the aptitude
+ resolver, this installs a dependency package to install build
+ dependencies and remove build conflicts, but uses apt-get rather
+ than aptitude. This resolver is currently experimental and may
+ not yet perform correctly under all circumstances. Use at your
+ own risk.
+
+ 2) It is now possible to run piuparts on the build packages with the
+ new --run-piuparts option.
+
+* Major changes in 0.60.4:
+
+ 1) The default dependency resolver has been reverted back to
+ 'internal' for the present. 'aptitude' will become the default
+ again once it has had further testing. Feedback regarding the
+ 'aptitude' resolver would be much appreciated.
+
+* Major changes in 0.60.3:
+
+ 1) The aptitude resolver is now the default resolver. Users wishing
+ to continue to use the old internal resolver should set
+ $build_dep_resolver='internal' in their configuration. The
+ aptitude resolver is far superior to the internal resolver,
+ because it can correctly resolve complex alternative and virtual
+ packages in build dependencies where the internal resolver would
+ fail.
+
+ 2) The aptitude resolver is now entirely separate from the internal
+ resolver, and greatly simplified. All Build-Depends and
+ -Conflicts are now passed directly to aptitude (with
+ architecture-specific dependencies filtered out). As before, a
+ dummy dependency package is used to install and remove build
+ dependencies and build-conflicts.
+
+ 3) The sbuild-clean program has been moved into sbuild-update, as
+ for the other sbuild- helper programs. The sbuild-* helper
+ programs now take the sbuild chroot lock used by sbuild to
+ prevent concurrent builds in the same chroot, making it safe to
+ run the maintenance commands while builds are running (they will
+ wait until the build has finished before making any changes).
+ The sbuild-* helper programs now operate on source chroots, where
+ available (for lvm-snapshot and btrfs-snapshot type chroots).
+
+ 4) The sbuild-createchroot program can now create tarballs from the
+ created chroot, with gzip, bzip2, lzma and xz compression. Use
+ the new --make-sbuild-tarball option for this purpose.
+
+ 5) sbuild may now be used on an unpacked source directory, in a
+ similar manner to debuild. The directory will be packaged using
+ 'dpkg-source -b' prior to building. If a source package or
+ directory is not specified, sbuild will use the current working
+ directory like debuild.
+
+ 6) Lintian may now be run after a package build with the
+ --run-lintian option.
+
+ 7) External commands may now be run before and after a build and
+ during chroot setup and cleanup. These may be used to do
+ additional setup and checking during a build, for example to run
+ piuparts.
+
+* Major changes in 0.60.2:
+
+ 1) Virtual packages may now be used in Build-Depends using the
+ internal resolver. This is very simple, and picks the first
+ package providing the dependency in alphabetical order. The
+ aptitude resolver is rather more sophisticated, and should be
+ used if this is insufficiently clever.
+
+ 2) Build-Conflicts are correctly removed and reinstalled, and sbuild
+ can now remove packages which depend upon other packages, due to
+ using "apt-get remove" in place of "dpkg --remove".
+
+ 3) Package build-dependency installation in now preceeded by
+ installation of "core" dependencies (currently build-essential)
+ and "essential" dependencies (essential packages listed in
+ build-essential), to ensure a working build environment. These
+ should, of course, already be installed; this change is an
+ additional consistency check to ensure the build environment is
+ sane.
+
+ 4) schroot sessions may be preserved on failure (failure to build or
+ failure to install build dependencies), to allow easier
+ troubleshooting.
+
+* Major changes in 0.60.1:
+
+ 1) Improvements in building of dpkg-source v3 source formats.
+
+ 2) sbuild includes a new option --no-source to disable building
+ of source packages. This is the reciprocal of the existing
+ --source option.
+
+* Major changes in 0.60.0:
+
+ 1) sbuild no longer defaults the distribution to "unstable", and
+ requires setting by hand with --distribution unless configured in
+ .sbuildrc. This is to prevent accidental uploads to the wrong
+ distribution.
+
+ 2) sbuild now lists all packages (including versions) installed in
+ the build chroot in the package build log. This may aid
+ identifying problems with builds using buggy or outdated package
+ versions.
+
+ 3) With a recent versions of man-db (>= 2.5.6-4), man-db will be
+ configured not to rebuild its database during package
+ installation and removal in the build chroot.
+
+ 4) sbuild has added new options to allow APT cleaning, upgrading and
+ dist-upgrading of the build chroot, in addition to the existing
+ updating option.
+
+ 5) buildd has been synched with the buildd code in use on the Debian
+ buildd infrastructure, and is now in use on most, if not all,
+ running buildds. It has undergone extensive changes since the
+ previous release, and is now suitable for production use. Note
+ that the packaged version of wanna-build is not yet ready for use
+ with buildd.
+
+ 6) buildd no longer bind mounts /home into the chroot.
+
+ 7) buildd now handles all wanna-build interaction. All sbuild
+ wanna-build usage has been removed, including the
+ --auto-give-back and --database options.
+
+* Major changes in 0.59.0:
+
+ 1) The wanna-build options --no-propagation and
+ --no-down-propagation have been removed. These options no longer
+ have any effect, and were only present for backward-compatibility
+ with older versions of buildd. buildd no longer uses these
+ options, making it safe to remove them.
+
+ 2) The wanna-build configuration options used by buildd in
+ buildd.conf have been renamed for better compatibility with future
+ planned sbuild changes. buildd.conf will require updating with the
+ new option names:
+
+ ┌───────────────────────┬─────────────────────────────────────┐
+ │ old │ new │
+ ├───────────────────────┼─────────────────────────────────────┤
+ │ $wanna_build_dbbase │ $wanna_build_db_name │
+ │ $wanna_build_user │ $wanna_build_db_user │
+ │ $ssh_user │ $wanna_build_ssh_user │
+ │ $ssh_host │ $wanna_build_ssh_host │
+ │ $ssh_socket │ $wanna_build_ssh_socket │
+ │ @ssh_options │ $wanna_build_ssh_options (arrayref) │
+ └───────────────────────┴─────────────────────────────────────┘
+
+ 3) sbuild will set the "attempted" and "given-back" states for
+ certain failures. If wanna-build updates are configured with
+ --database and --auto-give-back by buildd, it will set the
+ "built" state on build completion, and "attempted" if the build
+ failed. As before, "given-back" will be set for most other
+ failure conditions.
+
+* Major changes in 0.58.6:
+
+ None.
+
+* Major changes in 0.58.5:
+
+ None.
+
+* Major changes in 0.58.4:
+
+ None.
+
+* Major changes in 0.58.3:
+
+ 1) The broken options --add-depends and --force-depends have been
+ replaced by --add-depends, --add-conflicts, --add-depends-indep
+ and --add-conflicts-indep. These options each take a single
+ dependency, using the same dependency syntax one would use in
+ debian/control.
+
+ 2) The wanna-build configuration has been moved back to
+ /etc/buildd/wanna-build.conf. However, the option names have
+ been renamed and your configuration will require updating.
+
+ 3) When reading configuration files, stricter checking is performed.
+ If you see a "Global symbol "$x" requires explicit package
+ name" error, then the option $x is not valid and should be
+ removed from the configuration file indicated.
+
+ 4) The maintainer name no longer defaults to DEBEMAIL as in most
+ cases the maintainer in the .dsc should be used.
+
+* Major changes in 0.58.2:
+
+ None.
+
+* Major changes in 0.58.1:
+
+ None.
+
+* Major changes in 0.58.0:
+
+ 1) The configuration option @no_auto_upgrade, which was no longer
+ used, has been removed. To prevent packages being upgraded
+ automatically, please use sbuild-hold. See sbuild-hold(1).
+
+ 2) The configuration option $chroot_mode has been restored, allowing
+ chroot access by both schroot (which remains the default) and
+ sudo.
+
+ 3) A new configuration option $chroot_split has been added. This
+ allows both schroot and sudo chroot access to be modified to run
+ apt/dpkg on the host system. Note that schroot needs sudo to run
+ commands as root on the host system.
+
+ 4) Deprecated commands from /usr/lib/sbuild have been removed.
+ Please use the sbuild-* replacements found in /usr/bin.
+
+ 5) The maintainer name defaults to DEBEMAIL if set in the
+ environment, so no longer needs setting explicitly in .sbuildrc.
+
+ 6) Unless configured as a buildd ($sbuild_mode), when using sudo
+ chroot access, chroots are searched for in /etc/sbuild/chroots.
+ This directory should contain symlinks to the chroots, with the
+ distribution name being the name of the symlink. Buildd mode
+ will continue to use the old method of current-$distribution
+ symlinks in the current directory.
+
+ 7) The log format used for build logs has been updated to use a more
+ readable format, and is now encoded in UTF-8 rather than
+ US-ASCII. Code which parses the log file may need to be updated
+ to handle the new format. Further log format changes made in the
+ future.
+
+ 8) All commands now have a corresponding manual page.
+
+ 9) The wanna-build configuration has moved from
+ /etc/wanna-build/wanna-build.conf into /etc/sbuild/sbuild.conf as
+ part of ongoing refactoring work. It is expected that this will
+ be split into a separate file in the future once this work is
+ completed.
+
+ 10) wanna-build can now correctly dump and restore its databases
+ using the --export and --import options. Note that
+ wanna-build-catmldbm and related commands can not correctly dump
+ the current database format, can not be used to restore the dump
+ and will be removed in the future.
+
+ 11) The wanna-build commands do-merge-packages and do-merge-quinn
+ have been renamed to wanna-build-merge-packages and
+ wanna-build-merge-quinn.
+
+ 12) wanna-build changes from the official Debian wanna-build
+ installation have been merged.
+
+ 13) Initial support for a wanna-build PostgreSQL backend backend has
+ been added, but is not currently functional. The database
+ schema is also included, but not yet finalised.
+
+* Major changes in 0.57.5:
+
+ 1) sbuild-createchroot now supports most of the options found in
+ debootstrap. It no longer adds main and contrib to
+ /etc/apt/sources.list in the chroot (the components are now
+ specified with --components instead of being hard-coded).
+
+ 2) All sbuild commands now support --help and --version options.
+
+ 3) sbuild-abort and sbuild have been updated to work with the
+ current dpkg-buildpackage and handle build termination
+ gracefully.
+
+* Major changes in 0.57.4:
+
+ None.
+
+* Major changes in 0.57.3:
+
+ None.
+
+* Major changes in 0.57.2:
+
+ 1) The new dpkg source format is now supported.
+
+ 2) The chroot to build in may be specified independently of the
+ distribution.
+
+ 3) Purging of files in the chroot has been made more reliable, by
+ putting all of the build files in a single temporary directory
+ which is purged after the build completes.
+
+ 4) $mailto no longer needs setting in .sbuildrc if not running as a
+ buildd.
+
+ 5) An option to always update the APT package list has been added
+ ($apt_update in sbuild.conf).
+
+ 6) wanna-build, the database used to track which packages need
+ rebuilding, has been added as a separate package.
+
+* Major changes in 0.57.1:
+
+ 1) While not used in the Debian packaging, the buildd and
+ wanna-build sources have been merged from the separate buildd.git
+ repository. They will be built as packages in a future release.
+
+ 2) Helper scripts in the Debian package have been moved from
+ /usr/lib/sbuild back to /usr/share/sbuild, where they were
+ located prior to 0.57.0.
+
+ 3) schroot "directory" type chroots are now purged correctly
+ following a package build.
+
+* Major changes in 0.57.0:
+
+ 1) All programs have been moved from /usr/share/sbuild to /usr/bin
+ (with the exception of dobuildlog), with an sbuild- prefix. In
+ some cases, the names have been changed. The older programs are
+ still available under /usr/lib/sbuild.
+
+ 2) All programs (with the exception of dobuildlog) now have manual
+ pages.
+
+ 3) sbuild now runs apt-get with APT::Install-Recommends set to
+ false.
+
+ 4) sbuild can be optionally configured to use an alternative
+ algorithm to compute build dependencies, to allow installation of
+ alternative build dependencies.
+
+ 5) sbuild-createchroot (formerly buildd.chroot) now creates a fully
+ working chroot, with no manual setup required other than adding
+ the schroot configuration.
diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000..9b6b8c3
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,54 @@
+Working on sbuild -*- text -*-
+═════════════════
+
+This document is a short guide to the conventions used in the
+buildd-tools sbuild project.
+
+
+Coding
+──────
+
+The style should be apparent from the source. It is the default Emacs
+perl-mode style.
+
+Sbuild can be executed directly from source by running the following from the
+top level directory.
+
+ $ PERL5LIB=lib bin/sbuild
+
+
+Documentation
+─────────────
+
+All the documentation is in UNIX manual page format. GNU roff
+extensions are permitted, as is use of tbl. Make sure the printed
+output is as good as terminal display. Run "make ps" or "make pdf" to
+build the printed documentation.
+
+
+The following styles are used:
+
+ Style Formatting Syntax
+ --------------------------------------------------------------------
+ New term Bold .B or \fB
+ Option definition Bold, args in italic .BR and \fI
+ Option reference Italic .I or \fI
+ File definition Bold italic \f[BI]
+ File reference Italic .I or \fI
+ Config key definition Courier bold italic \f[CBI]
+ Config key reference Courier italic \f[CI]
+ Values Single quotes \[oq] and \[cq]
+ Example text Double quotes \[lq] and \[rq]
+ Cross references Italics in double quotes \[lq]\fI...\fP\[rq]
+ Verbatim examples Courier \f[CR]
+ Verbatim user input Courier bold \f[CB]
+
+
+Releasing
+─────────
+
+• The code must pass the testsuite (run 'sudo make check' after
+ ./configure --enable-chroot-checks). This requires a local schroot
+ called 'unstable' setup. These checks can take some time to
+ run. Plain "autoreconf -fi && ./configure && make check" runs only the
+ checks that can be done without schroot, which are very quick.
diff --git a/Makefile-buildd b/Makefile-buildd
new file mode 100644
index 0000000..2d4dba4
--- /dev/null
+++ b/Makefile-buildd
@@ -0,0 +1,38 @@
+# Makefile
+# Copyright © 1998-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/>.
+#
+#######################################################################
+
+CC = gcc
+CFLAGS = -O2 -Wall -g
+LD = gcc
+LDFLAGS =
+
+PRGS = wanna-build-mail buildd-mail-wrapper
+
+all: $(PRGS)
+
+wanna-build-mail: wanna-build-mail.c
+ $(CC) $(CFLAGS) -o $@ $^
+ strip $@
+
+buildd-mail-wrapper: buildd-mail-wrapper.c
+ $(CC) $(CFLAGS) -o $@ $^
+ strip $@
+
+clean:
+ rm -f $(PRGS)
diff --git a/Makefile.am b/Makefile.am
new file mode 100644
index 0000000..537c109
--- /dev/null
+++ b/Makefile.am
@@ -0,0 +1,49 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2007 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
+
+SUBDIRS = bin tools configs etc lib man db test
+
+dist-hook:
+# Remove junk from the generated tarball.
+ $(FIND) $(distdir) -name '.*' -print | $(XARGS) xargs $(RM) -rf
+ $(FIND) $(distdir) -name '#*' -print | $(XARGS) $(RM) -rf
+ $(FIND) $(distdir) -name '*~' -print | $(XARGS) $(RM) -rf
+
+EXTRA_DIST = \
+ bootstrap \
+ lisp \
+ scripts/git-tag-release \
+ ChangeLog-buildd \
+ HACKING \
+ Makefile-buildd \
+ NEWS-buildd \
+ README.buildd-admin \
+ README.chroot-building
+
+ps:
+ $(MAKE) -C doc sbuild.ps
+
+pdf:
+ $(MAKE) -C doc sbuild.pdf
+
+.PHONY: ps pdf
diff --git a/NEWS-buildd b/NEWS-buildd
new file mode 100644
index 0000000..79ca393
--- /dev/null
+++ b/NEWS-buildd
@@ -0,0 +1,1415 @@
+2004-09-05 Ryan Murray <rmurray@debian.org>
+
+ * buildd-mail: get filelist from changes file first when processing
+ ACCEPTED/INSTALLED mail. Limit filename pattern to valid filename
+ characters if the changes file doesn't exist. Solves possible
+ remote execution vulnerability (non-existant in default setup).
+
+2003-11-05 Ryan Murray <rmurray@debian.org>
+
+ * sbuild: display toolchain package versions
+ * sbuild: get build-essential information from the chroot, not
+ the root.
+ * sbuild: new --use-snapshot option that installs the gcc-snapshot
+ package as a build-depends, and sets the environment variables needed
+ to use it.
+ * buildd-mail: attempt to move installed packages to the apt cache dir
+ rather than deleting them. This saves bandwidth, as they will not
+ need to be downloaded again if they are build-depends, but uses a lot
+ of diskspace.
+ * wanna-build: add --clean-db option to free unused space in the dbm
+ file
+ * sbuild: set the state of installed build-dependencies to purge after
+ installation, so that in case of a crash, dpkg -P --pending should
+ remove all packages that no longer need to be there.
+ * sbuild: add $conf::build_env_cmnd to setup the build environment
+ for sparc32 building on sparc64 (and other things?)
+ * sbuild: add --binNMU=# parameter for use with --make-binNMU
+ for the case where more than one binNMU is needed between
+ upstream releases. defaults to 1.
+ * sbuild: remove unused --store-built-packages
+ * buildd: don't call sbuild with --store-built-packages
+ * sourcedeps-makerev: disable for now, needs a rewrite for apt
+ * update-sourcedeps: comment call to sourcedeps-makerev for now
+ * buildd-mail: check for a Distribution: line in signed changes files,
+ and at least one file.
+ * buildd: call apt-get autoclean after an apt-get update that updates
+ the apt package cache.
+ * buildd-uploader: don't upload what's in the current directory if we
+ can't chdir to the upload dir.
+ * buildd: fork and call setsid so we don't have a controlling terminal.
+ * abort-current-build: use /bin/kill instead of shell builtin
+ * add EXIT-DAEMON-PLEASE flagfile. buildd checks it once per loop
+ and will exit if found, taking the file with it. sbuild checks it
+ after finishing each build, and will exit when finished a job.
+ * Patch from Anthony Towns <ajt@debian.org> to sort standard and higher
+ priorities higher, and add it to the front of the default order
+ characters (the new character is S). This has the effect of making
+ standard and higher uncompiled sort higher than all optional and
+ extra, which is ideal for Debian.
+
+2001-01-11 Roman Hodek <roman@caldera.de>
+
+ * FetchFile.pm: Various fixes for package pools (tnx to Dan for
+ patches).
+
+ * sbuild.conf.local: New variable %dist_aliases can be used to
+ translate distribution names (entries under dists/ on a mirror).
+ Currently needed for strange unstable -> sid symlink, which IMHO
+ should point to woody instead.
+
+2000-10-19 Roman Hodek <roman@caldera.de>
+
+ * sbuild: By Dan: Make sure that /etc/ld.so.conf is readable
+ before building.
+
+ * sbuild: Introduced local source dependency overrides with
+ %conf::srcdep_over defined in some config file. You can replace
+ deps on arbitrary packages with something else, e.g.
+ $srcdep_over{'libfoo1-dev'} = [qw(libfoo2 >= 2.0)];
+
+ * buildd-mail: Added preliminary PGP/MIME support by Dan.
+
+2000-06-27 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: There are two new section types for special
+ dependencies. The first is 'prepre', which contains a shell script
+ like 'pre', but the script is run *before* dependencies are
+ installed ('pre' is run afterwards).
+ The second new section is 'unpack'. You can list a number of
+ source package names there. sbuild will fetch the .dsc files and
+ unpack the source trees in the build dir. After the build, those
+ trees will be removed again. This can replace the dpkg-source
+ calls that were used in pre scripts. Their problem were that they
+ needed access to a local mirror, which can't be guaranteed now
+ with chroot environments.
+
+2000-06-20 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: For chrooted builds, call apt-get with a bunch of
+ options to relocate all relevant files instead of chrooting apt
+ itself. This makes it unnecessary to have the Debian archive to be
+ available from inside the chroot environment. Also dpkg and
+ apt-cache calls are chrooted with options now.
+
+ * buildd-addpkg: Also here apt-get is called with options instead
+ of chrooting it.
+
+2000-06-19 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-addpkg: New option --chroot=DIR (can be given multiple
+ times). Causes apt-get update to be run chrooted for that dirs,
+ too.
+
+ * sbuild: If build was chrooted and produced source-deps, call
+ buildd-addpkg with --chroot options for all dists greater or
+ equal to the current one to get apt caches correctly updated.
+
+ * sbuild, sbuild.conf: New config var
+ @ignore_watches_no_build_deps. Watch packages listed there aren't
+ reported if the package has no Build-Depends. This is for things
+ like debhelper and debstd which are considered essential with
+ the central source deps but not with Build-Depends.
+
+2000-06-13 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Also execute shell scripts of special dependencies in
+ chroot environment.
+
+2000-06-09 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * rbuilder: Has been modified to use chroot environments. So you
+ finally can build stable packages on kullervo now without shared
+ library version problems!
+ It also uses wanna-build again, but doesn't give back packages
+ anymore so that the stable build daemon won't take them.
+
+2000-06-08 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Implemented building in chroot environments. See
+ README.chroot-building for details.
+
+ * buildd-setup-chroot: New script for setting up a chroot
+ environment for sbuild. See README.chroot-building for details.
+
+ * README.chroot-building: New documentation for the above.
+
+2000-06-06 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: New option --source (-s): Also build source package,
+ i.e. don't call dpkg-buildpackage with -b or -B. Also
+ automatically switches on --purge=never.
+
+2000-05-30 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * do-merge-{packages,quinn}: Rewritten in Perl and moved all
+ configuration to /etc/wanna-build.conf.local. It was really a mess
+ that those two scripts still had to be edited by hand... Also,
+ they support local mirrors and quinn-diff files now.
+
+ * buildd-addpkg: Local packages are now installed separated by
+ distributions to ~/AddPkg/DIST. With this, you can exclude e.g.
+ unstable packages from being installed on a frozen system by
+ excluding that directory from /etc/apt/sources.list. buildd-addpkg
+ has a new --dist option that defaults to unstable. --clean mode
+ always operates on all distributions.
+
+ * sbuild: Now calls buildd-addpkg with --dist option. Also does
+ automatically install freshly built packages only if
+ $conf::system_level is a "greater" or equal distribution that the
+ distro the pkg was built for. For example, if the system level is
+ "frozen", packages built for unstable won't be installed
+ automatically.
+
+2000-05-16 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Do a chmod -R go+rX on the build tree before building,
+ so that files are readable. Some .orig.tar.gz's contain rather
+ restrictive permissions that are simply inconvenient.
+
+2000-03-03 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: The deleting of Arch: all packages was a really Bad
+ Idea (TM)... It deleted all entries where *at least* one binary
+ packages was Arch: all, and that really wasn't what I meant...
+ Reverted this.
+
+ The correct solution: There's a --merge-source now that reads the
+ Sources.gz file (uncompressed) for a distribution and looks at
+ Arch: all packages. If such a package also exists in the database
+ with a less or equal version, the entry is deleted.
+
+2000-03-02 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: If a package with Architecture: all appears in the
+ database, it probably just has changed to this arch. Just delete
+ it, it's not needed anymore.
+
+ * wanna-build: If a package disappears from quinn-diff in states
+ Building or Upload, delete is as well as it would have been
+ Needs-Build, Failed, or Dep-Wait.
+
+ * buildd-mail: Fixed a bug that caused check_building_any_dist to
+ fail. As a consequence, a package still could be deleted if it
+ became outdated and still was building in another dist.
+
+2000-03-01 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Introduced new concept of failed categories. See
+ README.failcat for more details. (New option --category == -c)
+
+ * README.failcat: new file.
+
+ * wanna-build: New options --min-age=DAYS (-a DAYS) and
+ --max-age=DAYS to restrict listed package depending on the time of
+ last state change.
+
+ * buildd-reply.el:There are two new functions:
+ buildd-bug-change-category changes the failed category of a failed
+ package; you use it from a mail buffer with a reply to the bug
+ report (if the maintainer hasn't changed the subject...); you are
+ prompted for a new category, which can be TAB-expanded.
+ buildd-bug-comment adds next text to the fail message of a
+ package; used the same way as buildd-bug-change-category above,
+ but you can insert arbitrary text; with [CAT] you can also
+ set/change the category.
+
+ I've also changed keybindings: C-c C-a (formerly
+ buildd-bug-ack-append) is now a prefix for 3 bindings that append
+ something to fail messages:
+ C-c C-a C-n -> buildd-bug-ack-append (i.e., append a bug number)
+ C-c C-a C-a -> buildd-bug-comment (append arbitrary text)
+ C-c C-a C-c -> buildd-bug-change-category
+
+ * buildd: In get_changelog, remove an epoch if using the package
+ name as part of a file name.
+
+ * sbuild: Also match error message "dpkg: status database area is
+ locked" from apt and retry call later.
+
+2000-02-23 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Added a warning for packages that have a newer
+ version in the database than in quinn-diff, and the state is one
+ of Installed, Install-Wait, Reupload-Wait, or Not-For-Us. In those
+ cases, the newer version is questionable... it could be caused by
+ a lame mirror, or it is an error in the db. During --merge-quinn,
+ wanna-build now checks for such cases and if it detects some, it
+ sends a mail about them to the admin.
+
+2000-02-16 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Fix support for !needs-no-XXX deps (should now work as
+ advertised :-)
+
+ * sbuild: A package log contains now the version number of sbuild.
+
+2000-02-15 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Improved warnings about missing central source
+ dependencies: The list of package build dependencies is now not
+ only dependency-expanded, but also virtual packages among them are
+ expanded to an alternative of all providing packages. That way, a
+ concrete package in the central deps shouldn't any longer be
+ reported as missing if the build deps contain a corresponding
+ virtual package. (E.g.: build dep is libncurses-dev, central deps
+ contain libncurses5-dev.)
+
+2000-02-11 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Do not show warnings that !this-package-does-not-exist
+ or !needs-no-XXX type dependencies are missing in the package
+ build dependencies.
+
+ * sbuild: Do not activate watches for dependencies of build deps
+ and not for packages where a !needs-no-XXX dep exists.
+
+2000-02-04 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: --no-build now also propagates the new state to
+ other distributions, which makes it consistent to the other
+ actions.
+
+ * wanna-build: new options --no-propagation (-N) and
+ --no-down-propagation (-D) which suppress state propagation at all
+ or to ``smaller'' distributions, resp. The former is sometimes
+ useful for manually fixing things (e.g. a package fails only in
+ stable due to the build environment, but not in unstable).
+ --no-down-propagation is useful for the build daemon to avoid
+ unwanted propagations.
+
+ * buildd, buildd-mail, buildd-uploader, sbuild: Use
+ --no-down-propagation in wanna-build calls.
+
+2000-01-28 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Finally let it also accept abbrevs for distributions (s,
+ f, and u).
+
+ * buildd-reupload: Now finally can completely reconstruct a
+ .changes from the source package and the existing binary packages
+ that should be re-uploaded for different distributions. This
+ finally makes the script usable :-)
+
+ * buildd-reupload added to install and update-buildd scripts.
+
+ * sbuild: New option --make-binNMU='message'. This will create a
+ new changelog entry for a binary-only NMU. The message in the
+ option will be used as description in the changelog (it may
+ contain newlines and will be formatted properly). Please note that
+ the version number in the package argument(s) still must be the
+ existing, non-NMU versions so that sources can be downloaded. The
+ version in the logfile etc. will be modified automatically. An
+ bin-NMU entry won't be created twice in the same build tree.
+
+2000-01-27 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd: Remove $DISPLAY from the environment. The variable
+ doesn't make sense when running in the background.
+
+2000-01-20 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-mail: Allow "give-back" as answer to a Should-I-Build
+ question.
+
+2000-01-18 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-mail: "not-for-us" or "no-build" are now valid answers to
+ a Should-I-Build question. This is handy if a package is now arch
+ specific or the like.
+
+ * buildd-mail: Fix outdated handling (packages are only outdated
+ in a distrib if they were reported to be so...)
+
+ * buildd-mail: If a package is reported as being outdated now,
+ skip the build and/or delete the files only if we're not still
+ building it in any other distribution.
+
+2000-01-13 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Pass the --force-confold option to dpkg only if the
+ version is before 1.4.1.18. Those old versions don't understand
+ the option and fail. Newer versions need it, because they fail if
+ they ask the conffile question and stdin is /dev/zero.
+
+1999-12-17 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: even more improved printout of missing central
+ dependencies check. First sbuild reads the list of build-essential
+ packages (as according to policy, from the build-essential
+ package) and expands its dependencies. Those packages are filtered
+ out and separately. They should never be mentioned in
+ Build-Depends. Next, dependencies of the Build-Depends are
+ expanded and those missing deps are also printed separately. It
+ depends on the circumstances if those should be reported. This all
+ together should make bug reports based on the sbuild warnings more
+ reliable.
+
+1999-12-08 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: allow multi-line build dependencies when reading a .dsc.
+ Those are actually forbidden is you look into packaging manual
+ closely, but too many packages use them...
+
+ * sbuild: Fix a bug in reading packages status: If a name was both
+ provided and a real pkg name (probably an old one), the
+ "not-installed" on the real pkg could set the installed status to 0.
+
+ * sbuild: Special dependencies (those with a '*') are always
+ implicitly overrides, i.e. they're added to package-supplied build
+ dependencies.
+
+1999-12-03 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: When using source deps from a .dsc, compare them with
+ the central deps and report any missing things. It seems that many
+ packages lack some deps at the first try, and this aids reporting
+ bugs.
+
+1999-11-30 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: When a package has its own source dependencies in the
+ .dsc, store them in a file debian/.sbuild-build-deps in the build
+ tree. This is needed when the package is later built again and the
+ .dsc isn't available anymore. If a sbuild argument is a directory
+ (building from tree without .dsc), look if this file exists and if
+ yes, read build deps from it.
+
+ * sbuild: Print a message to the log if package build deps are
+ used. It already happened sometimes that I searched for an error
+ in madd_sd-* or andrea, but the bad dependencies came from the
+ packages :-) Such a message at least reminds you where to look...
+
+1999-11-12 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd: There's a new config variable, $secondary_daemon_threshold,
+ in buildd.conf. It's intended for secondary daemons that should
+ start building only if the primary daemon(s) are overloaded. The
+ threshold gives a number of Needs-Build packages that must be
+ reached before the secondary starts off.
+
+1999-10-29 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * Fixed buildd statistics: The packages given back by sbuild
+ weren't recorded, so some figures were a bit corrupted (specially
+ processed). sbuild now appends 1 for each given-back pkg if it's
+ given the new option --stats-dir. buildd passes that option.
+ buildd-watcher knows about the new stats file and prints it in the
+ summary.
+
+ * buildd-addpkg: If apt-get update fails with "E: Could not get
+ lock", try again a minute later.
+
+ * sbuild: Make apt always pass --force-confold to dpkg. The
+ </dev/null trick doesn't work anymore with dpkg >= 1.4.1.18 (it
+ gives an error on the conffile question if it read EOF.)
+
+ * sbuild: If using an already unpacked source tree, check if it's
+ really the version we want to build. (The requested version could
+ be different in the Debian revision, and you can't see that on the
+ directory's name.)
+
+1999-10-25 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * New script buildd-clean-upload: It removes all jobs (.changes +
+ associated files + .upload) that are installed in the archive
+ (must be local or NFS mounted). I needed this now, because
+ INSTALLED mails from maor-installer don't come through at the
+ moment. But it's more a quick fix that can be useful later, too,
+ than a real part of buildd.
+
+1999-10-21 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * New script buildd-addpkg that maintains a private package
+ archive for apt. buildd-addpkg copies a list of (reshly built)
+ packages to ~/AddPkg and updates the Packages file there. Older
+ versions of the pkg that existed in AddPkg/ are removed. Then
+ apt-get update is called to make the installed pkgs available for
+ apt. If called with --clean, buildd-addpkg checks apt knows about
+ the packages in AddPkg/ from somewhere else and removes those.
+ ~buildd/AddPkg should be in the last line of your
+ /etc/apt/sources.list.
+
+ * sourcedeps-makerev: New script that builds a reverse index of
+ source dependencies, i.e. a list of all packages that are a
+ referenced as source dependency. It is called now by
+ update-sourcedeps after installing a new file, and the reverse
+ index is stored as /etc/source-dependencies-DIST.rev. It is read
+ by sbuild if called with --store-built-packages.
+
+ * sbuild: New option --store-built-packages=PROG. PROG is a
+ program that makes built packages available for apt and is usually
+ buildd-addpkg. If this option is given, sbuild checks after each
+ build if any of the produced packages is a source dependency. If
+ yes, it is scheduled for installation. Also installed are packages
+ that already scheduled packages depend on (maybe via Provides:)
+ and that were also built. All those packages are passed to PROG,
+ which makes the available for apt.
+ Finally, if one of the src-dep packages is already installed and
+ it's not in @conf::no_auto_upgrade, apt is called to install the
+ new version.
+
+ * buildd: Now calls sbuild with --store-built-packages=buildd-addpkg
+ if that tool exists and $conf::no_store_packages isn't set
+ (default).
+
+1999-10-06 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-mail: before actions on retry, fail, dep-ret, man,
+ give-back and should-I-build replies are executed, check that the
+ state is Building in the given version. This should avoid that old
+ mails (stalled in a queue somewhere) cause wrong things to be done
+ on outdated stuff. Little exception: If a "fail" answer appends a
+ bug number, the required state is Failed instead.
+ Also, on should-I-build replies there is a check for outdated
+ packages.
+
+ * buildd-mail: There are now different error messages for the
+ reply mail and the log. Often the messages were too verbose for
+ the log.
+
+ * buildd-mail: Logging of queue daemon messages can be turned off
+ by $log_queued_messages = 0 in ~/buildd.conf.
+
+ * buildd-mail: Before moving a package to the upload dir, every
+ .deb is checked for correct md5 checksum. It happened in the past
+ that a package was built twice (due to errors) overwriting the
+ .debs. And if you then acknowledged the older log the md5sums didn't
+ match, resulting in later errors in the queue or on master.
+
+ * buildd: Removed message "DEBUG: not taking ... because given
+ back recently".
+
+1999-09-22 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-mail: Fix a bug that caused outdated packages to be added
+ to REDO sometimes on "retry" mails.
+
+1999-09-02 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * finish-build: I've added this script I'm using for some time to
+ CVS now. It's used to finish builds that aborted and that were
+ fixed manually. It runs the binary-arch if no debian/files exists,
+ generates a .changes and sends a mail like sbuild would have done.
+
+1999-09-01 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * {wanna-build,sbuild,buildd}.conf: All config files split into
+ local and shared parts. The shared parts are to be modified by the
+ buildd author only. The local parts contains some variables of
+ local nature (mail addresses etc.) and also can override shared
+ values. To keep compability with the previous names, the local and
+ shared files are named as follows:
+
+ | shared file | local file
+ -------------+-------------------------+----------------------------
+ wanna-build | /etc/wanna-build.conf | /etc/wanna-build.conf.local
+ sbuild | /etc/sbuild.conf | /etc/sbuild.conf.local
+ buildd | /etc/buildd.conf.global | ~buildd/buildd.conf
+
+ Except for buildd.conf, the shared file reads in the local file at
+ the end. For buildd.conf, it's the other way round.
+ On installation the shared files can be replaced without
+ questions, so don't make modifications there. These should go into
+ the local files.
+
+ * buildd: Additionally check /etc/buildd.conf.global for changes
+ and re-read configuration then.
+
+ * buildd-mail, buildd-watcher: Add a special check for error
+ mails: If more than 5 mails from a mailer daemon are received
+ within less than $conf::error_mail_window (default 8 hours),
+ they're not forwarded anymore to avoid possible mail loops. (This
+ happens if the error is about the admin address itself.) The dates
+ of error mails are stored in the new file ~buildd/mail-errormails.
+ buildd-watcher regularily purges old entries from this file and
+ removes it if it becomes empty.
+
+1999-08-27 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: --auto-give-back has a new optional argument giving the
+ user and hostname where to call wanna-build (like $conf::sshcmd of
+ buildd). This is needed that sbuild can do give-backs when there's
+ no local wanna-build. If the argument is missing, wanna-build is
+ called locally. Otherwise, it's used like
+
+ --auto-give-back=[[WBUSER@]USER@]HOST
+
+ An ssh calls is made as USER to HOST, and wanna-build called there
+ with --user=WBUSER. If WBUSER is missing, it's the same as USER.
+ If both are missing, the local username is used for both.
+
+ * buildd: Passes the new --auto-give-back arg to sbuild now if
+ $conf::sshcmd is set. Also correctly uses
+ --user=$conf::wanna_build_user when taking packages.
+
+1999-08-26 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-reply.el: Fixed several problems of alphabetical ordering
+ with buildd-edit-manual-source-deps (C-c C-s). New function
+ buildd-add-sd-map (C-c C-v) for editing sd_map files.
+
+1999-08-23 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: When merging a Packages file, ignore entries for
+ foreign architectures (including Arch: all). This is necessary for
+ merging the Packages from proposed-updates, and this includes
+ .debs for all archs.
+
+ * do-merge-packages: Also merge Packages from proposed-updates.
+
+ * Many changes in sbuild:
+
+ * Support for alternatives in src-deps. This means you can have
+ dependencies like "foo | bar (>= 1.2)". The installation is
+ handled as follows:
+ - If at least one alternative is installed in a matching version,
+ nothing has to be done.
+ - If at least one alternative is installed, but in the wrong
+ version, the first such alternative is tried to upgrade.
+ - If no alternative is installed, the first one is installed.
+ - If all alternatives would need a downgrade to satisfy the
+ version constraint, sbuild bails out that it can't do this.
+ The decision which package to install or upgrade is made before
+ apt is called and can't be changed later (yet). This means that,
+ e.g., if the first alternative is not installable but the second
+ would be, the package fails nevertheless. I might work on this in
+ future...
+
+ * Src-deps can now contain the [ARCH1 !ARCH2] architecture
+ restrictions that will be used soon by the Build-Depends: field in
+ the .dsc. The semantics are: The dependency is ignored if the
+ current build arch isn't mentioned in the list, or if it's
+ mentioned with a '!' prefix. The arch restrictions come after the
+ version relation (if present) and apply to single alternatives.
+ Example:
+
+ foo: svgalib (>= 1.3) [i386 alpha] | svgalib-dummy [!i386 !alpha]
+
+ * sbuild now checks the .dsc file for Build-{Depends,Conflicts}:
+ fields and uses those as src-deps if present. (The *-Indep variants
+ are also used if the --arch-all option is given.) The deps from
+ the .dsc override the ones from /etc/source-dependencies*, as they
+ are considered to be newer and/or more exact. However, there's
+ still a way to add dependencies to packages that come already with
+ src-deps: If you prefix a dep with '&' in the central src-dep
+ file, those deps won't be thrown away.
+
+ * There's a new feature of abbreviations in src-deps. You can
+ define them by
+
+ abbrev FOO = foo, bar, yxy
+
+ anywhere in the source-dependencies-* file. Then FOO can be used
+ just like any other package name. It will be replaced by "foo,
+ bar, yxy" later. Abbrevs can't be used with version relations and
+ inside alternatives.
+
+ * New options: --arch-all (-A) also builds Arch: all packages
+ (uses dpkg-buildpackage -b instead of -B); --add-depends (-a)
+ like -f, but doesn't override existing deps completely but adds to
+ them.
+
+ * sbuild now only parses the src-deps for package which should be
+ compiled. This should reduce the start-up time a bit.
+
+1999-08-10 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Allow section names like non-US/non-free when
+ parsing a quinn-diff. (The section can contain letters, digits, -,
+ and / now.)
+
+ * do-merge-{packages,quinn}: New config var $section, can be more
+ than just main (default is "main non-US/main"). Both scripts now
+ import data from all sections in $section. ftp.uni-erlangen.de
+ provides the necessary quinn-diff data.
+ Both scripts now also print the names of the files downloaded and
+ which will be merged.
+
+ * sbuild: Virtual packages as source dependencies didn't really
+ work yet... An alternative was installed ok, but the following
+ consistency check didn't see the provided package, because it used
+ dpkg --status, and that doesn't know virtual packages.
+ Solution: get_dpkg_status() now directly reads
+ /var/lib/dpkg/status (which should be a bit faster, too) and
+ parses the Provides: fields of installed packages. Provided
+ packages are considered installed for src-dep purposes.
+
+ * sbuild: Make some more messages go to the package log (instead
+ of the sbuild log, where they didn't belong). Also fix the
+ stdout/stderr redirecting when running scripts of special deps.
+
+ * FetchFile.pm: Redirect output of ftpget (lftp code).
+
+ * Buildd.pm: Increase lock_interval from 5s to 15s, giving a total
+ try time of 30m. I've seen cases where a valid lock has been
+ broken because it existed too long. This shouldn't happen.
+
+1999-08-09 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-mail, buildd-uploader: support for non-US packages
+ There's a new dir upload-non-US for packages that should go to the
+ non-US server. buildd-mail automatically recognizes such packages
+ if the .deb sections contain the string "non-US"
+ (case-insensitive). Also packages listed in @conf::non_us_packages
+ are moved to this upload dir.
+ Acknowledged packages are moved to the appropriate upload dir
+ (upload/ or upload-non-US/). When removing a job from the upload
+ dir (either if it has been installed into the archive, or on the
+ upload-rem command), check if the changes file is in upload-non-US
+ and use this dir then.
+ buildd-uploader simply uploads packages from both dirs. The
+ dupload in upload-non-US use a --to option of
+ $conf::dupload_to_non_us (default "non-us"). Please make sure you
+ can upload there without questions for passwords etc.
+
+ * buildd.conf: New config vars $dupload_to_non_us and
+ @non_us_packages.
+
+ * sbuild.conf: Add non-US/main to @do_dists, remove non-us (seems
+ obsolete).
+
+1999-08-04 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: When taking a package, print the warning that
+ previous version failed also if the previous state was
+ Failed-Removed.
+
+ * buildd*: Implemented the collecting of some statistical data.
+ All the figures are written to files in ~/stats, where some script
+ will pick them up. Written files are: taken, builds, uploads,
+ failed, dep-wait, no-build, idle-time, and build-time.
+
+ * buildd-watcher: Implemented statistics summary, which is mailed
+ once per week.
+
+ * buildd.conf: two new config vars: $statistics_period and
+ $statistics_mail.
+
+1999-08-03 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: I hope I've found out now what caused the
+ HASH(0x........): fields which corrupted the databases from time
+ to time. (It is something between unexpected Perl behaviour and a
+ proper bug in perl-5.005.)
+
+1999-07-28 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * rbuilder: Don't use wanna-build anymore at all -- it caused too
+ many problems (If a package was given back, the new version was
+ Needs-Build and the daemon grabbed it, but couldn't download the
+ source; often distribution lines confused users; ...) The code is
+ still there, but deactivated by $main::use_wb = 0.
+
+ * rbuilder: Don't remove the build dir if upload failed or on
+ .changes inconsistencies. Such stuff can probably be fixed
+ manually and the packages doesn't need be rebuilt.
+
+1999-07-27 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd: New config variable $idle_sleep_time (in buildd.conf)
+ instead of hardwired pause of 5 minutes between list-needs-build
+ calls.
+
+ * wanna-build: --info -dist=all created non-existing databases
+ (e.g. for frozen); fixed.
+
+1999-07-23 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Avoid that MTAs add domains to From: or To:
+ addresses: Add the local domain to the to addr if it doesn't
+ contain a domain yet. Also add a explicit From: header, as the MTA
+ might use the plain username otherwise.
+
+ * rbuilder: If the package can't be built or uploaded
+ successfully, give it back so it can be retried later.
+
+1999-07-21 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * FetchFile.pm: Made the FTP downloading (via the Perl-internal
+ Net::FTP module, not lftp) more fault-tolerant. If the received
+ file is smaller than expected, it is retried as long as each try
+ increases the file size.
+
+ * buildd-mail-wrapper now sets the umask to 022, because some
+ mailers seem use 077, which makes the files in ~/upload unreadable
+ for others.
+
+1999-07-09 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * check-security-uploaded: New script; to be run from crontab of
+ wanna-build db admin. It downloads the Packages files from
+ security.debian.org and compares the packages there with the ones
+ that 'rbuilder' is currently building. If such packages are found
+ to be installed, they're set to 'Uploaded' to avoid old-build
+ mails.
+
+ * rbuilder: Now maintains a file "builders" where it stores
+ package names + versions and the email addresses of the people who
+ requested to build those packages. The addresses are used for
+ old-build mails: They're forwarded to the responsible person
+ instead of being rejected because they're not PGP-signed.
+
+1999-06-21 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Also recognize new message "Package file ... is out of
+ sync" from apt-get and run apt-get update.
+
+1999-06-18 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * rbuilder, wanna-build-mail: Both now support also GnuPG for
+ signing the mails.
+ * wanna-build-mail: Now can also parse mails signed with MIME/PGP,
+ like rbuilder.
+
+1999-06-14 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Now packages can also changed from Installed to
+ Installed if a newer version than registered in the database
+ appears in the Packages file. This can happen if someone uploads a
+ package without any wanna-build interaction. Formerly, only the
+ Installed-Version: was updated, but not Version:.
+
+ * wanna-build: Don't use getlogin() anymore for determining the
+ user name; it returns wrong results if su has been used (returns
+ the user you su-ed from).
+
+ * rbuilder: Keep the logs for all requested builds for 100 days in
+ a logs/ subdir.
+
+ * rbuilder: In error replies, quote the mail that caused the
+ error.
+
+ * rbuilder: If taking a package fails, report the wanna-build
+ messages to the user.
+
+ * rbuilder: Fix silly syntax error ($main::wanna-build instead of
+ $main::wanna_build).
+
+1999-06-09 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Fixed some silly errors with global patches: Accept them
+ at all when parsing the source-dependencies file; fix the loop in
+ apply_patches to handle the args consistently; don't let the build
+ fail if a global patch didn't apply.
+
+1999-06-08 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: When reading quinn-diff output, accept empty
+ sections and priorities. (Those are generated by quinn-diff now on
+ packages from proposed-updates.) The empty values are replaced by
+ "unknown".
+
+1999-06-04 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: When giving back a package, don't forget the --dist
+ option to wanna-build.
+
+ * sbuild: Added support for virtual packages in source
+ dependencies. When sbuild tries to install such a package, apt-get
+ will tell it which alternatives exist. One of these is substitued
+ for the virtual package and apt-get will be retried. Which one to
+ use is either determined by %conf::alternatives or more or less by
+ random (first one listed).
+
+1999-06-02 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Removed as many die's as possible. The error exit status
+ caused by die can let buildd retry all jobs of this sbuild run,
+ even if some have already been finished.
+
+ * sbuild: Make sure that we're in the correct directory after a
+ build (some chdir()s in error cases were missing). Also don't use
+ chdir(".."), because we may have followed a symlink; better use
+ the absolute path of the wanted dir.
+
+ * sbuild: If the directory where a package should be unpacked
+ already exists but is a symlink, then complain.
+
+ * sbuild: With --batch write each finished job to a file
+ SBUILD-FINISHED. buildd can look at this file if sbuild crashed
+ for jobs that need not be rebuilt.
+
+ * buildd: If sbuild exited with error status and hasn't dumped
+ unfinished jobs to REDO itself, look for SBUILD-FINISHED to sort
+ out jobs that don't need rebuilding.
+
+ * check-old-builds: Add /usr/local/bin to $PATH if not included yet.
+
+1999-06-01 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-mail: Now creates the upload directory if it didn't exist
+ yet. And if not all files belonging to an upload could be moved
+ there, abort with an error.
+
+ * Removed some more hardcoded paths to binaries in buildd and
+ buildd-watcher.
+
+1999-05-31 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: The du after the build (to get the total space required)
+ is now run under sudo, because under some debian/tmp-like some
+ dirs can not be accessible to normal users.
+
+ * buildd.conf: Added build/{HOLD-BACK,Attic} to $no_warn_pattern.
+
+ * In all scripts remove calls to aliases of wanna-build (the
+ list-needs-build, build-info etc. symlinks) and use wanna-build
+ itself with appropriate option instead. This removes the necessity
+ to have the symlinks; you may still want them for typing
+ convenience...
+
+ * Removed hardcoded /usr/local paths.
+
+ * Removed hardcoded /usr/local/var/debbuild paths from do-merge-*
+ scripts. They extract the database dir now from
+ /etc/wanna-build.conf.
+
+1999-05-26 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Introduced epochs in the database (they were
+ ignored until now); now finally packages that have changed epoch
+ go to state Needs-Build automatically. You're still allowed to
+ omit the epoch in the usual commands (version comparison is fuzzy
+ here) to still be able to pass file names as arguments. However,
+ the input of --merge-packages and --merge-quinn must be exact can
+ contain epochs.
+ Caution #1: If the quinn-diff piped into wanna-build ignores epochs,
+ you need to set $ignore_epochs in /etc/wanna-build.conf.
+ Caution #2: When you install this wanna-build version (1.102) and
+ have an existing database without epochs, run --merge-packages and
+ --merge-quinn once with the --introduce-epochs option.
+ Caution #3: Epochs are also added for currently building packages.
+ buildd-mail might be confused by this if in the subjects of mails
+ the epoch is still missing. You must add the epoch manually in
+ this case.
+ Caution #4: The automatic adding of epochs fails in one case: If
+ someone has taken a version newer than what quinn-diff knows (e.g.
+ a NMU planned) and that version was meant already with the epoch.
+ In this case wanna-build will reset the state to Needs-Build and
+ send you a new-version-while-building notice. Just re-take the
+ package.
+
+ * sbuild: Ignore epochs when fetching source files.
+
+ * sbuild: If dpkg-source -x fails, remove the .tmp-nest dir if
+ it's left over.
+
+ * buildd-mail: Fix extraction of error message again (for replies
+ to should-I-build mails): ignore empty parts between separators.
+
+ * buildd-mail: Ignore epochs when constructing the name of a
+ changes file or the name of a build directory.
+
+ * wanna-build: If you take a package you have already taken, you
+ don't get an error message anymore ("NOT OK; already taken by
+ USER") but a notice instead that you already own the package and
+ an ok.
+
+1999-05-17 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * Fixed /tmp insecurity on wanna-build --manual-edit.
+
+1999-05-04 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: After a build the disk space required for it is
+ determined (final size of build dir before purging + size of
+ generated .deb's) and printed in the package log. Additionally,
+ the figure is stored in a new database avg-build-space, analogous
+ to avg-build-time. However, the calculation of the "average" is
+ different. Since disk space isn't expected to vary as much as
+ times, the last figure seems more important than later ones.
+ Therefore, max. 4 numbers are stored, and when calculating the
+ average, the latest is weighted with factor 4, the next with 3 and
+ so on.
+
+ * avg-pkg-build-time: Can now dump the space db, too, with option
+ --space (-s), or if called as avg-pkg-build-space. Output format
+ for space is a bit different, but adding, dumping, sorting etc.
+ works as usual.
+
+1999-05-03 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: After unpacking a source, run "chmod -R g-s" on it. Some
+ .orig.tar.gz files seems to be packaged with the setgid bit on,
+ which causes the debian dir and its subdirs (e.g. debian/tmp) to
+ be created setgid, too; and later dpkg-deb --build complains about
+ bad permissions.
+
+1999-04-26 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * rbuilder: Now can also parse mails signed with MIME/PGP.
+
+1999-04-21 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: sbuild can now check if certain binaries have been used
+ during a package build (to update the source dependencies). The
+ check just looks for access time of the binary being later than
+ the start of the build, so it could be fooled by parallel
+ activities on the machine. So better check manually...
+ There's a new config var %watches where one can list all the
+ packages and binaries therein to watch for.
+
+1999-03-12 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: New option --force-depends (-f) to override source
+ dependencies of a package, in case they're wrong. The argument
+ should be a line in the same format as used in
+ /etc/source-dependencies.
+
+1999-03-08 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: On --merge-packages, it can happen that multiple
+ versions exist for one source package (if old binary packages
+ from the same source haven't been removed from the archive yet).
+ So skip a package in Packages not always if the same (source)
+ packages has already been processed, but only if also the versions
+ match. That way, all versions will be processed.
+
+ * wanna-build: New option --pretend-avail. The packages given on
+ the command line (versions required) are treated as if they're
+ installed, and packages waiting for them are moved from Dep-Wait
+ to Needs-Build (if other dependencies are available, too, of
+ course).
+ To make implementation of this easier, the Depends: list of a
+ package in state Dep-Wait is now shortened each time a dependency
+ is available.
+
+1999-03-07 James Troup <james@nocrew.org>
+
+ * andrea, merger: new option --architecture (-a) to define the
+ architecture and override the default as set by the output of dpkg
+ --print-architecture.
+
+1999-03-05 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * FetchFile: Bigger redesign of caching: Do not cache all file
+ names anymore, but only directory names. Building a full cache
+ takes some time, specially if for a FTP site. And most of the
+ infos is never needed, specially if compiling only one package.
+ Additionally, the full cache must be cleared rather often after
+ "not found" results, because the mirror might have changed. This
+ isn't necessary if caching dir names, because they're very
+ unlikely to change. The new cache should be a major speedup for
+ single-package builds and/or if several source packages must be
+ fetched from a FTP site.
+
+ * sbuild: No not remove build-progress if not writing it (no
+ --batch).
+
+ * sbuild: New option --purge (-p) to define build dir purge mode
+ on command line.
+
+1999-03-02 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Now implemented source dependency locking. This allows
+ (securely) for multiple sbuild instances at the same time. For
+ each job, a lock file with needed source dependencies is written.
+ Before installing src deps, sbuild checks all those lock files if
+ any of the packages needed by other instances would be touched in
+ any way (removed, installed, updated). In that case, the second
+ instance must wait until all conflicting jobs are finished.
+ Special dependencies usually cause no conflict, except they are
+ marked with a "global { yes }" subsection, which means they
+ influence global system state. At most one package at a time may
+ be built that has such a global dependency.
+
+ * sbuild: Source dependencies that were newly installed for a
+ build are now removed immediately after the build has finished,
+ not anymore at the end of all builds. The old behaviour might be a
+ bit more efficient, but is complex to handle with src-dep locks.
+
+1999-03-01 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * rbuilder: New script for remote building on demand. Accompanying
+ is a rbuilder-wrapper.
+
+ * wanna-build: When merging Packages files, unknown (source)
+ packages aren't ignored anymore. The ignoring was initially done
+ to keep the databases smaller, but by now most packages are
+ included there anyway. So it is now better to have a full
+ database, which also makes the statistics a bit more correct.
+
+1999-02-24 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: New option -n (--nolog): Do not write any log files
+ (package log, build log), but only print to stdout. Also do not
+ send any mails.
+
+ * sbuild: Now a kind of source dependency locking information is
+ maintained: In $conf::srcdep_lock_dir (usually
+ /usr/local/var/debbuild/srcdep-lock) each instance of sbuild puts
+ a file named after its pid and writes all needed packages into
+ that file. Later instances of sbuild can use this information to
+ check if a src-dep conflict exists. (To be implemented...)
+
+1999-02-23 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Now URLs (http:// and ftp://) are allowed as command
+ line parameters; the URL should point to a .dsc file. This feature
+ can be used to build packages in arbitrary locations, not only
+ those listed in @archive_locations. Also no searching is
+ necessary, since the path is given.
+
+1999-02-18 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Fix an old typo in some output text (chnages vs. changes)
+
+1999-02-17 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: --give-back can now change to state Needs-Build
+ from any state (not only Building) with --override.
+
+ * wanna-build: --info and --forget (which take only package names
+ without version) now strip anything following a '_' to avoid
+ confusion.
+
+ * sbuild: With -v don't mail the build log, because the same
+ information is already mailed as the package log. And the
+ additional stuff in the build log (e.g. removing packages) can be
+ see interactively.
+
+ * buildd: New config variable @take_from_dists: buildd will only
+ take new packages from the distributions listed here. This can be
+ used to let one buildd make this distrib, the other buildd the
+ next and so on. If this variable isn't initialized in buildd.conf,
+ it's set to (stable frozen unstable), i.e. all distributions.
+
+1999-02-08 James Troup <james@nocrew.org>
+
+ * andrea is now architecture and distrubtion independent through
+ the use of multiple new format files for source dependency
+ mappings and manually added source dependencies.
+
+1999-02-03 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: If installation of source dependencies (apt-get run)
+ fails, try to reinstall removed packages and to remove newly
+ installed packages. If apt-get failed because some package didn't
+ configure, that package must be removed, otherwise all following
+ apt-get runs will fail because they try to configure that package!
+
+ * check-old-builds: Remove packages from reported-old-builds if
+ they're not in state Building anymore.
+
+ * wanna-build: "new version of ..." mails now include
+ distribution.
+
+ * wanna-build: With --info you can now also use --dist=all (or
+ -da). Then infos are printed for all known distribution.
+
+ * buildd-mail: Handle outdatedness for multiple distributions:
+ - The outdated-packages file now also records distributions in
+ which the package is outdated.
+ - In the check for outdatedness, it can happen that it's
+ outdated in none, some, or all distributions. The none and all
+ cases are the same as before, but in the some case, a modified
+ .changes needs to be sent, or an action cannot be applied
+ automatically.
+ - If the reply is no changes, we have to determine separately
+ to which distributions the packaged is targeted. This is done
+ by checking for state Building of the version in question.
+
+ * buildd-mail: Fixed get_fail_msg for new output format of
+ build-info.
+
+ * buildd-mail: Before moving a package to the upload dir, check if
+ it's really registered for building in all target distributions.
+ If this is not the case, it's an indication of error. Either it
+ should be registered, or some distributions should be deleted.
+
+1999-02-01 James Troup <james@nocrew.org>
+
+ * andrea's merger's ability to strip automatically generated
+ dependencies through the source-dependencies file has been fixed.
+
+1999-02-01 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * avg-pkg-build-time: New option -f (--dbfile) to select the
+ database to consult on the command line.
+
+1999-01-25 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: When parsing dpkg --status output, ignore the help
+ message appended at the end under some circumstances. This should
+ remove the "parse error in dpkg --status: no Package: field" error
+ messages.
+
+1999-01-19 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * check-old-builds now checks all distributions, not only unstable.
+
+1999-01-18 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: When opening the database, handle error EAGAIN:
+ Wait 2 seconds and then try again for max. 30 times. Starting with
+ the switch to GDBM, it often happened that after a lock was gone
+ the database still couldn't be opened (file locking?).
+
+1999-01-13 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: Added --quiet option to patch call to avoid some blurb
+ in the sbuild log file.
+ * do-merge-quinn: Mail the statistics file only if its size is
+ greater than 0.
+ * wanna-build-statistics: If wanna-build --list=all dies with
+ error status, print the last message from it.
+
+1999-01-08 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Don't warn if a package goes Reupload-Wait ->
+ Installed, this is rather normal.
+ * wanna-build now uses GDBM databases (instead of DB hash from
+ glibc), because the former database driver crashed randomly on
+ sufficiently big databases :-(
+
+1998-12-23 James Troup <james@nocrew.org>
+
+ * andrea now handles multi-line dependency information as found,
+ for example, with the sgml-tools package.
+
+1998-12-22 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * Several internal changes in wanna-build: make regexps faster by
+ not using $`,$&,$'; replace ugly $main::var syntax for global
+ variables by $var together with a use vars; made program name and
+ option handling more general by replacing big switches by
+ table-driven approach.
+
+ * wanna-build now by default uses its own comparison function,
+ written in Perl. This should be a great deal faster than forking
+ dpkg --compare-versions all the time. But since this isn't 100%
+ correct (dpkg is the only instance that really knows about version
+ ordering), a new option --correct-compare can be used to force
+ calling dpkg.
+
+1998-12-16 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * New script wanna-build-statistics: Prints some nice statistics
+ about how much packages are in which state, who put them into that
+ state and so on.
+
+ * do-merge-quinn now calls wanna-build-statistics after each merge
+ and sends the results to a mailing list.
+
+ * wanna-build now also allows --uploaded on packages in state
+ Install-Wait without --override.
+
+1998-12-15 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * Little correction to handling of Install-Wait state in
+ wanna-build: If the corresponding package in another distribution
+ changes state to Failed, Dep-Wait, or Needs-Build (on give-back),
+ propagate this state change to the distrib where the Install-Wait
+ is. This is necessary because in those cases, no upload will
+ happen, and thus we could wait endless for an installation.
+
+1998-12-11 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * On --merge-quinn, if a state is copied from another
+ distribution, wanna-build must also set the Package: field,
+ because the entry might not have existed before. This caused
+ (rarely) "entry misses Package: field" errors.
+
+1998-12-08 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * A SBUILD-REDO-DUMPED file is now also removed by buildd if
+ sbuild exits with 0 status (as it does if the shutdown goes ok).
+
+1998-12-07 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd now also catches SIGALRM around the call to
+ get_changelog, which uses FetchFile. This is similar to the recent
+ change to sbuild.
+
+1998-12-04 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * There's a new config variable in buildd.conf:
+ @weak_no_auto_build. Packages listed there are usually not taken
+ and built, just as those in @no_auto_build. However, if there is
+ really nothing to do (in no distribution) and the daemon would go
+ into its idle loop otherwise, it will also take one package that
+ is in @weak_no_auto_build (if one is available). Only one package
+ is taken, because it is assumed that those packages need
+ relatively much time. The consideration behind all this is that
+ it's better to start an expensive build instead of doing nothing.
+
+ * sbuild now also catches SIGALRM and calls its shutdown function
+ if receiving it. In the last time, it happened sometimes that the
+ Net::FTP module triggered some unhandled SIGALRMs after unexpected
+ FTP timeouts. This caused the whole sbuild to crash, and *all*
+ packages were dumped to REDO. Calling shutdown at least will dump
+ only the unfinished ones.
+
+1998-11-26 James Troup <james@nocrew.org>
+
+ * sbuild: sbuild can now use lftp instead of the perl FTP module
+ to download source files; this was necessary for cookie-monster
+ which is on a slow shared modem connection which consitently
+ defeated the perl FTP module. It's optional (see sbuild.conf) and
+ off by default because only machines with slow/poor connections
+ should use it as it's slower than using the perl FTP module due to
+ the overhead of spawning multiple copies of lftp.
+
+ sbuild also now handles the case of an aborted dpkg-source
+ extraction by removing a .orig.tmp-nest directory if it's in the
+ way.
+
+1998-11-25 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd-reupload: New tool to reupload packages, which are
+ already installed, for different distributions.
+
+ * wanna-build: There are now two new states to solve the
+ delayed-frozen-problem. Brief summary: If a (source) package is
+ for (e.g.) frozen and unstable, it will be installed in unstable
+ automatically, but in frozen later after acknowledgement by the
+ release manager. This means that quinn-diff lists the new version
+ for unstable earlier than for frozen, and that a build daemon
+ might pick up the package from unstable. Since the upload cannot
+ immediately go to frozen, too (the new version isn't accepted for
+ this yet), sbuild modifies the .changes so the upload only goes to
+ unstable (see sbuild change yesterday).
+
+ But still the frozen version must be uploaded at some point. This
+ is now handled by wanna-build (but not really completely; a
+ perfect solution would have been rather hard to implement.) If
+ quinn-diff lists a new version of some package, and the same
+ version is Building or Uploaded for some other dist, the new state
+ will be Install-Wait instead of Needs-Build. As soon as
+ --merge-packages detects that that version has been installed,
+ Install-Wait is turned into Reupload-Wait. If the packages was
+ already installed in some dist at the time quinn-diff listed it,
+ it goes to Reupload-Wait immediately. Whenever state Reupload-Wait
+ is entered, the previous builder of the package receives a mail
+ that a reupload for a different distribution is needed.
+ buildd-mail will do this for some cases automatically, but
+ sometimes manual work will be necessary.
+
+ Why this solution isn't perfect: If another unstable version is
+ installed before the previous version is needed for frozen, a
+ recompilation is needed.
+
+ Similar: If a version of a package is Failed or Dep-Wait in some
+ other distrib and now is listed by quinn-diff for some dist, the
+ Failed/Dep-Wait state is copied to the new dist.
+
+ * wanna-build: You can abbreviate the argument to -d or --dist now
+ as 's', 'f', or 'u'.
+
+ * buildd-reply.el: new file with some Emacs functions that make it
+ easier to reply to buildd mails.
+
+1998-11-24 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild: If the changelog of a package wants to upload also for
+ "lower" (stable < frozen < unstable) distributions than the
+ distrib. set on the command line, then delete those distrib.s in
+ the Distribution: field of the resulting .changes, because
+ probably the package hasn't been accepted for stable or frozen,
+ resp., yet. Instead, write a second, modified .changes to
+ SAVED-CHANGES, which lists the omitted distribs in the
+ Distribution: field.
+
+1998-11-21 James Troup <james@nocrew.org>
+
+ * do-merge-quinn: handle temporary files securely and don't
+ hardcode architecture.
+
+1998-11-20 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild, source-dependencies: Special dependencies now can also
+ have 'patch' subsections. These patches are applied when a source
+ tree has been unpacked (and the package depends on the special
+ dependency that has the patch subsection...) This is intended for
+ cases where some packages need to be patched, but the patch isn't
+ be appropriate for the main package. Examples are: ddd (cannot be
+ compiled with -g, because then needs to much memory), gcc (needs
+ -O in CFLAGS if compiled on a '060). For more infos on the patch
+ subsections, see the comment in source-dependencies.
+
+1998-11-19 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build: Implemented a global "maintenance" lock. Purpose:
+ The --merge-{packages,quinn} commands work on each distribution
+ separately, but the data still belong together. Concretely, it
+ happened that a build daemon built some packages twice, because at
+ the time of the first list-needs-build, they were Needs-Build for
+ frozen, but not yet for unstable because the unstable quinn-diff
+ wasn't merged in yet. The maintenance lock can be set or removed
+ by the --create-maintenance-lock or --remove-maintenance-lock
+ options, resp. If such a lock exists, every command except the two
+ merge commands will wait for the lock to disappear.
+ * do-merge-quinn, do-merge-packages: Use maintenance locks.
+ * Whenever sbuild does auto-givebacks, it now logs them into a
+ file SBUILD-GIVE-BACK, together with the current time. This list
+ is used later by buildd: If a give-back hasn't been done at least
+ $conf::delay_after_give_back minutes ago, the package will be
+ ignored in list-needs-build output. buildd also deletes older
+ entries from the file. This is to protect against loops, where
+ buildd takes the same packages over and over, and sbuild only
+ gives them back, for example because they're not on the mirror or
+ the files are damaged.
+
+1998-11-17 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild now tries to open /etc/source-dependencies-DISTRIB before
+ /etc/source-dependencies. This is necessary because andrea will
+ generate distrib-specific dependencies, so we need different files
+ for each dist. The fallback without -DISTRIB appended is for
+ compability.
+
+1998-11-15 Roman Hodek <rnhodek@emilio.informatik.uni-erlangen.de>
+
+ * In source-dependencies, more than one dependency on the same
+ package is now possible. (For example, one with >= relation and
+ the other with <<.) Dependencies of this type can be produced by
+ andrea.
+ * sbuild can now detect cases where a package would have to be
+ downgraded to fulfill a dependency. apt-get (how it is used
+ currently) cannot handle downgrades, it always upgrades to the
+ newest available version. Thus, in such cases a build is aborted.
+
+1998-11-14 James Troup <james@nocrew.org>
+
+ * do-merge-packages now handles temporary files securely, is no
+ longer architecture dependent and refuses to try and merge zero
+ length Packages files.
+
+1998-11-10 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * sbuild now doesn't pass packages that are already installed in a
+ sufficient version to apt-get. This removes the side effect that
+ packages that are source dependencies are always updated to the
+ newest version, which is not always desired.
+ * In wanna-build --list, the sorting order of sections has been
+ changed. They're not anymore sorted alphabetically, but by some
+ kind of importance. This has been done because the build daemons
+ pick up packages in the order listed by wanna-build.
+
+1998-11-09 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * buildd now also checks its config file (~/buildd.conf) for
+ changes and rereads it if the modification time has changed. No
+ extra SIGHUP is needed anymore.
+
+1998-11-06 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * New script check-old-builds, which sends warning mails for
+ packages being in state Building for more than 10 days.
+
+1998-11-05 James Troup <james@nocrew.org>
+
+ * buildd-mail-wrapper now checks the pid in mailer-running files
+ for validity (i.e. if the pid mentioned there really still exists).
+
+1998-11-04 Roman Hodek <rnhodek@faui22c.informatik.uni-erlangen.de>
+
+ * wanna-build does not anymore send out mails for Failed->Failed
+ state changes ("message appended").
+ * sbuild: Fixed the changes-hack-for-frozen (didn't work at all).
+
+# Local Variables:
+# mode: change-log
+# change-log-default-name: "NEWS"
+# End:
diff --git a/README b/README
new file mode 100644
index 0000000..37d33d2
--- /dev/null
+++ b/README
@@ -0,0 +1,54 @@
+Build-essential and friends
+===========================
+
+sbuild-createchroot installs build-essentials so your working
+environment should already be ok. You will only need to add a few more
+packages in the chroot, using sbuild-apt. eg.
+
+ sbuild-apt <chroot> apt-get install sudo fakeroot ssmtp
+
+To show what needs to be added or removed from the unstable chroot you
+can use:
+
+ sbuild-checkpackages --list <chroot>
+
+To add packages:
+
+ sbuild-apt <chroot> apt-get install
+
+or if you wish to remove packages:
+
+ sbuild-apt <chroot> apt-get --purge remove
+
+
+Alternatively, to enter the chroot and work inside it, use:
+
+ sbuild-shell <chroot>
+
+
+With these new tools buildd.chroot has been made obsolete.
+
+
+Possible problems
+=================
+
+ssmtp won't install and debootstrap will fail if it can't
+find the hostname. /etc/hosts not copied over?
+
+You may need to use the latest debootstrap.
+
+You'll have to log in again for you to be in group sbuild.
+
+debconf needs to be set noninteractive:
+$ sbuild-shell <chroot> ( or "sudo chroot /your/chroot /bin/sh" )
+$ dpkg-reconfigure -plow debconf
+ 1. Dialog 2. Readline 3. Gnome 4. Editor 5. Noninteractive
+ What interface should be used for configuring packages? [1] 5
+ 1. critical 2. high 3. medium 4. low
+ Ignore questions with a priority less than.. [3] 1
+ Show all old questions again and again? [no]
+
+md5sum mismatch when building a source package (sbuild --source):
+ - the original tarball has to be in the work directory -
+ $CHROOT/build/<user> - for a sourceful build or a new
+ orig.tar.gz will be created with an incorrect md5sum.
diff --git a/README.buildd-admin b/README.buildd-admin
new file mode 100644
index 0000000..ea7252c
--- /dev/null
+++ b/README.buildd-admin
@@ -0,0 +1,281 @@
+ -*- mode: indented-text -*-
+
+ Overview of buildd Administration
+ =================================
+
+This document should give you a rough idea how to administer a build
+daemon, i.e. basics of how it works, and how to reply to the mails sent
+to you. It does not yet cover configuration of buildd in a proper way,
+sorry.
+
+
+1) Overview of Operation
+========================
+
+The buildd system consists of the following parts:
+
+ buildd: This is the daemon itself. It basically runs in a loop,
+ first requesting some package in state Needs-Build from the
+ wanna-build database and then compiling those packages.
+
+ buildd-mail: This script processes mails directed to buildd, i.e.
+ your answers to build logs etc.
+
+ buildd-uploader: This script uploads finished jobs regularly and is
+ called by cron.
+
+ buildd-watcher: This does some regular maintenance stuff, like
+ archiving log files and finding outdated files so that the disk
+ doesn't fill up with them. It also restarts buildd should it
+ have died for some reason.
+
+1.1) buildd
+-----------
+
+The most important component is of course buildd itself. It runs as a
+daemon, i.e. all the time, no need to start it by cron or the like. It
+usually lowers its priority (config var $nice_level), so that other
+foreground jobs aren't delayed too much.
+
+buildd runs in a permanent loop where it tries to compile packages. To
+do this, it needs to know which packages to build. To determine this, it
+calls wanna-build --list=needs-build for all distributions (in order
+stable -> frozen -> unstable) and if the output is non-empty, it takes
+some of those packages needing rebuilding. You can restrict which
+distributions to look at with the @take_from_dists config var, and how
+many packages to take at a time with $max_build.
+
+Additionally, there is a file called REDO in ~/build, where
+some packages are occasionally dumped to be picked up later by buildd.
+This file is checked before new packages are requested from
+wanna-build. REDO also contains distributions for the packages, and
+here again stable has priority before frozen, which has priority over
+unstable. The rationale is that stable/frozen packages are usually more
+urgent than unstable ones.
+
+If buildd has some packages from wanna-build, it tries to take them for
+building (wanna-build --take). This should usually go ok, but in rare
+cases it can happen that somebody else takes the package in the time
+between listing and taking (race condition). Those packages are ignored.
+Another exception that can happen are packages that failed in the previous
+version. To avoid unnecessary work, buildd doesn't immediately start to build
+those packages but asks its administrator first. This will be discussed in
+greater length in a following section ("Should I build" mails).
+
+If a set of packages are taken (either from wanna-build or from REDO),
+buildd starts an sbuild run to build all the packages. sbuild will send
+log mails to the admin, who will (later) send answers, but this doesn't
+touch buildd anymore. It goes back to the start of the loop and tries
+to take more packages and build them...
+
+Some special notes:
+
+buildd automatically detects if either the config file ~/buildd.conf
+or its binary has been changed while it is running, and it takes
+appropriate action. So there's no need to restart the daemon or send
+it some signal.
+
+You can interrupt a running buildd by sending it a SIGINT or SIGTERM.
+It will then kill a subsidiary sbuild process (if currently running).
+sbuild also has shutdown handling for this, and dumps unfinished jobs
+into the REDO file, so they'll be retried later. buildd blocks these
+signals if they could lead to inconsistencies.
+
+There is also some way to influence which packages buildd takes (or
+more precisely) doesn't take to build. The config variable
+@no_auto_build lists packages that should never be taken by buildd,
+either because auto-building doesn't work or because they need to much
+time on this machine, or ... There is also a @weak_no_auto_build for
+packages that usually are too (time-)expensive to build here, but
+should be taken nevertheless if the daemon would be idle otherwise.
+Packages in @weak_no_auto_build are taken only if there are no other
+packages left in state Needs-Build in any distribution, and also REDO
+is empty.
+
+If sbuild gives back a package to wanna-build (because source couldn't
+be downloaded or the like), buildd will find the SBUILD-GIVEN-BACK
+file created by sbuild and it will remember when those give-backs have
+happened. To avoid trying the same package(s) over and over, such
+given back packages aren't taken immediately again. They have a
+waiting period of $delay_after_give_back minutes.
+
+
+1.2 buildd-mail
+---------------
+
+buildd-mail is the mail processor of the buildd system. It should be
+listed in the user buildd's ~/.forward file, e.g.:
+
+ "|/usr/bin/buildd-mail"
+
+The wrapper is necessary to allow setuid-ness, and also queues up
+incoming mails in ~/mqueue, so that the MTA process can exit and
+doesn't need to wait for buildd-mail to finish.
+
+There's not much to say here about operating buildd-mail, because
+it just receives your answers to log files etc., and this will be
+described in more detail in the next section.
+
+The only (a bit) special thing is that buildd-mail also receives the
+"INSTALLED" mails from dinstall and analyzes them. It deletes all
+files from ~/upload when they're installed in the master archive.
+It also receives the "new version of ..." mails from wanna-build, that
+are sent whenever a new version of a package in state Building
+arrives.
+
+There is also a file ~/mail-blacklist that contains a list of (Perl)
+regular expressions. If a mail arrives from an address that matches
+one of those patterns, the mail is deleted. This is simply to avoid
+spam.
+
+
+1.3 buildd-uploader
+-------------------
+
+buildd-uploader is started by cron a few times per day and uploads all
+finished jobs which are in ~/upload. It simply uses dupload, as you would
+expect. It of course ignores jobs for which an *.upload file already
+exists, i.e. those which already have been uploaded. After uploading,
+it calls wanna-build --uploaded for all the packages it processed.
+
+The --to option of dupload can be set with the $dupload_to config var;
+default is "erlangen". In all other respects, dupload is configured
+with ~/.duploadrc.
+
+In case of upload errors it sends a log mail to the admin, who has to
+fix the situation manually. However, errors should be seldom. The
+*.upload file of a failed job is deleted, so it's retried as a whole
+in the next run.
+
+
+1.4 buildd-watcher
+------------------
+
+buildd-watcher performs some regular maintenance tasks:
+
+ - If checks if buildd is running and restarts it if not. This is
+ meant as a safeguard against spuriously crashing daemons, no manual
+ intervention is required to restart it.
+
+ If you don't really need to have a daemon running, create a file
+ NO-DAEMON-PLEASE in the daemon's home dir. Then buildd-watcher
+ won't restart buildd.
+
+ - It purges build directories that are no longer needed. This would
+ be actually the job of buildd-mail, but since the latter is started
+ setuid, it can't use sudo and the build dirs can contain root-owned
+ files. So buildd-mail just appends the name of a directory to
+ delete to ~/build/PURGE, and buildd-watcher reads that file and
+ removes all the directories.
+
+ - It checks for old files in the build/ and upload/ directories, so
+ that no forgotten files can fill up the disk over time. Files are
+ considered "old" if they're older than $warning_age days. There's
+ additionally a Perl regexp $no_warn_pattern. buildd-watcher doesn't
+ warn about files matching this pattern. So files expected to be
+ there should be included in it, like build/buildd.pid, build/REDO,
+ ...
+
+ - It archives old log files into ~/old-logs/ and rotates the main log
+ file ~/daemon.log. Files to be archived are:
+
+ - package logs: logs/* -> old-logs/plog-<DATE>.tar.gz
+ - sbuild logs: build/build*.log -> old-logs/blog-<DATE>.tar.gz
+ - daemon logs: old-logs/daemon-*.log.gz -> old-logs/dlog-<DATE>.tar.gz
+
+ Additionally daemon.log is renamed to daemon.log.old, and SIGHUP is
+ sent to buildd to open the new log file. If a daemon.log.old already
+ exists, it's compressed and moved to old-logs/daemon-<DATE>.log.gz.
+
+ There are several config vars that control long to keep the various
+ log files until they're archived: $pkg_log_keep, $build_log_keep,
+ $daemon_log_rotate, and $daemon_log_keep. All are measured in days.
+
+
+2) Replying to Mails
+====================
+
+You'll receive lots of mail from buildd and its friends, most of it
+build logs. But there are also some other kinds of mail: "Should I
+build?" from buildd, dupload errors, and warnings about old files. The
+latter two are not complicated and are obvious to handle. But the former
+two require that you reply in a certain way.
+
+To make daily administration easier, there is a set of Emacs Lisp
+functions (for Rmail and Gnus) that allow you to send most answers
+with two keystrokes. The library is called buildd-reply.el and can be
+found in the wanna-build Git archive, where other buildd scripts reside.
+The keystrokes in Emacs are given in square brackets.
+
+2.1) Build Logs
+---------------
+
+All build log replies are recognized by their subject. It should be
+of the form
+
+ Re: Log for (successful|failed) build of PACKAGE_VERSION (dist=DIST)
+
+The subject line provides buildd-mail with a lot of needed info: The
+package name, version, and for which distribution it was compiled. The
+successful/failed information is ignored.
+
+The body of the reply mail is in most cases only a single keyword,
+sometimes with arguments. Despite this, there should be no excess blank
+lines or similar.
+
+
+2.1.1 Successful [C-c C-o]
+................
+
+This is the most often used reply, and also different from the others
+because it does not consist of a single keyword. Instead, you send
+back a signed version of the .changes file of the package. The
+.changes can be found at the end of the build log. The Emacs function
+automatically extracts the .changes from the log and signs it (with
+mailcrypt). If you do not Emacs, you have to cut the .changes manually
+somehow (depending on the mailer) and invoke some kind of PGP backend.
+
+buildd-mail recognizes "ok" mails by the "--- BEGIN PGP SIGNED MESSAGE"
+line. If such a mail is received, it first checks that the package
+isn't outdated.
+
+
+2.1.2 retry [C-c C-r]
+...........
+
+2.1.3 fail [C-c C-f]
+..........
+
+2.1.4 dep-wait [C-c C-d]
+.............
+
+2.1.5 giveback [C-c M-g]
+..............
+
+2.1.6 manual [C-c C-m]
+............
+
+2.1.7 newvers [C-c C-n]
+.............
+
+2.1.8 not-for-us [C-c M-n]
+................
+
+2.1.9 upload-remove [C-c C-]
+...................
+
+2.1.10 purge [C-c C-p]
+............
+
+
+2.2 Other Emacs Actions
+-----------------------
+
+"\C-c\C-s" 'buildd-edit-manual-source-deps
+"\C-c\C-b" 'buildd-bug
+"\C-c\C-a" 'buildd-bug-ack-append
+
+
+3) Log Files
+============
+
diff --git a/README.chroot-building b/README.chroot-building
new file mode 100644
index 0000000..0c55d1d
--- /dev/null
+++ b/README.chroot-building
@@ -0,0 +1,93 @@
+ -*- mode: indented-text -*-
+
+
+ Building in chroot environments with sbuild
+ -------------------------------------------
+
+ Roman Hodek, June 7th 2000
+
+
+Starting with revision 1.133, sbuild can build packages also in a
+chroot environment. (You also need buildd-addpkg >= 1.5). There are
+different such environments for each distribution. The advantages of
+this are:
+
+ - You can build for different distributions on the same machine
+ without headaches about shared library version or the like.
+
+ - Badly programmed build procedures can't modify files on the build
+ host.
+
+ - The access time check for binaries used without a source dependency
+ becomes a bit more reliable, because normal user activities can't
+ cause an atime change.
+
+It works like in the following outline:
+
+ - For each distribution (stable, frozen, unstable), there's a
+ separate installation in (e.g.) /usr/local/chroot/DIST. (This path
+ isn't hardwired, but used in this documentation.)
+
+ - The chroot environment can be prepared with buildd-setup-chroot.
+
+ - If in the build directory a symlink chroot-DIST exists (and the
+ build is for DIST), then sbuild will unpack the sources in
+ chroot-DIST/build/USERNAME and build the package there.
+ dpkg-buildpackage will be called under chroot, so that the whole
+ build process can't access files outside the build root.
+
+ - All apt and dpkg and similar operations are also chroot-ed, so only
+ the chroot environment for that distribution is affected. That way,
+ different shared lib versions can coexist on the same machine and
+ the like. (It also reduces waiting time for parallel builds: If
+ they're for different distributions, installations can happen at
+ the same time and don't need to wait for each other.)
+
+Ok, now a bit more slowly:
+
+Best you prepare your chroot environments with the script
+buildd-make-chroot. It does lots of things automatically. However,
+manual fixes may be required. buildd-make-chroot is usually called
+as:
+
+ sudo buildd-make-chroot buildd DIST chroot-DIST http://optional.nearby.mirror/debian
+
+This will call debootstrap 0.3.2 or higher to create a new chroot, and then set
+it up for the buildd user specified.
+
+Ok, after the chroot environments for all the distributions you want
+are set up, you can start building in them. sbuild recognizes the
+chroot environments by the existance of a chroot-DIST symlink in its
+build directory (the dir where sbuild is started). If such a link
+doesn't exist for the requested distribution, a normal build in the
+host filesystem is performed like you're used to.
+
+If, however, the symlink exists, it should point to the root of the
+chroot environment. Sources are unpacked in
+chroot-DIST/build/USERNAME. (The username is appended so that multiple
+users can utilize the chroot environment at the same time without
+mixing the source trees.) All checks for packages and package
+installations are performed --of course-- in the chroot. After
+building, the .debs and the .changes file are moved back to the normal
+build directory. (If a build fails, the source tree isn't moved back
+but remains in chroot-DIST/build/USERNAME.)
+
+One point still worth mentioning is how the AddPkg directories are
+handled. Those directories contain freshly built packages that might
+be needed for building other packages before they're installed in the
+archive. Since revision 1.5 of buildd-addpkg, the AddPkg packages are
+separated by distribution, which is necessary to export them
+selectivly into the chroot environments. For example, in the frozen
+chroot, only AddPkg/stable and AddPkg/frozen will be mounted but not
+AddPkg/unstable, because unstable packages may not be used for
+building frozen packages. Likewise the stable chroot mounts only
+AddPkg/stable, and the unstable one mounts all three.
+
+The access from inside the chroot environments to the AddPkg packages
+works over the local NFS mounts described above. The appropriate deb
+lines in sources.list should have been created by buildd-setup-chroot.
+
+The installation into an AddPkg directory by sbuild and buildd-addpkg
+is done from outside the chroot environment.
+
+
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..90d0453
--- /dev/null
+++ b/TODO
@@ -0,0 +1,121 @@
+Check packages in the alternatives and stalled_pkg_timeout lists in
+the configuration file and Conf.pm. Make sure they are current and
+valid.
+
+sbuild-createchroot should import most of buildd-make-chroot.
+(note: only needed for buildd use--accounts and passwords needed for access)
+
+Chroot.pm: Move chroot naming out of begin_session.
+
+Are 'Build Start Time' and 'Pkg Start Time' different?
+
+sbuild-setup references ~/build, even though not required.
+
+sbuild-setup should document all files copied by schroot setup scripts.
+
+bin/*: Add --help and --version
+
+Add a --verbose option to all commands
+
+All programs must use Getopt::Long for options parsing
+
+sbuild-stats option formatting and option processing perform more than
+one action at once? Only set mode once? actions have multiple args?
+
+sbuild-stats only has a single database. There's no distinguishing
+between different distributions or architectures. Fix with wannab.
+
+buildd-clean-upload hard-codes $HOME/upload. Configure location.
+
+All perl programs and modules should
+- use strict
+- use warnings
+- use function prototypes
+
+buildd hard-codes config file locations. Used in check_reread_config.
+
+buildd hard-codes main::HOME and PATH. Set in ::Conf.
+
+buildd-watcher should use strftime in place of own functions
+
+sbuild utilities should allow specification of chroot architecture
+
+Document and add examples for %mailto.
+
+sbuild-adduser should do schroot/sudo-specific setup depending on
+$chroot_mode. Now done in basesetup(). But, it may be useful to
+do additional setup here such as user/group database updates for
+sudo chroots and schroot when using plain chroot type (no
+automated setup).
+
+Use 'dcmd cp' to copy build files
+
+Copyright details of new files.
+
+Validate programs and paths only if required
+- programs which only work inside the chroot
+- programs which only work outside the chroot (if required by config)
+- programs which may or may not be required inside and/or outside
+
+Where did the 0.57.7 debian/changelog go to?
+
+buildd-mail: Why do we use lockfiles rather than real fcntl locks?
+
+Move locking functions out of Sbuild::Build. Use proper locking in
+place of lockfiles.
+
+Sbuild::Base: Add append method to simplify set(get() . ) idiom.
+
+buildd auto-restart isn't useful due to loss of @INC and failure to
+detect changes in modules. Create init script instead.
+
+buildd-mail: %header (to|from|subject|message-id|date) are mandatory.
+Check for these and bail out if not present.
+
+Buildd::Conf: Add Sbuild::Chroot heirarchy-specific configuration
+ keys.
+- split into separate initialisation function,
+- split wanna-build config into separate function also.
+- may be useful to have different config files here or else
+ we get parsing errors if variables aren't defined in the
+ parsing sandbox.
+
+buildd: shutdown_* should check if we are a daemon before removing
+the pidfile, as for END.
+
+END blocks should be inside object destructors.
+
+buildd-vlog: Rather than tailing the output of sbuild logs, once
+buildd uses Sbuild::Build directly, the daemon will be able to
+do its own output (if desired).
+
+buildd-mail: Moves built packages directly into the chroot
+ /var/cache/apt/archives. It should use Sbuild::Chroot.
+ This should also update the source chroot.
+
+buildd: SIGHUP should reread config, not SIGUSR1. Swap?
+
+buildd mails: Statistics start from 01-01-1970 when first started up.
+
+Buildd::*: Don't set Host until after object construction, since
+ the Log Stream isn't yet initialised. If log stream opening
+ is moved into Buildd::Base, this can be done automatically.
+
+bin/*, lib/*: Kill use of main:: globals
+
+Kill main::HOME hardcoding
+
+Kill PATH hardcoding
+
+Kill LOG/PLOG
+
+Kill all globals
+
+lib/Buildd: Move logger vfunc into Sbuild::Log as the default logger?
+
+Generalise chroot setup logic in Build.pm and Chroot.pm
+Put all APT and debconf configuration into sbuild.conf.
+
+Documentation improvements:
+
+- setup of cloned chroots (source-*)
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 $@
diff --git a/configs/Makefile.am b/configs/Makefile.am
new file mode 100644
index 0000000..0b23fcb
--- /dev/null
+++ b/configs/Makefile.am
@@ -0,0 +1,33 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2007 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
+
+exampleconfdir = $(docdir)/configs
+exampleconf_DATA = \
+ README \
+ ref-oldstable \
+ ref-stable \
+ ref-testing \
+ ref-unstable
+
+EXTRA_DIST = \
+ $(exampleconf_DATA) \ No newline at end of file
diff --git a/configs/README b/configs/README
new file mode 100644
index 0000000..0d9c0be
--- /dev/null
+++ b/configs/README
@@ -0,0 +1,18 @@
+The ref-* files are used by checkchroot to find unwanted packages in the chroots.
+
+# cp ref-* /usr/local/chroot/
+
+ - stable-old is potato
+ - stable is woody
+ - different for diff arches
+ - you can basically get the list using something like:
+
+ grep-available -F priority -e -s package "(required|standard)" $CHROOT/var/lib/dpkg/available| \
+ awk '{print $2}'|sort
+
+ - the only extras should be:
+ apt
+ build-essential
+ fakeroot (some arches only use sudo and change $fakeroot config item)
+ ssmtp
+ sudo
diff --git a/configs/ref-oldstable b/configs/ref-oldstable
new file mode 100644
index 0000000..5cdd8f7
--- /dev/null
+++ b/configs/ref-oldstable
@@ -0,0 +1,58 @@
+apt
+base-files
+base-passwd
+bash
+binutils
+bsdutils
+build-essential
+cpio
+cpp
+debianutils
+diff
+dpkg
+dpkg-dev
+e2fsprogs
+fakeroot
+fileutils
+findutils
+g++
+gcc
+grep
+gzip
+hostname
+ldso
+libc6
+libc6-dev
+libdb2
+libgdbmg1
+libncurses5
+libpam-modules
+libpam-runtime
+libpam0g
+libreadline4
+libstdc++2.10
+libstdc++2.10-dev
+login
+make
+makedev
+mawk
+modutils
+mount
+ncurses-base
+ncurses-bin
+passwd
+patch
+perl-5.005
+perl-5.005-base
+perl-base
+procps
+sed
+shellutils
+slang1
+ssmtp
+sudo
+sysvinit
+tar
+textutils
+update
+util-linux
diff --git a/configs/ref-stable b/configs/ref-stable
new file mode 100644
index 0000000..31bd023
--- /dev/null
+++ b/configs/ref-stable
@@ -0,0 +1,63 @@
+apt
+base-files
+base-passwd
+bash
+binutils
+bsdutils
+build-essential
+cpio
+cpp
+cpp-2.95
+debconf
+debianutils
+diff
+dpkg
+dpkg-dev
+e2fsprogs
+fakeroot
+fileutils
+findutils
+g++
+g++-2.95
+gcc
+gcc-2.95
+grep
+gzip
+hostname
+libc6
+libc6-dev
+libcap1
+libdb2
+libdb3
+libgdbmg1
+libncurses5
+libpam-modules
+libpam-runtime
+libpam0g
+libperl5.6
+libreadline4
+libstdc++2.10-dev
+libstdc++2.10-glibc2.2
+login
+make
+makedev
+mawk
+modutils
+mount
+ncurses-base
+ncurses-bin
+passwd
+patch
+perl
+perl-base
+perl-modules
+procps
+sed
+shellutils
+slang1
+ssmtp
+sudo
+sysvinit
+tar
+textutils
+util-linux
diff --git a/configs/ref-testing b/configs/ref-testing
new file mode 100644
index 0000000..71a0eb3
--- /dev/null
+++ b/configs/ref-testing
@@ -0,0 +1,76 @@
+apt
+base-files
+base-passwd
+bash
+binutils
+bsdutils
+build-essential
+coreutils
+cpio
+cpp
+cpp-3.3
+debianutils
+diff
+dpkg
+dpkg-dev
+dselect
+e2fslibs
+e2fsprogs
+fakeroot
+file
+findutils
+g++
+g++-3.3
+gcc
+gcc-3.3
+gcc-3.3-base
+gettext-base
+grep
+gzip
+hostname
+initscripts
+libacl1
+libattr1
+libblkid1
+libc6
+libc6-dev
+libcap1
+libcomerr2
+libdb1-compat
+libdb3
+libdb4.2
+libgcc1
+libgdbm3
+libmagic1
+libncurses5
+libpam0g
+libpam-modules
+libpam-runtime
+libss2
+libssl0.9.7
+libstdc++5
+libstdc++5-3.3-dev
+libuuid1
+linux-kernel-headers
+login
+make
+makedev
+mawk
+mount
+ncurses-base
+ncurses-bin
+passwd
+patch
+perl
+perl-base
+perl-modules
+sed
+slang1
+slang1a-utf8
+ssmtp
+sudo
+sysvinit
+sysv-rc
+tar
+util-linux
+zlib1g
diff --git a/configs/ref-unstable b/configs/ref-unstable
new file mode 100644
index 0000000..29f70d8
--- /dev/null
+++ b/configs/ref-unstable
@@ -0,0 +1,80 @@
+apt
+base-files
+base-passwd
+bash
+binutils
+bison
+bsdutils
+build-essential
+coreutils
+cpp
+cpp-3.3
+debianutils
+diff
+dpkg
+dpkg-dev
+dselect
+e2fslibs
+e2fsprogs
+fakeroot
+file
+findutils
+flex
+g++
+g++-3.3
+gcc
+gcc-3.3
+gcc-3.3-base
+gettext-base
+grep
+gzip
+hostname
+initscripts
+libacl1
+libattr1
+libblkid1
+libc6
+libc6-dev
+libcap1
+libcomerr2
+libdb1-compat
+libdb3
+libdb4.2
+libgcc1
+libgdbm3
+libgpmg1
+liblocale-gettext-perl
+libmagic1
+libncurses5
+libpam0g
+libpam-modules
+libpam-runtime
+libss2
+libssl0.9.7
+libstdc++5
+libstdc++5-3.3-dev
+libuuid1
+linux-kernel-headers
+login
+m4
+make
+makedev
+mawk
+mount
+ncurses-base
+ncurses-bin
+passwd
+patch
+perl
+perl-base
+perl-modules
+sed
+slang1
+slang1a-utf8
+ssmtp
+sudo
+sysvinit
+sysv-rc
+tar
+util-linux
+zlib1g
diff --git a/configure.ac b/configure.ac
new file mode 100644
index 0000000..0d4e047
--- /dev/null
+++ b/configure.ac
@@ -0,0 +1,340 @@
+# -*- Autoconf -*-
+# Copyright © 2004-2009 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/>.
+#
+#####################################################################
+#
+# Process this file with autoconf to produce a configure script.
+#
+#####################################################################
+dnl m4 magic from Eric Blake <ebb9@byu.net>, prior to automake inclusion
+m4_define([sbuild_m4_chomp],
+[m4_format([[%.*s]], m4_bregexp(m4_translit([$1], [
+]]m4_dquote(_m4_defn([m4_cr_all]))[, [/]]m4_format([%255s], [])[),
+ [/*$]), [$1])])
+m4_define([sbuild_m4_esyscmd_s],
+[sbuild_m4_chomp(m4_esyscmd([$1]))])
+AC_PREREQ(2.59)
+dnl Quoting the first argument results in a bizarrely corrupted package tarname
+AC_INIT(sbuild_m4_esyscmd_s([dpkg-parsechangelog --show-field Source]),
+ sbuild_m4_esyscmd_s([dpkg-parsechangelog --show-field Version]),
+ [buildd-tools-devel@lists.alioth.debian.org])
+dnl For safety, check we are in the right directory by
+dnl checking for a known unique file.
+AC_CONFIG_SRCDIR([bin/sbuild-createchroot])
+dnl Place auxilliary scripts here.
+AC_CONFIG_AUX_DIR([scripts])
+dnl Put macro definitions here.
+AC_CONFIG_HEADER([config.h])
+
+AC_COPYRIGHT(
+[
+Copyright © 2004-2008 Roger Leigh <rleigh@debian.org>
+
+Parts of this configure script come from the 'configure.ac' file, and
+are not based on freely-redistributable m4 macros; 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.
+
+This software 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/>.
+])
+
+dnl Initialise automake stuff.
+AM_INIT_AUTOMAKE([1.10 gnu check-news tar-pax foreign])
+m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
+RELEASE_DATE='sbuild_m4_esyscmd_s(date --utc --date=@'sbuild_m4_esyscmd_s([dpkg-parsechangelog --show-field Timestamp])' '+%d %B %Y')'
+RELEASE_DATE_S='sbuild_m4_esyscmd_s(date --utc --date=@'sbuild_m4_esyscmd_s([dpkg-parsechangelog --show-field Timestamp])' '+%s')'
+AC_DEFINE_UNQUOTED([RELEASE_DATE], ["$RELEASE_DATE_S"], [Package release date.])
+AC_SUBST([RELEASE_DATE])
+
+AC_MSG_CHECKING([whether to enable debugging messages])
+AC_ARG_ENABLE([debug], [AS_HELP_STRING([--enable-debug], [Enable debugging messages])],
+ [ case "${enableval}" in
+ yes) enable_debug="yes" ;;
+ no) enable_debug="no" ;;
+ unset) enable_debug="no" ;;
+ *) AC_MSG_RESULT([unknown])
+ AC_MSG_ERROR([bad value ${enableval} for --enable-debug]) ;;
+ esac ],
+ [ enable_debug="no" ])
+AC_MSG_RESULT([$enable_debug])
+if test "$enable_debug" = "yes"; then
+ AC_DEFINE_UNQUOTED([SBUILD_DEBUG], [1], [Enable debugging])
+fi
+
+AC_MSG_CHECKING([whether to enable compatibility features])
+AC_ARG_ENABLE([compat], [AS_HELP_STRING([--enable-compat], [Enable compatibililty features, for building on older Debian stable releases])],
+ [ case "${enableval}" in
+ yes) enable_compat="yes" ;;
+ no) enable_compat="no" ;;
+ unset) enable_compat="no" ;;
+ *) AC_MSG_RESULT([unknown])
+ AC_MSG_ERROR([bad value ${enableval} for --enable-compat]) ;;
+ esac ],
+ [ enable_compat="no" ])
+AC_MSG_RESULT([$enable_compat])
+compat=0
+if test "$enable_compat" = "yes"; then
+ compat=1
+fi
+AC_DEFINE_UNQUOTED([SBUILD_COMPAT], [$compat], [Enable compatibility features])
+AC_SUBST([SBUILD_COMPAT], [$compat])
+AM_CONDITIONAL([SBUILD_COMPAT], [test "$enable_compat" = "yes"])
+
+AC_MSG_CHECKING([whether to enable compatibility features])
+AC_ARG_ENABLE([chroot_checks], [AS_HELP_STRING([--enable-chroot-checks], [Enable additional chroot checks in testsuite (requires "unstable" chroot)])],
+ [ case "${enableval}" in
+ yes) enable_chroot_checks="yes" ;;
+ no) enable_chroot_checks="no" ;;
+ unset) enable_chroot_checks="no" ;;
+ *) AC_MSG_RESULT([unknown])
+ AC_MSG_ERROR([bad value ${enableval} for --enable-chroot-checks]) ;;
+ esac ],
+ [ enable_chroot_checks="no" ])
+AC_MSG_RESULT([$enable_chroot_checks])
+chroot_checks=0
+if test "$enable_chroot_checks" = "yes"; then
+ chroot_checks=1
+fi
+AC_DEFINE_UNQUOTED([SBUILD_CHROOT_CHECKS], [$chroot_checks], [Enable additional chroot checks])
+AC_SUBST([SBUILD_CHROOT_CHECKS], [$chroot_checks])
+AM_CONDITIONAL([SBUILD_CHROOT_CHECKS], [test "$enable_chroot_checks" = "yes"])
+
+# Checks for programs.
+AC_PROG_CXX
+AC_LANG([C++])
+
+# Check for host platform
+AC_CANONICAL_HOST
+AC_MSG_CHECKING([for supported host platform type])
+PLATFORM="generic"
+case $host_os in
+ linux*):
+ PLATFORM="linux";;
+ freebsd* | k*bsd*-gnu) :
+ PLATFORM="freebsd";;
+esac
+AC_MSG_RESULT([$PLATFORM])
+AC_SUBST([PLATFORM], [$PLATFORM])
+
+AM_CONDITIONAL([PLATFORM_GENERIC], [test "$PLATFORM" = "generic"])
+AM_CONDITIONAL([PLATFORM_LINUX], [test "$PLATFORM" = "linux"])
+AM_CONDITIONAL([PLATFORM_FREEBSD], [test "$PLATFORM" = "freebsd"])
+
+AH_TEMPLATE(SBUILD_PLATFORM, [Platform type, used to modify run-time platform-specific behaviour])
+AC_DEFINE_UNQUOTED(SBUILD_PLATFORM, ["$PLATFORM"])
+
+AH_TEMPLATE(SBUILD_HOST, [Host GNU architecture triplet])
+AH_TEMPLATE(SBUILD_HOST_OS, [Host OS])
+AH_TEMPLATE(SBUILD_HOST_VENDOR, [Host vendor])
+AH_TEMPLATE(SBUILD_HOST_CPU, [Host CPU])
+AC_DEFINE_UNQUOTED(SBUILD_HOST, ["$host"])
+AC_DEFINE_UNQUOTED(SBUILD_HOST_OS, ["$host_os"])
+AC_DEFINE_UNQUOTED(SBUILD_HOST_VENDOR, ["$host_vendor"])
+AC_DEFINE_UNQUOTED(SBUILD_HOST_CPU, ["$host_cpu"])
+
+dnl Set PACKAGE_LOCALE_DIR in config.h
+AH_TEMPLATE(PACKAGE_LOCALE_DIR, [Package locale directory])
+if test "x${prefix}" = "xNONE"; then
+ AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, ["${ac_default_prefix}/share/locale"])
+else
+ AC_DEFINE_UNQUOTED(PACKAGE_LOCALE_DIR, ["${prefix}/share/locale"])
+fi
+
+dnl Set PACKAGE_DATA_DIR in config.h.
+AH_TEMPLATE(PACKAGE_DATA_DIR, [Package data directory])
+if test "x${datadir}" = 'x${prefix}/share' || test "x${datadir}" = 'x${datarootdir}'; then
+ if test "x${prefix}" = "xNONE"; then
+ PACKAGE_DATA_DIR="${ac_default_prefix}/share/${PACKAGE}"
+ else
+ PACKAGE_DATA_DIR="${prefix}/share/${PACKAGE}"
+ fi
+else
+ PACKAGE_DATA_DIR="${datadir}/${PACKAGE}"
+fi
+
+AC_DEFINE_UNQUOTED(PACKAGE_DATA_DIR, "${PACKAGE_DATA_DIR}")
+
+PERL_MODULE_DIR="${datadir}/perl5"
+AC_SUBST(PERL_MODULE_DIR)
+
+dnl Set PACKAGE_LIB_DIR in config.h.
+AH_TEMPLATE(PACKAGE_LIB_DIR, [Package lib directory])
+if test "x${libdir}" = 'x${exec_prefix}/lib'; then
+ if test "x${exec_prefix}" = "xNONE"; then
+ if test "x${prefix}" = "xNONE"; then
+ PACKAGE_LIB_DIR="${ac_default_prefix}/lib/${PACKAGE}"
+ else
+ PACKAGE_LIB_DIR="${prefix}/lib/${PACKAGE}"
+ fi
+ else
+ PACKAGE_LIB_DIR="${exec_prefix}/lib/${PACKAGE}"
+ fi
+else
+ PACKAGE_LIB_DIR="${libdir}/${PACKAGE}"
+fi
+
+dnl Set PACKAGE_LIBEXEC_DIR in config.h.
+AH_TEMPLATE(PACKAGE_LIBEXEC_DIR, [Package libexec directory])
+if test "x${libexecdir}" = 'x${exec_prefix}/libexec'; then
+ if test "x${exec_prefix}" = "xNONE"; then
+ if test "x${prefix}" = "xNONE"; then
+ PACKAGE_LIBEXEC_DIR="${ac_default_prefix}/libexec"
+ else
+ PACKAGE_LIBEXEC_DIR="${prefix}/libexec"
+ fi
+ else
+ PACKAGE_LIBEXEC_DIR="${exec_prefix}/libexec"
+ fi
+else
+ PACKAGE_LIBEXEC_DIR="${libexecdir}"
+fi
+
+SBUILD_LIBEXEC_DIR="${PACKAGE_LIBEXEC_DIR}/sbuild"
+AC_SUBST(SBUILD_LIBEXEC_DIR)
+AH_TEMPLATE(SBUILD_LIBEXEC_DIR, [Package libexec directory])
+AC_DEFINE_UNQUOTED(SBUILD_LIBEXEC_DIR, ["$SBUILD_LIBEXEC_DIR"])
+
+dnl Set PACKAGE_LOCALSTATE_DIR in config.h.
+AH_TEMPLATE(PACKAGE_LOCALSTATE_DIR, [Package localstate directory])
+if test "x${localstatedir}" = 'x${prefix}/var'; then
+ if test "x${prefix}" = "xNONE"; then
+ if test "x${prefix}" = "xNONE"; then
+ PACKAGE_LOCALSTATE_DIR="${ac_default_prefix}/var"
+ else
+ PACKAGE_LOCALSTATE_DIR="${prefix}/var"
+ fi
+ else
+ PACKAGE_LOCALSTATE_DIR="${prefix}/var"
+ fi
+else
+ PACKAGE_LOCALSTATE_DIR="${localstatedir}"
+fi
+
+dnl Set SBUILD_SYSCONF_DIR in config.h.
+AH_TEMPLATE(SBUILD_SYSCONF_DIR, [Package system configuration directory])
+if test "x${sysconfdir}" = 'x${prefix}/etc'; then
+ if test "x${prefix}" = "xNONE"; then
+ SBUILD_SYSCONF_DIR="${ac_default_prefix}/etc"
+ else
+ SBUILD_SYSCONF_DIR="${prefix}/etc"
+ fi
+else
+ SBUILD_SYSCONF_DIR="${sysconfdir}"
+fi
+SBUILD_SYSCONF_DIR="${SBUILD_SYSCONF_DIR}/sbuild"
+AC_DEFINE_UNQUOTED([SBUILD_SYSCONF_DIR], ["$SBUILD_SYSCONF_DIR"])
+AC_SUBST([SBUILD_SYSCONF_DIR])
+
+SBUILD_CONF="${SBUILD_SYSCONF_DIR}/sbuild.conf"
+AC_SUBST([SBUILD_CONF])
+
+AH_TEMPLATE(SBUILD_CONF, [sbuild config file path])
+AC_DEFINE_UNQUOTED(SBUILD_CONF, ["$SBUILD_CONF"])
+
+dnl Set BUILDD_SYSCONF_DIR in config.h.
+AH_TEMPLATE(BUILDD_SYSCONF_DIR, [buildd system configuration directory])
+if test "x${sysconfdir}" = 'x${prefix}/etc'; then
+ if test "x${prefix}" = "xNONE"; then
+ BUILDD_SYSCONF_DIR="${ac_default_prefix}/etc"
+ else
+ BUILDD_SYSCONF_DIR="${prefix}/etc"
+ fi
+else
+ BUILDD_SYSCONF_DIR="${sysconfdir}"
+fi
+BUILDD_SYSCONF_DIR="${BUILDD_SYSCONF_DIR}/buildd"
+AC_DEFINE_UNQUOTED([BUILDD_SYSCONF_DIR], ["$BUILDD_SYSCONF_DIR"])
+AC_SUBST([BUILDD_SYSCONF_DIR])
+
+BUILDD_CONF="${BUILDD_SYSCONF_DIR}/buildd.conf"
+AC_SUBST([BUILDD_CONF])
+
+dnl Set SCHROOT_SYSCONF_DIR in config.h.
+AH_TEMPLATE(SCHROOT_SYSCONF_DIR, [schroot system configuration directory])
+if test "x${sysconfdir}" = 'x${prefix}/etc'; then
+ if test "x${prefix}" = "xNONE"; then
+ SCHROOT_SYSCONF_DIR="${ac_default_prefix}/etc"
+ else
+ SCHROOT_SYSCONF_DIR="${prefix}/etc"
+ fi
+else
+ SCHROOT_SYSCONF_DIR="${sysconfdir}"
+fi
+SCHROOT_SYSCONF_DIR="${SCHROOT_SYSCONF_DIR}/schroot"
+AC_DEFINE_UNQUOTED([SCHROOT_SYSCONF_DIR], ["$SCHROOT_SYSCONF_DIR"])
+AC_SUBST([SCHROOT_SYSCONF_DIR])
+
+SCHROOT_CONF="${SCHROOT_SYSCONF_DIR}/schroot.conf"
+AC_SUBST([SCHROOT_CONF])
+
+AH_TEMPLATE(BUILDD_CONF, [buildd config file path])
+AC_DEFINE_UNQUOTED(BUILDD_CONF, ["$BUILDD_CONF"])
+
+AH_TEMPLATE(SBUILD_DATA_DIR, [Package data directory])
+if test "x${datadir}" = 'x${prefix}/share' || test "x${datadir}" = 'x${datarootdir}'; then
+ if test "x${prefix}" = "xNONE"; then
+ SBUILD_DATA_DIR="${ac_default_prefix}/share/sbuild"
+ else
+ SBUILD_DATA_DIR="${prefix}/share/sbuild"
+ fi
+else
+ SBUILD_DATA_DIR="${datadir}/sbuild"
+fi
+AC_SUBST(SBUILD_DATA_DIR)
+AC_DEFINE_UNQUOTED(SBUILD_DATA_DIR, ["$SBUILD_DATA_DIR"])
+
+dnl Configure which files to generate.
+AC_CONFIG_FILES([Makefile])
+AC_CONFIG_FILES([bin/Makefile])
+AC_CONFIG_FILES([configs/Makefile])
+AC_CONFIG_FILES([db/Makefile])
+AC_CONFIG_FILES([etc/Makefile])
+AC_CONFIG_FILES([lib/Makefile])
+AC_CONFIG_FILES([lib/Buildd/Makefile])
+AC_CONFIG_FILES([lib/Sbuild/Makefile])
+AC_CONFIG_FILES([lib/Sbuild/Sysconfig.pm])
+AC_CONFIG_FILES([man/Makefile])
+AC_CONFIG_FILES([man/defs.man])
+AC_CONFIG_FILES([ChangeLog])
+AC_CONFIG_FILES([test/Makefile])
+AC_CONFIG_FILES([test/perl-syntax],
+ [chmod +x test/perl-syntax])
+AC_CONFIG_FILES([test/sbuild-checkpackages],
+ [chmod +x test/sbuild-checkpackages])
+AC_CONFIG_FILES([test/sbuild-hold],
+ [chmod +x test/sbuild-hold])
+AC_CONFIG_FILES([test/sbuild-update],
+ [chmod +x test/sbuild-update])
+AC_CONFIG_FILES([test/sbuild-upgrade],
+ [chmod +x test/sbuild-upgrade])
+AC_CONFIG_FILES([test/sbuild-distupgrade],
+ [chmod +x test/sbuild-distupgrade])
+AC_CONFIG_FILES([test/sbuild-clean],
+ [chmod +x test/sbuild-clean])
+AC_CONFIG_FILES([test/sbuild],
+ [chmod +x test/sbuild])
+AC_CONFIG_FILES([tools/Makefile])
+
+dnl Output the generated config.status script.
+AC_OUTPUT
diff --git a/db/.gitignore b/db/.gitignore
new file mode 100644
index 0000000..2d546fe
--- /dev/null
+++ b/db/.gitignore
@@ -0,0 +1 @@
+db.sql
diff --git a/db/Makefile.am b/db/Makefile.am
new file mode 100644
index 0000000..4466a3d
--- /dev/null
+++ b/db/Makefile.am
@@ -0,0 +1,50 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2009 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
+
+sqldir = $(datadir)/wanna-build/postgresql/install
+
+SQL = \
+ archive-data.sql \
+ archive.sql \
+ build-data.sql \
+ build.sql \
+ functions.sql \
+ install.sql \
+ language.sql \
+ schema.sql
+
+sql_DATA = \
+ $(SQL) \
+ db.sql
+
+db.sql: db.sql.in
+ sqlpath=$$(echo "$(sqldir)"); \
+ sqlpath=$$(echo "$$sqlpath" | sed -e 's/\//\\\//g'); \
+ sed "s,SQL_PATHNAME,$$sqlpath,g" $< >$@
+
+CLEANFILES = \
+ db.sql
+
+EXTRA_DIST = \
+ $(SQL) \
+ db.sql.in
diff --git a/db/archive-data.sql b/db/archive-data.sql
new file mode 100644
index 0000000..0e9a03b
--- /dev/null
+++ b/db/archive-data.sql
@@ -0,0 +1,121 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+INSERT INTO architectures (arch) VALUES
+ ('alpha'),
+ ('amd64'),
+ ('arm'),
+ ('armel'),
+ ('hppa'),
+ ('hurd-i386'),
+ ('i386'),
+ ('ia64'),
+ ('m68k'),
+ ('mips'),
+ ('mipsel'),
+ ('powerpc'),
+ ('s390'),
+ ('sparc');
+
+INSERT INTO suites (suite, priority) VALUES
+ ('oldstable', 1),
+ ('stable', 1),
+ ('testing', 2),
+ ('unstable', 3),
+ ('experimental', 4);
+
+INSERT INTO suites (suite, priority, depwait, hidden) VALUES
+ ('oldstable-security', 1, 'f', 't'),
+ ('stable-security', 1, 'f', 't'),
+ ('testing-security', 2, 'f', 't');
+
+INSERT INTO suite_arches (suite, arch) VALUES
+ ('oldstable', 'alpha'),
+ ('oldstable', 'amd64'),
+ ('oldstable', 'arm'),
+ ('oldstable', 'hppa'),
+ ('oldstable', 'i386'),
+ ('oldstable', 'ia64'),
+ ('oldstable', 'mips'),
+ ('oldstable', 'mipsel'),
+ ('oldstable', 'powerpc'),
+ ('oldstable', 's390'),
+ ('oldstable', 'sparc'),
+ ('stable', 'alpha'),
+ ('stable', 'amd64'),
+ ('stable', 'arm'),
+ ('stable', 'armel'),
+ ('stable', 'hppa'),
+ ('stable', 'i386'),
+ ('stable', 'ia64'),
+ ('stable', 'mips'),
+ ('stable', 'mipsel'),
+ ('stable', 'powerpc'),
+ ('stable', 's390'),
+ ('stable', 'sparc'),
+ ('testing', 'alpha'),
+ ('testing', 'amd64'),
+ ('testing', 'armel'),
+ ('testing', 'hppa'),
+ ('testing', 'i386'),
+ ('testing', 'ia64'),
+ ('testing', 'mips'),
+ ('testing', 'mipsel'),
+ ('testing', 'powerpc'),
+ ('testing', 's390'),
+ ('testing', 'sparc'),
+ ('unstable', 'alpha'),
+ ('unstable', 'amd64'),
+ ('unstable', 'armel'),
+ ('unstable', 'hppa'),
+ ('unstable', 'i386'),
+ ('unstable', 'ia64'),
+ ('unstable', 'mips'),
+ ('unstable', 'mipsel'),
+ ('unstable', 'powerpc'),
+ ('unstable', 's390'),
+ ('unstable', 'sparc'),
+ ('experimental', 'alpha'),
+ ('experimental', 'amd64'),
+ ('experimental', 'armel'),
+ ('experimental', 'hppa'),
+ ('experimental', 'i386'),
+ ('experimental', 'ia64'),
+ ('experimental', 'mips'),
+ ('experimental', 'mipsel'),
+ ('experimental', 'powerpc'),
+ ('experimental', 's390'),
+ ('experimental', 'sparc');
+
+INSERT INTO components (component) VALUES
+ ('main'),
+ ('contrib'),
+ ('non-free');
+
+INSERT INTO package_types (type) VALUES
+ ('deb'),
+ ('udeb');
+
+INSERT INTO package_priorities (pkg_prio, prio_val) VALUES
+ ('required', 1),
+ ('standard', 2),
+ ('important', 3),
+ ('optional', 4),
+ ('extra', 5);
diff --git a/db/archive.sql b/db/archive.sql
new file mode 100644
index 0000000..5c3d8f9
--- /dev/null
+++ b/db/archive.sql
@@ -0,0 +1,266 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+CREATE TABLE architectures (
+ arch text
+ CONSTRAINT arch_pkey PRIMARY KEY
+);
+
+COMMENT ON TABLE architectures IS 'Architectures known by this wanna-build instance';
+COMMENT ON COLUMN architectures.arch IS 'Architecture name';
+
+CREATE TABLE suites (
+ suite text
+ CONSTRAINT suite_pkey PRIMARY KEY,
+ priority integer,
+ depwait boolean
+ DEFAULT 't',
+ hidden boolean
+ DEFAULT 'f'
+);
+
+COMMENT ON TABLE suites IS 'Valid suites';
+COMMENT ON COLUMN suites.suite IS 'Suite name';
+COMMENT ON COLUMN suites.priority IS 'Sorting order (lower is higher priority)';
+COMMENT ON COLUMN suites.depwait IS 'Automatically wait on dependencies?';
+COMMENT ON COLUMN suites.hidden IS 'Hide suite from public view (e.g. for -security)?';
+
+CREATE TABLE suite_arches (
+ suite text
+ NOT NULL
+ CONSTRAINT suite_arches_suite_fkey REFERENCES suites(suite),
+ arch text
+ NOT NULL
+ CONSTRAINT suite_arches_arch_fkey REFERENCES architectures(arch),
+ CONSTRAINT suite_arches_pkey PRIMARY KEY (suite, arch)
+);
+
+COMMENT ON TABLE suite_arches IS 'List of architectures in each suite';
+COMMENT ON COLUMN suite_arches.suite IS 'Suite name';
+COMMENT ON COLUMN suite_arches.arch IS 'Architecture name';
+
+CREATE TABLE components (
+ component text
+ CONSTRAINT component_pkey PRIMARY KEY
+);
+
+COMMENT ON TABLE components IS 'Valid archive components';
+COMMENT ON COLUMN components.component IS 'Component name';
+
+CREATE TABLE package_types (
+ type text
+ CONSTRAINT pkg_tpe_pkey PRIMARY KEY
+);
+
+COMMENT ON TABLE package_types IS 'Valid types for binary packages';
+COMMENT ON COLUMN package_types.type IS 'Type name';
+
+CREATE TABLE package_architectures (
+ arch text
+ CONSTRAINT pkg_arch_pkey PRIMARY KEY
+);
+
+COMMENT ON TABLE package_architectures IS 'Possible values for the Architecture field';
+COMMENT ON COLUMN package_architectures.arch IS 'Architecture name';
+
+CREATE TABLE package_priorities (
+ pkg_prio text
+ CONSTRAINT pkg_pri_pkey PRIMARY KEY,
+ prio_val integer
+ DEFAULT 0
+);
+
+COMMENT ON TABLE package_priorities IS 'Valid package priorities';
+COMMENT ON COLUMN package_priorities.pkg_prio IS 'Priority name';
+COMMENT ON COLUMN package_priorities.prio_val IS 'Integer value for sorting priorities';
+
+CREATE TABLE package_sections (
+ section text
+ CONSTRAINT pkg_sect_pkey PRIMARY KEY
+);
+
+COMMENT ON TABLE package_sections IS 'Valid package sections';
+COMMENT ON COLUMN package_sections.section IS 'Section name';
+
+CREATE TABLE sources (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ component text
+ CONSTRAINT source_comp_fkey REFERENCES components(component)
+ ON DELETE CASCADE
+ NOT NULL,
+ section text
+ CONSTRAINT source_section_fkey REFERENCES package_sections(section)
+ NOT NULL,
+ pkg_prio text
+ CONSTRAINT source_pkg_prio_fkey REFERENCES package_priorities(pkg_prio)
+ NOT NULL,
+ maintainer text NOT NULL,
+ build_dep text,
+ build_dep_indep text,
+ build_confl text,
+ build_confl_indep text,
+ stdver text,
+ CONSTRAINT sources_pkey PRIMARY KEY (source, source_version)
+);
+
+CREATE INDEX sources_pkg_idx ON sources (source);
+
+COMMENT ON TABLE sources IS 'Source packages common to all architectures (from Sources)';
+COMMENT ON COLUMN sources.source IS 'Package name';
+COMMENT ON COLUMN sources.source_version IS 'Package version number';
+COMMENT ON COLUMN sources.component IS 'Archive component';
+COMMENT ON COLUMN sources.section IS 'Package section';
+COMMENT ON COLUMN sources.pkg_prio IS 'Package priority';
+COMMENT ON COLUMN sources.maintainer IS 'Package maintainer name';
+COMMENT ON COLUMN sources.build_dep IS 'Package build dependencies (architecture dependent)';
+COMMENT ON COLUMN sources.build_dep_indep IS 'Package build dependencies (architecture independent)';
+COMMENT ON COLUMN sources.build_confl IS 'Package build conflicts (architecture dependent)';
+COMMENT ON COLUMN sources.build_confl_indep IS 'Package build conflicts (architecture independent)';
+COMMENT ON COLUMN sources.stdver IS 'Debian Standards (policy) version number';
+
+CREATE TABLE source_architectures (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ arch text
+ CONSTRAINT source_arch_arch_fkey
+ REFERENCES package_architectures(arch)
+ ON DELETE CASCADE
+ NOT NULL,
+ UNIQUE (source, source_version, arch),
+ CONSTRAINT source_arch_source_fkey FOREIGN KEY (source, source_version)
+ REFERENCES sources (source, source_version)
+ ON DELETE CASCADE
+);
+
+COMMENT ON TABLE source_architectures IS 'Source package architectures (from Sources)';
+COMMENT ON COLUMN source_architectures.source IS 'Package name';
+COMMENT ON COLUMN source_architectures.source_version IS 'Package version number';
+COMMENT ON COLUMN source_architectures.arch IS 'Architecture name';
+
+CREATE TABLE uploaders (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ uploader text
+ NOT NULL,
+ UNIQUE (source, source_version, uploader),
+ CONSTRAINT uploader_source_fkey FOREIGN KEY (source, source_version)
+ REFERENCES sources (source, source_version)
+ ON DELETE CASCADE
+);
+
+COMMENT ON TABLE uploaders IS 'Uploader names for source packages';
+COMMENT ON COLUMN uploaders.source IS 'Package name';
+COMMENT ON COLUMN uploaders.source_version IS 'Package version number';
+COMMENT ON COLUMN uploaders.uploader IS 'Uploader name and address';
+
+CREATE TABLE binaries (
+ -- PostgreSQL won't allow "binary" as column name
+ package text NOT NULL,
+ version debversion NOT NULL,
+ arch text
+ CONSTRAINT bin_arch_fkey REFERENCES package_architectures(arch)
+ ON DELETE CASCADE
+ NOT NULL,
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ section text
+ CONSTRAINT bin_section_fkey REFERENCES package_sections(section)
+ NOT NULL,
+ type text
+ CONSTRAINT bin_pkg_type_fkey REFERENCES package_types(type)
+ NOT NULL,
+ pkg_prio text
+ CONSTRAINT bin_pkg_prio_fkey REFERENCES package_priorities(pkg_prio)
+ NOT NULL,
+ CONSTRAINT bin_pkey PRIMARY KEY (package, version, arch),
+ CONSTRAINT bin_src_fkey FOREIGN KEY (source, source_version)
+ REFERENCES sources (source, source_version)
+ ON DELETE CASCADE
+);
+
+COMMENT ON TABLE binaries IS 'Binary packages specific to single architectures (from Packages)';
+COMMENT ON COLUMN binaries.package IS 'Binary package name';
+COMMENT ON COLUMN binaries.version IS 'Binary package version number';
+COMMENT ON COLUMN binaries.arch IS 'Architecture name';
+COMMENT ON COLUMN binaries.source IS 'Source package name';
+COMMENT ON COLUMN binaries.source_version IS 'Source package version number';
+COMMENT ON COLUMN binaries.section IS 'Package section';
+COMMENT ON COLUMN binaries.pkg_prio IS 'Package priority';
+
+CREATE TABLE suite_sources (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ suite text
+ CONSTRAINT suite_sources_suite_fkey REFERENCES suites(suite)
+ ON DELETE CASCADE
+ NOT NULL,
+ CONSTRAINT suite_sources_pkey PRIMARY KEY (source, suite),
+ CONSTRAINT suite_sources_src_fkey FOREIGN KEY (source, source_version)
+ REFERENCES sources (source, source_version)
+ ON DELETE CASCADE
+);
+
+CREATE INDEX suite_sources_src_ver_idx ON suite_sources (source, source_version);
+
+COMMENT ON TABLE suite_sources IS 'Source packages contained within a suite';
+COMMENT ON COLUMN suite_sources.source IS 'Source package name';
+COMMENT ON COLUMN suite_sources.source_version IS 'Source package version number';
+COMMENT ON COLUMN suite_sources.suite IS 'Suite name';
+
+CREATE TABLE suite_binaries (
+ package text
+ NOT NULL,
+ version debversion
+ NOT NULL,
+ arch text
+ CONSTRAINT suite_bin_arch_fkey REFERENCES package_architectures(arch)
+ ON DELETE CASCADE
+ NOT NULL,
+ suite text
+ CONSTRAINT suite_bin_suite_fkey REFERENCES suites(suite)
+ ON DELETE CASCADE
+ NOT NULL,
+ CONSTRAINT suite_bin_pkey PRIMARY KEY (package, arch, suite),
+ CONSTRAINT suite_bin_bin_fkey FOREIGN KEY (package, version, arch)
+ REFERENCES binaries (package, version, arch)
+ ON DELETE CASCADE,
+ CONSTRAINT suite_bin_suite_arch_fkey FOREIGN KEY (suite, arch)
+ REFERENCES suite_arches (suite, arch)
+ ON DELETE CASCADE
+);
+
+CREATE INDEX suite_binaries_pkg_ver_idx ON suite_binaries (package, version);
+
+COMMENT ON TABLE suite_binaries IS 'Binary packages contained within a suite';
+COMMENT ON COLUMN suite_binaries.package IS 'Binary package name';
+COMMENT ON COLUMN suite_binaries.version IS 'Binary package version number';
+COMMENT ON COLUMN suite_binaries.arch IS 'Architecture name';
+COMMENT ON COLUMN suite_binaries.suite IS 'Suite name';
diff --git a/db/build-data.sql b/db/build-data.sql
new file mode 100644
index 0000000..6b78bc6
--- /dev/null
+++ b/db/build-data.sql
@@ -0,0 +1,41 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+INSERT INTO package_states (name) VALUES
+ ('build-attempted'),
+ ('building'),
+ ('built'),
+ ('dep-wait'),
+ ('dep-wait-removed'),
+ ('failed'),
+ ('failed-removed'),
+ ('install-wait'),
+ ('installed'),
+ ('needs-build'),
+ ('not-for-us'),
+ ('old-failed'),
+ ('reupload-wait'),
+ ('state'),
+ ('uploaded');
+
+INSERT INTO build_log_result (result, is_success) VALUES
+ ('maybe-failed', 'f'),
+ ('maybe-successful', 't'),
+ ('skipped', 'f');
diff --git a/db/build.sql b/db/build.sql
new file mode 100644
index 0000000..0aa204c
--- /dev/null
+++ b/db/build.sql
@@ -0,0 +1,245 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+CREATE TABLE builders (
+ builder text
+ CONSTRAINT builder_pkey PRIMARY KEY,
+ arch text
+ CONSTRAINT builder_arch_fkey REFERENCES architectures(arch)
+ NOT NULL,
+ address text
+ NOT NULL
+);
+
+COMMENT ON TABLE builders IS 'buildd usernames (database users from _userinfo in old MLDBM db format)';
+COMMENT ON COLUMN builders.builder IS 'Username';
+COMMENT ON COLUMN builders.arch IS 'Buildd architecture';
+COMMENT ON COLUMN builders.address IS 'Remote e-mail address of the buildd user';
+
+CREATE TABLE package_states (
+ name text
+ CONSTRAINT state_pkey PRIMARY KEY
+);
+
+COMMENT ON TABLE package_states IS 'Package states';
+COMMENT ON COLUMN package_states.name IS 'State name';
+
+CREATE TABLE build_status (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ arch text
+ CONSTRAINT build_status_arch_fkey REFERENCES architectures(arch)
+ ON DELETE CASCADE
+ NOT NULL,
+ suite text
+ CONSTRAINT build_status_suite_fkey REFERENCES suites(suite)
+ ON DELETE CASCADE
+ NOT NULL,
+ bin_nmu integer,
+ user_name text
+ NOT NULL
+ DEFAULT CURRENT_USER,
+ builder text
+ -- Can be NULL in case of states set up manually by people.
+ CONSTRAINT build_status_builder_fkey REFERENCES builders(builder),
+ status text
+ CONSTRAINT build_status_status_fkey REFERENCES package_states(name)
+ NOT NULL,
+ ctime timestamp with time zone
+ NOT NULL
+ DEFAULT 'epoch'::timestamp,
+ CONSTRAINT build_status_pkey PRIMARY KEY (source, arch, suite),
+ CONSTRAINT build_status_src_fkey FOREIGN KEY(source, source_version)
+ REFERENCES sources(source, source_version)
+ ON DELETE CASCADE,
+ CONSTRAINT suite_bin_suite_arch_fkey FOREIGN KEY (suite, arch)
+ REFERENCES suite_arches (suite, arch)
+ ON DELETE CASCADE
+);
+
+CREATE INDEX build_status_source ON build_status (source);
+
+COMMENT ON TABLE build_status IS 'Build status for each package';
+COMMENT ON COLUMN build_status.source IS 'Source package name';
+COMMENT ON COLUMN build_status.source_version IS 'Source package version number';
+COMMENT ON COLUMN build_status.arch IS 'Architecture name';
+COMMENT ON COLUMN build_status.suite IS 'Suite name';
+COMMENT ON COLUMN build_status.bin_nmu IS 'Scheduled binary NMU version, if any';
+COMMENT ON COLUMN build_status.user_name IS 'User making this change (username)';
+COMMENT ON COLUMN build_status.builder IS 'Build dæmon making this change (username)';
+COMMENT ON COLUMN build_status.status IS 'Status name';
+COMMENT ON COLUMN build_status.ctime IS 'Stage change time';
+
+CREATE TABLE build_status_history (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ arch text
+ CONSTRAINT build_status_history_arch_fkey REFERENCES architectures(arch)
+ ON DELETE CASCADE
+ NOT NULL,
+ suite text
+ CONSTRAINT build_status_history_suite_fkey REFERENCES suites(suite)
+ ON DELETE CASCADE
+ NOT NULL,
+ bin_nmu integer,
+ user_name text
+ NOT NULL
+ DEFAULT CURRENT_USER,
+ builder text
+ CONSTRAINT build_status_history_builder_fkey REFERENCES builders(builder),
+ status text
+ CONSTRAINT build_status_history_status_fkey REFERENCES package_states(name)
+ NOT NULL,
+ ctime timestamp with time zone
+ NOT NULL DEFAULT CURRENT_TIMESTAMP
+);
+
+CREATE INDEX build_status_history_source ON build_status_history (source);
+CREATE INDEX build_status_history_ctime ON build_status_history (ctime);
+
+COMMENT ON TABLE build_status_history IS 'Build status history for each package';
+COMMENT ON COLUMN build_status_history.source IS 'Source package name';
+COMMENT ON COLUMN build_status_history.source_version IS 'Source package version number';
+COMMENT ON COLUMN build_status_history.arch IS 'Architecture name';
+COMMENT ON COLUMN build_status_history.suite IS 'Suite name';
+COMMENT ON COLUMN build_status_history.bin_nmu IS 'Scheduled binary NMU version, if any';
+COMMENT ON COLUMN build_status_history.user_name IS 'User making this change (username)';
+COMMENT ON COLUMN build_status_history.builder IS 'Build dæmon making this change (username)';
+COMMENT ON COLUMN build_status_history.status IS 'Status name';
+COMMENT ON COLUMN build_status_history.ctime IS 'Stage change time';
+
+CREATE TABLE build_status_properties (
+ source text NOT NULL,
+ arch text NOT NULL,
+ source suite NOT NULL,
+ prop_name text NOT NULL,
+ prop_value text NOT NULL,
+ CONSTRAINT build_status_properties_fkey
+ FOREIGN KEY(source, arch)
+ REFERENCES build_status(id)
+ ON DELETE CASCADE,
+ CONSTRAINT build_status_properties_unique
+ UNIQUE (source, arch, prop_name)
+);
+
+COMMENT ON TABLE build_status_properties IS 'Additional package-specific properties (e.g. For PermBuildPri/BuildPri/Binary-NMU-(Version|ChangeLog)/Notes)';
+COMMENT ON COLUMN build_status_properties.source IS 'Source package name';
+COMMENT ON COLUMN build_status_properties.arch IS 'Architecture name';
+COMMENT ON COLUMN build_status_properties.suite IS 'Suite name';
+COMMENT ON COLUMN build_status_properties.prop_name IS 'Property name';
+COMMENT ON COLUMN build_status_properties.prop_value IS 'Property value';
+
+-- Make this a table because in the future we may have more fine-grained
+-- result states.
+CREATE TABLE build_log_result (
+ result text
+ CONSTRAINT build_log_result_pkey PRIMARY KEY,
+ is_success boolean
+ DEFAULT 'f'
+);
+
+COMMENT ON TABLE build_log_result IS 'Possible results states of a build log';
+COMMENT ON COLUMN build_log_result.result IS 'Meaningful and short name for the result';
+COMMENT ON COLUMN build_log_result.is_success IS 'Whether the result of the build is successful';
+
+CREATE TABLE build_logs (
+ source text
+ NOT NULL,
+ source_version debversion
+ NOT NULL,
+ arch text
+ CONSTRAINT build_logs_arch_fkey REFERENCES architectures(arch)
+ NOT NULL,
+ suite text
+ CONSTRAINT build_logs_suite_fkey REFERENCES suites(suite)
+ NOT NULL,
+ date timestamp with time zone
+ NOT NULL,
+ result text
+ CONSTRAINT build_logs_result_fkey REFERENCES build_log_result(result)
+ NOT NULL,
+ build_time interval,
+ used_space integer,
+ path text
+ CONSTRAINT build_logs_pkey PRIMARY KEY
+);
+CREATE INDEX build_logs_source_idx ON build_logs (source);
+
+COMMENT ON TABLE build_logs IS 'Available build logs';
+COMMENT ON COLUMN build_logs.source IS 'Source package name';
+COMMENT ON COLUMN build_logs.source_version IS 'Source package version';
+COMMENT ON COLUMN build_logs.arch IS 'Architecture name';
+COMMENT ON COLUMN build_logs.suite IS 'Suite name';
+COMMENT ON COLUMN build_logs.date IS 'Date of the log';
+COMMENT ON COLUMN build_logs.result IS 'Result state';
+COMMENT ON COLUMN build_logs.build_time IS 'Time needed by the build';
+COMMENT ON COLUMN build_logs.used_space IS 'Space needed by the build';
+COMMENT ON COLUMN build_logs.path IS 'Relative path to the log file';
+
+CREATE TABLE log (
+ time timestamp with time zone
+ NOT NULL DEFAULT CURRENT_TIMESTAMP,
+ username text NOT NULL DEFAULT CURRENT_USER,
+ message text NOT NULL
+);
+
+CREATE INDEX log_idx ON log (time);
+
+COMMENT ON TABLE log IS 'Log messages';
+COMMENT ON COLUMN log.time IS 'Log entry time';
+COMMENT ON COLUMN log.username IS 'Log user name';
+COMMENT ON COLUMN log.message IS 'Log entry message';
+
+CREATE TABLE people (
+ login text
+ CONSTRAINT people_pkey PRIMARY KEY,
+ full_name text
+ NOT NULL,
+ address text
+ NOT NULL
+);
+
+COMMENT ON TABLE people IS 'People wanna-build should know about';
+COMMENT ON COLUMN people.login IS 'Debian login';
+COMMENT ON COLUMN people.full_name IS 'Full name';
+COMMENT ON COLUMN people.address IS 'E-mail address';
+
+CREATE TABLE buildd_admins (
+ builder text
+ CONSTRAINT buildd_admin_builder_fkey REFERENCES builders(builder)
+ ON DELETE CASCADE
+ NOT NULL,
+ admin text
+ CONSTRAINT buildd_admin_admin_fkey REFERENCES people(login)
+ ON DELETE CASCADE
+ NOT NULL,
+ backup boolean
+ DEFAULT 'f',
+ UNIQUE (builder, admin)
+);
+
+COMMENT ON TABLE buildd_admins IS 'Admins for each buildd';
+COMMENT ON COLUMN buildd_admins.builder IS 'The buildd';
+COMMENT ON COLUMN buildd_admins.admin IS 'The admin login';
+COMMENT ON COLUMN buildd_admins.backup IS 'Whether this is only a backup admin';
diff --git a/db/db.sql.in b/db/db.sql.in
new file mode 100644
index 0000000..3ad6dd4
--- /dev/null
+++ b/db/db.sql.in
@@ -0,0 +1,32 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+\i /usr/share/postgresql/8.3/contrib/debversion.sql
+
+SET search_path = public;
+
+\i SQL_PATHNAME/language.sql
+\i SQL_PATHNAME/schema.sql
+\i SQL_PATHNAME/archive.sql
+\i SQL_PATHNAME/build.sql
+\i SQL_PATHNAME/functions.sql
+
+\i SQL_PATHNAME/archive-data.sql
+\i SQL_PATHNAME/build-data.sql
diff --git a/db/functions.sql b/db/functions.sql
new file mode 100644
index 0000000..7334bb0
--- /dev/null
+++ b/db/functions.sql
@@ -0,0 +1,129 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+--
+-- Triggers to insert missing sections and priorities
+--
+
+CREATE OR REPLACE FUNCTION package_checkrel() RETURNS trigger AS $package_checkrel$
+BEGIN
+ PERFORM section FROM package_sections WHERE (section = NEW.section);
+ IF FOUND = 'f' THEN
+ INSERT INTO package_sections (section) VALUES (NEW.section);
+ END IF;
+ PERFORM pkg_prio FROM package_priorities WHERE (pkg_prio = NEW.pkg_prio);
+ IF FOUND = 'f' THEN
+ INSERT INTO package_priorities (pkg_prio) VALUES (NEW.pkg_prio);
+ END IF;
+ RETURN NEW;
+END;
+$package_checkrel$ LANGUAGE plpgsql;
+COMMENT ON FUNCTION package_checkrel ()
+ IS 'Check foreign key references (package sections and priorities) exist';
+
+CREATE TRIGGER checkrel BEFORE INSERT OR UPDATE ON sources
+ FOR EACH ROW EXECUTE PROCEDURE package_checkrel();
+COMMENT ON TRIGGER checkrel ON sources
+ IS 'Check foreign key references (package sections and priorities) exist';
+
+CREATE TRIGGER checkrel BEFORE INSERT OR UPDATE ON binaries
+ FOR EACH ROW EXECUTE PROCEDURE package_checkrel();
+COMMENT ON TRIGGER checkrel ON binaries
+ IS 'Check foreign key references (package sections and priorities) exist';
+
+--
+-- Triggers to insert missing package architectures
+--
+
+CREATE OR REPLACE FUNCTION package_check_arch() RETURNS trigger AS $package_check_arch$
+BEGIN
+ PERFORM arch FROM package_architectures WHERE (arch = NEW.arch);
+ IF FOUND = 'f' THEN
+ INSERT INTO package_architectures (arch) VALUES (NEW.arch);
+ END IF;
+ RETURN NEW;
+END;
+$package_check_arch$ LANGUAGE plpgsql;
+
+COMMENT ON FUNCTION package_check_arch ()
+ IS 'Insert missing values into package_architectures (from NEW.arch)';
+
+CREATE TRIGGER check_arch BEFORE INSERT OR UPDATE ON source_architectures
+ FOR EACH ROW EXECUTE PROCEDURE package_check_arch();
+COMMENT ON TRIGGER check_arch ON source_architectures
+ IS 'Ensure foreign key references (arch) exist';
+
+CREATE TRIGGER check_arch BEFORE INSERT OR UPDATE ON binaries
+ FOR EACH ROW EXECUTE PROCEDURE package_check_arch();
+COMMENT ON TRIGGER check_arch ON binaries
+ IS 'Ensure foreign key references (arch) exist';
+
+-- Triggers on build_status:
+-- - unconditionally update ctime
+-- - verify bin_nmu is a positive integer (and change 0 to NULL)
+-- - insert a record into status_history for every change in build_status
+
+CREATE OR REPLACE FUNCTION set_ctime()
+RETURNS trigger AS $set_ctime$
+BEGIN
+ NEW.ctime = CURRENT_TIMESTAMP;
+ RETURN NEW;
+END;
+$set_ctime$ LANGUAGE plpgsql;
+
+CREATE OR REPLACE FUNCTION check_bin_nmu_number()
+RETURNS trigger AS $check_bin_nmu_number$
+BEGIN
+ IF NEW.bin_nmu = 0 THEN
+ NEW.bin_nmu = NULL; -- Avoid two values with same meaning
+ ELSIF NEW.bin_nmu < 0 THEN
+ RAISE EXCEPTION 'Invalid value for "bin_nmu" column: %', NEW.bin_nmu;
+ END IF;
+ RETURN NEW;
+END;
+$check_bin_nmu_number$ LANGUAGE plpgsql;
+
+CREATE TRIGGER check_bin_nmu BEFORE INSERT OR UPDATE ON build_status
+ FOR EACH ROW EXECUTE PROCEDURE check_bin_nmu_number();
+COMMENT ON TRIGGER check_bin_nmu ON build_status
+ IS 'Ensure "bin_nmu" is a positive integer, or set it to NULL if 0';
+
+CREATE TRIGGER set_or_update_ctime BEFORE INSERT OR UPDATE ON build_status
+ FOR EACH ROW EXECUTE PROCEDURE set_ctime();
+COMMENT ON TRIGGER set_or_update_ctime ON build_status
+ IS 'Set or update the "ctime" column to now()';
+
+CREATE OR REPLACE FUNCTION update_status_history()
+RETURNS trigger AS $update_status_history$
+BEGIN
+ INSERT INTO build_status_history
+ (source, source_version, arch, suite,
+ bin_nmu, user_name, builder, status, ctime)
+ VALUES
+ (NEW.source, NEW.source_version, NEW.arch, NEW.suite,
+ NEW.bin_nmu, NEW.user_name, NEW.builder, NEW.status, NEW.ctime);
+ RETURN NULL;
+END;
+$update_status_history$ LANGUAGE plpgsql;
+
+CREATE TRIGGER update_history AFTER INSERT OR UPDATE ON build_status
+ FOR EACH ROW EXECUTE PROCEDURE update_status_history();
+COMMENT ON TRIGGER update_history ON build_status
+ IS 'Insert a record of the status change into build_status_history';
diff --git a/db/install.sql b/db/install.sql
new file mode 100644
index 0000000..203c418
--- /dev/null
+++ b/db/install.sql
@@ -0,0 +1,26 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+CREATE DATABASE "sbuild-packages" ENCODING 'UTF8';
+COMMENT ON DATABASE "sbuild-packages"
+ IS 'Debian source builder package state management';
+\c "sbuild-packages"
+
+\i db.sql
diff --git a/db/language.sql b/db/language.sql
new file mode 100644
index 0000000..e961ac7
--- /dev/null
+++ b/db/language.sql
@@ -0,0 +1,38 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- Copyright © 2008-2009 Roger Leigh <rleigh@debian.org>
+--- Copyright © 2008-2009 Marc 'HE' Brockschmidt <he@debian.org>
+--- Copyright © 2008-2009 Adeodato Simó <adeodato@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/>.
+
+CREATE OR REPLACE FUNCTION create_plpgsql_language ()
+ RETURNS TEXT AS $$
+ CREATE LANGUAGE plpgsql;
+ SELECT 'language plpgsql created'::TEXT;
+$$
+LANGUAGE SQL;
+
+SELECT CASE WHEN
+ (SELECT 't'::boolean
+ FROM pg_language
+ WHERE lanname='plpgsql')
+ THEN
+ (SELECT 'language plpgsql already installed'::TEXT)
+ ELSE
+ (SELECT create_plpgsql_language())
+END;
+
+DROP FUNCTION create_plpgsql_language();
diff --git a/db/schema.sql b/db/schema.sql
new file mode 100644
index 0000000..ff51d39
--- /dev/null
+++ b/db/schema.sql
@@ -0,0 +1,27 @@
+--- Debian Source Builder: Database Schema for PostgreSQL -*- sql -*-
+---
+--- 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/>.
+
+CREATE TABLE schema (
+ version integer
+ CONSTRAINT schema_pkey PRIMARY KEY,
+ description text NOT NULL
+);
+
+COMMENT ON TABLE schema IS 'Schema revision history';
+COMMENT ON COLUMN schema.version IS 'Schema version';
+COMMENT ON COLUMN schema.description IS 'Schema change description';
diff --git a/etc/.gitignore b/etc/.gitignore
new file mode 100644
index 0000000..94e71ff
--- /dev/null
+++ b/etc/.gitignore
@@ -0,0 +1 @@
+profiles
diff --git a/etc/Makefile.am b/etc/Makefile.am
new file mode 100644
index 0000000..bc70408
--- /dev/null
+++ b/etc/Makefile.am
@@ -0,0 +1,53 @@
+# 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
+
+sbuildsysconfdir = $(SBUILD_SYSCONF_DIR)
+sbuildsysconf_DATA = sbuild.conf
+
+builddsysconfdir = $(BUILDD_SYSCONF_DIR)
+builddsysconf_DATA = buildd.conf
+
+exampleconfdir = $(docdir)/examples
+exampleconf_DATA = example.sbuildrc sbuild-debian-developer-setup-update-all
+
+SETUP =
+
+sbuild.conf: $(abs_top_srcdir)/tools/sbuild-dumpconfig
+ PERL5LIB=$(abs_top_srcdir)/lib:$(abs_top_builddir)/lib \
+ $< sbuild config > $@
+
+buildd.conf: $(abs_top_srcdir)/tools/sbuild-dumpconfig
+ PERL5LIB=$(abs_top_srcdir)/lib:$(abs_top_builddir)/lib \
+ $< buildd config > $@
+
+schrootsetupdir = $(SCHROOT_SYSCONF_DIR)/setup.d
+schrootsetup_SCRIPTS = $(SETUP)
+
+CLEANFILES = \
+ sbuild.conf \
+ buildd.conf
+
+EXTRA_DIST = \
+ $(exampleconf_DATA) \
+ $(SETUP) \
+ sbuild-debuild.conf
diff --git a/etc/example.sbuildrc b/etc/example.sbuildrc
new file mode 100644
index 0000000..035fa0a
--- /dev/null
+++ b/etc/example.sbuildrc
@@ -0,0 +1,118 @@
+# example for ~/.sbuildrc. (Also see /etc/sbuild/sbuild.conf.) -*- Perl -*-
+#
+# Default settings are commented out.
+# Additional options found in /etc/sbuild/sbuild.conf may be
+# overridden here.
+
+
+##
+## DPKG-BUILDPACKAGE OPTIONS
+##
+
+# Name to use as override in .changes files for the Maintainer: field
+# Defaults to the DEBEMAIL environment variable, if set, or else the
+# Maintainer: field will not be overridden unless set here.
+#$maintainer_name='Francesco Paolo Lovergine <frankie@debian.org>';
+
+# Name to use as override in .changes file for the Changed-By: field.
+#$uploader_name='Francesco Paolo Lovergine <frankie@debian.org>';
+
+# Key ID to use in .changes for the current upload.
+# It overrides both $maintainer_name and $uploader_name
+#$key_id='Francesco Paolo Lovergine <frankie@debian.org>';
+
+# PGP-related option to pass to dpkg-buildpackage. Usually neither .dsc
+# nor .changes files shall be signed automatically.
+#$pgp_options = ['-us', '-uc'];
+
+# By default, do not build a source package (binary only build).
+# Set to 1 to force creation of a source package, but note that
+# this is inappropriate for binary NMUs, where the option will
+# always be disabled.
+#$build_source = 0;
+
+# By default, the -s option only includes the .orig.tar.gz when needed
+# (i.e. when the Debian revision is 0 or 1). By setting this option
+# to 1, the .orig.tar.gz will always be included when -s is used.
+# This is equivalent to --force-orig-source.
+#$force_orig_source = 0;
+
+# PATH to set when running dpkg-buildpackage.
+#$path = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/X11R6/bin:/usr/games";
+
+# This command is run with the dpkg-buildpackage command line passed to it
+# (in the chroot, if doing a chrooted build). It is used by the sparc buildd
+# (which is sparc64) to call the wrapper script that sets the environment to
+# sparc (32-bit). It could be used for other build environment setup scripts
+#
+#$build_env_cmnd = "";
+
+
+##
+## SBUILD BEHAVIOUR
+##
+
+# Default distribution. By default, no distribution is defined, and
+# the user must specify it with the -d option. However, a default may
+# be configured here if desired. Users must take care not to upload
+# to the wrong distribution when this option is set, for example
+# experimental packages will be built for upload to unstable when this
+# is not what is required.
+#$distribution = 'unstable';
+
+# Default chroot (defaults to distribution[-arch][-sbuild])
+#$chroot = 'unstable-powerpc-sbuild';
+
+# When to purge the build directory afterwards; possible values are "never",
+# "successful", and "always"
+#$purge_build_directory="successful";
+
+# sbuild behaviour; possible values are "user" (exit status reports
+# build failures) and "buildd" (exit status does not report build
+# failures) for use in a buildd setup.
+#$sbuild_mode = "user";
+
+
+##
+## TIMEOUTS
+##
+
+# Time to wait for a source dependency lock. The default is 1 minute.
+#$srcdep_lock_wait = 1; # 1 minute
+
+# Time (in minutes) of inactivity after which a build is terminated. Activity
+# is measured by output to the log file.
+#$stalled_pkg_timeout = 150;
+
+# Some packages may exceed the general timeout (e.g. redirecting output to
+# a file) and need a different timeout. Below are some examples.
+#%individual_stalled_pkg_timeout = (smalleiffel => 300,
+# jade => 300,
+# atlas => 300,
+# glibc => 1000,
+# 'gcc-3.3' => 300,
+# kwave => 600);
+#
+
+##
+## FILE AND DIRECTORY LOCATIONS
+##
+
+# This option is deprecated. Directory for chroot symlinks and sbuild
+# logs. Defaults to the current directory if unspecified. It is used
+# as the location of chroot symlinks (obsolete) and for current build
+# log symlinks and some build logs. There is no default; if unset, it
+# defaults to the current working directory. $HOME/build is another
+# common configuration.
+#$build_dir = undef;
+
+# Directory for writing build logs to
+#$log_dir = "$HOME/logs";
+
+# Directory for writing build statistics to
+#$stats_dir = "$HOME/stats";
+
+
+# don't remove this, Perl needs it:
+1;
+
diff --git a/etc/sbuild-debian-developer-setup-update-all b/etc/sbuild-debian-developer-setup-update-all
new file mode 100755
index 0000000..9605e45
--- /dev/null
+++ b/etc/sbuild-debian-developer-setup-update-all
@@ -0,0 +1,106 @@
+#!/bin/sh
+# Example script for automatically updating sbuild chroots
+#
+# Simply create a crontab /etc/cron.d/sbuild-update-all and specify the
+# schedule that you want to use. The behaviour of this script can be influenced
+# by the following evironment variables:
+#
+# PATTERN glob pattern to match the chroot config name against, in
+# directory /etc/schroot/chroot.d/.
+#
+# Default: *-sbuild
+#
+# UPDATEARGS The arguments with which sbuild-update will be invoked.
+#
+# Default: --update --dist-upgrade --autoclean --autoremove
+#
+# LOGFILE Log file to write to. These files are not rotated, you must set
+# this up yourself. See logrotate(8)
+#
+# Default: /var/log/sbuild-update-all.log
+#
+# This script will refuse to run if another instance of it is running. In fact,
+# it will refuse to run if there is an active chroot session (regardless of
+# whether it is related to the matched patterns or not). Care for this must be
+# taken when scheduling cron jobs, as crontabs are processed sequentially (with
+# regards to variable assignment), but jobs are executed in parallel.
+#
+# Examples
+# ========
+#
+# 1. Update all sbuild chroots four times a day (at 00:15/06:15/12:15/18:15):
+#
+# 15 */6 * * * root /usr/share/doc/sbuild/examples/sbuild-debian-developer-setup-update-all
+#
+# 2. Update all sid sbuild chroots daily, and all jessie sbuild chroots weekly,
+# and log the latter to a separate file:
+#
+# PATTERN = sid-*-sbuild
+# @daily root /usr/share/doc/sbuild/examples/sbuild-debian-developer-setup-update-all
+#
+# PATTERN = jessie-*-sbuild
+# LOGFILE = /var/log/wheezy-chroot-update.log
+# @weekly root /usr/share/doc/sbuild/examples/sbuild-debian-developer-setup-update-all
+#
+# The following will NOT work. Both of these jobs are executed at 00:15, so
+# the second one will refuse to run:
+#
+# PATTERN = foo-*
+# 15 0 * * * root /usr/share/doc/sbuild/examples/sbuild-debian-developer-setup-update-all
+#
+# PATTERN = bar-*
+# 15 0 * * * root /usr/share/doc/sbuild/examples/sbuild-debian-developer-setup-update-all
+
+
+# Output of sbuild-update invocations will be written to this file
+LOGFILE=${LOGFILE:-/var/log/sbuild-update-all.log}
+
+# Arguments passed to sbuild-update
+UPDATEARGS=${UPDATEARGS:-"--update --dist-upgrade --autoclean --autoremove"}
+
+# chroot config names are identified by this pattern
+PATTERN=${PATTERN:-*-sbuild-*}
+
+
+# Open logfile for output, make sure we're the only instance active
+exec 8>>$LOGFILE
+if ! flock -x -n 8
+then
+ echo "$0: another instance is already running"
+ exit 1
+fi
+# Redirect stdout to logfile
+exec 9>&1
+exec 1>&8
+
+if ! ls /etc/schroot/chroot.d/$PATTERN >/dev/null 2>&1
+then
+ echo "No chroots defined"
+ exit 0
+fi
+
+for fullname in /etc/schroot/chroot.d/$PATTERN
+do
+ confname=`basename $fullname | sed -e "s/-sbuild-.*//"`
+
+ # Check for *any* active session and skip if there is one.
+ # chroots can share common factors, for example an LVM volume, so it's
+ # better to be safe than sorry.
+ if [ -n "`schroot -l --all-sessions`" ]
+ then
+ echo "Active schroot session, will not continue"
+ break
+ fi
+
+ echo "****** `date` ******"
+ echo "Action: sbuild-update $UPDATEARGS $confname"
+
+ if ! sbuild-update $UPDATEARGS $confname
+ then
+ echo "ERROR: failed to update $confname"
+ continue
+ fi
+done
+
+# Release lock and undo redirections
+exec 8>&- 1>&9 9>&-
diff --git a/etc/sbuild-debuild.conf b/etc/sbuild-debuild.conf
new file mode 100644
index 0000000..4e34e0a
--- /dev/null
+++ b/etc/sbuild-debuild.conf
@@ -0,0 +1,16 @@
+# sbuild-debuild.conf: global sbuild-debuild settings. -*- Perl -*-
+
+# Various options with their defaults used with sbuild-debuild
+# $dpkg_buildpackage_opts = ['-S','-us','-uc',];
+# $dpkg_buildpackage_extra_opts = [];
+# $sbuild_opts = [];
+# $sbuild_extra_opts = [];
+# $lintian_opts = [];
+# $lintian_extra_opts = [];
+# $no_lintian = 0;
+# $pre_dpkg_buildpackage_commands = [];
+# $pre_sbuild_commands = [];
+# $pre_lintian_commands = [];
+# $pre_exit_commands = [];
+
+1;
diff --git a/lib/Buildd.pm b/lib/Buildd.pm
new file mode 100644
index 0000000..9b90e07
--- /dev/null
+++ b/lib/Buildd.pm
@@ -0,0 +1,192 @@
+#! /usr/bin/perl
+#
+# Buildd.pm: library for buildd and friends
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# 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/>.
+#
+#######################################################################
+
+package Buildd;
+
+use strict;
+use warnings;
+use POSIX;
+use FileHandle;
+use Sbuild::LogBase;
+
+require Exporter;
+@Buildd::ISA = qw(Exporter);
+
+@Buildd::EXPORT = qw(unset_env lock_file unlock_file send_mail
+ ll_send_mail exitstatus isin);
+
+$Buildd::lock_interval = 15;
+$Buildd::max_lock_trys = 120;
+($Buildd::progname = $0) =~ s,.*/,,;
+my @pwinfo = getpwuid($>);
+$Buildd::username = $pwinfo[0];
+$Buildd::gecos = $pwinfo[6];
+$Buildd::gecos =~ s/,.*$//;
+$Buildd::hostname = `/bin/hostname -f`;
+$Buildd::hostname =~ /^(\S+)$/; $Buildd::hostname = $1; # untaint
+
+sub isin ($@);
+sub unset_env ();
+sub lock_file ($;$);
+sub unlock_file ($);
+sub send_mail ($$$;$);
+sub ll_send_mail ($$);
+sub exitstatus ($);
+
+sub isin ($@) {
+ my $val = shift;
+ return grep( $_ eq $val, @_ );
+}
+
+sub unset_env () {
+ # unset any locale variables (sorted to match output from locale(1))
+ delete $ENV{'LANG'};
+ delete $ENV{'LANGUAGE'};
+ delete $ENV{'LC_CTYPE'};
+ delete $ENV{'LC_NUMERIC'};
+ delete $ENV{'LC_TIME'};
+ delete $ENV{'LC_COLLATE'};
+ delete $ENV{'LC_MONETARY'};
+ delete $ENV{'LC_MESSAGES'};
+ delete $ENV{'LC_PAPER'};
+ delete $ENV{'LC_NAME'};
+ delete $ENV{'LC_ADDRESS'};
+ delete $ENV{'LC_TELEPHONE'};
+ delete $ENV{'LC_MEASUREMENT'};
+ delete $ENV{'LC_IDENTIFICATION'};
+ delete $ENV{'LC_ALL'};
+ # other unneeded variables that might be set
+ delete $ENV{'DISPLAY'};
+ delete $ENV{'TERM'};
+ delete $ENV{'XDG_RUNTIME_DIR'};
+ delete $ENV{'XDG_SEAT'};
+ delete $ENV{'XDG_SESSION_COOKIE'};
+ delete $ENV{'XDG_SESSION_ID'};
+ delete $ENV{'XDG_VTNR'};
+}
+
+sub lock_file ($;$) {
+ my $file = shift;
+ my $nowait = shift;
+ my $lockfile = "$file.lock";
+ my $try = 0;
+ my $username = (getpwuid($<))[0] || $ENV{'LOGNAME'} || $ENV{'USER'};
+
+ if (!defined($nowait)) {
+ $nowait = 0;
+ }
+
+ repeat:
+ if (!sysopen( F, $lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644 )){
+ if ($! == EEXIST) {
+ # lock file exists, wait
+ goto repeat if !open( F, "<$lockfile" );
+ my $line = <F>;
+ close( F );
+ # If this goes wrong it would be a spinlock and the world will
+ # end.
+ goto repeat if !defined( $line );
+ if ($line !~ /^(\d+)\s+([\w\d.-]+)$/) {
+ warn "Bad lock file contents ($lockfile) -- still trying\n";
+ }
+ else {
+ my($pid, $user) = ($1, $2);
+ my $cnt = kill( 0, $pid );
+ if ($cnt == 0 && $! == ESRCH) {
+ # process doesn't exist anymore, remove stale lock
+ warn "Removing stale lock file $lockfile ".
+ " (pid $pid, user $user)\n";
+ unlink( $lockfile );
+ goto repeat;
+ } elsif ($cnt >= 1 and $nowait == 1) {
+ # process exists.
+ return 0;
+ }
+ }
+ if (++$try > $Buildd::max_lock_trys) {
+ warn "Lockfile $lockfile still present after ".
+ "$Buildd::max_lock_trys * $Buildd::lock_interval ".
+ " seconds -- giving up\n";
+ return 0;
+ }
+ sleep $Buildd::lock_interval;
+ goto repeat;
+ }
+ die "$Buildd::progname: Can't create lock file $lockfile: $!\n";
+ }
+ F->print("$$ $username\n");
+ F->close();
+ return 1;
+}
+
+sub unlock_file ($) {
+ my $file = shift;
+ my $lockfile = "$file.lock";
+
+ unlink( $lockfile );
+}
+
+
+sub send_mail ($$$;$) {
+ my $addr = shift;
+ my $subject = shift;
+ my $text = shift;
+ my $add_headers = shift;
+
+ return ll_send_mail( $addr,
+ "To: $addr\n".
+ "Subject: $subject\n".
+ "From: $Buildd::gecos ".
+ "<$Buildd::username\@$Buildd::hostname>\n".
+ ($add_headers ? $add_headers : "").
+ "\n$text\n" );
+}
+
+sub ll_send_mail ($$) {
+ my $to = shift;
+ my $text = shift;
+ local( *MAIL );
+
+ # TODO: Don't log to STDERR: Implement as class method using
+ # standard pipe interface using normal log streams.
+
+ $text =~ s/^\.$/../mg;
+ local $SIG{'PIPE'} = 'IGNORE';
+ if (!open( MAIL, "|/usr/sbin/sendmail -oem '$to'" )) {
+ print STDERR "Could not open pipe to /usr/sbin/sendmail: $!\n";
+ return 0;
+ }
+ print MAIL $text;
+ if (!close( MAIL )) {
+ print STDERR "sendmail failed (exit status ", exitstatus($?), ")\n";
+ return 0;
+ }
+ return 1;
+}
+
+sub exitstatus ($) {
+ my $stat = shift;
+
+ return ($stat >> 8) . "/" . ($stat % 256);
+}
+
+1;
diff --git a/lib/Buildd/Base.pm b/lib/Buildd/Base.pm
new file mode 100644
index 0000000..c7240c0
--- /dev/null
+++ b/lib/Buildd/Base.pm
@@ -0,0 +1,190 @@
+# Buildd common base functionality
+# Copyright © 1998 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/>.
+#
+#######################################################################
+
+package Buildd::Base;
+
+use strict;
+use warnings;
+
+use IO::File;
+use Buildd qw(lock_file unlock_file);
+use Buildd::Client qw();
+
+use Sbuild::Base;
+use Sbuild qw($devnull);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('PID', $$);
+
+ $self->open_log();
+
+ return $self;
+}
+
+sub open_log ($) {
+ my $self = shift;
+
+ my $logfile = $self->get_conf('DAEMON_LOG_FILE');
+
+ my $log = IO::File->new("$logfile", O_CREAT|O_WRONLY|O_APPEND, 0640)
+ or die "$0: Cannot open logfile $logfile: $!\n";
+ $log->autoflush(1);
+
+ # Since we are a daemon, fully detach from terminal by reopening
+ # stdout and stderr to redirect to the log file. Note messages
+ # should be printed using log(), not printing directly to the
+ # filehandle. This is a fallback only.
+ open(STDOUT, '>&', $log) or warn "Can't redirect stderr\n";
+ open(STDERR, '>&', $log) or warn "Can't redirect stderr\n";
+
+ $self->set('Log Stream', $log);
+
+ return $log;
+}
+
+sub close_log ($) {
+ my $self = shift;
+
+ # We can't close stdout and stderr, so redirect to /dev/null.
+ open(STDOUT, '>&', $devnull) or warn "Can't redirect stderr\n";
+ open(STDERR, '>&', $devnull) or warn "Can't redirect stderr\n";
+
+ my $log = $self->get('Log Stream');
+ $self->set('Log Stream', undef);
+
+ return $log->close();
+}
+
+sub reopen_log ($) {
+ my $self = shift;
+
+ my $log = $self->get('Log Stream');
+
+ if ($self->close_log()) {
+ $log = $self->open_log();
+ }
+
+ return $log;
+}
+
+sub write_stats ($$$) {
+ my $self = shift;
+ my ($cat, $val) = @_;
+
+ local( *F );
+
+ my $home = $self->get_conf('HOME');
+
+ lock_file( "$home/stats" );
+ open( F, ">>$home/stats/$cat" );
+ print F "$val\n";
+ close( F );
+ unlock_file( "$home/stats" );
+}
+
+sub get_db_handle ($$) {
+ my $self = shift;
+ my $dist_config = shift;
+
+ my $db = Buildd::Client->new($dist_config);
+ $db->set('Log Stream', $self->get('Log Stream'));
+ return $db;
+}
+
+sub get_dist_config_by_name ($$) {
+ my $self = shift;
+ my $dist_name = shift;
+
+ my $dist_config;
+ for my $dist_config_entry (@{$self->get_conf('DISTRIBUTIONS')}) {
+ if ($dist_config_entry->get('DIST_NAME') eq $dist_name) {
+ $dist_config = $dist_config_entry;
+ }
+ }
+
+ if (!$dist_config) {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "No configuration found for dist $dist_name\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Answer could not be processed, as dist=$dist_name does not match any of\n".
+ "the entries in the buildd configuration.\n");
+ }
+
+ return $dist_config;
+}
+
+sub get_arch_dist_config_by_name ($$) {
+ my $self = shift;
+ my $arch_name = shift;
+ my $dist_name = shift;
+
+ my $arch_config, my $dist_config;
+ for my $dist_config_entry (@{$self->get_conf('DISTRIBUTIONS')}) {
+ if ($dist_config_entry->get('BUILT_ARCHITECTURE') eq $arch_name &&
+ $dist_config_entry->get('DIST_NAME') eq $dist_name) {
+ $dist_config = $dist_config_entry;
+ }
+ }
+
+ if (!$dist_config) {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "No configuration found for arch=$arch_name, dist=$dist_name\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Answer could not be processed, as arch=$arch_name, dist=$dist_name".
+ "does not match any of the entries in the buildd configuration.\n");
+ }
+
+ return $dist_config;
+}
+
+sub log {
+ my $self = shift;
+
+ my $timestamp = localtime;
+ # omit weekday and year for brevity
+ $timestamp =~ s/^\w+\s(.*)\s\d+$/$1/;
+ my $prefix = "$timestamp $Buildd::progname\[" .
+ $self->get('PID') . "\]: ";
+
+ for my $line (split(/\n/, join("", @_))) {
+ Sbuild::Base::log($self, $prefix, $line, "\n");
+ }
+}
+
+1;
diff --git a/lib/Buildd/Client.pm b/lib/Buildd/Client.pm
new file mode 100644
index 0000000..14a9104
--- /dev/null
+++ b/lib/Buildd/Client.pm
@@ -0,0 +1,135 @@
+#
+# Client.pm: client library for wanna-build
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# 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/>.
+#
+#######################################################################
+
+package Buildd::Client;
+
+use strict;
+use warnings;
+
+use Sbuild qw($devnull);
+use Sbuild::ChrootRoot;
+use Cwd;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('SETUP', 0);
+
+ return $self;
+}
+
+sub setup {
+ my $self = shift;
+
+ if (!$self->get('SETUP')) {
+ my $host = Sbuild::ChrootRoot->new($self->get('Config'));
+ $host->begin_session();
+ $host->set('Log Stream', $self->get('Log Stream'));
+ $self->set('Host', $host);
+ $self->set('SETUP', 1);
+ }
+}
+
+sub get_query {
+ my $self = shift;
+
+ my @command = (@{$self->get_conf('WANNA_BUILD_SSH_CMD')}, 'wanna-build');
+ if ($self->get_conf('WANNA_BUILD_DB_NAME')) {
+ push(@command, "--database=" . $self->get_conf('WANNA_BUILD_DB_NAME'));
+ } else {
+ if ($self->get_conf('BUILT_ARCHITECTURE')) {
+ push(@command, "--arch=" . $self->get_conf('BUILT_ARCHITECTURE'));
+ }
+ if ($self->get_conf('DIST_NAME')) {
+ push(@command, "--dist=" . $self->get_conf('DIST_NAME'));
+ }
+ }
+ push(@command, "--user=" . $self->get_conf('WANNA_BUILD_DB_USER'))
+ if $self->get_conf('WANNA_BUILD_DB_USER');
+ push(@command, @_);
+
+ return @command;
+}
+
+sub run_query {
+ my $self = shift;
+
+ my @command = $self->get_query(@_);
+
+ $self->setup();
+
+ my $pipe = $self->get('Host')->run_command(
+ { COMMAND => [@command],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ });
+}
+
+sub pipe_query {
+ my $self = shift;
+
+ my @command = $self->get_query(@_);
+
+ $self->setup();
+
+ my $pipe = $self->get('Host')->pipe_command(
+ { COMMAND => [@command],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ DIR => getcwd(),
+ STREAMERR => \*STDOUT,
+ });
+
+ return $pipe;
+}
+
+sub pipe_query_out {
+ my $self = shift;
+
+ my @command = $self->get_query(@_);
+
+ $self->setup();
+
+ my $pipe = $self->get('Host')->pipe_command(
+ { COMMAND => [@command],
+ USER => $self->get_conf('USERNAME'),
+ PIPE => 'out',
+ STREAMOUT => $devnull,
+ PRIORITY => 0,
+ });
+
+ return $pipe;
+}
+
+1;
diff --git a/lib/Buildd/ClientConf.pm b/lib/Buildd/ClientConf.pm
new file mode 100644
index 0000000..e38eb7c
--- /dev/null
+++ b/lib/Buildd/ClientConf.pm
@@ -0,0 +1,177 @@
+#
+# ClientConf.pm: configuration library for wanna-build clients
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2006-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/>.
+#
+#######################################################################
+
+package Buildd::ClientConf;
+
+use strict;
+use warnings;
+
+use Sbuild::Sysconfig;
+use File::Spec;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(setup);
+}
+
+sub setup ($) {
+ my $conf = shift;
+
+ my $validate_program = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $program = $conf->get($key);
+
+ die "$key binary is not defined"
+ if !defined($program) || !$program;
+
+ # Emulate execvp behaviour by searching the binary in the PATH.
+ my @paths = split(/:/, $ENV{'PATH'});
+ # Also consider the empty path for absolute locations.
+ push (@paths, '');
+ my $found = 0;
+ foreach my $path (@paths) {
+ $found = 1 if (-x File::Spec->catfile($path, $program));
+ }
+
+ die "$key binary '$program' does not exist or is not executable"
+ if !$found;
+ };
+
+ my $validate_ssh = sub {
+ my $conf = shift;
+ my $entry = shift;
+
+# TODO: Provide self, config and entry contexts, which functions to
+# get at needed data. Provide generic configuration functions.
+#
+ $validate_program->($conf, $conf->{'KEYS'}->{'SSH'});
+
+ my $ssh = $conf->get('SSH');
+ my $sshuser = $conf->get('WANNA_BUILD_SSH_USER');
+ my $sshhost = $conf->get('WANNA_BUILD_SSH_HOST');
+ my @sshoptions = @{$conf->get('WANNA_BUILD_SSH_OPTIONS')};
+ my $sshsocket = $conf->get('WANNA_BUILD_SSH_SOCKET');
+
+ my @command = ();
+
+ if ($sshhost) {
+ push (@command, $ssh);
+ push (@command, '-l', $sshuser) if $sshuser;
+ push (@command, '-S', $sshsocket) if $sshsocket;
+ push (@command, @sshoptions) if @sshoptions;
+ push (@command, $sshhost);
+ }
+
+ $conf->set('WANNA_BUILD_SSH_CMD', \@command);
+ };
+
+ our $HOME = $conf->get('HOME');
+ my $arch = $conf->get('ARCH');
+
+ my %db_keys = (
+ 'SSH' => {
+ TYPE => 'STRING',
+ VARNAME => 'ssh',
+ GROUP => 'Programs',
+ DEFAULT => 'ssh',
+ CHECK => $validate_ssh,
+ HELP => 'Path to ssh binary'
+ },
+ 'WANNA_BUILD_SSH_CMD' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => '',
+ HELP => 'Command to run wanna-build (set automatically from the other wanna-build options)'
+ },
+ 'WANNA_BUILD_SSH_USER' => {
+ TYPE => 'STRING',
+ VARNAME => 'wanna_build_ssh_user',
+ GROUP => 'wanna-build client',
+ DEFAULT => '',
+ CHECK => $validate_ssh,
+ HELP => 'Username for SSH connection'
+ },
+ 'WANNA_BUILD_SSH_HOST' => {
+ TYPE => 'STRING',
+ VARNAME => 'wanna_build_ssh_host',
+ GROUP => 'wanna-build client',
+ DEFAULT => '',
+ CHECK => $validate_ssh,
+ HELP => 'Host for SSH connection'
+ },
+ 'WANNA_BUILD_SSH_SOCKET' => {
+ TYPE => 'STRING',
+ VARNAME => 'wanna_build_ssh_socket',
+ GROUP => 'wanna-build client',
+ DEFAULT => '',
+ CHECK => $validate_ssh,
+ HELP => 'Socket for SSH connection'
+ },
+ 'WANNA_BUILD_SSH_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'wanna_build_ssh_options',
+ GROUP => 'wanna-build client',
+ DEFAULT => [],
+ CHECK => $validate_ssh,
+ HELP => 'SSH options. Note this is an array reference.'
+ },
+ 'WANNA_BUILD_DB_NAME' => {
+ TYPE => 'STRING',
+ VARNAME => 'wanna_build_db_name',
+ GROUP => 'wanna-build client',
+ DEFAULT => undef,
+ HELP => 'Database name'
+ },
+ 'WANNA_BUILD_DB_USER' => {
+ TYPE => 'STRING',
+ VARNAME => 'wanna_build_db_user',
+ GROUP => 'wanna-build client',
+ DEFAULT => $conf->get('USERNAME'),
+ # arch:all packages must not differ depending on which user is
+ # building the source package, so don't show the default for
+ # example config and man page generation
+ IGNORE_DEFAULT => 1,
+ HELP => 'Database user'
+ },
+ 'BUILT_ARCHITECTURE' => {
+ TYPE => 'STRING',
+ VARNAME => 'wanna_build_built_architecture',
+ GROUP => 'wanna-build client',
+ DEFAULT => $arch,
+ # the $native_arch is different depending on the machine where
+ # sbuild is built but arch:all packages must not differ depending on
+ # the architecture they are built on, so don't show the default for
+ # example config and man page generation
+ IGNORE_DEFAULT => 1,
+ HELP => 'Architecture for database'
+ });
+
+ $conf->set_allowed_keys(\%db_keys);
+}
+
+1;
diff --git a/lib/Buildd/Conf.pm b/lib/Buildd/Conf.pm
new file mode 100644
index 0000000..3dca902
--- /dev/null
+++ b/lib/Buildd/Conf.pm
@@ -0,0 +1,628 @@
+#
+# Conf.pm: configuration library for buildd
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2006-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/>.
+#
+#######################################################################
+
+package Buildd::Conf;
+
+use strict;
+use warnings;
+
+use Buildd::DistConf qw();
+use Buildd::UploadQueueConf qw();
+use Sbuild::ConfBase;
+use Sbuild::Sysconfig;
+use Buildd::ClientConf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw($reread_config new setup read);
+}
+
+our $reread_config = 0;
+
+sub setup ($);
+sub read ($);
+
+sub new {
+ my $conf = Sbuild::ConfBase->new(@_);
+ Buildd::Conf::setup($conf);
+ Buildd::Conf::read($conf);
+
+ return $conf;
+}
+
+sub setup ($) {
+ my $conf = shift;
+
+ my $validate_program = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $program = $conf->get($key);
+
+ die "$key binary is not defined"
+ if !defined($program) || !$program;
+
+ # Emulate execvp behaviour by searching the binary in the PATH.
+ my @paths = split(/:/, $conf->get('PATH'));
+ # Also consider the empty path for absolute locations.
+ push (@paths, '');
+ my $found = 0;
+ foreach my $path (@paths) {
+ $found = 1 if (-x File::Spec->catfile($path, $program));
+ }
+
+ die "$key binary '$program' does not exist or is not executable"
+ if !$found;
+ };
+
+ my $validate_directory = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $directory = $conf->get($key);
+
+ die "$key directory is not defined"
+ if !defined($directory) || !$directory;
+
+ die "$key directory '$directory' does not exist"
+ if !-d $directory;
+ };
+
+ our $HOME = $conf->get('HOME');
+ $main::HOME = $HOME; # TODO: Remove once Buildd.pm uses $conf
+ my $arch = $conf->get('ARCH');
+
+ my %buildd_keys = (
+ 'ADMIN_MAIL' => {
+ TYPE => 'STRING',
+ VARNAME => 'admin_mail',
+ GROUP => 'Mail',
+ DEFAULT => 'root',
+ HELP => 'email address for admin'
+ },
+ 'APT_GET' => {
+ TYPE => 'STRING',
+ VARNAME => 'apt_get',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'apt-get',
+ HELP => 'Path to apt-get binary'
+ },
+ 'DPKG_FILE_SUFFIX' => {
+ TYPE => 'STRING',
+ VARNAME => 'dpkg_file_suffix',
+ GROUP => 'Programs',
+ DEFAULT => '',
+ HELP => 'Value for the sbuild dpkg-file-suffix option, to be passed on to sbuild',
+ },
+ 'BUILD_LOG_KEEP' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'build_log_keep',
+ GROUP => 'Watcher',
+ DEFAULT => 2,
+ HELP => 'Number of days until build logs are archived'
+ },
+ 'DAEMON_LOG_FILE' => {
+ TYPE => 'STRING',
+ VARNAME => 'daemon_log_file',
+ GROUP => 'Daemon',
+ IGNORE_DEFAULT => 1, # Don't dump the current home
+ DEFAULT => "$HOME/daemon.log",
+ HELP => 'Main buildd daemon log file'
+ },
+ 'DAEMON_LOG_KEEP' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'daemon_log_keep',
+ GROUP => 'Watcher',
+ DEFAULT => 7,
+ HELP => 'Number of days until old daemon logs are archived in a .tar.gz file'
+ },
+ 'DAEMON_LOG_ROTATE' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'daemon_log_rotate',
+ GROUP => 'Watcher',
+ DEFAULT => 1,
+ HELP => 'Number how many days until daemon logs are rotated (one is kept as daemon.log.old, others are moved to old-logs and gzipped)'
+ },
+ 'DAEMON_LOG_SEND' => {
+ TYPE => 'BOOL',
+ VARNAME => 'daemon_log_send',
+ GROUP => 'Watcher',
+ DEFAULT => 1,
+ HELP => 'email rotated daemon logs to the admin?'
+ },
+ 'DELAY_AFTER_GIVE_BACK' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'delay_after_give_back',
+ GROUP => 'Daemon',
+ DEFAULT => 8 * 60, # 8 hours
+ HELP => 'Time to avoid packages that have automatically been given back by sbuild (in minutes)'
+ },
+ 'ERROR_MAIL_WINDOW' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'error_mail_window',
+ GROUP => 'Mail',
+ DEFAULT => 8*60*60,
+ HELP => 'If more than five error mails are received within the specified time (in seconds), do not forward (to avoid possible mail loops)'
+ },
+ 'IDLE_SLEEP_TIME' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'idle_sleep_time',
+ GROUP => 'Daemon',
+ DEFAULT => 5*60,
+ HELP => 'Time to sleep when idle (in seconds) between wanna-build --list=needs-build calls)'
+ },
+ 'LOG_QUEUED_MESSAGES' => {
+ TYPE => 'BOOL',
+ VARNAME => 'log_queued_messages',
+ GROUP => 'Mail',
+ DEFAULT => 0,
+ HELP => 'Log success messages from upload queue daemon?'
+ },
+ 'MAX_SBUILD_FAILS' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'max_sbuild_fails',
+ GROUP => 'Daemon',
+ DEFAULT => 2,
+ HELP => 'Maximum number of times sbuild can fail before sleeping'
+ },
+ 'MIN_FREE_SPACE' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'min_free_space',
+ GROUP => 'Daemon',
+ DEFAULT => 50*1024,
+ HELP => 'Minimum free space (in KiB) on build filesystem'
+ },
+ 'NICE_LEVEL' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'nice_level',
+ GROUP => 'Build options',
+ DEFAULT => 10,
+ HELP => 'Nice level to run sbuild. Dedicated build daemons should not be niced.'
+ },
+ 'NO_DETACH' => {
+ TYPE => 'BOOL',
+ VARNAME => 'no_detach',
+ GROUP => 'Daemon',
+ DEFAULT => 0,
+ HELP => 'Disable becoming a daemon, for debugging purposes. Set to 1 to stop daemonising, otherwise set to 0 to become a daemon.'
+ },
+ 'NO_WARN_PATTERN' => {
+ TYPE => 'STRING',
+ VARNAME => 'no_warn_pattern',
+ GROUP => 'Watcher',
+ DEFAULT => '^build/(SKIP|REDO|SBUILD-GIVEN-BACK|buildd\.pid|[^/]*.ssh|chroot-[^/]*|current-[^/]*)$',
+ HELP => 'Don\'t complain about old files if they match the regexp.'
+ },
+ 'PIDFILE' => {
+ TYPE => 'STRING',
+ VARNAME => 'pidfile',
+ GROUP => 'Daemon',
+# Set once running as a system service.
+# DEFAULT => "${Sbuild::Sysconfig::paths{'LOCALSTATEDIR'}/run/buildd.pid"
+ IGNORE_DEFAULT => 1, # Don't dump the current home
+ DEFAULT => "$HOME/build/buildd.pid",
+ HELP => 'PID file to identify running daemon.'
+ },
+ 'PKG_LOG_KEEP' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'pkg_log_keep',
+ GROUP => 'Watcher',
+ DEFAULT => 7,
+ HELP => 'Number of days until to package logs are archived'
+ },
+ 'SHOULD_BUILD_MSGS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'should_build_msgs',
+ GROUP => 'Daemon',
+ DEFAULT => 1,
+ HELP => 'Should buildd send "Should I build" messages?'
+ },
+ 'STATISTICS_MAIL' => {
+ TYPE => 'STRING',
+ VARNAME => 'statistics_mail',
+ GROUP => 'Watcher',
+ DEFAULT => 'root',
+ HELP => 'email address for statistics summaries'
+ },
+ 'STATISTICS_PERIOD' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'statistics_period',
+ GROUP => 'Watcher',
+ DEFAULT => 7,
+ HELP => 'Period for statistic summaries (days)'
+ },
+ 'SUDO' => {
+ TYPE => 'STRING',
+ VARNAME => 'sudo',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'sudo',
+ HELP => 'Path to sudo binary'
+ },
+ 'WARNING_AGE' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'warning_age',
+ GROUP => 'Watcher',
+ DEFAULT => 7,
+ HELP => 'Age (in days) after which a warning is issued for files in upload and dirs in build'
+ },
+ 'CONFIG_TIME' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'config_time',
+ GROUP => '__INTERNAL',
+ DEFAULT => {},
+ HELP => 'Time configuration was last read'
+ },
+ 'DISTRIBUTIONS' => {
+ TYPE => 'ARRAY:HASH:SCALAR',
+ VARNAME => 'distributions',
+ GROUP => 'Build options',
+ DEFAULT => [],
+ IGNORE_DEFAULT => 1, # Don't dump class to config
+ HELP => 'List of distributions that buildd should take packages from',
+ EXAMPLE =>
+'$distributions = [
+ {
+ # name of the suite to build (also used to query wanna-build)
+ dist_name => ["unstable", "testing"],
+
+ # architecture to be built (will be passed to sbuild and can be
+ # used to compute wanna_build_db_name)
+ built_architecture => undef,
+
+ # host on which wanna-build is run
+ wanna_build_ssh_host => "buildd.debian.org",
+
+ # user as who we are going to connect to the host running wanna-build
+ wanna_build_ssh_user => "buildd_arch",
+
+ # SSH control socket path for ssh -S option
+ wanna_build_ssh_socket => "",
+
+ # Additional SSH options used when connecting
+ wanna_build_ssh_options => [],
+
+ # database used for wanna-build
+ wanna_build_db_name => "arch/build-db",
+
+ # Username to use for wanna-build.
+ wanna_build_db_user => $Buildd::username,
+
+ # Local queue directory where binaries are stored before uploaded
+ # by dupload. You need to configure this directory in
+ # @upload_queues to get packages uploaded from there.
+ dupload_local_queue_dir => "upload",
+
+ # list of packages which shouldn\'t be picked up by buildd
+ no_auto_build => [],
+
+ # list of packages which should only be taken if there absolutely
+ # nothing else to do (probably packages included in no_auto_build
+ # because they take too long)
+ weak_no_auto_build => [],
+
+ # regex used to filter out unwanted packages:
+ #no_build_regex => "^(contrib/|non-free/)?non-US/",
+
+ # regex used to filter packages to build:
+ #build_regex => "",
+
+ # mail addr of buildd admin handling packages from this distribution
+ logs_mailed_to => $admin_mail,
+
+ # schroot name (or alias) of the chrooted environment to use for
+ # building (will be passed to sbuild). sbuild\'s default is
+ # the first of $distribution-$arch-sbuild, $distribution-sbuild,
+ # $distribution-$arch and $distribution.
+ sbuild_chroot => undef,
+
+ }
+];'
+ },
+ 'UPLOAD_QUEUES' => {
+ TYPE => 'ARRAY:HASH:SCALAR',
+ VARNAME => 'upload_queues',
+ GROUP => 'Uploader',
+ DEFAULT => [],
+ IGNORE_DEFAULT => 1, # Don't dump class to config
+ HELP => 'Package upload queues',
+ EXAMPLE =>
+'$upload_queues = [
+ {
+ # Local queue directory where binaries are stored before uploaded
+ # by dupload.
+ dupload_local_queue_dir => "upload",
+
+ # Upload site for buildd-upload to pass to dupload(1); see
+ # /etc/dupload.conf for possible values.
+ dupload_archive_name => "anonymous-ftp-master",
+ },
+
+ {
+ # Local queue directory where binaries are stored before uploaded
+ # by dupload.
+ dupload_local_queue_dir => "upload-security",
+
+ # Upload site for buildd-upload to pass to dupload(1); see
+ # /etc/dupload.conf for possible values.
+ dupload_archive_name => "security",
+ }
+];'
+ });
+
+ $conf->set_allowed_keys(\%buildd_keys);
+ Buildd::ClientConf::setup($conf);
+}
+
+sub read ($) {
+ my $conf = shift;
+
+ my $HOME = $conf->get('HOME');
+
+ my $global = $Sbuild::Sysconfig::paths{'BUILDD_CONF'};
+ my $user = "$HOME/.builddrc";
+ my %config_time = ();
+ my $user_time = 0;
+
+ my $reread = 0;
+
+ sub ST_MTIME () { 9 }
+
+ my @config_files = ($global, $user);
+
+ $reread = 1 if $reread_config;
+
+ foreach (@config_files) {
+ if (-r $_) {
+ $config_time{$_} = 0;
+ my @stat = stat($_);
+ if (!defined($conf->get('CONFIG_TIME')->{$_}) ||
+ $conf->get('CONFIG_TIME')->{$_} < $stat[ST_MTIME]) {
+ $config_time{$_} = $stat[ST_MTIME];
+ $reread = 1;
+ }
+ }
+ }
+
+ # For compatibility only. Non-scalars are deprecated.
+ my $deprecated_init = <<END;
+# Variables are undefined, so config will default to DEFAULT if unset.
+my \$defaults;
+my \@distributions;
+undef \@distributions;
+my \@upload_queues;
+undef \@upload_queues;
+
+#legacy fields:
+my \@weak_no_auto_build;
+undef \@weak_no_auto_build;
+my \$build_regex = undef; # Should this be user settable?
+my \@no_auto_build;
+undef \@no_auto_build;
+my \$no_build_regex = undef;
+my \@take_from_dists;
+undef \@take_from_dists;
+my \$sshcmd = undef;
+my \$sshsocket = undef;
+my \$wanna_build_user = undef;
+my \$wanna_build_dbbase = undef;
+END
+
+ my $deprecated_setup = '';
+
+ my $custom_setup = <<END;
+if (\$sshcmd && \$sshcmd =~ /^\\s*(\\S+)\\s+(.+)/) {
+ my \$rest = \$2;
+ \$conf->set('SSH', \$1);
+
+ #Try to pry the user out:
+ if (\$rest =~ /(-l\\s*(\\S+))\\s+/) {
+ \$wanna_build_ssh_user = \$2;
+ #purge this from the rest:
+ \$rest =~ s/\\Q\$1//;
+ } elsif (\$rest =~ /\\s+(\\S+)\@/) {
+ \$wanna_build_ssh_user = \$1;
+ \$rest =~ s/\\Q\$1\\E\@//;
+ }
+
+ #Hope that the last argument is the host:
+ if (\$rest =~ /\\s+(\\S+)\\s*\$/) {
+ \$wanna_build_ssh_host = \$1;
+ \$rest =~ s/\\Q\$1//;
+ }
+
+ #rest should be options:
+ if (\$rest !~ /\\s*/) {
+ \$wanna_build_ssh_options = [split \$rest];
+ }
+}
+
+if (\$sshsocket) {
+ \$wanna_build_ssh_socket = \$sshsocket;
+}
+
+if (\$wanna_build_user) {
+ \$wanna_build_db_user = \$wanna_build_user;
+}
+
+if (\$wanna_build_dbbase) {
+ \$wanna_build_db_name = \$wanna_build_dbbase;
+}
+
+#Convert old config, if needed:
+my \@distributions_info;
+if (\@take_from_dists) {
+ for my \$dist (\@take_from_dists) {
+ my \%entry;
+
+ \$entry{DIST_NAME} = \$dist;
+ \$entry{SSH} = \$ssh;
+
+ if (\$dist =~ /security/) {
+ \$entry{DUPLOAD_LOCAL_QUEUE_DIR} = 'upload-security';
+ }
+ if (\$build_regex) {
+ \$entry{BUILD_REGEX} = \$build_regex;
+ }
+ if (\$no_build_regex) {
+ \$entry{NO_BUILD_REGEX} = \$build_regex;
+ }
+ if (\@no_auto_build) {
+ \$entry{NO_AUTO_BUILD} = \\\@no_auto_build;
+ }
+ if (\@weak_no_auto_build) {
+ \$entry{WEAK_NO_AUTO_BUILD} = \\\@weak_no_auto_build;
+ }
+
+ \$entry{WANNA_BUILD_DB_NAME} = \$wanna_build_db_name;
+ \$entry{WANNA_BUILD_DB_USER} = \$wanna_build_db_user;
+ \$entry{WANNA_BUILD_SSH_HOST} = \$wanna_build_ssh_host;
+ \$entry{WANNA_BUILD_SSH_USER} = \$wanna_build_ssh_user;
+ \$entry{WANNA_BUILD_SSH_SOCKET} = \$wanna_build_ssh_socket;
+ \$entry{WANNA_BUILD_SSH_OPTIONS} = \$wanna_build_ssh_options;
+ \$entry{WANNA_BUILD_API} = 0;
+
+ my \$dist_config = Buildd::DistConf::new_hash(CHECK=>$conf->{'CHECK'},
+ HASH=>\\\%entry);
+
+ push \@distributions_info, \$dist_config;
+ }
+} else {
+ my \@dists = ();
+ push \@dists, \@{\$distributions} if defined \$distributions;
+
+ if (\@distributions) {
+ warn 'W: \@distributions is deprecated; please use the array reference \$distributions[]\n';
+ push \@dists, \@distributions;
+ }
+
+ for my \$raw_entry (\@dists) {
+ my \%entry;
+ my \@dist_names;
+
+ #Find out for which distributions this entry is intended:
+ for my \$key (keys \%\$raw_entry) {
+ if (uc(\$key) eq "DIST_NAME") {
+ if (ref(\$raw_entry->{\$key}) eq "ARRAY") {
+ push \@dist_names, \@{\$raw_entry->{\$key}};
+ } else {
+ push \@dist_names, \$raw_entry->{\$key};
+ }
+ }
+ }
+
+ for my \$key (keys \%\$raw_entry) {
+ if (uc(\$key) ne "DIST_NAME") {
+ \$entry{uc(\$key)} = \$raw_entry->{\$key};
+ }
+ }
+
+ for my \$key (keys \%\$defaults) {
+ if (uc(\$key) ne "DIST_NAME" && not defined \$entry{uc(\$key)}) {
+ \$entry{uc(\$key)} = \$defaults->{\$key};
+ }
+ }
+
+ \$entry{WANNA_BUILD_API} //= 1;
+
+
+ #We need this to pass this to Buildd::Client:
+ \$entry{SSH} = \$ssh;
+
+ #Make one entry per distribution, it's easier later on:
+ for my \$dist (\@dist_names) {
+ \$entry{'DIST_NAME'} = \$dist;
+ my \$dist_config = Buildd::DistConf::new_hash(HASH=>\\\%entry);
+ push \@distributions_info, \$dist_config;
+ }
+ }
+}
+
+\$conf->set('DISTRIBUTIONS', \\\@distributions_info);
+
+my \@queues = ();
+push \@queues, \@{\$upload_queues} if defined \$upload_queues;
+
+if (\@upload_queues) {
+ warn 'W: \@upload_queues is deprecated; please use the array reference \$upload_queues[]\n';
+ push \@queues, \@upload_queues;
+}
+
+if (\@queues) {
+ my \@upload_queue_configs;
+ for my \$raw_entry (\@queues) {
+ my \%entry;
+ for my \$key (keys \%\$raw_entry) {
+ \$entry{uc(\$key)} = \$raw_entry->{\$key};
+ }
+
+ my \$queue_config = Buildd::UploadQueueConf::new_hash(CHECK=>$conf->{'CHECK'},
+ HASH=>\\\%entry);
+
+ push \@upload_queue_configs, \$queue_config;
+ }
+ \$conf->set('UPLOAD_QUEUES', \\\@upload_queue_configs);
+} else {
+ push \@{\$conf->get('UPLOAD_QUEUES')},
+ Buildd::UploadQueueConf::new_hash(CHECK=>$conf->{'CHECK'},
+ HASH=>
+ {
+ DUPLOAD_LOCAL_QUEUE_DIR => 'upload',
+ DUPLOAD_ARCHIVE_NAME => 'anonymous-ftp-master'
+ }
+ ),
+ Buildd::UploadQueueConf::new_hash(CHECK=>$conf->{'CHECK'},
+ HASH=>
+ {
+ DUPLOAD_LOCAL_QUEUE_DIR => 'upload-security',
+ DUPLOAD_ARCHIVE_NAME => 'security'
+ }
+ );
+}
+
+# Set here to allow user to override.
+if (-t STDIN && -t STDOUT && \$conf->get('NO_DETACH')) {
+ \$conf->_set_default('VERBOSE', 1);
+} else {
+ \$conf->_set_default('VERBOSE', 0);
+}
+END
+
+ $conf->read(\@config_files, $deprecated_init, $deprecated_setup,
+ $custom_setup);
+
+ # Update times
+ if ($reread) {
+ foreach (@config_files) {
+ if (-r $_) {
+ $conf->get('CONFIG_TIME')->{$_} = $config_time{$_};
+ }
+ }
+ }
+}
+
+1;
diff --git a/lib/Buildd/Daemon.pm b/lib/Buildd/Daemon.pm
new file mode 100644
index 0000000..dc07254
--- /dev/null
+++ b/lib/Buildd/Daemon.pm
@@ -0,0 +1,998 @@
+# 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/>.
+#
+#######################################################################
+
+package Buildd::Daemon;
+
+use strict;
+use warnings;
+
+use POSIX;
+use Buildd qw(isin lock_file unlock_file send_mail exitstatus);
+use Buildd::Conf qw();
+use Buildd::Base;
+use Sbuild qw($devnull df);
+use Sbuild::Sysconfig;
+use Sbuild::ChrootRoot;
+use Buildd::Client;
+use Cwd;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Buildd::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('Daemon', 0);
+
+ return $self;
+}
+
+sub ST_MTIME () { 9 }
+
+sub run {
+ my $self = shift;
+
+ my $host = Sbuild::ChrootRoot->new($self->get('Config'));
+ $host->set('Log Stream', $self->get('Log Stream'));
+ $self->set('Host', $host);
+ $host->begin_session() or die "Can't begin session\n";
+
+ my $my_binary = $0;
+ $my_binary = cwd . "/" . $my_binary if $my_binary !~ m,^/,;
+ $self->set('MY_BINARY', $my_binary);
+
+ my @bin_stats = stat( $my_binary );
+ die "Cannot stat $my_binary: $!\n" if !@bin_stats;
+ $self->set('MY_BINARY_TIME', $bin_stats[ST_MTIME]);
+
+ chdir( $self->get_conf('HOME') . "/build" )
+ or die "Can't cd to " . $self->get_conf('HOME') . "/build: $!\n";
+
+ open( STDIN, "</dev/null" )
+ or die "$0: can't redirect stdin to /dev/null: $!\n";
+
+ if (open( PID, "<" . $self->get_conf('PIDFILE') )) {
+ my $pid = <PID>;
+ close( PID );
+ $pid =~ /^[[:space:]]*(\d+)/; $pid = $1;
+ if (!$pid || (kill( 0, $pid ) == 0 && $! == ESRCH)) {
+ warn "Removing stale pid file (process $pid dead)\n";
+ }
+ else {
+ die "Another buildd (pid $pid) is already running.\n";
+ }
+ }
+
+ if (!@{$self->get_conf('DISTRIBUTIONS')}) {
+ die "distribution list is empty, aborting.";
+ }
+
+ if (!$self->get_conf('NO_DETACH')) {
+ defined(my $pid = fork) or die "can't fork: $!\n";
+ exit if $pid; # parent exits
+ setsid or die "can't start a new session: $!\n";
+ }
+
+ $self->set('PID', $$); # Needed for cleanup
+ $self->set('Daemon', 1);
+
+ open( PID, ">" . $self->get_conf('PIDFILE') )
+ or die "can't create " . $self->get_conf('PIDFILE') . ": $!\n";
+ printf PID "%5d\n", $self->get('PID');
+ close( PID );
+
+ $self->log("Daemon started. (pid=$$)\n");
+
+ undef $ENV{'DISPLAY'};
+
+# the main loop
+ MAINLOOP:
+ while( 1 ) {
+ $self->check_restart();
+
+ my ( $dist_config, $pkg_ver) = get_next_REDO($self);
+ $self->do_build( $dist_config, $pkg_ver) if $pkg_ver;
+ next MAINLOOP if $pkg_ver;
+
+ ( $dist_config, $pkg_ver) = get_next_WANNABUILD($self);
+ $self->do_build( $dist_config, $pkg_ver) if $pkg_ver;
+ next MAINLOOP if $pkg_ver;
+
+ # sleep a little bit if there was nothing to do this time
+ $self->log("Nothing to do -- sleeping " .
+ $self->get_conf('IDLE_SLEEP_TIME') . " seconds\n");
+ my $idle_start_time = time;
+ sleep( $self->get_conf('IDLE_SLEEP_TIME') );
+ my $idle_end_time = time;
+ $self->write_stats("idle-time", $idle_end_time - $idle_start_time);
+ }
+
+ return 0;
+}
+
+sub get_next_WANNABUILD {
+ my $self = shift;
+ foreach my $dist_config (@{$self->get_conf('DISTRIBUTIONS')}) {
+ $self->check_ssh_master($dist_config);
+ my $dist_name = $dist_config->get('DIST_NAME');
+ my %givenback = $self->read_givenback();
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query(
+ ($dist_config->get('WANNA_BUILD_API') ? '--api '.$dist_config->get('WANNA_BUILD_API') : ''),
+ '--list=needs-build',
+ ($dist_config->get('WANNA_BUILD_MIN_AGE') ? '--min-age '.$dist_config->get('WANNA_BUILD_MIN_AGE') : ''),
+ );
+ if (!$pipe) {
+ $self->log("Can't spawn wanna-build --list=needs-build: $!\n");
+ next MAINLOOP;
+ }
+
+ my($pkg_ver, $total, $nonex, $lowprio_pkg_ver);
+ while( <$pipe> ) {
+ my $socket = $dist_config->get('WANNA_BUILD_SSH_SOCKET');
+ if ($socket &&
+ (/^Couldn't connect to $socket: Connection refused[\r]?$/ ||
+ /^Control socket connect\($socket\): Connection refused[\r]?$/)) {
+ unlink($socket);
+ $self->check_ssh_master($dist_config);
+ next;
+ }
+ elsif (/^Total (\d+) package/) {
+ $total = $1;
+ next;
+ }
+ elsif (/^Database for \S+ doesn.t exist/) {
+ $nonex = 1;
+ }
+ next if $nonex;
+ next if defined($pkg_ver); #we only want one!
+ my @line = (split( /\s+/, $_));
+ my $pv = $line[0];
+ my $no_build_regex = $dist_config->get('NO_BUILD_REGEX');
+ my $build_regex = $dist_config->get('BUILD_REGEX');
+ next if $no_build_regex && $pv =~ m,$no_build_regex,;
+ next if $build_regex && $pv !~ m,$build_regex,;
+ $pv =~ s,^.*/,,;
+ my $p;
+ ($p = $pv) =~ s/_.*$//;
+ next if isin( $p, @{$dist_config->get('NO_AUTO_BUILD')} );
+ next if $givenback{$pv};
+ if (isin( $p, @{$dist_config->get('WEAK_NO_AUTO_BUILD')} )) {
+ # only consider the first lowprio item if there are
+ # multiple ones
+ next if defined($lowprio_pkg_ver);
+ $lowprio_pkg_ver = $pv;
+ next;
+ }
+ $pkg_ver = $pv;
+ }
+ close( $pipe );
+ next if $nonex;
+ if ($?) {
+ $self->log("wanna-build --list=needs-build --dist=${dist_name} failed; status ",
+ exitstatus($?), "\n");
+ next;
+ }
+ $self->log("${dist_name}: total $total packages to build.\n") if defined($total);
+
+ # Build weak_no_auto packages before the next dist
+ if (!defined($pkg_ver) && defined($lowprio_pkg_ver)) {
+ $pkg_ver = $lowprio_pkg_ver;
+ }
+
+ next if !defined($pkg_ver);
+ my $todo = $self->do_wanna_build( $dist_config, $pkg_ver );
+ last if !$todo;
+ return ( $dist_config, $todo );
+ }
+}
+
+sub get_next_REDO {
+ my $self = shift;
+ my ( $dist_config, $pkg_ver);
+ foreach my $current_dist_config (@{$self->get_conf('DISTRIBUTIONS')}) {
+ $pkg_ver = $self->get_from_REDO( $current_dist_config );
+ $dist_config = $current_dist_config;
+ last if defined($pkg_ver);
+ }
+ return ( $dist_config, $pkg_ver);
+}
+
+
+sub get_from_REDO {
+ my $self = shift;
+ my $wanted_dist_config = shift;
+ my $ret = undef;
+ local( *F );
+
+ lock_file( "REDO" );
+ goto end if ! -f "REDO";
+ if (!open( F, "<REDO" )) {
+ $self->log("File REDO exists, but can't open it: $!\n");
+ goto end;
+ }
+ my @lines = <F>;
+ close( F );
+
+ $self->block_signals();
+ if (!open( F, ">REDO" )) {
+ $self->log("Can't open REDO for writing: $!\n",
+ "Raw contents:\n@lines\n");
+ goto end;
+ }
+ foreach (@lines) {
+ if (!/^(\S+)\s+(\S+)(?:\s*|\s+(\d+)\s+(\S.*))?$/) {
+ $self->log("Ignoring/deleting bad line in REDO: $_");
+ next;
+ }
+ my($pkg, $dist, $binNMUver, $changelog) = ($1, $2, $3, $4);
+ if ($dist eq $wanted_dist_config->get('DIST_NAME') && !defined($ret)) {
+ $ret = {'pv' => $pkg };
+ if (defined $binNMUver) {
+ $ret->{'changelog'} = $changelog;
+ $ret->{'binNMU'} = $binNMUver;
+ }
+ } else {
+ print F $_;
+ }
+ }
+ close( F );
+
+ end:
+ unlock_file( "REDO" );
+ $self->unblock_signals();
+ return $ret;
+}
+
+sub add_given_back ($$) {
+ my $self = shift;
+ my $pkg_ver = shift;
+
+ local( *F );
+ lock_file("SBUILD-GIVEN-BACK", 0);
+
+ if (open( F, ">>SBUILD-GIVEN-BACK" )) {
+ print F $pkg_ver . " " . time() . "\n";
+ close( F );
+ } else {
+ $self->log("Can't open SBUILD-GIVEN-BACK: $!\n");
+ }
+
+ unlock_file("SBUILD-GIVEN-BACK");
+}
+
+sub read_givenback {
+ my $self = shift;
+
+ my %gb;
+ my $now = time;
+ local( *F );
+
+ lock_file( "SBUILD-GIVEN-BACK" );
+
+ if (open( F, "<SBUILD-GIVEN-BACK" )) {
+ %gb = map { split } <F>;
+ close( F );
+ }
+
+ if (open( F, ">SBUILD-GIVEN-BACK" )) {
+ foreach (keys %gb) {
+ if ($now - $gb{$_} > $self->get_conf('DELAY_AFTER_GIVE_BACK') *60) {
+ delete $gb{$_};
+ }
+ else {
+ print F "$_ $gb{$_}\n";
+ }
+ }
+ close( F );
+ }
+ else {
+ $self->log("Can't open SBUILD-GIVEN-BACK: $!\n");
+ }
+
+ unlock:
+ unlock_file( "SBUILD-GIVEN-BACK" );
+ return %gb;
+}
+
+sub do_wanna_build {
+ my $self = shift;
+
+ my $dist_config = shift;
+ my $pkgver = shift;
+ my @output = ();
+ my $ret = undef;
+ my $n = 0;
+
+ $self->block_signals();
+
+ my $db = $self->get_db_handle($dist_config);
+ if ($dist_config->get('WANNA_BUILD_API') >= 1) {
+ use YAML::Tiny;
+ my $pipe = $db->pipe_query(
+ '--api '.$dist_config->get('WANNA_BUILD_API'),
+ $pkgver);
+ unless ($pipe) {
+ $self->unblock_signals();
+ $self->log("Can't spawn wanna-build: $!\n");
+ return undef;
+ }
+ local $/ = undef;
+ my $yaml = <$pipe>;
+ $yaml =~ s,^update transactions:.*$,,m; # get rid of simulate output in case simulate is specified above
+ $self->log($yaml);
+ $yaml = YAML::Tiny->read_string($yaml);
+ $yaml = $yaml->[0];
+ foreach my $pkgv (@$yaml) {
+ my $pkg = (keys %$pkgv)[0];
+ my $pkgd;
+ foreach my $k (@{$pkgv->{$pkg}}) {
+ foreach my $l (keys %$k) {
+ $pkgd->{$l} = $k->{$l};
+ }
+ };
+ if ($pkgd->{'status'} ne 'ok') {
+ $self->log("Can't take $pkg: $pkgd->{'status'}\n");
+ next;
+ }
+ $ret = { 'pv' => $pkgver };
+ # fix SHOULD_BUILD_MSGS
+# if ($self->get_conf('SHOULD_BUILD_MSGS')) {
+# $self->handle_prevfailed( $dist_config, grep( /^\Q$pkg\E_/, @_ ) );
+# } else {
+# push( @output, grep( /^\Q$pkg\E_/, @_ ) );
+ my $fields = { 'changelog' => 'extra-changelog',
+ 'binNMU' => 'binNMU',
+ 'extra-depends' => 'extra-depends',
+ 'extra-conflicts' => 'extra-conflicts',
+ 'build_dep_resolver' => 'build_dep_resolver',
+ 'arch_all' => 'arch_all',
+ 'mail_logs' => 'mail_logs',
+ };
+ for my $f (keys %$fields) {
+ $ret->{$f} = $pkgd->{$fields->{$f}} if $pkgd->{$fields->{$f}};
+ }
+ last;
+ }
+ close( $pipe );
+ $self->unblock_signals();
+ $self->write_stats("taken", $n) if $n;
+ return $ret;
+ }
+ my $pipe = $db->pipe_query(
+ '-v',
+ $pkgver);
+ if ($pipe) {
+ while( <$pipe> ) {
+ next if /^wanna-build Revision/;
+ if (/^(\S+):\s*ok/) {
+ $ret = { 'pv' => $pkgver };
+ ++$n;
+ }
+ elsif (/^(\S+):.*NOT OK/) {
+ my $pkg = $1;
+ my $nextline = <$pipe>;
+ chomp( $nextline );
+ $nextline =~ s/^\s+//;
+ $self->log("Can't take $pkg: $nextline\n");
+ }
+ elsif (/^(\S+):.*previous version failed/i) {
+ my $pkg = $1;
+ ++$n;
+ if ($self->get_conf('SHOULD_BUILD_MSGS')) {
+ $self->handle_prevfailed( $dist_config, $pkgver );
+ } else {
+ $ret = { 'pv' => $pkgver };
+ }
+ # skip until ok line
+ while( <$pipe> ) {
+ last if /^\Q$pkg\E:\s*ok/;
+ }
+ }
+ elsif (/^(\S+):.*needs binary NMU (\d+)/) {
+ my $pkg = $1;
+ my $binNMUver = $2;
+ chop (my $changelog = <$pipe>);
+ my $newpkg;
+ ++$n;
+
+ push( @output, grep( /^\Q$pkg\E_/, @_ ) );
+ $ret = { 'pv' => $pkgver };
+ $ret->{'changelog'} = $changelog;
+ $ret->{'binNMU'} = $binNMUver;
+ # skip until ok line
+ while( <$pipe> ) {
+ last if /^\Q$pkg\E:\s*aok/;
+ }
+ }
+ }
+ close( $pipe );
+ $self->unblock_signals();
+ $self->write_stats("taken", $n) if $n;
+ return $ret;
+ }
+ else {
+ $self->unblock_signals();
+ $self->log("Can't spawn wanna-build: $!\n");
+ return undef;
+ }
+}
+
+sub should_skip {
+ my $self = shift;
+ my $pkgv = shift;
+
+ my $found = 0;
+
+ $self->lock_file("SKIP", 0);
+ goto unlock if !open( F, "SKIP" );
+ my @pkgs = <F>;
+ close(F);
+
+ if (!open( F, ">SKIP" )) {
+ $self->log("Can't open SKIP for writing: $!\n",
+ "Would write: @pkgs\nminus $pkgv\n");
+ goto unlock;
+ }
+ foreach (@pkgs) {
+ if (/^\Q$pkgv\E$/) {
+ ++$found;
+ $self->log("$pkgv found in SKIP file -- skipping building it\n");
+ }
+ else {
+ print F $_;
+ }
+ }
+ close( F );
+ unlock:
+ $self->unlock_file("SKIP");
+ return $found;
+}
+
+sub do_build {
+ my $self = shift;
+ my $dist_config = shift;
+ my $todo = shift;
+ # $todo = { 'pv' => $pkg_ver, 'changelog' => $binNMUlog->{$pkg_ver}, 'binNMU' => $binNMUver; };
+
+ # If the package to build is in SKIP, then skip.
+ if ($self->should_skip($todo->{'pv'})) {
+ return;
+ }
+
+ my $free_space;
+
+ while (($free_space = df(".")) < $self->get_conf('MIN_FREE_SPACE')) {
+ $self->log("Delaying build, because free space is low ($free_space KB)\n");
+ my $idle_start_time = time;
+ sleep( 10*60 );
+ my $idle_end_time = time;
+ $self->write_stats("idle-time", $idle_end_time - $idle_start_time);
+ }
+
+ $self->log("Starting build (dist=" . $dist_config->get('DIST_NAME') . ") of "
+ .($todo->{'binNMU'} ? "!".$todo->{'binNMU'}."!" : "")
+ ."$todo->{'pv'}\n");
+ $self->write_stats("builds", 1);
+
+ my @sbuild_args = ();
+ if ($self->get_conf('NICE_LEVEL') != 0) {
+ @sbuild_args = ( 'nice', '-n', $self->get_conf('NICE_LEVEL') );
+ }
+
+ push @sbuild_args, 'sbuild',
+ '--apt-update',
+ '--no-apt-upgrade',
+ '--no-apt-distupgrade',
+ '--no-run-lintian',
+ '--batch',
+ "--stats-dir=" . $self->get_conf('HOME') . "/stats",
+ "--dist=" . $dist_config->get('DIST_NAME');
+
+ push @sbuild_args, "--sbuild-mode=buildd";
+ push @sbuild_args, "--mailfrom=".$dist_config->get('MAILFROM') if $dist_config->get('MAILFROM');
+ push @sbuild_args, "--maintainer=".$dist_config->get('MAINTAINER_NAME') if $dist_config->get('MAINTAINER_NAME');
+ push @sbuild_args, "--dpkg-file-suffix=".$self->get_conf('DPKG_FILE_SUFFIX') if $self->get_conf('DPKG_FILE_SUFFIX');
+
+ if ($dist_config->get('SIGN_WITH')) {
+ push @sbuild_args, '--keyid=' . $dist_config->get('SIGN_WITH');
+ }
+
+ #multi-archive-buildd keeps the mailto configuration in the builddrc, so
+ #this needs to be passed over to sbuild. If the buildd config doesn't have
+ #it, we hope that the address is configured in .sbuildrc and the right one:
+ if ($dist_config->get('LOGS_MAILED_TO')) {
+ push @sbuild_args, '--mail-log-to=' . $dist_config->get('LOGS_MAILED_TO');
+ } elsif ($dist_config->get('LOGS_MAIL_ALSO') || $todo->{'mail_logs'}) {
+ push @sbuild_args, '--mail-log-to=' . join (',', grep { $_ } ($dist_config->get('LOGS_MAIL_ALSO'), $todo->{'mail_logs'}));
+ }
+ #Some distributions (bpo, experimental) require a more complex dep resolver.
+ #Ask sbuild to use another build-dep resolver if the config says so:
+ if ($dist_config->get('BUILD_DEP_RESOLVER') || $todo->{'build_dep_resolver'}) {
+ push @sbuild_args, '--build-dep-resolver=' . ($dist_config->get('BUILD_DEP_RESOLVER') || $todo->{'build_dep_resolver'});
+ }
+ if ($dist_config->get('BUILT_ARCHITECTURE')) {
+ if ($dist_config->get('BUILT_ARCHITECTURE') eq 'all') {
+ push ( @sbuild_args, "--arch-all", "--no-arch-any" );
+ } else {
+ push ( @sbuild_args, "--no-arch-all", "--arch-any", "--arch=" . $dist_config->get('BUILT_ARCHITECTURE') );
+ }
+ }
+ push ( @sbuild_args, "--chroot=" . $dist_config->get('SBUILD_CHROOT') )
+ if $dist_config->get('SBUILD_CHROOT');
+
+
+ push ( @sbuild_args, "--binNMU=$todo->{'binNMU'}") if $todo->{'binNMU'};
+ push ( @sbuild_args, "--make-binNMU=$todo->{'changelog'}") if $todo->{'changelog'};
+ push ( @sbuild_args, "--add-conflicts=$todo->{'extra-conflicts'}") if $todo->{'extra-conflicts'};
+ push ( @sbuild_args, "--add-depends=$todo->{'extra-depends'}") if $todo->{'extra-depends'};
+ push @sbuild_args, $todo->{'pv'};
+ $self->log("command line: @sbuild_args\n");
+
+ $main::sbuild_pid = open(SBUILD_OUT, "-|");
+
+ #We're childish, so call sbuild:
+ if ($main::sbuild_pid == 0) {
+ { exec (@sbuild_args) };
+ $self->log("Cannot execute sbuild: $!\n");
+ exit(64);
+ }
+
+ if (!defined $main::sbuild_pid) {
+ $self->log("Cannot fork for sbuild: $!\n");
+ goto failed;
+ }
+
+ #We want to collect the first few lines of sbuild output:
+ my ($sbuild_output_line_count, @sbuild_output_buffer) = (0, ());
+ while (<SBUILD_OUT>) {
+ #5 lines are enough:
+ if (++$sbuild_output_line_count < 5) {
+ push @sbuild_output_buffer, $_;
+ }
+ }
+
+ #We got enough output, now just wait for sbuild to die:
+ my $rc;
+ while (($rc = wait) != $main::sbuild_pid) {
+ if ($rc == -1) {
+ last if $! == ECHILD;
+ next if $! == EINTR;
+ $self->log("wait for sbuild: $!; continuing to wait\n");
+ } elsif ($rc != $main::sbuild_pid) {
+ $self->log("wait for sbuild: returned unexpected pid $rc\n");
+ }
+ }
+ my $sbuild_exit_code = $?;
+ undef $main::sbuild_pid;
+ close(SBUILD_OUT);
+
+ #Process sbuild's results:
+ my $db = $self->get_db_handle($dist_config);
+ my $failed = 1;
+ my $giveback = 1;
+
+ if (WIFEXITED($sbuild_exit_code)) {
+ my $status = WEXITSTATUS($sbuild_exit_code);
+
+ if ($status == 0) {
+ $failed = 0;
+ $giveback = 0;
+ $self->log("sbuild of $todo->{'pv'} succeeded -- marking as built in wanna-build\n");
+ $db->run_query('--built', '--dist=' . $dist_config->get('DIST_NAME'), $todo->{'pv'});
+
+ if ($dist_config->get('SIGN_WITH') && $dist_config->get('BUILT_ARCHITECTURE')) {
+ # XXX: Check if signature is present.
+ $self->move_to_upload($dist_config, $todo->{'pv'}, $todo->{'binNMU'});
+ }
+ } elsif ($status == 2) {
+ $giveback = 0;
+ $self->log("sbuild of $todo->{'pv'} failed with status $status (build failed) -- marking as attempted in wanna-build\n");
+ $db->run_query('--attempted', '--dist=' . $dist_config->get('DIST_NAME'), $todo->{'pv'});
+ $self->write_stats("failed", 1);
+ } else {
+ $self->log("sbuild of $todo->{'pv'} failed with status $status (local problem) -- giving back\n");
+ }
+ } elsif (WIFSIGNALED($sbuild_exit_code)) {
+ my $sig = WTERMSIG($sbuild_exit_code);
+ $self->log("sbuild of $todo->{'pv'} failed with signal $sig (local problem) -- giving back\n");
+ } else {
+ $self->log("sbuild of $todo->{'pv'} failed with unknown reason (local problem) -- giving back\n");
+ }
+
+ if ($giveback) {
+ $db->run_query('--give-back', '--dist=' . $dist_config->get('DIST_NAME'), $todo->{'pv'});
+ $self->add_given_back($todo->{'pv'});
+ $self->write_stats("give-back", 1);
+ }
+
+ # Check if we encountered some local error to stop further building
+ if ($giveback) {
+ if (!defined $main::sbuild_fails) {
+ $main::sbuild_fails = 0;
+ }
+
+ $main::sbuild_fails++;
+
+ if ($main::sbuild_fails > $self->get_conf('MAX_SBUILD_FAILS')) {
+ $self->log("sbuild now failed $main::sbuild_fails times in ".
+ "a row; going to sleep\n");
+ send_mail( $self->get_conf('ADMIN_MAIL'),
+ "Repeated mess with sbuild",
+ <<EOF );
+The execution of sbuild now failed for $main::sbuild_fails times.
+These are the first $sbuild_output_line_count lines of the last failed sbuild call:
+@sbuild_output_buffer
+
+The daemon is going to sleep for 1 hour, or can be restarted with SIGUSR2.
+EOF
+ my $oldsig;
+ eval <<'EOF';
+$oldsig = $SIG{'USR2'};
+$SIG{'USR2'} = sub ($) { die "signal\n" };
+my $idle_start_time = time;
+sleep( 60*60 );
+my $idle_end_time = time;
+$SIG{'USR2'} = $oldsig;
+$self->write_stats("idle-time", $idle_end_time - $idle_start_time);
+EOF
+ $main::sbuild_fails = 0;
+ }
+ }
+ else {
+ # Either a build success or an attempted build will cause the
+ # counter to reset.
+ $main::sbuild_fails = 0;
+ }
+ $self->log("Build finished.\n");
+}
+
+sub move_to_upload {
+ my $self = shift;
+ my $dist_config = shift;
+ my $pv = shift;
+ my $binNMUver = shift;
+
+ my $arch = $dist_config->get('BUILT_ARCHITECTURE');
+ my $upload_dir = $dist_config->get('DUPLOAD_LOCAL_QUEUE_DIR');
+ my $file_suffix = $self->get_conf('DPKG_FILE_SUFFIX');
+
+ if ($binNMUver) {
+ $pv .= '+b' . $binNMUver;
+ }
+
+ my $pkg_noepoch = $pv;
+ $pkg_noepoch =~ s/_\d*:/_/;
+
+ my $changes_name = $pkg_noepoch . '_' . $arch . $file_suffix . '.changes';
+ my $upload_path = $self->get_conf('HOME') . '/' . $dist_config->get('DUPLOAD_LOCAL_QUEUE_DIR') . '/' . $pkg_noepoch . '_' . $arch . '.upload';
+
+ $self->log("$pv is autosigned, moving to '$upload_dir'\n");
+ if ( -f $upload_path ) {
+ unlink( $upload_path );
+ $self->log("'$upload_path' removed.\n");
+ }
+ system(qw(dcmd mv --),
+ sprintf('%s/build/%s', $self->get_conf('HOME'), $changes_name),
+ sprintf('%s/%s/', $self->get_conf('HOME'), $dist_config->get('DUPLOAD_LOCAL_QUEUE_DIR'))
+ );
+ $self->log("$pv moved to '$upload_dir'\n");
+}
+
+sub handle_prevfailed {
+ my $self = shift;
+ my $dist_config = shift;
+ my $pkgv = shift;
+
+ my $dist_name = $dist_config->get('DIST_NAME');
+ my( $pkg, $fail_msg, $changelog);
+
+ $self->log("$pkgv previously failed -- asking admin first\n");
+ ($pkg = $pkgv) =~ s/_.*$//;
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query(
+ '--info',
+ $pkg);
+ if (!$pipe) {
+ $self->log("Can't run wanna-build: $!\n");
+ return;
+ }
+
+ $fail_msg = "";
+ while (<$pipe>) {
+ $fail_msg .= $_;
+ }
+
+ close($pipe);
+ if ($?) {
+ $self->log("wanna-build exited with error $?\n");
+ return;
+ }
+
+ send_mail( $self->get_conf('ADMIN_MAIL'),
+ "Should I build $pkgv (dist=${dist_name})?",
+ "The package $pkg failed to build in a previous version. ".
+ "The fail\n".
+ "messages are:\n\n$fail_msg\n".
+ "Should buildd try to build the new version, or should it ".
+ "fail with the\n".
+ "same messages again.? Please answer with 'build' (or 'ok'), ".
+ "or 'fail'.\n" );
+}
+
+sub get_changelog {
+ # This method is currently broken. It makes some assumptions about source
+ # layout that are no longer true. Furthermore it tries fetching through
+ # the host instead of creating a session (which is necessary for snapshot-
+ # based chroots) and work in the chroot.
+
+ my $self = shift;
+ my $dist_config = shift;
+ my $pkg = shift;
+
+ my $dist_name = $dist_config->get('DIST_NAME');
+ my $changelog = "";
+ my $analyze = "";
+ my $chroot_apt_options;
+ my $file;
+ my $retried = 0;
+
+ $pkg =~ /^([\w\d.+-]+)_([\w\d:.~+-]+)/;
+ my ($n, $v) = ($1, $2);
+ (my $v_ne = $v) =~ s/^\d+://;
+ my $pkg_ne = "${n}_${v_ne}";
+
+retry:
+ my @schroot = ($self->get_conf('SCHROOT'), '-c',
+ $dist_name . '-' . $self->get_conf('ARCH') . '-sbuild', '--');
+ my @schroot_root = ($self->get_conf('SCHROOT'), '-c',
+ $dist_name . '-' . $self->get_conf('ARCH') . '-sbuild',
+ '-u', 'root', '--');
+ my $apt_get = $self->get_conf('APT_GET');
+
+ my $pipe = $self->get('Host')->pipe_command(
+ { COMMAND => [@schroot,
+ "$apt_get", '-q', '-d',
+ '--diff-only', 'source', "$n=$v"],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ });
+ if (!$pipe) {
+ $self->log("Can't run schroot: $!\n");
+ return;
+ }
+
+ my $msg = "";
+ while (<$pipe>) {
+ $msg .= $_;
+ }
+
+ close($pipe);
+
+ if ($? == 0 && $msg !~ /get 0B/) {
+ $analyze = "diff";
+ $file = "${n}_${v_ne}.diff.gz";
+ }
+
+ if (!$analyze) {
+ my $pipe2 = $self->get('Host')->pipe_command(
+ { COMMAND => [@schroot,
+ "$apt_get", '-q', '-d',
+ '--tar-only', 'source', "$n=$v"],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ });
+ if (!$pipe2) {
+ $self->log("Can't run schroot: $!\n");
+ return;
+ }
+
+ my $msg = <$pipe2>;
+
+ close($pipe2);
+
+ if ($? == 0 && $msg !~ /get 0B/) {
+ $analyze = "tar";
+ $file = "${n}_${v_ne}.tar.gz";
+ }
+ }
+
+ if (!$analyze && !$retried) {
+ $self->get('Host')->run_command(
+ { COMMAND => [@schroot_root,
+ $apt_get, '-qq',
+ 'update'],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ STREAMOUT => $devnull
+ });
+
+ $retried = 1;
+ goto retry;
+ }
+
+ return "ERROR: cannot find any source" if !$analyze;
+
+ if ($analyze eq "diff") {
+ if (!open( F, "gzip -dc '$file' 2>/dev/null |" )) {
+ return "ERROR: Cannot spawn gzip to zcat $file: $!";
+ }
+ while( <F> ) {
+ # look for header line of a file */debian/changelog
+ last if m,^\+\+\+\s+[^/]+/debian/changelog(\s+|$),;
+ }
+ while( <F> ) {
+ last if /^---/; # end of control changelog patch
+ next if /^\@\@/;
+ $changelog .= "$1\n" if /^\+(.*)$/;
+ last if /^\+\s+--\s+/;
+ }
+ while( <F> ) { } # read to end of file to avoid broken pipe
+ close( F );
+ if ($?) {
+ return "ERROR: error status ".exitstatus($?)." from gzip on $file";
+ }
+ unlink( $file );
+ }
+ elsif ($analyze eq "tar") {
+ if (!open( F, "tar -xzOf '$file' '*/debian/changelog' ".
+ "2>/dev/null |" )) {
+ return "ERROR: Cannot spawn tar for $file: $!";
+ }
+ while( <F> ) {
+ $changelog .= $_;
+ last if /^\s+--\s+/;
+ }
+ while( <F> ) { } # read to end of file to avoid broken pipe
+ close( F );
+ if ($?) {
+ return "ERROR: error status ".exitstatus($?)." from tar on $file";
+ }
+ unlink( $file );
+ }
+
+ return $changelog;
+}
+
+sub check_restart {
+ my $self = shift;
+ my @stats = stat( $self->get('MY_BINARY') );
+
+ if (@stats && $self->get('MY_BINARY_TIME') != $stats[ST_MTIME]) {
+ $self->log("My binary has been updated -- restarting myself (pid=$$)\n");
+ unlink( $self->get_conf('PIDFILE') );
+ kill ( 15, $main::ssh_pid ) if $main::ssh_pid;
+ exec $self->get('MY_BINARY');
+ }
+
+ if ( -f $self->get_conf('HOME') . "/EXIT-DAEMON-PLEASE" ) {
+ unlink($self->get_conf('HOME') . "/EXIT-DAEMON-PLEASE");
+ $self->shutdown("NONE (flag file exit)");
+ }
+}
+
+sub block_signals {
+ my $self = shift;
+
+ POSIX::sigprocmask( SIG_BLOCK, $main::block_sigset );
+}
+
+sub unblock_signals {
+ my $self = shift;
+
+ POSIX::sigprocmask( SIG_UNBLOCK, $main::block_sigset );
+}
+
+sub check_ssh_master {
+ my $self = shift;
+ my $dist_config = shift;
+
+ my $ssh_socket = $dist_config->get('WANNA_BUILD_SSH_SOCKET');
+
+ return 1 if (!$ssh_socket);
+ return 1 if ( -S $ssh_socket );
+
+ my $ssh_master_pids = {};
+ if ($self->get('SSH_MASTER_PIDS')) {
+ $ssh_master_pids = $self->get('SSH_MASTER_PIDS');
+ } else {
+ $self->set('SSH_MASTER_PIDS', $ssh_master_pids);
+ }
+
+ if ($ssh_master_pids->{$ssh_socket})
+ {
+ my $wpid = waitpid ( $ssh_master_pids->{$ssh_socket}, WNOHANG );
+ return 1 if ($wpid != -1 and $wpid != $ssh_master_pids->{$ssh_socket});
+ }
+
+ my $new_master_pid = fork;
+
+ #We are in the newly forked child:
+ if (defined($new_master_pid) && $new_master_pid == 0) {
+ exec (@{$dist_config->get('WANNA_BUILD_SSH_CMD')}, "-MN");
+ }
+
+ #We are the parent:
+ if (!defined $new_master_pid) {
+ $self->log("Cannot fork for ssh master: $!\n");
+ return 0;
+ }
+
+ $ssh_master_pids->{$ssh_socket} = $new_master_pid;
+
+ while ( ! -S $ssh_socket )
+ {
+ sleep 1;
+ my $wpid = waitpid ( $new_master_pid, WNOHANG );
+ return 0 if ($wpid == -1 or $wpid == $new_master_pid);
+ }
+ return 1;
+}
+
+sub shutdown {
+ my $self = shift;
+ my $signame = shift;
+
+ $self->log("buildd ($$) received SIG$signame -- shutting down\n");
+
+ if ($self->get('SSH_MASTER_PIDS')) {
+ my $ssh_master_pids = $self->get('SSH_MASTER_PIDS');
+ for my $ssh_socket (keys %{$ssh_master_pids}) {
+ my $master_pid = $ssh_master_pids->{$ssh_socket};
+ kill ( 15, $master_pid );
+ delete ( $ssh_master_pids->{$ssh_socket} );
+ }
+ }
+
+ if (defined $main::sbuild_pid) {
+ $self->log("Killing sbuild (pid=$main::sbuild_pid)\n");
+ kill( 15, $main::sbuild_pid );
+ $self->log("Waiting max. 2 minutes for sbuild to finish\n");
+ $SIG{'ALRM'} = sub ($) { die "timeout\n"; };
+ alarm( 120 );
+ eval "waitpid( $main::sbuild_pid, 0 )";
+ alarm( 0 );
+ if ($@) {
+ $self->log("sbuild did not die!");
+ }
+ else {
+ $self->log("sbuild died normally");
+ }
+ unlink( "SBUILD-REDO-DUMPED" );
+ }
+ unlink( $self->get('Config')->get('PIDFILE') );
+ $self->log("exiting now\n");
+ $self->close_log();
+ exit 1;
+}
+
+1;
diff --git a/lib/Buildd/DistConf.pm b/lib/Buildd/DistConf.pm
new file mode 100644
index 0000000..6edffba
--- /dev/null
+++ b/lib/Buildd/DistConf.pm
@@ -0,0 +1,159 @@
+#
+# Conf.pm: configuration library for buildd
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2006-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/>.
+#
+#######################################################################
+
+package Buildd::DistConf;
+
+use strict;
+use warnings;
+
+use Sbuild::ConfBase;
+use Sbuild::Sysconfig;
+use Buildd::ClientConf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(new_hash setup read_hash);
+}
+
+sub new_hash (@);
+sub setup ($);
+sub read_hash ($$);
+
+sub new_hash (@) {
+ my %opts = @_;
+
+ my $queue_config = Sbuild::ConfBase->new(%opts);
+
+ Buildd::DistConf::setup($queue_config);
+ Buildd::DistConf::read_hash($queue_config, $opts{'HASH'});
+
+ return $queue_config;
+}
+
+sub setup ($) {
+ my $conf = shift;
+
+ my $validate_directory_in_home = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $directory = $conf->get($key);
+ my $home_directory = $conf->get('HOME');
+
+ die "$key directory is not defined"
+ if !defined($directory) || !$directory;
+
+ die "$key directory '$home_directory/$directory' does not exist"
+ if !-d $home_directory . "/" . $directory;
+ };
+
+ my $arch = $conf->get('ARCH');
+
+ my %buildd_dist_keys = (
+ 'DIST_NAME' => {
+ DEFAULT => 'unstable'
+ },
+ 'BUILT_ARCHITECTURE' => {
+ DEFAULT => undef,
+ },
+ 'SBUILD_CHROOT' => {
+ DEFAULT => undef,
+ },
+ 'WANNA_BUILD_SSH_HOST' => {
+ DEFAULT => 'buildd.debian.org'
+ },
+ 'WANNA_BUILD_SSH_USER' => {
+ DEFAULT => 'buildd_' . $arch
+ },
+ 'WANNA_BUILD_SSH_SOCKET' => {
+ DEFAULT => undef
+ },
+ 'WANNA_BUILD_SSH_OPTIONS' => {
+ DEFAULT => []
+ },
+ 'WANNA_BUILD_DB_NAME' => {
+ DEFAULT => undef,
+ },
+ 'WANNA_BUILD_DB_USER' => {
+ DEFAULT => $Buildd::username
+ },
+ 'WANNA_BUILD_API' => {
+ DEFAULT => undef,
+ },
+ 'WANNA_BUILD_MIN_AGE' => {
+ DEFAULT => undef,
+ },
+ 'DUPLOAD_LOCAL_QUEUE_DIR' => {
+ CHECK => $validate_directory_in_home,
+ DEFAULT => 'upload'
+ },
+ 'NO_AUTO_BUILD' => {
+ DEFAULT => []
+ },
+ 'WEAK_NO_AUTO_BUILD' => {
+ DEFAULT => []
+ },
+ 'NO_BUILD_REGEX' => {
+ DEFAULT => undef
+ },
+ 'BUILD_REGEX' => {
+ DEFAULT => undef
+ },
+ 'LOGS_MAILED_TO' => {
+ DEFAULT => undef
+ },
+ 'LOGS_MAIL_ALSO' => {
+ DEFAULT => undef
+ },
+ 'BUILD_DEP_RESOLVER' => {
+ DEFAULT => undef
+ },
+ 'SIGN_WITH' => {
+ DEFAULT => undef
+ },
+ 'MAINTAINER_NAME' => {
+ DEFAULT => undef
+ },
+ 'MAILFROM' => {
+ DEFAULT => undef
+ },
+ );
+
+ $conf->set_allowed_keys(\%buildd_dist_keys);
+
+ Buildd::ClientConf::setup($conf);
+}
+
+sub read_hash($$) {
+ my $conf = shift;
+ my $data = shift;
+
+ for my $key (keys %$data) {
+ $conf->set($key, $data->{$key});
+ }
+}
+
+1;
diff --git a/lib/Buildd/Mail.pm b/lib/Buildd/Mail.pm
new file mode 100644
index 0000000..09f43ec
--- /dev/null
+++ b/lib/Buildd/Mail.pm
@@ -0,0 +1,1354 @@
+# 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/>.
+#
+#######################################################################
+
+package Buildd::Mail;
+
+use strict;
+use warnings;
+
+use Buildd qw(ll_send_mail lock_file unlock_file send_mail exitstatus);
+use Buildd::Conf qw();
+use Buildd::Base;
+use Sbuild qw(binNMU_version $devnull);
+use Sbuild::ChrootRoot;
+use Buildd::Client;
+use POSIX;
+use File::Basename;
+use MIME::QuotedPrint;
+use MIME::Base64;
+use Encode;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Buildd::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('Mail Error', undef);
+ $self->set('Mail Short Error', undef);
+ $self->set('Mail Header', {});
+ $self->set('Mail Body Text', '');
+
+ $self->open_log();
+
+ return $self;
+}
+
+sub run {
+ my $self = shift;
+
+ chdir($self->get_conf('HOME'));
+
+ $self->set('Mail Error', undef);
+ $self->set('Mail Short Error', undef);
+ $self->set('Mail Header', {});
+ $self->set('Mail Body Text', '');
+
+ $self->process_mail();
+
+ return 0;
+}
+
+
+sub process_mail () {
+ my $self = shift;
+
+# Note: Mail Header (to|from|subject|message-id|date) are mandatory.
+# Check for these and bail out if not present.
+ my $header_text = "";
+ my $lastheader = "";
+
+ $self->set('Mail Header', {});
+
+ $self->set('Mail Error', '');
+ $self->set('Mail Short Error', '');
+ $self->set('Mail Header', {});
+ $self->set('Mail Body Text', '');
+
+ while( <STDIN> ) {
+ $header_text .= $_;
+ last if /^$/;
+
+ if (/^\s/ && $lastheader) {
+ $_ =~ s/^\s+//;
+ $_ = "$lastheader $_";
+ }
+
+ if (/^From (\S+)/) {
+ ;
+ }
+ if (/^([\w\d-]+):\s*(.*)\s*$/) {
+ my $hname;
+ ($hname = $1) =~ y/A-Z/a-z/;
+ $self->get('Mail Header')->{$hname} = $2;
+ $lastheader = $_;
+ chomp( $lastheader );
+ }
+ else {
+ $lastheader = "";
+ }
+ }
+ while( <STDIN> ) {
+ last if !/^\s*$/;
+ }
+
+ $self->set('Mail Body Text',
+ $self->get('Mail Body Text') . $_)
+ if defined($_);
+
+ if (!eof)
+ {
+ local($/);
+ undef $/;
+ $self->set('Mail Body Text',
+ $self->get('Mail Body Text') . <STDIN>);
+ }
+
+ if ($self->get('Mail Header')->{'from'} =~ /mail\s+delivery\s+(sub)?system|mailer.\s*daemon/i) {
+ # is an error mail from a mailer daemon
+ # To avoid mail loops if this error resulted from a mail we sent
+ # outselves, we break the loop by not forwarding this mail after the 5th
+ # error mail within 8 hours or so.
+ my $n = $self->add_error_mail();
+ if ($n > 5) {
+ $self->log("Too much error mails ($n) within ",
+ int($self->get_conf('ERROR_MAIL_WINDOW')/(60*60)), " hours\n",
+ "Not forwarding mail from ".$self->get('Mail Header')->{'from'}."\n",
+ "Subject: " . $self->get('Mail Header')->{'subject'} . "\n");
+ return;
+ }
+ }
+
+ goto forward_mail if !$self->get('Mail Header')->{'subject'};
+ my $subject = $self->get('Mail Header')->{'subject'};
+ Encode::from_to($subject, "MIME-Header", "utf-8");
+
+ if ($subject =~ /^Re: Log for \S+ build of (\S+)(?: on [\w-]+)? \(dist=(\S+)\)/i) {
+ # reply to a build log
+ my( $package, $dist_name ) = ( $1, $2 );
+
+ my $dist_config = $self->get_dist_config_by_name($dist_name);
+ return if (!$dist_config); #get_dist_config sets the error mail
+
+ my $text = $self->get('Mail Body Text');
+ $text =~ /^(\S+)/;
+ $self->set('Mail Body Text', $text);
+ if (defined($self->get('Mail Header')->{'content-transfer-encoding'})) {
+ # Decode the mail if necessary.
+ if ($self->get('Mail Header')->{'content-transfer-encoding'} =~ /quoted-printable/) {
+ $self->set('Mail Body Text',
+ decode_qp($self->get('Mail Body Text')));
+ } elsif ($self->get('Mail Header')->{'content-transfer-encoding'} =~ /base64/) {
+ $self->set('Mail Body Text',
+ decode_base64($self->get('Mail Body Text')));
+ }
+ }
+ my $keyword = $1;
+ my $from = $self->get('Mail Header')->{'from'};
+ $from = $1 if $from =~ /<(.+)>/;
+ $self->log("Log reply from $from\n");
+ my %newv;
+
+ if ($keyword =~ /^not-for-us/) {
+ $self->no_build( $package, $dist_config );
+ $self->purge_pkg( $package, $dist_config );
+ }
+ elsif ($keyword =~ /^up(l(oad)?)?-rem/) {
+ $self->remove_from_upload( $package, $dist_config );
+ }
+ elsif ($self->check_is_outdated( $dist_config, $package )) {
+ # Error has been set already -> no action here
+ }
+ elsif ($keyword =~ /^fail/) {
+ my $text = $self->get('Mail Body Text');
+ $text =~ s/^fail.*\n(\s*\n)*//;
+ $text =~ s/\n+$/\n/;
+ $self->set_to_failed( $package, $dist_config, $text );
+ $self->purge_pkg( $package, $dist_config );
+ }
+ elsif ($keyword =~ /^ret/) {
+ if (!$self->check_state( $package, $dist_config, qw(Built Building Build-Attempted) )) {
+ # Error already set
+ }
+ else {
+ $self->append_to_REDO( $package, $dist_config );
+ }
+ }
+ elsif ($keyword =~ /^d(ep(endency)?)?-(ret|w)/) {
+ if (!$self->check_state( $package, $dist_config, qw(Built Building Build-Attempted) )) {
+ # Error already set
+ }
+ else {
+ $self->get('Mail Body Text') =~ /^\S+\s+(.*)$/m;
+ my $deps = $1;
+ $self->set_to_depwait( $package, $dist_config, $deps );
+ $self->purge_pkg( $package, $dist_config );
+ }
+ }
+ elsif ($keyword =~ /^man/) {
+ if (!$self->check_state( $package, $dist_config, qw(Built Building Build-Attempted) )) {
+ # Error already set
+ }
+ else {
+ # no action
+ $self->log("$package($dist_name) will be finished manually\n");
+ }
+ }
+ elsif ($keyword =~ /^newv/) {
+ # build a newer version instead
+ $self->get('Mail Body Text') =~ /^newv\S*\s+(\S+)/;
+ my $newv = $1;
+ if ($newv =~ /_/) {
+ $self->log("Removing unneeded package name from $newv\n");
+ $newv =~ s/^.*_//;
+ $self->log("Result: $newv\n");
+ }
+ my $pkgname;
+ ($pkgname = $package) =~ s/_.*$//;
+ $self->redo_new_version( $dist_config, $package, "${pkgname}_${newv}" );
+ $self->purge_pkg( $package, $dist_config );
+ }
+ elsif ($keyword =~ /^(give|back)/) {
+ $self->get('Mail Body Text') =~ /^(give|back) ([-0-9]+)/;
+ my $pri = $1;
+ if (!$self->check_state( $package, $dist_config, qw(Built Building Build-Attempted) )) {
+ # Error already set
+ }
+ else {
+ $self->give_back( $package, $dist_config );
+ $self->purge_pkg( $package, $dist_config );
+ }
+ }
+ elsif ($keyword =~ /^purge/) {
+ $self->purge_pkg( $package, $dist_config );
+ }
+ elsif ($self->get('Mail Body Text') =~ /^---+\s*BEGIN PGP SIGNED MESSAGE/) {
+ if ($self->prepare_for_upload( $package,
+ $self->get('Mail Body Text') )) {
+ $self->purge_pkg( $package, $dist_config );
+ }
+ }
+ elsif ($self->get('Mail Body Text') =~ /^--/ &&
+ $self->get('Mail Header')->{'content-type'} =~ m,multipart/signed,) {
+ my ($prot) = ($self->get('Mail Header')->{'content-type'} =~ m,protocol="([^"]*)",);
+ my ($bound) = ($self->get('Mail Header')->{'content-type'} =~ m,boundary="([^"]*)",);
+ my $text = $self->get('Mail Body Text');
+ $text =~ s,^--\Q$bound\E\nContent-Type: text/plain; charset=us-ascii\n\n,-----BEGIN PGP SIGNED MESSAGE-----\n\n,;
+ $text =~ s,--\Q$bound\E\nContent-Type: application/pgp-signature\n\n,,;
+ $text =~ s,\n\n--\Q$bound\E--\n,,;
+ $self->set('Mail Body Text', $text);
+ if ($self->prepare_for_upload($package,
+ $self->get('Mail Body Text'))) {
+ $self->purge_pkg( $package, $dist_config );
+ }
+ }
+ else {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "Bad keyword in answer $keyword\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Answer not understood (expected retry, failed, manual,\n".
+ "dep-wait, giveback, not-for-us, purge, upload-rem,\n".
+ "newvers, or a signed changes file)\n");
+ }
+ }
+ elsif ($subject =~ /^Re: Should I build (\S+) \(dist=(\S+)\)/i) {
+ # reply whether a prev-failed package should be built
+ my( $package, $dist_name ) = ( $1, $2 );
+
+ my $dist_config = $self->get_dist_config_by_name($dist_name);
+ return if (!$dist_config); #get_dist_config sets the error mail
+
+ $self->get('Mail Body Text') =~ /^(\S+)/;
+ my $keyword = $1;
+ $self->log("Should-build reply for $package($dist_name)\n");
+ if ($self->check_is_outdated( $dist_config, $package )) {
+ # Error has been set already -> no action here
+ }
+ elsif (!$self->check_state( $package, $dist_config, qw(Built Building Build-Attempted) )) {
+ # Error already set
+ }
+ elsif ($keyword =~ /^(build|ok)/) {
+ $self->append_to_REDO( $package, $dist_config );
+ }
+ elsif ($keyword =~ /^fail/) {
+ my $text = $self->get_fail_msg( $package, $dist_config );
+ $self->set_to_failed( $package, $dist_config, $text );
+ }
+ elsif ($keyword =~ /^(not|no-b)/) {
+ $self->no_build( $package, $dist_config );
+ }
+ elsif ($keyword =~ /^(give|back)/) {
+ $self->give_back( $package, $dist_config );
+ }
+ else {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "Bad keyword in answer $keyword\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Answer not understood (expected build, ok, fail, ".
+ "give-back, or no-build)\n");
+ }
+ }
+ elsif ($subject =~ /^Processing of (\S+)/) {
+ my $job = $1;
+ # mail from Erlangen queue daemon: forward all non-success messages
+ my $text = $self->get('Mail Body Text');
+ goto forward_mail if $text !~ /uploaded successfully/mi;
+ $self->log("$job processed by upload queue\n")
+ if $self->get_conf('LOG_QUEUED_MESSAGES');
+ }
+ elsif ($subject =~ /^([-+~\.\w]+\.changes) (INSTALL|ACCEPT)ED/) {
+ # success mail from dinstall
+ my $changes_f = $1;
+ my( @to_remove, $upload_f, $pkgv );
+ my @upload_dirs = $self->find_upload_dirs_for_changes_file($changes_f);
+
+ if ((scalar @upload_dirs) < 1) {
+ $self->log("Can't identify upload directory for $changes_f!\n");
+ return 0;
+ } elsif ((scalar @upload_dirs) > 1) {
+ $self->log("Found more than one upload directory for $changes_f - not deleting binaries!\n");
+ return 0;
+ }
+ my $upload_dir = $upload_dirs[0];
+
+ if (-f "$upload_dir/$changes_f" && open( F, "<$upload_dir/$changes_f" )) {
+ local($/); undef $/;
+ my $changetext = <F>;
+ close( F );
+ push( @to_remove, $self->get_files_from_changes( $changetext ) );
+ } else {
+ foreach (split( "\n", $self->get('Mail Body Text'))) {
+ if (/^(\[-+~\.\w]+\.(u?deb))$/) {
+ my $f = $1;
+ push( @to_remove, $f ) if !grep { $_ eq $f } @to_remove;
+ }
+ }
+ }
+ ($upload_f = $changes_f) =~ s/\.changes$/\.upload/;
+ push( @to_remove, $changes_f, $upload_f );
+ ($pkgv = $changes_f) =~ s/_(\S+)\.changes//;
+ $self->log("$pkgv has been installed; removing from upload dir:\n",
+ "@to_remove\n");
+
+ my @dists;
+ if (open( F, "<$upload_dir/$changes_f" )) {
+ my $changes_text;
+ { local($/); undef $/; $changes_text = <F>; }
+ close( F );
+ @dists = $self->get_dists_from_changes( $changes_text );
+ } else {
+ $self->log("Cannot get dists from $upload_dir/$changes_f: $! (assuming unstable)\n");
+ @dists = ( "unstable" );
+ }
+
+FILE: foreach (@to_remove) {
+ if (/\.deb$/) {
+ # first listed wins
+ foreach my $dist (@dists) {
+ if ( -d $self->get_conf('HOME') . "/build/chroot-$dist" &&
+ -w $self->get_conf('HOME') . "/build/chroot-$dist/var/cache/apt/archives/") {
+ # TODO: send all of to_remove to perl-apt if it's available, setting a try_mv list
+ # that only has build-depends in it.
+ # if that's too much cpu, have buildd use perl-apt if avail to export the
+ # build-depends list, which could then be read in at this point
+ if (system (qw(mv --), "$upload_dir/$_",
+ $self->get_conf('HOME') .
+ "/build/chroot-$dist/var/cache/apt/archives/")) {
+ $self->log("Cannot move $upload_dir/$_ to cache dir\n");
+ } else {
+ next FILE;
+ }
+ }
+ }
+ }
+ unlink "$upload_dir/$_"
+ or $self->log("Can't remove $upload_dir/$_: $!\n");
+}
+ }
+ elsif ($subject =~ /^(\S+\.changes) is NEW$/) {
+ # "is new" mail from dinstall
+ my $changes_f = $1;
+ my $pkgv;
+ ($pkgv = $changes_f) =~ s/_(\S+)\.changes//;
+ $self->log("$pkgv must be manually dinstall-ed -- delayed\n");
+ }
+ elsif ($subject =~ /^new version of (\S+) \(dist=(\S+)\)$/) {
+ # notice from wanna-build
+ my ($pkg, $dist_name) = ($1, $2);
+ my $dist_config = $self->get_dist_config_by_name($dist_name);
+ goto forward if $self->get('Mail Body Text') !~ /^in version (\S+)\.$/m;
+ my $pkgv = $pkg."_".$1;
+ $self->get('Mail Body Text') =~ /new source version (\S+)\./m;
+ my $newv = $1;
+ $self->log("Build of $pkgv ($dist_name) obsolete -- new version $newv\n");
+ $self->register_outdated( $dist_name, $pkgv, $pkg."_".$newv );
+
+ my @ds;
+ if (!(@ds = $self->check_building_any_dist( $pkgv ))) {
+ if (!$self->remove_from_REDO( $pkgv )) {
+ $self->append_to_SKIP( $pkgv );
+ }
+ $self->purge_pkg( $pkgv, $dist_config );
+ }
+ else {
+ $self->log("Not deleting, still building for @ds\n");
+ }
+ }
+ elsif ($self->get('Mail Body Text') =~ /^blacklist (\S+)\n$/) {
+ my $pattern = "\Q$1\E";
+ if (open( F, ">>mail-blacklist" )) {
+ print F "$pattern\n";
+ close( F );
+ $self->log("Added $pattern to blacklist.\n");
+ }
+ else {
+ $self->log("Can't open mail-blacklist for appending: $!\n");
+ }
+ }
+ else {
+ goto forward_mail;
+ }
+
+
+ if ($self->get('Mail Error')) {
+ $self->log("Error: ",
+ $self->get('Mail Short Error') || $self->get('Mail Error'));
+ $self->reply("Your mail could not be processed:\n" .
+ $self->get('Mail Error'));
+ }
+ return;
+
+forward_mail:
+ my $header = $self->get('Mail Header');
+ $self->log("Mail from $header->{'from'}\nSubject: $subject\n");
+ if ($self->is_blacklisted( $self->get('Mail Header')->{'from'} )) {
+ $self->log("Address is blacklisted, deleting mail.\n");
+ }
+ else {
+ $self->log("Not for me, forwarding to admin\n");
+ ll_send_mail( $self->get_conf('ADMIN_MAIL'),
+ "To: $header->{'to'}\n".
+ ($header->{'cc'} ? "Cc: $header->{'cc'}\n" : "").
+ "From: $header->{'from'}\n".
+ "Subject: $header->{'subject'}\n".
+ "Date: $header->{'date'}\n".
+ "Message-Id: $header->{'message-id'}\n".
+ ($header->{'reply-to'} ? "Reply-To: $header->{'reply-to'}\n" : "").
+ ($header->{'in-reply-to'} ? "In-Reply-To: $header->{'in-reply-to'}\n" : "").
+ ($header->{'references'} ? "References: $header->{'references'}\n" : "").
+ ($header->{'content-type'} ? "Content-Type: $header->{'content-type'}\n": "").
+ "Resent-From: $Buildd::gecos <$Buildd::username\@$Buildd::hostname>\n".
+ "Resent-To: " . $self->get_conf('ADMIN_MAIL') . "\n\n".
+ $self->get('Mail Body Text') );
+ }
+}
+
+
+sub prepare_for_upload ($$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $changes = shift;
+
+ $changes =~ s/\n+$/\n/;
+
+ my( @files, @md5, @missing, @md5fail, $i );
+
+ my @to_dists = $self->get_dists_from_changes( $changes );
+ if (!@to_dists) { # probably not a valid changes
+
+ $self->set('Mail Short Error',
+ $self->get('Mail Error'));
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Couldn't find a valid Distribution: line.\n");
+ return 0;
+ }
+
+ my $changes_filename_arch = $self->get_conf('ARCH');
+ #Try to extract the arch from the actual changes file (see #566398)
+ if ($changes =~ /^Architecture:\s*(.+)/m) {
+ my @arches = grep { $_ ne "all" } split /\s+/, $1;
+ if (@arches > 1) {
+ $changes_filename_arch = "multi";
+ } else {
+ $changes_filename_arch = $arches[0];
+ }
+ }
+
+ $changes =~ /^Files:\s*\n((^[ ]+.*\n)*)/m;
+ foreach (split( "\n", $1 )) {
+ push( @md5, (split( /\s+/, $_ ))[1] );
+ push( @files, (split( /\s+/, $_ ))[5] );
+ }
+ if (!@files) { # probably not a valid changes
+ $self->set('Mail Short Error',
+ $self->get('Mail Error'));
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "No files listed in changes.\n");
+ return 0;
+ }
+ my @wrong_dists = ();
+ foreach my $d (@to_dists) {
+ push( @wrong_dists, $d )
+ if !$self->check_state(
+ $pkg,
+ $self->get_dist_config_by_name($d),
+ qw(Building Built Install-Wait Reupload-Wait Build-Attempted));
+ }
+ if (@wrong_dists) {
+ $self->set('Mail Short Error',
+ $self->get('Mail Error'));
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Package $pkg has target distributions @wrong_dists\n".
+ "for which it isn't registered as Building.\n".
+ "Please fix this by either modifying the Distribution: ".
+ "header or\n".
+ "taking the package in those distributions, too.\n");
+ return 0;
+ }
+
+ for( $i = 0; $i < @files; ++$i ) {
+ if (! -f $self->get_conf('HOME') . "/build/$files[$i]") {
+ push( @missing, $files[$i] ) ;
+ }
+ else {
+ my $home = $self->get_conf('HOME');
+ chomp( my $sum = `md5sum $home/build/$files[$i]` );
+ push( @md5fail, $files[$i] ) if (split(/\s+/,$sum))[0] ne $md5[$i];
+ }
+ }
+ if (@missing) {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "Missing files for move: @missing\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "While trying to move the built package $pkg to upload,\n".
+ "the following files mentioned in the .changes were not found:\n".
+ "@missing\n");
+ return 0;
+ }
+ if (@md5fail) {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "md5 failure during move: @md5fail\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "While trying to move the built package $pkg to upload,\n".
+ "the following files had bad md5 checksums:\n".
+ "@md5fail\n");
+ return 0;
+ }
+
+ my @upload_dirs = $self->get_upload_queue_dirs ( $changes );
+
+ my $pkg_noep = $pkg;
+ $pkg_noep =~ s/_\d*:/_/;
+ my $changes_name = $pkg_noep . "_" . $changes_filename_arch . ".changes";
+
+ for my $upload_dir (@upload_dirs) {
+ if (! -d $upload_dir &&!mkdir( $upload_dir, 0750 )) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Cannot create directory $upload_dir");
+ $self->log("Cannot create dir $upload_dir\n");
+ return 0;
+ }
+ }
+
+ my $errs = 0;
+ for my $upload_dir (@upload_dirs) {
+ lock_file( $upload_dir );
+ foreach (@files) {
+ if (system('cp', '--', $self->get_conf('HOME')."/build/$_", "$upload_dir/$_")) {
+ $self->log("Cannot copy $_ to $upload_dir/\n");
+ ++$errs;
+ }
+ }
+
+ open( F, ">$upload_dir/$changes_name" );
+ print F $changes;
+ close( F );
+ unlock_file( $upload_dir );
+ $self->log("Moved $pkg to ", basename($upload_dir), "\n");
+ }
+
+ foreach (@files) {
+ if (!unlink($self->get_conf('HOME') . "/build/$_")) {
+ $self->log("Cannot remove build/$_\n");
+ ++$errs;
+ }
+ }
+
+ if ($errs) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Could not move all files to upload dir.");
+ return 0;
+ }
+
+ unlink( $self->get_conf('HOME') . "/build/$changes_name" )
+ or $self->log("Cannot remove " . $self->get_conf('HOME') . "/$changes_name: $!\n");
+}
+
+sub redo_new_version ($$$) {
+ my $self = shift;
+ my $dist_config = shift;
+ my $oldv = shift;
+ my $newv = shift;
+
+ my $err = 0;
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query('-v', $newv);
+ if ($pipe) {
+ while(<$pipe>) {
+ next if /^wanna-build Revision/ ||
+ /^\S+: Warning: Older version / ||
+ /^\S+: ok$/;
+ $self->set('Mail Error',
+ $self->get('Mail Error') . $_);
+ $err = 1;
+ }
+ close($pipe);
+ } else {
+ $self->log("Can't spawn wanna-build: $!\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Can't spawn wanna-build: $!\n");
+ return;
+ }
+ if ($err) {
+ $self->log("Can't take newer version $newv due to wanna-build errors\n");
+ return;
+ }
+ $self->log("Going to build $newv instead of $oldv\n");
+
+ $self->append_to_REDO( $newv, $dist_config );
+}
+
+sub purge_pkg ($$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ my $dir;
+ local( *F );
+
+ $self->remove_from_REDO( $pkg );
+
+ # remove .changes and .deb in build dir (if existing)
+ my $pkg_noep = $pkg;
+ $pkg_noep =~ s/_\d*:/_/;
+ my $changes = "${pkg_noep}_" . $self->get_conf('ARCH') . ".changes";
+ if (-f "build/$changes" && open( F, "<build/$changes" )) {
+ local($/); undef $/;
+ my $changetext = <F>;
+ close( F );
+ my @files = $self->get_files_from_changes( $changetext );
+ push( @files, $changes );
+ $self->log("Purging files: $changes\n");
+ unlink( map { "build/$_" } @files );
+ }
+
+ # schedule dir for purging
+ ($dir = $pkg_noep) =~ s/-[^-]*$//; # remove Debian revision
+ $dir =~ s/_/-/; # change _ to -
+ if (-d "build/chroot-$dist_name/build/$Buildd::username/$dir") {
+ $dir = "build/chroot-$dist_name/build/$Buildd::username/$dir";
+ }
+ else {
+ $dir = "build/$dir";
+ }
+ return if ! -d $dir;
+
+ lock_file( "build/PURGE" );
+ if (open( F, ">>build/PURGE" )) {
+ print F "$dir\n";
+ close( F );
+ $self->log("Scheduled $dir for purging\n");
+ }
+ else {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Can't open build/PURGE: $!\n");
+ $self->log("Can't open build/PURGE: $!\n");
+ }
+ unlock_file( "build/PURGE" );
+}
+
+sub remove_from_upload ($) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+
+ my($changes_f, $upload_f, $changes_text, @to_remove);
+ local( *F );
+
+ $self->log("Remove $pkg from upload dir\n");
+ my $pkg_noep = $pkg;
+ $pkg_noep =~ s/_\d*:/_/;
+ $changes_f = "${pkg_noep}_" . $self->get_conf('ARCH') . ".changes";
+
+ my $upload_dir = $self->get_conf('HOME') . '/' . $dist_config->get('DUPLOAD_LOCAL_QUEUE_DIR');
+
+ if (!-f "$upload_dir/$changes_f") {
+ $self->log("$changes_f does not exist\n");
+ return;
+ }
+ if (!open( F, "<$upload_dir/$changes_f" )) {
+ $self->log("Cannot open $upload_dir/$changes_f: $!\n");
+ return;
+ }
+ { local($/); undef $/; $changes_text = <F>; }
+ close( F );
+ @to_remove = $self->get_files_from_changes( $changes_text );
+
+ ($upload_f = $changes_f) =~ s/\.changes$/\.upload/;
+ push( @to_remove, $changes_f, $upload_f );
+
+ $self->log("Removing files:\n", "@to_remove\n");
+ foreach (@to_remove) {
+ unlink "$upload_dir/$_"
+ or $self->log("Can't remove $upload_dir/$_: $!\n");
+ }
+}
+
+sub append_to_REDO ($$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ local( *F );
+
+ lock_file( "build/REDO" );
+
+ if (open( F, "build/REDO" )) {
+ my @pkgs = <F>;
+ close( F );
+ if (grep( /^\Q$pkg\E\s/, @pkgs )) {
+ $self->log("$pkg is already in REDO -- not rescheduled\n");
+ goto unlock;
+ }
+ }
+
+ if (open( F, ">>build/REDO" )) {
+ print F "$pkg $dist_name\n";
+ close( F );
+ $self->log("Scheduled $pkg for rebuild\n");
+ }
+ else {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Can't open build/REDO: $!\n");
+ $self->log("Can't open build/REDO: $!\n");
+ }
+
+ unlock:
+ unlock_file( "build/REDO" );
+}
+
+sub remove_from_REDO ($) {
+ my $self = shift;
+ my $pkg = shift;
+
+ local( *F );
+
+ lock_file( "build/REDO" );
+ goto unlock if !open( F, "<build/REDO" );
+ my @pkgs = <F>;
+ close( F );
+ if (!open( F, ">build/REDO" )) {
+ $self->log("Can't open REDO for writing: $!\n",
+ "Would write: @pkgs\nminus $pkg\n");
+ goto unlock;
+ }
+ my $done = 0;
+ foreach (@pkgs) {
+ if (/^\Q$pkg\E\s/) {
+ ++$done;
+ }
+ else {
+ print F $_;
+ }
+ }
+ close( F );
+ $self->log("Deleted $pkg from REDO list.\n") if $done;
+ unlock:
+ unlock_file( "build/REDO" );
+ return $done;
+}
+
+sub append_to_SKIP ($) {
+ my $self = shift;
+ my $pkg = shift;
+
+ local( *F );
+
+ return if !open( F, "<build/build-progress" );
+ my @lines = <F>;
+ close( F );
+
+ if (grep( /^\s*\Q$pkg\E$/, @lines )) {
+ # pkg is in build-progress, but without a suffix (failed,
+ # successful, currently building), so it can be skipped
+ lock_file( "build/SKIP" );
+ if (open( F, ">>build/SKIP" )) {
+ print F "$pkg\n";
+ close( F );
+ $self->log("Told sbuild to skip $pkg\n");
+ }
+ unlock_file( "build/SKIP" );
+ }
+}
+
+sub check_is_outdated ($$) {
+ my $self = shift;
+ my $dist_config = shift;
+ my $package = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ my %newv;
+ return 0 if !(%newv = $self->is_outdated( $dist_name, $package ));
+
+ my $have_changes = 1 if $self->get('Mail Body Text') =~ /^---+\s*BEGIN PGP SIGNED MESSAGE/;
+
+ # If we have a changes file, we can see which distributions that
+ # package is aimed to. Otherwise, we're out of luck because we can't see
+ # reliably anymore for which distribs the package was for. Let the user
+ # find out this...
+ #
+ # If the package is outdated in all dists we have to consider,
+ # send a plain error message. If only outdated in some of them, send a
+ # modified error that tells to send a restricted changes (with
+ # Distribution: only for those dists where it isn't outdated), or to do
+ # the action manually, because it would be (wrongly) propagated.
+ goto all_outdated if !$have_changes;
+
+ my @check_dists = ();
+ @check_dists = $self->get_dists_from_changes($self->get('Mail Body Text'));
+
+ my @not_outdated = ();
+ my @outdated = ();
+ foreach (@check_dists) {
+ if (!exists $newv{$_}) {
+ push( @not_outdated, $_ );
+ }
+ else {
+ push( @outdated, $_ );
+ }
+ }
+ return 0 if !@outdated;
+ if (@not_outdated) {
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "$package ($dist_name) partially outdated ".
+ "(ok for @not_outdated)\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Package $package ($dist_name) is partially outdated.\n".
+ "The following new versions have appeared in the meantime:\n ".
+ join( "\n ", map { "$_: $newv{$_}" } keys %newv )."\n\n".
+ "Please send a .changes for the following distributions only:\n".
+ " Distribution: ".join( " ", @not_outdated )."\n");
+ }
+ else {
+ all_outdated:
+ $self->set('Mail Short Error',
+ $self->get('Mail Short Error') .
+ "$package ($dist_name) outdated; new versions ".
+ join( ", ", map { "$_:$newv{$_}" } keys %newv )."\n");
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Package $package ($dist_name) is outdated.\n".
+ "The following new versions have appeared in the meantime:\n ".
+ join( "\n ", map { "$_: $newv{$_}" } keys %newv )."\n");
+ }
+ return 1;
+}
+
+sub is_outdated ($$) {
+ my $self = shift;
+ my $dist_name = shift;
+ my $pkg = shift;
+
+ my %result = ();
+ local( *F );
+
+ lock_file( "outdated-packages" );
+ goto unlock if !open( F, "<outdated-packages" );
+ while( <F> ) {
+ my($oldpkg, $newpkg, $t, $d) = split( /\s+/, $_ );
+ $d ||= "unstable";
+ if ($oldpkg eq $pkg && $d eq $dist_name) {
+ $result{$d} = $newpkg;
+ }
+ }
+ close( F );
+ unlock:
+ unlock_file( "outdated-packages" );
+ return %result;
+}
+
+sub register_outdated ($$$) {
+ my $self = shift;
+ my $dist = shift;
+ my $oldv = shift;
+ my $newv = shift;
+
+ my(@pkgs);
+ local( *F );
+
+ lock_file( "outdated-packages" );
+
+ if (open( F, "<outdated-packages" )) {
+ @pkgs = <F>;
+ close( F );
+ }
+
+ if (!open( F, ">outdated-packages" )) {
+ $self->log("Cannot open outdated-packages for writing: $!\n");
+ goto unlock;
+ }
+ my $now = time;
+ my @d = ();
+ foreach (@pkgs) {
+ my($oldpkg, $newpkg, $t, $d) = split( /\s+/, $_ );
+ $d ||= "unstable";
+ next if ($oldpkg eq $oldv && $d eq $dist) || ($now - $t) > 21*24*60*60;
+ print F $_;
+ }
+ print F "$oldv $newv $now $dist\n";
+ close( F );
+ unlock:
+ unlock_file( "outdated-packages" );
+}
+
+sub set_to_failed ($$$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $text = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ my $is_bugno = 0;
+
+ $text =~ s/^\.$/../mg;
+ $is_bugno = 1 if $text =~ /^\(see #\d+\)$/;
+ return if !$self->check_state( $pkg, $dist_config, $is_bugno ? "Failed" : qw(Built Building Build-Attempted BD-Uninstallable) );
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query_out('--failed', $pkg);
+ if ($pipe) {
+ print $pipe "${text}.\n";
+ close($pipe);
+ }
+ if ($?) {
+ my $t = "wanna-build --failed failed with status ".exitstatus($?)."\n";
+ $self->log($t);
+ $self->set('Mail Error',
+ $self->get('Mail Error') . $t);
+ } elsif ($is_bugno) {
+ $self->log("Bug# appended to fail message of $pkg ($dist_name)\n");
+ }
+ else {
+ $self->log("Set package $pkg ($dist_name) to Failed\n");
+ $self->write_stats("failed", 1);
+ }
+}
+
+sub set_to_depwait ($$$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $deps = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query_out('--dep-wait', $pkg);
+ if ($pipe) {
+ print $pipe "$deps\n";
+ close($pipe);
+ }
+ if ($?) {
+ my $t = "wanna-build --dep-wait failed with status ".exitstatus($?)."\n";
+ $self->log($t);
+ $self->set('Mail Error',
+ $self->get('Mail Error') . $t);
+ }
+ else {
+ $self->log("Set package $pkg ($dist_name) to Dep-Wait\nDependencies: $deps\n");
+ }
+ $self->write_stats("dep-wait", 1);
+}
+
+sub give_back ($$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ my $answer;
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query('--give-back', $pkg);
+ if ($pipe) {
+ $answer = <$pipe>;
+ close($pipe);
+ }
+ if ($?) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "wanna-build --give-back failed:\n$answer");
+ }
+ else {
+ $self->log("Given back package $pkg ($dist_name)\n");
+ }
+}
+
+sub no_build ($$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+ my $answer_cmd;
+
+ my $answer;
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query('--no-build', $pkg);
+ if ($pipe) {
+ $answer = <$pipe>;
+ close($pipe);
+ }
+ if ($?) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "no-build failed:\n$answer");
+ }
+ else {
+ $self->log("Package $pkg ($dist_name) to set Not-For-Us\n");
+ }
+ $self->write_stats("no-build", 1);
+}
+
+sub get_fail_msg ($$) {
+ my $self = shift;
+ my $pkg = shift;
+ my $dist_config = shift;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ $pkg =~ s/_.*//;
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query('--info', $pkg);
+ if ($pipe) {
+ my $msg = "";
+ while(<$pipe>) {
+ if (/^\s*Old-Failed\s*:/) {
+ while(<$pipe>) {
+ last if /^ \S+\s*/;
+ $_ =~ s/^\s+//;
+ if (/^----+\s+\S+\s+----+$/) {
+ last if $msg;
+ }
+ else {
+ $msg .= $_;
+ }
+ }
+ last;
+ }
+ }
+ close($pipe);
+ return $msg if $msg;
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Couldn't find Old-Failed in info for $pkg\n");
+ return "Same as previous version (couldn't extract the text)\n";
+ } else {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Couldn't start wanna-build --info: $!\n");
+ return "Same as previous version (couldn't extract the text)\n";
+ }
+}
+
+sub check_state ($@) {
+ my $self = shift;
+ my $mail_error = $self->get('Mail Error');
+ my $retval = $self->check_state_internal(@_);
+ # check if we should retry the call
+ if ($retval == -1) {
+ my $interval = int(rand(120));
+ $self->log("Retrying --info in $interval seconds...\n");
+ # reset error to old value
+ $self->set('Mail Error', $mail_error);
+ # 0..120s of sleep ought to be enough for retrying;
+ # for mail bursts, this should get us out of the
+ # crticial mass
+ sleep $interval;
+ $retval = $self->check_state_internal(@_);
+ # remap the -1 retry code to failure
+ if ($retval == -1) {
+ return 0;
+ } else {
+ return $retval;
+ }
+ }
+ return $retval;
+}
+
+sub check_state_internal ($$@) {
+ my $self = shift;
+ my $pkgv = shift;
+ my $dist_config = shift;
+ my @wanted_states = @_;
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ $pkgv =~ /^([^_]+)_(.+)/;
+ my ($pkg, $vers) = ($1, $2);
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query('--info', $pkg);
+ if (!$pipe) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Couldn't start wanna-build --info: $!\n");
+ $self->log("Couldn't start wanna-build --info: $!\n");
+ # let check_state() retry if needed
+ return -1;
+ }
+
+ my ($av, $as, $ab, $an);
+ while(<$pipe>) {
+ $av = $1 if /^\s*Version\s*:\s*(\S+)/;
+ $as = $1 if /^\s*State\s*:\s*(\S+)/;
+ $ab = $1 if /^\s*Builder\s*:\s*(\S+)/;
+ $an = $1 if /^\s*Binary-NMU-Version\s*:\s*(\d+)/;
+ }
+ close($pipe);
+
+ if ($?) {
+ my $t = "wanna-build --info failed with status ".exitstatus($?)."\n";
+ $self->log($t);
+ $self->set('Mail Error',
+ $self->get('Mail Error') . $t);
+ return 0;
+ }
+
+ my $msg = "$pkgv($dist_name) check_state(@wanted_states): ";
+ $av = binNMU_version($av,$an,undef) if (defined $an);
+ if ($av ne $vers) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ $msg."version $av registered as $as\n");
+ return 0;
+ }
+ if (!Buildd::isin( $as, @wanted_states)) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ $msg."state is $as\n");
+ return 0;
+ }
+ if ($as eq "Building" && $ab ne $dist_config->get('WANNA_BUILD_DB_USER')) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ $msg."is building by $ab\n");
+ return 0;
+ }
+ return 1;
+}
+
+sub check_building_any_dist ($) {
+ my $self = shift;
+ my $pkgv = shift;
+
+ my @dists;
+
+ $pkgv =~ /^([^_]+)_(.+)/;
+ my ($pkg, $vers) = ($1, $2);
+
+ for my $dist_config (@{$self->get_conf('DISTRIBUTIONS')}) {
+ my $dist_name = $dist_config->get('DIST_NAME');
+
+ my $db = $self->get_db_handle($dist_config);
+ my $pipe = $db->pipe_query('--info', $pkg);
+ if (!$pipe) {
+ $self->set('Mail Error',
+ $self->get('Mail Error') .
+ "Couldn't start wanna-build --info: $!\n");
+ return 0;
+ }
+
+ my $text;
+ { local ($/); $text = <$pipe>; }
+ close($pipe);
+
+ while( $text =~ /^\Q$pkg\E\((\w+)\):(.*)\n((\s.*\n)*)/mg ) {
+ my ($dist, $rest, $info) = ($1, $2, $3);
+ next if $rest =~ /not registered/;
+ my ($av, $as, $ab);
+ $av = $1 if $info =~ /^\s*Version\s*:\s*(\S+)/mi;
+ $as = $1 if $info =~ /^\s*State\s*:\s*(\S+)/mi;
+ $ab = $1 if $info =~ /^\s*Builder\s*:\s*(\S+)/mi;
+ push( @dists, $dist )
+ if $av eq $vers && $as eq "Building" &&
+ $ab eq $self->get_conf('WANNA_BUILD_DB_USER');
+ }
+ }
+ return @dists;
+}
+
+sub get_files_from_changes ($) {
+ my $self = shift;
+ my $changes_text = shift;
+
+ my(@filelines, @files);
+
+ $changes_text =~ /^Files:\s*\n((^[ ]+.*\n)*)/m;
+ @filelines = split( "\n", $1 );
+ foreach (@filelines) {
+ push( @files, (split( /\s+/, $_ ))[5] );
+ }
+ return @files;
+}
+
+sub get_dists_from_changes ($) {
+ my $self = shift;
+ my $changes_text = shift;
+
+ $changes_text =~ /^Distribution:\s*(.*)\s*$/mi;
+ return split( /\s+/, $1 );
+}
+
+sub get_upload_queue_dirs ($) {
+ my $self = shift;
+ my $changes_text = shift;
+
+ my %upload_dirs;
+ my @dists = $self->get_dists_from_changes( $changes_text );
+ for my $dist_config (@{$self->get_conf('DISTRIBUTIONS')}) {
+ my $upload_dir = $self->get_conf('HOME') . '/' . $dist_config->get('DUPLOAD_LOCAL_QUEUE_DIR');
+
+ if (grep { $dist_config->get('DIST_NAME') eq $_ } @dists) {
+ $upload_dirs{$upload_dir} = 1;
+ }
+ }
+ return keys %upload_dirs;
+}
+
+sub find_upload_dirs_for_changes_file ($) {
+ my $self = shift;
+ my $changes_file_name = shift;
+
+ my %dirs;
+
+ for my $dist_config (@{$self->get_conf('DISTRIBUTIONS')}) {
+ my $upload_dir = $self->get_conf('HOME') . '/' . $dist_config->get('DUPLOAD_LOCAL_QUEUE_DIR');
+ if (-f "$upload_dir/$changes_file_name") {
+ $dirs{$upload_dir} = 1;
+ }
+ }
+
+ return keys %dirs;
+}
+
+sub reply ($) {
+ my $self = shift;
+ my $text = shift;
+
+ my( $to, $subj, $quoting );
+
+ $to = $self->get('Mail Header')->{'reply-to'} ||
+ $self->get('Mail Header')->{'from'};
+ $subj = $self->get('Mail Header')->{'subject'};
+ $subj = "Re: $subj" if $subj !~ /^Re\S{0,2}:/;
+ ($quoting = $self->get('Mail Body Text')) =~ s/\n+$/\n/;
+ $quoting =~ s/^/> /mg;
+
+ send_mail( $to, $subj, "$text\n$quoting",
+ "In-Reply-To: ". $self->get('Mail Header')->{'message-id'}. "\n" );
+}
+
+sub is_blacklisted ($) {
+ my $self = shift;
+ my $addr = shift;
+
+ local( *BL );
+
+ $addr = $1 if $addr =~ /<(.*)>/;
+ return 0 if !open( BL, "<mail-blacklist" );
+ while( <BL> ) {
+ chomp;
+ if ($addr =~ /$_$/) {
+ close( BL );
+ return 1;
+ }
+ }
+ close( BL );
+ return 0;
+}
+
+sub add_error_mail () {
+ my $self = shift;
+
+ local( *F );
+ my $now = time;
+ my @em = ();
+
+ if (open( F, "<mail-errormails" )) {
+ chomp( @em = <F> );
+ close( F );
+ }
+ push( @em, $now );
+ shift @em while @em && ($now - $em[0]) > $self->get_conf('ERROR_MAIL_WINDOW');
+
+ if (@em) {
+ open( F, ">mail-errormails" );
+ print F join( "\n", @em ), "\n";
+ close( F );
+ }
+ else {
+ unlink( "mail-errormails" );
+ }
+
+ return scalar(@em);
+}
+
+1;
diff --git a/lib/Buildd/Makefile.am b/lib/Buildd/Makefile.am
new file mode 100644
index 0000000..5b5b930
--- /dev/null
+++ b/lib/Buildd/Makefile.am
@@ -0,0 +1,42 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2009 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
+
+perlmodbuildddir = $(perlmoddir)/Buildd
+
+MODULES = \
+ Base.pm \
+ Client.pm \
+ ClientConf.pm \
+ Conf.pm \
+ DistConf.pm \
+ UploadQueueConf.pm \
+ Daemon.pm \
+ Mail.pm \
+ Uploader.pm \
+ Watcher.pm
+
+perlmodbuildd_DATA = \
+ $(MODULES)
+
+EXTRA_DIST = \
+ $(MODULES)
diff --git a/lib/Buildd/UploadQueueConf.pm b/lib/Buildd/UploadQueueConf.pm
new file mode 100644
index 0000000..d0fb1e0
--- /dev/null
+++ b/lib/Buildd/UploadQueueConf.pm
@@ -0,0 +1,96 @@
+#
+# Conf.pm: configuration library for buildd
+# Copyright © 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2006-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/>.
+#
+#######################################################################
+
+package Buildd::UploadQueueConf;
+
+use strict;
+use warnings;
+
+use Sbuild::ConfBase;
+use Sbuild::Sysconfig;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(new_hash setup read_hash);
+}
+
+sub new_hash (@);
+sub setup ($);
+sub read_hash ($$);
+
+sub new_hash (@) {
+ my %opts = @_;
+
+ my $queue_config = Sbuild::ConfBase->new(%opts);
+
+ Buildd::UploadQueueConf::setup($queue_config);
+ Buildd::UploadQueueConf::read_hash($queue_config, $opts{'HASH'});
+
+ return $queue_config;
+}
+
+sub setup ($) {
+ my $conf = shift;
+
+ my $validate_directory_in_home = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $directory = $conf->get($key);
+ my $home_directory = $conf->get('HOME');
+
+ die "$key directory is not defined"
+ if !defined($directory) || !$directory;
+
+ die "$key directory '$home_directory/$directory' does not exist"
+ if !-d $home_directory . "/" . $directory;
+ };
+
+ my %dupload_queue_keys = (
+ 'DUPLOAD_LOCAL_QUEUE_DIR' => {
+ CHECK => $validate_directory_in_home,
+ DEFAULT => 'upload'
+ },
+ 'DUPLOAD_ARCHIVE_NAME' => {
+ DEFAULT => 'anonymous-ftp-master'
+ },
+ );
+
+ $conf->set_allowed_keys(\%dupload_queue_keys);
+
+ Buildd::ClientConf::setup($conf);
+}
+
+sub read_hash ($$) {
+ my $conf = shift;
+ my $data = shift;
+
+ for my $key (keys %$data) {
+ $conf->set($key, $data->{$key});
+ }
+}
+
+1;
diff --git a/lib/Buildd/Uploader.pm b/lib/Buildd/Uploader.pm
new file mode 100644
index 0000000..302f5da
--- /dev/null
+++ b/lib/Buildd/Uploader.pm
@@ -0,0 +1,274 @@
+# 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/>.
+#
+#######################################################################
+
+package Buildd::Uploader;
+
+use strict;
+use warnings;
+
+use Buildd qw(lock_file unlock_file unset_env exitstatus send_mail);
+use Buildd::Base;
+use Buildd::Conf qw();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Buildd::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('Uploader Lock', undef);
+ $self->set('Uploaded Pkgs', {});
+
+ $self->open_log();
+
+ return $self;
+}
+
+sub run {
+ my $self = shift;
+
+ unset_env();
+
+ $self->set('Uploader Lock',
+ lock_file("$main::HOME/buildd-uploader", 1));
+
+ if (!$self->get('Uploader Lock')) {
+ $self->log("exiting; another buildd-uploader is still running");
+ return 1;
+ }
+
+ for my $queue_config (@{$self->get_conf('UPLOAD_QUEUES')}) {
+ $self->upload(
+ $queue_config->get('DUPLOAD_LOCAL_QUEUE_DIR'),
+ $queue_config->get('DUPLOAD_ARCHIVE_NAME'));
+ }
+
+ my $uploaded_pkgs = $self->get('Uploaded Pkgs');
+
+ foreach my $archdist (keys %{$uploaded_pkgs}) {
+ $self->log("Set to Uploaded($archdist):$uploaded_pkgs->{$archdist}");
+ }
+
+ return 0;
+}
+
+sub uploaded ($@) {
+ my $self = shift;
+ my $pkg = shift;
+ my $arch_name = shift;
+ my $dist_name = shift;
+
+ my $msgs = "";
+
+ my $dist_config = $self->get_arch_dist_config_by_name($arch_name, $dist_name);
+ my $db = $self->get_db_handle($dist_config);
+
+ my $pipe = $db->pipe_query('--uploaded', $pkg);
+
+ if ($pipe) {
+ while(<$pipe>) {
+ if (!/^(\S+): Propagating new state /) {
+ $msgs .= $_;
+ }
+ }
+ close($pipe);
+ if ($msgs or $?) {
+ $self->log($msgs) if $msgs;
+ $self->log("wanna-build --uploaded failed with status ",
+ exitstatus($?), "\n" )
+ if $?;
+ } else {
+ my $archdist_name = "$arch_name/$dist_name";
+ $self->get('Uploaded Pkgs')->{$archdist_name} .= " $pkg";
+ }
+ } else {
+ $self->log("Can't spawn wanna-build --uploaded: $!\n");
+ }
+}
+
+sub upload ($$) {
+ my $self = shift;
+ my $udir = shift;
+ my $upload_to = shift;
+
+ chdir( "$main::HOME/$udir" ) || return;
+ lock_file( "$main::HOME/$udir" );
+
+ my( $f, $g, @before, @after );
+
+ foreach $f (<*.changes>) {
+ ($g = $f) =~ s/\.changes$/\.upload/;
+ push( @before, $f ) if ! -f $g;
+ }
+
+ unlock_file( "$main::HOME/$udir" );
+
+ if (!@before) {
+ $self->log("Nothing to do for $udir\n");
+ return;
+ }
+
+ $self->log(scalar(@before), " jobs to upload in $udir: @before\n");
+
+ foreach $f (@before) {
+ ($g = $f) =~ s/\.changes$/\.upload/;
+ my $logref = $self->do_dupload( $upload_to, $f );
+
+ if (defined $logref and scalar(@$logref) > 0) {
+ my $line;
+
+ foreach $line (@$logref) {
+ $self->log($line);
+ }
+ }
+
+ if ( -f $g ) {
+ if (!open( F, "<$f" )) {
+ $self->log("Cannot open $f: $!\n");
+ next;
+ }
+ my $text;
+ { local($/); undef $/; $text = <F>; }
+ close( F );
+ if ($text !~ /^Architecture:\s*(.*)\s*$/m) {
+ $self->log("$f doesn't have a Architecture: field\n");
+ next;
+ }
+ my @archs = split( /\s+/, $1 );
+ if ($text !~ /^Distribution:\s*(.*)\s*$/m) {
+ $self->log("$f doesn't have a Distribution: field\n");
+ next;
+ }
+ my @dists = split( /\s+/, $1 );
+ my ($version,$source,$pkg);
+ if ($text =~ /^Version:\s*(\S+)\s*$/m) {
+ $version = $1;
+ }
+ if ($text =~ /^Source:\s*(\S+)(?:\s+\(\S+\))?\s*$/m) {
+ $source = $1;
+ }
+ if (defined($version) and defined($source)) {
+ $pkg = "${source}_$version";
+ } else {
+ ($pkg = $f) =~ s/_\S+\.changes$//;
+ }
+ $self->uploaded($pkg, @archs, @dists);
+ } else {
+ push (@after, $f);
+ }
+ }
+
+ if (@after) {
+ $self->log("The following jobs were not processed (successfully):\n" .
+ "@after\n");
+ }
+ else {
+ $self->log("dupload successful.\n");
+ }
+ $self->write_stats("uploads", scalar(@before) - scalar(@after));
+}
+
+sub do_dupload ($@) {
+ my $self = shift;
+ my $upload_to = shift;
+
+ my @jobs = @_;
+ my @log;
+ local( *PIPE );
+ my( $current_job, $current_file, @failed, $errs );
+
+ if (!open( PIPE, "dupload -k --to $upload_to @jobs </dev/null 2>&1 |" )) {
+ return "Cannot spawn dupload: $!";
+ }
+
+ my $dup_log = "";
+ while( <PIPE> ) {
+ $dup_log .= $_;
+ chomp;
+ if (/^\[ job \S+ from (\S+\.changes)$/) {
+ $current_job = $1;
+ }
+ elsif (/^warning: MD5sum mismatch for (\S+), skipping/i) {
+ my $f = $1;
+ push( @log, "dupload error: md5sum mismatch for $f\n" );
+ $errs .= "md5sum mismatch on file $f ($current_job)\n";
+ push( @failed, $current_job );
+ }
+ elsif (/^\[ Uploading job (\S+)$/) {
+ $current_job = "$1.changes";
+ }
+ elsif (/dupload fatal error: Can't upload (\S+)/i ||
+ /^\s(\S+).*scp: (.*)$/) {
+ my($f, $e) = ($1, $2);
+ push( @log, "dupload error: upload error for $f\n" );
+ push( @log, "($e)\n" ) if $e;
+ $errs .= "upload error on file $f ($current_job)\n";
+ push( @failed, $current_job );
+ }
+ elsif (/Timeout at [\S]+ line [\d]+$/) {
+ $errs .= "upload timeout on file $current_job\n";
+ push( @failed, $current_job );
+ }
+ elsif (/^\s(\S+)\s+[\d.]+ kB /) {
+ $current_file = $1;
+ }
+ }
+ close( PIPE );
+ if ($?) {
+ if (($? >> 8) == 141) {
+ push( @log, "dupload error: SIGPIPE (broken connection)\n" );
+ $errs .= "upload error (broken connection) during ".
+ "file $current_file ($current_job)\n";
+ push( @failed, $current_job );
+ }
+ else {
+ push( @log, "dupload exit status ". exitstatus($?) );
+ $errs .= "dupload exit status ".exitstatus($?)."\n";
+ push( @failed, $current_job );
+ }
+ }
+
+ foreach (@failed) {
+ my $u = $_;
+ $u =~ s/\.changes$/\.upload/;
+ unlink( $u );
+ push( @log, "Removed $u due to upload errors.\n" );
+ $errs .= "Removed $u to reupload later.\n";
+ }
+
+ if ($errs) {
+ $errs .= "\nComplete output from dupload:\n\n$dup_log";
+ send_mail($self->get_conf('ADMIN_MAIL'), "dupload errors", $errs);
+ }
+ return \@log;
+}
+
+1;
diff --git a/lib/Buildd/Watcher.pm b/lib/Buildd/Watcher.pm
new file mode 100644
index 0000000..fd83d4e
--- /dev/null
+++ b/lib/Buildd/Watcher.pm
@@ -0,0 +1,528 @@
+# buildd-watcher:
+# Copyright © 1998 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/>.
+#
+#######################################################################
+
+package Buildd::Watcher;
+
+use strict;
+use warnings;
+use Buildd qw(send_mail lock_file unlock_file unset_env);
+use Buildd::Conf qw();
+use Buildd::Base;
+
+use POSIX qw(ESRCH LONG_MAX);
+use Cwd;
+
+sub ST_MTIME () { 9 }
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Buildd::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('Fudge', 1/24/6); # 10 minutes in units of a day
+ $self->set('Graph Maxval', {
+ 'builds-per-day' => 100,
+ 'uploads-per-day' => 100,
+ 'failed-per-day' => 50,
+ 'dep-wait-per-day' => 50,
+ 'give-back-per-day' => 50,
+ 'time-per-build' => 10*60*60,
+ 'build-time-percent' => 1,
+ 'idle-time-percent' => 1});
+
+ $self->open_log();
+
+ return $self;
+}
+
+sub run {
+ my $self = shift;
+
+ unset_env();
+ chdir($self->get_conf('HOME'));
+
+# check if another watcher is still running
+ my $watcher_pid;
+ if (open( PID, "<watcher-running")) {
+ $watcher_pid = <PID>;
+ close( PID );
+ $watcher_pid =~ /^\s*(\d+)/; $watcher_pid = $1;
+ if (!$watcher_pid || (kill( 0, $watcher_pid ) == 0 && $! == ESRCH)) {
+ $self->log("Ignoring stale watcher-running file (pid $watcher_pid).\n");
+ }
+ else {
+ $self->log("Another buildd-watcher is still running ".
+ "(pid $watcher_pid) -- exiting.\n");
+ return 0;
+ }
+ }
+ open( F, ">watcher-running.new" )
+ or die "Can't create watcher-running.new: $!\n";
+ printf F "%5d\n", $$;
+ close( F );
+ rename( "watcher-running.new", "watcher-running" )
+ or die "Can't rename watcher-running.new: $!\n";
+
+# check if buildd is still running, restart it if needed.
+ my $restart = 0;
+ my $daemon_pid;
+ if (open( PID, "<" . $self->get_conf('PIDFILE') )) {
+ $daemon_pid = <PID>;
+ close( PID );
+ $daemon_pid =~ /^\s*(\d+)/; $daemon_pid = $1;
+ if (!$daemon_pid || (kill( 0, $daemon_pid ) == 0 && $! == ESRCH)) {
+ $self->log("pid file exists, but process $daemon_pid doesn't exist.\n");
+ $restart = 1;
+ }
+ }
+ else {
+ $self->log("daemon not running (no pid file).\n");
+ $restart = 1;
+ }
+
+# do dir-purges that buildd-mail can't do (is running as nobody, so no sudo)
+ lock_file( "build/PURGE" );
+ my @to_purge = ();
+ if (open( F, "<build/PURGE" )) {
+ @to_purge = <F>;
+ close( F );
+ unlink( "build/PURGE" );
+ chomp( @to_purge );
+ }
+ unlock_file( "build/PURGE" );
+
+ foreach (@to_purge) {
+ next if ! -d $_;
+ system(qw(sudo rm -rf --), $_);
+ $self->log("Purged $_\n");
+ }
+
+# cut down mail-errormails file
+ my $now = time;
+ my @em = ();
+ if (open( F, "<mail-errormails" )) {
+ chomp( @em = <F> );
+ close( F );
+ }
+ shift @em while @em && ($now - $em[0]) > $self->get_conf('ERROR_MAIL_WINDOW');
+ if (@em) {
+ open( F, ">mail-errormails" );
+ print F join( "\n", @em ), "\n";
+ close( F );
+ }
+ else {
+ unlink( "mail-errormails" );
+ }
+
+# check for old stuff in build and upload dirs
+ my %warnfile;
+ my $file;
+ my $dev;
+ my $ino;
+ foreach $file (<upload/*>) {
+ ($dev,$ino) = lstat $file;
+ $warnfile{"$dev:$ino"} = $file if -M $file >= $self->get_conf('WARNING_AGE');
+ }
+ # TODO: Glob is incompatible with modern sbuild, which doesn't use
+ # separate user directories.
+ my $username = $self->get_conf('USERNAME');
+ foreach $file (<build/chroot-*/build/$username/*>) {
+ ($dev,$ino) = lstat $file;
+ if (! -d _ && ! -l _) {
+ $warnfile{"$dev:$ino"} = $file if -C _ >= $self->get_conf('WARNING_AGE');
+ }
+ else {
+ my $warnage = $self->get_conf('WARNING_AGE');
+ my $changed_files =
+ `find $file -ctime -$warnage -print 2>/dev/null`;
+ $warnfile{"$dev:$ino"} = $file if !$changed_files;
+ }
+ }
+ foreach $file (<build/*>) {
+ next if $file =~ m#^build/chroot-[^/]+$#;
+ ($dev,$ino) = lstat $file;
+ if (! -d _ && ! -l _) {
+ $warnfile{"$dev:$ino"} = $file if -C _ >= $self->get_conf('WARNING_AGE');
+ }
+ else {
+ my $warnage = $self->get_conf('WARNING_AGE');
+ my $changed_files =
+ `find $file -ctime -$warnage -print 2>/dev/null`;
+ $warnfile{"$dev:$ino"} = $file if !$changed_files;
+ }
+ }
+ my $nowarnpattern = $self->get_conf('NO_WARN_PATTERN');
+ my @warnings = grep( !/$nowarnpattern/, sort values %warnfile );
+ if (@warnings) {
+ my %reported;
+ my @do_warn;
+ if (open( W, "<reported-old-files" )) {
+ while( <W> ) {
+ next if !/^(\S+)\s+(\d+)$/;
+ $reported{$1} = $2;
+ }
+ close( W );
+ }
+
+ foreach (@warnings) {
+ if (!exists($reported{$_}) ||
+ ($now - $reported{$_}) >= $self->get_conf('WARNING_AGE')*24*60*60) {
+ push( @do_warn, $_ );
+ $reported{$_} = $now;
+ }
+ }
+
+ my $old_umask = umask 007;
+ open( W, ">reported-old-files" )
+ or die "Can't create/write reported-old-files: $!\n";
+ foreach (keys %reported) {
+ print W "$_ $reported{$_}\n" if -e $_ || -l $_;
+ }
+ close( W );
+ umask $old_umask;
+
+ send_mail( $self->get_conf('ADMIN_MAIL'), "buildd-watcher found some old files",
+ "buildd-watcher has found some old files or directories in\n".
+ "~buildd/upload and/or ~buildd/build. Those are:\n\n ".
+ join( "\n ", @do_warn ). "\n\n".
+ "Please have a look at them and remove them if ".
+ "they're obsolete.\n" )
+ if @do_warn;
+ }
+
+# archive old package/build log files
+ $self->archive_logs( "logs", "*", "old-logs/plog", $self->get_conf('PKG_LOG_KEEP') );
+ $self->archive_logs( "build", "build-*.log", "old-logs/blog", $self->get_conf('BUILD_LOG_KEEP') );
+
+# rotate daemon's log file
+ if (!-f "old-logs/daemon-stamp" ||
+ -M "old-logs/daemon-stamp" > $self->get_conf('DAEMON_LOG_ROTATE')-$self->get('Fudge')) {
+
+ $self->log("Rotating daemon log file\n");
+ system(qw(touch old-logs/daemon-stamp));
+
+ my $d = $self->format_time(time);
+ if (-f $self->get_conf('DAEMON_LOG_FILE') . ".old") {
+ system(qw(mv --), $self->get_conf('DAEMON_LOG_FILE') . '.old', "old-logs/daemon-$d.log");
+ system(qw(gzip -9), "old-logs/daemon-$d.log");
+ }
+
+ rename( $self->get_conf('DAEMON_LOG_FILE'),
+ $self->get_conf('DAEMON_LOG_FILE') . ".old" );
+ my $old_umask = umask 0007;
+ system(qw(touch --), $self->get_conf('DAEMON_LOG_FILE'));
+ umask $old_umask;
+ kill( 1, $daemon_pid ) if $daemon_pid;
+ $self->reopen_log();
+
+ if ($self->get_conf('DAEMON_LOG_SEND')) {
+ my $text;
+ open( F, "<" . $self->get_conf('DAEMON_LOG_FILE') . ".old" );
+ { local($/); undef $/; $text = <F>; }
+ close( F );
+ send_mail( $self->get_conf('ADMIN_MAIL'), "Build Daemon Log $d", $text );
+ }
+ }
+ $self->archive_logs( "old-logs", "daemon-*.log.gz", "old-logs/dlog", $self->get_conf('DAEMON_LOG_KEEP') );
+
+# make buildd statistics
+ if (!-f "stats/Stamp" ||
+ -M "stats/Stamp" > $self->get_conf('STATISTICS_PERIOD')-$self->get('Fudge')) {
+
+ $self->log("Making buildd statistics\n");
+ lock_file( "stats" );
+ my $lasttime = 0;
+ if (open( F, "<stats/Stamp" )) {
+ chomp( $lasttime = <F> );
+ close( F );
+ }
+ my $now = time;
+
+ $self->make_statistics( $lasttime, $now );
+
+ open( F, ">stats/Stamp" );
+ print F "$now\n";
+ close( F );
+ unlock_file( "stats" );
+
+ my $text;
+ open( F, "<stats/Summary" );
+ { local($/); undef $/; $text = <F>; }
+ close( F );
+ send_mail( $self->get_conf('STATISTICS_MAIL'), "Build Daemon Statistics", $text );
+ }
+
+ if ($restart) {
+ if (-f "NO-DAEMON-PLEASE") {
+ $self->log("NO-DAEMON-PLEASE exists, not starting daemon\n");
+ }
+ else {
+ $self->close_log();
+ unlink ("watcher-running");
+ exec "buildd";
+ }
+ }
+
+ unlink ("watcher-running");
+ return 0;
+}
+
+sub archive_logs ($$$$) {
+ my $self = shift;
+ my $dir = shift;
+ my $pattern = shift;
+ my $destpat = shift;
+ my $minage = shift;
+
+ my( $olddir, $file, @todo, $oldest, $newest, $oldt, $newt );
+
+ return if -f "$destpat-stamp" && -M "$destpat-stamp" < $minage-$self->get('Fudge');
+ $self->log("Archiving logs in $dir:\n");
+ system(qw(touch --), "$destpat-stamp");
+
+ $olddir = cwd;
+ chdir( $dir );
+
+ $oldest = LONG_MAX;
+ $newest = 0;
+ foreach $file (glob($pattern)) {
+ if (-M $file >= $minage) {
+ push( @todo, $file );
+ my $modtime = (stat(_))[ST_MTIME];
+ $oldest = $modtime if $oldest > $modtime;
+ $newest = $modtime if $newest < $modtime;
+ }
+ }
+ if (@todo) {
+ $oldt = $self->format_time($oldest);
+ $newt = $self->format_time($newest);
+ $file = $self->get_conf('HOME') . "/$destpat-$oldt-$newt.tar";
+
+ system(qw(tar cf), $file, '--', @todo);
+ system(qw(gzip -9 --), $file);
+
+ if ($dir eq "logs") {
+ local (*F);
+ my $index = $self->get_conf('HOME') . "/$destpat-$oldt-$newt.index";
+ if (open( F, ">$index" )) {
+ print F join( "\n", @todo ), "\n";
+ close( F );
+ }
+ }
+
+ unlink( @todo );
+ $self->log("Archived ", scalar(@todo), " files from $oldt to $newt\n");
+ }
+ else {
+ $self->log("No files to archive\n");
+ }
+
+ chdir( $olddir );
+}
+
+sub make_statistics ($$) {
+ my $self = shift;
+ my $start_time = shift;
+ my $end_time = shift;
+
+ my @svars = qw(taken builds uploads failed dep-wait no-build
+ give-back idle-time build-time remove-time
+ install-download-time);
+ my ($s_taken, $s_builds, $s_uploads, $s_failed, $s_dep_wait,
+ $s_no_build, $s_give_back, $s_idle_time, $s_build_time,
+ $s_remove_time, $s_install_download_time);
+ local( *F, *G, *OUT );
+
+ my $var;
+ foreach $var (@svars) {
+ my $svar = "s_$var";
+ $svar =~ s/-/_/g;
+ eval "\$$svar = 0;";
+ if (-f "stats/$var") {
+ if (!open( F, "<stats/$var" )) {
+ $self->log("can't open stats/$var: $!\n");
+ next;
+ }
+ my $n = 0;
+ while( <F> ) {
+ chomp;
+ $n += $_;
+ }
+ close( F );
+ eval "\$$svar = $n;";
+ unlink( "stats/$var" );
+ }
+ }
+
+ my $total_time = $end_time - $start_time;
+ my $days = $total_time / (24*60*60);
+
+ if (!open( OUT, ">stats/Summary" )) {
+ $self->log("Can't create stats/Summary: $!\n");
+ return;
+ }
+
+ printf OUT "Build daemon statistics from %s to %s (%3.2f days):\n\n",
+ $self->format_time($start_time), $self->format_time($end_time), $days;
+
+ print OUT " #packages % of taken pkgs/day\n";
+ print OUT "-------------------------------------------\n";
+ printf OUT "taken : %5d %7.2f\n",
+ $s_taken, $s_taken/$days;
+ printf OUT "builds : %5d %7.2f%% %7.2f\n",
+ $s_builds, $s_taken ? $s_builds*100/$s_taken : 0, $s_builds/$days;
+ printf OUT "uploaded : %5d %7.2f%% %7.2f\n",
+ $s_uploads, $s_taken ? $s_uploads*100/$s_taken : 0, $s_uploads/$days;
+ printf OUT "failed : %5d %7.2f%% %7.2f\n",
+ $s_failed, $s_taken ? $s_failed*100/$s_taken : 0, $s_failed/$days;
+ printf OUT "dep-wait : %5d %7.2f%% %7.2f\n",
+ $s_dep_wait, $s_taken ? $s_dep_wait*100/$s_taken : 0, $s_dep_wait/$days;
+ printf OUT "give-back: %5d %7.2f%% %7.2f\n",
+ $s_give_back, $s_taken ? $s_give_back*100/$s_taken : 0, $s_give_back/$days;
+ printf OUT "no-build : %5d %7.2f%% %7.2f\n",
+ $s_no_build, $s_taken ? $s_no_build*100/$s_taken : 0, $s_no_build/$days;
+ print OUT "\n";
+
+ print OUT " time % of total\n";
+ print OUT "----------------------------------\n";
+ printf OUT "building: %s %7.2f%%\n",
+ $self->print_time($s_build_time), $s_build_time*100/$total_time;
+ printf OUT "install : %s %7.2f%%\n",
+ $self->print_time($s_install_download_time), $s_install_download_time*100/$total_time;
+ printf OUT "removing: %s %7.2f%%\n",
+ $self->print_time($s_remove_time), $s_remove_time*100/$total_time;
+ printf OUT "idle : %s %7.2f%%\n",
+ $self->print_time($s_idle_time), $s_idle_time*100/$total_time;
+ printf OUT "total : %s\n", $self->print_time($total_time);
+ print OUT "\n";
+
+ my $proc = $s_uploads+$s_failed+$s_dep_wait+$s_no_build+$s_give_back;
+ printf OUT "processed package (upl+fail+dep+nob): %7d\n", $proc;
+ printf OUT "slipped (proc-taken) : %7d\n", $proc-$s_taken;
+ printf OUT "builds/taken package : %7.2f\n",
+ $s_builds/$s_taken
+ if $s_taken;
+ printf OUT "avg. time/taken package : %s\n",
+ $self->print_time($s_build_time/$s_taken)
+ if $s_taken;
+ printf OUT "avg. time/processed package : %s\n",
+ $self->print_time($s_build_time/$proc)
+ if $proc;
+ printf OUT "avg. time/build : %s\n",
+ $self->print_time($s_build_time/$s_builds)
+ if $s_builds;
+ print OUT "\n";
+
+ my $date = $self->format_date(time);
+ $self->print_graph( $s_builds/$days, $date, "builds-per-day" );
+ $self->print_graph( $s_uploads/$days, $date, "uploads-per-day" );
+ $self->print_graph( $s_failed/$days, $date, "failed-per-day" );
+ $self->print_graph( $s_dep_wait/$days, $date, "dep-wait-per-day" );
+ $self->print_graph( $s_give_back/$days, $date, "give-back-per-day" );
+ $self->print_graph( $s_build_time/$s_builds, $date, "time-per-build" )
+ if $s_builds;
+ $self->print_graph( $s_build_time/$total_time, $date, "build-time-percent" );
+ $self->print_graph( $s_idle_time/$total_time, $date, "idle-time-percent" );
+
+ my $g;
+ my $graph_maxval = $self->get('Graph Maxval');
+
+ foreach $g (keys %{$graph_maxval}) {
+ next if !open( G, "<stats/graphs/$g" );
+
+ print OUT "$g (max. $graph_maxval->{$g}):\n\n";
+ while( <G> ) {
+ print OUT $_;
+ }
+ close( G );
+ print OUT "\n";
+ }
+
+ close( OUT );
+}
+
+sub print_time ($) {
+ my $self = shift;
+ my $t = shift;
+
+ my $str = sprintf "%02d:%02d:%02d", int($t/3600), int(($t%3600)/60),
+ int($t%60);
+ $str = " "x(10-length($str)) . $str;
+
+ return $str;
+}
+
+sub print_graph ($$$) {
+ my $self = shift;
+ my $val = shift;
+ my $date = shift;
+ my $graph = shift;
+
+ my $width = 72;
+ local( *G );
+
+ my $graph_maxval = $self->get('Graph Maxval');
+ if (!exists $graph_maxval->{$graph}) {
+ $self->log("Unknown graph $graph\n");
+ return;
+ }
+ if (!open( G, ">>stats/graphs/$graph" )) {
+ $self->log("Can't create stats/graphs/$graph: $!\n");
+ return;
+ }
+ $val = int( $val*$width/$graph_maxval->{$graph} + 0.5 );
+ my $str = $val > $width ? "*"x($width-1)."+" : "*"x$val;
+ $date = substr( $date, 0, 6 );
+ $date .= " " x (6-length($date));
+ print G "$date $str\n";
+ close( G );
+}
+
+sub format_time ($) {
+ my $self = shift;
+ my $t = shift;
+
+ my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($t);
+
+ return sprintf "%04d%02d%02d-%02d%02d",
+ $year+1900, $mon+1, $mday, $hour, $min;
+}
+
+sub format_date ($) {
+ my $self = shift;
+ my $t = shift;
+
+ my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday) = localtime($t);
+
+ return sprintf "%02d%02d%02d", $year%100, $mon+1, $mday;
+}
+
+1;
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..f0da554
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,31 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2007 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
+
+SUBDIRS = Buildd Sbuild
+
+perlmod_DATA = \
+ Buildd.pm \
+ Sbuild.pm
+
+EXTRA_DIST = \
+ $(perlmod_DATA)
diff --git a/lib/Sbuild.pm b/lib/Sbuild.pm
new file mode 100644
index 0000000..d4ddf52
--- /dev/null
+++ b/lib/Sbuild.pm
@@ -0,0 +1,454 @@
+#
+# Sbuild.pm: library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-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/>.
+#
+#######################################################################
+
+package Sbuild;
+
+use Sbuild::Sysconfig;
+
+use strict;
+use warnings;
+use POSIX;
+use FileHandle;
+use Filesys::Df qw();
+use Time::Local;
+use IO::Zlib;
+use MIME::Base64;
+use Dpkg::Control;
+use Dpkg::Checksums;
+use POSIX qw(locale_h);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw($debug_level $devnull binNMU_version parse_date isin
+ copy dump_file check_packages help_text version_text
+ usage_error send_mail debug debug2 df
+ check_group_membership dsc_files dsc_pkgver shellescape strftime_c);
+}
+
+our $devnull;
+our $debug_level = 0;
+
+BEGIN {
+ # A file representing /dev/null
+ if (!open($devnull, '+<', '/dev/null')) {
+ die "Cannot open /dev/null: $!\n";;
+ }
+}
+
+sub binNMU_version ($$$);
+sub parse_date ($);
+sub isin ($@);
+sub copy ($);
+sub dump_file ($);
+sub check_packages ($$);
+sub help_text ($$);
+sub version_text ($);
+sub usage_error ($$);
+sub debug (@);
+sub debug2 (@);
+sub check_group_membership();
+sub dsc_files ($);
+sub shellescape ($);
+sub strftime_c ($@);
+
+sub binNMU_version ($$$) {
+ my $v = shift;
+ my $binNMUver = shift;
+ my $append_to_version = shift;
+
+ my $ver = $v;
+ if (defined($append_to_version) && $append_to_version) {
+ $ver .= $append_to_version;
+ }
+ if (defined($binNMUver) && $binNMUver) {
+ $ver .= "+b$binNMUver";
+ }
+ return $ver;
+}
+
+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 );
+
+sub parse_date ($) {
+ my $text = shift;
+
+ return 0 if !$text;
+ 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 timegm($sec, $min, $hour, $day, $mon, $year);
+}
+
+sub isin ($@) {
+ my $val = shift;
+ return grep( $_ eq $val, @_ );
+}
+
+sub copy ($) {
+ my $r = shift;
+ my $new;
+
+ if (ref($r) eq "HASH") {
+ $new = { };
+ foreach (keys %$r) {
+ $new->{$_} = copy($r->{$_});
+ }
+ }
+ elsif (ref($r) eq "ARRAY") {
+ my $i;
+ $new = [ ];
+ for( $i = 0; $i < @$r; ++$i ) {
+ $new->[$i] = copy($r->[$i]);
+ }
+ }
+ elsif (!ref($r)) {
+ $new = $r;
+ }
+ else {
+ die "unknown ref type in copy\n";
+ }
+
+ return $new;
+}
+
+sub dump_file ($) {
+ my $file = shift;
+
+ if (-r "$file" &&
+ open(SOURCES, "<$file")) {
+
+ print " +------------------------------------------------------------------------\n";
+ while (<SOURCES>) {
+ chomp;
+ print " |$_\n";
+ }
+ print " +------------------------------------------------------------------------\n";
+ close(SOURCES) or print "Failed to close $file\n";
+ } else {
+ print "W: Failed to open $file\n";
+ }
+}
+
+# set and list saved package list (used by sbuild-checkpackages)
+sub check_packages ($$) {
+ my $session = shift;
+ my $mode = shift;
+
+ my $package_checklist = $session->get_conf('PACKAGE_CHECKLIST');
+ my $chroot_dir = $session->get('Location');
+
+ my (@status, @ref, @install, @remove);
+
+ my $pipe = $session->pipe_command({
+ COMMAND => ['dpkg-query', '--show', '--showformat=${Package} ${db:Status-Status}\n']
+ });
+ while (<$pipe>) {
+ chomp;
+ my @token = split / /, $_;
+ my $pkgname = shift @token;
+ my $state = shift @token;
+ next if $state ne "installed";
+ push @status, $pkgname;
+ }
+ if (! close $pipe) {
+ print STDERR "Error reading dpkg status file in chroot: $!\n";
+ return 1;
+ }
+ @status = sort @status;
+ if (!@status) {
+ print STDERR "dpkg status file is empty\n";
+ return 1;
+ }
+
+ if ($mode eq "set") {
+ if (! open WREF, "> $chroot_dir/$package_checklist") {
+ print STDERR "Can't write reference status file $chroot_dir/$package_checklist: $!\n";
+ return 1;
+ }
+ foreach (@status) {
+ print WREF "$_\n";
+ }
+ if (! close WREF) {
+ print STDERR "Error writing reference status file: $!\n";
+ return 1;
+ }
+ } else { # "list"
+ if (! open REF, "< $chroot_dir/$package_checklist") {
+ print STDERR "Can't read reference status file $chroot_dir/$package_checklist: $!\n";
+ return 1;
+ }
+ while (<REF>) {
+ chomp;
+ push @ref, $_;
+ }
+ if (! close REF) {
+ print STDERR "Error reading reference status file: $!\n";
+ return 1;
+ }
+
+ @ref = sort @ref;
+ if (!@ref) {
+ print STDERR "Reference status file is empty\n";
+ return 1;
+ }
+
+ print "DELETE ADD\n";
+ print "--------------------------------------\n";
+ my $i = 0;
+ my $j = 0;
+
+ while ($i < scalar @status && $j < scalar @ref) {
+
+ my $c = $status[$i] cmp $ref[$j];
+ if ($c < 0) {
+ # In status, not reference; remove.
+ print "$status[$i]\n";
+ $i++;
+ } elsif ($c > 0) {
+ # In reference, not status; install.
+ print " $ref[$j]\n";
+ $j++;
+ } else {
+ # Identical; skip.
+ $i++; $j++;
+ }
+ }
+
+ # Print any remaining elements
+ while ($i < scalar @status) {
+ print "$status[$i]\n";
+ $i++;
+ }
+ while ($j < scalar @ref) {
+ print " $ref[$j]\n";
+ $j++;
+ }
+ }
+}
+
+sub help_text ($$) {
+ my $section = shift;
+ my $page = shift;
+
+ system('man', '--', $section, $page);
+ exit 0;
+}
+
+sub version_text ($) {
+ my $program = shift;
+
+ print <<"EOF";
+$program (Debian sbuild) $Sbuild::Sysconfig::version ($Sbuild::Sysconfig::release_date)
+
+Written by Roman Hodek, James Troup, Ben Collins, Ryan Murray, Rick
+Younie, Francesco Paolo Lovergine, Michael Banck, Roger Leigh and
+Andres Mejia.
+
+Copyright © 1998-2000 Roman Hodek <roman\@hodek.net>
+ © 1998-1999 James Troup <troup\@debian.org>
+ © 2003-2006 Ryan Murray <rmurray\@debian.org>
+ © 2001-2003 Rick Younie <younie\@debian.org>
+ © 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+ © 2005 Michael Banck <mbanck\@debian.org>
+ © 2005-2010 Roger Leigh <rleigh\@debian.org>
+ © 2009-2010 Andres Mejia <mcitadel\@gmail.com>
+
+This is free software; see the source for copying conditions. There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+EOF
+ exit 0;
+}
+
+# Print an error message about incorrect command-line options
+sub usage_error ($$) {
+ my $program = shift;
+ my $message = shift;
+
+ print STDERR "E: $message\n";
+ print STDERR "I: Run '$program --help' to list usage example and all available options\n";
+ exit 1;
+}
+
+sub send_mail ($$$$) {
+ my $conf = shift;
+ my $to = shift;
+ my $subject = shift;
+ my $file = shift;
+ local( *MAIL, *F );
+
+ if (!open( F, "<$file" )) {
+ warn "Cannot open $file for mailing: $!\n";
+ return 0;
+ }
+ local $SIG{'PIPE'} = 'IGNORE';
+
+ if (!open( MAIL, "|" . $conf->get('MAILPROG') . " -oem $to" )) {
+ warn "Could not open pipe to " . $conf->get('MAILPROG') . ": $!\n";
+ close( F );
+ return 0;
+ }
+
+ print MAIL "From: " . $conf->get('MAILFROM') . "\n";
+ print MAIL "To: $to\n";
+ print MAIL "Subject: $subject\n";
+ print MAIL "Content-Type: text/plain; charset=UTF-8\n";
+ print MAIL "Content-Transfer-Encoding: 8bit\n";
+ print MAIL "\n";
+ while( <F> ) {
+ print MAIL "." if $_ eq ".\n";
+ print MAIL $_;
+ }
+
+ close( F );
+ if (!close( MAIL )) {
+ warn $conf->get('MAILPROG') . " failed (exit status $?)\n";
+ return 0;
+ }
+ return 1;
+}
+
+# Note: split to stderr
+sub debug (@) {
+
+ # TODO: Add debug level checking.
+ if ($debug_level) {
+ print STDERR "D: ", @_;
+ }
+}
+
+sub debug2 (@) {
+
+ # TODO: Add debug level checking.
+ if ($debug_level && $debug_level >= 2) {
+ print STDERR "D2: ", @_;
+ }
+}
+
+sub df {
+ my $dir = shift;
+
+ my $stat = Filesys::Df::df($dir);
+
+ return $stat->{bfree} if (defined($stat));
+
+# This only happens if $dir was not a valid file or directory.
+ return 0;
+}
+
+sub check_group_membership () {
+ # Skip for root
+ return if ($< == 0);
+
+ my $user = getpwuid($<);
+ my ($name,$passwd,$gid,$members) = getgrnam("sbuild");
+
+ if (!$gid) {
+ die "Group sbuild does not exist";
+ }
+
+ my $in_group = 0;
+ my @groups = getgroups();
+ push @groups, getgid();
+ foreach (@groups) {
+ ($name, $passwd, $gid, $members) = getgrgid($_);
+ $in_group = 1 if defined($name) && $name eq 'sbuild';
+ }
+
+ if (!$in_group) {
+ print STDERR "User $user is not currently an effective member of group sbuild. Please run:\n";
+ print STDERR " sudo sbuild-adduser $user\n";
+ print STDERR "And then either log out and log in again or use `newgrp sbuild` to gain sbuild group privileges\n";
+ exit(1);
+ }
+
+ return;
+}
+
+sub dsc_files ($) {
+ my $dsc = shift;
+
+ debug("Parsing $dsc\n");
+ my $pdsc = Dpkg::Control->new(type => CTRL_PKG_SRC);
+ $pdsc->set_options(allow_pgp => 1);
+ if (!$pdsc->load($dsc)) {
+ print STDERR "Could not parse $dsc\n";
+ return undef;
+ }
+
+ my $csums = Dpkg::Checksums->new();
+ $csums->add_from_control($pdsc, use_files_for_md5 => 1);
+ return $csums->get_files();
+}
+
+sub dsc_pkgver ($) {
+ my $dsc = shift;
+
+ debug("Parsing $dsc\n");
+ my $pdsc = Dpkg::Control->new(type => CTRL_PKG_SRC);
+ $pdsc->set_options(allow_pgp => 1);
+ if (!$pdsc->load($dsc)) {
+ print STDERR "Could not parse $dsc\n";
+ return undef;
+ }
+
+ return ($pdsc->{'Source'}, $pdsc->{'Version'});
+}
+
+# avoid dependency on String::ShellQuote by implementing the mechanism
+# from python's shlex.quote function
+sub shellescape ($) {
+ my $string = shift;
+ if (length $string == 0) {
+ return "''";
+ }
+ # search for occurrences of characters that are not safe
+ # the 'a' regex modifier makes sure that \w only matches ASCII
+ if ($string !~ m/[^\w@\%+=:,.\/-]/a) {
+ return $string;
+ }
+ # wrap the string in single quotes and handle existing single quotes by
+ # putting them outside of the single-quoted string
+ $string =~ s/'/'"'"'/g;
+ return "'$string'";
+};
+
+# this function uses strftime to format a timestamp as a string but makes sure
+# to use the C locale to do so instead of the system locale
+sub strftime_c ($@) {
+ my $format = shift;
+ my @time = @_;
+
+ my $old_locale = setlocale(LC_TIME);
+ setlocale(LC_TIME, "C.UTF-8");
+ my $ret = strftime $format, @time;
+ setlocale(LC_TIME, $old_locale);
+
+ return $ret;
+}
+
+1;
diff --git a/lib/Sbuild/.gitignore b/lib/Sbuild/.gitignore
new file mode 100644
index 0000000..4394f55
--- /dev/null
+++ b/lib/Sbuild/.gitignore
@@ -0,0 +1 @@
+Sysconfig.pm
diff --git a/lib/Sbuild/AptResolver.pm b/lib/Sbuild/AptResolver.pm
new file mode 100644
index 0000000..c5293a8
--- /dev/null
+++ b/lib/Sbuild/AptResolver.pm
@@ -0,0 +1,219 @@
+# ResolverBase.pm: build library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::AptResolver;
+
+use strict;
+use warnings;
+
+use Sbuild qw(debug copy);
+use Sbuild::Base;
+use Sbuild::ResolverBase;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ResolverBase);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $self = $class->SUPER::new($conf, $session, $host);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub install_deps {
+ my $self = shift;
+ my $name = shift;
+ my @pkgs = @_;
+
+ my $status = 0;
+ my $session = $self->get('Session');
+ my $dummy_pkg_name = $self->get_sbuild_dummy_pkg_name($name);
+
+ # Call functions to setup an archive to install dummy package.
+ $self->log_subsubsection("Setup apt archive");
+
+ if (!$self->setup_apt_archive($dummy_pkg_name, @pkgs)) {
+ $self->log_error("Setting up apt archive failed\n");
+ return 0;
+ }
+
+ if (!$self->update_archive()) {
+ $self->log_error("Updating apt archive failed\n");
+ return 0;
+ }
+
+ $self->log_subsubsection("Install $name build dependencies (apt-based resolver)");
+
+ # Install the dummy package
+ my (@instd, @rmvd);
+ $self->log("Installing build dependencies\n");
+ my @apt_args = ("-yf", \@instd, \@rmvd, 'install', $dummy_pkg_name);
+
+ if (!$self->run_apt(@apt_args)) {
+ $self->log_error("Package installation failed\n");
+ if (defined ($self->get('Session')->get('Session Purged')) &&
+ $self->get('Session')->get('Session Purged') == 1) {
+ $self->log("Not removing build depends: cloned chroot in use\n");
+ } else {
+ $self->set_installed(@instd);
+ $self->set_removed(@rmvd);
+ goto package_cleanup;
+ }
+ return 0;
+ }
+ $self->set_installed(@instd);
+ $self->set_removed(@rmvd);
+ $status = 1;
+
+ package_cleanup:
+ if ($status == 0) {
+ if (defined ($session->get('Session Purged')) &&
+ $session->get('Session Purged') == 1) {
+ $self->log("Not removing installed packages: cloned chroot in use\n");
+ } else {
+ $self->uninstall_deps();
+ }
+ }
+
+ return $status;
+}
+
+sub purge_extra_packages {
+ my $self = shift;
+ my $name = shift;
+
+ my $dummy_pkg_name = $self->get_sbuild_dummy_pkg_name($name);
+
+ my $session = $self->get('Session');
+
+ # we partition the packages into those we want to mark as manual (all of
+ # Essential:yes plus sbuild dummy packages) and those we want to mark as
+ # auto
+ #
+ # We don't use the '*' glob of apt-mark because then we'd have all packages
+ # apt knows about in the build log.
+ my $pipe = $session->pipe_command({
+ COMMAND => [ 'dpkg-query', '--showformat', '${Essential} ${Package}\\n', '--show' ],
+ USER => $self->get_conf('BUILD_USER')
+ });
+ if (!$pipe) {
+ $self->log_error("unable to execute dpkg-query\n");
+ return 0;
+ }
+ my @essential;
+ my @nonessential;
+ while (my $line = <$pipe>) {
+ chomp $line;
+ if ($line !~ /^(yes|no) ([a-zA-Z0-9][a-zA-Z0-9+.-]*)$/) {
+ $self->log_error("dpkg-query output has unexpected format\n");
+ return 0;
+ }
+ # we only want to keep packages that are Essential:yes and the dummy
+ # packages created by sbuild. Apt takes care to also keep their
+ # transitive dependencies.
+ if ($1 eq "yes" || $2 eq $dummy_pkg_name || $2 eq $self->get_sbuild_dummy_pkg_name('core')) {
+ push @essential, $2;
+ } else {
+ push @nonessential, $2;
+ }
+ }
+ close $pipe;
+ if (scalar @essential == 0) {
+ $self->log_error("no essential packages found \n");
+ return 0;
+ }
+ if (scalar @nonessential == 0) {
+ $self->log_error("no non-essential packages found \n");
+ return 0;
+ }
+
+ if (!$session->run_command({ COMMAND => [ 'apt-mark', 'auto', @nonessential ], USER => 'root' })) {
+ $self->log_error("unable to run apt-mark\n");
+ return 0;
+ }
+
+ # We must mark all Essential:yes packages as manual because later on we
+ # must run apt with --allow-remove-essential so that apt agrees to remove
+ # itself and at that point we don't want to remove the Essential:yes
+ # packages.
+ if (!$session->run_command({ COMMAND => [ 'apt-mark', 'manual', @essential ], USER => 'root' })) {
+ $self->log_error("unable to run apt-mark\n");
+ return 0;
+ }
+ # apt currently suffers from bug #837066. It will never autoremove
+ # priority:required packages, thus we use a temporary (famous last words)
+ # hack here and feed apt a modified /var/lib/dpkg/status file with all
+ # packages marked as Priority:extra. This is a hack because
+ # /var/lib/dpkg/status should not be read by others than dpkg (we for
+ # example do not take into account the journal that way).
+ my $read_fh = $session->pipe_command({
+ COMMAND => [ 'sed', 's/^Priority: .*$/Priority: extra/', '/var/lib/dpkg/status' ],
+ USER => $self->get_conf('BUILD_USER')
+ });
+ if (!$read_fh) {
+ $session->log_error("cannot run sed\n");
+ return 0;
+ }
+ my $tmpfilename = $session->mktemp({ USER => $self->get_conf('BUILD_USER') });
+ if (!$tmpfilename) {
+ $session->log_error("cannot mktemp\n");
+ return 0;
+ }
+ my $write_fh = $session->get_write_file_handle($tmpfilename);
+ if (!$write_fh) {
+ $session->log_error("cannot open $tmpfilename for writing\n");
+ return 0;
+ }
+ while (read($read_fh, my $buffer, 1024)) {
+ print $write_fh $buffer;
+ }
+ close $read_fh;
+ close $write_fh;
+
+ my (@instd, @rmvd);
+ # apt considers itself as Essential:yes, that's why we need
+ # --allow-remove-essential to remove it and that's why we must explicitly
+ # specify to remove it.
+ #
+ # The /dev/null prevents apt from overriding the Priorities that we set in
+ # our modified dpkg status file by the ones it finds in the package list
+ # files
+ $self->run_apt("-yf", \@instd, \@rmvd, 'autoremove',
+ 'apt',
+ '-o', 'Dir::State::Lists=/dev/null',
+ '-o', "Dir::State::Status=$tmpfilename",
+ '--allow-remove-essential');
+
+ $session->unlink($tmpfilename);
+}
+
+1;
diff --git a/lib/Sbuild/AptitudeResolver.pm b/lib/Sbuild/AptitudeResolver.pm
new file mode 100644
index 0000000..213c678
--- /dev/null
+++ b/lib/Sbuild/AptitudeResolver.pm
@@ -0,0 +1,187 @@
+# ResolverBase.pm: build library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::AptitudeResolver;
+
+use strict;
+use warnings;
+use File::Temp qw(tempdir);
+
+use Sbuild qw(debug);
+use Sbuild::Base;
+use Sbuild::ResolverBase;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ResolverBase);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $self = $class->SUPER::new($conf, $session, $host);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub install_deps {
+ my $self = shift;
+ my $name = shift;
+ my @pkgs = @_;
+
+
+ my $status = 0;
+ my $session = $self->get('Session');
+ my $dummy_pkg_name = $self->get_sbuild_dummy_pkg_name($name);
+ my $dummy_pkg_name_for_install = $dummy_pkg_name;
+ # Debian without multiarch (squeeze and older) does not support
+ # architecture qualifiers for dependencies, so we only add them if the
+ # chroot supports multiarch
+ if ($self->get('Multiarch Support')) {
+ $dummy_pkg_name_for_install .= ':' . $self->get('Host Arch');
+ }
+
+ # Call functions to setup an archive to install dummy package.
+ $self->log_subsubsection("Setup apt archive");
+
+ if (!$self->setup_apt_archive($dummy_pkg_name, @pkgs)) {
+ $self->log_error("Setting up apt archive failed\n");
+ return 0;
+ }
+
+ if (!$self->update_archive()) {
+ $self->log_error("Updating apt archive failed\n");
+ return 0;
+ }
+
+ $self->log_subsection("Install $name build dependencies (aptitude-based resolver)");
+
+ #install aptitude first:
+ my (@aptitude_installed_packages, @aptitude_removed_packages);
+ if (!$self->run_apt('-y', \@aptitude_installed_packages, \@aptitude_removed_packages, 'install', 'aptitude')) {
+ $self->log_warning('Could not install aptitude!');
+ goto cleanup;
+ }
+ $self->set_installed(@aptitude_installed_packages);
+ $self->set_removed(@aptitude_removed_packages);
+
+
+ my $ignore_trust_violations =
+ $self->get_conf('APT_ALLOW_UNAUTHENTICATED') ? 'true' : 'false';
+
+ my @aptitude_install_command = (
+ $self->get_conf('APTITUDE'),
+ '-y',
+ '--without-recommends',
+ '-o', 'Dpkg::Options::=--force-confold',
+ '-o', "Aptitude::CmdLine::Ignore-Trust-Violations=$ignore_trust_violations",
+ '-o', 'Aptitude::ProblemResolver::StepScore=100',
+ '-o', "Aptitude::ProblemResolver::SolutionCost=safety, priority, non-default-versions",
+ '-o', "Aptitude::ProblemResolver::Hints::KeepDummy=reject $dummy_pkg_name :UNINST",
+ '-o', 'Aptitude::ProblemResolver::Keep-All-Level=55000',
+ '-o', 'Aptitude::ProblemResolver::Remove-Essential-Level=maximum',
+ 'install',
+ $dummy_pkg_name_for_install
+ );
+
+ $self->log(join(" ", @aptitude_install_command), "\n");
+
+ my $pipe = $self->pipe_aptitude_command(
+ { COMMAND => \@aptitude_install_command,
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ PIPE => 'in',
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+
+ if (!$pipe) {
+ $self->log_warning('Cannot open pipe from aptitude: ' . $! . "\n");
+ goto package_cleanup;
+ }
+
+ my $aptitude_output = "";
+ while(<$pipe>) {
+ $aptitude_output .= $_;
+ $self->log($_);
+ }
+ close($pipe);
+ my $aptitude_exit_code = $?;
+
+ if ($aptitude_output =~ /^E:/m) {
+ $self->log('Satisfying build-deps with aptitude failed.' . "\n");
+ goto package_cleanup;
+ }
+
+ my ($installed_pkgs, $removed_pkgs) = ("", "");
+ while ($aptitude_output =~ /The following NEW packages will be installed:\n((^[ ].*\n)*)/gmi) {
+ ($installed_pkgs = $1) =~ s/^[ ]*((.|\n)*)\s*$/$1/m;
+ $installed_pkgs =~ s/\*//g;
+ $installed_pkgs =~ s/\{.\}//g;
+ }
+ while ($aptitude_output =~ /The following packages will be REMOVED:\n((^[ ].*\n)*)/gmi) {
+ ($removed_pkgs = $1) =~ s/^[ ]*((.|\n)*)\s*$/$1/m;
+ $removed_pkgs =~ s/\*//g;
+ $removed_pkgs =~ s/\{.\}//g; #remove {u}, {a} in output...
+ }
+
+ my @installed_packages = split( /\s+/, $installed_pkgs);
+
+ $self->set_installed(keys %{$self->get('Changes')->{'installed'}}, @installed_packages);
+ $self->set_removed(keys %{$self->get('Changes')->{'removed'}}, split( /\s+/, $removed_pkgs));
+
+ if ($aptitude_exit_code != 0) {
+ goto package_cleanup;
+ }
+
+ #Seems it all went fine.
+
+ $status = 1;
+
+ package_cleanup:
+ if ($status == 0) {
+ if (defined ($session->get('Session Purged')) &&
+ $session->get('Session Purged') == 1) {
+ $self->log("Not removing installed packages: cloned chroot in use\n");
+ } else {
+ $self->uninstall_deps();
+ }
+ }
+
+ cleanup:
+ return $status;
+}
+
+sub purge_extra_packages {
+ my $self = shift;
+ my $name = shift;
+
+ $self->log_error('Aptitude resolver doesn\'t implement purging of extra packages yet.\n');
+}
+
+1;
diff --git a/lib/Sbuild/AspcudResolver.pm b/lib/Sbuild/AspcudResolver.pm
new file mode 100644
index 0000000..b86db29
--- /dev/null
+++ b/lib/Sbuild/AspcudResolver.pm
@@ -0,0 +1,165 @@
+# ResolverBase.pm: build library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::AspcudResolver;
+
+use strict;
+use warnings;
+
+use Sbuild::Base;
+use Sbuild::ResolverBase;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ResolverBase);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $self = $class->SUPER::new($conf, $session, $host);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub install_deps {
+ my $self = shift;
+ my $name = shift;
+ my @pkgs = @_;
+
+ my $status = 0;
+ my $session = $self->get('Session');
+ my $dummy_pkg_name = $self->get_sbuild_dummy_pkg_name($name);
+
+ # Call functions to setup an archive to install dummy package.
+ $self->log_subsubsection("Setup apt archive");
+
+ if (!$self->setup_apt_archive($dummy_pkg_name, @pkgs)) {
+ $self->log_error("Setting up apt archive failed\n");
+ return 0;
+ }
+
+ if (!$self->update_archive()) {
+ $self->log_error("Updating apt archive failed\n");
+ return 0;
+ }
+
+ $self->log_subsection("Install $name build dependencies (aspcud-based resolver)");
+ #install aspcud first:
+ my (@aspcud_installed_packages, @aspcud_removed_packages);
+ if (!$self->run_apt('-y', \@aspcud_installed_packages, \@aspcud_removed_packages, 'install', 'apt-cudf', 'aspcud')) {
+ $self->log_warning('Could not install aspcud!');
+ goto cleanup;
+ }
+ $self->set_installed(@aspcud_installed_packages);
+ $self->set_removed(@aspcud_removed_packages);
+
+ # Install the dummy package
+ my (@instd, @rmvd);
+ $self->log("Installing build dependencies\n");
+ my @apt_args = ("-yf", \@instd, \@rmvd);
+ push @apt_args, 'install', $dummy_pkg_name;
+
+ push @apt_args, '--solver', 'aspcud',
+ '-o', 'APT::Solver::Strict-Pinning=false',
+ '-o', 'APT::Solver::aspcud::Preferences='.$self->get_conf('ASPCUD_CRITERIA');
+
+ if (!$self->run_apt(@apt_args)) {
+ $self->log("Package installation failed\n");
+ if (defined ($self->get('Session')->get('Session Purged')) &&
+ $self->get('Session')->get('Session Purged') == 1) {
+ $self->log("Not removing build depends: cloned chroot in use\n");
+ } else {
+ $self->set_installed(@instd);
+ $self->set_removed(@rmvd);
+ goto package_cleanup;
+ }
+ return 0;
+ }
+ $self->set_installed(@instd);
+ $self->set_removed(@rmvd);
+ $status = 1;
+
+ package_cleanup:
+ if ($status == 0) {
+ if (defined ($session->get('Session Purged')) &&
+ $session->get('Session Purged') == 1) {
+ $self->log("Not removing installed packages: cloned chroot in use\n");
+ } else {
+ $self->uninstall_deps();
+ }
+ }
+
+ cleanup:
+ return $status;
+}
+
+sub purge_extra_packages {
+ my $self = shift;
+ my $name = shift;
+
+ my $dummy_pkg_name = $self->get_sbuild_dummy_pkg_name($name);
+
+ my $session = $self->get('Session');
+
+ # we retrieve the list of installed Essential:yes packages because these
+ # must not be removed
+ my $pipe = $session->pipe_command({
+ COMMAND => [ 'dpkg-query', '--showformat', '${Essential} ${Package}\\n', '--show' ],
+ USER => $self->get_conf('BUILD_USER')
+ });
+ if (!$pipe) {
+ $self->log_error("unable to execute dpkg-query\n");
+ return 0;
+ }
+ my @essential;
+ while (my $line = <$pipe>) {
+ chomp $line;
+ if ($line !~ /^yes ([a-zA-Z0-9][a-zA-Z0-9+.-]*)$/) {
+ next;
+ }
+ push @essential, "$1+";
+ }
+ close $pipe;
+ if (scalar @essential == 0) {
+ $self->log_error("no essential packages found \n");
+ return 0;
+ }
+ # the /dev/null prevents acpcud from even looking at external repositories, so all it can do is remove stuff
+ # it is also much faster that way
+ my (@instd, @rmvd);
+ $self->run_apt("-yf", \@instd, \@rmvd, 'autoremove',
+ @essential, $self->get_sbuild_dummy_pkg_name('core') . '+', "$dummy_pkg_name+",
+ '--solver', 'aspcud',
+ '-o', 'APT::Solver::aspcud::Preferences=+removed',
+ '-o', 'Dir::State::Lists=/dev/null',
+ '--allow-remove-essential');
+}
+
+1;
diff --git a/lib/Sbuild/Base.pm b/lib/Sbuild/Base.pm
new file mode 100644
index 0000000..48d559d
--- /dev/null
+++ b/lib/Sbuild/Base.pm
@@ -0,0 +1,165 @@
+#
+# Base.pm: base class containing common class infrastructure
+# 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/>.
+#
+#######################################################################
+
+package Sbuild::Base;
+
+use strict;
+use warnings;
+
+use Sbuild qw(debug);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = {};
+ bless($self, $class);
+
+ $self->set('Config', $conf);
+
+ return $self;
+}
+
+sub get {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->{$key};
+}
+
+sub set {
+ my $self = shift;
+ my $key = shift;
+ my $value = shift;
+
+ if (defined($value)) {
+ debug("Setting $key=$value\n");
+ } else {
+ debug("Setting $key=undef\n");
+ }
+
+ return $self->{$key} = $value;
+}
+
+sub get_conf {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->get('Config')->get($key);
+}
+
+sub set_conf {
+ my $self = shift;
+ my $key = shift;
+ my $value = shift;
+
+ return $self->get('Config')->set($key,$value);
+}
+
+sub log {
+ my $self = shift;
+
+ my $logfile = $self->get('Log Stream');
+ if (defined($logfile)) {
+ print $logfile @_;
+ } else {
+ debug("E: Attempt to log to nonexistent log stream\n")
+ if (!defined($self->get('Log Stream Error')) ||
+ !$self->get('Log Stream Error'));
+ print STDERR @_;
+ $self->set('Log Stream Error', 1)
+ }
+}
+
+sub log_info {
+ my $self = shift;
+
+ $self->log("I: ", @_);
+}
+
+sub log_warning {
+ my $self = shift;
+
+ $self->log("W: ", @_);
+}
+
+sub log_error {
+ my $self = shift;
+
+ $self->log("E: ", @_);
+}
+
+sub log_section {
+ my $self = shift;
+ my $section = shift;
+
+ $self->log("\n");
+ if (length($section) <= 76 ) {
+ $self->log('+', '=' x 78, '+', "\n");
+ $self->log('|', " $section ", ' ' x (76 - length($section)), '|', "\n");
+ $self->log('+', '=' x 78, '+', "\n\n");
+ } else {
+ $self->log('+', '=' x (length($section) + 2), '+', "\n");
+ $self->log('|', " $section ", '|', "\n");
+ $self->log('+', '=' x (length($section) + 2), '+', "\n\n");
+ }
+}
+
+sub log_subsection {
+ my $self = shift;
+ my $section = shift;
+
+ $self->log("\n");
+ if (length($section) <= 76 ) {
+ $self->log('+', '-' x 78, '+', "\n");
+ $self->log('|', " $section ", ' ' x (76 - length($section)), '|', "\n");
+ $self->log('+', '-' x 78, '+', "\n\n");
+ } else {
+ $self->log('+', '-' x (length($section) + 2), '+', "\n");
+ $self->log('|', " $section ", '|', "\n");
+ $self->log('+', '-' x (length($section) + 2), '+', "\n\n");
+ }
+}
+
+sub log_subsubsection {
+ my $self = shift;
+ my $section = shift;
+
+ $self->log("\n");
+ $self->log("$section\n");
+ $self->log('-' x (length($section)), "\n\n");
+}
+
+sub log_sep {
+ my $self = shift;
+
+ $self->log('-' x 80, "\n");
+}
+
+1;
diff --git a/lib/Sbuild/Build.pm b/lib/Sbuild/Build.pm
new file mode 100644
index 0000000..cca1c89
--- /dev/null
+++ b/lib/Sbuild/Build.pm
@@ -0,0 +1,3686 @@
+#
+# Build.pm: build library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2010 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::Build;
+
+use strict;
+use warnings;
+
+use English;
+use POSIX;
+use Errno qw(:POSIX);
+use Fcntl;
+use File::Temp qw(mkdtemp);
+use File::Basename qw(basename dirname);
+use File::Path qw(make_path);
+use FileHandle;
+use File::Copy qw(); # copy is already exported from Sbuild, so don't export
+ # anything.
+use Dpkg::Arch;
+use Dpkg::Control;
+use Dpkg::Index;
+use Dpkg::Version;
+use Dpkg::Deps qw(deps_concat deps_parse);
+use Dpkg::Changelog::Debian;
+use Scalar::Util 'refaddr';
+
+use MIME::Lite;
+use Term::ANSIColor;
+
+use Sbuild qw($devnull binNMU_version copy isin debug send_mail
+ dsc_files dsc_pkgver strftime_c);
+use Sbuild::Base;
+use Sbuild::ChrootInfoSchroot;
+use Sbuild::ChrootInfoUnshare;
+use Sbuild::ChrootInfoSudo;
+use Sbuild::ChrootInfoAutopkgtest;
+use Sbuild::ChrootRoot;
+use Sbuild::Sysconfig qw($version $release_date);
+use Sbuild::Sysconfig;
+use Sbuild::Resolver qw(get_resolver);
+use Sbuild::Exception;
+
+use version;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+our $saved_stdout = undef;
+our $saved_stderr = undef;
+
+sub new {
+ my $class = shift;
+ my $dsc = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('ABORT', undef);
+ $self->set('Job', $dsc);
+ $self->set('Build Dir', '');
+ $self->set('Max Lock Trys', 120);
+ $self->set('Lock Interval', 5);
+ $self->set('Pkg Status', 'pending');
+ $self->set('Pkg Status Trigger', undef);
+ $self->set('Pkg Start Time', 0);
+ $self->set('Pkg End Time', 0);
+ $self->set('Pkg Fail Stage', 'init');
+ $self->set('Build Start Time', 0);
+ $self->set('Build End Time', 0);
+ $self->set('Install Start Time', 0);
+ $self->set('Install End Time', 0);
+ $self->set('This Time', 0);
+ $self->set('This Space', 0);
+ $self->set('Sub Task', 'initialisation');
+ $self->set('Host', Sbuild::ChrootRoot->new($self->get('Config')));
+ # Host execution defaults
+ my $host_defaults = $self->get('Host')->get('Defaults');
+ $host_defaults->{'USER'} = $self->get_conf('USERNAME');
+ $host_defaults->{'DIR'} = $self->get_conf('HOME');
+ $host_defaults->{'STREAMIN'} = $devnull;
+ $host_defaults->{'ENV'}->{'LC_ALL'} = 'C.UTF-8';
+ $host_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
+ $host_defaults->{'ENV_FILTER'} = $self->get_conf('ENVIRONMENT_FILTER');
+ # Note, this should never fail. But, we should handle failure anyway.
+ $self->get('Host')->begin_session();
+
+ $self->set('Session', undef);
+ $self->set('Dependency Resolver', undef);
+ $self->set('Log File', undef);
+ $self->set('Log Stream', undef);
+ $self->set('Summary Stats', {});
+ $self->set('dpkg-buildpackage pid', undef);
+ $self->set('Dpkg Version', undef);
+
+ # DSC, package and version information:
+ $self->set_dsc($dsc);
+
+ # If the job name contains an underscore then it is either the filename of
+ # a dsc or a pkgname_version string. In both cases we can already extract
+ # the version number. Otherwise it is a bare source package name and the
+ # version will initially be unknown.
+ if ($dsc =~ m/_/) {
+ $self->set_version($dsc);
+ } else {
+ $self->set('Package', $dsc);
+ }
+
+ return $self;
+}
+
+sub request_abort {
+ my $self = shift;
+ my $reason = shift;
+
+ $self->log_error("ABORT: $reason (requesting cleanup and shutdown)\n");
+ $self->set('ABORT', $reason);
+
+ # Send signal to dpkg-buildpackage immediately if it's running.
+ if (defined $self->get('dpkg-buildpackage pid')) {
+ # Handling ABORT in the loop reading from the stdout/stderr output of
+ # dpkg-buildpackage is suboptimal because then the ABORT signal would
+ # only be handled once the build process writes to stdout or stderr
+ # which might not be immediately.
+ my $pid = $self->get('dpkg-buildpackage pid');
+ # Sending the pid negated to send to the whole process group.
+ kill "TERM", -$pid;
+ }
+}
+
+sub check_abort {
+ my $self = shift;
+
+ if ($self->get('ABORT')) {
+ Sbuild::Exception::Build->throw(error => "Aborting build: " .
+ $self->get('ABORT'),
+ failstage => "abort");
+ }
+}
+
+sub set_dsc {
+ my $self = shift;
+ my $dsc = shift;
+
+ debug("Setting DSC: $dsc\n");
+
+ $self->set('DSC', $dsc);
+ $self->set('Source Dir', dirname($dsc));
+ $self->set('DSC Base', basename($dsc));
+
+ debug("DSC = " . $self->get('DSC') . "\n");
+ debug("Source Dir = " . $self->get('Source Dir') . "\n");
+ debug("DSC Base = " . $self->get('DSC Base') . "\n");
+}
+
+sub set_version {
+ my $self = shift;
+ my $pkgv = shift;
+
+ debug("Setting package version: $pkgv\n");
+
+ my ($pkg, $version);
+ if (-f $pkgv && -r $pkgv) {
+ ($pkg, $version) = dsc_pkgver($pkgv);
+ } else {
+ ($pkg, $version) = split /_/, $pkgv;
+ }
+ my $pver = Dpkg::Version->new($version, check => 1);
+ return if (!defined($pkg) || !defined($version) || !defined($pver));
+ my ($o_version);
+ $o_version = $pver->version();
+
+ # Original version (no binNMU or other addition)
+ my $oversion = $version;
+ # Original version with stripped epoch
+ my $osversion = $o_version;
+ $osversion .= '-' . $pver->revision() unless $pver->{'no_revision'};
+
+ # Add binNMU to version if needed.
+ if ($self->get_conf('BIN_NMU') || $self->get_conf('APPEND_TO_VERSION')
+ || defined $self->get_conf('BIN_NMU_CHANGELOG')) {
+ if (defined $self->get_conf('BIN_NMU_CHANGELOG')) {
+ # extract the binary version from the custom changelog entry
+ open(CLOGFH, '<', \$self->get_conf('BIN_NMU_CHANGELOG'));
+ my $changes = Dpkg::Changelog::Debian->new();
+ $changes->parse(*CLOGFH, "descr");
+ my @data = $changes->get_range({count => 1});
+ $version = $data[0]->get_version();
+ close(CLOGFH);
+ } else {
+ # compute the binary version from the original version and the
+ # requested binNMU and append-to-version parameters
+ $version = binNMU_version($version,
+ $self->get_conf('BIN_NMU_VERSION'),
+ $self->get_conf('APPEND_TO_VERSION'));
+ }
+ }
+
+ my $bver = Dpkg::Version->new($version, check => 1);
+ return if (!defined($bver));
+ my ($b_epoch, $b_version, $b_revision);
+ $b_epoch = $bver->epoch();
+ $b_epoch = "" if $bver->{'no_epoch'};
+ $b_version = $bver->version();
+ $b_revision = $bver->revision();
+ $b_revision = "" if $bver->{'no_revision'};
+
+ # Version with binNMU or other additions and stripped epoch
+ my $sversion = $b_version;
+ $sversion .= '-' . $b_revision if $b_revision ne '';
+
+ $self->set('Package', $pkg);
+ $self->set('Version', $version);
+ $self->set('Package_Version', "${pkg}_$version");
+ $self->set('Package_OVersion', "${pkg}_$oversion");
+ $self->set('Package_OSVersion', "${pkg}_$osversion");
+ $self->set('Package_SVersion', "${pkg}_$sversion");
+ $self->set('OVersion', $oversion);
+ $self->set('OSVersion', $osversion);
+ $self->set('SVersion', $sversion);
+ $self->set('VersionEpoch', $b_epoch);
+ $self->set('VersionUpstream', $b_version);
+ $self->set('VersionDebian', $b_revision);
+ $self->set('DSC File', "${pkg}_${osversion}.dsc");
+ if (length $self->get_conf('DSC_DIR')) {
+ $self->set('DSC Dir', $self->get_conf('DSC_DIR'));
+ } else {
+ $self->set('DSC Dir', "${pkg}-${b_version}");
+ }
+
+ debug("Package = " . $self->get('Package') . "\n");
+ debug("Version = " . $self->get('Version') . "\n");
+ debug("Package_Version = " . $self->get('Package_Version') . "\n");
+ debug("Package_OVersion = " . $self->get('Package_OVersion') . "\n");
+ debug("Package_OSVersion = " . $self->get('Package_OSVersion') . "\n");
+ debug("Package_SVersion = " . $self->get('Package_SVersion') . "\n");
+ debug("OVersion = " . $self->get('OVersion') . "\n");
+ debug("OSVersion = " . $self->get('OSVersion') . "\n");
+ debug("SVersion = " . $self->get('SVersion') . "\n");
+ debug("VersionEpoch = " . $self->get('VersionEpoch') . "\n");
+ debug("VersionUpstream = " . $self->get('VersionUpstream') . "\n");
+ debug("VersionDebian = " . $self->get('VersionDebian') . "\n");
+ debug("DSC File = " . $self->get('DSC File') . "\n");
+ debug("DSC Dir = " . $self->get('DSC Dir') . "\n");
+}
+
+sub set_status {
+ my $self = shift;
+ my $status = shift;
+
+ $self->set('Pkg Status', $status);
+ if (defined($self->get('Pkg Status Trigger'))) {
+ $self->get('Pkg Status Trigger')->($self, $status);
+ }
+}
+
+sub get_status {
+ my $self = shift;
+
+ return $self->get('Pkg Status');
+}
+
+# This function is the main entry point into the package build. It
+# provides a top-level exception handler and does the initial setup
+# including initiating logging and creating host chroot. The nested
+# run_ functions it calls are separate in order to permit running
+# cleanup tasks in a strict order.
+sub run {
+ my $self = shift;
+
+ eval {
+ $self->check_abort();
+
+ $self->set_status('building');
+
+ $self->set('Pkg Start Time', time);
+ $self->set('Pkg End Time', $self->get('Pkg Start Time'));
+
+ # Acquire the architectures we're building for and on.
+ $self->set('Host Arch', $self->get_conf('HOST_ARCH'));
+ $self->set('Build Arch', $self->get_conf('BUILD_ARCH'));
+ $self->set('Build Profiles', $self->get_conf('BUILD_PROFILES'));
+
+ # Acquire the build type in the nomenclature used by the --build
+ # argument of dpkg-buildpackage
+ my $buildtype;
+ if ($self->get_conf('BUILD_SOURCE')) {
+ if ($self->get_conf('BUILD_ARCH_ANY')) {
+ if ($self->get_conf('BUILD_ARCH_ALL')) {
+ $buildtype = "full";
+ } else {
+ $buildtype = "source,any";
+ }
+ } else {
+ if ($self->get_conf('BUILD_ARCH_ALL')) {
+ $buildtype = "source,all";
+ } else {
+ $buildtype = "source";
+ }
+ }
+ } else {
+ if ($self->get_conf('BUILD_ARCH_ANY')) {
+ if ($self->get_conf('BUILD_ARCH_ALL')) {
+ $buildtype = "binary";
+ } else {
+ $buildtype = "any";
+ }
+ } else {
+ if ($self->get_conf('BUILD_ARCH_ALL')) {
+ $buildtype = "all";
+ } else {
+ Sbuild::Exception::Build->throw(error => "Neither architecture specific nor architecture independent or source package specified to be built.",
+ failstage => "init");
+ }
+ }
+ }
+ $self->set('Build Type', $buildtype);
+
+ my $dist = $self->get_conf('DISTRIBUTION');
+ if (!defined($dist) || !$dist) {
+ Sbuild::Exception::Build->throw(error => "No distribution defined",
+ failstage => "init");
+ }
+
+ # TODO: Get package name from build object
+ if (!$self->open_build_log()) {
+ Sbuild::Exception::Build->throw(error => "Failed to open build log",
+ failstage => "init");
+ }
+
+ # Set a chroot to run commands in host
+ my $host = $self->get('Host');
+
+ # Host execution defaults (set streams)
+ my $host_defaults = $host->get('Defaults');
+ $host_defaults->{'STREAMIN'} = $devnull;
+ $host_defaults->{'STREAMOUT'} = $self->get('Log Stream');
+ $host_defaults->{'STREAMERR'} = $self->get('Log Stream');
+
+ $self->check_abort();
+ $self->run_chroot();
+ };
+
+ debug("Error run(): $@") if $@;
+
+ my $e;
+ if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
+ if ($e->status) {
+ $self->set_status($e->status);
+ } else {
+ $self->set_status("failed");
+ }
+ $self->set('Pkg Fail Stage', $e->failstage);
+ $e->rethrow();
+ }
+}
+
+# Pack up source if needed and then run the main chroot session.
+# Close log during return/failure.
+sub run_chroot {
+ my $self = shift;
+
+ eval {
+ $self->check_abort();
+ $self->run_chroot_session();
+ };
+
+ debug("Error run_chroot(): $@") if $@;
+
+ # Log exception info and set status and fail stage prior to
+ # closing build log.
+ my $e;
+ if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
+ $self->log_error("$e\n");
+ $self->log_info($e->info."\n")
+ if ($e->info);
+ if ($e->status) {
+ $self->set_status($e->status);
+ } else {
+ $self->set_status("failed");
+ }
+ $self->set('Pkg Fail Stage', $e->failstage);
+ }
+
+ $self->close_build_log();
+
+ if ($e) {
+ $e->rethrow();
+ }
+}
+
+# Create main chroot session and package resolver. Creates a lock in
+# the chroot to prevent concurrent chroot usage (only important for
+# non-snapshot chroots). Ends chroot session on return/failure.
+sub run_chroot_session {
+ my $self=shift;
+
+ eval {
+ $self->check_abort();
+ my $chroot_info;
+ if ($self->get_conf('CHROOT_MODE') eq 'schroot') {
+ $chroot_info = Sbuild::ChrootInfoSchroot->new($self->get('Config'));
+ } elsif ($self->get_conf('CHROOT_MODE') eq 'autopkgtest') {
+ $chroot_info = Sbuild::ChrootInfoAutopkgtest->new($self->get('Config'));
+ } elsif ($self->get_conf('CHROOT_MODE') eq 'unshare') {
+ $chroot_info = Sbuild::ChrootInfoUnshare->new($self->get('Config'));
+ } else {
+ $chroot_info = Sbuild::ChrootInfoSudo->new($self->get('Config'));
+ }
+
+ my $host = $self->get('Host');
+
+ my $session = $chroot_info->create('chroot',
+ $self->get_conf('DISTRIBUTION'),
+ $self->get_conf('CHROOT'),
+ $self->get_conf('BUILD_ARCH'));
+ if (!defined $session) {
+ Sbuild::Exception::Build->throw(error => "Error creating chroot",
+ failstage => "create-session");
+ }
+
+ $self->check_abort();
+ if (!$session->begin_session()) {
+ Sbuild::Exception::Build->throw(error => "Error creating chroot session: skipping " .
+ $self->get('Package'),
+ failstage => "create-session");
+ }
+
+ $self->set('Session', $session);
+
+ $self->check_abort();
+ my $chroot_arch = $self->chroot_arch();
+ if ($self->get_conf('BUILD_ARCH') ne $chroot_arch) {
+ Sbuild::Exception::Build->throw(
+ error => "Requested build architecture (" .
+ $self->get_conf('BUILD_ARCH') .
+ ") and chroot architecture (" . $chroot_arch .
+ ") do not match. Skipping build.",
+ info => "Please specify the correct architecture with --build, or use a chroot of the correct architecture",
+ failstage => "create-session");
+ }
+
+ if (length $self->get_conf('BUILD_PATH')) {
+ my $build_path = $self->get_conf('BUILD_PATH');
+ $self->set('Build Dir', $build_path);
+ if (!($session->test_directory($build_path))) {
+ if (!$session->mkdir($build_path, { PARENTS => 1})) {
+ Sbuild::Exception::Build->throw(
+ error => "Buildpath: " . $build_path . " cannot be created",
+ failstage => "create-session");
+ }
+ } else {
+ my $isempty = <<END;
+if (opendir my \$dfh, "$build_path") {
+ while (defined(my \$file=readdir \$dfh)) {
+ next if \$file eq "." or \$file eq "..";
+ closedir \$dfh;
+ exit 1
+ }
+ closedir \$dfh;
+ exit 0
+}
+exit 2
+END
+ $session->run_command({
+ COMMAND => ['perl', '-e', $isempty],
+ USER => 'root',
+ DIR => '/'
+ });
+ if ($? == 1) {
+ Sbuild::Exception::Build->throw(
+ error => "Buildpath: " . $build_path . " is not empty",
+ failstage => "create-session");
+ }
+ elsif ($? == 2) {
+ Sbuild::Exception::Build->throw(
+ error => "Buildpath: " . $build_path . " cannot be read. Insufficient permissions?",
+ failstage => "create-session");
+ }
+ }
+ } else {
+ # we run mktemp within the chroot instead of using File::Temp::tempdir because the user
+ # running sbuild might not have permissions creating a directory in /build. This happens
+ # when the chroot was extracted in a different user namespace than the outer user
+ $self->check_abort();
+ my $tmpdir = $session->mktemp({
+ TEMPLATE => "/build/" . $self->get('Package') . '-XXXXXX',
+ DIRECTORY => 1});
+ if (!$tmpdir) {
+ $self->log_error("unable to mktemp\n");
+ Sbuild::Exception::Build->throw(error => "unable to mktemp",
+ failstage => "create-build-dir");
+ }
+ $self->check_abort();
+ $self->set('Build Dir', $tmpdir);
+ }
+
+ # Copy in external solvers if we are cross-building
+ if ($self->get('Host Arch') ne $self->get('Build Arch')) {
+ if (!$session->test_directory("/usr/lib/apt/solvers")) {
+ if (!$session->mkdir("/usr/lib/apt/solvers", { PARENTS => 1})) {
+ Sbuild::Exception::Build->throw(
+ error => "/usr/lib/apt/solvers cannot be created",
+ failstage => "create-session");
+ }
+ }
+ foreach my $solver ('apt', 'sbuild-cross-resolver') {
+ if ($session->test_regular_file_readable("/usr/lib/apt/solvers/$solver")) {
+ next;
+ }
+ if (! -e "/usr/lib/apt/solvers/$solver") {
+ Sbuild::Exception::Build->throw(
+ error => "/usr/lib/apt/solvers/$solver is missing",
+ failstage => "create-session");
+ }
+ if (! $session->copy_to_chroot("/usr/lib/apt/solvers/$solver", "/usr/lib/apt/solvers/$solver")) {
+ Sbuild::Exception::Build->throw(
+ error => "/usr/lib/apt/solvers/$solver cannot be copied",
+ failstage => "create-session");
+ }
+ if (! $session->chmod("/usr/lib/apt/solvers/$solver", "0755")) {
+ Sbuild::Exception::Build->throw(
+ error => "/usr/lib/apt/solvers/$solver cannot chmod",
+ failstage => "create-session");
+ }
+ }
+ }
+
+ # Run pre build external commands
+ $self->check_abort();
+ if(!$self->run_external_commands("pre-build-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute pre-build-commands",
+ failstage => "run-pre-build-commands");
+ }
+
+ # Log colouring
+ $self->build_log_colour('red', '^E: ');
+ $self->build_log_colour('yellow', '^W: ');
+ $self->build_log_colour('green', '^I: ');
+ $self->build_log_colour('red', '^Status:');
+ $self->build_log_colour('green', '^Status: successful$');
+ $self->build_log_colour('yellow', '^Keeping session: ');
+ $self->build_log_colour('red', '^Lintian:');
+ $self->build_log_colour('yellow', '^Lintian: warn$');
+ $self->build_log_colour('green', '^Lintian: pass$');
+ $self->build_log_colour('green', '^Lintian: info$');
+ $self->build_log_colour('red', '^Piuparts:');
+ $self->build_log_colour('green', '^Piuparts: pass$');
+ $self->build_log_colour('red', '^Autopkgtest:');
+ $self->build_log_colour('yellow', '^Autopkgtest: no tests$');
+ $self->build_log_colour('green', '^Autopkgtest: pass$');
+
+ # Log filtering
+ my $filter;
+ $filter = $session->get('Location');
+ $filter =~ s;^/;;;
+ $self->build_log_filter($filter , 'CHROOT');
+
+ # Need tempdir to be writable and readable by sbuild group.
+ $self->check_abort();
+ if (!$session->chown($self->get('Build Dir'), $self->get_conf('BUILD_USER'), 'sbuild')) {
+ Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
+ failstage => "create-build-dir");
+ }
+ $self->check_abort();
+ if (!$session->chmod($self->get('Build Dir'), "ug=rwx,o=,a-s")) {
+ Sbuild::Exception::Build->throw(error => "Failed to set sbuild group ownership on chroot build dir",
+ failstage => "create-build-dir");
+ }
+
+ $self->check_abort();
+ # Needed so chroot commands log to build log
+ $session->set('Log Stream', $self->get('Log Stream'));
+ $host->set('Log Stream', $self->get('Log Stream'));
+
+ # Chroot execution defaults
+ my $chroot_defaults = $session->get('Defaults');
+ $chroot_defaults->{'DIR'} = $self->get('Build Dir');
+ $chroot_defaults->{'STREAMIN'} = $devnull;
+ $chroot_defaults->{'STREAMOUT'} = $self->get('Log Stream');
+ $chroot_defaults->{'STREAMERR'} = $self->get('Log Stream');
+ $chroot_defaults->{'ENV'}->{'LC_ALL'} = 'C.UTF-8';
+ $chroot_defaults->{'ENV'}->{'SHELL'} = '/bin/sh';
+ $chroot_defaults->{'ENV'}->{'HOME'} = '/sbuild-nonexistent';
+ $chroot_defaults->{'ENV_FILTER'} = $self->get_conf('ENVIRONMENT_FILTER');
+
+ my $resolver = get_resolver($self->get('Config'), $session, $host);
+ $resolver->set('Log Stream', $self->get('Log Stream'));
+ $resolver->set('Arch', $self->get_conf('ARCH'));
+ $resolver->set('Host Arch', $self->get_conf('HOST_ARCH'));
+ $resolver->set('Build Arch', $self->get_conf('BUILD_ARCH'));
+ $resolver->set('Build Profiles', $self->get_conf('BUILD_PROFILES'));
+ $resolver->set('Build Dir', $self->get('Build Dir'));
+ $self->set('Dependency Resolver', $resolver);
+
+ # Lock chroot so it won't be tampered with during the build.
+ $self->check_abort();
+ my $jobname;
+ # the version might not yet be known if the user only passed a package
+ # name without a version to sbuild
+ if ($self->get('Package_SVersion')) {
+ $jobname = $self->get('Package_SVersion');
+ } else {
+ $jobname = $self->get('Package');
+ }
+ if (!$session->lock_chroot($jobname, $$, $self->get_conf('USERNAME'))) {
+ Sbuild::Exception::Build->throw(error => "Error locking chroot session: skipping " .
+ $self->get('Package'),
+ failstage => "lock-session");
+ }
+
+ $self->check_abort();
+ $self->run_chroot_session_locked();
+ };
+
+ debug("Error run_chroot_session(): $@") if $@;
+
+ if ($self->get('Pkg Status') ne "successful") {
+ if(!$self->run_external_commands("post-build-failed-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute post-build-commands",
+ failstage => "run-post-build-failed-commands");
+ }
+ }
+
+ # End chroot session
+ my $session = $self->get('Session');
+ if (defined $session) {
+ my $end_session =
+ ($self->get_conf('PURGE_SESSION') eq 'always' ||
+ ($self->get_conf('PURGE_SESSION') eq 'successful' &&
+ $self->get_status() eq 'successful')) ? 1 : 0;
+ if ($end_session) {
+ $session->end_session();
+ } else {
+ $self->log("Keeping session: " . $session->get('Session ID') . "\n");
+ }
+ $session = undef;
+ }
+ $self->set('Session', $session);
+
+ my $e;
+ if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
+ $e->rethrow();
+ }
+}
+
+# Run tasks in a *locked* chroot. Update and upgrade packages.
+# Unlocks chroot on return/failure.
+sub run_chroot_session_locked {
+ my $self = shift;
+
+ eval {
+ my $session = $self->get('Session');
+ my $resolver = $self->get('Dependency Resolver');
+
+ # Run specified chroot setup commands
+ $self->check_abort();
+ if(!$self->run_external_commands("chroot-setup-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute chroot-setup-commands",
+ failstage => "run-chroot-setup-commands");
+ }
+
+ $self->check_abort();
+
+
+ $self->check_abort();
+ if (!$resolver->setup()) {
+ Sbuild::Exception::Build->throw(error => "resolver setup failed",
+ failstage => "resolver setup");
+ }
+
+ my $filter;
+ $filter = $resolver->get('Dummy package path');
+ $filter =~ s;^/;;;
+ $self->build_log_filter($filter , 'RESOLVERDIR');
+
+ $self->check_abort();
+ $self->run_chroot_update();
+
+ $self->check_abort();
+ $self->run_fetch_install_packages();
+ };
+
+ debug("Error run_chroot_session_locked(): $@") if $@;
+
+ my $session = $self->get('Session');
+ my $resolver = $self->get('Dependency Resolver');
+
+ $resolver->cleanup();
+ # Unlock chroot now it's cleaned up and ready for other users.
+ $session->unlock_chroot();
+
+ my $e;
+ if ($e = Exception::Class->caught('Sbuild::Exception::Build')) {
+ $e->rethrow();
+ }
+}
+
+sub run_chroot_update {
+ my $self = shift;
+ my $resolver = $self->get('Dependency Resolver');
+
+ eval {
+ if ($self->get_conf('APT_CLEAN') || $self->get_conf('APT_UPDATE') ||
+ $self->get_conf('APT_DISTUPGRADE') || $self->get_conf('APT_UPGRADE')) {
+ $self->log_subsection('Update chroot');
+ }
+
+ # Clean APT cache.
+ $self->check_abort();
+ if ($self->get_conf('APT_CLEAN')) {
+ if ($resolver->clean()) {
+ # Since apt-clean was requested specifically, fail on
+ # error when not in buildd mode.
+ $self->log_error("apt-get clean failed\n");
+ if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
+ Sbuild::Exception::Build->throw(error => "apt-get clean failed",
+ failstage => "apt-get-clean");
+ }
+ }
+ }
+
+ # Update APT cache.
+ $self->check_abort();
+ if ($self->get_conf('APT_UPDATE')) {
+ if ($resolver->update()) {
+ # Since apt-update was requested specifically, fail on
+ # error when not in buildd mode.
+ if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
+ Sbuild::Exception::Build->throw(error => "apt-get update failed",
+ failstage => "apt-get-update");
+ }
+ }
+ } else {
+ # If it was requested not to do an apt update, the build and host
+ # architecture must already be part of the chroot. If they are not
+ # and thus added during the sbuild run, issue a warning because
+ # then the package build dependencies will likely fail to be
+ # installable.
+ #
+ # The logic which checks which architectures are needed is in
+ # ResolverBase.pm, so we just check whether any architectures
+ # where added with 'dpkg --add-architecture' because if any were
+ # added an update is most likely needed.
+ if (keys %{$resolver->get('Added Foreign Arches')}) {
+ $self->log_warning("Additional architectures were added but apt update was disabled. Build dependencies might not be satisfiable.\n");
+ }
+ }
+
+ # Upgrade using APT.
+ $self->check_abort();
+ if ($self->get_conf('APT_DISTUPGRADE')) {
+ if ($resolver->distupgrade()) {
+ # Since apt-distupgrade was requested specifically, fail on
+ # error when not in buildd mode.
+ if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
+ Sbuild::Exception::Build->throw(error => "apt-get dist-upgrade failed",
+ failstage => "apt-get-dist-upgrade");
+ }
+ }
+ } elsif ($self->get_conf('APT_UPGRADE')) {
+ if ($resolver->upgrade()) {
+ # Since apt-upgrade was requested specifically, fail on
+ # error when not in buildd mode.
+ if ($self->get_conf('SBUILD_MODE') ne 'buildd') {
+ Sbuild::Exception::Build->throw(error => "apt-get upgrade failed",
+ failstage => "apt-get-upgrade");
+ }
+ }
+ }
+ };
+
+ debug("Error run_chroot_update(): $@") if $@;
+
+ my $e = Exception::Class->caught('Sbuild::Exception::Build');
+ if ($e) {
+ $self->run_external_commands("chroot-update-failed-commands");
+ $e->rethrow();
+ }
+}
+
+# Fetch sources, run setup, fetch and install core and package build
+# deps, then run build. Cleans up build directory and uninstalls
+# build depends on return/failure.
+sub run_fetch_install_packages {
+ my $self = shift;
+
+ $self->check_abort();
+ eval {
+ my $session = $self->get('Session');
+ my $resolver = $self->get('Dependency Resolver');
+
+ $self->check_abort();
+ if (!$self->fetch_source_files()) {
+ Sbuild::Exception::Build->throw(error => "Failed to fetch source files",
+ failstage => "fetch-src");
+ }
+
+ # Display message about chroot setup script option use being deprecated
+ if ($self->get_conf('CHROOT_SETUP_SCRIPT')) {
+ my $msg = "setup-hook option is deprecated. It has been superseded by ";
+ $msg .= "the chroot-setup-commands feature. setup-hook script will be ";
+ $msg .= "run via chroot-setup-commands.\n";
+ $self->log_warning($msg);
+ }
+
+ $self->check_abort();
+ $self->set('Install Start Time', time);
+ $self->set('Install End Time', $self->get('Install Start Time'));
+ my @coredeps = @{$self->get_conf('CORE_DEPENDS')};
+ if ($self->get('Host Arch') ne $self->get('Build Arch')) {
+ my $crosscoredeps = $self->get_conf('CROSSBUILD_CORE_DEPENDS');
+ if (defined($crosscoredeps->{$self->get('Host Arch')})) {
+ push(@coredeps, @{$crosscoredeps->{$self->get('Host Arch')}});
+ } else {
+ push(@coredeps, 'crossbuild-essential-' . $self->get('Host Arch') . ':native');
+ # Also add the following to work around bug #815172
+ push(@coredeps, 'libc-dev:' . $self->get('Host Arch'),
+ 'libstdc++-dev:' . $self->get('Host Arch'));
+ }
+ }
+
+ my @snapshot = ();
+ @snapshot = ("gcc-snapshot") if ($self->get_conf('GCC_SNAPSHOT'));
+
+ $resolver->add_dependencies('MAIN',
+ join(", ", $self->get('Build Depends') // (),
+ @{$self->get_conf('MANUAL_DEPENDS')}, @snapshot, @coredeps),
+ join(", ", $self->get('Build Depends Arch') // (),
+ @{$self->get_conf('MANUAL_DEPENDS_ARCH')}),
+ join(", ", $self->get('Build Depends Indep') // (),
+ @{$self->get_conf('MANUAL_DEPENDS_INDEP')}),
+ join(", ", $self->get('Build Conflicts') // (),
+ @{$self->get_conf('MANUAL_CONFLICTS')}),
+ join(", ", $self->get('Build Conflicts Arch') // (),
+ @{$self->get_conf('MANUAL_CONFLICTS_ARCH')}),
+ join(", ", $self->get('Build Conflicts Indep') // (),
+ @{$self->get_conf('MANUAL_CONFLICTS_INDEP')}));
+
+ $self->log_subsection("Install package build dependencies");
+
+ $self->check_abort();
+ if (!$resolver->install_deps('main', 'MAIN')) {
+ Sbuild::Exception::Build->throw(error => "Package build dependencies not satisfied; skipping",
+ failstage => "install-deps");
+ }
+ $self->check_abort();
+ if ($self->get_conf('PURGE_EXTRA_PACKAGES')) {
+ if (!$resolver->purge_extra_packages($self->get('Package'))) {
+ Sbuild::Exception::Build->throw(error => "Chroot could not be cleaned of extra packages",
+ failstage => "install-deps");
+ }
+ }
+ $self->set('Install End Time', time);
+
+ # the architecture check has to be done *after* build-essential is
+ # installed because as part of the architecture check a perl script is
+ # run inside the chroot which requires the Dpkg::Arch module which is
+ # in libdpkg-perl which might not exist in the chroot but will get
+ # installed by the build-essential package
+ if(!$self->check_architectures()) {
+ Sbuild::Exception::Build->throw(error => "Architecture check failed",
+ failstage => "check-architecture");
+ }
+
+ $self->check_abort();
+ my $dpkg_version = $resolver->dump_build_environment();
+ $self->set('Dpkg Version',Dpkg::Version->new($dpkg_version));
+
+ $self->check_abort();
+ if ($self->build()) {
+ $self->set_status('successful');
+ } else {
+ $self->set('Pkg Fail Stage', "build");
+ $self->set_status('failed');
+ }
+
+ # We run it here and not inside build() because otherwise, we cannot
+ # set the overall status to failed due to lintian errors
+ if ($self->get('Pkg Status') eq "successful") {
+ # Run lintian.
+ $self->check_abort();
+ my $ret = $self->run_lintian();
+ if (!$ret && $self->get_conf('LINTIAN_REQUIRE_SUCCESS')) {
+ $self->set('Pkg Fail Stage', "post-build");
+ $self->set_status("failed");
+ }
+ }
+
+ # Run specified chroot cleanup commands
+ $self->check_abort();
+ if (!$self->run_external_commands("chroot-cleanup-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute chroot-cleanup-commands",
+ failstage => "run-chroot-cleanup-commands");
+ }
+
+ # piuparts and autopkgtest must be run while the chroot is still open
+ # because they might need files that are not available on the host,
+ # for example the .dsc which might have been downloaded
+ if ($self->get('Pkg Status') eq "successful") {
+ $self->log_subsection("Post Build");
+
+ # Run piuparts.
+ $self->check_abort();
+ my $ret = $self->run_piuparts();
+ if (!$ret && $self->get_conf('PIUPARTS_REQUIRE_SUCCESS')) {
+ $self->set('Pkg Fail Stage', "post-build");
+ $self->set_status("failed");
+ }
+
+ # Run autopkgtest.
+ $self->check_abort();
+ $ret = $self->run_autopkgtest();
+ if (!$ret && $self->get_conf('AUTOPKGTEST_REQUIRE_SUCCESS')) {
+ $self->set('Pkg Fail Stage', "post-build");
+ $self->set_status("failed");
+ }
+
+ # Run post build external commands
+ $self->check_abort();
+ if(!$self->run_external_commands("post-build-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute post-build-commands",
+ failstage => "run-post-build-commands");
+ }
+
+ }
+ };
+
+ # If 'This Time' is still zero, then build() raised an exception and thus
+ # the end time was never set. Thus, setting it here.
+ # If we would set 'This Time' here unconditionally, then it would also
+ # possibly include the times to run piuparts and autopkgtest.
+ if ($self->get('This Time') == 0) {
+ $self->set('This Time', $self->get('Pkg End Time') - $self->get('Pkg Start Time'));
+ $self->set('This Time', 0) if $self->get('This Time') < 0;
+ }
+ # Same for 'This Space' which we must set here before everything gets
+ # cleaned up.
+ if ($self->get('This Space') == 0) {
+ # Since the build apparently failed, we pass an empty list of the
+ # build artifacts
+ $self->set('This Space', $self->check_space());
+ }
+
+ debug("Error run_fetch_install_packages(): $@") if $@;
+
+ # I catch the exception here and trigger the hook, if needed. Normally I'd
+ # do this at the end of the function, but I want the hook to fire before we
+ # clean up the environment. I re-throw the exception at the end, as usual
+ my $e = Exception::Class->caught('Sbuild::Exception::Build');
+ if ($e) {
+ if ($e->status) {
+ $self->set_status($e->status);
+ } else {
+ $self->set_status("failed");
+ }
+ $self->set('Pkg Fail Stage', $e->failstage);
+ }
+ if (!$self->get('ABORT') && defined $self->get('Pkg Fail Stage')) {
+ if ($self->get('Pkg Fail Stage') eq 'build' ) {
+ if(!$self->run_external_commands("build-failed-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute build-failed-commands",
+ failstage => "run-build-failed-commands");
+ }
+ } elsif($self->get('Pkg Fail Stage') eq 'install-deps' ) {
+ my $could_not_explain = undef;
+
+ if (defined $self->get_conf('BD_UNINSTALLABLE_EXPLAINER')
+ && $self->get_conf('BD_UNINSTALLABLE_EXPLAINER') ne ''
+ && $self->get_conf('BD_UNINSTALLABLE_EXPLAINER') ne 'none') {
+ if (!$self->explain_bd_uninstallable()) {
+ $could_not_explain = 1;
+ }
+ }
+
+ if(!$self->run_external_commands("build-deps-failed-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute build-deps-failed-commands",
+ failstage => "run-build-deps-failed-commands");
+ }
+
+ if( $could_not_explain ) {
+ Sbuild::Exception::Build->throw(error => "Failed to explain bd-uninstallable",
+ failstage => "explain-bd-uninstallable");
+ }
+ }
+ }
+
+ $self->log_subsection("Cleanup");
+ my $session = $self->get('Session');
+ my $resolver = $self->get('Dependency Resolver');
+
+ my $purge_build_directory =
+ ($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'always' ||
+ ($self->get_conf('PURGE_BUILD_DIRECTORY') eq 'successful' &&
+ $self->get_status() eq 'successful')) ? 1 : 0;
+ my $purge_build_deps =
+ ($self->get_conf('PURGE_BUILD_DEPS') eq 'always' ||
+ ($self->get_conf('PURGE_BUILD_DEPS') eq 'successful' &&
+ $self->get_status() eq 'successful')) ? 1 : 0;
+ my $is_cloned_session = (defined ($session->get('Session Purged')) &&
+ $session->get('Session Purged') == 1) ? 1 : 0;
+
+ if ($purge_build_directory) {
+ # Purge package build directory
+ $self->log("Purging " . $self->get('Build Dir') . "\n");
+ if (!$self->get('Session')->unlink($self->get('Build Dir'), { RECURSIVE => 1 })) {
+ $self->log_error("unable to remove build directory\n");
+ }
+ }
+
+ # Purge non-cloned session
+ if ($is_cloned_session) {
+ $self->log("Not cleaning session: cloned chroot in use\n");
+ } else {
+ if ($purge_build_deps) {
+ # Removing dependencies
+ $resolver->uninstall_deps();
+ } else {
+ $self->log("Not removing build depends: as requested\n");
+ }
+ }
+
+
+ # re-throw the previously-caught exception
+ if ($e) {
+ $e->rethrow();
+ }
+}
+
+sub copy_to_chroot {
+ my $self = shift;
+ my $source = shift;
+ my $chrootdest = shift;
+
+ my $session = $self->get('Session');
+
+ $self->check_abort();
+ if(!$session->copy_to_chroot($source, $chrootdest)) {
+ $self->log_error("Failed to copy $source to $chrootdest\n");
+ return 0;
+ }
+
+ if (!$session->chown($chrootdest, $self->get_conf('BUILD_USER'), 'sbuild')) {
+ $self->log_error("Failed to set sbuild group ownership on $chrootdest\n");
+ return 0;
+ }
+ if (!$session->chmod($chrootdest, "ug=rw,o=r,a-s")) {
+ $self->log_error("Failed to set 0644 permissions on $chrootdest\n");
+ return 0;
+ }
+
+ return 1;
+}
+sub fetch_source_files {
+ my $self = shift;
+
+ my $build_dir = $self->get('Build Dir');
+ my $host_arch = $self->get('Host Arch');
+ my $resolver = $self->get('Dependency Resolver');
+
+ my ($dscarchs, $dscpkg, $dscver, $dsc);
+
+ my $build_depends = "";
+ my $build_depends_arch = "";
+ my $build_depends_indep = "";
+ my $build_conflicts = "";
+ my $build_conflicts_arch = "";
+ my $build_conflicts_indep = "";
+ local( *F );
+
+ $self->log_subsection("Fetch source files");
+
+ $self->check_abort();
+ if ($self->get('DSC Base') =~ m/\.dsc$/) {
+ my $dir = $self->get('Source Dir');
+
+ # Work with a .dsc file.
+ my $file = $self->get('DSC');
+ $dsc = $self->get('DSC File');
+ if (! -f $file || ! -r $file) {
+ $self->log_error("Could not find $file\n");
+ return 0;
+ }
+ my @cwd_files = dsc_files($file);
+
+ # Copy the local source files into the build directory.
+ $self->log_subsubsection("Local sources");
+ $self->log("$file exists in $dir; copying to chroot\n");
+ if (! $self->copy_to_chroot("$file", "$build_dir/$dsc")) {
+ $self->log_error("Could not copy $file to $build_dir/$dsc\n");
+ return 0;
+ }
+ foreach (@cwd_files) {
+ if (! $self->copy_to_chroot("$dir/$_", "$build_dir/$_")) {
+ $self->log_error("Could not copy $dir/$_ to $build_dir/$_\n");
+ return 0;
+ }
+ }
+ } else {
+ my $pkg = $self->get('DSC');
+ my $ver;
+
+ if ($pkg =~ m/_/) {
+ ($pkg, $ver) = split /_/, $pkg;
+ }
+
+ # Use apt to download the source files
+ $self->log_subsubsection("Check APT");
+
+ my $indextargets;
+ {
+ my $pipe = $self->get('Session')->pipe_command(
+ {
+ COMMAND => [ 'apt-get', 'indextargets' ],
+ USER => $self->get_conf('BUILD_USER'),
+ }
+ );
+ if ( !$pipe ) {
+ $self->log_error("Can't open pipe to apt-get: $!\n");
+ return 0;
+ }
+ $indextargets =
+ Dpkg::Index->new( get_key_func => sub { return $_[0]->{URI}; } );
+
+ if ( !$indextargets->parse( $pipe, 'apt-get indextargets' ) ) {
+ $self->log_error(
+ "Cannot parse output of apt-get indextargets: $!\n");
+ return 0;
+ }
+ close($pipe);
+
+ if ($?) {
+ $self->log_error("apt-get indextargets exit status $?: $!\n");
+ return 0;
+ }
+ }
+ my $found_sources_entry = 0;
+ my $num_packages_entries = 0;
+ my $entry_uri = undef;
+ my $entry_codename = undef;
+ my $entry_component = undef;
+ foreach my $key ( $indextargets->get_keys() ) {
+ my $cdata = $indextargets->get_by_key($key);
+ my $createdby = $cdata->{"Created-By"} // "";
+ my $targetof = $cdata->{"Target-Of"} // "";
+ my $identifier = $cdata->{"Identifier"} // "";
+ if ( $createdby eq "Sources"
+ and $identifier eq "Sources"
+ and $targetof eq "deb-src" )
+ {
+ $found_sources_entry = 1;
+ }
+ if ( $createdby eq 'Packages'
+ and $identifier eq 'Packages'
+ and $targetof eq 'deb'
+ and length $cdata->{"Repo-URI"} > 0
+ and length $cdata->{"Codename"} > 0
+ and length $cdata->{"Label"} > 0
+ and length $cdata->{"Origin"} > 0
+ and length $cdata->{"Suite"} > 0
+ and $cdata->{"Repo-URI"} =~ /^file:\//
+ and $cdata->{"Codename"} eq 'invalid-sbuild-codename'
+ and $cdata->{'Label'} eq 'sbuild-build-depends-archive'
+ and $cdata->{'Origin'} eq 'sbuild-build-depends-archive'
+ and $cdata->{'Suite'} eq 'invalid-sbuild-suite' )
+ {
+ # do not count the sbuild dummy repository created by any
+ # --extra-package options
+ next;
+ }
+ if ( $createdby eq 'Packages'
+ and $identifier eq 'Packages'
+ and $targetof eq 'deb'
+ and length $cdata->{"Repo-URI"} > 0
+ and length $cdata->{"Codename"} > 0
+ and length $cdata->{"Component"} > 0 )
+ {
+ $num_packages_entries += 1;
+ $entry_uri = $cdata->{"Repo-URI"};
+ $entry_codename = $cdata->{"Codename"};
+ $entry_component = $cdata->{"Component"};
+ }
+ }
+ if ( !$found_sources_entry ) {
+ $self->log("There are no deb-src lines in your sources.list\n");
+ if ( $num_packages_entries == 0 ) {
+ $self->log("Cannot generate deb-src entry without deb entry\n");
+ }
+ elsif ( $num_packages_entries > 1 ) {
+ $self->log( "Cannot generate deb-src entry "
+ . "with more than one deb entry\n" );
+ }
+ else {
+ my $entry =
+ "deb-src $entry_uri $entry_codename $entry_component";
+ $self->log(
+ "Automatically adding to EXTRA_REPOSITORIES: $entry\n");
+ push @{ $self->get_conf('EXTRA_REPOSITORIES') }, $entry;
+ $resolver->add_extra_repositories();
+ $self->run_chroot_update();
+ }
+ }
+
+ $self->log("Checking available source versions...\n");
+
+ # We would like to call apt-cache with --only-source so that the
+ # result only contains source packages with the given name but this
+ # feature was only introduced in apt 1.1~exp10 so it is only available
+ # in Debian Stretch and later
+ my $pipe = $self->get('Dependency Resolver')->pipe_apt_command(
+ { COMMAND => [$self->get_conf('APT_CACHE'),
+ '-q', 'showsrc', $pkg],
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0,
+ DIR => '/'});
+ if (!$pipe) {
+ $self->log_error("Can't open pipe to ".$self->get_conf('APT_CACHE').": $!\n");
+ return 0;
+ }
+
+ my $key_func = sub {
+ return $_[0]->{Package} . '_' . $_[0]->{Version};
+ };
+
+ my $index = Dpkg::Index->new(get_key_func=>$key_func);
+
+ if (!$index->parse($pipe, 'apt-cache showsrc')) {
+ $self->log_error("Cannot parse output of apt-cache showsrc: $!\n");
+ return 0;
+ }
+
+ close($pipe);
+
+ if ($?) {
+ $self->log_error($self->get_conf('APT_CACHE') . " exit status $?: $!\n");
+ return 0;
+ }
+
+ my $highestversion;
+ my $highestdsc;
+
+ foreach my $key ($index->get_keys()) {
+ my $cdata = $index->get_by_key($key);
+ my $pkgname = $cdata->{"Package"};
+ if (not defined($pkgname)) {
+ $self->log_warning("apt-cache output without Package field\n");
+ next;
+ }
+ # Since we cannot run apt-cache with --only-source because that
+ # feature was only introduced with apt 1.1~exp10, the result can
+ # contain source packages that we didn't ask for (but which
+ # contain binary packages of the name we specified). Since we only
+ # are interested in source packages of the given name, we skip
+ # everything that is a different source package.
+ if ($pkg ne $pkgname) {
+ next;
+ }
+ my $pkgversion = $cdata->{"Version"};
+ if (not defined($pkgversion)) {
+ $self->log_warning("apt-cache output without Version field\n");
+ next;
+ }
+ if (defined($ver) and $ver ne $pkgversion) {
+ next;
+ }
+ my $checksums = Dpkg::Checksums->new();
+ $checksums->add_from_control($cdata, use_files_for_md5 => 1);
+ my @files = grep {/\.dsc$/} $checksums->get_files();
+ if (scalar @files != 1) {
+ $self->log_warning("apt-cache output with more than one .dsc\n");
+ next;
+ }
+ if (!defined $highestdsc) {
+ $highestdsc = $files[0];
+ $highestversion = $pkgversion;
+ } else {
+ if (version_compare($highestversion, $pkgversion) < 0) {
+ $highestdsc = $files[0];
+ $highestversion = $pkgversion;
+ }
+ }
+ }
+
+ if (!defined $highestdsc) {
+ $self->log_error($self->get_conf('APT_CACHE') .
+ " returned no information about $pkg source\n");
+ $self->log_error("Are there any deb-src lines in your /etc/apt/sources.list?\n");
+ return 0;
+ }
+
+ $self->set_dsc($highestdsc);
+ $dsc = $highestdsc;
+
+ $self->log_subsubsection("Download source files with APT");
+
+ my $pipe2 = $self->get('Dependency Resolver')->pipe_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), '--only-source', '-q', '-d', 'source', "$pkg=$highestversion"],
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0}) || return 0;
+
+ while(<$pipe2>) {
+ $self->log($_);
+ }
+ close($pipe2);
+ if ($?) {
+ $self->log_error($self->get_conf('APT_GET') . " for sources failed\n");
+ return 0;
+ }
+ }
+
+ my $pipe = $self->get('Session')->get_read_file_handle("$build_dir/$dsc");
+ if (!$pipe) {
+ $self->log_error("unable to open pipe\n");
+ return 0;
+ }
+
+ my $pdsc = Dpkg::Control->new(type => CTRL_PKG_SRC);
+ $pdsc->set_options(allow_pgp => 1);
+ if (!$pdsc->parse($pipe, "$build_dir/$dsc")) {
+ $self->log_error("Error parsing $build_dir/$dsc\n");
+ return 0;
+ }
+
+ close($pipe);
+
+ $build_depends = $pdsc->{'Build-Depends'};
+ $build_depends_arch = $pdsc->{'Build-Depends-Arch'};
+ $build_depends_indep = $pdsc->{'Build-Depends-Indep'};
+ $build_conflicts = $pdsc->{'Build-Conflicts'};
+ $build_conflicts_arch = $pdsc->{'Build-Conflicts-Arch'};
+ $build_conflicts_indep = $pdsc->{'Build-Conflicts-Indep'};
+ $dscarchs = $pdsc->{'Architecture'};
+ $dscpkg = $pdsc->{'Source'};
+ $dscver = $pdsc->{'Version'};
+
+ $self->set_version("${dscpkg}_${dscver}");
+
+ $build_depends =~ s/\n\s+/ /g if defined $build_depends;
+ $build_depends_arch =~ s/\n\s+/ /g if defined $build_depends_arch;
+ $build_depends_indep =~ s/\n\s+/ /g if defined $build_depends_indep;
+ $build_conflicts =~ s/\n\s+/ /g if defined $build_conflicts;
+ $build_conflicts_arch =~ s/\n\s+/ /g if defined $build_conflicts_arch;
+ $build_conflicts_indep =~ s/\n\s+/ /g if defined $build_conflicts_indep;
+
+ $self->set('Build Depends', $build_depends);
+ $self->set('Build Depends Arch', $build_depends_arch);
+ $self->set('Build Depends Indep', $build_depends_indep);
+ $self->set('Build Conflicts', $build_conflicts);
+ $self->set('Build Conflicts Arch', $build_conflicts_arch);
+ $self->set('Build Conflicts Indep', $build_conflicts_indep);
+
+ $self->set('Dsc Architectures', $dscarchs);
+
+ # we set up the following filters this late because the user might only
+ # have specified a source package name to build without a version in which
+ # case we only get to know the final build directory now
+ my $filter;
+ $filter = $self->get('Build Dir') . '/' . $self->get('DSC Dir');
+ $filter =~ s;^/;;;
+ $self->build_log_filter($filter, 'PKGBUILDDIR');
+ $filter = $self->get('Build Dir');
+ $filter =~ s;^/;;;
+ $self->build_log_filter($filter, 'BUILDDIR');
+
+ return 1;
+}
+
+sub check_architectures {
+ my $self = shift;
+ my $resolver = $self->get('Dependency Resolver');
+ my $dscarchs = $self->get('Dsc Architectures');
+ my $build_arch = $self->get('Build Arch');
+ my $host_arch = $self->get('Host Arch');
+ my $session = $self->get('Session');
+
+ $self->log_subsection("Check architectures");
+ # Check for cross-arch dependencies
+ # parse $build_depends* for explicit :arch and add the foreign arches, as needed
+ #
+ # This check only looks at the immediate build dependencies. This could
+ # fail in a future where a foreign architecture direct build dependency of
+ # architecture X depends on another foreign architecture package of
+ # architecture Y. Architecture Y would not be added through this check as
+ # sbuild will not traverse the dependency graph. Doing so would be very
+ # complicated as new architectures would have to be added to a dependency
+ # solver like dose3 as the graph is traversed and new architectures are
+ # found.
+ sub get_explicit_arches
+ {
+ my $visited_deps = pop;
+ my @deps = @_;
+
+ my %set;
+ for my $dep (@deps)
+ {
+ # Break any recursion in the deps data structure (is this overkill?)
+ next if !defined $dep;
+ my $id = ref($dep) ? refaddr($dep) : "str:$dep";
+ next if $visited_deps->{$id};
+ $visited_deps->{$id} = 1;
+
+ if ( exists( $dep->{archqual} ) )
+ {
+ if ( $dep->{archqual} )
+ {
+ $set{$dep->{archqual}} = 1;
+ }
+ }
+ else
+ {
+ for my $key (get_explicit_arches($dep->get_deps,
+ $visited_deps)) {
+ $set{$key} = 1;
+ }
+ }
+ }
+
+ return keys %set;
+ }
+
+ # we don't need to look at build conflicts here because conflicting with a
+ # package of an explicit architecture does not mean that we need to enable
+ # that architecture in the chroot
+ my $build_depends_concat =
+ deps_concat( grep {defined $_} ($self->get('Build Depends'),
+ $self->get('Build Depends Arch'),
+ $self->get('Build Depends Indep')));
+ my $merged_depends = deps_parse( $build_depends_concat,
+ reduce_arch => 1,
+ host_arch => $self->get('Host Arch'),
+ build_arch => $self->get('Build Arch'),
+ build_dep => 1,
+ reduce_profiles => 1,
+ build_profiles => [ split / /, $self->get('Build Profiles') ]);
+ if( !defined $merged_depends ) {
+ my $msg = "Error! deps_parse() couldn't parse the Build-Depends '$build_depends_concat'";
+ $self->log_error("$msg\n");
+ return 0;
+ }
+
+ my @explicit_arches = get_explicit_arches($merged_depends, {});
+ my @foreign_arches = grep {$_ !~ /any|all|native/} @explicit_arches;
+ my $added_any_new;
+ for my $foreign_arch(@foreign_arches)
+ {
+ $resolver->add_foreign_architecture($foreign_arch);
+ $added_any_new = 1;
+ }
+
+ my @keylist=keys %{$resolver->get('Initial Foreign Arches')};
+ $self->log('Initial Foreign Architectures: ' . join ' ', @keylist, "\n")
+ if @keylist;
+ $self->log('Foreign Architectures in build-deps: '. join ' ', @foreign_arches, "\n\n")
+ if @foreign_arches;
+
+ $self->run_chroot_update() if $added_any_new;
+
+ # At this point, all foreign architectures should have been added to dpkg.
+ # Thus, we now examine, whether the packages passed via --extra-package
+ # can even be considered by dpkg inside the chroot with respect to their
+ # architecture.
+
+ # Retrieve all foreign architectures from the chroot. We need to do this
+ # step because the user might've added more foreign arches to the chroot
+ # beforehand.
+ my @all_foreign_arches = split /\s+/, $session->read_command({
+ COMMAND => ['dpkg', '--print-foreign-architectures'],
+ USER => $self->get_conf('USERNAME'),
+ });
+ # we use an anonymous subroutine so that the referenced variables are
+ # automatically rebound to their current values
+ my $check_deb_arch = sub {
+ my $pkg = shift;
+ # Investigate the Architecture field of the binary package
+ my $arch = $self->get('Host')->read_command({
+ COMMAND => ['dpkg-deb', '--field', Cwd::abs_path($pkg), 'Architecture'],
+ USER => $self->get_conf('USERNAME')
+ });
+ if (!defined $arch) {
+ $self->log_warning("Failed to run dpkg-deb on $pkg. Skipping...\n");
+ next;
+ }
+ chomp $arch;
+ # Only packages that are Architecture:all, the native architecture or
+ # one of the configured foreign architectures are allowed.
+ if ($arch ne 'all' and $arch ne $build_arch
+ and !isin($arch, @all_foreign_arches)) {
+ $self->log_warning("Extra package $pkg of architecture $arch cannot be installed in the chroot\n");
+ }
+ };
+ for my $deb (@{$self->get_conf('EXTRA_PACKAGES')}) {
+ if (-f $deb) {
+ &$check_deb_arch($deb);
+ } elsif (-d $deb) {
+ opendir(D, $deb);
+ while (my $f = readdir(D)) {
+ next if (! -f "$deb/$f");
+ next if ("$deb/$f" !~ /\.deb$/);
+ &$check_deb_arch("$deb/$f");
+ }
+ closedir(D);
+ } else {
+ $self->log_warning("$deb is neither a regular file nor a directory. Skipping...\n");
+ }
+ }
+
+ # Check package arch makes sense to build
+ if (!$dscarchs) {
+ $self->log_warning("dsc has no Architecture: field -- skipping arch check!\n");
+ } elsif ($self->get_conf('BUILD_SOURCE')) {
+ # If the source package is to be built, then we do not need to check
+ # if any of the source package's architectures can be built given the
+ # current host architecture because then no matter the Architectures
+ # field, at least the source package will end up getting built.
+ } else {
+ my $valid_arch;
+ for my $a (split(/\s+/, $dscarchs)) {
+ # Check architecture wildcard matching with dpkg inside the chroot
+ # to avoid situations in which dpkg outside the chroot doesn't
+ # know about a new architecture yet
+ my $command = <<"EOF";
+ use strict;
+ use warnings;
+ use Dpkg::Arch;
+ if (Dpkg::Arch::debarch_is('$host_arch', '$a')) {
+ exit 0;
+ }
+ exit 1;
+EOF
+ $session->run_command(
+ { COMMAND => ['perl',
+ '-e',
+ $command],
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+ if ($? == 0) {
+ $valid_arch = 1;
+ last;
+ }
+ }
+ if ($dscarchs ne "any" && !($valid_arch) &&
+ !($dscarchs =~ /\ball\b/ && $self->get_conf('BUILD_ARCH_ALL')) ) {
+ my $msg = "dsc: $host_arch not in arch list or does not match any arch wildcards: $dscarchs -- skipping";
+ $self->log_error("$msg\n");
+ Sbuild::Exception::Build->throw(error => $msg,
+ status => "skipped",
+ failstage => "arch-check");
+ return 0;
+ }
+ }
+
+ $self->log("Arch check ok ($host_arch included in $dscarchs)\n");
+
+ return 1;
+}
+
+# Subroutine that runs any command through the system (i.e. not through the
+# chroot. It takes a string of a command with arguments to run along with
+# arguments whether to save STDOUT and/or STDERR to the log stream
+sub run_command {
+ my $self = shift;
+ my $command = shift;
+ my $log_output = shift;
+ my $log_error = shift;
+ my $chroot = shift;
+
+ # Used to determine if we are to log from commands
+ my ($out, $err, $defaults);
+
+ # Run the command and save the exit status
+ if (!$chroot)
+ {
+ $defaults = $self->get('Host')->{'Defaults'};
+ $out = $defaults->{'STREAMOUT'} if ($log_output);
+ $err = $defaults->{'STREAMERR'} if ($log_error);
+
+ my %args = (PRIORITY => 0,
+ STREAMOUT => $out,
+ STREAMERR => $err);
+ if(ref $command) {
+ $args{COMMAND} = \@{$command};
+ $args{COMMAND_STR} = "@{$command}";
+ } else {
+ $args{COMMAND} = [split('\s+', $command)];
+ $args{COMMAND_STR} = $command;
+ }
+
+ $self->get('Host')->run_command( \%args );
+ } else {
+ $defaults = $self->get('Session')->{'Defaults'};
+ $out = $defaults->{'STREAMOUT'} if ($log_output);
+ $err = $defaults->{'STREAMERR'} if ($log_error);
+
+ my %args = (USER => 'root',
+ PRIORITY => 0,
+ STREAMOUT => $out,
+ STREAMERR => $err);
+ if(ref $command) {
+ $args{COMMAND} = \@{$command};
+ $args{COMMAND_STR} = "@{$command}";
+ } else {
+ $args{COMMAND} = [split('\s+', $command)];
+ $args{COMMAND_STR} = $command;
+ }
+
+ $self->get('Session')->run_command( \%args );
+ }
+ my $status = $?;
+
+ # Check if the command failed
+ if ($status != 0) {
+ return 0;
+ }
+ return 1;
+}
+
+# Subroutine that processes external commands to be run during various stages of
+# an sbuild run. We also ask if we want to log any output from the commands
+sub run_external_commands {
+ my $self = shift;
+ my $stage = shift;
+
+ my $log_output = $self->get_conf('LOG_EXTERNAL_COMMAND_OUTPUT');
+ my $log_error = $self->get_conf('LOG_EXTERNAL_COMMAND_ERROR');
+
+ # Return success now unless there are commands to run
+ return 1 unless (${$self->get_conf('EXTERNAL_COMMANDS')}{$stage});
+
+ # Determine which set of commands to run based on the parameter $stage
+ my @commands = @{${$self->get_conf('EXTERNAL_COMMANDS')}{$stage}};
+ return 1 if !(@commands);
+
+ # Create appropriate log message and determine if the commands are to be
+ # run inside the chroot or not, and as root or not.
+ my $chroot = 0;
+ if ($stage eq "pre-build-commands") {
+ $self->log_subsection("Pre Build Commands");
+ } elsif ($stage eq "chroot-setup-commands") {
+ $self->log_subsection("Chroot Setup Commands");
+ $chroot = 1;
+ } elsif ($stage eq "chroot-update-failed-commands") {
+ $self->log_subsection("Chroot-update Install Failed Commands");
+ $chroot = 1;
+ } elsif ($stage eq "build-deps-failed-commands") {
+ $self->log_subsection("Build-Deps Install Failed Commands");
+ $chroot = 1;
+ } elsif ($stage eq "build-failed-commands") {
+ $self->log_subsection("Generic Build Failed Commands");
+ $chroot = 1;
+ } elsif ($stage eq "starting-build-commands") {
+ $self->log_subsection("Starting Timed Build Commands");
+ $chroot = 1;
+ } elsif ($stage eq "finished-build-commands") {
+ $self->log_subsection("Finished Timed Build Commands");
+ $chroot = 1;
+ } elsif ($stage eq "chroot-cleanup-commands") {
+ $self->log_subsection("Chroot Cleanup Commands");
+ $chroot = 1;
+ } elsif ($stage eq "post-build-commands") {
+ $self->log_subsection("Post Build Commands");
+ } elsif ($stage eq "post-build-failed-commands") {
+ $self->log_subsection("Post Build Failed Commands");
+ }
+
+ # Run each command, substituting the various percent escapes (like
+ # %SBUILD_DSC) from the commands to run with the appropriate subsitutions.
+ my $hostarch = $self->get('Host Arch');
+ my $buildarch = $self->get('Build Arch');
+ my $build_dir = $self->get('Build Dir');
+ my $shell_cmd = "bash -i </dev/tty >/dev/tty 2>/dev/tty";
+ my %percent = (
+ "%" => "%",
+ "a" => $hostarch, "SBUILD_HOST_ARCH" => $hostarch,
+ "SBUILD_BUILD_ARCH" => $buildarch,
+ "b" => $build_dir, "SBUILD_BUILD_DIR" => $build_dir,
+ "s" => $shell_cmd, "SBUILD_SHELL" => $shell_cmd,
+ );
+ if ($self->get('Changes File')) {
+ my $changes = $self->get('Changes File');
+ $percent{c} = $changes;
+ $percent{SBUILD_CHANGES} = $changes;
+ }
+ # In case set_version has not been run yet, we do not know the dsc file or
+ # directory yet. This can happen if the user only specified a source
+ # package name without a version on the command line.
+ if ($self->get('DSC Dir')) {
+ my $dsc = $self->get('DSC');
+ $percent{d} = $dsc;
+ $percent{SBUILD_DSC} = $dsc;
+ my $pkgbuild_dir = $build_dir . '/' . $self->get('DSC Dir');
+ $percent{p} = $pkgbuild_dir;
+ $percent{SBUILD_PKGBUILD_DIR} = $pkgbuild_dir;
+ }
+ if ($chroot == 0) {
+ my $chroot_dir = $self->get('Session')->get('Location');
+ $percent{r} = $chroot_dir;
+ $percent{SBUILD_CHROOT_DIR} = $chroot_dir;
+ # the %SBUILD_CHROOT_EXEC escape is only defined when the command is
+ # to be run outside the chroot
+ my $exec_string = $self->get('Session')->get_internal_exec_string();
+ $percent{e} = $exec_string;
+ $percent{SBUILD_CHROOT_EXEC} = $exec_string;
+ }
+ # Our escapes pattern, with longer escapes first, then sorted lexically.
+ my $keyword_pat = join("|",
+ sort {length $b <=> length $a || $a cmp $b} keys %percent);
+ my $returnval = 1;
+ foreach my $command (@commands) {
+
+ my $substitute = sub {
+ foreach(@_) {
+ if (/\%SBUILD_CHROOT_DIR/ || /\%r/) {
+ $self->log_warning("The %SBUILD_CHROOT_DIR and %r percentage escapes are deprecated and should not be used anymore. Please use %SBUILD_CHROOT_EXEC or %e instead.");
+ }
+ s{
+ # Match a percent followed by a valid keyword
+ \%($keyword_pat)
+ }{
+ # Substitute with the appropriate value only if it's defined
+ $percent{$1} || $&
+ }msxge;
+ }
+ };
+
+ my $command_str;
+ if( ref $command ) {
+ $substitute->(@{$command});
+ $command_str = join(" ", @{$command});
+ } else {
+ $substitute->($command);
+ $command_str = $command;
+ }
+
+ $self->log_subsubsection("$command_str");
+
+ $returnval = $self->run_command($command, $log_output, $log_error, $chroot);
+ $self->log("\n");
+ if (!$returnval) {
+ $self->log_error("Command '$command_str' failed to run.\n");
+ # do not run any other commands of this type after the first
+ # failure
+ last;
+ } else {
+ $self->log_info("Finished running '$command_str'.\n");
+ }
+ }
+ $self->log("\nFinished processing commands.\n");
+ $self->log_sep();
+ return $returnval;
+}
+
+sub run_lintian {
+ my $self = shift;
+ my $session = $self->get('Session');
+
+ return 1 unless ($self->get_conf('RUN_LINTIAN'));
+ $self->set('Lintian Reason', 'error');
+
+ if (!defined($session)) {
+ $self->log_error("Session is undef. Cannot run lintian.\n");
+ return 0;
+ }
+
+ $self->log_subsubsection("lintian");
+
+ my $build_dir = $self->get('Build Dir');
+ my $resolver = $self->get('Dependency Resolver');
+ my $lintian = $self->get_conf('LINTIAN');
+ my $changes = $self->get_changes();
+ if (!defined($changes)) {
+ $self->log_error(".changes is undef. Cannot run lintian.\n");
+ return 0;
+ }
+
+ my @lintian_command = ($lintian);
+ push @lintian_command, @{$self->get_conf('LINTIAN_OPTIONS')} if
+ ($self->get_conf('LINTIAN_OPTIONS'));
+ push @lintian_command, $changes;
+
+ # If the source package was not instructed to be built, then it will not
+ # be part of the .changes file and thus, the .dsc has to be passed to
+ # lintian in addition to the .changes file.
+ if (!$self->get_conf('BUILD_SOURCE')) {
+ my $dsc = $self->get('DSC File');
+ push @lintian_command, $dsc;
+ }
+
+ $resolver->add_dependencies('LINTIAN', 'lintian:native', "", "", "", "", "");
+ return 1 unless $resolver->install_deps('lintian', 'LINTIAN');
+
+ $self->log("Running lintian...\n");
+
+ # we are not using read_command() because we also need the output for
+ # non-zero exit codes
+ my $pipe = $session->pipe_command(
+ { COMMAND => \@lintian_command,
+ PRIORITY => 0,
+ DIR => $self->get('Build Dir'),
+ PIPE => "in"
+ });
+ if (!$pipe) {
+ $self->log_error("Failed to exec Lintian: $!\n");
+ return 0;
+ }
+ my $lintian_output = "";
+ while (my $line = <$pipe>) {
+ $self->log($line);
+ $lintian_output .= $line;
+ }
+ close $pipe;
+
+ $self->log("\n");
+ if ($?) {
+ my $status = $? >> 8;
+ my $why = "unknown reason";
+ $self->set('Lintian Reason', 'fail') if ($status == 1);
+ $why = "runtime error" if ($status == 2);
+ $why = "policy violation" if ($status == 1);
+ $why = "received signal " . $? & 127 if ($? & 127);
+ $self->log_error("Lintian run failed ($why)\n");
+
+ return 0;
+ } else {
+ $self->set('Lintian Reason', 'pass');
+ if ($lintian_output =~ m/^I: /m) {
+ $self->set('Lintian Reason', 'info');
+ }
+ if ($lintian_output =~ m/^W: /m) {
+ $self->set('Lintian Reason', 'warn');
+ }
+ }
+
+ $self->log_info("Lintian run was successful.\n");
+ return 1;
+}
+
+sub run_piuparts {
+ my $self = shift;
+
+ return 1 unless ($self->get_conf('RUN_PIUPARTS'));
+ $self->set('Piuparts Reason', 'fail');
+
+ $self->log_subsubsection("piuparts");
+
+ my $piuparts = $self->get_conf('PIUPARTS');
+ my @piuparts_command;
+ # The default value is the empty array.
+ # If the value is the default (empty array) prefix with 'sudo --'
+ # If the value is a non-empty array, prefix with its values except if the
+ # first value is an empty string in which case, prefix with nothing
+ # If the value is not an array, prefix with that scalar except if the
+ # scalar is the empty string in which case, prefix with nothing
+ if (ref($self->get_conf('PIUPARTS_ROOT_ARGS')) eq "ARRAY") {
+ if (scalar(@{$self->get_conf('PIUPARTS_ROOT_ARGS')})) {
+ if (@{$self->get_conf('PIUPARTS_ROOT_ARGS')}[0] ne '') {
+ push @piuparts_command, @{$self->get_conf('PIUPARTS_ROOT_ARGS')};
+ }
+ } else {
+ push @piuparts_command, 'sudo', '--';
+ }
+ } else {
+ if ($self->get_conf('PIUPARTS_ROOT_ARGS') ne '') {
+ push @piuparts_command, $self->get_conf('PIUPARTS_ROOT_ARGS');
+ }
+ }
+ push @piuparts_command, $piuparts;
+ push @piuparts_command, @{$self->get_conf('PIUPARTS_OPTIONS')} if
+ ($self->get_conf('PIUPARTS_OPTIONS'));
+ push @piuparts_command, $self->get('Changes File');
+ $self->get('Host')->run_command(
+ { COMMAND => \@piuparts_command,
+ PRIORITY => 0,
+ });
+ my $status = $? >> 8;
+
+ # We must check for Ctrl+C (and other aborting signals) directly after
+ # running the command so that we do not mark the piuparts run as successful
+ # (the exit status will be zero)
+ $self->check_abort();
+
+ $self->log("\n");
+
+ if ($status == 0) {
+ $self->set('Piuparts Reason', 'pass');
+ } else {
+ $self->log_error("Piuparts run failed.\n");
+ return 0;
+ }
+
+ $self->log_info("Piuparts run was successful.\n");
+ return 1;
+}
+
+sub run_autopkgtest {
+ my $self = shift;
+
+ return 1 unless ($self->get_conf('RUN_AUTOPKGTEST'));
+
+ $self->set('Autopkgtest Reason', 'fail');
+
+ $self->log_subsubsection("autopkgtest");
+
+ my $session = $self->get('Session');
+
+ # sbuild used to check whether debian/tests/control exists and would not
+ # run autopkgtest at all if it didn't. This is wrong behaviour because
+ # even packages without a debian/tests/control or packages without a
+ # Testsuite: field in debian/control might still have autopkgtests as they
+ # are generated by autodep8. We will not attempt to recreate the autodep8
+ # heuristics here and thus we will always run autopkgtest if
+ # RUN_AUTOPKGTEST was set to true. Also see
+ # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=916924
+
+ my $autopkgtest = $self->get_conf('AUTOPKGTEST');
+ my @autopkgtest_command;
+ # The default value is the empty array.
+ # If the value is the default (empty array) prefix with 'sudo --'
+ # If the value is a non-empty array, prefix with its values except if the
+ # first value is an empty string in which case, prefix with nothing
+ # If the value is not an array, prefix with that scalar except if the
+ # scalar is the empty string in which case, prefix with nothing
+ if (ref($self->get_conf('AUTOPKGTEST_ROOT_ARGS')) eq "ARRAY") {
+ if (scalar(@{$self->get_conf('AUTOPKGTEST_ROOT_ARGS')}) == 0) {
+ push @autopkgtest_command, 'sudo', '--';
+ } elsif (@{$self->get_conf('AUTOPKGTEST_ROOT_ARGS')}[0] eq '') {
+ # do nothing if the first array element is the empty string
+ } else {
+ push @autopkgtest_command, @{$self->get_conf('AUTOPKGTEST_ROOT_ARGS')};
+ }
+ } elsif ($self->get_conf('AUTOPKGTEST_ROOT_ARGS') eq '') {
+ # do nothing if the configuration value is the empty string
+ } else {
+ push @autopkgtest_command, $self->get_conf('AUTOPKGTEST_ROOT_ARGS');
+ }
+ push @autopkgtest_command, $autopkgtest;
+ my $tmpdir;
+ my @cwd_files;
+ # If the source package was not instructed to be built, then it will not
+ # be part of the .changes file and thus, the .dsc has to be passed to
+ # autopkgtest in addition to the .changes file.
+ if (!$self->get_conf('BUILD_SOURCE')) {
+ my $dsc = $self->get('DSC');
+ # If the source package was downloaded by sbuild, then the .dsc
+ # and the files it references have to be made available to the
+ # host
+ if (! -f $dsc || ! -r $dsc) {
+ my $build_dir = $self->get('Build Dir');
+ $tmpdir = mkdtemp("/tmp/tmp.sbuild.XXXXXXXXXX");
+ if (!$session->copy_from_chroot("$build_dir/$dsc", "$tmpdir/$dsc")) {
+ $self->log_error("cannot copy .dsc from chroot\n");
+ rmdir $tmpdir;
+ return 0;
+ }
+ @cwd_files = dsc_files("$tmpdir/$dsc");
+ foreach (@cwd_files) {
+ if (!$session->copy_from_chroot("$build_dir/$_", "$tmpdir/$_")) {
+ $self->log_error("cannot copy $_ from chroot\n");
+ unlink "$tmpdir/$.dsc";
+ foreach (@cwd_files) {
+ unlink "$tmpdir/$_" if -f "$tmpdir/$_";
+ }
+ rmdir $tmpdir;
+ return 0;
+ }
+ }
+ $dsc = "$tmpdir/$dsc";
+ }
+ push @autopkgtest_command, $dsc;
+ }
+ push @autopkgtest_command, $self->get('Changes File');
+ if (scalar(@{$self->get_conf('AUTOPKGTEST_OPTIONS')})) {
+ push @autopkgtest_command, @{$self->get_conf('AUTOPKGTEST_OPTIONS')};
+ } else {
+ push @autopkgtest_command, '--', 'null';
+ }
+ $self->get('Host')->run_command(
+ { COMMAND => \@autopkgtest_command,
+ PRIORITY => 0,
+ });
+ my $status = $? >> 8;
+ # if the source package wasn't built and also initially downloaded by
+ # sbuild, then the temporary directory that was created must be removed
+ if (defined $tmpdir) {
+ my $dsc = $self->get('DSC');
+ unlink "$tmpdir/$dsc";
+ foreach (@cwd_files) {
+ unlink "$tmpdir/$_";
+ }
+ rmdir $tmpdir;
+ }
+
+ # We must check for Ctrl+C (and other aborting signals) directly after
+ # running the command so that we do not mark the autopkgtest as successful
+ # (the exit status will be zero)
+ # But we must check only after the temporary directory has been removed.
+ $self->check_abort();
+
+ $self->log("\n");
+
+ if ($status == 0 || $status == 2) { # 2 is "at least one test was skipped (or at least one flaky test failed)"
+ $self->set('Autopkgtest Reason', 'pass');
+ } elsif ($status == 8) {
+ $self->set('Autopkgtest Reason', 'no tests');
+ } else {
+ # fail if neither all tests passed nor was the package without tests
+ $self->log_error("Autopkgtest run failed.\n");
+ return 0;
+ }
+
+ $self->log_info("Autopkgtest run was successful.\n");
+ return 1;
+}
+
+sub explain_bd_uninstallable {
+ my $self = shift;
+
+ my $resolver = $self->get('Dependency Resolver');
+
+ my $dummy_pkg_name = $resolver->get_sbuild_dummy_pkg_name('main');
+
+ if (!defined $self->get_conf('BD_UNINSTALLABLE_EXPLAINER')) {
+ return 0;
+ } elsif ($self->get_conf('BD_UNINSTALLABLE_EXPLAINER') eq '') {
+ return 0;
+ } elsif ($self->get_conf('BD_UNINSTALLABLE_EXPLAINER') eq 'apt') {
+ my (@instd, @rmvd);
+ my @apt_args = ('--simulate', \@instd, \@rmvd, 'install', $dummy_pkg_name,
+ '-oDebug::pkgProblemResolver=true', '-oDebug::pkgDepCache::Marker=1',
+ '-oDebug::pkgDepCache::AutoInstall=1', '-oDebug::BuildDeps=1'
+ );
+ $resolver->run_apt(@apt_args);
+ } elsif ($self->get_conf('BD_UNINSTALLABLE_EXPLAINER') eq 'dose3') {
+ # To retrieve all Packages files apt knows about we use "apt-get
+ # indextargets" and "apt-helper cat-file". The former is able to
+ # report the filesystem path of all input Packages files. The latter
+ # is able to decompress the files if necessary.
+ #
+ # We do not use "apt-cache dumpavail" or convert the EDSP output to a
+ # Packages file because that would make the package selection subject
+ # to apt pinning. This limitation would be okay if there was only the
+ # apt resolver but since there also exists the aptitude and aspcud
+ # resolvers which are able to find solution without pinning
+ # restrictions, we don't want to limit ourselves by it. In cases where
+ # apt cannot find a solution, this check is supposed to allow the user
+ # to know that choosing a different resolver might fix the problem.
+ $resolver->add_dependencies('DOSE3', 'dose-distcheck:native', "", "", "", "", "");
+ if (!$resolver->install_deps('dose3', 'DOSE3')) {
+ return 0;
+ }
+
+ my $session = $self->get('Session');
+ my $pipe_apt = $session->pipe_command({
+ COMMAND => [ 'apt-get', 'indextargets', '--format', '$(FILENAME)', 'Created-By: Packages' ],
+ USER => $self->get_conf('BUILD_USER'),
+ });
+ if (!$pipe_apt) {
+ $self->log_error("cannot open reading pipe from apt-get indextargets\n");
+ return 0;
+ }
+
+ my $host = $self->get_conf('HOST_ARCH');
+ my $build = $self->get_conf('BUILD_ARCH');
+ my @debforeignarg = ();
+ if ($build ne $host) {
+ @debforeignarg = ('--deb-foreign-archs', $host);
+ }
+
+ # - We run dose-debcheck instead of dose-builddebcheck because we want
+ # to check the dummy binary package created by sbuild instead of the
+ # original source package Build-Depends.
+ # - We use dose-debcheck instead of dose-distcheck because we cannot
+ # use the deb:// prefix on data from standard input.
+ my $pipe_dose = $session->pipe_command({
+ COMMAND => ['dose-debcheck',
+ '--checkonly', "$dummy_pkg_name:$host", '--verbose',
+ '--failures', '--successes', '--explain',
+ '--deb-native-arch', $self->get_conf('BUILD_ARCH'), @debforeignarg ],
+ PRIORITY => 0,
+ USER => $self->get_conf('BUILD_USER'),
+ PIPE => 'out'
+ });
+ if (!$pipe_dose) {
+ $self->log_error("cannot open writing pipe to dose-debcheck\n");
+ return 0;
+ }
+
+ # We parse file by file instead of concatenating all files because if
+ # there are many files, we might exceed the maximum command length and
+ # it avoids having to have the data from all Packages files in memory
+ # all at once. Working with a smaller Dpkg::Index structure should
+ # also result in faster store and retrieval times.
+ while (my $fname = <$pipe_apt>) {
+ chomp $fname;
+ my $pipe_cat = $session->pipe_command({
+ COMMAND => [ '/usr/lib/apt/apt-helper', 'cat-file', $fname ],
+ USER => $self->get_conf('BUILD_USER'),
+ });
+ if (!$pipe_cat) {
+ $self->log_error("cannot open reading pipe from apt-helper\n");
+ return 0;
+ }
+
+ # For native compilation we just pipe the output of apt-helper to
+ # dose3. For cross compilation we need to filter foreign
+ # architecture packages that are Essential:yes or
+ # Multi-Arch:foreign or otherwise dose3 might present a solution
+ # that installs foreign architecture Essential:yes or
+ # Multi-Arch:foreign packages.
+ if ($build eq $host) {
+ File::Copy::copy $pipe_cat, $pipe_dose;
+ } else {
+ my $key_func = sub {
+ return $_[0]->{Package} . ' ' . $_[0]->{Version} . ' ' . $_[0]->{Architecture};
+ };
+
+ my $index = Dpkg::Index->new(get_key_func=>$key_func);
+
+ if (!$index->parse($pipe_cat, 'apt-helper cat-file')) {
+ $self->log_error("Cannot parse output of apt-helper cat-file: $!\n");
+ return 0;
+ }
+
+ foreach my $key ($index->get_keys()) {
+ my $cdata = $index->get_by_key($key);
+ my $arch = $cdata->{'Architecture'} // '';
+ my $ess = $cdata->{'Essential'} // '';
+ my $ma = $cdata->{'Multi-Arch'} // '';
+ if ($arch ne 'all' && $arch ne $build
+ && ($ess eq 'yes' || $ma eq 'foreign')) {
+ next;
+ }
+ $cdata->output($pipe_dose);
+ print $pipe_dose "\n";
+ }
+ }
+
+ close($pipe_cat);
+ if (($? >> 8) != 0) {
+ $self->log_error("apt-helper failed\n");
+ return 0;
+ }
+ }
+
+ close $pipe_dose;
+ # - We expect an exit code of less than 64 of dose-debcheck. Any other
+ # exit code indicates abnormal program termination.
+ if (($? >> 8) >= 64) {
+ $self->log_error("dose-debcheck failed\n");
+ return 0;
+ }
+
+ close $pipe_apt;
+ if (($? >> 8) != 0) {
+ $self->log_error("apt-get indextargets failed\n");
+ return 0;
+ }
+
+
+ }
+
+ return 1;
+}
+
+sub build {
+ my $self = shift;
+
+ my $dscfile = $self->get('DSC File');
+ my $dscdir = $self->get('DSC Dir');
+ my $pkg = $self->get('Package');
+ my $build_dir = $self->get('Build Dir');
+ my $host_arch = $self->get('Host Arch');
+ my $build_arch = $self->get('Build Arch');
+ my $session = $self->get('Session');
+
+ my( $rv, $changes );
+ local( *PIPE, *F, *F2 );
+
+ $self->log_subsection("Build");
+ $self->set('This Space', 0);
+
+ my $tmpunpackdir = $dscdir;
+ $tmpunpackdir =~ s/-.*$/.orig.tmp-nest/;
+ $tmpunpackdir =~ s/_/-/;
+ $tmpunpackdir = "$build_dir/$tmpunpackdir";
+
+ $dscdir = "$build_dir/$dscdir";
+
+ $self->log_subsubsection("Unpack source");
+ if ($session->test_directory($dscdir) && $session->test_symlink($dscdir)) {
+ # if the package dir already exists but is a symlink, complain
+ $self->log_error("Cannot unpack source: a symlink to a directory with the\n".
+ "same name already exists.\n");
+ return 0;
+ }
+ my $dsccontent = $session->read_file("$build_dir/$dscfile");
+ if (!$dsccontent) {
+ $self->log_error("Cannot read $build_dir/$dscfile\n");
+ } else {
+ $self->log($dsccontent);
+ $self->log("\n");
+ }
+ if (!$session->test_directory($dscdir)) {
+ $self->set('Sub Task', "dpkg-source");
+ $session->run_command({
+ COMMAND => [$self->get_conf('DPKG_SOURCE'),
+ '-x', $dscfile, $dscdir],
+ USER => $self->get_conf('BUILD_USER'),
+ DIR => $build_dir,
+ PRIORITY => 0});
+ if ($?) {
+ $self->log_error("FAILED [dpkg-source died]\n");
+ Sbuild::Exception::Build->throw(error => "FAILED [dpkg-source died]",
+ failstage => "unpack");
+ }
+
+ if (!$session->chmod($dscdir, 'g-s,go+rX', { RECURSIVE => 1 })) {
+ $self->log_error("chmod -R g-s,go+rX $dscdir failed.\n");
+ Sbuild::Exception::Build->throw(error => "chmod -R g-s,go+rX $dscdir failed",
+ failstage => "unpack");
+ }
+ }
+ else {
+ $self->log_subsubsection("Check unpacked source");
+ # check if the unpacked tree is really the version we need
+ my $clog = $session->read_command(
+ { COMMAND => ['dpkg-parsechangelog'],
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0,
+ DIR => $dscdir});
+ if (!$clog) {
+ $self->log_error("unable to read from dpkg-parsechangelog\n");
+ Sbuild::Exception::Build->throw(error => "unable to read from dpkg-parsechangelog",
+ failstage => "check-unpacked-version");
+ }
+ $self->set('Sub Task', "dpkg-parsechangelog");
+
+ if ($clog !~ /^Version:\s*(.+)\s*$/mi) {
+ $self->log_error("dpkg-parsechangelog didn't print Version:\n");
+ Sbuild::Exception::Build->throw(error => "dpkg-parsechangelog didn't print Version:",
+ failstage => "check-unpacked-version");
+ }
+ }
+
+ $self->log_subsubsection("Check disk space");
+ chomp(my $current_usage = $session->read_command({ COMMAND => ["du", "-k", "-s", "$dscdir"]}));
+ if ($?) {
+ $self->log_error("du exited with non-zero exit status $?\n");
+ Sbuild::Exception::Build->throw(error => "du exited with non-zero exit status $?", failstage => "check-space");
+ }
+ $current_usage =~ /^(\d+)/;
+ $current_usage = $1;
+ if ($current_usage) {
+ my $pipe = $session->pipe_command({ COMMAND => ["df", "-k", "$dscdir"]});
+ my $free;
+ while (<$pipe>) {
+ $free = (split /\s+/)[3];
+ }
+ close $pipe;
+ if ($?) {
+ $self->log_error("df exited with non-zero exit status $?\n");
+ Sbuild::Exception::Build->throw(error => "df exited with non-zero exit status $?", failstage => "check-space");
+ }
+ if ($free < 2*$current_usage && $self->get_conf('CHECK_SPACE')) {
+ Sbuild::Exception::Build->throw(error => "Disk space is probably not sufficient for building.",
+ info => "Source needs $current_usage KiB, while $free KiB is free.)",
+ failstage => "check-space");
+ } else {
+ $self->log("Sufficient free space for build\n");
+ }
+ }
+
+ my $clogpipe = $session->pipe_command(
+ { COMMAND => ['dpkg-parsechangelog'],
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0,
+ DIR => $dscdir });
+ if (!$clogpipe) {
+ $self->log_error("unable to read from dpkg-parsechangelog\n");
+ Sbuild::Exception::Build->throw(error => "unable to read from dpkg-parsechangelog",
+ failstage => "check-unpacked-version");
+ }
+
+ my $clog = Dpkg::Control->new(type => CTRL_CHANGELOG);
+ if (!$clog->parse($clogpipe, "$dscdir/debian/changelog")) {
+ $self->log_error("unable to parse debian/changelog\n");
+ Sbuild::Exception::Build->throw(error => "unable to parse debian/changelog",
+ failstage => "check-unpacked-version");
+ }
+
+ close($clogpipe);
+
+ my $name = $clog->{Source};
+ my $version = $clog->{Version};
+ my $dists = $clog->{Distribution};
+ my $urgency = $clog->{Urgency};
+
+ if ($dists ne $self->get_conf('DISTRIBUTION')) {
+ $self->build_log_colour('yellow',
+ "^Distribution: " . $self->get_conf('DISTRIBUTION') . "\$");
+ }
+
+ if ($self->get_conf('BIN_NMU') || $self->get_conf('APPEND_TO_VERSION')
+ || defined $self->get_conf('BIN_NMU_CHANGELOG')) {
+ $self->log_subsubsection("Hack binNMU version");
+
+ my $text = $session->read_file("$dscdir/debian/changelog");
+
+ if (!$text) {
+ $self->log_error("Can't open debian/changelog -- no binNMU hack!\n");
+ Sbuild::Exception::Build->throw(error => "Can't open debian/changelog -- no binNMU hack: $!!",
+ failstage => "hack-binNMU");
+ }
+
+ my $NMUversion = $self->get('Version');
+
+ my $clogpipe = $session->get_write_file_handle("$dscdir/debian/changelog");
+
+ if (!$clogpipe) {
+ $self->log_error("Can't open debian/changelog for binNMU hack: $!\n");
+ Sbuild::Exception::Build->throw(error => "Can't open debian/changelog for binNMU hack: $!",
+ failstage => "hack-binNMU");
+ }
+ if (defined $self->get_conf('BIN_NMU_CHANGELOG')) {
+ my $clogentry = $self->get_conf('BIN_NMU_CHANGELOG');
+ # trim leading and trailing whitespace and linebreaks
+ $clogentry =~ s/^\s+|\s+$//g;
+ print $clogpipe $clogentry . "\n\n";
+ } else {
+ if (!$self->get_conf('MAINTAINER_NAME')) {
+ Sbuild::Exception::Build->throw(error => "No maintainer specified.",
+ info => 'When making changelog additions for a binNMU or appending a version suffix, a maintainer must be specified for the changelog entry e.g. using $maintainer_name, $uploader_name or $key_id, (or the equivalent command-line options)',
+ failstage => "check-space");
+ }
+
+ $dists = $self->get_conf('DISTRIBUTION');
+
+ print $clogpipe "$name ($NMUversion) $dists; urgency=low, binary-only=yes\n\n";
+ if ($self->get_conf('APPEND_TO_VERSION')) {
+ print $clogpipe " * Append ", $self->get_conf('APPEND_TO_VERSION'),
+ " to version number; no source changes\n";
+ }
+ if ($self->get_conf('BIN_NMU')) {
+ print $clogpipe " * Binary-only non-maintainer upload for $host_arch; ",
+ "no source changes.\n";
+ print $clogpipe " * ", join( " ", split( "\n", $self->get_conf('BIN_NMU') )), "\n";
+ }
+ print $clogpipe "\n";
+
+ # Earlier implementations used the date of the last changelog
+ # entry for the new entry so that Multi-Arch:same packages would
+ # be co-installable (their shared changelogs had to match). This
+ # is not necessary anymore as binNMU changelogs are now written
+ # into architecture specific paths. Re-using the date of the last
+ # changelog entry has the disadvantage that this will effect
+ # SOURCE_DATE_EPOCH which in turn will make the timestamps of the
+ # files in the new package equal to the last version which can
+ # confuse backup programs. By using the build date for the new
+ # binNMU changelog timestamp we make sure that the timestamps of
+ # changed files inside the new package advanced in comparison to
+ # the last version.
+ #
+ # The timestamp format has to follow Debian Policy §4.4
+ # https://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog
+ # which is the same format as `date -R`
+ my $date;
+ if (defined $self->get_conf('BIN_NMU_TIMESTAMP')) {
+ if ($self->get_conf('BIN_NMU_TIMESTAMP') =~ /^\+?[1-9]\d*$/) {
+ $date = strftime_c "%a, %d %b %Y %H:%M:%S +0000",
+ gmtime($self->get_conf('BIN_NMU_TIMESTAMP'));
+ } else {
+ $date = $self->get_conf('BIN_NMU_TIMESTAMP');
+ }
+ } else {
+ $date = strftime_c "%a, %d %b %Y %H:%M:%S +0000", gmtime();
+ }
+ print $clogpipe " -- " . $self->get_conf('MAINTAINER_NAME') . " $date\n\n";
+ }
+ print $clogpipe $text;
+ close($clogpipe);
+ $self->log("Created changelog entry for binNMU version $NMUversion\n");
+ }
+
+ if ($session->test_regular_file("$dscdir/debian/files")) {
+ local( *FILES );
+ my @lines;
+ my $FILES = $session->get_read_file_handle("$dscdir/debian/files");
+ chomp( @lines = <$FILES> );
+ close( $FILES );
+
+ $self->log_warning("After unpacking, there exists a file debian/files with the contents:\n");
+
+ $self->log_sep();
+ foreach (@lines) {
+ $self->log($_);
+ }
+ $self->log_sep();
+ $self->log("\n");
+
+ $self->log_info("This should be reported as a bug.\n");
+ $self->log_info("The file has been removed to avoid dpkg-genchanges errors.\n");
+
+ unlink "$dscdir/debian/files";
+ }
+
+ # Build tree not writable during build (except for the sbuild
+ # user performing the build).
+ if (!$session->chmod($self->get('Build Dir'), 'go-w', { RECURSIVE => 1 })) {
+ $self->log_error("chmod og-w " . $self->get('Build Dir') . " failed.\n");
+ return 0;
+ }
+
+ if (!$self->run_external_commands("starting-build-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute starting-build-commands",
+ failstage => "run-starting-build-commands");
+ }
+
+ $self->set('Build Start Time', time);
+ $self->set('Build End Time', $self->get('Build Start Time'));
+
+ if ($session->test_regular_file("/etc/ld.so.conf") &&
+ ! $session->test_regular_file_readable("/etc/ld.so.conf")) {
+ $session->chmod('/etc/ld.so.conf', 'a+r');
+
+ $self->log_subsubsection("Fix ld.so");
+ $self->log("ld.so.conf was not readable! Fixed.\n");
+ }
+
+ my $buildcmd = [];
+ if (length $self->get_conf('BUILD_ENV_CMND') ) {
+ push( @{$buildcmd}, $self->get_conf('BUILD_ENV_CMND') );
+ }
+ push (@{$buildcmd}, 'dpkg-buildpackage');
+
+ my $dpkgversion = version->new(0);
+ {
+ # we use pipe_command instead of read_command because we want to
+ # ignore non-zero exit code without printing an error message from
+ # dpkg versions before 1.20 which didn't have --robot
+ my $pipe = $session->pipe_command(
+ {
+ COMMAND => [ 'dpkg', '--robot', '--version' ],
+ STREAMERR => $devnull
+ }
+ );
+ chomp(
+ my $content = do { local $/; <$pipe> }
+ );
+ close $pipe;
+ if ( $? == 0 and $content =~ /^([0-9.]+)( .*)?$/ ) {
+ # dpkg is new enough for the --robot option
+ $dpkgversion = version->new($1);
+ }
+ }
+ # since dpkg 1.20.0
+ # will reset environment and umask to their vendor specific defaults
+ if ($dpkgversion >= "1.20.0") {
+ push (@{$buildcmd}, '--sanitize-env');
+ }
+
+ if ($host_arch ne $build_arch) {
+ push (@{$buildcmd}, '-a' . $host_arch);
+ }
+
+ if (length $self->get_conf('BUILD_PROFILES')) {
+ my $profiles = $self->get_conf('BUILD_PROFILES');
+ $profiles =~ tr/ /,/;
+ push (@{$buildcmd}, '-P' . $profiles);
+ }
+
+ if (defined $self->get_conf('PGP_OPTIONS')) {
+ if (ref($self->get_conf('PGP_OPTIONS')) eq 'ARRAY') {
+ push (@{$buildcmd}, @{$self->get_conf('PGP_OPTIONS')});
+ } elsif (length $self->get_conf('PGP_OPTIONS')) {
+ push (@{$buildcmd}, $self->get_conf('PGP_OPTIONS'));
+ }
+ }
+
+ if (defined $self->get_conf('SIGNING_OPTIONS')) {
+ if (ref($self->get_conf('SIGNING_OPTIONS')) eq 'ARRAY') {
+ push (@{$buildcmd}, @{$self->get_conf('SIGNING_OPTIONS')});
+ } elsif (length $self->get_conf('SIGNING_OPTIONS')) {
+ push (@{$buildcmd}, $self->get_conf('SIGNING_OPTIONS'));
+ }
+ }
+
+ use constant dpkgopt => [[["", "-B"], ["-A", "-b" ]], [["-S", "-G"], ["-g", ""]]];
+ my $binopt = dpkgopt->[$self->get_conf('BUILD_SOURCE')]
+ [$self->get_conf('BUILD_ARCH_ALL')]
+ [$self->get_conf('BUILD_ARCH_ANY')];
+ push (@{$buildcmd}, $binopt) if $binopt;
+ push (@{$buildcmd}, "-r" . $self->get_conf('FAKEROOT'));
+
+ if ($self->get_conf('DPKG_FILE_SUFFIX')) {
+ my $dpkg_version_ok = Dpkg::Version->new("1.18.11");
+ if ($self->get('Dpkg Version') >= $dpkg_version_ok) {
+ my $changes = $self->get_changes();
+ push (@{$buildcmd}, "--changes-option=-O../$changes");
+ my $buildinfo = $self->get_buildinfo();
+ push (@{$buildcmd}, "--buildinfo-option=-O../$buildinfo");
+ } else {
+ $self->log("Ignoring dpkg file suffix: dpkg version too old\n");
+ $self->set_conf('DPKG_FILE_SUFFIX',undef);
+ }
+ }
+
+ if (defined $self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')) {
+ push (@{$buildcmd}, @{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')});
+ }
+
+ # Set up additional build environment variables.
+ my %buildenv = %{$self->get_conf('BUILD_ENVIRONMENT')};
+ $buildenv{'PATH'} = $self->get_conf('PATH');
+ $buildenv{'LD_LIBRARY_PATH'} = $self->get_conf('LD_LIBRARY_PATH')
+ if defined($self->get_conf('LD_LIBRARY_PATH'));
+
+ # Add cross environment config
+ if ($host_arch ne $build_arch) {
+ $buildenv{'CONFIG_SITE'} = "/etc/dpkg-cross/cross-config." . $host_arch;
+ # when cross-building, only set "nocheck" if DEB_BUILD_OPTIONS
+ # was not already set. This allows overwriting the default by
+ # setting the DEB_BUILD_OPTIONS environment variable
+ if (!defined($ENV{'DEB_BUILD_OPTIONS'})) {
+ $ENV{'DEB_BUILD_OPTIONS'} = "nocheck";
+ }
+ }
+
+ # Explicitly add any needed environment to the environment filter
+ # temporarily for dpkg-buildpackage.
+ my @env_filter;
+ foreach my $envvar (keys %buildenv) {
+ push(@env_filter, "^$envvar\$");
+ }
+
+ # Dump build environment
+ $self->log_subsubsection("User Environment");
+ {
+ my $envcmd = $session->read_command(
+ { COMMAND => ['env'],
+ ENV => \%buildenv,
+ ENV_FILTER => \@env_filter,
+ USER => $self->get_conf('BUILD_USER'),
+ SETSID => 1,
+ PRIORITY => 0,
+ DIR => $dscdir
+ });
+ if (!$envcmd) {
+ $self->log_error("unable to open pipe\n");
+ Sbuild::Exception::Build->throw(error => "unable to open pipe",
+ failstage => "dump-build-env");
+ }
+
+ my @lines=sort(split /\n/, $envcmd);
+ foreach my $line (@lines) {
+ $self->log("$line\n");
+ }
+ }
+
+ $self->log_subsubsection("dpkg-buildpackage");
+ $self->log("Command: " . join(' ', @{$buildcmd}) . "\n");
+
+ my $command = {
+ COMMAND => $buildcmd,
+ ENV => \%buildenv,
+ ENV_FILTER => \@env_filter,
+ USER => $self->get_conf('BUILD_USER'),
+ SETSID => 1,
+ PRIORITY => 0,
+ DIR => $dscdir,
+ STREAMERR => \*STDOUT,
+ DISABLE_NETWORK => 1,
+ };
+
+ my $pipe = $session->pipe_command($command);
+ if (!$pipe) {
+ $self->log_error("unable to open pipe\n");
+ Sbuild::Exception::Build->throw(error => "unable to open pipe",
+ failstage => "dpkg-buildpackage");
+ }
+
+ $self->set('dpkg-buildpackage pid', $command->{'PID'});
+ $self->set('Sub Task', "dpkg-buildpackage");
+
+ my $timeout = $self->get_conf('INDIVIDUAL_STALLED_PKG_TIMEOUT')->{$pkg} ||
+ $self->get_conf('STALLED_PKG_TIMEOUT');
+ $timeout *= 60;
+ my $timed_out = 0;
+ my(@timeout_times, @timeout_sigs, $last_time);
+
+ local $SIG{'ALRM'} = sub {
+ my $pid = $self->get('dpkg-buildpackage pid');
+ my $signal = ($timed_out > 0) ? "KILL" : "TERM";
+ # negative pid to send to whole process group
+ kill "$signal", -$pid;
+
+ $timeout_times[$timed_out] = time - $last_time;
+ $timeout_sigs[$timed_out] = $signal;
+ $timed_out++;
+ $timeout = 5*60; # only wait 5 minutes until next signal
+ };
+
+ alarm($timeout);
+ # We do not use a while(<$pipe>) {} loop because that one would only read
+ # full lines (until $/ is reached). But we do not want to tie "activity"
+ # to receiving complete lines on standard output and standard error.
+ # Receiving any data should be sufficient for a process to signal that it
+ # is still active. Thus, instead of reading lines, we use sysread() which
+ # will return us data once it is available even if the data is not
+ # terminated by a newline. To still print correctly to the log, we collect
+ # unterminated strings into an accumulator and print them to the log once
+ # the newline shows up. This has the added advantage that we can now not
+ # only treat \n as producing new lines ($/ is limited to a single
+ # character) but can also produce new lines when encountering a \r as it
+ # is common for progress-meter output of long-running processes.
+ my $acc = "";
+ while(1) {
+ alarm($timeout);
+ $last_time = time;
+ # The buffer size is really arbitrary and just makes sure not to call
+ # this function too often if lots of data is produced by the build.
+ # The function will immediately return even with less data than the
+ # buffer size once it is available.
+ my $ret = sysread($pipe, my $buf, 1024);
+ # sysread failed - this for example happens when the build timeouted
+ # and is killed as a result
+ if (!defined $ret) {
+ last;
+ }
+ # A return value of 0 signals EOF
+ if ($ret == 0) {
+ last;
+ }
+ # We choose that lines shall not only be terminated by \n but that new
+ # log lines are also produced after encountering a \r.
+ # A negative limit is used to also produce trailing empty fields if
+ # required (think of multiple trailing empty lines).
+ my @parts = split /\r|\n/, $buf, -1;
+ my $numparts = scalar @parts;
+ if ($numparts == 1) {
+ # line terminator was not found
+ $acc .= $buf;
+ } elsif ($numparts >= 2) {
+ # first match needs special treatment as it needs to be
+ # concatenated with $acc
+ my $first = shift @parts;
+ $self->log($acc . $first . "\n");
+ my $last = pop @parts;
+ for (my $i = 0; $i < $numparts - 2; $i++) {
+ $self->log($parts[$i] . "\n");
+ }
+ # the last part is put into the accumulator. This might
+ # just be the empty string if $buf ended in a line
+ # terminator
+ $acc = $last;
+ }
+ }
+ # If the output didn't end with a line terminator, just print out the rest
+ # as we have it.
+ if ($acc ne "") {
+ $self->log($acc . "\n");
+ }
+ close($pipe);
+ alarm(0);
+ $rv = $?;
+ $self->set('dpkg-buildpackage pid', undef);
+
+ my $i;
+ for( $i = 0; $i < $timed_out; ++$i ) {
+ $self->log_error("Build killed with signal " . $timeout_sigs[$i] .
+ " after " . int($timeout_times[$i]/60) .
+ " minutes of inactivity\n");
+ }
+ $self->set('Build End Time', time);
+ $self->set('Pkg End Time', time);
+ $self->set('This Time', $self->get('Pkg End Time') - $self->get('Pkg Start Time'));
+ $self->set('This Time', 0) if $self->get('This Time') < 0;
+
+ $self->write_stats('build-time',
+ $self->get('Build End Time')-$self->get('Build Start Time'));
+ $self->write_stats('install-download-time',
+ $self->get('Install End Time')-$self->get('Install Start Time'));
+ my $finish_date = strftime_c "%FT%TZ", gmtime($self->get('Build End Time'));
+ $self->log_sep();
+ $self->log("Build finished at $finish_date\n");
+
+
+ if (!$self->run_external_commands("finished-build-commands")) {
+ Sbuild::Exception::Build->throw(error => "Failed to execute finished-build-commands",
+ failstage => "run-finished-build-commands");
+ }
+
+ my @space_files = ();
+
+ $self->log_subsubsection("Finished");
+ if ($rv) {
+ Sbuild::Exception::Build->throw(error => "Build failure (dpkg-buildpackage died)",
+ failstage => "build");
+ } else {
+ $self->log_info("Built successfully\n");
+
+ if ($session->test_regular_file_readable("$dscdir/debian/files")) {
+ my @files = $self->debian_files_list("$dscdir/debian/files");
+
+ foreach (@files) {
+ if (!$session->test_regular_file("$build_dir/$_")) {
+ $self->log_error("Package claims to have built ".basename($_).", but did not. This is a bug in the packaging.\n");
+ next;
+ }
+ if (/_all.u?deb$/ and not $self->get_conf('BUILD_ARCH_ALL')) {
+ $self->log_error("Package builds ".basename($_)." when binary-indep target is not called. This is a bug in the packaging.\n");
+ $session->unlink("$build_dir/$_");
+ next;
+ }
+ }
+ }
+
+ # Restore write access to build tree now build is complete.
+ if (!$session->chmod($self->get('Build Dir'), 'g+w', { RECURSIVE => 1 })) {
+ $self->log_error("chmod g+w " . $self->get('Build Dir') . " failed.\n");
+ return 0;
+ }
+
+ $self->log_subsection("Changes");
+
+ # we use an anonymous subroutine so that the referenced variables are
+ # automatically rebound to their current values
+ my $copy_changes = sub {
+ my $changes = shift;
+
+ my $F = $session->get_read_file_handle("$build_dir/$changes");
+ if (!$F) {
+ $self->log_error("cannot get read file handle for $build_dir/$changes\n");
+ Sbuild::Exception::Build->throw(error => "cannot get read file handle for $build_dir/$changes",
+ failstage => "parse-changes");
+ }
+ my $pchanges = Dpkg::Control->new(type => CTRL_FILE_CHANGES);
+ if (!$pchanges->parse($F, "$build_dir/$changes")) {
+ $self->log_error("cannot parse $build_dir/$changes\n");
+ Sbuild::Exception::Build->throw(error => "cannot parse $build_dir/$changes",
+ failstage => "parse-changes");
+ }
+ close($F);
+
+
+ if ($self->get_conf('OVERRIDE_DISTRIBUTION')) {
+ $pchanges->{Distribution} = $self->get_conf('DISTRIBUTION');
+ }
+
+ my $sys_build_dir = $self->get_conf('BUILD_DIR');
+ my $F2 = $session->get_write_file_handle("$build_dir/$changes.new");
+ if (!$F2) {
+ $self->log("Cannot create $build_dir/$changes.new\n");
+ $self->log("Distribution field may be wrong!!!\n");
+ if ($build_dir) {
+ if(!$session->copy_from_chroot("$build_dir/$changes", ".")) {
+ $self->log_error("Could not copy $build_dir/$changes to .\n");
+ }
+ }
+ } else {
+ $pchanges->output(\*STDOUT);
+ $pchanges->output(\*$F2);
+
+ close( $F2 );
+
+ $session->rename("$build_dir/$changes.new", "$build_dir/$changes");
+ if ($?) {
+ $self->log("$build_dir/$changes.new could not be " .
+ "renamed to $build_dir/$changes: $?\n");
+ $self->log("Distribution field may be wrong!!!");
+ }
+ if ($build_dir) {
+ if (!$session->copy_from_chroot("$build_dir/$changes", "$sys_build_dir")) {
+ $self->log("Could not copy $build_dir/$changes to $sys_build_dir");
+ }
+ }
+ }
+
+ return $pchanges;
+ };
+
+ $changes = $self->get_changes();
+ if (!defined($changes)) {
+ $self->log_error(".changes is undef. Cannot copy build results.\n");
+ return 0;
+ }
+ my @cfiles;
+ if ($session->test_regular_file_readable("$build_dir/$changes")) {
+ my(@do_dists, @saved_dists);
+ $self->log_subsubsection("$changes:");
+
+ my $pchanges = &$copy_changes($changes);
+ $self->set('Changes File', $self->get_conf('BUILD_DIR') . "/$changes");
+
+ my $checksums = Dpkg::Checksums->new();
+ $checksums->add_from_control($pchanges);
+
+ push(@cfiles, $checksums->get_files());
+
+ }
+ else {
+ $self->log_error("Can't find $changes -- can't dump info\n");
+ }
+
+ if ($self->get_conf('SOURCE_ONLY_CHANGES')) {
+ my $so_changes = $self->get('Package_SVersion') . "_source.changes";
+ $self->log_subsubsection("$so_changes:");
+ my $genchangescmd = ['dpkg-genchanges', '--build=source'];
+ if (defined $self->get_conf('SIGNING_OPTIONS')) {
+ if (ref($self->get_conf('SIGNING_OPTIONS')) eq 'ARRAY') {
+ push (@{$genchangescmd}, @{$self->get_conf('SIGNING_OPTIONS')});
+ } elsif (length $self->get_conf('SIGNING_OPTIONS')) {
+ push (@{$genchangescmd}, $self->get_conf('SIGNING_OPTIONS'));
+ }
+ }
+ my $changes_opts = $self->get_changes_opts();
+ if ($changes_opts) {
+ push (@{$genchangescmd}, @{$changes_opts});
+ }
+ my $cfile = $session->read_command(
+ { COMMAND => $genchangescmd,
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0,
+ DIR => $dscdir});
+ if (!$cfile) {
+ $self->log_error("dpkg-genchanges --build=source failed\n");
+ Sbuild::Exception::Build->throw(error => "dpkg-genchanges --build=source failed",
+ failstage => "source-only-changes");
+ }
+ if (!$session->write_file("$build_dir/$so_changes", $cfile)) {
+ $self->log_error("cannot write content to $build_dir/$so_changes\n");
+ Sbuild::Exception::Build->throw(error => "cannot write content to $build_dir/$so_changes",
+ failstage => "source-only-changes");
+ }
+
+ my $pchanges = &$copy_changes($so_changes);
+ }
+
+ $self->log_subsection("Buildinfo");
+
+ foreach (@cfiles) {
+ my $deb = "$build_dir/$_";
+ next if $deb !~ /\.buildinfo$/;
+ my $buildinfo = $session->read_file($deb);
+ if (!$buildinfo) {
+ $self->log_error("Cannot read $deb\n");
+ } else {
+ $self->log($buildinfo);
+ $self->log("\n");
+ }
+ }
+
+ $self->log_subsection("Package contents");
+
+ my @debcfiles = @cfiles;
+ foreach (@debcfiles) {
+ my $deb = "$build_dir/$_";
+ next if $deb !~ /(\Q$host_arch\E|all)\.(udeb|deb)$/;
+
+ $self->log_subsubsection("$_");
+ my $dpkg_info = $session->read_command({COMMAND => ["dpkg", "--info", $deb]});
+ if (!$dpkg_info) {
+ $self->log_error("Can't spawn dpkg: $! -- can't dump info\n");
+ }
+ else {
+ $self->log($dpkg_info);
+ }
+ $self->log("\n");
+ my $dpkg_contents = $session->read_command({COMMAND => ["sh", "-c", "dpkg --contents $deb 2>&1 | sort -k6"]});
+ if (!$dpkg_contents) {
+ $self->log_error("Can't spawn dpkg: $! -- can't dump info\n");
+ }
+ else {
+ $self->log($dpkg_contents);
+ }
+ $self->log("\n");
+ }
+
+ foreach (@cfiles) {
+ push( @space_files, $self->get_conf('BUILD_DIR') . "/$_");
+ if (!$session->copy_from_chroot("$build_dir/$_", $self->get_conf('BUILD_DIR'))) {
+ $self->log_error("Could not copy $build_dir/$_ to " . $self->get_conf('BUILD_DIR') . "\n");
+ }
+ }
+ }
+
+ $self->set('This Space', $self->check_space(@space_files));
+
+ return $rv == 0 ? 1 : 0;
+}
+
+# Produce a hash suitable for ENV export
+sub get_env ($$) {
+ my $self = shift;
+ my $prefix = shift;
+
+ sub _env_loop ($$$$) {
+ my ($env,$ref,$keysref,$prefix) = @_;
+
+ foreach my $key (keys( %{ $keysref } )) {
+ my $value = $ref->get($key);
+ next if (!defined($value));
+ next if (ref($value));
+ my $name = "${prefix}${key}";
+ $name =~ s/ /_/g;
+ $env->{$name} = $value;
+ }
+ }
+
+ my $envlist = {};
+ _env_loop($envlist, $self, $self, $prefix);
+ _env_loop($envlist, $self->get('Config'), $self->get('Config')->{'KEYS'}, "${prefix}CONF_");
+ return $envlist;
+}
+
+sub get_build_filename {
+ my $self=shift;
+ my $filetype=shift;
+ my $changes = $self->get('Package_SVersion');
+
+ if ($self->get_conf('BUILD_ARCH_ANY')) {
+ $changes .= '_' . $self->get('Host Arch');
+ } elsif ($self->get_conf('BUILD_ARCH_ALL')) {
+ $changes .= "_all";
+ } elsif ($self->get_conf('BUILD_SOURCE')) {
+ $changes .= "_source";
+ }
+
+ my $suffix = $self->get_conf('DPKG_FILE_SUFFIX');
+ $changes .= $suffix if ($suffix);
+
+ $changes .= '.' . $filetype;
+
+ return $changes;
+}
+
+sub get_changes {
+ my $self=shift;
+ return $self->get_build_filename("changes");
+}
+
+sub get_buildinfo {
+ my $self=shift;
+ return $self->get_build_filename("buildinfo");
+}
+
+sub check_space {
+ my $self = shift;
+ my @files = @_;
+ my $sum = 0;
+
+ my $dscdir = $self->get('DSC Dir');
+ return -1 unless (defined $dscdir);
+
+ my $build_dir = $self->get('Build Dir');
+ my $pkgbuilddir = "$build_dir/$dscdir";
+
+ # if the source package was not yet unpacked, we will not attempt to compute
+ # the required space.
+ return -1 unless ($self->get('Session')->test_directory($pkgbuilddir));
+
+ my ($space, $spacenum);
+
+ # get the required space for the unpacked source package in the chroot
+ $space = $self->get('Session')->read_command(
+ { COMMAND => ['du', '-k', '-s', $pkgbuilddir],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ DIR => '/'});
+
+ if (!$space) {
+ $self->log_error("Cannot determine space needed for $pkgbuilddir (du failed)\n");
+ return -1;
+ }
+ # remove the trailing path from the du output
+ if (($spacenum) = $space =~ /^(\d+)/) {
+ $sum += $spacenum;
+ } else {
+ $self->log_error("Cannot determine space needed for $pkgbuilddir (unexpected du output): $space\n");
+ return -1;
+ }
+
+ # get the required space for all produced build artifacts on the host
+ # running sbuild
+ foreach my $file (@files) {
+ $space = $self->get('Host')->read_command(
+ { COMMAND => ['du', '-k', '-s', $file],
+ USER => $self->get_conf('USERNAME'),
+ PRIORITY => 0,
+ DIR => '/'});
+
+ if (!$space) {
+ $self->log_error("Cannot determine space needed for $file (du failed): $!\n");
+ return -1;
+ }
+ # remove the trailing path from the du output
+ if (($spacenum) = $space =~ /^(\d+)/) {
+ $sum += $spacenum;
+ } else {
+ $self->log_error("Cannot determine space needed for $file (unexpected du output): $space\n");
+ return -1;
+ }
+ }
+
+ return $sum;
+}
+
+sub lock_file {
+ my $self = shift;
+ my $file = shift;
+ my $for_srcdep = shift;
+ my $lockfile = "$file.lock";
+ my $try = 0;
+
+ repeat:
+ if (!sysopen( F, $lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644 )){
+ if ($! == EEXIST) {
+ # lock file exists, wait
+ goto repeat if !open( F, "<$lockfile" );
+ my $line = <F>;
+ my ($pid, $user);
+ close( F );
+ if ($line !~ /^(\d+)\s+([\w\d.-]+)$/) {
+ $self->log_warning("Bad lock file contents ($lockfile) -- still trying\n");
+ }
+ else {
+ ($pid, $user) = ($1, $2);
+ if (kill( 0, $pid ) == 0 && $! == ESRCH) {
+ # process doesn't exist anymore, remove stale lock
+ $self->log_warning("Removing stale lock file $lockfile ".
+ "(pid $pid, user $user)\n");
+ unlink( $lockfile );
+ goto repeat;
+ }
+ }
+ ++$try;
+ if (!$for_srcdep && $try > $self->get_conf('MAX_LOCK_TRYS')) {
+ $self->log_warning("Lockfile $lockfile still present after " .
+ $self->get_conf('MAX_LOCK_TRYS') *
+ $self->get_conf('LOCK_INTERVAL') .
+ " seconds -- giving up\n");
+ return;
+ }
+ $self->log("Another sbuild process ($pid by $user) is currently installing or removing packages -- waiting...\n")
+ if $for_srcdep && $try == 1;
+ sleep $self->get_conf('LOCK_INTERVAL');
+ goto repeat;
+ }
+ $self->log_warning("Can't create lock file $lockfile: $!\n");
+ }
+
+ my $username = $self->get_conf('USERNAME');
+ F->print("$$ $username\n");
+ F->close();
+}
+
+sub unlock_file {
+ my $self = shift;
+ my $file = shift;
+ my $lockfile = "$file.lock";
+
+ unlink( $lockfile );
+}
+
+sub add_stat {
+ my $self = shift;
+ my $key = shift;
+ my $value = shift;
+
+ $self->get('Summary Stats')->{$key} = $value;
+}
+
+sub generate_stats {
+ my $self = shift;
+ my $resolver = $self->get('Dependency Resolver');
+
+ $self->add_stat('Job', $self->get('Job'));
+ $self->add_stat('Package', $self->get('Package'));
+ # If the package fails early, then the version might not yet be known.
+ # This can happen if the user only specified a source package name on the
+ # command line and then the version will only be known after the source
+ # package was successfully downloaded.
+ if ($self->get('Version')) {
+ $self->add_stat('Version', $self->get('Version'));
+ }
+ if ($self->get('OVersion')) {
+ $self->add_stat('Source-Version', $self->get('OVersion'));
+ }
+ $self->add_stat('Machine Architecture', $self->get_conf('ARCH'));
+ $self->add_stat('Host Architecture', $self->get('Host Arch'));
+ $self->add_stat('Build Architecture', $self->get('Build Arch'));
+ $self->add_stat('Build Profiles', $self->get('Build Profiles'))
+ if $self->get('Build Profiles');
+ $self->add_stat('Build Type', $self->get('Build Type'));
+ my @keylist;
+ if (defined $resolver) {
+ @keylist=keys %{$resolver->get('Initial Foreign Arches')};
+ push @keylist, keys %{$resolver->get('Added Foreign Arches')};
+ }
+ my $foreign_arches = join ' ', @keylist;
+ $self->add_stat('Foreign Architectures', $foreign_arches )
+ if $foreign_arches;
+ $self->add_stat('Distribution', $self->get_conf('DISTRIBUTION'));
+ if ($self->get('This Space') >= 0) {
+ $self->add_stat('Space', $self->get('This Space'));
+ } else {
+ $self->add_stat('Space', "n/a");
+ }
+ $self->add_stat('Build-Time',
+ $self->get('Build End Time')-$self->get('Build Start Time'));
+ $self->add_stat('Install-Time',
+ $self->get('Install End Time')-$self->get('Install Start Time'));
+ $self->add_stat('Package-Time',
+ $self->get('Pkg End Time')-$self->get('Pkg Start Time'));
+ if ($self->get('This Space') >= 0) {
+ $self->add_stat('Build-Space', $self->get('This Space'));
+ } else {
+ $self->add_stat('Build-Space', "n/a");
+ }
+ $self->add_stat('Status', $self->get_status());
+ $self->add_stat('Fail-Stage', $self->get('Pkg Fail Stage'))
+ if ($self->get_status() ne "successful");
+ $self->add_stat('Lintian', $self->get('Lintian Reason'))
+ if $self->get('Lintian Reason');
+ $self->add_stat('Piuparts', $self->get('Piuparts Reason'))
+ if $self->get('Piuparts Reason');
+ $self->add_stat('Autopkgtest', $self->get('Autopkgtest Reason'))
+ if $self->get('Autopkgtest Reason');
+}
+
+sub log_stats {
+ my $self = shift;
+ foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
+ $self->log("${stat}: " . $self->get('Summary Stats')->{$stat} . "\n");
+ }
+}
+
+sub print_stats {
+ my $self = shift;
+ foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
+ print STDOUT "${stat}: " . $self->get('Summary Stats')->{$stat} . "\n";
+ }
+}
+
+sub write_stats {
+ my $self = shift;
+
+ return if (!$self->get_conf('BATCH_MODE'));
+
+ my $stats_dir = $self->get_conf('STATS_DIR');
+
+ return if not defined $stats_dir;
+
+ if (! -d $stats_dir &&
+ !mkdir $stats_dir) {
+ $self->log_warning("Could not create $stats_dir: $!\n");
+ return;
+ }
+
+ my ($cat, $val) = @_;
+ local( *F );
+
+ $self->lock_file($stats_dir, 0);
+ open( F, ">>$stats_dir/$cat" );
+ print F "$val\n";
+ close( F );
+ $self->unlock_file($stats_dir);
+}
+
+sub debian_files_list {
+ my $self = shift;
+ my $files = shift;
+
+ my @list;
+
+ debug("Parsing $files\n");
+ my $session = $self->get('Session');
+
+ my $pipe = $session->get_read_file_handle($files);
+ if ($pipe) {
+ while (<$pipe>) {
+ chomp;
+ my $f = (split( /\s+/, $_ ))[0];
+ push( @list, "$f" );
+ debug(" $f\n");
+ }
+ close( $pipe ) or $self->log_error("Failed to close $files\n") && return 1;
+ }
+
+ return @list;
+}
+
+# Figure out chroot architecture
+sub chroot_arch {
+ my $self = shift;
+
+ chomp(my $chroot_arch = $self->get('Session')->read_command(
+ { COMMAND => ['dpkg', '--print-architecture'],
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0,
+ DIR => '/' }));
+
+ if (!$chroot_arch) {
+ Sbuild::Exception::Build->throw(error => "Can't determine architecture of chroot: $!",
+ failstage => "chroot-arch")
+ }
+
+ return $chroot_arch;
+}
+
+sub build_log_filter {
+ my $self = shift;
+ my $text = shift;
+ my $replacement = shift;
+
+ if ($self->get_conf('LOG_FILTER')) {
+ $self->log($self->get('FILTER_PREFIX') . $text . ':' . $replacement . "\n");
+ }
+}
+
+sub build_log_colour {
+ my $self = shift;
+ my $regex = shift;
+ my $colour = shift;
+
+ if ($self->get_conf('LOG_COLOUR')) {
+ $self->log($self->get('COLOUR_PREFIX') . $colour . ':' . $regex . "\n");
+ }
+}
+
+sub open_build_log {
+ my $self = shift;
+
+ my $date = strftime_c "%FT%TZ", gmtime($self->get('Pkg Start Time'));
+
+ my $filter_prefix = '__SBUILD_FILTER_' . $$ . ':';
+ $self->set('FILTER_PREFIX', $filter_prefix);
+ my $colour_prefix = '__SBUILD_COLOUR_' . $$ . ':';
+ $self->set('COLOUR_PREFIX', $colour_prefix);
+
+ my $filename = $self->get_conf('LOG_DIR') . '/';
+ # we might not know the pkgname_ver string if the user only specified a
+ # package name without version
+ if ($self->get('Package_SVersion')) {
+ $filename .= $self->get('Package_SVersion');
+ } else {
+ $filename .= $self->get('Package');
+ }
+ $filename .= '_' . $self->get('Host Arch') . "-$date";
+ $filename .= ".build" if $self->get_conf('SBUILD_MODE') ne 'buildd';
+
+ open($saved_stdout, ">&STDOUT") or warn "Can't redirect stdout\n";
+ open($saved_stderr, ">&STDERR") or warn "Can't redirect stderr\n";
+
+ my $PLOG;
+
+ my $pid;
+ ($pid = open($PLOG, "|-"));
+ if (!defined $pid) {
+ warn "Cannot open pipe to '$filename': $!\n";
+ } elsif ($pid == 0) {
+ $SIG{'INT'} = 'IGNORE';
+ $SIG{'TERM'} = 'IGNORE';
+ $SIG{'QUIT'} = 'IGNORE';
+ $SIG{'PIPE'} = 'IGNORE';
+
+ $saved_stdout->autoflush(1);
+ if (!$self->get_conf('NOLOG') &&
+ $self->get_conf('LOG_DIR_AVAILABLE')) {
+ unlink $filename; # To prevent opening symlink to elsewhere
+ open( CPLOG, ">$filename" ) or
+ Sbuild::Exception::Build->throw(error => "Failed to open build log $filename: $!",
+ failstage => "init");
+ CPLOG->autoflush(1);
+
+ # Create 'current' symlinks
+ if ($self->get_conf('SBUILD_MODE') eq 'buildd') {
+ $self->log_symlink($filename,
+ $self->get_conf('BUILD_DIR') . '/current-' .
+ $self->get_conf('DISTRIBUTION'));
+ } else {
+ my $symlinktarget = $filename;
+ # if symlink target is in the same directory as the symlink
+ # itself, make it a relative link instead of an absolute one
+ if (Cwd::abs_path($self->get_conf('BUILD_DIR')) eq Cwd::abs_path(dirname($filename))) {
+ $symlinktarget = basename($filename)
+ }
+ my $symlinkname = $self->get_conf('BUILD_DIR') . '/';
+ # we might not know the pkgname_ver string if the user only specified a
+ # package name without version
+ if ($self->get('Package_SVersion')) {
+ $symlinkname .= $self->get('Package_SVersion');
+ } else {
+ $symlinkname .= $self->get('Package');
+ }
+ $symlinkname .= '_' . $self->get('Host Arch') . ".build";
+ $self->log_symlink($symlinktarget, $symlinkname);
+ }
+ }
+
+ # Cache vars to avoid repeated hash lookups.
+ my $nolog = $self->get_conf('NOLOG');
+ my $log = $self->get_conf('LOG_DIR_AVAILABLE');
+ my $verbose = $self->get_conf('VERBOSE');
+ my $log_colour = $self->get_conf('LOG_COLOUR');
+ my @filter = ();
+ my @colour = ();
+ my ($text, $replacement);
+ my $filter_regex = "^$filter_prefix(.*):(.*)\$";
+ my $colour_regex = "^$colour_prefix(.*):(.*)\$";
+ my @ignore = ();
+
+ while (<STDIN>) {
+ # Add a replacement pattern to filter (sent from main
+ # process in log stream).
+ if (m/$filter_regex/) {
+ ($text,$replacement)=($1,$2);
+ $replacement = "<<$replacement>>";
+ push (@filter, [$text, $replacement]);
+ $_ = "I: NOTICE: Log filtering will replace '$text' with '$replacement'\n";
+ } elsif (m/$colour_regex/) {
+ my ($colour, $regex);
+ ($colour,$regex)=($1,$2);
+ push (@colour, [$colour, $regex]);
+# $_ = "I: NOTICE: Log colouring will colour '$regex' in $colour\n";
+ next;
+ } else {
+ # Filter out any matching patterns
+ foreach my $pattern (@filter) {
+ ($text,$replacement) = @{$pattern};
+ s/\Q$text\E/$replacement/g;
+ }
+ }
+ if (m/Deprecated key/ || m/please update your configuration/) {
+ my $skip = 0;
+ foreach my $ignore (@ignore) {
+ $skip = 1 if ($ignore eq $_);
+ }
+ next if $skip;
+ push(@ignore, $_);
+ }
+
+ if ($nolog || $verbose) {
+ my $colour = 'reset';
+ if (-t $saved_stdout && $log_colour) {
+ foreach my $pattern (@colour) {
+ if (m/$$pattern[0]/) {
+ $colour = $$pattern[1];
+ }
+ }
+ if ($colour ne 'reset') {
+ print $saved_stdout color $colour;
+ }
+ }
+
+ print $saved_stdout $_;
+ if (-t $saved_stdout && $log_colour && $colour ne 'reset') {
+ print $saved_stdout color 'reset';
+ }
+ }
+ if (!$nolog && $log) {
+ print CPLOG $_;
+ }
+ }
+
+ close CPLOG;
+ exit 0;
+ }
+
+ $PLOG->autoflush(1);
+ open(STDOUT, '>&', $PLOG) or warn "Can't redirect stdout\n";
+ open(STDERR, '>&', $PLOG) or warn "Can't redirect stderr\n";
+ $self->set('Log File', $filename);
+ $self->set('Log Stream', $PLOG);
+
+ my $hostname = $self->get_conf('HOSTNAME');
+ $self->log("sbuild (Debian sbuild) $version ($release_date) on $hostname\n");
+
+ my $arch_string = $self->get('Host Arch');
+ my $head1 = $self->get('Package');
+ if ($self->get('Version')) {
+ $head1 .= ' ' . $self->get('Version');
+ }
+ $head1 .= ' (' . $arch_string . ') ';
+ my $head2 = strftime_c "%a, %d %b %Y %H:%M:%S +0000",
+ gmtime($self->get('Pkg Start Time'));
+ my $head = $head1;
+ # If necessary, insert spaces so that $head1 is left aligned and $head2 is
+ # right aligned. If the sum of the length of both is greater than the
+ # available space of 76 characters, then no additional padding is
+ # inserted.
+ if (length($head1) + length($head2) <= 76) {
+ $head .= ' ' x (76 - length($head1) - length($head2));
+ }
+ $head .= $head2;
+ $self->log_section($head);
+
+ $self->log("Package: " . $self->get('Package') . "\n");
+ if (defined $self->get('Version')) {
+ $self->log("Version: " . $self->get('Version') . "\n");
+ }
+ if (defined $self->get('OVersion')) {
+ $self->log("Source Version: " . $self->get('OVersion') . "\n");
+ }
+ $self->log("Distribution: " . $self->get_conf('DISTRIBUTION') . "\n");
+ $self->log("Machine Architecture: " . $self->get_conf('ARCH') . "\n");
+ $self->log("Host Architecture: " . $self->get('Host Arch') . "\n");
+ $self->log("Build Architecture: " . $self->get('Build Arch') . "\n");
+ $self->log("Build Profiles: " . $self->get('Build Profiles') . "\n") if $self->get('Build Profiles');
+ $self->log("Build Type: " . $self->get('Build Type') . "\n");
+ $self->log("\n");
+}
+
+sub close_build_log {
+ my $self = shift;
+
+ my $time = $self->get('Pkg End Time');
+ if ($time == 0) {
+ $time = time;
+ }
+ my $date = strftime_c "%FT%TZ", gmtime($time);
+
+ my $hours = int($self->get('This Time')/3600);
+ my $minutes = int(($self->get('This Time')%3600)/60),
+ my $seconds = int($self->get('This Time')%60),
+ my $space = "no";
+ if ($self->get('This Space') >= 0) {
+ $space = sprintf("%dk", $self->get('This Space'));
+ }
+
+ my $filename = $self->get('Log File');
+
+ # building status at this point means failure.
+ if ($self->get_status() eq "building") {
+ $self->set_status('failed');
+ }
+
+ $self->log_subsection('Summary');
+ $self->generate_stats();
+ $self->log_stats();
+
+ $self->log_sep();
+ $self->log("Finished at ${date}\n");
+ $self->log(sprintf("Build needed %02d:%02d:%02d, %s disk space\n",
+ $hours, $minutes, $seconds, $space));
+
+ if ($self->get_status() eq "successful") {
+ if (length $self->get_conf('KEY_ID')) {
+ my $key_id = $self->get_conf('KEY_ID');
+ my $build_dir = $self->get_conf('BUILD_DIR');
+ my $changes;
+ $self->log(sprintf("Signature with key '%s' requested:\n", $key_id));
+ $changes = $self->get_changes();
+ if (!defined($changes)) {
+ $self->log_error(".changes is undef. Cannot sign .changes.\n");
+ } else {
+ system('debsign', '--re-sign', "-k$key_id", '--', "$build_dir/$changes");
+ }
+ if ($self->get_conf('SOURCE_ONLY_CHANGES')) {
+ # We would like to run debsign with --no-re-sign so that a file
+ # referenced by the normal changes file and was already signed
+ # there does not get changed here by re-signing. Otherwise, the
+ # checksum from the normal changes file might not match
+ # anymore. https://bugs.debian.org/977674
+ #
+ # The problem is, that with --no-re-sign, debsign will see a
+ # signed buildinfo file and skip signing the dsc.
+ # https://bugs.debian.org/981021
+ my $so_changes = $build_dir . '/' . $self->get('Package_SVersion') . "_source.changes";
+ if (-r $so_changes) {
+ system('debsign', '--re-sign', "-k$key_id", '--', "$so_changes");
+ } else {
+ $self->log_error("$so_changes unreadable. Cannot sign .changes.\n");
+ }
+ }
+ }
+ }
+
+ my $subject = "Log for " . $self->get_status() . " build of ";
+ if ($self->get('Package_Version')) {
+ $subject .= $self->get('Package_Version');
+ } else {
+ $subject .= $self->get('Package');
+ }
+
+ if ($self->get_conf('BUILD_SOURCE') && !$self->get_conf('BUILD_ARCH_ALL') && !$self->get_conf('BUILD_ARCH_ANY')) {
+ $subject .= " source";
+ }
+ if ($self->get_conf('BUILD_ARCH_ALL') && !$self->get_conf('BUILD_ARCH_ANY')) {
+ $subject .= " on all";
+ } elsif ($self->get('Host Arch')) {
+ $subject .= " on " . $self->get('Host Arch');
+ }
+ if ($self->get_conf('ARCHIVE')) {
+ $subject .= " (" . $self->get_conf('ARCHIVE') . "/" . $self->get_conf('DISTRIBUTION') . ")";
+ }
+ else {
+ $subject .= " (dist=" . $self->get_conf('DISTRIBUTION') . ")";
+ }
+
+ open(STDERR, '>&', $saved_stderr) or warn "Can't redirect stderr\n"
+ if defined($saved_stderr);
+ open(STDOUT, '>&', $saved_stdout) or warn "Can't redirect stdout\n"
+ if defined($saved_stdout);
+ $saved_stderr->close();
+ undef $saved_stderr;
+ $saved_stdout->close();
+ undef $saved_stdout;
+ $self->set('Log File', undef);
+ if (defined($self->get('Log Stream'))) {
+ $self->get('Log Stream')->close(); # Close child logger process
+ $self->set('Log Stream', undef);
+ }
+
+ $self->send_build_log($self->get_conf('MAILTO'), $subject, $filename)
+ if (defined($filename) && -f $filename &&
+ $self->get_conf('MAILTO'));
+}
+
+sub send_build_log {
+ my $self = shift;
+ my $to = shift;
+ my $subject = shift;
+ my $filename = shift;
+
+ my $conf = $self->get('Config');
+
+ if ($conf->get('MIME_BUILD_LOG_MAILS')) {
+ return $self->send_mime_build_log($to, $subject, $filename);
+ } else {
+ return send_mail($conf, $to, $subject, $filename);
+ }
+}
+
+sub send_mime_build_log {
+ my $self = shift;
+ my $to = shift;
+ my $subject = shift;
+ my $filename = shift;
+
+ my $conf = $self->get('Config');
+ my $tmp; # Needed for gzip, here for proper scoping.
+
+ my $msg = MIME::Lite->new(
+ From => $conf->get('MAILFROM'),
+ To => $to,
+ Subject => $subject,
+ Type => 'multipart/mixed'
+ );
+
+ # Add the GPG key ID to the mail if present so that it's clear if the log
+ # still needs signing or not.
+ if (length $self->get_conf('KEY_ID')) {
+ $msg->add('Key-ID', $self->get_conf('KEY_ID'));
+ }
+
+ if (!$conf->get('COMPRESS_BUILD_LOG_MAILS')) {
+ my $log_part = MIME::Lite->new(
+ Type => 'text/plain',
+ Path => $filename,
+ Filename => basename($filename)
+ );
+ $log_part->attr('content-type.charset' => 'UTF-8');
+ $msg->attach($log_part);
+ } else {
+ local( *F, *GZFILE );
+
+ if (!open( F, "<$filename" )) {
+ warn "Cannot open $filename for mailing: $!\n";
+ return 0;
+ }
+
+ $tmp = File::Temp->new();
+ tie *GZFILE, 'IO::Zlib', $tmp->filename, 'wb';
+
+ while( <F> ) {
+ print GZFILE $_;
+ }
+ untie *GZFILE;
+
+ close F;
+ close GZFILE;
+
+ $msg->attach(
+ Type => 'application/x-gzip',
+ Path => $tmp->filename,
+ Filename => basename($filename) . '.gz'
+ );
+ }
+ my $build_dir = $self->get_conf('BUILD_DIR');
+ my $changes = $self->get_changes();
+ if ($self->get_status() eq 'successful' && -r "$build_dir/$changes") {
+ my $log_part = MIME::Lite->new(
+ Type => 'text/plain',
+ Path => "$build_dir/$changes",
+ Filename => basename($changes)
+ );
+ $log_part->attr('content-type.charset' => 'UTF-8');
+ $msg->attach($log_part);
+ }
+
+ my $stats = '';
+ foreach my $stat (sort keys %{$self->get('Summary Stats')}) {
+ $stats .= sprintf("%s: %s\n", $stat, $self->get('Summary Stats')->{$stat});
+ }
+ $msg->attach(
+ Type => 'text/plain',
+ Filename => basename($filename) . '.summary',
+ Data => $stats
+ );
+
+ local $SIG{'PIPE'} = 'IGNORE';
+
+ if (!open( MAIL, "|" . $conf->get('MAILPROG') . " -oem $to" )) {
+ warn "Could not open pipe to " . $conf->get('MAILPROG') . ": $!\n";
+ close( F );
+ return 0;
+ }
+
+ $msg->print(\*MAIL);
+
+ if (!close( MAIL )) {
+ warn $conf->get('MAILPROG') . " failed (exit status $?)\n";
+ return 0;
+ }
+ return 1;
+}
+
+sub log_symlink {
+ my $self = shift;
+ my $log = shift;
+ my $dest = shift;
+
+ unlink $dest; # Don't return on failure, since the symlink will fail.
+ symlink $log, $dest;
+}
+
+sub get_changes_opts {
+ my $self = shift;
+ my @changes_opts = ();
+ foreach (@{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')}) {
+ if (/^--changes-option=(.*)$/) {
+ push @changes_opts, $1;
+ } elsif (/^-s[iad]$/) {
+ push @changes_opts, $_;
+ } elsif (/^--build=.*$/) {
+ push @changes_opts, $_;
+ } elsif (/^-m.*$/) {
+ push @changes_opts, $_;
+ } elsif (/^-e.*$/) {
+ push @changes_opts, $_;
+ } elsif (/^-v.*$/) {
+ push @changes_opts, $_;
+ } elsif (/^-C.*$/) {
+ push @changes_opts, $_;
+ }
+ }
+
+ return \@changes_opts;
+}
+
+1;
diff --git a/lib/Sbuild/Chroot.pm b/lib/Sbuild/Chroot.pm
new file mode 100644
index 0000000..018e199
--- /dev/null
+++ b/lib/Sbuild/Chroot.pm
@@ -0,0 +1,963 @@
+#
+# Chroot.pm: chroot library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::Chroot;
+
+use Sbuild qw(copy debug debug2);
+use Sbuild::Base;
+use Sbuild::ChrootInfo;
+use Sbuild::ChrootSetup qw(basesetup);
+use Sbuild qw($devnull shellescape);
+
+use strict;
+use warnings;
+use POSIX;
+use FileHandle;
+use File::Temp ();
+use File::Basename qw(basename);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $chroot_id = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ my @filter;
+ @filter = @{$self->get_conf('ENVIRONMENT_FILTER')}
+ if (defined($self->get_conf('ENVIRONMENT_FILTER')));
+
+ $self->set('Session ID', "");
+ $self->set('Chroot ID', $chroot_id) if defined $chroot_id;
+ $self->set('Defaults', {
+ 'COMMAND' => [],
+ 'INTCOMMAND' => [], # Private
+ 'EXPCOMMAND' => [], # Private
+ 'ENV' => {},
+ 'ENV_FILTER' => \@filter,
+ 'USER' => 'root',
+ 'CHROOT' => 1,
+ 'PRIORITY' => 0,
+ 'DIR' => '/',
+ 'SETSID' => 0,
+ 'STREAMIN' => undef,
+ 'STREAMOUT' => undef,
+ 'STREAMERR' => undef});
+
+ return $self;
+}
+
+sub _setup_options {
+ my $self = shift;
+
+ if (basesetup($self, $self->get('Config'))) {
+ print STDERR "Failed to set up chroot\n";
+ return 0;
+ }
+
+ return 1;
+}
+
+sub get_option {
+ my $self = shift;
+ my $options = shift;
+ my $option = shift;
+
+ my $value = undef;
+ $value = $self->get('Defaults')->{$option} if
+ (defined($self->get('Defaults')) &&
+ defined($self->get('Defaults')->{$option}));
+ $value = $options->{$option} if
+ (defined($options) &&
+ exists($options->{$option}));
+
+ return $value;
+}
+
+sub log_command {
+ my $self = shift;
+ my $options = shift;
+
+ my $priority = $options->{'PRIORITY'};
+
+ if ((defined($priority) && $priority >= 1) || $self->get_conf('DEBUG')) {
+ my $command;
+ if ($self->get_conf('DEBUG')) {
+ $command = $options->{'EXPCOMMAND'};
+ } else {
+ $command = $options->{'COMMAND'};
+ }
+
+ $self->log_info(join(" ", @$command), "\n");
+ }
+}
+
+# create a temporary file or directory inside the chroot
+sub mktemp {
+ my $self = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $mktempcmd = ['mktemp'];
+
+ if (defined $options->{'DIRECTORY'} && $options->{'DIRECTORY'}) {
+ push(@{$mktempcmd}, "-d");
+ }
+
+ if (defined $options->{'TEMPLATE'}) {
+ push(@{$mktempcmd}, $options->{'TEMPLATE'});
+ }
+
+ my $pipe = $self->pipe_command({ COMMAND => $mktempcmd, USER => $user, DIR => $dir });
+ if (!$pipe) {
+ $self->log_error("cannot open pipe\n");
+ return;
+ }
+ chomp (my $tmpdir = do { local $/; <$pipe> });
+ close $pipe;
+ if ($?) {
+ if (defined $options->{'TEMPLATE'}) {
+ $self->log_error("cannot run mktemp " . $options->{'TEMPLATE'} . ": $!\n");
+ } else {
+ $self->log_error("cannot run mktemp: $!\n");
+ }
+ return;
+ }
+ return $tmpdir;
+}
+
+# copy a file from the outside into the chroot
+sub copy_to_chroot {
+ my $self = shift;
+ my $source = shift;
+ my $dest = shift;
+ my $options = shift;
+
+ # if the destination inside the chroot is a directory, then the file has
+ # to be copied into that directory with the same filename as outside
+ if($self->test_directory($dest)) {
+ $dest .= '/' . (basename $source);
+ }
+
+ my $pipe = $self->get_write_file_handle($dest, $options);
+ if (!defined $pipe) {
+ $self->log_error("get_write_file_handle failed\n");
+ return;
+ }
+
+ local *INFILE;
+ if(!open(INFILE, "<", $source)) {
+ $self->log_error("cannot open $source\n");
+ close $pipe;
+ return;
+ }
+
+ while ( (read (INFILE, my $buffer, 65536)) != 0 ) {
+ print $pipe $buffer;
+ }
+
+ close INFILE;
+ close $pipe;
+
+ return 1;
+}
+
+# copy a file inside the chroot to the outside
+sub copy_from_chroot {
+ my $self = shift;
+ my $source = shift;
+ my $dest = shift;
+ my $options = shift;
+
+ my $pipe = $self->get_read_file_handle($source, $options);
+ if (!defined $pipe) {
+ $self->log_error("get_read_file_handle failed\n");
+ return;
+ }
+
+ # if the destination outside the chroot is a directory, then the file has
+ # to be copied into that directory with the same filename as inside
+ if (-d $dest) {
+ $dest .= '/' . (basename $source);
+ }
+
+ local *OUTFILE;
+ if(!open(OUTFILE, ">", $dest)) {
+ $self->log_error("cannot open $dest\n");
+ close $pipe;
+ return;
+ }
+
+ while ( (read ($pipe, my $buffer, 65536)) != 0 ) {
+ print OUTFILE $buffer;
+ }
+
+ close OUTFILE;
+ close $pipe;
+
+ return 1;
+}
+
+# returns a file handle to read a file inside the chroot
+sub get_read_file_handle {
+ my $self = shift;
+ my $source = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $escapedsource = shellescape $source;
+
+ my $pipe = $self->pipe_command({
+ COMMAND => [ "sh", "-c", "cat $escapedsource" ],
+ DIR => $dir,
+ USER => $user,
+ PIPE => 'in'
+ });
+ if (!$pipe) {
+ $self->log_error("cannot open pipe\n");
+ return;
+ }
+
+ return $pipe;
+}
+
+# returns a string with the content of a file inside the chroot
+sub read_file {
+ my $self = shift;
+ my $source = shift;
+ my $options = shift;
+
+ my $pipe = $self->get_read_file_handle($source, $options);
+ if (!defined $pipe) {
+ $self->log_error("get_read_file_handle failed\n");
+ return;
+ }
+
+ my $content = do { local $/; <$pipe> };
+ close $pipe;
+
+ return $content;
+}
+
+# returns a file handle to write to a file inside the chroot
+sub get_write_file_handle {
+ my $self = shift;
+ my $dest = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $escapeddest = shellescape $dest;
+
+ my $pipe = $self->pipe_command({
+ COMMAND => [ "sh", "-c", "cat > $escapeddest" ],
+ DIR => $dir,
+ USER => $user,
+ PIPE => 'out'
+ });
+ if (!$pipe) {
+ $self->log_error("cannot open pipe\n");
+ return;
+ }
+
+ return $pipe;
+}
+
+sub read_command {
+ my $self = shift;
+ my $options = shift;
+
+ $options->{PIPE} = "in";
+
+ my $pipe = $self->pipe_command($options);
+ if (!$pipe) {
+ $self->log_error("cannot open pipe\n");
+ return;
+ }
+
+ my $content = do { local $/; <$pipe> };
+ close $pipe;
+
+ if ($?) {
+ $self->log_error("read_command failed to execute " . $options->{COMMAND}->[0] . "\n");
+ return;
+ }
+
+ return $content;
+}
+
+# writes a string to a file inside the chroot
+sub write_file {
+ my $self = shift;
+ my $dest = shift;
+ my $content = shift;
+ my $options = shift;
+
+ my $pipe = $self->get_write_file_handle($dest, $options);
+ if (!defined $pipe) {
+ $self->log_error("get_read_file_handle failed\n");
+ return;
+ }
+
+ print $pipe $content;
+ close $pipe;
+
+ return 1;
+}
+
+sub write_command {
+ my $self = shift;
+ my $content = shift;
+ my $options = shift;
+
+ $options->{PIPE} = "out";
+
+ my $pipe = $self->pipe_command($options);
+ if (!$pipe) {
+ $self->log_error("cannot open pipe\n");
+ return;
+ }
+
+ if(!print $pipe $content) {
+ $self->log_error("failed to print to file handle\n");
+ close $pipe;
+ }
+
+ close $pipe;
+
+ if ($?) {
+ $self->log_error("write_command failed to execute " . $options->{COMMAND}->[0] . "\n");
+ return;
+ }
+
+ return 1;
+}
+
+# rename a file inside the chroot
+sub rename {
+ my $self = shift;
+ my $source = shift;
+ my $dest = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ $self->run_command({ COMMAND => ["mv", $source, $dest], USER => $user, DIR => $dir});
+ if ($?) {
+ $self->log_error("Can't rename $source to $dest: $!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+# create a directory inside the chroot
+sub mkdir {
+ my $self = shift;
+ my $path = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $mkdircmd = [ "mkdir", $path ];
+
+ if (defined $options->{'PARENTS'} && $options->{'PARENTS'}) {
+ push(@{$mkdircmd}, "-p");
+ }
+
+ if (defined $options->{'MODE'}) {
+ push(@{$mkdircmd}, "--mode", $options->{'MODE'});
+ }
+
+ $self->run_command({ COMMAND => $mkdircmd, USER => $user, DIR => $dir});
+ if ($?) {
+ $self->log_error("Can't mkdir $path: $!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+sub test_internal {
+ my $self = shift;
+ my $path = shift;
+ my $arg = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ $self->run_command({ COMMAND => [ "test", $arg, $path ], USER => $user, DIR => $dir});
+ if ($? eq 0) {
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+# test if a path inside the chroot is a directory
+sub test_directory {
+ my $self = shift;
+ my $path = shift;
+ my $options = shift;
+
+ return $self->test_internal($path, "-d", $options);
+}
+
+# test if a path inside the chroot is a regular file
+sub test_regular_file {
+ my $self = shift;
+ my $path = shift;
+ my $options = shift;
+
+ return $self->test_internal($path, "-f", $options);
+}
+
+# test if a path inside the chroot is a regular readable file
+sub test_regular_file_readable {
+ my $self = shift;
+ my $path = shift;
+ my $options = shift;
+
+ return $self->test_internal($path, "-r", $options);
+}
+
+# test if a path inside the chroot is a symlink
+sub test_symlink {
+ my $self = shift;
+ my $path = shift;
+ my $options = shift;
+
+ return $self->test_internal($path, "-L", $options);
+}
+
+# remove a file inside the chroot
+sub unlink {
+ my $self = shift;
+ my $path = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $rmcmd = [ "rm", $path ];
+
+ if (defined $options->{'RECURSIVE'} && $options->{'RECURSIVE'}) {
+ push(@{$rmcmd}, "-r");
+ }
+
+ if (defined $options->{'FORCE'} && $options->{'FORCE'}) {
+ push(@{$rmcmd}, "-f");
+ }
+
+ if (defined $options->{'DIRECTORY'} && $options->{'DIRECTORY'}) {
+ push(@{$rmcmd}, "-d");
+ }
+
+ $self->run_command({ COMMAND => $rmcmd, USER => $user, DIR => $dir});
+ if ($?) {
+ $self->log_error("Can't unlink $path: $!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+# chmod a path inside the chroot
+sub chmod {
+ my $self = shift;
+ my $path = shift;
+ my $mode = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $chmodcmd = [ "chmod" ];
+
+ if (defined $options->{'RECURSIVE'} && $options->{'RECURSIVE'}) {
+ push(@{$chmodcmd}, "-R");
+ }
+
+ push(@{$chmodcmd}, $mode, $path);
+
+ $self->run_command({ COMMAND => $chmodcmd, USER => $user, DIR => $dir});
+ if ($?) {
+ $self->log_error("Can't chmod $path to $mode: $!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+# chown a path inside the chroot
+sub chown {
+ my $self = shift;
+ my $path = shift;
+ my $owner = shift;
+ my $group = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $chowncmd = [ "chown" ];
+
+ if (defined $options->{'RECURSIVE'} && $options->{'RECURSIVE'}) {
+ push(@{$chowncmd}, "-R");
+ }
+
+ push(@{$chowncmd}, "$owner:$group", $path);
+
+ $self->run_command({ COMMAND => $chowncmd, USER => $user, DIR => $dir});
+ if ($?) {
+ $self->log_error("Can't chown $path to $owner:$group: $!\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+# test if a program inside the chroot can be run
+# we use the function name "can_run" as it is similar to the function in
+# IPC::Cmd
+sub can_run {
+ my $self = shift;
+ my $program = shift;
+ my $options = shift;
+
+ my $user = "root";
+ $user = $options->{'USER'} if defined $options->{'USER'};
+
+ my $dir = "/";
+ $dir = $options->{'DIR'} if defined $options->{'DIR'};
+
+ my $escapedprogram = shellescape $program;
+
+ my $commandcmd = [ 'sh', '-c', "command -v $escapedprogram >/dev/null 2>&1" ];
+
+ $self->run_command({ COMMAND => $commandcmd, USER => $user, DIR => $dir});
+ if ($?) {
+ return 0;
+ }
+
+ return 1;
+}
+
+# Note, do not run with $user="root", and $chroot=0, because root
+# access to the host system is not allowed by schroot, nor required
+# via sudo.
+sub pipe_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ my $pipetype = "-|";
+ $pipetype = "|-" if (defined $options->{'PIPE'} &&
+ $options->{'PIPE'} eq 'out');
+
+ my $pipe = undef;
+ my $pid = open($pipe, $pipetype);
+ if (!defined $pid) {
+ warn "Cannot open pipe: $!\n";
+ } elsif ($pid == 0) { # child
+ if (!defined $options->{'PIPE'} ||
+ $options->{'PIPE'} ne 'out') { # redirect stdin
+ my $in = $self->get_option($options, 'STREAMIN');
+ if (defined($in) && $in && \*STDIN != $in) {
+ open(STDIN, '<&', $in)
+ or warn "Can't redirect stdin\n";
+ }
+ } else { # redirect stdout
+ my $out = $self->get_option($options, 'STREAMOUT');
+ if (defined($out) && $out && \*STDOUT != $out) {
+ open(STDOUT, '>&', $out)
+ or warn "Can't redirect stdout\n";
+ }
+ }
+ # redirect stderr
+ my $err = $self->get_option($options, 'STREAMERR');
+ if (defined($err) && $err && \*STDERR != $err) {
+ open(STDERR, '>&', $err)
+ or warn "Can't redirect stderr\n";
+ }
+
+ my $setsid = $self->get_option($options, 'SETSID');
+ setsid() if defined($setsid) && $setsid;
+
+ $self->exec_command($options);
+ }
+
+ debug2("Pipe (PID $pid, $pipe) created for: ",
+ join(" ", @{$options->{'COMMAND'}}),
+ "\n");
+
+ $options->{'PID'} = $pid;
+
+ return $pipe;
+}
+
+# Note, do not run with $user="root", and $chroot=0, because root
+# access to the host system is not allowed by schroot, nor required
+# via sudo.
+sub run_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ my $pid = fork();
+
+ if (!defined $pid) {
+ warn "Cannot fork: $!\n";
+ } elsif ($pid == 0) { # child
+
+ # redirect stdin
+ my $in = $self->get_option($options, 'STREAMIN');
+ if (defined($in) && $in && \*STDIN != $in) {
+ open(STDIN, '<&', $in)
+ or warn "Can't redirect stdin\n";
+ }
+
+ # redirect stdout
+ my $out = $self->get_option($options, 'STREAMOUT');
+ if (defined($out) && $out && \*STDOUT != $out) {
+ open(STDOUT, '>&', $out)
+ or warn "Can't redirect stdout\n";
+ }
+
+ # redirect stderr
+ my $err = $self->get_option($options, 'STREAMERR');
+ if (defined($err) && $err && \*STDERR != $err) {
+ open(STDERR, '>&', $err)
+ or warn "Can't redirect stderr\n";
+ }
+
+ my $setsid = $self->get_option($options, 'SETSID');
+ setsid() if defined($setsid) && $setsid;
+
+ $self->exec_command($options);
+ }
+
+ debug2("Pipe (PID $pid) created for: ",
+ join(" ", @{$options->{'COMMAND'}}),
+ "\n");
+
+ waitpid($pid, 0);
+}
+
+# Note, do not run with $user="root", and $chroot=0, because root
+# access to the host system is not allowed by schroot, nor required
+# via sudo.
+sub run_command {
+ my $self = shift;
+ my $options = shift;
+
+ $options->{'INTCOMMAND'} = copy($options->{'COMMAND'});
+ $options->{'INTCOMMAND_STR'} = copy($options->{'COMMAND_STR'});
+
+ return $self->run_command_internal($options);
+}
+
+# Note, do not run with $user="root", and $chroot=0, because root
+# access to the host system is not allowed by schroot, nor required
+# via sudo.
+sub pipe_command {
+ my $self = shift;
+ my $options = shift;
+
+ $options->{'INTCOMMAND'} = copy($options->{'COMMAND'});
+ $options->{'INTCOMMAND_STR'} = copy($options->{'COMMAND_STR'});
+
+ return $self->pipe_command_internal($options);
+}
+
+sub get_internal_exec_string {
+ return;
+}
+
+# This function must not print anything to standard output or standard error
+# when it dies because its output will be treated as the output of the program
+# it executes. So error handling can only happen with "die()".
+sub exec_command {
+ my $self = shift;
+ my $options = shift;
+
+ my @filter;
+ my $chrootfilter = $self->get('Defaults')->{'ENV_FILTER'};
+ push(@filter, @{$chrootfilter});
+
+ my $commandfilter = $options->{'ENV_FILTER'};
+ push(@filter, @{$commandfilter}) if defined($commandfilter);
+
+ # Sanitise environment
+ foreach my $var (keys %ENV) {
+ my $match = 0;
+ foreach my $regex (@filter) {
+ $match = 1 if
+ $var =~ m/($regex)/;
+ }
+ delete $ENV{$var} if
+ $match == 0;
+ if (!$match) {
+ debug2("Environment filter: Deleted $var\n");
+ } else {
+ debug2("Environment filter: Kept $var\n");
+ }
+ }
+
+ my $chrootenv = $self->get('Defaults')->{'ENV'};
+ foreach (keys %$chrootenv) {
+ $ENV{$_} = $chrootenv->{$_};
+ }
+
+ my $commandenv = $options->{'ENV'};
+ foreach (keys %$commandenv) {
+ $ENV{$_} = $commandenv->{$_};
+ }
+
+ # get_command_internal has to be called *after* $ENV was set because
+ # depending on the backend, environment variables have to be handled
+ # differently. For example the autopkgtest backend has to insert an
+ # explicit call to env into the command so that the environment variables
+ # survive.
+ $self->get_command_internal($options);
+
+ if (!defined($options->{'EXPCOMMAND'}) || $options->{'EXPCOMMAND'} eq ''
+ || !defined($options->{'COMMAND'}) || scalar(@{$options->{'COMMAND'}}) == 0
+ || !defined($options->{'INTCOMMAND'}) || scalar(@{$options->{'INTCOMMAND'}}) == 0) {
+ die "get_command_internal failed during exec_command\n";
+ }
+
+ $self->log_command($options);
+
+ my $command = $options->{'EXPCOMMAND'};
+
+ my $program = $command->[0];
+ $program = $options->{'PROGRAM'} if defined($options->{'PROGRAM'});
+
+ debug2("PROGRAM: $program\n");
+ debug2("COMMAND: ", join(" ", @{$options->{'COMMAND'}}), "\n");
+ debug2("COMMAND_STR: ", $options->{'COMMAND'} // 'UNDEFINED', "\n");
+ debug2("INTCOMMAND: ", join(" ", @{$options->{'INTCOMMAND'}}), "\n");
+ debug2("INTCOMMAND_STR: ", $options->{'INTCOMMAND_STR:'} // 'UNDEFINED', "\n");
+ debug2("EXPCOMMAND: ", join(" ", @{$options->{'EXPCOMMAND'}}), "\n");
+
+ debug2("Environment set:\n");
+ foreach (sort keys %ENV) {
+ debug2(' ' . $_ . '=' . ($ENV{$_} || '') . "\n");
+ }
+
+ debug("Running command: ", join(" ", @$command), "\n");
+ exec { $program } @$command;
+ die "Failed to exec: $command->[0]: $!";
+}
+
+sub lock_chroot {
+ my $self = shift;
+ my $new_job = shift;
+ my $new_pid = shift;
+ my $new_user = shift;
+
+ my $lockfile = '/var/lock/sbuild';
+ my $max_trys = $self->get_conf('MAX_LOCK_TRYS');
+ my $lock_interval = $self->get_conf('LOCK_INTERVAL');
+
+ # The following command in run /inside/ the chroot to create the lockfile.
+ my $command = <<"EOF";
+
+ use strict;
+ use warnings;
+ use POSIX;
+ use FileHandle;
+
+ my \$lockfile="$lockfile";
+ my \$try = 0;
+
+ repeat:
+ if (!sysopen( F, \$lockfile, O_WRONLY|O_CREAT|O_TRUNC|O_EXCL, 0644 )){
+ if (\$! == EEXIST) {
+ # lock file exists, wait
+ goto repeat if !open( F, "<\$lockfile" );
+ my \$line = <F>;
+ my (\$job, \$pid, \$user);
+ close( F );
+ if (\$line !~ /^(\\S+)\\s+(\\S+)\\s+(\\S+)/) {
+ print STDERR "Bad lock file contents (\$lockfile) -- still trying\\n";
+ } else {
+ (\$job, \$pid, \$user) = (\$1, \$2, \$3);
+ if (kill( 0, \$pid ) == 0 && \$! == ESRCH) {
+ # process no longer exists, remove stale lock
+ print STDERR "Removing stale lock file \$lockfile ".
+ "(job \$job, pid \$pid, user \$user)\\n";
+ if (!unlink(\$lockfile)) {
+ if (\$! != ENOENT) {
+ print STDERR "Cannot remove chroot lock file \$lockfile: \$!\\n";
+ exit 1;
+ }
+ }
+ }
+ }
+ ++\$try;
+ if (\$try > $max_trys) {
+ print STDERR "Lockfile \$lockfile still present after " .
+ $max_trys * $lock_interval . " seconds -- giving up\\n";
+ exit 1;
+ }
+ print STDERR "Another sbuild process (job \$job, pid \$pid by user \$user) is currently using the build chroot; waiting...\\n"
+ if \$try == 1;
+ sleep $lock_interval;
+ goto repeat;
+ } else {
+ print STDERR "Failed to create lock file \$lockfile: \$!\\n";
+ exit 1;
+ }
+ }
+
+ F->print("$new_job $new_pid $new_user\\n");
+ F->close();
+
+ exit 0;
+EOF
+
+ $self->run_command(
+ { COMMAND => ['perl',
+ '-e',
+ $command],
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+
+ if ($?) {
+ return 0;
+ }
+ return 1;
+}
+
+sub unlock_chroot {
+ my $self = shift;
+
+ my $lockfile = '/var/lock/sbuild';
+
+ # The following command in run /inside/ the chroot to remove the lockfile.
+ my $command = <<"EOF";
+
+ use strict;
+ use warnings;
+ use POSIX;
+
+ my \$lockfile="$lockfile";
+ if (!unlink(\$lockfile)) {
+ print STDERR "Cannot remove chroot lock file \$lockfile: \$!\\n"
+ if \$! != ENOENT;
+ exit 1;
+ }
+ exit 0;
+EOF
+
+ debug("Removing chroot lock file $lockfile\n");
+ $self->run_command(
+ { COMMAND => ['perl',
+ '-e',
+ $command],
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+
+ if ($?) {
+ return 0;
+ }
+ return 1;
+}
+
+sub useradd {
+ my $self = shift;
+ my @args = @_;
+ $self->run_command(
+ { COMMAND => ['useradd', @args],
+ USER => 'root',
+ STREAMIN => $devnull,
+ STREAMOUT => $devnull,
+ DIR => '/' });
+ if ($?) {
+ return 1;
+ }
+ return 0;
+}
+
+sub groupadd {
+ my $self = shift;
+ my @args = @_;
+ $self->run_command(
+ { COMMAND => ['groupadd', @args],
+ USER => 'root',
+ STREAMIN => $devnull,
+ STREAMOUT => $devnull,
+ DIR => '/' });
+ if ($?) {
+ return 1;
+ }
+ return 0;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootAutopkgtest.pm b/lib/Sbuild/ChrootAutopkgtest.pm
new file mode 100644
index 0000000..cea1c81
--- /dev/null
+++ b/lib/Sbuild/ChrootAutopkgtest.pm
@@ -0,0 +1,297 @@
+#
+# Chroot.pm: chroot library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::ChrootAutopkgtest;
+
+use strict;
+use warnings;
+
+use POSIX qw(setsid);
+use Sbuild qw(shellescape);
+
+BEGIN {
+ use Exporter ();
+ use Sbuild::Chroot;
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Chroot);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $chroot_id = shift;
+
+ my $self = $class->SUPER::new($conf, $chroot_id);
+ bless($self, $class);
+
+ $self->set('Autopkgtest Pipe In', undef);
+ $self->set('Autopkgtest Pipe Out', undef);
+ $self->set('Autopkgtest Virt PID', undef);
+
+ return $self;
+}
+
+sub begin_session {
+ my $self = shift;
+
+ # We are manually setting up bidirectional communication with autopkgtest
+ # instead of using IPC::Open2 because we must call setsid() from the
+ # child.
+ #
+ # Calling setsid() is necessary to place autopkgtest into a new process
+ # group and thus prevent it from receiving for example a Ctrl+C that can be
+ # sent by the user from a terminal. If autopkgtest would receive the
+ # SIGINT, then it would close the session immediately without us being able
+ # to do anything about it. Instead, we want to close the session later
+ # ourselves.
+ pipe(my $prnt_out, my $chld_in);
+ pipe(my $chld_out, my $prnt_in);
+
+ my $pid = fork();
+ if (!defined $pid) {
+ die "Cannot fork: $!";
+ } elsif ($pid == 0) {
+ # child
+ close($chld_in);
+ close($chld_out);
+
+ # redirect stdin
+ open(STDIN, '<&', $prnt_out)
+ or die "Can't redirect stdin\n";
+
+ # redirect stdout
+ open(STDOUT, '>&', $prnt_in)
+ or die "Can't redirect stdout\n";
+
+ # put process into new group
+ setsid();
+
+ my @command = ($self->get_conf('AUTOPKGTEST_VIRT_SERVER'),
+ @{$self->get_conf('AUTOPKGTEST_VIRT_SERVER_OPTIONS')});
+ exec { $self->get_conf('AUTOPKGTEST_VIRT_SERVER') } @command;
+ die "Failed to exec $self->get_conf('AUTOPKGTEST_VIRT_SERVER'): $!";
+ }
+ close($prnt_out);
+ close($prnt_in);
+
+ # We must enable autoflushing for the stdin of the child process or
+ # otherwise the commands we write will never reach the child.
+ $chld_in->autoflush(1);
+
+ if (!$pid) {
+ print STDERR "Chroot setup failed\n";
+ return 0;
+ }
+
+ my $status = <$chld_out>;
+
+ if (!defined $status) {
+ print STDERR "Undefined chroot status\n";
+ return 0;
+ }
+
+ chomp $status;
+
+ if (! defined $status || $status ne "ok") {
+ print STDERR "autopkgtest-virt server returned unexpected value: $status\n";
+ kill 'KILL', $pid;
+ return 0;
+ }
+
+ print $chld_in "open\n";
+
+ $status = <$chld_out>;
+
+ if (!defined $status) {
+ print STDERR "Undefined return value after 'open'\n";
+ return 0;
+ }
+
+ chomp $status;
+
+ my $autopkgtest_session;
+ if ($status =~ /^ok (.*)$/) {
+ $autopkgtest_session = $1;
+ $self->set('Session ID', $autopkgtest_session);
+ } else {
+ print STDERR "autopkgtest-virt server: cannot open: $status\n";
+ kill 'KILL', $pid;
+ return 0;
+ }
+
+ print STDERR "Setting up chroot with session id $autopkgtest_session\n"
+ if $self->get_conf('DEBUG');
+
+ print $chld_in "capabilities\n";
+
+ chomp ($status = <$chld_out>);
+
+ my @capabilities;
+ if ($status =~ /^ok (.*)$/) {
+ @capabilities = split /\s+/, $1;
+ } else {
+ print STDERR "autopkgtest-virt server: cannot capabilities: $status\n";
+ kill 'KILL', $pid;
+ return 0;
+ }
+
+ if (! grep {$_ eq "root-on-testbed"} @capabilities) {
+ print STDERR "autopkgtest-virt server: capability root-on-testbed missing\n";
+ kill 'KILL', $pid;
+ return 0;
+ }
+
+ # TODO: also test "revert" capability
+
+ print $chld_in "print-execute-command\n";
+
+ chomp ($status = <$chld_out>);
+
+ my $exec_cmd;
+ if ($status =~ /^ok (.*)$/) {
+ $exec_cmd = $1;
+ } else {
+ print STDERR "autopkgtest-virt server: cannot print-execute-command: $status\n";
+ kill 'KILL', $pid;
+ return 0;
+ }
+
+ my @exec_args = split /,/, $exec_cmd;
+
+ @exec_args = map { s/%([0-9A-Fa-f]{2})/chr(hex($1))/eg; $_ } @exec_args;
+
+ $self->set('Location', '/autopkgtest-virt-dummy-location');
+ $self->set('Autopkgtest Pipe In', $chld_in);
+ $self->set('Autopkgtest Pipe Out', $chld_out);
+ $self->set('Autopkgtest Virt PID', $pid);
+ $self->set('Autopkgtest Exec Command', \@exec_args);
+
+ return 0 if !$self->_setup_options();
+
+ return 1;
+}
+
+sub end_session {
+ my $self = shift;
+
+ return if $self->get('Session ID') eq "";
+
+ print STDERR "Cleaning up chroot (session id " . $self->get('Session ID') . ")\n"
+ if $self->get_conf('DEBUG');
+
+ my $chld_in = $self->get('Autopkgtest Pipe In');
+ my $chld_out = $self->get('Autopkgtest Pipe Out');
+ my $pid = $self->get('Autopkgtest Virt PID');
+
+ print $chld_in "close\n";
+
+ my $status = <$chld_out>;
+
+ if (!defined $status) {
+ print STDERR "Undefined return value after 'close'\n";
+ return 0;
+ }
+
+ chomp $status;
+
+ if ($status ne "ok") {
+ print STDERR "autopkgtest-virt server: cannot close: $status\n";
+ return 0;
+ }
+
+ print $chld_in "quit\n";
+
+ waitpid $pid, 0;
+
+ if ($?) {
+ my $child_exit_status = $? >> 8;
+ print STDERR "autopkgtest-virt quit with exit status $child_exit_status\n";
+ return 0;
+ }
+
+ close($chld_in);
+ close($chld_out);
+
+ $self->set('Autopkgtest Pipe In', undef);
+ $self->set('Autopkgtest Pipe Out', undef);
+ $self->set('Autopkgtest Virt PID', undef);
+
+ return 1;
+}
+
+sub get_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ # Command to run. If I have a string, use it. Otherwise use the list-ref
+ my $command = $options->{'INTCOMMAND_STR'} // $options->{'INTCOMMAND'};
+
+ my $user = $options->{'USER'}; # User to run command under
+ my $dir; # Directory to use (optional)
+ $dir = $self->get('Defaults')->{'DIR'} if
+ (defined($self->get('Defaults')) &&
+ defined($self->get('Defaults')->{'DIR'}));
+ $dir = $options->{'DIR'} if
+ defined($options->{'DIR'}) && $options->{'DIR'};
+
+ if (!defined $user || $user eq "") {
+ $user = $self->get_conf('USERNAME');
+ }
+
+ my @cmdline = ();
+
+ @cmdline = @{$self->get('Autopkgtest Exec Command')};
+
+ push @cmdline, 'env';
+ foreach my $var (keys %ENV) {
+ push @cmdline, "$var=$ENV{$var}";
+ }
+
+ if ($user ne "root") {
+ push @cmdline, "/sbin/runuser", '-u', $user, '--';
+ }
+
+ if (defined($dir)) {
+ my $shelldir = shellescape $dir;
+ push @cmdline, 'sh', '-c', "cd $shelldir && exec \"\$@\"", 'exec';
+ } else {
+ $dir = '/';
+ }
+
+ if (ref $command) {
+ push @cmdline, @$command;
+ } else {
+ push @cmdline, ('/bin/sh', '-c', $command);
+ $command = [split(/\s+/, $command)];
+ }
+
+ $options->{'USER'} = $user;
+ $options->{'COMMAND'} = $command;
+ $options->{'EXPCOMMAND'} = \@cmdline;
+ $options->{'CHDIR'} = undef;
+ $options->{'DIR'} = $dir;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootInfo.pm b/lib/Sbuild/ChrootInfo.pm
new file mode 100644
index 0000000..30ef290
--- /dev/null
+++ b/lib/Sbuild/ChrootInfo.pm
@@ -0,0 +1,143 @@
+#
+# ChrootInfo.pm: chroot utility library for sbuild
+# Copyright © 2005-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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootInfo;
+
+use strict;
+use warnings;
+
+use Sbuild::Base;
+
+use POSIX;
+use FileHandle;
+use File::Temp ();
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('Chroots', {});
+
+ $self->get_info_all();
+
+ return $self;
+}
+
+
+
+sub create {
+ my $self = shift;
+ my $namespace = shift;
+ my $distribution = shift;
+ my $chroot = shift;
+ my $arch = shift; # this is the build arch
+
+ my $chrootid = $self->find($namespace, $distribution, $chroot, $arch);
+
+ my $newchroot = $self->_create($chrootid);
+
+ if (defined($newchroot)) {
+ $newchroot->set('Chroots', $self);
+ }
+
+ return $newchroot;
+}
+
+
+sub find {
+ my $self = shift;
+ my $namespace = shift;
+ my $distribution = shift;
+ my $chroot = shift;
+ my $arch = shift; # this is the build arch
+
+ # Use namespace given from $distribution if one is found
+ if ($distribution =~ /^([^:]+):/msx) {
+ $namespace = $1;
+ $distribution =~ s/^[^:]+://msx;
+ }
+
+ my $chroots = $self->get('Chroots');
+
+ # Don't do strict arch checking if ARCH == HOST_ARCH.
+ if (!defined($arch) || $arch eq "") {
+ $arch = $self->get_conf('BUILD_ARCH');
+ }
+ my $host_arch = $self->get_conf('HOST_ARCH');
+
+ if (!defined $chroot) {
+ my $ns = $chroots->{$namespace};
+ if (!defined($ns)) {
+ if ($namespace ne 'chroot') {
+ $chroot = $self->find('chroot', $distribution, $chroot, $arch);
+ } else {
+ return undef;
+ }
+ }
+
+ if ($arch ne $host_arch &&
+ defined($ns->{"${distribution}-${arch}-${host_arch}-sbuild"})) {
+ $chroot = "${namespace}:${distribution}-${arch}-${host_arch}-sbuild";
+ }
+ elsif ($arch ne $host_arch &&
+ defined($ns->{"${distribution}-${arch}-${host_arch}"})) {
+ $chroot = "${namespace}:${distribution}-${arch}-${host_arch}";
+ }
+ elsif ($arch ne "" &&
+ defined($ns->{"${distribution}-${arch}-sbuild"})) {
+ $chroot = "${namespace}:${distribution}-${arch}-sbuild";
+ }
+ elsif (defined($ns->{"${distribution}-sbuild"})) {
+ $chroot = "${namespace}:${distribution}-sbuild";
+ }
+ elsif ($arch ne "" &&
+ defined($ns->{"${distribution}-${arch}"})) {
+ $chroot = "${namespace}:${distribution}-${arch}";
+ } elsif (defined($ns->{$distribution})) {
+ $chroot = "${namespace}:${distribution}";
+ }
+ }
+
+ if (!$chroot) {
+ # Fall back to chroot namespace.
+ if ($namespace ne 'chroot') {
+ $chroot = $self->find('chroot', $distribution, $chroot, $arch);
+ } else {
+ $self->log_error("Chroot for distribution $distribution, architecture $arch not found\n");
+ return undef;
+ }
+ }
+
+ return $chroot;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootInfoAutopkgtest.pm b/lib/Sbuild/ChrootInfoAutopkgtest.pm
new file mode 100644
index 0000000..e43c52a
--- /dev/null
+++ b/lib/Sbuild/ChrootInfoAutopkgtest.pm
@@ -0,0 +1,67 @@
+#
+# ChrootInfo.pm: chroot utility library for sbuild
+# 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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootInfoAutopkgtest;
+
+use Sbuild::ChrootInfo;
+use Sbuild::ChrootAutopkgtest;
+
+use strict;
+use warnings;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ChrootInfo);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub get_info_all {
+ my $self = shift;
+
+ my $chroots = {};
+
+ $self->set('Chroots', $chroots);
+}
+
+sub _create {
+ my $self = shift;
+ my $chroot_id = shift;
+
+ my $chroot = undef;
+
+ $chroot = Sbuild::ChrootAutopkgtest->new($self->get('Config'), $chroot_id);
+
+ return $chroot;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootInfoSchroot.pm b/lib/Sbuild/ChrootInfoSchroot.pm
new file mode 100644
index 0000000..92b4c03
--- /dev/null
+++ b/lib/Sbuild/ChrootInfoSchroot.pm
@@ -0,0 +1,186 @@
+#
+# ChrootInfo.pm: chroot utility library for sbuild
+# 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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootInfoSchroot;
+
+use Sbuild::ChrootInfo;
+use Sbuild::ChrootSchroot;
+
+use strict;
+use warnings;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ChrootInfo);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub get_info_from_stream {
+ my $self = shift;
+ my $stream = shift;
+
+ my $chroot_type = '';
+ my %tmp = ('Namespace' => '',
+ 'Name' => '',
+ 'Priority' => 0,
+ 'Location' => '',
+ 'Session Purged' => 0);
+
+ while (<$stream>) {
+ chomp;
+
+ last if ! $_;
+
+ if (/^\s*(───|---) Chroot \1$/ &&
+ $tmp{'Namespace'} eq "") {
+ $tmp{'Namespace'} = 'chroot';
+ }
+ if (/^\s*(───|---) Session \1$/ &&
+ $tmp{'Namespace'} eq "") {
+ $tmp{'Namespace'} = 'session';
+ }
+ if (/^\s*(───|---) Source \1$/ &&
+ $tmp{'Namespace'} eq "") {
+ $tmp{'Namespace'} = 'source';
+ }
+ if (/^\s*Name:?\s+(.*)$/ &&
+ $tmp{'Name'} eq "") {
+ $tmp{'Name'} = $1;
+ }
+ if (/^\s*Type:?\s+(.*)$/) {
+ $chroot_type = $1;
+ }
+ if (/^\s*Location:?\s+(.*)$/ &&
+ $tmp{'Location'} eq "") {
+ $tmp{'Location'} = $1;
+ }
+ if (/^\s*Mount Location:?\s+(.*)$/ &&
+ $tmp{'Location'} eq "") {
+ $tmp{'Location'} = $1;
+ }
+ # Path takes priority over Location and Mount Location.
+ if (/^\s*Path:?\s+(.*)$/) {
+ $tmp{'Location'} = $1;
+ }
+ if (/^\s*Priority:?\s+(\d+)$/) {
+ $tmp{'Priority'} = $1;
+ }
+ if (/^\s*Session Purged\s+(.*)$/) {
+ if ($1 eq "true") {
+ $tmp{'Session Purged'} = 1;
+ }
+ }
+ if (/^\s*Aliases:?\s+(.*)$/) {
+ $tmp{'Aliases'} = $1;
+ }
+ }
+
+ if ($self->get_conf('DEBUG') && $tmp{'Name'}) {
+ print STDERR "Found schroot chroot: $tmp{'Namespace'}:$tmp{'Name'}\n";
+ foreach (sort keys %tmp) {
+ print STDERR " $_ $tmp{$_}\n";
+ }
+ }
+
+ if (!$tmp{'Name'}) {
+ return undef;
+ }
+ return \%tmp;
+}
+
+sub get_info {
+ my $self = shift;
+ my $chroot = shift;
+
+ my $chroot_type = "";
+
+ # If namespaces aren't supported, try to fall back to old style session.
+ open CHROOT_DATA, '-|', $self->get_conf('SCHROOT'), '--info', '--chroot', "session:$chroot" or
+ open CHROOT_DATA, '-|', $self->get_conf('SCHROOT'), '--info', '--chroot', $chroot or
+ die 'Can\'t run ' . $self->get_conf('SCHROOT') . ' to get chroot data';
+
+ my $tmp = $self->get_info_from_stream(\*CHROOT_DATA);
+
+ if (!$tmp) {
+ close CHROOT_DATA;
+ return undef;
+ }
+
+ close CHROOT_DATA or die "Can't close schroot pipe getting chroot data";
+
+ return $tmp;
+}
+
+sub get_info_all {
+ my $self = shift;
+
+ my $chroots = {};
+
+ local %ENV;
+
+ $ENV{'LC_ALL'} = 'C';
+ $ENV{'LANGUAGE'} = 'C';
+
+ open CHROOTS, '-|', $self->get_conf('SCHROOT'), '--info'
+ or die 'Can\'t run ' . $self->get_conf('SCHROOT');
+ my $tmp = undef;
+ while (defined($tmp = $self->get_info_from_stream(\*CHROOTS))) {
+ my $namespace = $tmp->{'Namespace'};
+ $namespace = "chroot"
+ if !$tmp->{'Namespace'};
+ $chroots->{$namespace} = {}
+ if (!exists($chroots->{$namespace}));
+ $chroots->{$namespace}->{$tmp->{'Name'}} = $tmp;
+ foreach my $alias (split(/\s+/, $tmp->{'Aliases'})) {
+ $chroots->{$namespace}->{$alias} = $tmp;
+ }
+ }
+ close CHROOTS or die "Can't close schroot pipe";
+
+ $self->set('Chroots', $chroots);
+}
+
+sub _create {
+ my $self = shift;
+ my $chroot_id = shift;
+
+ my $chroot = undef;
+
+ if (defined($chroot_id)) {
+ $chroot = Sbuild::ChrootSchroot->new($self->get('Config'), $chroot_id);
+ }
+
+ return $chroot;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootInfoSudo.pm b/lib/Sbuild/ChrootInfoSudo.pm
new file mode 100644
index 0000000..5e0d28f
--- /dev/null
+++ b/lib/Sbuild/ChrootInfoSudo.pm
@@ -0,0 +1,120 @@
+#
+# ChrootInfo.pm: chroot utility library for sbuild
+# Copyright © 2005-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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootInfoSudo;
+
+use Sbuild::ChrootInfo;
+use Sbuild::ChrootSudo;
+
+use strict;
+use warnings;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ChrootInfo);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub get_info {
+ my $self = shift;
+ my $chroot = shift;
+
+ $chroot =~ /(\S+):(\S+)/;
+ my ($namespace, $chrootname) = ($1, $2);
+
+ my $info = undef;
+
+ if (exists($self->get('Chroots')->{$namespace}) &&
+ defined($self->get('Chroots')->{$namespace}) &&
+ exists($self->get('Chroots')->{$namespace}->{$chrootname})) {
+ $info = $self->get('Chroots')->{$namespace}->{$chrootname}
+ }
+
+ return $info;
+}
+
+sub get_info_all {
+ my $self = shift;
+
+ my $chroots = {};
+ # All sudo chroots are in the chroot namespace.
+ my $namespace = "chroot";
+ $chroots->{$namespace} = {};
+
+ my $build_dir = $self->get_conf('BUILD_DIR');
+
+ # TODO: Configure $build_dir as $sudo_chroot_dir
+ foreach (glob($self->get_conf('SBUILD_MODE') eq "user" ?
+ "/etc/sbuild/chroot/*" :
+ "$build_dir/chroot-*")) {
+ my %tmp = ('Priority' => 0,
+ 'Location' => $_,
+ 'Session Purged' => 0);
+ if (-d $tmp{'Location'}) {
+ my $name = $_;
+ if ($self->get_conf('SBUILD_MODE') eq "user") {
+ $name =~ s/^\/etc\/sbuild\/chroot\///;
+ } else {
+ $name =~ s/\Q${build_dir}\/chroot-\E//;
+ }
+ if ($self->get_conf('DEBUG')) {
+ print STDERR "Found chroot $name\n";
+ foreach (sort keys %tmp) {
+ print STDERR " $_ $tmp{$_}\n";
+ }
+ }
+
+ $tmp{'Name'} = $name;
+ $tmp{'Namespace'} = $namespace;
+
+ $chroots->{$namespace}->{$name} = \%tmp;
+ }
+ }
+
+ $self->set('Chroots', $chroots);
+}
+
+sub _create {
+ my $self = shift;
+ my $chroot_id = shift;
+
+ my $chroot = undef;
+
+ if (defined($chroot_id)) {
+ $chroot = Sbuild::ChrootSudo->new($self->get('Config'), $chroot_id);
+ }
+
+ return $chroot;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootInfoUnshare.pm b/lib/Sbuild/ChrootInfoUnshare.pm
new file mode 100644
index 0000000..adb1a9a
--- /dev/null
+++ b/lib/Sbuild/ChrootInfoUnshare.pm
@@ -0,0 +1,94 @@
+#
+# ChrootInfoUnshare.pm: chroot utility library for sbuild
+# Copyright © 2018 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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootInfoUnshare;
+
+use Sbuild::ChrootInfo;
+use Sbuild::ChrootUnshare;
+
+use strict;
+use warnings;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ChrootInfo);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub get_info_all {
+ my $self = shift;
+
+ my $chroots = {};
+
+ my $xdg_cache_home = $self->get_conf('HOME') . "/.cache/sbuild";
+ if (defined($ENV{'XDG_CACHE_HOME'})) {
+ $xdg_cache_home = $ENV{'XDG_CACHE_HOME'} . '/sbuild';
+ }
+
+ my $num_found = 0;
+ if (opendir my $dh, $xdg_cache_home) {
+ while (defined(my $file = readdir $dh)) {
+ next if $file eq '.' || $file eq '..';
+ next if $file !~ /^[^-]+-[^-]+(-[^-]+)?(-sbuild)?\.t.+$/;
+ my $isdir = -d "$xdg_cache_home/$file";
+ $file =~ s/\.t.+$//; # chop off extension
+ if (! $isdir) {
+ $chroots->{'chroot'}->{$file} = 1;
+ }
+ $chroots->{'source'}->{$file} = 1;
+ $num_found += 1;
+ }
+ closedir $dh;
+ }
+
+ if ($num_found == 0) {
+ print STDERR "I: No tarballs found in $xdg_cache_home\n";
+ }
+
+ $self->set('Chroots', $chroots);
+}
+
+sub _create {
+ my $self = shift;
+ my $chroot_id = shift;
+
+ my $chroot = undef;
+
+ if (defined($chroot_id)) {
+ $chroot = Sbuild::ChrootUnshare->new($self->get('Config'), $chroot_id);
+ }
+
+ return $chroot;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootPlain.pm b/lib/Sbuild/ChrootPlain.pm
new file mode 100644
index 0000000..f293a12
--- /dev/null
+++ b/lib/Sbuild/ChrootPlain.pm
@@ -0,0 +1,150 @@
+#
+# Chroot.pm: chroot library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2006 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::ChrootPlain;
+
+use strict;
+use warnings;
+
+use Sbuild qw(shellescape);
+
+use POSIX;
+use FileHandle;
+use File::Temp ();
+
+use Sbuild::Sysconfig;
+
+BEGIN {
+ use Exporter ();
+ use Sbuild::Chroot;
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Chroot);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $chroot_id = shift;
+
+ my $self = $class->SUPER::new($conf, $chroot_id);
+ bless($self, $class);
+
+ # Only run split, because plain makes no guarantee that networking
+ # works inside the chroot.
+ $self->set('Split', 1);
+
+ return $self;
+}
+
+sub begin_session {
+ my $self = shift;
+
+ $self->set('Priority', 0);
+ $self->set('Location', $self->get('Chroot ID'));
+ $self->set('Session Purged', 0);
+
+ return 0 if !defined $self->get('Chroot ID');
+
+ # only setup plain chroot if the directory is not the root of the host
+ if ($self->get('Location') ne '/') {
+ return 0 if !$self->_setup_options();
+ }
+
+ return 1;
+}
+
+sub end_session {
+ my $self = shift;
+
+ # No-op for sudo.
+
+ return 1;
+}
+
+sub get_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ # Command to run. If I have a string, use it. Otherwise use the list-ref
+ my $command = $options->{'INTCOMMAND_STR'} // $options->{'INTCOMMAND'};
+
+ my $user = $options->{'USER'}; # User to run command under
+ my $dir; # Directory to use (optional)
+ $dir = $self->get('Defaults')->{'DIR'} if
+ (defined($self->get('Defaults')) &&
+ defined($self->get('Defaults')->{'DIR'}));
+ $dir = $options->{'DIR'} if
+ defined($options->{'DIR'}) && $options->{'DIR'};
+
+ if (!defined $user || $user eq "") {
+ $user = $self->get_conf('USERNAME');
+ }
+
+ my @cmdline;
+
+ if (!defined($dir)) {
+ $dir = '/';
+ }
+
+ my $need_chroot = 0;
+ $need_chroot = 1
+ if ($self->get('Location') ne '/');
+
+ my $need_su = 0;
+ $need_su = 1
+ if (($need_chroot && $user ne 'root') ||
+ (!$need_chroot && $user ne $self->get_conf('USERNAME')));
+
+ push(@cmdline, $self->get_conf('SUDO'))
+ if (($need_chroot || $need_su) && $user ne 'root');
+ push(@cmdline, '/usr/sbin/chroot', $self->get('Location'))
+ if ($need_chroot);
+ push(@cmdline, $self->get_conf('SU'), "$user", '-s')
+ if ($need_su);
+
+
+ if( ref $command ) {
+ my $shellcommand;
+ foreach (@$command) {
+ my $tmp = $_;
+ if ($shellcommand) {
+ $shellcommand .= " " . shellescape $tmp;
+ } else {
+ $shellcommand = shellescape $tmp;
+ }
+ }
+ push(@cmdline, '/bin/sh', '-c', "cd " . (shellescape $dir) . " && $shellcommand");
+ } else {
+ push(@cmdline, '/bin/sh', '-c', "cd " . (shellescape $dir) . " && ( $command )");
+ }
+
+ $options->{'USER'} = $user;
+ $options->{'COMMAND'} = ref($command) ? $command : [split(/\s+/, $command)];
+ $options->{'EXPCOMMAND'} = \@cmdline;
+ $options->{'CHDIR'} = undef;
+ $options->{'DIR'} = $dir;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootRoot.pm b/lib/Sbuild/ChrootRoot.pm
new file mode 100644
index 0000000..7f51572
--- /dev/null
+++ b/lib/Sbuild/ChrootRoot.pm
@@ -0,0 +1,53 @@
+#
+# ChrootRoot.pm: Run commands on the root filesystem
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::ChrootRoot;
+
+use strict;
+use warnings;
+
+use Sbuild::ChrootPlain;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ChrootPlain);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+
+ my $self = $class->SUPER::new($conf, '/');
+ bless($self, $class);
+
+ # There's no difference between split and unsplit when running on
+ # the root filesystem.
+ $self->set('Split', 0);
+
+ return $self;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootSchroot.pm b/lib/Sbuild/ChrootSchroot.pm
new file mode 100644
index 0000000..8c88284
--- /dev/null
+++ b/lib/Sbuild/ChrootSchroot.pm
@@ -0,0 +1,173 @@
+#
+# Chroot.pm: chroot library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::ChrootSchroot;
+
+use strict;
+use warnings;
+
+use Sbuild qw(shellescape);
+
+BEGIN {
+ use Exporter ();
+ use Sbuild::Chroot;
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Chroot);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $chroot_id = shift;
+
+ my $self = $class->SUPER::new($conf, $chroot_id);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub begin_session {
+ my $self = shift;
+ my $chroot = $self->get('Chroot ID');
+
+ return 0 if !defined $chroot;
+
+ # Don't use namespaces in compat mode.
+ if ($Sbuild::Sysconfig::compat_mode) {
+ $chroot =~ s/^[^:]+://msx;
+ }
+
+ my $schroot_session=readpipe($self->get_conf('SCHROOT') . " -c $chroot --begin-session");
+ chomp($schroot_session);
+ if ($?) {
+ print STDERR "Chroot setup failed\n";
+ return 0;
+ }
+
+ $self->set('Session ID', $schroot_session);
+ print STDERR "Setting up chroot $chroot (session id $schroot_session)\n"
+ if $self->get_conf('DEBUG');
+
+ my $info = $self->get('Chroots')->get_info($schroot_session);
+ if (defined($info) &&
+ defined($info->{'Location'}) && -d $info->{'Location'}) {
+ $self->set('Priority', $info->{'Priority'});
+ $self->set('Location', $info->{'Location'});
+ $self->set('Session Purged', $info->{'Session Purged'});
+ } else {
+ print STDERR $self->get('Chroot ID') . " chroot does not exist\n";
+ return 0;
+ }
+
+ return 0 if !$self->_setup_options();
+
+ return 1;
+}
+
+sub end_session {
+ my $self = shift;
+
+ return if $self->get('Session ID') eq "";
+
+ print STDERR "Cleaning up chroot (session id " . $self->get('Session ID') . ")\n"
+ if $self->get_conf('DEBUG');
+ system($self->get_conf('SCHROOT'), '-c', $self->get('Session ID'), '--end-session');
+ $self->set('Session ID', "");
+ if ($?) {
+ print STDERR "Chroot cleanup failed\n";
+ return 0;
+ }
+
+ return 1;
+}
+
+sub _get_exec_argv {
+ my $self = shift;
+ my $dir = shift;
+ my $user = shift;
+
+ return ($self->get_conf('SCHROOT'),
+ '-d', $dir,
+ '-c', $self->get('Session ID'),
+ '--run-session',
+ @{$self->get_conf('SCHROOT_OPTIONS')},
+ '-u', "$user", '-p', '--');
+}
+
+sub get_internal_exec_string {
+ my $self = shift;
+
+ return if $self->get('Session ID') eq "";
+
+ return join " ", (map
+ { shellescape $_ }
+ $self->_get_exec_argv('/', 'root'));
+}
+
+sub get_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ return if $self->get('Session ID') eq "";
+
+ if (defined($options->{'DISABLE_NETWORK'}) && $options->{'DISABLE_NETWORK'}) {
+ print STDERR "Disabling the network for this command was requested but the schroot backend doesn't support this feature yet: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=802849\n" if $self->get_conf('DEBUG');
+ }
+
+ # Command to run. If I have a string, use it. Otherwise use the list-ref
+ my $command = $options->{'INTCOMMAND_STR'} // $options->{'INTCOMMAND'};
+
+ my $user = $options->{'USER'}; # User to run command under
+ my $dir; # Directory to use (optional)
+ $dir = $self->get('Defaults')->{'DIR'} if
+ (defined($self->get('Defaults')) &&
+ defined($self->get('Defaults')->{'DIR'}));
+ $dir = $options->{'DIR'} if
+ defined($options->{'DIR'}) && $options->{'DIR'};
+
+ if (!defined $user || $user eq "") {
+ $user = $self->get_conf('USERNAME');
+ }
+
+ if (!defined($dir)) {
+ $dir = '/';
+ }
+
+ my @cmdline = $self->_get_exec_argv($dir, $user);
+
+ if (ref $command) {
+ push @cmdline, @$command;
+ } else {
+ push @cmdline, ('/bin/sh', '-c', $command);
+ $command = [split(/\s+/, $command)];
+ }
+ $options->{'USER'} = $user;
+ $options->{'COMMAND'} = $command;
+ $options->{'EXPCOMMAND'} = \@cmdline;
+ $options->{'CHDIR'} = undef;
+ $options->{'DIR'} = $dir;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootSetup.pm b/lib/Sbuild/ChrootSetup.pm
new file mode 100644
index 0000000..223f273
--- /dev/null
+++ b/lib/Sbuild/ChrootSetup.pm
@@ -0,0 +1,252 @@
+#
+# ChrootSetup.pm: chroot maintenance operations
+# 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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootSetup;
+
+use strict;
+use warnings;
+
+use File::Temp qw(tempfile);
+use Sbuild qw($devnull shellescape);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(basesetup shell hold_packages unhold_packages
+ list_packages set_package_status);
+}
+
+sub basesetup ($$);
+sub shell ($$);
+sub hold_packages ($$@);
+sub unhold_packages ($$@);
+sub list_packages ($$@);
+sub set_package_status ($$$@);
+
+sub basesetup ($$) {
+ my $session = shift;
+ my $conf = shift;
+
+ # Add sbuild group
+ $session->run_command(
+ { COMMAND => ['getent', 'group', 'sbuild'],
+ USER => 'root',
+ STREAMIN => $devnull,
+ STREAMOUT => $devnull,
+ DIR => '/' });
+ if ($?) {
+ # This will require root privileges. However, this should
+ # only get run at initial chroot setup time.
+ if ($session->groupadd("--system", "sbuild")) {
+ print STDERR "E: Failed to create group sbuild\n";
+ return $?
+ }
+ }
+
+ # Add users
+ foreach my $user ('sbuild', $session->get_conf('BUILD_USER')) {
+ $session->run_command(
+ { COMMAND => ['getent', 'passwd', $user],
+ USER => 'root',
+ STREAMIN => $devnull,
+ STREAMOUT => $devnull,
+ DIR => '/' });
+ if ($?) {
+ # This will require root privileges. However, this should
+ # only get run at initial chroot setup time.
+ if ($session->useradd("--system",
+ '--home-dir', '/var/lib/sbuild', '--no-create-home',
+ '--shell', '/bin/bash', '--gid', 'sbuild',
+ '--comment', 'Debian source builder,,,', $user)) {
+ print STDERR "E: Failed to create user $user\n";
+ return $?
+ }
+ }
+ }
+
+ my $build_path = '/build';
+ if (defined($session->get_conf('BUILD_PATH')) && $session->get_conf('BUILD_PATH')) {
+ $build_path = $session->get_conf('BUILD_PATH');
+ }
+
+ $session->run_command(
+ { COMMAND => ['/bin/sh', '-c',
+ 'set -e; if [ ! -d ' . (shellescape $build_path) . ' ] ; then mkdir -p -m 0775 ' . (shellescape $build_path) . '; fi'],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to create build directory $build_path\n";
+ return $?
+ }
+
+ $session->run_command(
+ { COMMAND => ['chown', 'sbuild:sbuild', $build_path],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to set sbuild:sbuild ownership on $build_path\n";
+ return $?
+ }
+
+ $session->run_command(
+ { COMMAND => ['chmod', '02770', $build_path],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to set 0750 permissions on $build_path\n";
+ return $?
+ }
+
+ $session->run_command(
+ { COMMAND => ['/bin/sh', '-c',
+ 'set -e; if [ ! -d /var/lib/sbuild ] ; then mkdir -m 2775 /var/lib/sbuild; fi'],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to create build directory /var/lib/sbuild\n";
+ return $?
+ }
+
+ $session->run_command(
+ { COMMAND => ['/bin/sh', '-c',
+ 'set -e; if [ ! -d /var/lib/sbuild/srcdep-lock ] ; then mkdir -m 2770 /var/lib/sbuild/srcdep-lock; fi'],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to create sbuild directory /var/lib/sbuild/srcdep-lock\n";
+ return $?
+ }
+
+ $session->run_command(
+ { COMMAND => ['chown', '-R', 'sbuild:sbuild', '/var/lib/sbuild'],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to set sbuild:sbuild ownership on /var/lib/sbuild/\n";
+ return $?
+ }
+
+ $session->run_command(
+ { COMMAND => ['chmod', '02775', '/var/lib/sbuild'],
+ USER => 'root',
+ DIR => '/' });
+ if ($?) {
+ print STDERR "E: Failed to set 02775 permissions on /var/lib/sbuild/\n";
+ return $?
+ }
+
+ # Set up debconf selections.
+ my $pipe = $session->pipe_command(
+ { COMMAND => ['/usr/bin/debconf-set-selections'],
+ PIPE => 'out',
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+
+ if (!$pipe) {
+ warn "Cannot open pipe: $!\n";
+ } else {
+ foreach my $selection ('man-db man-db/auto-update boolean false') {
+ print $pipe "$selection\n";
+ }
+ close($pipe);
+ if ($?) {
+ print STDERR "E: debconf-set-selections failed\n";
+ return $?
+ }
+ }
+
+ return 0;
+}
+
+sub shell ($$) {
+ my $session = shift;
+ my $conf = shift;
+
+ $session->run_command(
+ { COMMAND => ['/bin/sh'],
+ PRIORITY => 1,
+ USER => $conf->get('BUILD_USER'),
+ STREAMIN => \*STDIN,
+ STREAMOUT => \*STDOUT,
+ STREAMERR => \*STDERR });
+ return $?
+}
+
+sub hold_packages ($$@) {
+ my $session = shift;
+ my $conf = shift;
+
+ my $status = set_package_status($session, $conf, "hold", @_);
+
+ return $status;
+}
+
+sub unhold_packages ($$@) {
+ my $session = shift;
+ my $conf = shift;
+
+ my $status = set_package_status($session, $conf, "install", @_);
+
+ return $status;
+}
+
+sub list_packages ($$@) {
+ my $session = shift;
+ my $conf = shift;
+
+ $session->run_command(
+ {COMMAND => ['dpkg', '--list', @_],
+ USER => 'root',
+ PRIORITY => 0});
+ return $?;
+}
+
+sub set_package_status ($$$@) {
+ my $session = shift;
+ my $conf = shift;
+ my $status = shift;
+
+ my $pipe = $session->pipe_command(
+ {COMMAND => ['dpkg', '--set-selections'],
+ PIPE => 'out',
+ USER => 'root',
+ PRIORITY => 0});
+
+ if (!$pipe) {
+ print STDERR "Can't run dpkg --set-selections in chroot\n";
+ return 1;
+ }
+
+ foreach (@_) {
+ print $pipe "$_ $status\n";
+ }
+
+ if (!close $pipe) {
+ print STDERR "Can't run dpkg --set-selections in chroot\n";
+ }
+
+ return $?;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootSudo.pm b/lib/Sbuild/ChrootSudo.pm
new file mode 100644
index 0000000..e258fd0
--- /dev/null
+++ b/lib/Sbuild/ChrootSudo.pm
@@ -0,0 +1,137 @@
+#
+# Chroot.pm: chroot library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::ChrootSudo;
+
+use strict;
+use warnings;
+
+use Sbuild qw(shellescape);
+use Sbuild::Sysconfig;
+
+BEGIN {
+ use Exporter ();
+ use Sbuild::Chroot;
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Chroot);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $chroot_id = shift;
+
+ my $self = $class->SUPER::new($conf, $chroot_id);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub begin_session {
+ my $self = shift;
+ my $chroot = $self->get('Chroot ID');
+
+ return 0 if !defined $chroot;
+
+ my $info = $self->get('Chroots')->get_info($chroot);
+
+ print STDERR "Setting up chroot $chroot\n"
+ if $self->get_conf('DEBUG');
+
+ if (defined($info) &&
+ defined($info->{'Location'}) && -d $info->{'Location'}) {
+ $self->set('Priority', $info->{'Priority'});
+ $self->set('Location', $info->{'Location'});
+ $self->set('Session Purged', $info->{'Session Purged'});
+ } else {
+ die $self->get('Chroot ID') . " chroot does not exist\n";
+ }
+
+ if ($self->get('Location') ne '/') {
+ return 0 if !$self->_setup_options();
+ }
+
+ return 1;
+}
+
+sub end_session {
+ my $self = shift;
+
+ # No-op for sudo.
+
+ return 1;
+}
+
+sub get_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ # Command to run. If I have a string, use it. Otherwise use the list-ref
+ my $command = $options->{'INTCOMMAND_STR'} // $options->{'INTCOMMAND'};
+
+ my $user = $options->{'USER'}; # User to run command under
+ my $dir; # Directory to use (optional)
+ $dir = $self->get('Defaults')->{'DIR'} if
+ (defined($self->get('Defaults')) &&
+ defined($self->get('Defaults')->{'DIR'}));
+ $dir = $options->{'DIR'} if
+ defined($options->{'DIR'}) && $options->{'DIR'};
+
+ if (!defined $user || $user eq "") {
+ $user = $self->get_conf('USERNAME');
+ }
+
+ my @cmdline;
+
+ if (!defined($dir)) {
+ $dir = '/';
+ }
+
+ @cmdline = ($self->get_conf('SUDO'), '/usr/sbin/chroot', $self->get('Location'),
+ $self->get_conf('SU'), "$user", '-s');
+
+ if( ref $command ) {
+ my $shellcommand;
+ foreach (@$command) {
+ my $tmp = $_;
+ if ($shellcommand) {
+ $shellcommand .= " " . shellescape $tmp;
+ } else {
+ $shellcommand = shellescape $tmp;
+ }
+ }
+ push(@cmdline, '/bin/sh', '-c', "cd " . (shellescape $dir) . " && $shellcommand");
+ } else {
+ push(@cmdline, '/bin/sh', '-c', "cd " . (shellescape $dir) . " && ( $command )");
+ }
+
+ $options->{'USER'} = $user;
+ $options->{'COMMAND'} = ref($command) ? $command : [split(/\s+/, $command)];
+ $options->{'EXPCOMMAND'} = \@cmdline;
+ $options->{'CHDIR'} = undef;
+ $options->{'DIR'} = $dir;
+}
+
+1;
diff --git a/lib/Sbuild/ChrootUnshare.pm b/lib/Sbuild/ChrootUnshare.pm
new file mode 100644
index 0000000..91a7fa4
--- /dev/null
+++ b/lib/Sbuild/ChrootUnshare.pm
@@ -0,0 +1,398 @@
+#
+# ChrootUnshare.pm: chroot library for sbuild
+# Copyright © 2018 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/>.
+#
+#######################################################################
+
+package Sbuild::ChrootUnshare;
+
+use strict;
+use warnings;
+
+use English;
+use Sbuild::Utility;
+use File::Temp qw(mkdtemp tempfile);
+use File::Copy;
+use Cwd qw(abs_path);
+use Sbuild qw(shellescape);
+
+BEGIN {
+ use Exporter ();
+ use Sbuild::Chroot;
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Chroot);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $chroot_id = shift;
+
+ my $self = $class->SUPER::new($conf, $chroot_id);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub begin_session {
+ my $self = shift;
+ my $chroot = $self->get('Chroot ID');
+
+ return 0 if !defined $chroot;
+
+ my $namespace = undef;
+ if ($chroot =~ m/^(chroot|source):(.+)$/) {
+ $namespace = $1;
+ $chroot = $2;
+ }
+
+ my $tarball = undef;
+ if ($chroot =~ '/') {
+ if (! -e $chroot) {
+ print STDERR "Chroot $chroot does not exist\n";
+ return 0;
+ }
+ $tarball = abs_path($chroot);
+ } else {
+ my $xdg_cache_home = $self->get_conf('HOME') . "/.cache/sbuild";
+ if (defined($ENV{'XDG_CACHE_HOME'})) {
+ $xdg_cache_home = $ENV{'XDG_CACHE_HOME'} . '/sbuild';
+ }
+
+ if (opendir my $dh, $xdg_cache_home) {
+ while (defined(my $file = readdir $dh)) {
+ next if $file eq '.' || $file eq '..';
+ my $path = "$xdg_cache_home/$file";
+ # FIXME: support directory chroots
+ #if (-d $path) {
+ # if ($file eq $chroot) {
+ # $tarball = $path;
+ # last;
+ # }
+ #} else {
+ if ($file =~ /^$chroot\.t.+$/) {
+ $tarball = $path;
+ last;
+ }
+ #}
+ }
+ closedir $dh;
+ }
+
+ if (!defined($tarball)) {
+ print STDERR "Unable to find $chroot in $xdg_cache_home\n";
+ return 0;
+ }
+ }
+
+ my @idmap = read_subuid_subgid;
+
+ # sanity check
+ if ( scalar(@idmap) != 2
+ || $idmap[0][0] ne 'u'
+ || $idmap[1][0] ne 'g'
+ || length $idmap[0][1] == 0
+ || length $idmap[0][2] == 0
+ || length $idmap[1][1] == 0
+ || length $idmap[1][2] == 0)
+ {
+ printf STDERR "invalid idmap\n";
+ return 0;
+ }
+
+ $self->set('Uid Gid Map', \@idmap);
+
+ my @cmd;
+ my $exit;
+
+ if(!test_unshare) {
+ print STDERR "E: unable to to unshare\n";
+ return 0;
+ }
+
+ my @unshare_cmd = get_unshare_cmd({IDMAP => \@idmap});
+
+ my $rootdir = mkdtemp($self->get_conf('UNSHARE_TMPDIR_TEMPLATE'));
+
+ # $REAL_GROUP_ID is a space separated list of all groups the current user
+ # is in with the first group being the result of getgid(). We reduce the
+ # list to the first group by forcing it to be numeric
+ my $outer_gid = $REAL_GROUP_ID+0;
+ @cmd = (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', $rootdir);
+ if ($self->get_conf('DEBUG')) {
+ printf STDERR "running @cmd\n";
+ }
+ system(@cmd);
+ $exit = $? >> 8;
+ if ($exit) {
+ print STDERR "bad exit status ($exit): @cmd\n";
+ return 0;
+ }
+
+ if (! -e $tarball) {
+ print STDERR "$tarball does not exist, check \$unshare_tarball config option\n";
+ return 0;
+ }
+
+ # The tarball might be in a location where it cannot be accessed by the
+ # user from within the unshared namespace
+ if (! -r $tarball) {
+ print STDERR "$tarball is not readable\n";
+ return 0;
+ }
+
+ print STDOUT "Unpacking $tarball to $rootdir...\n";
+ @cmd = (@unshare_cmd, 'tar',
+ '--exclude=./dev/urandom',
+ '--exclude=./dev/random',
+ '--exclude=./dev/full',
+ '--exclude=./dev/null',
+ '--exclude=./dev/console',
+ '--exclude=./dev/zero',
+ '--exclude=./dev/tty',
+ '--exclude=./dev/ptmx',
+ '--directory', $rootdir,
+ '--extract'
+ );
+ push @cmd, get_tar_compress_options($tarball);
+
+ if ($self->get_conf('DEBUG')) {
+ printf STDERR "running @cmd\n";
+ }
+ my $pid = open(my $out, '|-', @cmd);
+ if (!defined($pid)) {
+ print STDERR "Can't fork: $!\n";
+ return 0;
+ }
+ if (copy($tarball, $out) != 1) {
+ print STDERR "copy() failed: $!\n";
+ return 0;
+ }
+ close($out);
+ $exit = $? >> 8;
+ if ($exit) {
+ print STDERR "bad exit status ($exit): @cmd\n";
+ return 0;
+ }
+
+ $self->set('Session ID', $rootdir);
+
+ $self->set('Location', '/sbuild-unshare-dummy-location');
+
+ $self->set('Session Purged', 1);
+
+ # if a source type chroot was requested, then we need to memorize the
+ # tarball location for when the session is ended
+ if (defined($namespace) && $namespace eq "source") {
+ $self->set('Tarball', $tarball);
+ }
+
+ return 0 if !$self->_setup_options();
+
+ return 1;
+}
+
+sub end_session {
+ my $self = shift;
+
+ return if $self->get('Session ID') eq "";
+
+ if (defined($self->get('Tarball'))) {
+ my ($tmpfh, $tmpfile) = tempfile("XXXXXX");
+ my @program_list = ("/bin/tar", "-c", "-C", $self->get('Session ID'));
+ push @program_list, get_tar_compress_options($self->get('Tarball'));
+ push @program_list, './';
+
+ print "I: Creating tarball...\n";
+ open(my $in, '-|', get_unshare_cmd(
+ {IDMAP => $self->get('Uid Gid Map')}), @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";
+
+ move("$tmpfile", $self->get('Tarball'));
+ chmod 0644, $self->get('Tarball');
+
+ print "I: Done creating " . $self->get('Tarball') . "\n";
+ }
+
+ print STDERR "Cleaning up chroot (session id " . $self->get('Session ID') . ")\n"
+ if $self->get_conf('DEBUG');
+
+ # 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
+ my @cmd = (get_unshare_cmd({IDMAP => $self->get('Uid Gid Map')}), 'rm', '-rf', $self->get('Session ID'));
+ if ($self->get_conf('DEBUG')) {
+ printf STDERR "running @cmd\n";
+ }
+ system(@cmd);
+ # we ignore the exit status, because the command will fail to remove the
+ # unpack directory itself because of insufficient permissions
+
+ if(-d $self->get('Session ID') && !rmdir($self->get('Session ID'))) {
+ print STDERR "unable to remove " . $self->get('Session ID') . ": $!\n";
+ $self->set('Session ID', "");
+ return 0;
+ }
+
+ $self->set('Session ID', "");
+
+ return 1;
+}
+
+sub _get_exec_argv {
+ my $self = shift;
+ my $dir = shift;
+ my $user = shift;
+ my $disable_network = shift // 0;
+
+ # On systems with libnss-resolve installed there is no need for a
+ # /etc/resolv.conf. This works around this by adding 127.0.0.53 (default
+ # for systemd-resolved) in that case.
+ my $network_setup = '[ -f /etc/resolv.conf ] && cat /etc/resolv.conf > "$rootdir/etc/resolv.conf" || echo "nameserver 127.0.0.53" > "$rootdir/etc/resolv.conf";';
+ my $unshare = CLONE_NEWNS | CLONE_NEWPID | CLONE_NEWUTS | CLONE_NEWIPC;
+ if ($disable_network) {
+ $unshare |= CLONE_NEWNET;
+ $network_setup = 'ip link set lo up;> "$rootdir/etc/resolv.conf";';
+ }
+
+ my @bind_mounts = ();
+ for my $entry (@{$self->get_conf('UNSHARE_BIND_MOUNTS')}) {
+ push @bind_mounts, $entry->{directory}, $entry->{mountpoint};
+ }
+
+ return (
+ 'env', 'PATH=' . $self->get_conf('PATH'),
+ get_unshare_cmd({UNSHARE_FLAGS => $unshare, FORK => 1, IDMAP => $self->get('Uid Gid Map')}), 'sh', '-c', "
+ rootdir=\"\$1\"; shift;
+ user=\"\$1\"; shift;
+ dir=\"\$1\"; shift;
+ while [ \$# -gt 0 ]; do
+ if [ \"\$1\" = \"--\" ]; then shift; break; fi;
+ mkdir -p \"\$rootdir\$2\";
+ mount -o rbind \"\$1\" \"\$rootdir\$2\";
+ shift; shift;
+ done;
+ hostname sbuild;
+ echo \"127.0.0.1 localhost\\n127.0.1.1 sbuild\" > \"\$rootdir/etc/hosts\";
+ $network_setup
+ mkdir -p \"\$rootdir/dev\";
+ for f in null zero full random urandom tty console; do
+ touch \"\$rootdir/dev/\$f\";
+ chmod -rwx \"\$rootdir/dev/\$f\";
+ mount -o bind \"/dev/\$f\" \"\$rootdir/dev/\$f\";
+ done;
+ ln -sfT /proc/self/fd \"\$rootdir/dev/fd\";
+ ln -sfT /proc/self/fd/0 \"\$rootdir/dev/stdin\";
+ ln -sfT /proc/self/fd/1 \"\$rootdir/dev/stdout\";
+ ln -sfT /proc/self/fd/2 \"\$rootdir/dev/stderr\";
+ mkdir -p \"\$rootdir/dev/pts\";
+ mount -o noexec,nosuid,gid=5,mode=620,ptmxmode=666 -t devpts none \"\$rootdir/dev/pts\";
+ ln -sfT /dev/pts/ptmx \"\$rootdir/dev/ptmx\";
+ mkdir -p \"\$rootdir/dev/shm\";
+ mount -t tmpfs tmpfs \"\$rootdir/dev/shm\";
+ mkdir -p \"\$rootdir/sys\";
+ mount -o rbind /sys \"\$rootdir/sys\";
+ mkdir -p \"\$rootdir/proc\";
+ mount -t proc proc \"\$rootdir/proc\";
+ exec /usr/sbin/chroot \"\$rootdir\" /sbin/runuser -u \"\$user\" -- sh -c \"cd \\\"\\\$1\\\" && shift && \\\"\\\$@\\\"\" -- \"\$dir\" \"\$@\";
+ ", '--', $self->get('Session ID'), $user, $dir, @bind_mounts, '--'
+ );
+}
+
+sub get_internal_exec_string {
+ my $self = shift;
+
+ return join " ", (map
+ { shellescape $_ }
+ $self->_get_exec_argv('/', 'root'));
+}
+
+sub get_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ # Command to run. If I have a string, use it. Otherwise use the list-ref
+ my $command = $options->{'INTCOMMAND_STR'} // $options->{'INTCOMMAND'};
+
+ my $user = $options->{'USER'}; # User to run command under
+ my $dir; # Directory to use (optional)
+ $dir = $self->get('Defaults')->{'DIR'} if
+ (defined($self->get('Defaults')) &&
+ defined($self->get('Defaults')->{'DIR'}));
+ $dir = $options->{'DIR'} if
+ defined($options->{'DIR'}) && $options->{'DIR'};
+
+ if (!defined $user || $user eq "") {
+ $user = $self->get_conf('USERNAME');
+ }
+
+ if (!defined($dir)) {
+ $dir = '/';
+ }
+
+ my $disable_network = 0;
+ if (defined($options->{'DISABLE_NETWORK'}) && $options->{'DISABLE_NETWORK'}) {
+ $disable_network = 1;
+ }
+
+ my @cmdline = $self->_get_exec_argv($dir, $user, $disable_network);
+ if (ref $command) {
+ push @cmdline, @$command;
+ } else {
+ push @cmdline, ('/bin/sh', '-c', $command);
+ $command = [split(/\s+/, $command)];
+ }
+ $options->{'USER'} = $user;
+ $options->{'COMMAND'} = $command;
+ $options->{'EXPCOMMAND'} = \@cmdline;
+ $options->{'CHDIR'} = undef;
+ $options->{'DIR'} = $dir;
+}
+
+# create users from outside the chroot so we don't need user/groupadd inside.
+sub useradd {
+ my $self = shift;
+ my @args = @_;
+ my $rootdir = $self->get('Session ID');
+ my @idmap = read_subuid_subgid;
+ my @unshare_cmd = get_unshare_cmd({IDMAP => \@idmap});
+ return system(@unshare_cmd, "/usr/sbin/useradd", "--root", $rootdir, @args);
+}
+
+sub groupadd {
+ my $self = shift;
+ my @args = @_;
+ my $rootdir = $self->get('Session ID');
+ my @idmap = read_subuid_subgid;
+ my @unshare_cmd = get_unshare_cmd({IDMAP => \@idmap});
+ return system(@unshare_cmd, "/usr/sbin/groupadd", "--root", $rootdir, @args);
+}
+
+1;
diff --git a/lib/Sbuild/Conf.pm b/lib/Sbuild/Conf.pm
new file mode 100644
index 0000000..77e3db3
--- /dev/null
+++ b/lib/Sbuild/Conf.pm
@@ -0,0 +1,1582 @@
+#
+# Conf.pm: configuration library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2006-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/>.
+#
+#######################################################################
+
+package Sbuild::Conf;
+
+use strict;
+use warnings;
+
+use Cwd qw(cwd);
+use File::Spec;
+use POSIX qw(getgroups getgid);
+use Sbuild qw(isin);
+use Sbuild::ConfBase;
+use Sbuild::Sysconfig;
+use Dpkg::Build::Info;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(new setup read);
+}
+
+sub setup ($);
+sub read ($);
+
+sub new {
+ my $conf = Sbuild::ConfBase->new(@_);
+ Sbuild::Conf::setup($conf);
+ Sbuild::Conf::read($conf);
+
+ return $conf;
+}
+
+sub setup ($) {
+ my $conf = shift;
+
+ my $validate_program = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $program = $conf->get($key);
+
+ die "$key binary is not defined"
+ if !defined($program) || !$program;
+
+ # Emulate execvp behaviour by searching the binary in the PATH.
+ my @paths = split(/:/, $conf->get('PATH'));
+ # Also consider the empty path for absolute locations.
+ push (@paths, '');
+ my $found = 0;
+ foreach my $path (@paths) {
+ $found = 1 if (-x File::Spec->catfile($path, $program));
+ }
+
+ die "$key binary '$program' does not exist or is not executable"
+ if !$found;
+ };
+
+ my $validate_directory = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $directory = $conf->get($key);
+
+ die "$key directory is not defined"
+ if !defined($directory) || !$directory;
+
+ die "$key directory '$directory' does not exist"
+ if !-d $directory;
+ };
+
+ my $validate_append_version = sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ if (defined($conf->get('APPEND_TO_VERSION')) &&
+ $conf->get('APPEND_TO_VERSION') &&
+ $conf->get('BUILD_SOURCE') != 0) {
+ # See <http://bugs.debian.org/475777> for details
+ die "The --append-to-version option is incompatible with a source upload\n";
+ }
+
+ if ($conf->get('BUILD_SOURCE') &&
+ $conf->get('BIN_NMU')) {
+ print STDERR "Not building source package for binNMU\n";
+ $conf->_set_value('BUILD_SOURCE', 0);
+ }
+ };
+
+ my $set_signing_option = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $value = shift;
+ my $key = $entry->{'NAME'};
+ $conf->_set_value($key, $value);
+
+ my @signing_options = ();
+ push @signing_options, "-m".$conf->get('MAINTAINER_NAME')
+ if defined $conf->get('MAINTAINER_NAME');
+ push @signing_options, "-e".$conf->get('UPLOADER_NAME')
+ if defined $conf->get('UPLOADER_NAME');
+ $conf->set('SIGNING_OPTIONS', \@signing_options);
+ };
+
+ our $HOME = $conf->get('HOME');
+
+ my %sbuild_keys = (
+ 'CHROOT' => {
+ TYPE => 'STRING',
+ VARNAME => 'chroot',
+ GROUP => 'Chroot options',
+ DEFAULT => undef,
+ HELP => 'Default chroot (defaults to distribution[-arch][-sbuild])',
+ CLI_OPTIONS => ['-c', '--chroot']
+ },
+ 'BUILD_ARCH_ALL' => {
+ TYPE => 'BOOL',
+ VARNAME => 'build_arch_all',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ if (!defined($retval)) {
+ if ($conf->get('BUILD_ARCH') ne $conf->get('HOST_ARCH')) {
+ # default for cross
+ $retval = 0;
+ } else {
+ # default for native
+ $retval = 1;
+ }
+ }
+
+ return $retval;
+ },
+ HELP => 'Build architecture: all packages by default.',
+ CLI_OPTIONS => ['--arch-all', '--no-arch-all']
+ },
+ 'BUILD_ARCH_ANY' => {
+ TYPE => 'BOOL',
+ VARNAME => 'build_arch_any',
+ GROUP => 'Build options',
+ DEFAULT => 1,
+ HELP => 'Build architecture: any packages by default.',
+ CLI_OPTIONS => ['--arch-any', '--no-arch-any']
+ },
+ 'BUILD_PROFILES' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_profiles',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ if (!defined($retval)) {
+ if ($conf->get('BUILD_ARCH') ne $conf->get('HOST_ARCH')) {
+ # default for cross
+ $retval = $ENV{'DEB_BUILD_PROFILES'} || 'cross nocheck';
+ } else {
+ # default for native
+ $retval = $ENV{'DEB_BUILD_PROFILES'} || '';
+ }
+ }
+
+ return $retval;
+ },
+ HELP => 'Build profiles. Separated by spaces. Defaults to the value of the DEB_BUILD_PROFILES environment variable when building natively and to the cross and nocheck profiles when cross-building.',
+ CLI_OPTIONS => ['--profiles']
+ },
+ 'NOLOG' => {
+ TYPE => 'BOOL',
+ VARNAME => 'nolog',
+ GROUP => 'Logging options',
+ DEFAULT => 0,
+ HELP => 'Disable use of log file',
+ CLI_OPTIONS => ['-n', '--nolog']
+ },
+ 'SUDO' => {
+ TYPE => 'STRING',
+ VARNAME => 'sudo',
+ GROUP => 'Programs',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('CHROOT_MODE') eq 'split' ||
+ ($conf->get('CHROOT_MODE') eq 'schroot' &&
+ $conf->get('CHROOT_SPLIT'))) {
+ $validate_program->($conf, $entry);
+
+ local (%ENV) = %ENV; # make local environment
+ $ENV{'DEBIAN_FRONTEND'} = "noninteractive";
+ $ENV{'APT_CONFIG'} = "test_apt_config";
+ $ENV{'SHELL'} = '/bin/sh';
+
+ my $sudo = $conf->get('SUDO');
+ chomp( my $test_df = `$sudo sh -c 'echo \$DEBIAN_FRONTEND'` );
+ chomp( my $test_ac = `$sudo sh -c 'echo \$APT_CONFIG'` );
+ chomp( my $test_sh = `$sudo sh -c 'echo \$SHELL'` );
+
+ if ($test_df ne "noninteractive" ||
+ $test_ac ne "test_apt_config" ||
+ $test_sh ne '/bin/sh') {
+ print STDERR "$sudo is stripping APT_CONFIG, DEBIAN_FRONTEND and/or SHELL from the environment\n";
+ print STDERR "'Defaults:" . $conf->get('USERNAME') . " env_keep+=\"APT_CONFIG DEBIAN_FRONTEND SHELL\"' is not set in /etc/sudoers\n";
+ die "$sudo is incorrectly configured"
+ }
+ }
+ },
+ DEFAULT => 'sudo',
+ HELP => 'Path to sudo binary'
+ },
+ 'SU' => {
+ TYPE => 'STRING',
+ VARNAME => 'su',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'su',
+ HELP => 'Path to su binary'
+ },
+ 'SCHROOT' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('CHROOT_MODE') eq 'schroot') {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => 'schroot',
+ HELP => 'Path to schroot binary'
+ },
+ 'SCHROOT_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'schroot_options',
+ GROUP => 'Programs',
+ DEFAULT => ['-q'],
+ HELP => 'Additional command-line options for schroot'
+ },
+ 'UNSHARE_TMPDIR_TEMPLATE' => {
+ TYPE => 'STRING',
+ VARNAME => 'unshare_tmpdir_template',
+ GROUP => 'Programs',
+ DEFAULT => '/tmp/tmp.sbuild.XXXXXXXXXX',
+ HELP => 'Template used to create the temporary unpack directory for the unshare chroot mode.'
+ # CLI_OPTIONS => ['--unshare-tmpdir-template']
+ },
+ 'UNSHARE_BIND_MOUNTS' => {
+ TYPE => 'ARRAY',
+ VARNAME => 'unshare_bind_mounts',
+ GROUP => 'Programs',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ for my $entry (@{$conf->get($key)}) {
+ die "$entry->{directory} doesn't exist" if ! -e $entry->{directory};
+ die "mountpoint $entry->{mountpoint} must be an absolute path inside the chroot" if $entry->{mountpoint} !~ /^\//;
+ }
+ },
+ DEFAULT => [],
+ HELP => 'Bind mount directories from the outside to a mountpoint inside the chroot in unshare mode.',
+ EXAMPLE => '$unshare_bind_mounts = [ { directory => "/home/path/outside", mountpoint => "/path/inside" } ];'
+ },
+ 'AUTOPKGTEST_VIRT_SERVER' => {
+ TYPE => 'STRING',
+ VARNAME => 'autopkgtest_virt_server',
+ GROUP => 'Programs',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $program = $conf->get($key);
+
+ # if the autopkgtest virtualization server name is only letters
+ # a-z then it is missing the autopkgtest-virt- prefix
+ if ($program =~ /^[a-z]+$/) {
+ $conf->set($key, "autopkgtest-virt-$program");
+ }
+
+ # Only validate if needed.
+ if ($conf->get('CHROOT_MODE') eq 'autopkgtest') {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => 'autopkgtest-virt-schroot',
+ HELP => 'Path to autopkgtest-virt-* binary, selecting the virtualization server.',
+ CLI_OPTIONS => ['--autopkgtest-virt-server']
+ },
+ 'AUTOPKGTEST_VIRT_SERVER_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'autopkgtest_virt_server_options',
+ GROUP => 'Programs',
+ DEFAULT => [],
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ my $dist = $conf->get('DISTRIBUTION');
+ my $hostarch = $conf->get('HOST_ARCH');
+ my %percent = (
+ '%' => '%',
+ 'a' => $hostarch, 'SBUILD_HOST_ARCH' => $hostarch,
+ 'r' => $dist, 'SBUILD_DISTRIBUTION' => $dist,
+ );
+
+ my $keyword_pat = join("|",
+ sort {length $b <=> length $a || $a cmp $b} keys %percent);
+ foreach (@{$retval}) {
+ s{
+ # Match a percent followed by a valid keyword
+ \%($keyword_pat)
+ }{
+ # Substitute with the appropriate value only if it's defined
+ $percent{$1} || $&
+ }msxge;
+ }
+ return $retval;
+ },
+ HELP => 'Additional command-line options for autopkgtest-virt-*',
+ CLI_OPTIONS => ['--autopkgtest-virt-server-opt', '--autopkgtest-virt-server-opts']
+ },
+ # Do not check for the existance of fakeroot because it's needed
+ # inside the chroot and not on the host
+ 'FAKEROOT' => {
+ TYPE => 'STRING',
+ VARNAME => 'fakeroot',
+ GROUP => 'Programs',
+ DEFAULT => 'fakeroot',
+ HELP => 'Path to fakeroot binary'
+ },
+ 'APT_GET' => {
+ TYPE => 'STRING',
+ VARNAME => 'apt_get',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'apt-get',
+ HELP => 'Path to apt-get binary'
+ },
+ 'APT_CACHE' => {
+ TYPE => 'STRING',
+ VARNAME => 'apt_cache',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'apt-cache',
+ HELP => 'Path to apt-cache binary'
+ },
+ 'APTITUDE' => {
+ TYPE => 'STRING',
+ VARNAME => 'aptitude',
+ GROUP => 'Programs',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('BUILD_DEP_RESOLVER') eq 'aptitude') {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => 'aptitude',
+ HELP => 'Path to aptitude binary'
+ },
+ 'XAPT' => {
+ TYPE => 'STRING',
+ VARNAME => 'xapt',
+ GROUP => 'Programs',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('BUILD_DEP_RESOLVER') eq 'xapt') {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => 'xapt'
+ },
+ 'DPKG_BUILDPACKAGE_USER_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'dpkg_buildpackage_user_options',
+ GROUP => 'Programs',
+ DEFAULT => [],
+ HELP => 'Additional command-line options for dpkg-buildpackage.',
+ CLI_OPTIONS => ['--debbuildopt', '--debbuildopts', '--jobs']
+ },
+ 'DPKG_FILE_SUFFIX' => {
+ TYPE => 'STRING',
+ VARNAME => 'dpkg_file_suffix',
+ GROUP => 'Programs',
+ DEFAULT => '',
+ HELP => 'Suffix to add to filename for files generated by dpkg-buildpackage',
+ CLI_OPTIONS => ['--dpkg-file-suffix']
+ },
+ 'DPKG_SOURCE' => {
+ TYPE => 'STRING',
+ VARNAME => 'dpkg_source',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'dpkg-source',
+ HELP => 'Path to dpkg-source binary'
+ },
+ 'DPKG_SOURCE_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'dpkg_source_opts',
+ GROUP => 'Programs',
+ DEFAULT => [],
+ HELP => 'Additional command-line options for dpkg-source',
+ CLI_OPTIONS => ['--dpkg-source-opt', '--dpkg-source-opts']
+ },
+ 'MD5SUM' => {
+ TYPE => 'STRING',
+ VARNAME => 'md5sum',
+ GROUP => 'Programs',
+ CHECK => $validate_program,
+ DEFAULT => 'md5sum',
+ HELP => 'Path to md5sum binary'
+ },
+ 'STATS_DIR' => {
+ TYPE => 'STRING',
+ VARNAME => 'stats_dir',
+ GROUP => 'Statistics',
+ IGNORE_DEFAULT => 1, # Don't dump the current home
+ DEFAULT => "$HOME/stats",
+ HELP => 'Directory for writing build statistics to',
+ CLI_OPTIONS => ['--stats-dir']
+ },
+ 'PACKAGE_CHECKLIST' => {
+ TYPE => 'STRING',
+ VARNAME => 'package_checklist',
+ GROUP => 'Chroot options',
+ DEFAULT => "$Sbuild::Sysconfig::paths{'SBUILD_LOCALSTATE_DIR'}/package-checklist",
+ HELP => 'Where to store list currently installed packages inside chroot'
+ },
+ 'BUILD_ENV_CMND' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_env_cmnd',
+ GROUP => 'Build options',
+ DEFAULT => "",
+ HELP => 'This command is run with the dpkg-buildpackage command line passed to it (in the chroot, if doing a chrooted build). It is used by the sparc buildd (which is sparc64) to call the wrapper script that sets the environment to sparc (32-bit). It could be used for other build environment setup scripts. Note that this is superseded by schroot\'s \'command-prefix\' option'
+ },
+ 'PGP_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'pgp_options',
+ GROUP => 'Build options',
+ DEFAULT => ['-us', '-uc'],
+ HELP => 'Additional signing options for dpkg-buildpackage'
+ },
+ 'LOG_DIR' => {
+ TYPE => 'STRING',
+ VARNAME => 'log_dir',
+ GROUP => 'Logging options',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $directory = $conf->get($key);
+ },
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ # user mode defaults to the build directory, while buildd mode
+ # defaults to $HOME/logs.
+ if (!defined($retval)) {
+ $retval = $conf->get('BUILD_DIR');
+ if ($conf->get('SBUILD_MODE') eq 'buildd') {
+ $retval = "$HOME/logs";
+ }
+ }
+
+ return $retval;
+ },
+ HELP => 'Directory for storing build logs. This defaults to \'.\' when SBUILD_MODE is set to \'user\' (the default), and to \'$HOME/logs\' when SBUILD_MODE is set to \'buildd\'.'
+ },
+ 'LOG_COLOUR' => {
+ TYPE => 'BOOL',
+ VARNAME => 'log_colour',
+ GROUP => 'Logging options',
+ DEFAULT => 1,
+ HELP => 'Add colour highlighting to interactive log messages (informational, warning and error messages). Log files will not be coloured.'
+ },
+ 'LOG_FILTER' => {
+ TYPE => 'BOOL',
+ VARNAME => 'log_filter',
+ GROUP => 'Logging options',
+ DEFAULT => 1,
+ HELP => 'Filter variable strings from log messages such as the chroot name and build directory'
+ },
+ 'LOG_DIR_AVAILABLE' => {
+ TYPE => 'BOOL',
+ GROUP => '__INTERNAL',
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $nolog = $conf->get('NOLOG');
+ my $directory = $conf->get('LOG_DIR');
+ my $log_dir_available = 1;
+
+ if ($nolog) {
+ $log_dir_available = 0;
+ } elsif ($conf->get('SBUILD_MODE') ne "buildd") {
+ if ($directory && ! -d $directory) {
+ $log_dir_available = 0;
+ }
+ } elsif ($directory && ! -d $directory &&
+ !mkdir $directory) {
+ # Only create the log dir in buildd mode
+ warn "Could not create '$directory': $!\n";
+ $log_dir_available = 0;
+ }
+
+ return $log_dir_available;
+ }
+ },
+ 'MAILTO' => {
+ TYPE => 'STRING',
+ VARNAME => 'mailto',
+ GROUP => 'Logging options',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ },
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ # Now, we might need to adjust the MAILTO based on the
+ # config data. We shouldn't do this if it was already
+ # explicitly set by the command line option:
+ if (defined($conf->get('MAILTO_FORCED_BY_CLI')) &&
+ !$conf->get('MAILTO_FORCED_BY_CLI')
+ && defined($conf->get('DISTRIBUTION'))
+ && $conf->get('DISTRIBUTION')
+ && defined($conf->get('MAILTO_HASH'))
+ && $conf->get('MAILTO_HASH')->{$conf->get('DISTRIBUTION')}) {
+ $retval = $conf->get('MAILTO_HASH')->{$conf->get('DISTRIBUTION')};
+ }
+
+ return $retval;
+ },
+ DEFAULT => "",
+ HELP => 'email address to mail build logs to',
+ CLI_OPTIONS => ['--mail-log-to']
+ },
+ 'MAILTO_FORCED_BY_CLI' => {
+ TYPE => 'BOOL',
+ GROUP => '__INTERNAL',
+ DEFAULT => 0
+ },
+ 'MAILTO_HASH' => {
+ TYPE => 'HASH:STRING',
+ VARNAME => 'mailto_hash',
+ GROUP => 'Logging options',
+ DEFAULT => {},
+ HELP => 'Like MAILTO, but per-distribution. This is a hashref mapping distribution name to MAILTO. Note that for backward compatibility, this is also settable using the hash %mailto (deprecated), rather than a hash reference.'
+ },
+ 'MAILFROM' => {
+ TYPE => 'STRING',
+ VARNAME => 'mailfrom',
+ GROUP => 'Logging options',
+ DEFAULT => "Source Builder <sbuild>",
+ HELP => 'email address set in the From line of build logs',
+ CLI_OPTIONS => ['--mailfrom']
+ },
+ 'COMPRESS_BUILD_LOG_MAILS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'compress_build_log_mails',
+ GROUP => 'Logging options',
+ DEFAULT => 1,
+ HELP => 'Should build log mails be compressed?'
+ },
+ 'MIME_BUILD_LOG_MAILS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'mime_build_log_mails',
+ GROUP => 'Logging options',
+ DEFAULT => 1,
+ HELP => 'Should build log mails be MIME encoded?'
+ },
+ 'PURGE_BUILD_DEPS' => {
+ TYPE => 'STRING',
+ VARNAME => 'purge_build_deps',
+ GROUP => 'Chroot options',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ die "Bad purge mode \'" .
+ $conf->get('PURGE_BUILD_DEPS') . "\'"
+ if !isin($conf->get('PURGE_BUILD_DEPS'),
+ qw(always successful never));
+ },
+ DEFAULT => 'always',
+ HELP => 'When to purge the build dependencies after a build; possible values are "never", "successful", and "always"',
+ CLI_OPTIONS => ['-p', '--purge', '--purge-deps']
+ },
+ 'PURGE_BUILD_DIRECTORY' => {
+ TYPE => 'STRING',
+ VARNAME => 'purge_build_directory',
+ GROUP => 'Chroot options',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ die "Bad purge mode \'" .
+ $conf->get('PURGE_BUILD_DIRECTORY') . "\'"
+ if !isin($conf->get('PURGE_BUILD_DIRECTORY'),
+ qw(always successful never));
+ },
+ DEFAULT => 'always',
+ HELP => 'When to purge the build directory after a build; possible values are "never", "successful", and "always"',
+ CLI_OPTIONS => ['-p', '--purge', '--purge-build']
+ },
+ 'PURGE_SESSION' => {
+ TYPE => 'STRING',
+ VARNAME => 'purge_session',
+ GROUP => 'Chroot options',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ die "Bad purge mode \'" .
+ $conf->get('PURGE_SESSION') . "\'"
+ if !isin($conf->get('PURGE_SESSION'),
+ qw(always successful never));
+ },
+ DEFAULT => 'always',
+ HELP => 'Purge the schroot session following a build. This is useful in conjunction with the --purge and --purge-deps options when using snapshot chroots, since by default the snapshot will be deleted. Possible values are "always" (default), "never", and "successful"',
+ CLI_OPTIONS => ['-p', '--purge', '--purge-session']
+ },
+ 'TOOLCHAIN_REGEX' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'toolchain_regex',
+ GROUP => 'Build options',
+ DEFAULT => ['binutils$',
+ 'dpkg-dev$',
+ 'gcc-[\d.]+$',
+ 'g\+\+-[\d.]+$',
+ 'libstdc\+\+',
+ 'libc[\d.]+-dev$',
+ 'linux-kernel-headers$',
+ 'linux-libc-dev$',
+ 'gnumach-dev$',
+ 'hurd-dev$',
+ 'kfreebsd-kernel-headers$'
+ ],
+ HELP => 'Regular expressions identifying toolchain packages. Note that for backward compatibility, this is also settable using the array @toolchain_regex (deprecated), rather than an array reference.'
+ },
+ 'STALLED_PKG_TIMEOUT' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'stalled_pkg_timeout',
+ GROUP => 'Build timeouts',
+ DEFAULT => 150, # minutes
+ HELP => 'Time (in minutes) of inactivity after which a build is terminated. Activity is measured by output to the log file.'
+ },
+ 'MAX_LOCK_TRYS' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'max_lock_trys',
+ GROUP => 'Build timeouts',
+ DEFAULT => 120,
+ HELP => 'Number of times to try waiting for a lock.'
+ },
+ 'LOCK_INTERVAL' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'lock_interval',
+ GROUP => 'Build timeouts',
+ DEFAULT => 5,
+ HELP => 'Lock wait interval (seconds). Maximum wait time is (max_lock_trys x lock_interval).'
+ },
+ 'CHROOT_MODE' => {
+ TYPE => 'STRING',
+ VARNAME => 'chroot_mode',
+ GROUP => 'Chroot options',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ die "Bad chroot mode \'" . $conf->get('CHROOT_MODE') . "\'"
+ if !isin($conf->get('CHROOT_MODE'),
+ qw(schroot sudo autopkgtest unshare));
+ },
+ DEFAULT => 'schroot',
+ HELP => 'Mechanism to use for chroot virtualisation. Possible value are "schroot" (default), "sudo", "autopkgtest" and "unshare".',
+ CLI_OPTIONS => ['--chroot-mode']
+ },
+ 'CHROOT_SPLIT' => {
+ TYPE => 'BOOL',
+ VARNAME => 'chroot_split',
+ GROUP => 'Chroot options',
+ DEFAULT => 0,
+ HELP => 'Run in split mode? In split mode, apt-get and dpkg are run on the host system, rather than inside the chroot.'
+ },
+ 'CHECK_SPACE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'check_space',
+ GROUP => 'Build options',
+ DEFAULT => 1,
+ HELP => 'Check free disk space prior to starting a build. sbuild requires the free space to be at least twice the size of the unpacked sources to allow a build to proceed. Can be disabled to allow building if space is very limited, but the threshold to abort a build has been exceeded despite there being sufficient space for the build to complete.'
+ },
+ 'BUILD_DIR' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_dir',
+ GROUP => 'Core options',
+ DEFAULT => cwd(),
+ IGNORE_DEFAULT => 1, # Don't dump class to config
+ EXAMPLE => '$build_dir = \'/home/pete/build\';',
+ CHECK => $validate_directory,
+ HELP => 'Output directory for build artifacts created by dpkg-buildpackage and the log file. Defaults to the current directory if unspecified. It is used as the location of chroot symlinks (obsolete) and for current build log symlinks and some build logs. There is no default; if unset, it defaults to the current working directory. $HOME/build is another common configuration.',
+ CLI_OPTIONS => ['--build-dir']
+ },
+ 'BUILD_PATH' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_path',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ HELP => 'By default the package is built in a path of the following format /build/packagename-XXXXXX/packagename-version/ where XXXXXX is a random ascii string. This option allows one to specify a custom path where the package is built inside the chroot. The sbuild user in the chroot must have permissions to create the path. Common writable locations are subdirectories of /tmp or /build. Using /tmp might be dangerous, because (depending on the chroot backend) the /tmp inside the chroot might be a world writable location that can be accessed by processes outside the chroot. The directory /build can only be accessed by the sbuild user and group and should be a safe location. The buildpath must be an empty directory because the last component of the path will be removed after the build is finished. Notice that depending on the chroot backend (see CHROOT_MODE), some locations inside the chroot might be bind mounts that are shared with other sbuild instances. You must avoid using these shared locations as the build path or otherwise concurrent runs of sbuild will likely fail. With the default schroot chroot backend, the directory /build is shared between multiple schroot sessions. You can change this behaviour in /etc/schroot/sbuild/fstab. The behaviour of other chroot backends will vary.',
+ CLI_OPTIONS => ['--build-path']
+ },
+ 'DSC_DIR' => {
+ TYPE => 'STRING',
+ VARNAME => 'dsc_dir',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ HELP => 'By default the package is built in a path of the following format /build/packagename-XXXXXX/packagename-version/ where packagename-version are replaced by the values in debian/changelog. This option allows one to specify a custom packagename-version path where the package is built inside the chroot. This is useful to specify a static path for different versions for example for ccache.',
+ CLI_OPTIONS => ['--dsc-dir']
+ },
+ 'SBUILD_MODE' => {
+ TYPE => 'STRING',
+ VARNAME => 'sbuild_mode',
+ GROUP => 'Core options',
+ DEFAULT => 'user',
+ HELP => 'sbuild behaviour; possible values are "user" (exit status reports build failures) and "buildd" (exit status does not report build failures) for use in a buildd setup. "buildd" also currently implies enabling of "legacy features" such as chroot symlinks in the build directory and the creation of current symlinks in the build directory.',
+ CLI_OPTIONS => ['--sbuild-mode']
+ },
+ 'CHROOT_SETUP_SCRIPT' => {
+ TYPE => 'STRING',
+ VARNAME => 'chroot_setup_script',
+ GROUP => 'Chroot options',
+ DEFAULT => undef,
+ HELP => 'Script to run to perform custom setup tasks in the chroot.',
+ CLI_OPTIONS => ['--setup-hook']
+ },
+ 'FORCE_ORIG_SOURCE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'force_orig_source',
+ GROUP => 'Build options',
+ DEFAULT => 0,
+ HELP => 'By default, the -s option only includes the .orig.tar.gz when needed (i.e. when the Debian revision is 0 or 1). By setting this option to 1, the .orig.tar.gz will always be included when -s is used.',
+ CLI_OPTIONS => ['--force-orig-source']
+ },
+ 'INDIVIDUAL_STALLED_PKG_TIMEOUT' => {
+ TYPE => 'HASH:NUMERIC',
+ VARNAME => 'individual_stalled_pkg_timeout',
+ GROUP => 'Build timeouts',
+ DEFAULT => {},
+ HELP => 'Some packages may exceed the general timeout (e.g. redirecting output to a file) and need a different timeout. This has is a mapping between source package name and timeout. Note that for backward compatibility, this is also settable using the hash %individual_stalled_pkg_timeout (deprecated) , rather than a hash reference.',
+ EXAMPLE =>
+'$individual_stalled_pkg_timeout->{\'llvm-toolchain-3.8\'} = 300;
+$individual_stalled_pkg_timeout->{\'kicad-packages3d\'} = 90;'
+ },
+ 'ENVIRONMENT_FILTER' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'environment_filter',
+ GROUP => 'Core options',
+ DEFAULT => [ sort (map "^$_\$", Dpkg::BuildInfo::get_build_env_allowed()) ],
+# GET => sub {
+# my $conf = shift;
+# my $entry = shift;
+#
+# my $retval = $conf->_get($entry->{'NAME'});
+#
+# if (!defined($retval)) {
+# $retval = [ map "^$_\$", Dpkg::BuildInfo::get_build_env_allowed() ];
+# }
+#
+# return $retval;
+# },
+ HELP => 'Only environment variables matching one of the regular expressions in this arrayref will be passed to dpkg-buildpackage and other programs run by sbuild. The default value for this configuration setting is the list of variable names as returned by Dpkg::BuildInfo::get_build_env_allowed() which is also the list of variable names that is whitelisted to be recorded in .buildinfo files. Caution: the default value listed below was retrieved from the dpkg Perl library version available when this man page was generated. It might be different if your dpkg Perl library version differs.',
+ EXAMPLE =>
+'# Setting the old environment filter
+$environment_filter = [\'^PATH$\',
+ \'^DEB(IAN|SIGN)?_[A-Z_]+$\',
+ \'^(C(PP|XX)?|LD|F)FLAGS(_APPEND)?$\',
+ \'^USER(NAME)?$\',
+ \'^LOGNAME$\',
+ \'^HOME$\',
+ \'^TERM$\',
+ \'^SHELL$\'];
+# Appending FOOBAR to the default
+use Dpkg::Build::Info;
+$environment_filter = [Dpkg::BuildInfo::get_build_env_allowed(), \'FOOBAR\'];
+# Removing FOOBAR from the default
+use Dpkg::Build::Info;
+$environment_filter = [map /^FOOBAR$/ ? () : $_, Dpkg::BuildInfo::get_build_env_allowed()];
+'
+ },
+ 'BUILD_ENVIRONMENT' => {
+ TYPE => 'HASH:STRING',
+ VARNAME => 'build_environment',
+ GROUP => 'Core options',
+ DEFAULT => {},
+ HELP => 'Environment to set during the build. Defaults to setting PATH and LD_LIBRARY_PATH only. Note that these environment variables are not subject to filtering with ENVIRONMENT_FILTER. Example:',
+ EXAMPLE =>
+'$build_environment = {
+ \'CCACHE_DIR\' => \'/build/cache\'
+};'
+ },
+ 'LD_LIBRARY_PATH' => {
+ TYPE => 'STRING',
+ VARNAME => 'ld_library_path',
+ GROUP => 'Build environment',
+ DEFAULT => undef,
+ HELP => 'Library search path to use inside the chroot.',
+ CLI_OPTIONS => ['--use-snapshot']
+ },
+ 'MAINTAINER_NAME' => {
+ TYPE => 'STRING',
+ VARNAME => 'maintainer_name',
+ GROUP => 'Maintainer options',
+ DEFAULT => undef,
+ SET => $set_signing_option,
+ HELP => 'Name to use as override in .changes files for the Maintainer field. The Maintainer field will not be overridden unless set here.',
+ CLI_OPTIONS => ['-m', '--maintainer']
+ },
+ 'UPLOADER_NAME' => {
+ VARNAME => 'uploader_name',
+ TYPE => 'STRING',
+ GROUP => 'Maintainer options',
+ DEFAULT => undef,
+ SET => $set_signing_option,
+ HELP => 'Name to use as override in .changes file for the Changed-By: field.',
+ CLI_OPTIONS => ['-e', '--uploader']
+ },
+ 'KEY_ID' => {
+ TYPE => 'STRING',
+ VARNAME => 'key_id',
+ GROUP => 'Maintainer options',
+ DEFAULT => undef,
+ HELP => 'Key ID to use in .changes for the current upload. It overrides both $maintainer_name and $uploader_name.',
+ CLI_OPTIONS => ['-k', '--keyid']
+ },
+ 'SIGNING_OPTIONS' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => "",
+ HELP => 'PGP-related identity options to pass to dpkg-buildpackage. Usually neither .dsc nor .changes files are not signed automatically.'
+ },
+ 'APT_CLEAN' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_clean',
+ GROUP => 'Chroot options',
+ DEFAULT => 0,
+ HELP => 'APT clean. 1 to enable running "apt-get clean" at the start of each build, or 0 to disable.',
+ CLI_OPTIONS => ['--apt-clean', '--no-apt-clean']
+ },
+ 'APT_KEEP_DOWNLOADED_PACKAGES' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_keep_downloaded_packages',
+ GROUP => 'Chroot options',
+ DEFAULT => 0,
+ HELP => 'Keep downloaded packages in cache by APT. Controls APT::Keep-Downloaded-Packages option used when downloading dependencies. This option only makes sense if /var/cache/apt/archive inside the chroot is made persistent between multiple sbuild invocations. 1 to keep downloaded packages in cache, or 0 to delete them after installation.'
+ },
+ 'APT_UPDATE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_update',
+ GROUP => 'Chroot options',
+ DEFAULT => 1,
+ HELP => 'APT update. 1 to enable running "apt-get update" at the start of each build, or 0 to disable. This option has no effect on updating the internal sbuild apt repository, the repository for extra packages (see EXTRA_PACKAGES) and the repositories given via EXTRA_REPOSITORIES. These are always updated. Thus, this option only influences updates of the default repositories of the chroot.',
+ CLI_OPTIONS => ['--apt-update', '--no-apt-update']
+ },
+ 'APT_UPDATE_ARCHIVE_ONLY' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_update_archive_only',
+ GROUP => 'Chroot options',
+ DEFAULT => 1,
+ HELP => 'Update local temporary APT archive directly (1, the default) or set to 0 to disable and do a full apt update (not recommended in case the mirror content has changed since the build started).'
+ },
+ 'APT_UPGRADE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_upgrade',
+ GROUP => 'Chroot options',
+ DEFAULT => 0,
+ HELP => 'APT upgrade. 1 to enable running "apt-get upgrade" at the start of each build, or 0 to disable.',
+ CLI_OPTIONS => ['--apt-upgrade', '--no-apt-upgrade']
+ },
+ 'APT_DISTUPGRADE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_distupgrade',
+ GROUP => 'Chroot options',
+ DEFAULT => 1,
+ HELP => 'APT distupgrade. 1 to enable running "apt-get dist-upgrade" at the start of each build, or 0 to disable.',
+ CLI_OPTIONS => ['--apt-distupgrade', '--no-apt-distupgrade']
+ },
+ 'APT_ALLOW_UNAUTHENTICATED' => {
+ TYPE => 'BOOL',
+ VARNAME => 'apt_allow_unauthenticated',
+ GROUP => 'Chroot options',
+ DEFAULT => 0,
+ HELP => 'Force APT to accept unauthenticated packages. By default, unauthenticated packages are not allowed. This is to keep the build environment secure, using apt-secure(8). By setting this to 1, APT::Get::AllowUnauthenticated is set to "true" when running apt-get. This is disabled by default: only enable it if you know what you are doing.'
+ },
+ 'BATCH_MODE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'batch_mode',
+ GROUP => 'Core options',
+ DEFAULT => 0,
+ HELP => 'Enable batch mode?',
+ CLI_OPTIONS => ['-b', '--batch']
+ },
+ 'CORE_DEPENDS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'core_depends',
+ GROUP => 'Core options',
+ DEFAULT => ['build-essential:native', 'fakeroot:native'],
+ HELP => 'Packages which must be installed in the chroot for all builds.'
+ },
+ 'MANUAL_DEPENDS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'manual_depends',
+ GROUP => 'Core options',
+ DEFAULT => [],
+ HELP => 'Additional per-build dependencies.',
+ CLI_OPTIONS => ['--add-depends']
+ },
+ 'MANUAL_CONFLICTS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'manual_conflicts',
+ GROUP => 'Core options',
+ DEFAULT => [],
+ HELP => 'Additional per-build dependencies.',
+ CLI_OPTIONS => ['--add-conflicts']
+ },
+ 'MANUAL_DEPENDS_ARCH' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'manual_depends_arch',
+ GROUP => 'Core options',
+ DEFAULT => [],
+ HELP => 'Additional per-build dependencies.',
+ CLI_OPTIONS => ['--add-depends-arch']
+ },
+ 'MANUAL_CONFLICTS_ARCH' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'manual_conflicts_arch',
+ GROUP => 'Core options',
+ DEFAULT => [],
+ HELP => 'Additional per-build dependencies.',
+ CLI_OPTIONS => ['--add-conflicts-arch']
+ },
+ 'MANUAL_DEPENDS_INDEP' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'manual_depends_indep',
+ GROUP => 'Core options',
+ DEFAULT => [],
+ HELP => 'Additional per-build dependencies.',
+ CLI_OPTIONS => ['--add-depends-indep']
+ },
+ 'MANUAL_CONFLICTS_INDEP' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'manual_conflicts_indep',
+ GROUP => 'Core options',
+ DEFAULT => [],
+ HELP => 'Additional per-build dependencies.',
+ CLI_OPTIONS => ['--add-conflicts-indep']
+ },
+ 'CROSSBUILD_CORE_DEPENDS' => {
+ TYPE => 'HASH:ARRAY:STRING',
+ VARNAME => 'crossbuild_core_depends',
+ GROUP => 'Multiarch support (transitional)',
+ DEFAULT => {},
+ HELP => 'Per-architecture dependencies required for cross-building. By default, if a Debian architecture is not found as a key in this hash, the following will be added to the Build-Depends: crossbuild-essential-${hostarch}:native, libc-dev, libstdc++-dev. The latter two are to work around bug #815172.',
+ EXAMPLE => '
+$crossbuild_core_depends = {
+ nios2 => [\'crossbuild-essential-nios2:native\', \'special-package\'],
+ musl-linux-mips => [\'crossbuild-essential-musl-linux-mips:native\', \'super-special\'],
+}
+'
+ }, 'BUILD_SOURCE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'build_source',
+ GROUP => 'Build options',
+ DEFAULT => 0,
+ CHECK => $validate_append_version,
+ HELP => 'By default, do not build a source package (binary only build). Set to 1 to force creation of a source package, but note that this is inappropriate for binary NMUs, where the option will always be disabled.',
+ CLI_OPTIONS => ['-s', '--source', '--no-source']
+ },
+ 'ARCHIVE' => {
+ TYPE => 'STRING',
+ VARNAME => 'archive',
+ GROUP => 'Core options',
+ DEFAULT => undef,
+ HELP => 'Archive being built. Only set in build log. This might be useful for derivative distributions.',
+ CLI_OPTIONS => ['--archive']
+ },
+ 'BIN_NMU' => {
+ TYPE => 'STRING',
+ VARNAME => 'bin_nmu',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ CHECK => $validate_append_version,
+ HELP => 'Binary NMU changelog entry.',
+ CLI_OPTIONS => ['--make-binNMU']
+ },
+ 'BIN_NMU_VERSION' => {
+ TYPE => 'STRING',
+ VARNAME => 'bin_nmu_version',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ HELP => 'Binary NMU version number.',
+ CLI_OPTIONS => ['--binNMU', '--make-binNMU']
+ },
+ 'BIN_NMU_TIMESTAMP' => {
+ TYPE => 'STRING',
+ VARNAME => 'bin_nmu_timestamp',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ HELP => 'Binary NMU timestamp. The timestamp is either given as n integer in Unix time or as a string in the format compatible with Debian changelog entries (i.e. as it is generated by date -R). If set to the default (undef) the date at build time is used.',
+ CLI_OPTIONS => ['--binNMU-timestamp']
+ },
+ 'APPEND_TO_VERSION' => {
+ TYPE => 'STRING',
+ VARNAME => 'append_to_version',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ CHECK => $validate_append_version,
+ HELP => 'Suffix to append to version number. May be useful for derivative distributions.',
+ CLI_OPTIONS => ['--append-to-version']
+ },
+ 'BIN_NMU_CHANGELOG' => {
+ TYPE => 'STRING',
+ VARNAME => 'bin_nmu_changelog',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ HELP => 'The content of a binary-only changelog entry. Leading and trailing newlines will be stripped.',
+ CLI_OPTIONS => ['--binNMU-changelog']
+ },
+ 'GCC_SNAPSHOT' => {
+ TYPE => 'BOOL',
+ VARNAME => 'gcc_snapshot',
+ GROUP => 'Build options',
+ DEFAULT => 0,
+ HELP => 'Build using current GCC snapshot?',
+ CLI_OPTIONS => ['--use-snapshot']
+ },
+ 'JOB_FILE' => {
+ TYPE => 'STRING',
+ VARNAME => 'job_file',
+ GROUP => 'Core options',
+ DEFAULT => 'build-progress',
+ HELP => 'Job status file (only used in batch mode)'
+ },
+ 'BUILD_DEP_RESOLVER' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_dep_resolver',
+ GROUP => 'Dependency resolution',
+ DEFAULT => 'apt',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ if ($conf->get($key) eq 'internal') {
+ warn "W: Build dependency resolver 'internal' has been removed; defaulting to 'apt'. Please update your configuration.\n";
+ $conf->set('BUILD_DEP_RESOLVER', 'apt');
+ }
+
+ die '$key: Invalid build-dependency resolver \'' .
+ $conf->get($key) .
+ "'\nValid algorithms are 'apt', 'aptitude', 'aspcud', 'xapt', and 'null'\n"
+ if !isin($conf->get($key),
+ qw(apt aptitude aspcud xapt null));
+ },
+ HELP => 'Build dependency resolver. The \'apt\' resolver is currently the default, and recommended for most users. This resolver uses apt-get to resolve dependencies. Alternative resolvers are \'apt\', \'aptitude\' and \'aspcud\'. The \'apt\' resolver uses a built-in resolver module while the \'aptitude\' resolver uses aptitude to resolve build dependencies. The aptitude resolver is similar to apt, but is useful in more complex situations, such as where multiple distributions are required, for example when building from experimental, where packages are needed from both unstable and experimental, but defaulting to unstable. If the dependency situation is too complex for either apt or aptitude to solve it, you can use the \'aspcud\' resolver which (in contrast to apt and aptitude) is a real solver (in the math sense) and will thus always find a solution if a solution exists. Additionally, the \'null\' solver is provided. It is a dummy resolver which does not install, upgrade or remove any packages. This allows one to completely control package installation via hooks.',
+ CLI_OPTIONS => ['--build-dep-resolver']
+ },
+ 'ASPCUD_CRITERIA' => {
+ TYPE => 'STRING',
+ VARNAME => 'aspcud_criteria',
+ GROUP => 'Dependency resolution',
+ DEFAULT => '-removed,-changed,-new',
+ HELP => 'Optimization criteria in extended MISC 2012 syntax passed to aspcud through apt-cudf. Optimization criteria are separated by commas, sorted by decreasing order of priority and are prefixed with a polarity (+ to maximize and - to minimize). The default criteria is \'-removed,-changed,-new\' which first minimizes the number of removed packages, then the number of changed packages (up or downgrades) and then the number of new packages. A common task is to minimize the number of packages from experimental. To do this you can add a criteria like \'-count(solution,APT-Release:=/a=experimental/)\' to the default criteria. This will then minimize the number of packages in the solution which contain the string \'a=experimental\' in the \'APT-Release\' field of the EDSP output created by apt. See the apt-cudf man page help on the --criteria option for more information.',
+ CLI_OPTIONS => ['--aspcud-criteria']
+ },
+ 'CLEAN_SOURCE' => {
+ TYPE => 'BOOL',
+ VARNAME => 'clean_source',
+ GROUP => 'Build options',
+ DEFAULT => 1,
+ HELP => 'When running sbuild from within an unpacked source tree, run the \'clean\' target before generating the source package. This might require some of the build dependencies necessary for running the \'clean\' target to be installed on the host machine. Only disable if you start from a clean checkout and you know what you are doing.',
+ CLI_OPTIONS => ['--clean-source', '--no-clean-source']
+ },
+ 'LINTIAN' => {
+ TYPE => 'STRING',
+ VARNAME => 'lintian',
+ GROUP => 'Build validation',
+ DEFAULT => 'lintian',
+ HELP => 'Path to lintian binary'
+ },
+ 'RUN_LINTIAN' => {
+ TYPE => 'BOOL',
+ VARNAME => 'run_lintian',
+ GROUP => 'Build validation',
+ CHECK => sub {
+ my $conf = shift;
+ $conf->check('LINTIAN');
+ },
+ DEFAULT => 1,
+ HELP => 'Run lintian?',
+ CLI_OPTIONS => ['--run-lintian', '--no-run-lintian']
+ },
+ 'LINTIAN_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'lintian_opts',
+ GROUP => 'Build validation',
+ DEFAULT => [],
+ HELP => 'Options to pass to lintian. Each option is a separate arrayref element. For example, [\'-i\', \'-v\'] to add -i and -v.',
+ CLI_OPTIONS => ['--lintian-opt', '--lintian-opts']
+ },
+ 'LINTIAN_REQUIRE_SUCCESS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'lintian_require_success',
+ GROUP => 'Build validation',
+ DEFAULT => 0,
+ HELP => 'Let sbuild fail if lintian fails.'
+ },
+ 'PIUPARTS' => {
+ TYPE => 'STRING',
+ VARNAME => 'piuparts',
+ GROUP => 'Build validation',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('RUN_PIUPARTS')) {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => 'piuparts',
+ HELP => 'Path to piuparts binary',
+ CLI_OPTIONS => ['--piuparts-opt', '--piuparts-opts']
+ },
+ 'RUN_PIUPARTS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'run_piuparts',
+ GROUP => 'Build validation',
+ CHECK => sub {
+ my $conf = shift;
+ $conf->check('PIUPARTS');
+ },
+ DEFAULT => 0,
+ HELP => 'Run piuparts',
+ CLI_OPTIONS => ['--run-piuparts', '--no-run-piuparts']
+ },
+ 'PIUPARTS_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'piuparts_opts',
+ GROUP => 'Build validation',
+ DEFAULT => [],
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ my $dist = $conf->get('DISTRIBUTION');
+ my $hostarch = $conf->get('HOST_ARCH');
+ my %percent = (
+ '%' => '%',
+ 'a' => $hostarch, 'SBUILD_HOST_ARCH' => $hostarch,
+ 'r' => $dist, 'SBUILD_DISTRIBUTION' => $dist,
+ );
+
+ my $keyword_pat = join("|",
+ sort {length $b <=> length $a || $a cmp $b} keys %percent);
+ foreach (@{$retval}) {
+ s{
+ # Match a percent followed by a valid keyword
+ \%($keyword_pat)
+ }{
+ # Substitute with the appropriate value only if it's defined
+ $percent{$1} || $&
+ }msxge;
+ }
+ return $retval;
+ },
+ HELP => 'Options to pass to piuparts. Each option is a separate arrayref element. For example, [\'-b\', \'<chroot_tarball>\'] to add -b and <chroot_tarball>.'
+ },
+ 'PIUPARTS_ROOT_ARGS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'piuparts_root_args',
+ GROUP => 'Build validation',
+ DEFAULT => [],
+ HELP => 'Preceding arguments to launch piuparts as root. With the default value (the empty array) "sudo --" will be used as a prefix. If the first element in the array is the empty string, no prefixing will be done. If the value is a scalar, it will be prefixed by that string. If the scalar is an empty string, no prefixing will be done.',
+ EXAMPLE =>
+'# prefix with "sudo --":
+$piuparts_root_args = [];
+$piuparts_root_args = [\'sudo\', \'--\'];
+# prefix with "env":
+$piuparts_root_args = [\'env\'];
+$piuparts_root_args = \'env\';
+# prefix with nothing:
+$piuparts_root_args = \'\';
+$piuparts_root_args = [\'\'];
+$piuparts_root_args = [\'\', \'whatever\'];
+',
+ CLI_OPTIONS => ['--piuparts-root-arg', '--piuparts-root-args']
+ },
+ 'PIUPARTS_REQUIRE_SUCCESS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'piuparts_require_success',
+ GROUP => 'Build validation',
+ DEFAULT => 0,
+ HELP => 'Let sbuild fail if piuparts fails.'
+ },
+ 'AUTOPKGTEST' => {
+ TYPE => 'STRING',
+ VARNAME => 'autopkgtest',
+ GROUP => 'Build validation',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('RUN_AUTOPKGTEST')) {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => 'autopkgtest',
+ HELP => 'Path to autopkgtest binary',
+ CLI_OPTIONS => ['--autopkgtest-opt', '--autopkgtest-opts']
+ },
+ 'RUN_AUTOPKGTEST' => {
+ TYPE => 'BOOL',
+ VARNAME => 'run_autopkgtest',
+ GROUP => 'Build validation',
+ CHECK => sub {
+ my $conf = shift;
+ $conf->check('AUTOPKGTEST');
+ },
+ DEFAULT => 0,
+ HELP => 'Run autopkgtest',
+ CLI_OPTIONS => ['--run-autopkgtest', '--no-run-autopkgtest']
+ },
+ 'AUTOPKGTEST_OPTIONS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'autopkgtest_opts',
+ GROUP => 'Build validation',
+ DEFAULT => [],
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ my $dist = $conf->get('DISTRIBUTION');
+ my $hostarch = $conf->get('HOST_ARCH');
+ my %percent = (
+ '%' => '%',
+ 'a' => $hostarch, 'SBUILD_HOST_ARCH' => $hostarch,
+ 'r' => $dist, 'SBUILD_DISTRIBUTION' => $dist,
+ );
+
+ my $keyword_pat = join("|",
+ sort {length $b <=> length $a || $a cmp $b} keys %percent);
+ foreach (@{$retval}) {
+ s{
+ # Match a percent followed by a valid keyword
+ \%($keyword_pat)
+ }{
+ # Substitute with the appropriate value only if it's defined
+ $percent{$1} || $&
+ }msxge;
+ }
+ return $retval;
+ },
+ HELP => 'Options to pass to autopkgtest. Each option is a separate arrayref element. For example, [\'-b\', \'<chroot_tarball>\'] to add -b and <chroot_tarball>.'
+ },
+ 'AUTOPKGTEST_ROOT_ARGS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'autopkgtest_root_args',
+ GROUP => 'Build validation',
+ DEFAULT => [],
+ HELP => 'Preceding arguments to launch autopkgtest as root. With the default value (the empty array) "sudo --" will be used as a prefix. If the first element in the array is the empty string, no prefixing will be done. If the value is a scalar, it will be prefixed by that string. If the scalar is an empty string, no prefixing will be done.',
+ EXAMPLE =>
+'# prefix with "sudo --":
+$autopkgtest_root_args = [];
+$autopkgtest_root_args = [\'sudo\', \'--\'];
+# prefix with "env":
+$autopkgtest_root_args = [\'env\'];
+$autopkgtest_root_args = \'env\';
+# prefix with nothing:
+$autopkgtest_root_args = \'\';
+$autopkgtest_root_args = [\'\'];
+$autopkgtest_root_args = [\'\', \'whatever\'];
+',
+ CLI_OPTIONS => ['--autopkgtest-root-arg', '--autopkgtest-root-args']
+ },
+ 'AUTOPKGTEST_REQUIRE_SUCCESS' => {
+ TYPE => 'BOOL',
+ VARNAME => 'autopkgtest_require_success',
+ GROUP => 'Build validation',
+ DEFAULT => 0,
+ HELP => 'Let sbuild fail if autopkgtest fails.'
+ },
+ 'EXTERNAL_COMMANDS' => {
+ TYPE => 'HASH:ARRAY:STRING',
+ VARNAME => 'external_commands',
+ GROUP => 'Chroot options',
+ DEFAULT => {
+ "pre-build-commands" => [],
+ "chroot-setup-commands" => [],
+ "chroot-update-failed-commands" => [],
+ "build-deps-failed-commands" => [],
+ "build-failed-commands" => [],
+ "starting-build-commands" => [],
+ "finished-build-commands" => [],
+ "chroot-cleanup-commands" => [],
+ "post-build-commands" => [],
+ },
+ HELP => 'External commands to run at various stages of a build. Commands are held in a hash of arrays of arrays data structure. There is no equivalent for the --anything-failed-commands command line option. All percent escapes mentioned in the sbuild man page can be used.',
+ EXAMPLE =>
+'# general format
+$external_commands = {
+ "pre-build-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "chroot-setup-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "chroot-update-failed-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "build-deps-failed-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "build-failed-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "starting-build-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "finished-build-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "chroot-cleanup-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "post-build-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+ "post-build-failed-commands" => [
+ [\'foo\', \'arg1\', \'arg2\'],
+ [\'bar\', \'arg1\', \'arg2\', \'arg3\'],
+ ],
+};
+# the equivalent of specifying --anything-failed-commands=%SBUILD_SHELL on the
+# command line
+$external_commands = {
+ "chroot-update-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
+ "build-deps-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
+ "build-failed-commands" => [ [ \'%SBUILD_SHELL\' ] ],
+};',
+ CLI_OPTIONS => ['--setup-hook', '--pre-build-commands', '--chroot-setup-commands', '--chroot-update-failed-commands', '--build-deps-failed-commands', '--build-failed-commands', '--anything-failed-commands', '--starting-build-commands', '--finished-build-commands', '--chroot-cleanup-commands', '--post-build-commands', '--post-build-failed-commands']
+ },
+ 'LOG_EXTERNAL_COMMAND_OUTPUT' => {
+ TYPE => 'BOOL',
+ VARNAME => 'log_external_command_output',
+ GROUP => 'Chroot options',
+ DEFAULT => 1,
+ HELP => 'Log standard output of commands run by sbuild?',
+ CLI_OPTIONS => ['--log-external-command-output']
+ },
+ 'LOG_EXTERNAL_COMMAND_ERROR' => {
+ TYPE => 'BOOL',
+ VARNAME => 'log_external_command_error',
+ GROUP => 'Chroot options',
+ DEFAULT => 1,
+ HELP => 'Log standard error of commands run by sbuild?',
+ CLI_OPTIONS => ['--log-external-command-error']
+ },
+ 'RESOLVE_ALTERNATIVES' => {
+ TYPE => 'BOOL',
+ VARNAME => 'resolve_alternatives',
+ GROUP => 'Dependency resolution',
+ DEFAULT => undef,
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ if (!defined($retval)) {
+ $retval = 0;
+ $retval = 1
+ if ($conf->get('BUILD_DEP_RESOLVER') eq 'aptitude');
+ }
+
+ return $retval;
+ },
+ EXAMPLE => '$resolve_alternatives = 0;',
+ HELP => 'Should the dependency resolver use alternatives in Build-Depends, Build-Depends-Arch and Build-Depends-Indep? By default, using \'apt\' resolver, only the first alternative will be used; all other alternatives will be removed. When using the \'aptitude\' resolver, it will default to using all alternatives. Note that this does not include architecture-specific alternatives, which are reduced to the build architecture prior to alternatives removal. This should be left disabled when building for unstable; it may be useful when building for experimental or backports. Set to undef to use the default, 1 to enable, or 0 to disable.',
+ CLI_OPTIONS => ['--resolve-alternatives', '--no-resolve-alternatives']
+ },
+ 'EXTRA_PACKAGES' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'extra_packages',
+ GROUP => 'Dependency resolution',
+ DEFAULT => [],
+ HELP => 'Additional per-build packages available as build dependencies.',
+ CLI_OPTIONS => ['--extra-package']
+ },
+ 'EXTRA_REPOSITORY_KEYS' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'extra_repository_keys',
+ GROUP => 'Dependency resolution',
+ DEFAULT => [],
+ HELP => 'Additional per-build apt repository keys.',
+ CLI_OPTIONS => ['--extra-repository-key']
+ },
+ 'EXTRA_REPOSITORIES' => {
+ TYPE => 'ARRAY:STRING',
+ VARNAME => 'extra_repositories',
+ GROUP => 'Dependency resolution',
+ DEFAULT => [],
+ HELP => 'Additional per-build apt repositories.',
+ CLI_OPTIONS => ['--extra-repository']
+ },
+ 'SOURCE_ONLY_CHANGES' => {
+ TYPE => 'BOOL',
+ VARNAME => 'source_only_changes',
+ GROUP => 'Build options',
+ DEFAULT => 0,
+ HELP => 'Also produce a changes file suitable for a source-only upload.',
+ CLI_OPTIONS => ['--source-only-changes']
+ },
+ 'BD_UNINSTALLABLE_EXPLAINER' => {
+ TYPE => 'STRING',
+ VARNAME => 'bd_uninstallable_explainer',
+ GROUP => 'Dependency resolution',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ die "Bad bd-uninstallable explainer \'" . $conf->get('BD_UNINSTALLABLE_EXPLAINER') . "\'"
+ if defined $conf->get('BD_UNINSTALLABLE_EXPLAINER')
+ && !isin($conf->get('BD_UNINSTALLABLE_EXPLAINER'),
+ ('apt', 'dose3', '', 'none'));
+ },
+ DEFAULT => 'dose3',
+ HELP => 'Method to use for explaining build dependency installation failures. Possible value are "dose3" (default), "apt" and "none". Set to "none", the empty string "" or Perl undef to disable running any explainer.',
+ CLI_OPTIONS => ['--bd-uninstallable-explainer']
+ },
+ 'PURGE_EXTRA_PACKAGES' => {
+ TYPE => 'BOOL',
+ VARNAME => 'purge_extra_packages',
+ GROUP => 'Chroot options',
+ DEFAULT => 0,
+ HELP => 'Try to remove all additional packages that are not strictly required for the build right after build dependencies were installed. This currently works best with the aspcud resolver. The apt resolver will not make as much effort to remove all unneeded packages and will keep all providers of a virtual package and all packages from any dependency alternative that happen to be installed. The aptitude and xapt resolver do not implement this feature yet. The removed packages are not yet added again after the build finished. This can have undesirable side effects like lintian not working (because there is no apt to install its dependencies) or bare chroots becoming totally unusable after apt was removed from them. Thus, this option should only be used with throw-away chroots like schroot provides them where the original state is automatically restored after each build.',
+ CLI_OPTIONS => ['--purge-extra-packages']
+ }
+ );
+
+ $conf->set_allowed_keys(\%sbuild_keys);
+}
+
+sub read ($) {
+ my $conf = shift;
+
+ # Set here to allow user to override.
+ if (-t STDIN && -t STDOUT) {
+ $conf->_set_default('VERBOSE', 1);
+ } else {
+ $conf->_set_default('VERBOSE', 0);
+ }
+
+ my $HOME = $conf->get('HOME');
+
+ my $files = ["$Sbuild::Sysconfig::paths{'SBUILD_CONF'}",
+ "$HOME/.sbuildrc"];
+
+ push @{$files}, $ENV{'SBUILD_CONFIG'} if defined $ENV{'SBUILD_CONFIG'};
+
+ # For compatibility only. Non-scalars are deprecated.
+ my $deprecated_init = <<END;
+my \%mailto;
+undef \%mailto;
+my \@toolchain_regex;
+undef \@toolchain_regex;
+my \%individual_stalled_pkg_timeout;
+undef \%individual_stalled_pkg_timeout;
+END
+
+ my $deprecated_setup = <<END;
+# Non-scalar values, for backward compatibility.
+if (\%mailto) {
+ warn 'W: \%mailto is deprecated; please use the hash reference \$mailto{}\n';
+ \$conf->set('MAILTO_HASH', \\\%mailto);
+}
+if (\@toolchain_regex) {
+ warn 'W: \@toolchain_regex is deprecated; please use the array reference \$toolchain_regexp[]\n';
+ \$conf->set('TOOLCHAIN_REGEX', \\\@toolchain_regex);
+}
+if (\%individual_stalled_pkg_timeout) {
+ warn 'W: \%individual_stalled_pkg_timeout is deprecated; please use the hash reference \$individual_stalled_pkg_timeout{}\n';
+ \$conf->set('INDIVIDUAL_STALLED_PKG_TIMEOUT',
+ \\\%individual_stalled_pkg_timeout);
+}
+END
+
+ my $custom_setup = <<END;
+push(\@{\${\$conf->get('EXTERNAL_COMMANDS')}{"chroot-user-setup-commands"}},
+\$chroot_setup_script) if (\$chroot_setup_script);
+
+ # Trigger log directory creation if needed
+ \$conf->get('LOG_DIR_AVAILABLE');
+
+END
+
+
+ $conf->read($files, $deprecated_init, $deprecated_setup,
+ $custom_setup);
+}
+
+1;
diff --git a/lib/Sbuild/ConfBase.pm b/lib/Sbuild/ConfBase.pm
new file mode 100644
index 0000000..2951238
--- /dev/null
+++ b/lib/Sbuild/ConfBase.pm
@@ -0,0 +1,548 @@
+#
+# ConfBase.pm: configuration library (base functionality) for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# 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/>.
+#
+#######################################################################
+
+package Sbuild::ConfBase;
+
+use strict;
+use warnings;
+
+use Sbuild qw(isin);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw();
+}
+
+sub init_allowed_keys {
+ my $self = shift;
+
+ my $validate_program = sub {
+ my $self = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $program = $self->get($key);
+
+ die "$key binary is not defined"
+ if !defined($program) || !$program;
+
+ # Emulate execvp behaviour by searching the binary in the PATH.
+ my @paths = split(/:/, $self->get('PATH'));
+ # Also consider the empty path for absolute locations.
+ push (@paths, '');
+ my $found = 0;
+ foreach my $path (@paths) {
+ $found = 1 if (-x File::Spec->catfile($path, $program));
+ }
+
+ die "$key binary '$program' does not exist or is not executable"
+ if !$found;
+ };
+
+ my $validate_directory = sub {
+ my $self = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+ my $directory = $self->get($key);
+
+ die "$key directory is not defined"
+ if !defined($directory);
+
+ die "$key directory $directory does not exist"
+ if !-d $directory;
+ };
+
+ my $set_distribution = sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $value = shift;
+ my $key = $entry->{'NAME'};
+ $conf->_set_value($key, $value);
+
+ $conf->set('MAILTO',
+ $conf->get('MAILTO_HASH')->{$value})
+ if ($value &&
+ $conf->get('DISTRIBUTION') &&
+ $conf->get('MAILTO_HASH') &&
+ $conf->get('MAILTO_HASH')->{$value});
+ };
+
+ my @pwinfo = getpwuid($<);
+ die "Can't get passwd entry for uid $<: $!" if (!@pwinfo);
+ my $home = $ENV{'HOME'};
+ if (!$home) {
+ print STDERR "W: HOME not set in environment; falling back to $pwinfo[7]\n";
+ $home = $pwinfo[7];
+ }
+ my $username = $pwinfo[0];
+ my $fullname = $pwinfo[6];
+ $fullname =~ s/,.*$//;
+
+ chomp(my $hostname = `hostname -f`);
+
+ # Not user-settable.
+ chomp(my $native_arch =
+ readpipe("dpkg --print-architecture"));
+
+ my %common_keys = (
+ 'PATH' => {
+ TYPE => 'STRING',
+ VARNAME => 'path',
+ GROUP => 'Build environment',
+ DEFAULT => "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games",
+ HELP => 'PATH to set when running dpkg-buildpackage.',
+ CLI_OPTIONS => ['--use-snapshot']
+ },
+ 'DISTRIBUTION' => {
+ TYPE => 'STRING',
+ VARNAME => 'distribution',
+ GROUP => 'Build options',
+ DEFAULT => undef,
+ SET => $set_distribution,
+ HELP => 'Default distribution. By default, no distribution is defined, and the user must specify it with the -d option. However, a default may be configured here if desired. Users must take care not to upload to the wrong distribution when this option is set, for example experimental packages will be built for upload to unstable when this is not what is required.',
+ CLI_OPTIONS => ['-d', '--dist']
+ },
+ 'OVERRIDE_DISTRIBUTION' => {
+ TYPE => 'BOOL',
+ GROUP => '__INTERNAL',
+ DEFAULT => 0,
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $dist = $conf->get('DISTRIBUTION');
+
+ my $overridden = 0;
+ $overridden = 1
+ if (defined($dist));
+
+ return $overridden;
+ },
+ HELP => 'Default distribution has been overridden'
+ },
+ 'MAILPROG' => {
+ TYPE => 'STRING',
+ VARNAME => 'mailprog',
+ GROUP => 'Programs',
+ CHECK => sub {
+ my $conf = shift;
+ my $entry = shift;
+ my $key = $entry->{'NAME'};
+
+ # Only validate if needed.
+ if ($conf->get('MAILTO')) {
+ $validate_program->($conf, $entry);
+ }
+ },
+ DEFAULT => '/usr/sbin/sendmail',
+ HELP => 'Program to use to send mail'
+ },
+ # TODO: Check if defaulted in code assuming undef
+ # ARCH is the native (build-system) architecture. Not used for host/build.
+ 'ARCH' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => $native_arch,
+ HELP => 'Build architecture (Arch we are building on).'
+ },
+ 'HOST_ARCH' => {
+ TYPE => 'STRING',
+ VARNAME => 'host_arch',
+ GROUP => 'Build options',
+ DEFAULT => $native_arch,
+ # the $native_arch is different depending on the machine where
+ # sbuild is built but arch:all packages must not differ depending on
+ # the architecture they are built on, so don't show the default for
+ # example config and man page generation
+ IGNORE_DEFAULT => 1,
+ HELP => 'Host architecture (Arch we are building for)',
+ CLI_OPTIONS => ['--arch', '--host']
+ },
+ 'BUILD_ARCH' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_arch',
+ GROUP => 'Build options',
+ DEFAULT => $native_arch,
+ # the $native_arch is different depending on the machine where
+ # sbuild is built but arch:all packages must not differ depending on
+ # the architecture they are built on, so don't show the default for
+ # example config and man page generation
+ IGNORE_DEFAULT => 1,
+ HELP => 'Build architecture (Arch we are building on).',
+ CLI_OPTIONS => ['--arch', '--build']
+ },
+ 'HOSTNAME' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => $hostname,
+ HELP => 'System hostname. Should not require setting.'
+ },
+ 'HOME' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => $home,
+ HELP => 'User\'s home directory. Should not require setting.'
+ },
+ 'USERNAME' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => $username,
+ HELP => 'User\'s username. Should not require setting.'
+ },
+ 'FULLNAME' => {
+ TYPE => 'STRING',
+ GROUP => '__INTERNAL',
+ DEFAULT => $fullname,
+ HELP => 'User\'s full name. Should not require setting.'
+ },
+ 'BUILD_USER' => {
+ TYPE => 'STRING',
+ VARNAME => 'build_user',
+ GROUP => 'Core options',
+ DEFAULT => $username,
+ IGNORE_DEFAULT => 1, # don't write the username into the config
+ HELP => 'Username used for running dpkg-buildpackage. By default the user running sbuild is used within the chroot as well but that might allow a process from within the chroot to break out of the chroot by attaching to a process running outside the chroot with eg. gdb and then becoming root inside the chroot through schroot and thus be able to leave the chroot.'
+ },
+ 'VERBOSE' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'verbose',
+ GROUP => 'Logging options',
+ DEFAULT => undef,
+ GET => sub {
+ my $conf = shift;
+ my $entry = shift;
+
+ my $retval = $conf->_get($entry->{'NAME'});
+
+ # Note that during a build, STDOUT is redirected, so
+ # this test will fail. So set explicitly at start to
+ # ensure correctness.
+ if (!defined($retval)) {
+ $retval = 0;
+ $retval = 1 if (-t STDIN && -t STDOUT);
+ }
+
+ return $retval;
+ },
+ HELP => 'Verbose logging level'
+ },
+ 'DEBUG' => {
+ TYPE => 'NUMERIC',
+ VARNAME => 'debug',
+ GROUP => 'Logging options',
+ DEFAULT => 0,
+ HELP => 'Debug logging level'
+ },
+ );
+
+ $self->set_allowed_keys(\%common_keys);
+}
+
+sub new {
+ my $class = shift;
+ my %opts = @_;
+
+ my $self = {};
+ bless($self, $class);
+
+ $self->{'CHECK'} = 1;
+ $self->{'CHECK'} = $opts{'CHECK'} if exists $opts{'CHECK'};
+
+ $self->init_allowed_keys();
+
+ return $self;
+}
+
+sub get_keys {
+ my $self = shift;
+
+ return keys(%{$self->{'KEYS'}});
+}
+
+sub is_default {
+ my $self = shift;
+ my $key = shift;
+
+ return ($self->_get_value($key) == undef);
+}
+
+sub _get_property_value {
+ my $self = shift;
+ my $key = shift;
+ my $property = shift;
+
+ my $entry = $self->{'KEYS'}->{$key};
+
+ return $entry->{$property};
+}
+
+sub _get_value {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'VALUE');
+}
+
+sub _get_default {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'DEFAULT');
+}
+
+sub _get_type {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'TYPE');
+}
+
+sub _get_varname {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'VARNAME');
+}
+
+sub _get_group {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'GROUP');
+}
+
+sub _get_help {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'HELP');
+}
+
+sub _get_example {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'EXAMPLE');
+}
+
+sub _get_ignore_default {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'IGNORE_DEFAULT');
+}
+
+sub _get_cli_options {
+ my $self = shift;
+ my $key = shift;
+
+ return $self->_get_property_value($key, 'CLI_OPTIONS');
+}
+
+sub _get {
+ my $self = shift;
+ my $key = shift;
+
+ my $value = undef;
+ $value = $self->_get_value($key);
+ $value = $self->_get_default($key)
+ if (!defined($value));
+
+ return $value;
+}
+
+sub get {
+ my $self = shift;
+ my $key = shift;
+
+ my $entry = $self->{'KEYS'}->{$key};
+
+ my $value = undef;
+ if ($entry) {
+ if (defined($entry->{'GET'})) {
+ $value = $entry->{'GET'}->($self, $entry);
+ } else {
+ $value = $self->_get($key);
+ }
+ }
+
+ return $value;
+}
+
+sub _set_property_value {
+ my $self = shift;
+ my $key = shift;
+ my $property = shift;
+ my $value = shift;
+
+ my $entry = $self->{'KEYS'}->{$key};
+
+ return $entry->{$property} = $value;
+}
+
+sub _set_value {
+ my $self = shift;
+ my $key = shift;
+ my $value = shift;
+
+ return $self->_set_property_value($key, 'VALUE', $value);
+}
+
+sub _set_default {
+ my $self = shift;
+ my $key = shift;
+ my $value = shift;
+
+ return $self->_set_property_value($key, 'DEFAULT', $value);
+}
+
+sub set {
+ my $self = shift;
+ my $key = shift;
+ my $value = shift;
+
+ # Set global debug level.
+ $Sbuild::debug_level = $value
+ if ($key eq 'DEBUG' && defined($value));
+
+ my $entry = $self->{'KEYS'}->{$key};
+
+ if (defined($entry)) {
+ if (defined($entry->{'SET'})) {
+ $value = $entry->{'SET'}->($self, $entry, $value);
+ } else {
+ $value = $self->_set_value($key, $value);
+ }
+ if ($self->{'CHECK'} && defined($entry->{'CHECK'})) {
+ $entry->{'CHECK'}->($self, $entry);
+ }
+ $entry->{'NAME'} = $key;
+ return $value;
+ } else {
+ warn "W: key \"$key\" is not allowed in configuration";
+ return undef;
+ }
+}
+
+sub set_allowed_keys {
+ my $self = shift;
+ my $allowed_keys = shift;
+
+ foreach (keys %{$allowed_keys}) {
+ $allowed_keys->{$_}->{'NAME'} = $_;
+ $self->{'KEYS'}->{$_} = $allowed_keys->{$_}
+ }
+
+}
+
+sub check {
+ my $self = shift;
+ my $key = shift;
+
+ my $entry = $self->{'KEYS'}->{$key};
+
+ if (defined($entry)) {
+ if ($self->{'CHECK'} && defined($entry->{'CHECK'})) {
+ $entry->{'CHECK'}->($self, $entry);
+ }
+ }
+}
+
+sub warn_deprecated {
+ my $oldtype = shift;
+ my $oldopt = shift;
+ my $newtype = shift;
+ my $newopt = shift;
+
+ warn "W: Obsolete $oldtype option '$oldopt' used in configuration";
+ warn "I: The replacement is $newtype option '$newopt'"
+}
+
+sub read ($$$$) {
+ my $conf = shift;
+ my $paths = shift;
+ my $deprecated_init = shift;
+ my $deprecated_setup = shift;
+ my $custom_setup = shift;
+
+ foreach my $path (@{$paths}) {
+ $path = "'$path'";
+ }
+ my $pathstring = join(", ", @{$paths});
+
+ my $HOME = $conf->get('HOME');
+
+ # Variables are undefined, so config will default to DEFAULT if unset.
+
+ # Create script to source configuration.
+ my $script = "use strict;\nuse warnings;\n";
+ my @keys = $conf->get_keys();
+ foreach my $key (@keys) {
+ next if $conf->_get_group($key) =~ m/^__/;
+
+ my $varname = $conf->_get_varname($key);
+ $script .= "my \$$varname = undef;\n";
+ }
+
+ # For compatibility only. Non-scalars are deprecated.
+ $script .= $deprecated_init
+ if ($deprecated_init);
+
+ $script .= <<END;
+
+foreach ($pathstring) {
+ if (-r \$_) {
+ my \$e = eval `cat "\$_"`;
+ if (!defined(\$e)) {
+ print STDERR "E: \$_: Errors found in configuration file:\n\$\@";
+ exit(1);
+ }
+ }
+}
+
+# Needed before any program validation.
+\$conf->set('PATH', \$path);
+END
+
+# Non-scalar values, for backward compatibility.
+ $script .= $deprecated_setup
+ if ($deprecated_setup);
+
+ foreach my $key (@keys) {
+ next if $conf->_get_group($key) =~ m/^__/;
+
+ my $varname = $conf->_get_varname($key);
+ $script .= "\$conf->set('$key', \$$varname);\n";
+ }
+
+ $script .= $custom_setup
+ if ($custom_setup);
+
+
+ $script .= "return 1;\n";
+
+ eval $script or die "Error reading configuration: $@";
+}
+
+1;
diff --git a/lib/Sbuild/Exception.pm b/lib/Sbuild/Exception.pm
new file mode 100644
index 0000000..d35f194
--- /dev/null
+++ b/lib/Sbuild/Exception.pm
@@ -0,0 +1,34 @@
+#
+# Exception.pm: exceptions for sbuild
+# Copyright © 2011 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/>.
+#
+#######################################################################
+
+package Sbuild::Exception;
+
+use strict;
+use warnings;
+
+use Exception::Class (
+ 'Sbuild::Exception::Base',
+
+ 'Sbuild::Exception::Build' => { isa => 'Sbuild::Exception::Base',
+ fields => [ 'info', 'status', 'failstage' ] }
+
+ );
+
+1;
diff --git a/lib/Sbuild/LogBase.pm b/lib/Sbuild/LogBase.pm
new file mode 100644
index 0000000..d3de8de
--- /dev/null
+++ b/lib/Sbuild/LogBase.pm
@@ -0,0 +1,116 @@
+#
+# LogBase.pm: logging library (base functionality) for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# 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/>.
+#
+#######################################################################
+
+package Sbuild::LogBase;
+
+use strict;
+use warnings;
+use English;
+
+sub open_log ($$$);
+sub close_log ($);
+
+our $log = undef;
+our $saved_stdout = undef;
+our $saved_stderr = undef;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT_OK);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT_OK = qw(open_log close_log $log $saved_stdout $saved_stderr);
+}
+
+sub open_log ($$$) {
+ my $conf = shift;
+ my $log_file = shift; # File to log to
+ my $logfunc = shift; # Function to handle logging
+
+ if (!defined($logfunc)) {
+ $logfunc = sub {
+ my $log_file = shift;
+ my $message = shift;
+
+ print $log_file $message;
+ }
+ }
+
+ $log_file->autoflush(1) if defined($log_file);
+
+ my $pid;
+ ($pid = open($log, "|-"));
+ if (!defined $pid) {
+ warn "Cannot open pipe to log: $!\n";
+ }
+ elsif ($pid == 0) {
+ # We ignore SIG(INT|QUIT|TERM) because they will be caught in
+ # the parent which will subsequently close the logging stream
+ # resulting in our termination. This is needed to ensure the
+ # final log messages are sent and the parent doesn't die with
+ # SIGPIPE.
+ $SIG{'INT'} = 'IGNORE';
+ $SIG{'QUIT'} = 'IGNORE';
+ $SIG{'TERM'} = 'IGNORE';
+ $PROGRAM_NAME = 'main log for ' . $PROGRAM_NAME;
+ while (<STDIN>) {
+ $logfunc->($log_file, $_)
+ if (!$conf->get('NOLOG') && defined($log_file));
+ $logfunc->(\*STDOUT, $_)
+ if ($conf->get('VERBOSE'));
+ }
+ undef $log_file;
+ exit 0;
+ }
+
+ undef $log_file; # Close in parent
+ $log->autoflush(1); # Automatically flush
+ select($log); # It's the default stream
+
+ open($saved_stdout, ">&STDOUT") or warn "Can't redirect stdout\n";
+ open($saved_stderr, ">&STDERR") or warn "Can't redirect stderr\n";
+ open(STDOUT, '>&', $log) or warn "Can't redirect stdout\n";
+ open(STDERR, '>&', $log) or warn "Can't redirect stderr\n";
+
+ return $log;
+}
+
+sub close_log ($) {
+ my $conf = shift;
+
+ # Note: It's imperative to close and reopen in the exact order in
+ # which we originally opened and reopened, or else we can deadlock
+ # in wait4 when closing the log stream due to waiting on the child
+ # forever.
+ open(STDERR, '>&', $saved_stderr) or warn "Can't redirect stderr\n"
+ if defined($saved_stderr);
+ open(STDOUT, '>&', $saved_stdout) or warn "Can't redirect stdout\n"
+ if defined($saved_stdout);
+ $saved_stderr->close();
+ undef $saved_stderr;
+ $saved_stdout->close();
+ undef $saved_stdout;
+ $log->close();
+ undef $log;
+}
+
+1;
diff --git a/lib/Sbuild/Makefile.am b/lib/Sbuild/Makefile.am
new file mode 100644
index 0000000..217181f
--- /dev/null
+++ b/lib/Sbuild/Makefile.am
@@ -0,0 +1,62 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2007 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
+
+perlmodsbuilddir = $(perlmoddir)/Sbuild
+
+MODULES = \
+ Base.pm \
+ Build.pm \
+ Chroot.pm \
+ ChrootPlain.pm \
+ ChrootRoot.pm \
+ ChrootSchroot.pm \
+ ChrootSudo.pm \
+ ChrootAutopkgtest.pm \
+ ChrootUnshare.pm \
+ ChrootSetup.pm \
+ ChrootInfo.pm \
+ ChrootInfoSchroot.pm \
+ ChrootInfoSudo.pm \
+ ChrootInfoAutopkgtest.pm \
+ ChrootInfoUnshare.pm \
+ Exception.pm \
+ ResolverBase.pm \
+ AptitudeResolver.pm \
+ AptResolver.pm \
+ AspcudResolver.pm \
+ XaptResolver.pm \
+ NullResolver.pm \
+ Resolver.pm \
+ Conf.pm \
+ ConfBase.pm \
+ LogBase.pm \
+ Options.pm \
+ OptionsBase.pm \
+ Utility.pm
+
+perlmodsbuild_DATA = \
+ $(MODULES) \
+ Sysconfig.pm
+
+EXTRA_DIST = \
+ $(MODULES)
diff --git a/lib/Sbuild/NullResolver.pm b/lib/Sbuild/NullResolver.pm
new file mode 100644
index 0000000..3e2bf8c
--- /dev/null
+++ b/lib/Sbuild/NullResolver.pm
@@ -0,0 +1,69 @@
+# ResolverBase.pm: build library for sbuild
+# Copyright © 2018 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/>.
+#
+#######################################################################
+
+package Sbuild::NullResolver;
+
+use strict;
+use warnings;
+
+use Sbuild qw(debug copy);
+use Sbuild::Base;
+use Sbuild::ResolverBase;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ResolverBase);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $self = $class->SUPER::new($conf, $session, $host);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub install_deps {
+ my $self = shift;
+ my $name = shift;
+ my @pkgs = @_;
+
+ $self->log("Null resolver: not installing $name dependencies\n");
+
+ return 1;
+}
+
+sub purge_extra_packages {
+ my $self = shift;
+ my $name = shift;
+
+ $self->log("Null resolver: not removing $name dependencies\n");
+
+ return 1;
+}
+
+1;
diff --git a/lib/Sbuild/Options.pm b/lib/Sbuild/Options.pm
new file mode 100644
index 0000000..8cc70dc
--- /dev/null
+++ b/lib/Sbuild/Options.pm
@@ -0,0 +1,633 @@
+#
+# Options.pm: options parser for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2006 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::Options;
+
+use strict;
+use warnings;
+
+use Sbuild::OptionsBase;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::OptionsBase);
+
+ @EXPORT = qw();
+}
+
+sub set_options {
+ my $self = shift;
+
+ my ($opt_arch_all, $opt_no_arch_all);
+ my ($opt_build_arch, $opt_host_arch, $opt_arch);
+ my ($opt_arch_any, $opt_no_arch_any);
+ my ($opt_source, $opt_no_source);
+ my ($opt_apt_clean, $opt_no_apt_clean, $opt_apt_update, $opt_no_apt_update,
+ $opt_apt_upgrade, $opt_no_apt_upgrade, $opt_apt_distupgrade, $opt_no_apt_distupgrade);
+ my ($opt_purge, $opt_purge_build, $opt_purge_deps, $opt_purge_session);
+ my ($opt_resolve_alternatives, $opt_no_resolve_alternatives);
+ my ($opt_clean_source, $opt_no_clean_source);
+ my ($opt_run_lintian, $opt_no_run_lintian);
+ my ($opt_run_piuparts, $opt_no_run_piuparts);
+ my ($opt_run_autopkgtest, $opt_no_run_autopkgtest);
+ my ($opt_make_binnmu, $opt_binnmu, $opt_binnmu_timestamp, $opt_binnmu_changelog, $opt_append_to_version);
+
+ $self->add_options("arch=s" => sub {
+ if (defined $opt_arch && $opt_arch ne $_[1]) {
+ die "cannot specify differing --arch multiple times";
+ }
+ if (defined $opt_build_arch && $opt_build_arch ne $_[1]) {
+ die "cannot specify --arch together with differing --build-arch";
+ }
+ if (defined $opt_host_arch && $opt_host_arch ne $_[1]) {
+ die "cannot specify --arch together with differing --host-arch";
+ }
+ $self->set_conf('HOST_ARCH', $_[1]);
+ $self->set_conf('BUILD_ARCH', $_[1]);
+ $opt_arch = $_[1];
+ },
+ "build=s" => sub {
+ if (defined $opt_build_arch && $opt_build_arch ne $_[1]) {
+ die "cannot specify differing --build-arch multiple times";
+ }
+ if (defined $opt_arch && $opt_arch ne $_[1]) {
+ die "cannot specify --build-arch together with differing --arch";
+ }
+ $self->set_conf('BUILD_ARCH', $_[1]);
+ $opt_build_arch = $_[1];
+ },
+ "host=s" => sub {
+ if (defined $opt_host_arch && $opt_host_arch ne $_[1]) {
+ die "cannot specify differing --host-arch multiple times";
+ }
+ if (defined $opt_arch && $opt_arch ne $_[1]) {
+ die "cannot specify --host-arch together with differing --arch";
+ }
+ $self->set_conf('HOST_ARCH', $_[1]);
+ $opt_host_arch = $_[1];
+ },
+ "A|arch-all" => sub {
+ if ($opt_no_arch_all) {
+ die "--arch-all cannot be used together with --no-arch-all";
+ }
+ if ($opt_make_binnmu) {
+ die "--arch-all cannot be used together with --make-binNMU";
+ }
+ if ($opt_append_to_version) {
+ die "--arch-all cannot be used together with --append-to-version";
+ }
+ $self->set_conf('BUILD_ARCH_ALL', 1);
+ $opt_arch_all = 1;
+ },
+ "no-arch-all" => sub {
+ if ($opt_arch_all) {
+ die "--no-arch-all cannot be used together with --arch-all";
+ }
+ $self->set_conf('BUILD_ARCH_ALL', 0);
+ $opt_no_arch_all = 1;
+ },
+ "arch-any" => sub {
+ if ($opt_no_arch_any) {
+ die "--arch-any cannot be used together with --no-arch-any";
+ }
+ $self->set_conf('BUILD_ARCH_ANY', 1);
+ $opt_arch_any = 1;
+ },
+ "no-arch-any" => sub {
+ if ($opt_arch_any) {
+ die "--no-arch-any cannot be used together with --arch-any";
+ }
+ $self->set_conf('BUILD_ARCH_ANY', 0);
+ $opt_no_arch_any = 1;
+ },
+ "profiles=s" => sub {
+ $_[1] =~ tr/,/ /;
+ $self->set_conf('BUILD_PROFILES', $_[1]);
+ },
+ "add-depends=s" => sub {
+ push(@{$self->get_conf('MANUAL_DEPENDS')}, $_[1]);
+ },
+ "add-conflicts=s" => sub {
+ push(@{$self->get_conf('MANUAL_CONFLICTS')}, $_[1]);
+ },
+ "add-depends-arch=s" => sub {
+ push(@{$self->get_conf('MANUAL_DEPENDS_ARCH')}, $_[1]);
+ },
+ "add-conflicts-arch=s" => sub {
+ push(@{$self->get_conf('MANUAL_CONFLICTS_ARCH')}, $_[1]);
+ },
+ "add-depends-indep=s" => sub {
+ push(@{$self->get_conf('MANUAL_DEPENDS_INDEP')}, $_[1]);
+ },
+ "add-conflicts-indep=s" => sub {
+ push(@{$self->get_conf('MANUAL_CONFLICTS_INDEP')}, $_[1]);
+ },
+ "b|batch" => sub {
+ $self->set_conf('BATCH_MODE', 1);
+ },
+ "make-binNMU=s" => sub {
+ if ($opt_binnmu_changelog) {
+ die "--make-binNMU cannot be used together with --binNMU-changelog";
+ }
+ $self->set_conf('BIN_NMU', $_[1]);
+ $self->set_conf('BIN_NMU_VERSION', 1)
+ if (!defined $self->get_conf('BIN_NMU_VERSION'));
+ $opt_make_binnmu = 1;
+ $self->set_conf('BUILD_ARCH_ALL', 0);
+ },
+ "binNMU=i" => sub {
+ if ($opt_binnmu_changelog) {
+ die "--binNMU cannot be used together with --binNMU-changelog";
+ }
+ $self->set_conf('BIN_NMU_VERSION', $_[1]);
+ $opt_binnmu = 1;
+ },
+ "binNMU-timestamp=s" => sub {
+ if ($opt_binnmu_changelog) {
+ die "--binNMU-timestamp cannot be used together with --binNMU-changelog";
+ }
+ $self->set_conf('BIN_NMU_TIMESTAMP', $_[1]);
+ $opt_binnmu_timestamp = 1;
+ },
+ "append-to-version=s" => sub {
+ if ($opt_binnmu_changelog) {
+ die "--append-to-version cannot be used together with --binNMU-changelog";
+ }
+ $self->set_conf('APPEND_TO_VERSION', $_[1]);
+ $opt_append_to_version = 1;
+ $self->set_conf('BUILD_ARCH_ALL', 0);
+ },
+ "binNMU-changelog=s" => sub {
+ if ($opt_make_binnmu) {
+ die "--binNMU-changelog cannot be used together with --make-binNMU";
+ }
+ if ($opt_binnmu) {
+ die "--binNMU-changelog cannot be used together with --binNMU";
+ }
+ if ($opt_binnmu_timestamp) {
+ die "--binNMU-changelog cannot be used together with --binNMU-timestamp";
+ }
+ if ($opt_append_to_version) {
+ die "--binNMU-changelog cannot be used together with --append-to-version";
+ }
+ $self->set_conf('BIN_NMU_CHANGELOG', $_[1]);
+ $opt_binnmu_changelog = 1;
+ },
+ "build-dir=s" => sub {
+ $self->set_conf('BUILD_DIR', $_[1]);
+ },
+ "c|chroot=s" => sub {
+ $self->set_conf('CHROOT', $_[1]);
+ },
+ "chroot-mode=s" => sub {
+ $self->set_conf('CHROOT_MODE', $_[1]);
+ },
+ "autopkgtest-virt-server=s" => sub {
+ $self->set_conf('AUTOPKGTEST_VIRT_SERVER', $_[1]);
+ },
+ "autopkgtest-virt-server-opts=s" => sub {
+ push(@{$self->get_conf('AUTOPKGTEST_VIRT_SERVER_OPTIONS')},
+ split(/\s+/, $_[1]));
+ },
+ "autopkgtest-virt-server-opt=s" => sub {
+ push(@{$self->get_conf('AUTOPKGTEST_VIRT_SERVER_OPTIONS')}, $_[1]);
+ },
+ "apt-clean" => sub {
+ if ($opt_no_apt_clean) {
+ die "--apt-clean cannot be used together with --no-apt-clean";
+ }
+ $self->set_conf('APT_CLEAN', 1);
+ $opt_apt_clean = 1;
+ },
+ "apt-update" => sub {
+ if ($opt_no_apt_update) {
+ die "--apt-update cannot be used together with --no-apt-update";
+ }
+ $self->set_conf('APT_UPDATE', 1);
+ $opt_apt_update = 1;
+ },
+ "apt-upgrade" => sub {
+ if ($opt_no_apt_upgrade) {
+ die "--apt-upgrade cannot be used together with --no-apt-upgrade";
+ }
+ $self->set_conf('APT_UPGRADE', 1);
+ $opt_apt_upgrade = 1;
+ },
+ "apt-distupgrade" => sub {
+ if ($opt_no_apt_distupgrade) {
+ die "--apt-distupgrade cannot be used together with --no-apt-distupgrade";
+ }
+ $self->set_conf('APT_DISTUPGRADE', 1);
+ $opt_apt_distupgrade = 1;
+ },
+ "no-apt-clean" => sub {
+ if ($opt_apt_clean) {
+ die "--no-apt-clean cannot be used together with --apt-clean";
+ }
+ $self->set_conf('APT_CLEAN', 0);
+ $opt_no_apt_clean = 1;
+ },
+ "no-apt-update" => sub {
+ if ($opt_apt_update) {
+ die "--no-apt-update cannot be used together with --apt-update";
+ }
+ $self->set_conf('APT_UPDATE', 0);
+ $opt_no_apt_update = 1;
+ },
+ "no-apt-upgrade" => sub {
+ if ($opt_apt_upgrade) {
+ die "--no-apt-upgrade cannot be used together with --apt-upgrade";
+ }
+ $self->set_conf('APT_UPGRADE', 0);
+ $opt_no_apt_upgrade = 1;
+ },
+ "no-apt-distupgrade" => sub {
+ if ($opt_apt_distupgrade) {
+ die "--no-apt-distupgrade cannot be used together with --apt-distupgrade";
+ }
+ $self->set_conf('APT_DISTUPGRADE', 0);
+ $opt_no_apt_distupgrade = 1;
+ },
+ "d|dist=s" => sub {
+ $self->set_conf('DISTRIBUTION', $_[1]);
+ $self->set_conf('DISTRIBUTION', "oldstable")
+ if $self->get_conf('DISTRIBUTION') eq "o";
+ $self->set_conf('DISTRIBUTION', "stable")
+ if $self->get_conf('DISTRIBUTION') eq "s";
+ $self->set_conf('DISTRIBUTION', "testing")
+ if $self->get_conf('DISTRIBUTION') eq "t";
+ $self->set_conf('DISTRIBUTION', "unstable")
+ if $self->get_conf('DISTRIBUTION') eq "u";
+ $self->set_conf('DISTRIBUTION', "experimental")
+ if $self->get_conf('DISTRIBUTION') eq "e";
+ $self->set_conf('OVERRIDE_DISTRIBUTION', 1);
+ },
+ "force-orig-source" => sub {
+ $self->set_conf('FORCE_ORIG_SOURCE', 1);
+ },
+ "m|maintainer=s" => sub {
+ $self->set_conf('MAINTAINER_NAME', $_[1]);
+ },
+ "mailfrom=s" => sub {
+ $self->set_conf('MAILFROM', $_[1]);
+ },
+ "sbuild-mode=s" => sub {
+ $self->set_conf('SBUILD_MODE', $_[1]);
+ },
+ "k|keyid=s" => sub {
+ $self->set_conf('KEY_ID', $_[1]);
+ },
+ "e|uploader=s" => sub {
+ $self->set_conf('UPLOADER_NAME', $_[1]);
+ },
+ "debbuildopts=s" => sub {
+ push(@{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')},
+ split(/\s+/, $_[1]));
+ },
+ "debbuildopt=s" => sub {
+ push(@{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')},
+ $_[1]);
+ },
+ "dpkg-file-suffix=s" => sub {
+ $self->set_conf('DPKG_FILE_SUFFIX', $_[1]);
+ },
+ "j|jobs=i" => sub {
+ push(@{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')},
+ '-j'.$_[1])
+ },
+ "dpkg-source-opts=s" => sub {
+ push(@{$self->get_conf('DPKG_SOURCE_OPTIONS')},
+ split(/\s+/, $_[1]));
+ },
+ "dpkg-source-opt=s" => sub {
+ push(@{$self->get_conf('DPKG_SOURCE_OPTIONS')},
+ $_[1]);
+ },
+ "mail-log-to=s" => sub {
+ $self->set_conf('MAILTO', $_[1]);
+ $self->set_conf('MAILTO_FORCED_BY_CLI', "yes");
+ },
+ "n|nolog" => sub {
+ $self->set_conf('NOLOG', 1);
+ },
+ "p|purge=s" => sub {
+ if (defined $opt_purge_build) {
+ die "cannot specify --purge together with --purge-build";
+ }
+ if (defined $opt_purge_deps) {
+ die "cannot specify --purge together with --purge-deps";
+ }
+ if (defined $opt_purge_session) {
+ die "cannot specify --purge together with --purge-session";
+ }
+ $self->set_conf('PURGE_BUILD_DEPS', $_[1]);
+ $self->set_conf('PURGE_BUILD_DIRECTORY', $_[1]);
+ $self->set_conf('PURGE_SESSION', $_[1]);
+ $opt_purge = 1;
+ },
+ "purge-build=s" => sub {
+ if (defined $opt_purge) {
+ die "cannot specify --purge-build together with --purge";
+ }
+ $self->set_conf('PURGE_BUILD_DIRECTORY', $_[1]);
+ $opt_purge_build = 1;
+ },
+ "purge-deps=s" => sub {
+ if (defined $opt_purge) {
+ die "cannot specify --purge-deps together with --purge";
+ }
+ $self->set_conf('PURGE_BUILD_DEPS', $_[1]);
+ $opt_purge_deps = 1;
+ },
+ "purge-session=s" => sub {
+ if (defined $opt_purge) {
+ die "cannot specify --purge-session together with --purge";
+ }
+ $self->set_conf('PURGE_SESSION', $_[1]);
+ $opt_purge_session = 1;
+ },
+ "s|source" => sub {
+ if ($opt_no_source) {
+ die "--source cannot be used together with --no-source";
+ }
+ $self->set_conf('BUILD_SOURCE', 1);
+ $opt_source = 1;
+ },
+ "no-source" => sub {
+ if ($opt_source) {
+ die "--no-source cannot be used together with --source";
+ }
+ $self->set_conf('BUILD_SOURCE', 0);
+ $opt_no_source = 1;
+ },
+ "archive=s" => sub {
+ $self->set_conf('ARCHIVE', $_[1]);
+ },
+ "stats-dir=s" => sub {
+ $self->set_conf('STATS_DIR', $_[1]);
+ },
+ "setup-hook=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"chroot-setup-commands"}},
+ $_[1]);
+ $self->set_conf('CHROOT_SETUP_SCRIPT', $_[1]);
+ },
+ "use-snapshot" => sub {
+ my $newldpath = '/usr/lib/gcc-snapshot/lib';
+ my $ldpath = $self->get_conf('LD_LIBRARY_PATH');
+ if (defined($ldpath) && $ldpath ne '') {
+ $newldpath .= ':' . $ldpath;
+ }
+
+ $self->set_conf('GCC_SNAPSHOT', 1);
+ $self->set_conf('LD_LIBRARY_PATH', $newldpath);
+ $self->set_conf('PATH',
+ '/usr/lib/gcc-snapshot/bin' .
+ $self->get_conf('PATH') ne '' ? ':' . $self->get_conf('PATH') : '');
+ },
+ "build-dep-resolver=s" => sub {
+ $self->set_conf('BUILD_DEP_RESOLVER', $_[1]);
+ },
+ "aspcud-criteria=s" => sub {
+ $self->set_conf('ASPCUD_CRITERIA', $_[1]);
+ },
+ "resolve-alternatives" => sub {
+ if ($opt_no_resolve_alternatives) {
+ die "--resolve-alternatives cannot be used together with --no-resolve-alternatives";
+ }
+ $self->set_conf('RESOLVE_ALTERNATIVES', 1);
+ $opt_resolve_alternatives = 1;
+ },
+ "no-resolve-alternatives" => sub {
+ if ($opt_resolve_alternatives) {
+ die "--no-resolve-alternatives cannot be used together with --resolve-alternatives";
+ }
+ $self->set_conf('RESOLVE_ALTERNATIVES', 0);
+ $opt_no_resolve_alternatives = 1;
+ },
+ "clean-source" => sub {
+ if ($opt_no_clean_source) {
+ die "--clean-source cannot be used together with --no-clean-source";
+ }
+ $self->set_conf('CLEAN_SOURCE', 1);
+ $opt_clean_source = 1;
+ },
+ "no-clean-source" => sub {
+ if ($opt_clean_source) {
+ die "--no-clean-source cannot be used together with --clean-source";
+ }
+ $self->set_conf('CLEAN_SOURCE', 0);
+ $opt_no_clean_source = 1;
+ },
+ "run-lintian" => sub {
+ if ($opt_no_run_lintian) {
+ die "--run-lintian cannot be used together with --no-run-lintian";
+ }
+ $self->set_conf('RUN_LINTIAN', 1);
+ $opt_run_lintian = 1;
+ },
+ "no-run-lintian" => sub {
+ if ($opt_run_lintian) {
+ die "--no-run-lintian cannot be used together with --run-lintian";
+ }
+ $self->set_conf('RUN_LINTIAN', 0);
+ $opt_no_run_lintian = 1;
+ },
+ "lintian-opts=s" => sub {
+ push(@{$self->get_conf('LINTIAN_OPTIONS')},
+ split(/\s+/, $_[1]));
+ },
+ "lintian-opt=s" => sub {
+ push(@{$self->get_conf('LINTIAN_OPTIONS')},
+ $_[1]);
+ },
+ "run-piuparts" => sub {
+ if ($opt_no_run_piuparts) {
+ die "--run-piuparts cannot be used together with --no-run-piuparts";
+ }
+ $self->set_conf('RUN_PIUPARTS', 1);
+ $opt_run_piuparts = 1;
+ },
+ "no-run-piuparts" => sub {
+ if ($opt_run_piuparts) {
+ die "--no-run-piuparts cannot be used together with --run-piuparts";
+ }
+ $self->set_conf('RUN_PIUPARTS', 0);
+ $opt_no_run_piuparts = 1;
+ },
+ "piuparts-opts=s" => sub {
+ push(@{$self->get_conf('PIUPARTS_OPTIONS')},
+ split(/\s+/, $_[1]));
+ },
+ "piuparts-opt=s" => sub {
+ push(@{$self->get_conf('PIUPARTS_OPTIONS')},
+ $_[1]);
+ },
+ "piuparts-root-args=s" => sub {
+ push(@{$self->get_conf('PIUPARTS_ROOT_ARGS')},
+ split(/\s+/, $_[1]));
+ },
+ "piuparts-root-arg=s" => sub {
+ push(@{$self->get_conf('PIUPARTS_ROOT_ARGS')},
+ $_[1]);
+ },
+ "run-autopkgtest" => sub {
+ if ($opt_no_run_autopkgtest) {
+ die "--run-autopkgtest cannot be used together with --no-run-autopkgtest";
+ }
+ $self->set_conf('RUN_AUTOPKGTEST', 1);
+ $opt_run_autopkgtest = 1;
+ },
+ "no-run-autopkgtest" => sub {
+ if ($opt_run_autopkgtest) {
+ die "--no-run-autopkgtest cannot be used together with --run-autopkgtest";
+ }
+ $self->set_conf('RUN_AUTOPKGTEST', 0);
+ $opt_no_run_autopkgtest = 1;
+ },
+ "autopkgtest-opts=s" => sub {
+ push(@{$self->get_conf('AUTOPKGTEST_OPTIONS')},
+ split(/\s+/, $_[1]));
+ },
+ "autopkgtest-opt=s" => sub {
+ push(@{$self->get_conf('AUTOPKGTEST_OPTIONS')},
+ $_[1]);
+ },
+ "autopkgtest-root-args=s" => sub {
+ # special handling of the case when the string
+ # argument is the empty string. In that case, the
+ # empty string is appended. The split function
+ # would just return an empty list when splitting
+ # the empty string
+ if ($_[1] eq '') {
+ push(@{$self->get_conf('AUTOPKGTEST_ROOT_ARGS')},
+ '');
+ } else {
+ push(@{$self->get_conf('AUTOPKGTEST_ROOT_ARGS')},
+ split(/\s+/, $_[1]));
+ }
+ },
+ "autopkgtest-root-arg=s" => sub {
+ push(@{$self->get_conf('AUTOPKGTEST_ROOT_ARGS')},
+ $_[1]);
+ },
+ "pre-build-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"pre-build-commands"}},
+ $_[1]);
+ },
+ "chroot-setup-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"chroot-setup-commands"}},
+ $_[1]);
+ },
+ "chroot-update-failed-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"chroot-update-failed-commands"}},
+ $_[1]);
+ },
+ "build-deps-failed-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"build-deps-failed-commands"}},
+ $_[1]);
+ },
+ "build-failed-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"build-failed-commands"}},
+ $_[1]);
+ },
+ "anything-failed-commands=s" => sub {
+
+ # --anything-failed-commands simply triggers all the
+ # --xxx-failed-commands I know about
+
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"chroot-update-failed-commands"}},
+ $_[1]);
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"build-deps-failed-commands"}},
+ $_[1]);
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"build-failed-commands"}},
+ $_[1]);
+ },
+ "starting-build-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"starting-build-commands"}},
+ $_[1]);
+ },
+ "finished-build-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"finished-build-commands"}},
+ $_[1]);
+ },
+ "chroot-cleanup-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"chroot-cleanup-commands"}},
+ $_[1]);
+ },
+ "post-build-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"post-build-commands"}},
+ $_[1]);
+ },
+ "post-build-failed-commands=s" => sub {
+ push(@{${$self->get_conf('EXTERNAL_COMMANDS')}{"post-build-failed-commands"}},
+ $_[1]);
+ },
+ "log-external-command-output" => sub {
+ $self->set_conf('LOG_EXTERNAL_COMMAND_OUTPUT', 1);
+ },
+ "log-external-command-error" => sub {
+ $self->set_conf('LOG_EXTERNAL_COMMAND_ERROR', 1);
+ },
+ "extra-package=s" => sub {
+ push(@{$self->get_conf('EXTRA_PACKAGES')}, $_[1]);
+ },
+ "extra-repository=s" => sub {
+ push(@{$self->get_conf('EXTRA_REPOSITORIES')}, $_[1]);
+ },
+ "extra-repository-key=s" => sub {
+ push(@{$self->get_conf('EXTRA_REPOSITORY_KEYS')}, $_[1]);
+ },
+ "build-path=s" => sub {
+ $self->set_conf('BUILD_PATH', $_[1]);
+ },
+ "dsc-dir=s" => sub {
+ $self->set_conf('DSC_DIR', $_[1]);
+ },
+ "source-only-changes" => sub {
+ $self->set_conf('SOURCE_ONLY_CHANGES', 1);
+ },
+ "no-source-only-changes" => sub {
+ $self->set_conf('SOURCE_ONLY_CHANGES', 0);
+ },
+ "purge-extra-packages" => sub {
+ $self->set_conf('PURGE_EXTRA_PACKAGES', 1);
+ },
+ "bd-uninstallable-explainer=s" => sub {
+ $self->set_conf('BD_UNINSTALLABLE_EXPLAINER', $_[1]);
+ }
+ );
+}
+
+=pod
+This function allows to extrapolate from the parsed and set options some
+expected behaviours.
+=cut
+sub extrapolate_options {
+ my $self = shift;
+
+ # This allows to pass -sa to all commands instead of passing it just to dpkg-buildpackage
+ push (@{$self->get_conf('DPKG_BUILDPACKAGE_USER_OPTIONS')}, "-sa") if ($self->get_conf('BUILD_SOURCE') && $self->get_conf('FORCE_ORIG_SOURCE'));
+}
+
+1;
diff --git a/lib/Sbuild/OptionsBase.pm b/lib/Sbuild/OptionsBase.pm
new file mode 100644
index 0000000..d9b801b
--- /dev/null
+++ b/lib/Sbuild/OptionsBase.pm
@@ -0,0 +1,102 @@
+#
+# OptionsBase.pm: options parser (base functionality) for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::OptionsBase;
+
+use strict;
+use warnings;
+
+use Getopt::Long qw(:config no_ignore_case auto_abbrev gnu_getopt);
+use Sbuild qw(help_text version_text usage_error);
+use Sbuild::Base;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $program = shift;
+ my $section = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->add_options("h|help" => sub { help_text($section, $program); },
+ "V|version" => sub {version_text($program); },
+ "D|debug" => sub {
+ $self->set_conf('DEBUG',
+ $self->get_conf('DEBUG') + 1); },
+ "v|verbose" => sub {
+ $self->set_conf('VERBOSE',
+ $self->get_conf('VERBOSE') + 1);
+ },
+ "q|quiet" => sub {
+ $self->set_conf('VERBOSE',
+ $self->get_conf('VERBOSE') - 1)
+ if $self->get_conf('VERBOSE');
+ });
+
+ $self->set_options();
+
+ if (!$self->parse_options()) {
+ usage_error($program, "Error parsing command-line options");
+ return undef;
+ }
+ $self->extrapolate_options();
+ return $self;
+}
+
+sub add_options () {
+ my $self = shift;
+ my @newopts = @_;
+
+ my %options;
+ if (defined($self->get('Options'))) {
+ %options = (%{$self->get('Options')}, @newopts);
+ } else {
+ %options = (@newopts);
+ }
+ $self->set('Options', \%options);
+}
+
+sub set_options () {
+ my $self = shift;
+}
+
+sub extrapolate_options () {
+ my $self = shift;
+}
+
+sub parse_options {
+ my $self = shift;
+
+ return GetOptions((%{$self->get('Options')}));
+}
+
+1;
diff --git a/lib/Sbuild/Resolver.pm b/lib/Sbuild/Resolver.pm
new file mode 100644
index 0000000..d811fa9
--- /dev/null
+++ b/lib/Sbuild/Resolver.pm
@@ -0,0 +1,66 @@
+#
+# Resolver.pm: library for sbuild
+# Copyright © 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/>.
+#
+#######################################################################
+
+package Sbuild::Resolver;
+
+use Sbuild::AptResolver;
+use Sbuild::XaptResolver;
+use Sbuild::AptitudeResolver;
+use Sbuild::AspcudResolver;
+use Sbuild::NullResolver;
+
+use strict;
+use warnings;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(get_resolver);
+}
+
+sub get_resolver ($$$);
+
+sub get_resolver ($$$) {
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $resolver;
+ if ($conf->get('BUILD_DEP_RESOLVER') eq "apt") {
+ $resolver = Sbuild::AptResolver->new($conf, $session, $host);
+ } elsif ($conf->get('BUILD_DEP_RESOLVER') eq "xapt") {
+ $resolver = Sbuild::XaptResolver->new($conf, $session, $host);
+ } elsif ($conf->get('BUILD_DEP_RESOLVER') eq "aptitude") {
+ $resolver = Sbuild::AptitudeResolver->new($conf, $session, $host);
+ } elsif ($conf->get('BUILD_DEP_RESOLVER') eq "aspcud") {
+ $resolver = Sbuild::AspcudResolver->new($conf, $session, $host);
+ } elsif ($conf->get('BUILD_DEP_RESOLVER') eq "null") {
+ $resolver = Sbuild::NullResolver->new($conf, $session, $host);
+ } else {
+ $resolver = Sbuild::AptResolver->new($conf, $session, $host);
+ }
+
+ return $resolver;
+}
+
+1;
diff --git a/lib/Sbuild/ResolverBase.pm b/lib/Sbuild/ResolverBase.pm
new file mode 100644
index 0000000..dff1905
--- /dev/null
+++ b/lib/Sbuild/ResolverBase.pm
@@ -0,0 +1,1685 @@
+# Resolver.pm: build library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2010 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::ResolverBase;
+
+use strict;
+use warnings;
+use POSIX;
+use Fcntl;
+use File::Temp qw(mktemp);
+use File::Basename qw(basename);
+use File::Copy;
+use MIME::Base64;
+
+use Dpkg::Deps;
+use Sbuild::Base;
+use Sbuild qw(isin debug debug2);
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::Base);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $self = $class->SUPER::new($conf);
+ bless($self, $class);
+
+ $self->set('Session', $session);
+ $self->set('Host', $host);
+ $self->set('Changes', {});
+ $self->set('AptDependencies', {});
+ $self->set('Split', $self->get_conf('CHROOT_SPLIT'));
+ # Typically set by Sbuild::Build, but not outside a build context.
+ $self->set('Host Arch', $self->get_conf('HOST_ARCH'));
+ $self->set('Build Arch', $self->get_conf('BUILD_ARCH'));
+ $self->set('Build Profiles', $self->get_conf('BUILD_PROFILES'));
+ $self->set('Multiarch Support', 1);
+ $self->set('Initial Foreign Arches', {});
+ $self->set('Added Foreign Arches', {});
+
+ my $dummy_archive_list_file =
+ '/etc/apt/sources.list.d/sbuild-build-depends-archive.list';
+ $self->set('Dummy archive list file', $dummy_archive_list_file);
+
+ my $extra_repositories_archive_list_file =
+ '/etc/apt/sources.list.d/sbuild-extra-repositories.list';
+ $self->set('Extra repositories archive list file', $extra_repositories_archive_list_file);
+
+ my $extra_packages_archive_list_file =
+ '/etc/apt/sources.list.d/sbuild-extra-packages-archive.list';
+ $self->set('Extra packages archive list file', $extra_packages_archive_list_file);
+
+ return $self;
+}
+
+sub add_extra_repositories {
+ my $self = shift;
+ my $session = $self->get('Session');
+
+ # Add specified extra repositories into /etc/apt/sources.list.d/.
+ # This has to be done this early so that the early apt
+ # update/upgrade/distupgrade steps also consider the extra repositories.
+ # If this step would be done too late, extra repositories would only be
+ # considered when resolving build dependencies but not for upgrading the
+ # base chroot.
+ if (scalar @{$self->get_conf('EXTRA_REPOSITORIES')} > 0) {
+ my $extra_repositories_archive_list_file = $self->get('Extra repositories archive list file');
+ if ($session->test_regular_file($extra_repositories_archive_list_file)) {
+ $self->log_error("$extra_repositories_archive_list_file exists - will not write extra repositories to it\n");
+ } else {
+ my $tmpfilename = $session->mktemp();
+
+ my $tmpfh = $session->get_write_file_handle($tmpfilename);
+ if (!$tmpfh) {
+ $self->log_error("Cannot open pipe: $!\n");
+ return 0;
+ }
+ for my $repospec (@{$self->get_conf('EXTRA_REPOSITORIES')}) {
+ print $tmpfh "$repospec\n";
+ }
+ close $tmpfh;
+ # List file needs to be moved with root.
+ if (!$session->chmod($tmpfilename, '0644')) {
+ $self->log("Failed to create apt list file for dummy archive.\n");
+ $session->unlink($tmpfilename);
+ return 0;
+ }
+ if (!$session->rename($tmpfilename, $extra_repositories_archive_list_file)) {
+ $self->log("Failed to create apt list file for dummy archive.\n");
+ $session->unlink($tmpfilename);
+ return 0;
+ }
+ }
+ }
+}
+
+sub setup {
+ my $self = shift;
+
+ my $session = $self->get('Session');
+ my $chroot_dir = $session->get('Location');
+
+ #Set up dpkg config
+ $self->setup_dpkg();
+
+ my $aptconf = "/var/lib/sbuild/apt.conf";
+ $self->set('APT Conf', $aptconf);
+
+ my $chroot_aptconf = $session->get('Location') . "/$aptconf";
+ $self->set('Chroot APT Conf', $chroot_aptconf);
+
+ my $tmpaptconf = $session->mktemp({ TEMPLATE => "$aptconf.XXXXXX"});
+ if (!$tmpaptconf) {
+ $self->log_error("Can't create $chroot_aptconf.XXXXXX: $!\n");
+ return 0;
+ }
+
+ my $F = $session->get_write_file_handle($tmpaptconf);
+ if (!$F) {
+ $self->log_error("Cannot open pipe: $!\n");
+ return 0;
+ }
+
+ # Always write out apt.conf, because it may become outdated.
+ if ($self->get_conf('APT_ALLOW_UNAUTHENTICATED')) {
+ print $F qq(APT::Get::AllowUnauthenticated "true";\n);
+ }
+ print $F qq(APT::Install-Recommends "false";\n);
+ print $F qq(APT::AutoRemove::SuggestsImportant "false";\n);
+ print $F qq(APT::AutoRemove::RecommendsImportant "false";\n);
+ print $F qq(Acquire::Languages "none";\n); # do not download translations
+
+ if ($self->get_conf('APT_KEEP_DOWNLOADED_PACKAGES')) {
+ print $F qq(APT::Keep-Downloaded-Packages "true";\n);
+ } else {
+ # remove packages from /var/cache/apt/archive/*.deb after installation
+ print $F qq(APT::Keep-Downloaded-Packages "false";\n);
+ }
+
+ if ($self->get('Split')) {
+ print $F "Dir \"$chroot_dir\";\n";
+ }
+
+ close $F;
+
+ if (!$session->rename($tmpaptconf, $aptconf)) {
+ $self->log_error("Can't rename $tmpaptconf to $aptconf: $!\n");
+ return 0;
+ }
+
+ if (!$session->chown($aptconf, $self->get_conf('BUILD_USER'), 'sbuild')) {
+ $self->log_error("Failed to set " . $self->get_conf('BUILD_USER') .
+ ":sbuild ownership on apt.conf at $aptconf\n");
+ return 0;
+ }
+ if (!$session->chmod($aptconf, '0664')) {
+ $self->log_error("Failed to set 0664 permissions on apt.conf at $aptconf\n");
+ return 0;
+ }
+
+ # unsplit mode uses an absolute path inside the chroot, rather
+ # than on the host system.
+ if ($self->get('Split')) {
+ $self->set('APT Options',
+ ['-o', "Dir::State::status=$chroot_dir/var/lib/dpkg/status",
+ '-o', "DPkg::Options::=--root=$chroot_dir",
+ '-o', "DPkg::Run-Directory=$chroot_dir"]);
+
+ $self->set('Aptitude Options',
+ ['-o', "Dir::State::status=$chroot_dir/var/lib/dpkg/status",
+ '-o', "DPkg::Options::=--root=$chroot_dir",
+ '-o', "DPkg::Run-Directory=$chroot_dir"]);
+
+ # sudo uses an absolute path on the host system.
+ $session->get('Defaults')->{'ENV'}->{'APT_CONFIG'} =
+ $self->get('Chroot APT Conf');
+ } else { # no split
+ $self->set('APT Options', []);
+ $self->set('Aptitude Options', []);
+ $session->get('Defaults')->{'ENV'}->{'APT_CONFIG'} =
+ $self->get('APT Conf');
+ }
+
+ $self->add_extra_repositories();
+
+ # Create an internal repository for packages given via --extra-package
+ # If this step would be done too late, extra packages would only be
+ # considered when resolving build dependencies but not for upgrading the
+ # base chroot.
+ if (scalar @{$self->get_conf('EXTRA_PACKAGES')} > 0) {
+ my $extra_packages_archive_list_file = $self->get('Extra packages archive list file');
+ if ($session->test_regular_file($extra_packages_archive_list_file)) {
+ $self->log_error("$extra_packages_archive_list_file exists - will not write extra packages archive list to it\n");
+ } else {
+ #Prepare a path to place the extra packages
+ if (! defined $self->get('Extra packages path')) {
+ my $tmpdir = $session->mktemp({ TEMPLATE => $self->get('Build Dir') . '/resolver-XXXXXX', DIRECTORY => 1});
+ if (!$tmpdir) {
+ $self->log_error("mktemp -d " . $self->get('Build Dir') . '/resolver-XXXXXX failed\n');
+ return 0;
+ }
+ $self->set('Extra packages path', $tmpdir);
+ }
+ if (!$session->chown($self->get('Extra packages path'), $self->get_conf('BUILD_USER'), 'sbuild')) {
+ $self->log_error("Failed to set " . $self->get_conf('BUILD_USER') .
+ ":sbuild ownership on extra packages dir\n");
+ return 0;
+ }
+ if (!$session->chmod($self->get('Extra packages path'), '0770')) {
+ $self->log_error("Failed to set 0770 permissions on extra packages dir\n");
+ return 0;
+ }
+ my $extra_packages_dir = $self->get('Extra packages path');
+ my $extra_packages_archive_dir = $extra_packages_dir . '/apt_archive';
+ my $extra_packages_release_file = $extra_packages_archive_dir . '/Release';
+
+ $self->set('Extra packages archive directory', $extra_packages_archive_dir);
+ $self->set('Extra packages release file', $extra_packages_release_file);
+ my $extra_packages_archive_list_file = $self->get('Extra packages archive list file');
+
+ if (!$session->test_directory($extra_packages_dir)) {
+ $self->log_warning('Could not create build-depends extra packages dir ' . $extra_packages_dir . ': ' . $!);
+ return 0;
+ }
+ if (!($session->test_directory($extra_packages_archive_dir) || $session->mkdir($extra_packages_archive_dir, { MODE => "00775"}))) {
+ $self->log_warning('Could not create build-depends extra packages archive dir ' . $extra_packages_archive_dir . ': ' . $!);
+ return 0;
+ }
+
+ # Copy over all the extra binary packages from the host into the
+ # chroot
+ for my $deb (@{$self->get_conf('EXTRA_PACKAGES')}) {
+ if (-f $deb) {
+ my $base_deb = basename($deb);
+ if ($session->test_regular_file("$extra_packages_archive_dir/$base_deb")) {
+ $self->log_warning("$base_deb already exists in $extra_packages_archive_dir inside the chroot. Skipping...\n");
+ next;
+ }
+ $self->log("Copying $deb to " . $session->get('Location') . "...\n");
+ $session->copy_to_chroot($deb, $extra_packages_archive_dir);
+ } elsif (-d $deb) {
+ opendir(D, $deb);
+ while (my $f = readdir(D)) {
+ next if (! -f "$deb/$f");
+ next if ("$deb/$f" !~ /\.deb$/);
+ if ($session->test_regular_file("$extra_packages_archive_dir/$f")) {
+ $self->log_warning("$f already exists in $extra_packages_archive_dir inside the chroot. Skipping...\n");
+ next;
+ }
+ $self->log("Copying $deb/$f to " . $session->get('Location') . "...\n");
+ $session->copy_to_chroot("$deb/$f", $extra_packages_archive_dir);
+ }
+ closedir(D);
+ } else {
+ $self->log_warning("$deb is neither a regular file nor a directory. Skipping...\n");
+ }
+ }
+
+ # Do code to run apt-ftparchive
+ if (!$self->run_apt_ftparchive($self->get('Extra packages archive directory'))) {
+ $self->log("Failed to run apt-ftparchive.\n");
+ return 0;
+ }
+
+ # Write a list file for the extra packages archive if one not create yet.
+ if (!$session->test_regular_file($extra_packages_archive_list_file)) {
+ my $tmpfilename = $session->mktemp();
+
+ if (!$tmpfilename) {
+ $self->log_error("Can't create tempfile\n");
+ return 0;
+ }
+
+ my $tmpfh = $session->get_write_file_handle($tmpfilename);
+ if (!$tmpfh) {
+ $self->log_error("Cannot open pipe: $!\n");
+ return 0;
+ }
+
+ # We always trust the extra packages apt repositories.
+ print $tmpfh 'deb [trusted=yes] file://' . $extra_packages_archive_dir . " ./\n";
+ print $tmpfh 'deb-src [trusted=yes] file://' . $extra_packages_archive_dir . " ./\n";
+
+ close($tmpfh);
+ # List file needs to be moved with root.
+ if (!$session->chmod($tmpfilename, '0644')) {
+ $self->log("Failed to create apt list file for extra packages archive.\n");
+ $session->unlink($tmpfilename);
+ return 0;
+ }
+ if (!$session->rename($tmpfilename, $extra_packages_archive_list_file)) {
+ $self->log("Failed to create apt list file for extra packages archive.\n");
+ $session->unlink($tmpfilename);
+ return 0;
+ }
+ }
+
+ }
+ }
+
+ # Now, we'll add in any provided OpenPGP keys into the archive, so that
+ # builds can (optionally) trust an external key for the duration of the
+ # build.
+ #
+ # Keys have to be in a format that apt expects to land in
+ # /etc/apt/trusted.gpg.d as they are just copied to there. We could also
+ # support more formats by first importing them using gpg and then
+ # exporting them but that would require gpg to be installed inside the
+ # chroot.
+ if (@{$self->get_conf('EXTRA_REPOSITORY_KEYS')}) {
+ my $host = $self->get('Host');
+ # remember whether running gpg worked or not
+ my $has_gpg = 1;
+ for my $repokey (@{$self->get_conf('EXTRA_REPOSITORY_KEYS')}) {
+ debug("Adding archive key: $repokey\n");
+ if (!-f $repokey) {
+ $self->log("Failed to add archive key '${repokey}' - it doesn't exist!\n");
+ return 0;
+ }
+ # key might be armored but apt requires keys in binary format
+ # We first try to run gpg from the host to convert the key into
+ # binary format (this works even when the key already is in binary
+ # format).
+ my $tmpfilename = mktemp("/tmp/tmp.XXXXXXXXXX");
+ if ($has_gpg == 1) {
+ $host->run_command({
+ COMMAND => ['gpg', '--yes', '--batch', '--output', $tmpfilename, '--dearmor', $repokey],
+ USER => $self->get_conf('BUILD_USER'),
+ });
+ if ($?) {
+ # don't try to use gpg again in later loop iterations
+ $has_gpg = 0;
+ }
+ }
+ # If that doesn't work, then we manually convert the key
+ # as it is just base64 encoded data with a header and footer.
+ #
+ # The decoding of armored gpg keys can even be done from a shell
+ # script by using:
+ #
+ # awk '/^$/{ x = 1; } /^[^=-]/{ if (x) { print $0; } ; }' | base64 -d
+ #
+ # As explained by dkg here: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=831409#67
+ if ($has_gpg == 0) {
+ # Test if we actually have an armored key. Otherwise, no
+ # conversion is needed.
+ open my $fh, '<', $repokey;
+ read $fh, my $first_line, 36;
+ if ($first_line eq "-----BEGIN PGP PUBLIC KEY BLOCK-----") {
+ # Read the remaining part of the line until the newline.
+ # We do it like this because the line might contain
+ # additional whitespace characters or \r\n newlines.
+ <$fh>;
+ open my $out, '>', $tmpfilename;
+ # the file is an armored gpg key, so we convert it to the
+ # binary format
+ my $header = 1;
+ while( my $line = <$fh>) {
+ chomp $line;
+ # an empty line marks the end of the header
+ if ($line eq "") {
+ $header = 0;
+ next;
+ }
+ if ($header == 1) {
+ next;
+ }
+ # the footer might contain lines starting with an
+ # equal sign or minuses
+ if ($line =~ /^[=-]/) {
+ last;
+ }
+ print $out (decode_base64($line));
+ }
+ close $out;
+ }
+ close $fh;
+ }
+ # we could use incrementing integers to number the extra
+ # repository keys but mktemp will also make sure that the new name
+ # doesn't exist yet and avoids the complexity of an additional
+ # variable
+ my $keyfilename = $session->mktemp({TEMPLATE => "/etc/apt/trusted.gpg.d/sbuild-extra-repository-XXXXXXXXXX.gpg"});
+ if (!$keyfilename) {
+ $self->log_error("Can't create tempfile for external repository key\n");
+ $session->unlink($keyfilename);
+ unlink $tmpfilename;
+ return 0;
+ }
+ if (!$session->copy_to_chroot($tmpfilename, $keyfilename)) {
+ $self->log_error("Failed to copy external repository key $repokey into chroot $keyfilename\n");
+ $session->unlink($keyfilename);
+ unlink $tmpfilename;
+ return 0;
+ }
+ unlink $tmpfilename;
+ if (!$session->chmod($keyfilename, '0644')) {
+ $self->log_error("Failed to chmod $keyfilename inside the chroot\n");
+ $session->unlink($keyfilename);
+ return 0;
+ }
+ }
+
+ }
+
+ # We have to do this early so that we can setup log filtering for the RESOLVERDIR
+ # We only set it up, if 'Build Dir' was set. It is not when the resolver
+ # is used by sbuild-createchroot, for example.
+ #Prepare a path to build a dummy package containing our deps:
+ if (! defined $self->get('Dummy package path') && defined $self->get('Build Dir')) {
+ my $tmpdir = $session->mktemp({ TEMPLATE => $self->get('Build Dir') . '/resolver-XXXXXX', DIRECTORY => 1});
+ if (!$tmpdir) {
+ $self->log_error("mktemp -d " . $self->get('Build Dir') . '/resolver-XXXXXX failed\n');
+ return 0;
+ }
+ $self->set('Dummy package path', $tmpdir);
+ }
+
+ return 1;
+}
+
+sub get_foreign_architectures {
+ my $self = shift;
+
+ my $session = $self->get('Session');
+
+ $session->run_command({ COMMAND => ['dpkg', '--assert-multi-arch'],
+ USER => 'root'});
+ if ($?)
+ {
+ $self->set('Multiarch Support', 0);
+ $self->log_error("dpkg does not support multi-arch\n");
+ return {};
+ }
+
+ my $foreignarchs = $session->read_command({ COMMAND => ['dpkg', '--print-foreign-architectures'], USER => 'root' });
+
+ if (!defined($foreignarchs)) {
+ $self->set('Multiarch Support', 0);
+ $self->log_error("dpkg does not support multi-arch\n");
+ return {};
+ }
+
+ if (!$foreignarchs)
+ {
+ debug("There are no foreign architectures configured\n");
+ return {};
+ }
+
+ my %set;
+ foreach my $arch (split /\s+/, $foreignarchs) {
+ chomp $arch;
+ next unless $arch;
+ $set{$arch} = 1;
+ }
+
+ return \%set;
+}
+
+sub add_foreign_architecture {
+
+ my $self = shift;
+ my $arch = shift;
+
+ # just skip if dpkg is to old for multiarch
+ if (! $self->get('Multiarch Support')) {
+ debug("not adding $arch because of no multiarch support\n");
+ return 1;
+ };
+
+ # if we already have this architecture, we're done
+ if ($self->get('Initial Foreign Arches')->{$arch}) {
+ debug("not adding $arch because it is an initial arch\n");
+ return 1;
+ }
+ if ($self->get('Added Foreign Arches')->{$arch}) {
+ debug("not adding $arch because it has already been aded");
+ return 1;
+ }
+
+ my $session = $self->get('Session');
+
+ # FIXME - allow for more than one foreign arch
+ $session->run_command(
+ # This is the Ubuntu dpkg 1.16.0~ubuntuN interface;
+ # we ought to check (or configure) which to use with
+ # check_dpkg_version:
+ # { COMMAND => ['sh', '-c', 'echo "foreign-architecture ' . $self->get('Host Arch') . '" > /etc/dpkg/dpkg.cfg.d/sbuild'],
+ # USER => 'root' });
+ # This is the Debian dpkg >= 1.16.2 interface:
+ { COMMAND => ['dpkg', '--add-architecture', $arch],
+ USER => 'root' });
+ if ($?)
+ {
+ $self->log_error("Failed to set dpkg foreign-architecture config\n");
+ return 0;
+ }
+ debug("Added foreign arch: $arch\n") if $arch;
+
+ $self->get('Added Foreign Arches')->{$arch} = 1;
+ return 1;
+}
+
+sub cleanup_foreign_architectures {
+ my $self = shift;
+
+ # just skip if dpkg is to old for multiarch
+ if (! $self->get('Multiarch Support')) { return 1 };
+
+ my $added_foreign_arches = $self->get('Added Foreign Arches');
+
+ my $session = $self->get('Session');
+
+ if (defined ($session->get('Session Purged')) && $session->get('Session Purged') == 1) {
+ debug("Not removing foreign architectures: cloned chroot in use\n");
+ return;
+ }
+
+ foreach my $arch (keys %{$added_foreign_arches}) {
+ $self->log("Removing foreign architecture $arch\n");
+ $session->run_command({ COMMAND => ['dpkg', '--remove-architecture', $arch],
+ USER => 'root',
+ DIR => '/'});
+ if ($?)
+ {
+ $self->log_error("Failed to remove dpkg foreign-architecture $arch\n");
+ return;
+ }
+ }
+}
+
+sub setup_dpkg {
+ my $self = shift;
+
+ my $session = $self->get('Session');
+
+ # Record initial foreign arch state so it can be restored
+ $self->set('Initial Foreign Arches', $self->get_foreign_architectures());
+
+ if ($self->get('Host Arch') ne $self->get('Build Arch')) {
+ $self->add_foreign_architecture($self->get('Host Arch'))
+ }
+}
+
+sub cleanup {
+ my $self = shift;
+
+ #cleanup dpkg cross-config
+ # rm /etc/dpkg/dpkg.cfg.d/sbuild
+ $self->cleanup_apt_archive();
+ $self->cleanup_foreign_architectures();
+}
+
+sub update {
+ my $self = shift;
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), 'update'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ return $?;
+}
+
+sub update_archive {
+ my $self = shift;
+
+ if (!$self->get_conf('APT_UPDATE_ARCHIVE_ONLY')) {
+ # Update with apt-get; causes complete archive update
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), 'update'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ } else {
+ my $session = $self->get('Session');
+ # Create an empty sources.list.d directory that we can set as
+ # Dir::Etc::sourceparts to suppress the real one. /dev/null
+ # works in recent versions of apt, but not older ones (we want
+ # 448eaf8 in apt 0.8.0 and af13d14 in apt 0.9.3). Since this
+ # runs against the target chroot's apt, be conservative.
+ my $dummy_sources_list_d = $self->get('Dummy package path') . '/sources.list.d';
+ if (!($session->test_directory($dummy_sources_list_d) || $session->mkdir($dummy_sources_list_d, { MODE => "00700"}))) {
+ $self->log_warning('Could not create build-depends dummy sources.list directory ' . $dummy_sources_list_d . ': ' . $!);
+ return 0;
+ }
+
+ # Run apt-get update pointed at our dummy archive list file, and
+ # the empty sources.list.d directory, so that we only update
+ # this one source. Since apt doesn't have all the sources
+ # available to it in this run, any caches it generates are
+ # invalid, so we then need to run gencaches with all sources
+ # available to it. (Note that the tempting optimization to run
+ # apt-get update -o pkgCacheFile::Generate=0 is broken before
+ # 872ed75 in apt 0.9.1.)
+ for my $list_file ($self->get('Dummy archive list file'),
+ $self->get('Extra packages archive list file'),
+ $self->get('Extra repositories archive list file')) {
+ if (!$session->test_regular_file_readable($list_file)) {
+ next;
+ }
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), 'update',
+ '-o', 'Dir::Etc::sourcelist=' . $list_file,
+ '-o', 'Dir::Etc::sourceparts=' . $dummy_sources_list_d,
+ '--no-list-cleanup'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ if ($? != 0) {
+ return 0;
+ }
+ }
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_CACHE'), 'gencaches'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ }
+
+ if ($? != 0) {
+ return 0;
+ }
+
+ return 1;
+}
+
+sub upgrade {
+ my $self = shift;
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), '-uy', '-o', 'Dpkg::Options::=--force-confold', 'upgrade'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ return $?;
+}
+
+sub distupgrade {
+ my $self = shift;
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), '-uy', '-o', 'Dpkg::Options::=--force-confold', 'dist-upgrade'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ return $?;
+}
+
+sub clean {
+ my $self = shift;
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), '-y', 'clean'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ return $?;
+}
+
+sub autoclean {
+ my $self = shift;
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), '-y', 'autoclean'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ return $?;
+}
+
+sub autoremove {
+ my $self = shift;
+
+ $self->run_apt_command(
+ { COMMAND => [$self->get_conf('APT_GET'), '-y', 'autoremove'],
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ DIR => '/' });
+ return $?;
+}
+
+sub add_dependencies {
+ my $self = shift;
+ my $pkg = shift;
+ my $build_depends = shift;
+ my $build_depends_arch = shift;
+ my $build_depends_indep = shift;
+ my $build_conflicts = shift;
+ my $build_conflicts_arch = shift;
+ my $build_conflicts_indep = shift;
+
+ debug("Build-Depends: $build_depends\n") if $build_depends;
+ debug("Build-Depends-Arch: $build_depends_arch\n") if $build_depends_arch;
+ debug("Build-Depends-Indep: $build_depends_indep\n") if $build_depends_indep;
+ debug("Build-Conflicts: $build_conflicts\n") if $build_conflicts;
+ debug("Build-Conflicts-Arch: $build_conflicts_arch\n") if $build_conflicts_arch;
+ debug("Build-Conflicts-Indep: $build_conflicts_indep\n") if $build_conflicts_indep;
+
+ my $deps = {
+ 'Build Depends' => $build_depends,
+ 'Build Depends Arch' => $build_depends_arch,
+ 'Build Depends Indep' => $build_depends_indep,
+ 'Build Conflicts' => $build_conflicts,
+ 'Build Conflicts Arch' => $build_conflicts_arch,
+ 'Build Conflicts Indep' => $build_conflicts_indep
+ };
+
+ $self->get('AptDependencies')->{$pkg} = $deps;
+}
+
+sub uninstall_deps {
+ my $self = shift;
+
+ my( @pkgs, @instd, @rmvd );
+
+ @pkgs = keys %{$self->get('Changes')->{'removed'}};
+ debug("Reinstalling removed packages: @pkgs\n");
+ $self->log("Failed to reinstall removed packages!\n")
+ if !$self->run_apt("-y", \@instd, \@rmvd, 'install', @pkgs);
+ debug("Installed were: @instd\n");
+ debug("Removed were: @rmvd\n");
+ $self->unset_removed(@instd);
+ $self->unset_installed(@rmvd);
+
+ @pkgs = keys %{$self->get('Changes')->{'installed'}};
+ debug("Removing installed packages: @pkgs\n");
+ $self->log("Failed to remove installed packages!\n")
+ if !$self->run_apt("-y", \@instd, \@rmvd, 'remove', @pkgs);
+ $self->unset_removed(@instd);
+ $self->unset_installed(@rmvd);
+}
+
+sub set_installed {
+ my $self = shift;
+
+ foreach (@_) {
+ $self->get('Changes')->{'installed'}->{$_} = 1;
+ }
+ debug("Added to installed list: @_\n");
+}
+
+sub set_removed {
+ my $self = shift;
+ foreach (@_) {
+ $self->get('Changes')->{'removed'}->{$_} = 1;
+ if (exists $self->get('Changes')->{'installed'}->{$_}) {
+ delete $self->get('Changes')->{'installed'}->{$_};
+ $self->get('Changes')->{'auto-removed'}->{$_} = 1;
+ debug("Note: $_ was installed\n");
+ }
+ }
+ debug("Added to removed list: @_\n");
+}
+
+sub unset_installed {
+ my $self = shift;
+ foreach (@_) {
+ delete $self->get('Changes')->{'installed'}->{$_};
+ }
+ debug("Removed from installed list: @_\n");
+}
+
+sub unset_removed {
+ my $self = shift;
+ foreach (@_) {
+ delete $self->get('Changes')->{'removed'}->{$_};
+ if (exists $self->get('Changes')->{'auto-removed'}->{$_}) {
+ delete $self->get('Changes')->{'auto-removed'}->{$_};
+ $self->get('Changes')->{'installed'}->{$_} = 1;
+ debug("Note: revived $_ to installed list\n");
+ }
+ }
+ debug("Removed from removed list: @_\n");
+}
+
+sub dump_build_environment {
+ my $self = shift;
+
+ my $status = $self->get_dpkg_status();
+
+ my $arch = $self->get('Arch');
+ my ($sysname, $nodename, $release, $version, $machine) = POSIX::uname();
+ $self->log_subsection("Build environment");
+ $self->log("Kernel: $sysname $release $version $arch ($machine)\n");
+
+ $self->log("Toolchain package versions:");
+ foreach my $name (sort keys %{$status}) {
+ foreach my $regex (@{$self->get_conf('TOOLCHAIN_REGEX')}) {
+ if ($name =~ m,^$regex, && defined($status->{$name}->{'Version'})) {
+ $self->log(' ' . $name . '_' . $status->{$name}->{'Version'});
+ }
+ }
+ }
+ $self->log("\n");
+
+ $self->log("Package versions:");
+ foreach my $name (sort keys %{$status}) {
+ if (defined($status->{$name}->{'Version'})) {
+ $self->log(' ' . $name . '_' . $status->{$name}->{'Version'});
+ }
+ }
+ $self->log("\n");
+
+ return $status->{'dpkg-dev'}->{'Version'};
+}
+
+sub run_apt {
+ my $self = shift;
+ my $mode = shift;
+ my $inst_ret = shift;
+ my $rem_ret = shift;
+ my $action = shift;
+ my @packages = @_;
+ my( $msgs, $status, $pkgs, $rpkgs );
+
+ $msgs = "";
+ # redirection of stdin from /dev/null so that conffile question
+ # are treated as if RETURN was pressed.
+ # dpkg since 1.4.1.18 issues an error on the conffile question if
+ # it reads EOF -- hardwire the new --force-confold option to avoid
+ # the questions.
+ my @apt_command = ($self->get_conf('APT_GET'), '--purge',
+ '-o', 'DPkg::Options::=--force-confold',
+ '-o', 'DPkg::Options::=--refuse-remove-essential',
+ '-o', 'APT::Install-Recommends=false',
+ '-o', 'Dpkg::Use-Pty=false',
+ '-q');
+ push @apt_command, '--allow-unauthenticated' if
+ ($self->get_conf('APT_ALLOW_UNAUTHENTICATED'));
+ if ( $self->get('Host Arch') ne $self->get('Build Arch') ) {
+ # drop m-a:foreign and essential:yes packages that are not arch:all
+ # and not arch:native
+ push @apt_command, '--solver', 'sbuild-cross-resolver';
+ }
+ push @apt_command, "$mode", $action, @packages;
+ my $pipe =
+ $self->pipe_apt_command(
+ { COMMAND => \@apt_command,
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+ if (!$pipe) {
+ $self->log("Can't open pipe to apt-get: $!\n");
+ return 0;
+ }
+
+ while(<$pipe>) {
+ $msgs .= $_;
+ $self->log($_) if $mode ne "-s" || debug($_);
+ }
+ close($pipe);
+ $status = $?;
+
+ $pkgs = $rpkgs = "";
+ if ($msgs =~ /NEW packages will be installed:\n((^[ ].*\n)*)/mi) {
+ ($pkgs = $1) =~ s/^[ ]*((.|\n)*)\s*$/$1/m;
+ $pkgs =~ s/\*//g;
+ }
+ if ($msgs =~ /packages will be REMOVED:\n((^[ ].*\n)*)/mi) {
+ ($rpkgs = $1) =~ s/^[ ]*((.|\n)*)\s*$/$1/m;
+ $rpkgs =~ s/\*//g;
+ }
+ @$inst_ret = split( /\s+/, $pkgs );
+ @$rem_ret = split( /\s+/, $rpkgs );
+
+ $self->log("apt-get failed.\n") if $status && $mode ne "-s";
+ return $mode eq "-s" || $status == 0;
+}
+
+sub run_xapt {
+ my $self = shift;
+ my $mode = shift;
+ my $inst_ret = shift;
+ my $rem_ret = shift;
+ my $action = shift;
+ my @packages = @_;
+ my( $msgs, $status, $pkgs, $rpkgs );
+
+ $msgs = "";
+ # redirection of stdin from /dev/null so that conffile question
+ # are treated as if RETURN was pressed.
+ # dpkg since 1.4.1.18 issues an error on the conffile question if
+ # it reads EOF -- hardwire the new --force-confold option to avoid
+ # the questions.
+ my @xapt_command = ($self->get_conf('XAPT'));
+ my $pipe =
+ $self->pipe_xapt_command(
+ { COMMAND => \@xapt_command,
+ ENV => {'DEBIAN_FRONTEND' => 'noninteractive'},
+ USER => 'root',
+ PRIORITY => 0,
+ DIR => '/' });
+ if (!$pipe) {
+ $self->log("Can't open pipe to xapt: $!\n");
+ return 0;
+ }
+
+ while(<$pipe>) {
+ $msgs .= $_;
+ $self->log($_) if $mode ne "-s" || debug($_);
+ }
+ close($pipe);
+ $status = $?;
+
+ $pkgs = $rpkgs = "";
+ if ($msgs =~ /NEW packages will be installed:\n((^[ ].*\n)*)/mi) {
+ ($pkgs = $1) =~ s/^[ ]*((.|\n)*)\s*$/$1/m;
+ $pkgs =~ s/\*//g;
+ }
+ if ($msgs =~ /packages will be REMOVED:\n((^[ ].*\n)*)/mi) {
+ ($rpkgs = $1) =~ s/^[ ]*((.|\n)*)\s*$/$1/m;
+ $rpkgs =~ s/\*//g;
+ }
+ @$inst_ret = split( /\s+/, $pkgs );
+ @$rem_ret = split( /\s+/, $rpkgs );
+
+ $self->log("xapt failed.\n") if $status && $mode ne "-s";
+ return $mode eq "-s" || $status == 0;
+}
+
+sub format_deps {
+ my $self = shift;
+
+ return join( ", ",
+ map { join( "|",
+ map { ($_->{'Neg'} ? "!" : "") .
+ $_->{'Package'} .
+ ($_->{'Rel'} ? " ($_->{'Rel'} $_->{'Version'})":"")}
+ scalar($_), @{$_->{'Alternatives'}}) } @_ );
+}
+
+sub get_dpkg_status {
+ my $self = shift;
+ my @interest = @_;
+ my %result;
+
+ debug("Requesting dpkg status for packages: @interest\n");
+ my $STATUS = $self->get('Session')->get_read_file_handle('/var/lib/dpkg/status');
+ if (!$STATUS) {
+ $self->log("Can't open /var/lib/dpkg/status inside chroot: $!\n");
+ return ();
+ }
+ local( $/ ) = "";
+ while( <$STATUS> ) {
+ my( $pkg, $status, $version, $provides );
+ /^Package:\s*(.*)\s*$/mi and $pkg = $1;
+ /^Status:\s*(.*)\s*$/mi and $status = $1;
+ /^Version:\s*(.*)\s*$/mi and $version = $1;
+ /^Provides:\s*(.*)\s*$/mi and $provides = $1;
+ if (!$pkg) {
+ $self->log_error("parse error in /var/lib/dpkg/status: no Package: field\n");
+ next;
+ }
+ if (defined($version)) {
+ debug("$pkg ($version) status: $status\n") if $self->get_conf('DEBUG') >= 2;
+ } else {
+ debug("$pkg status: $status\n") if $self->get_conf('DEBUG') >= 2;
+ }
+ if (!$status) {
+ $self->log_error("parse error in /var/lib/dpkg/status: no Status: field for package $pkg\n");
+ next;
+ }
+ if ($status !~ /\sinstalled$/) {
+ $result{$pkg}->{'Installed'} = 0
+ if !(exists($result{$pkg}) &&
+ $result{$pkg}->{'Version'} eq '~*=PROVIDED=*=');
+ next;
+ }
+ if (!defined $version || $version eq "") {
+ $self->log_error("parse error in /var/lib/dpkg/status: no Version: field for package $pkg\n");
+ next;
+ }
+ $result{$pkg} = { Installed => 1, Version => $version }
+ if (isin( $pkg, @interest ) || !@interest);
+ if ($provides) {
+ foreach (split( /\s*,\s*/, $provides )) {
+ $result{$_} = { Installed => 1, Version => '~*=PROVIDED=*=' }
+ if isin( $_, @interest ) and (not exists($result{$_}) or
+ ($result{$_}->{'Installed'} == 0));
+ }
+ }
+ }
+ close( $STATUS );
+ return \%result;
+}
+
+# Create an apt archive. Add to it if one exists.
+sub setup_apt_archive {
+ my $self = shift;
+ my $dummy_pkg_name = shift;
+ my @pkgs = @_;
+
+ my $session = $self->get('Session');
+
+
+ if (!$session->chown($self->get('Dummy package path'), $self->get_conf('BUILD_USER'), 'sbuild')) {
+ $self->log_error("Failed to set " . $self->get_conf('BUILD_USER') .
+ ":sbuild ownership on dummy package dir\n");
+ return 0;
+ }
+ if (!$session->chmod($self->get('Dummy package path'), '0770')) {
+ $self->log_error("Failed to set 0770 permissions on dummy package dir\n");
+ return 0;
+ }
+ my $dummy_dir = $self->get('Dummy package path');
+ my $dummy_gpghome = $dummy_dir . '/gpg';
+ my $dummy_archive_dir = $dummy_dir . '/apt_archive';
+ my $dummy_release_file = $dummy_archive_dir . '/Release';
+ my $dummy_archive_seckey = $dummy_archive_dir . '/sbuild-key.sec';
+ my $dummy_archive_pubkey = $dummy_archive_dir . '/sbuild-key.pub';
+
+ $self->set('Dummy archive directory', $dummy_archive_dir);
+ $self->set('Dummy Release file', $dummy_release_file);
+ my $dummy_archive_list_file = $self->get('Dummy archive list file');
+
+ if (!$session->test_directory($dummy_dir)) {
+ $self->log_warning('Could not create build-depends dummy dir ' . $dummy_dir . ': ' . $!);
+ return 0;
+ }
+ if (!($session->test_directory($dummy_gpghome) || $session->mkdir($dummy_gpghome, { MODE => "00700"}))) {
+ $self->log_warning('Could not create build-depends dummy gpg home dir ' . $dummy_gpghome . ': ' . $!);
+ return 0;
+ }
+ if (!$session->chown($dummy_gpghome, $self->get_conf('BUILD_USER'), 'sbuild')) {
+ $self->log_error('Failed to set ' . $self->get_conf('BUILD_USER') .
+ ':sbuild ownership on $dummy_gpghome\n');
+ return 0;
+ }
+ if (!($session->test_directory($dummy_archive_dir) || $session->mkdir($dummy_archive_dir, { MODE => "00775"}))) {
+ $self->log_warning('Could not create build-depends dummy archive dir ' . $dummy_archive_dir . ': ' . $!);
+ return 0;
+ }
+
+ my $dummy_pkg_dir = $dummy_dir . '/' . $dummy_pkg_name;
+ my $dummy_deb = $dummy_archive_dir . '/' . $dummy_pkg_name . '.deb';
+ my $dummy_dsc = $dummy_archive_dir . '/' . $dummy_pkg_name . '.dsc';
+
+ if (!($session->mkdir("$dummy_pkg_dir", { MODE => "00775"}))) {
+ $self->log_warning('Could not create build-depends dummy dir ' . $dummy_pkg_dir . $!);
+ return 0;
+ }
+
+ if (!($session->mkdir("$dummy_pkg_dir/DEBIAN", { MODE => "00775"}))) {
+ $self->log_warning('Could not create build-depends dummy dir ' . $dummy_pkg_dir . '/DEBIAN: ' . $!);
+ return 0;
+ }
+
+ my $DUMMY_CONTROL = $session->get_write_file_handle("$dummy_pkg_dir/DEBIAN/control");
+ if (!$DUMMY_CONTROL) {
+ $self->log_warning('Could not open ' . $dummy_pkg_dir . '/DEBIAN/control for writing: ' . $!);
+ return 0;
+ }
+
+ my $arch = $self->get('Host Arch');
+ print $DUMMY_CONTROL <<"EOF";
+Package: $dummy_pkg_name
+Version: 0.invalid.0
+Architecture: $arch
+EOF
+
+ my @positive;
+ my @negative;
+ my @positive_arch;
+ my @negative_arch;
+ my @positive_indep;
+ my @negative_indep;
+
+ for my $pkg (@pkgs) {
+ my $deps = $self->get('AptDependencies')->{$pkg};
+
+ push(@positive, $deps->{'Build Depends'})
+ if (defined($deps->{'Build Depends'}) &&
+ $deps->{'Build Depends'} ne "");
+ push(@negative, $deps->{'Build Conflicts'})
+ if (defined($deps->{'Build Conflicts'}) &&
+ $deps->{'Build Conflicts'} ne "");
+ if ($self->get_conf('BUILD_ARCH_ANY')) {
+ push(@positive_arch, $deps->{'Build Depends Arch'})
+ if (defined($deps->{'Build Depends Arch'}) &&
+ $deps->{'Build Depends Arch'} ne "");
+ push(@negative_arch, $deps->{'Build Conflicts Arch'})
+ if (defined($deps->{'Build Conflicts Arch'}) &&
+ $deps->{'Build Conflicts Arch'} ne "");
+ }
+ if ($self->get_conf('BUILD_ARCH_ALL')) {
+ push(@positive_indep, $deps->{'Build Depends Indep'})
+ if (defined($deps->{'Build Depends Indep'}) &&
+ $deps->{'Build Depends Indep'} ne "");
+ push(@negative_indep, $deps->{'Build Conflicts Indep'})
+ if (defined($deps->{'Build Conflicts Indep'}) &&
+ $deps->{'Build Conflicts Indep'} ne "");
+ }
+ }
+
+ my $positive_build_deps = join(", ", @positive,
+ @positive_arch, @positive_indep);
+ my $positive = deps_parse($positive_build_deps,
+ reduce_arch => 1,
+ host_arch => $self->get('Host Arch'),
+ build_arch => $self->get('Build Arch'),
+ build_dep => 1,
+ reduce_profiles => 1,
+ build_profiles => [ split / /, $self->get('Build Profiles') ]);
+ if( !defined $positive ) {
+ my $msg = "Error! deps_parse() couldn't parse the positive Build-Depends '$positive_build_deps'";
+ $self->log_error("$msg\n");
+ return 0;
+ }
+
+ my $negative_build_deps = join(", ", @negative,
+ @negative_arch, @negative_indep);
+ my $negative = deps_parse($negative_build_deps,
+ reduce_arch => 1,
+ host_arch => $self->get('Host Arch'),
+ build_arch => $self->get('Build Arch'),
+ build_dep => 1,
+ union => 1,
+ reduce_profiles => 1,
+ build_profiles => [ split / /, $self->get('Build Profiles') ]);
+ if( !defined $negative ) {
+ my $msg = "Error! deps_parse() couldn't parse the negative Build-Depends '$negative_build_deps'";
+ $self->log_error("$msg\n");
+ return 0;
+ }
+
+
+ # sbuild turns build dependencies into the dependencies of a dummy binary
+ # package. Since binary package dependencies do not support :native the
+ # architecture qualifier, these have to either be removed during native
+ # compilation or replaced by the build (native) architecture during cross
+ # building
+ my $handle_native_archqual = sub {
+ my ($dep) = @_;
+ if ($dep->{archqual} && $dep->{archqual} eq "native") {
+ if ($self->get('Host Arch') eq $self->get('Build Arch')) {
+ $dep->{archqual} = undef;
+ } else {
+ $dep->{archqual} = $self->get('Build Arch');
+ }
+ }
+ return 1;
+ };
+ deps_iterate($positive, $handle_native_archqual);
+ deps_iterate($negative, $handle_native_archqual);
+
+ $self->log("Merged Build-Depends: $positive\n") if $positive;
+ $self->log("Merged Build-Conflicts: $negative\n") if $negative;
+
+ # Filter out all but the first alternative except in special
+ # cases.
+ if (!$self->get_conf('RESOLVE_ALTERNATIVES')) {
+ my $positive_filtered = Dpkg::Deps::AND->new();
+ foreach my $item ($positive->get_deps()) {
+ my $alt_filtered = Dpkg::Deps::OR->new();
+ my @alternatives = $item->get_deps();
+ my $first = shift @alternatives;
+ $alt_filtered->add($first) if defined $first;
+ # Allow foo (rel x) | foo (rel y) as the only acceptable
+ # form of alternative. i.e. where the package is the
+ # same, but different relations are needed, since these
+ # are effectively a single logical dependency.
+ foreach my $alt (@alternatives) {
+ if ($first->{'package'} eq $alt->{'package'}) {
+ $alt_filtered->add($alt);
+ } else {
+ last;
+ }
+ }
+ $positive_filtered->add($alt_filtered);
+ }
+ $positive = $positive_filtered;
+ }
+
+ if ($positive ne "") {
+ print $DUMMY_CONTROL 'Depends: ' . $positive . "\n";
+ }
+ if ($negative ne "") {
+ print $DUMMY_CONTROL 'Conflicts: ' . $negative . "\n";
+ }
+
+ $self->log("Filtered Build-Depends: $positive\n") if $positive;
+ $self->log("Filtered Build-Conflicts: $negative\n") if $negative;
+
+ print $DUMMY_CONTROL <<"EOF";
+Maintainer: Debian buildd-tools Developers <buildd-tools-devel\@lists.alioth.debian.org>
+Description: Dummy package to satisfy dependencies with apt - created by sbuild
+ This package was created automatically by sbuild and should never appear on
+ a real system. You can safely remove it.
+EOF
+ close ($DUMMY_CONTROL);
+
+ foreach my $path ($dummy_pkg_dir . '/DEBIAN/control',
+ $dummy_pkg_dir . '/DEBIAN',
+ $dummy_pkg_dir,
+ $dummy_archive_dir) {
+ if (!$session->chown($path, $self->get_conf('BUILD_USER'), 'sbuild')) {
+ $self->log_error("Failed to set " . $self->get_conf('BUILD_USER')
+ . ":sbuild ownership on $path\n");
+ return 0;
+ }
+ }
+
+ # Now build the package:
+ # NO_PKG_MANGLE=1 disables https://launchpad.net/pkgbinarymangler (only used on Ubuntu)
+ $session->run_command(
+ { COMMAND => ['env', 'NO_PKG_MANGLE=1', 'dpkg-deb', '--build', $dummy_pkg_dir, $dummy_deb],
+ USER => $self->get_conf('BUILD_USER'),
+ PRIORITY => 0});
+ if ($?) {
+ $self->log("Dummy package creation failed\n");
+ return 0;
+ }
+
+ # Write the dummy dsc file.
+ my $dummy_dsc_fh = $session->get_write_file_handle($dummy_dsc);
+ if (!$dummy_dsc_fh) {
+ $self->log_warning('Could not open ' . $dummy_dsc . ' for writing: ' . $!);
+ return 0;
+ }
+
+ print $dummy_dsc_fh <<"EOF";
+Format: 1.0
+Source: $dummy_pkg_name
+Binary: $dummy_pkg_name
+Architecture: any
+Version: 0.invalid.0
+Maintainer: Debian buildd-tools Developers <buildd-tools-devel\@lists.alioth.debian.org>
+EOF
+ if (scalar(@positive)) {
+ print $dummy_dsc_fh 'Build-Depends: ' . join(", ", @positive) . "\n";
+ }
+ if (scalar(@negative)) {
+ print $dummy_dsc_fh 'Build-Conflicts: ' . join(", ", @negative) . "\n";
+ }
+ if (scalar(@positive_arch)) {
+ print $dummy_dsc_fh 'Build-Depends-Arch: ' . join(", ", @positive_arch) . "\n";
+ }
+ if (scalar(@negative_arch)) {
+ print $dummy_dsc_fh 'Build-Conflicts-Arch: ' . join(", ", @negative_arch) . "\n";
+ }
+ if (scalar(@positive_indep)) {
+ print $dummy_dsc_fh 'Build-Depends-Indep: ' . join(", ", @positive_indep) . "\n";
+ }
+ if (scalar(@negative_indep)) {
+ print $dummy_dsc_fh 'Build-Conflicts-Indep: ' . join(", ", @negative_indep) . "\n";
+ }
+ print $dummy_dsc_fh "\n";
+ close $dummy_dsc_fh;
+
+ # Do code to run apt-ftparchive
+ if (!$self->run_apt_ftparchive($self->get('Dummy archive directory'))) {
+ $self->log("Failed to run apt-ftparchive.\n");
+ return 0;
+ }
+
+ # Write a list file for the dummy archive if one not create yet.
+ if (!$session->test_regular_file($dummy_archive_list_file)) {
+ my $tmpfilename = $session->mktemp();
+
+ if (!$tmpfilename) {
+ $self->log_error("Can't create tempfile\n");
+ return 0;
+ }
+
+ my $tmpfh = $session->get_write_file_handle($tmpfilename);
+ if (!$tmpfh) {
+ $self->log_error("Cannot open pipe: $!\n");
+ return 0;
+ }
+
+ # We always trust the dummy apt repositories by setting trusted=yes.
+ #
+ # We use copy:// instead of file:// as URI because the latter will make
+ # apt use symlinks in /var/lib/apt/lists. These symlinks will become
+ # broken after the dummy archive is removed. This in turn confuses
+ # launchpad-buildd which directly tries to access
+ # /var/lib/apt/lists/*_Packages and cannot use `apt-get indextargets` as
+ # that apt feature is too new for it.
+ print $tmpfh 'deb [trusted=yes] copy://' . $dummy_archive_dir . " ./\n";
+ print $tmpfh 'deb-src [trusted=yes] copy://' . $dummy_archive_dir . " ./\n";
+
+ close($tmpfh);
+ # List file needs to be moved with root.
+ if (!$session->chmod($tmpfilename, '0644')) {
+ $self->log("Failed to create apt list file for dummy archive.\n");
+ $session->unlink($tmpfilename);
+ return 0;
+ }
+ if (!$session->rename($tmpfilename, $dummy_archive_list_file)) {
+ $self->log("Failed to create apt list file for dummy archive.\n");
+ $session->unlink($tmpfilename);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+# Remove the apt archive.
+sub cleanup_apt_archive {
+ my $self = shift;
+
+ my $session = $self->get('Session');
+
+ if (defined $self->get('Dummy package path')) {
+ $session->unlink($self->get('Dummy package path'), { RECURSIVE => 1, FORCE => 1 });
+ }
+
+ if (defined $self->get('Extra packages path')) {
+ $session->unlink($self->get('Extra packages path'), { RECURSIVE => 1, FORCE => 1 });
+ }
+
+ $session->unlink($self->get('Dummy archive list file'), { FORCE => 1 });
+
+ $session->unlink($self->get('Extra repositories archive list file'), { FORCE => 1 });
+
+ $session->unlink($self->get('Extra packages archive list file'), { FORCE => 1 });
+
+ $self->set('Extra packages path', undef);
+ $self->set('Extra packages archive directory', undef);
+ $self->set('Extra packages release file', undef);
+ $self->set('Dummy archive directory', undef);
+ $self->set('Dummy Release file', undef);
+}
+
+# Function that runs apt-ftparchive
+sub run_apt_ftparchive {
+ my $self = shift;
+ my $dummy_archive_dir = shift;
+
+ my $session = $self->get('Session');
+
+ # We create the Packages, Sources and Release file inside the chroot.
+ # We cannot use Digest::MD5, or Digest::SHA because
+ # they are not available inside a chroot with only Essential:yes and apt
+ # installed.
+ # We cannot use apt-ftparchive as this is not available inside the chroot.
+ # Apt-ftparchive outside the chroot might not have access to the files
+ # inside the chroot (for example when using qemu or ssh backends).
+ # The only alternative would've been to set up the archive outside the
+ # chroot using apt-ftparchive and to then copy Packages, Sources and
+ # Release into the chroot.
+ # We do not do this to avoid copying files from and to the chroot.
+ # At the same time doing it like this has the advantage to have less
+ # dependencies of sbuild itself (no apt-ftparchive needed).
+ # The disadvantage of doing it this way is that we now have to maintain
+ # our own code creating the Release file which might break in the future.
+ my $packagessourcescmd = <<'SCRIPTEND';
+use strict;
+use warnings;
+
+use POSIX qw(strftime);
+use POSIX qw(locale_h);
+
+# Execute a command without /bin/sh but plain execvp while redirecting its
+# standard output to a file given as the first argument.
+# Using "print $fh `my_command`" has the disadvantage that "my_command" might
+# be executed through /bin/sh (depending on the characters used) or that the
+# output of "my_command" is very long.
+
+sub hash_file($$)
+{
+ my ($filename, $util) = @_;
+ my $output = `$util $filename`;
+ my ($hash, undef) = split /\s+/, $output;
+ return $hash;
+}
+
+{
+ opendir(my $dh, '.') or die "Can't opendir('.'): $!";
+ open my $out, '>', 'Packages';
+ while (my $entry = readdir $dh) {
+ next if $entry !~ /\.deb$/;
+ open my $in, '-|', 'dpkg-deb', '-I', $entry, 'control' or die "cannot fork dpkg-deb";
+ while (my $line = <$in>) {
+ print $out $line;
+ }
+ close $in;
+ my $size = -s $entry;
+ my $md5 = hash_file($entry, 'md5sum');
+ my $sha1 = hash_file($entry, 'sha1sum');
+ my $sha256 = hash_file($entry, 'sha256sum');
+ print $out "Size: $size\n";
+ print $out "MD5sum: $md5\n";
+ print $out "SHA1: $sha1\n";
+ print $out "SHA256: $sha256\n";
+ print $out "Filename: ./$entry\n";
+ print $out "\n";
+ }
+ close $out;
+ closedir($dh);
+}
+{
+ opendir(my $dh, '.') or die "Can't opendir('.'): $!";
+ open my $out, '>', 'Sources';
+ while (my $entry = readdir $dh) {
+ next if $entry !~ /\.dsc$/;
+ my $size = -s $entry;
+ my $md5 = hash_file($entry, 'md5sum');
+ my $sha1 = hash_file($entry, 'sha1sum');
+ my $sha256 = hash_file($entry, 'sha256sum');
+ my ($sha1_printed, $sha256_printed, $files_printed) = (0, 0, 0);
+ open my $in, '<', $entry or die "cannot open $entry";
+ while (my $line = <$in>) {
+ next if $line eq "\n";
+ $line =~ s/^Source:/Package:/;
+ print $out $line;
+ if ($line eq "Checksums-Sha1:\n") {
+ print $out " $sha1 $size $entry\n";
+ $sha1_printed = 1;
+ } elsif ($line eq "Checksums-Sha256:\n") {
+ print $out " $sha256 $size $entry\n";
+ $sha256_printed = 1;
+ } elsif ($line eq "Files:\n") {
+ print $out " $md5 $size $entry\n";
+ $files_printed = 1;
+ }
+ }
+ close $in;
+ if ($sha1_printed == 0) {
+ print $out "Checksums-Sha1:\n";
+ print $out " $sha1 $size $entry\n";
+ }
+ if ($sha256_printed == 0) {
+ print $out "Checksums-Sha256:\n";
+ print $out " $sha256 $size $entry\n";
+ }
+ if ($files_printed == 0) {
+ print $out "Files:\n";
+ print $out " $md5 $size $entry\n";
+ }
+ print $out "Directory: .\n";
+ print $out "\n";
+ }
+ close $out;
+ closedir($dh);
+}
+
+my $packages_md5 = hash_file('Packages', 'md5sum');
+my $sources_md5 = hash_file('Sources', 'md5sum');
+
+my $packages_sha1 = hash_file('Packages', 'sha1sum');
+my $sources_sha1 = hash_file('Sources', 'sha1sum');
+
+my $packages_sha256 = hash_file('Packages', 'sha256sum');
+my $sources_sha256 = hash_file('Sources', 'sha256sum');
+
+my $packages_size = -s 'Packages';
+my $sources_size = -s 'Sources';
+
+# The timestamp format of release files is documented here:
+# https://wiki.debian.org/RepositoryFormat#Date.2CValid-Until
+# It is specified to be the same format as described in Debian Policy §4.4
+# https://www.debian.org/doc/debian-policy/ch-source.html#s-dpkgchangelog
+# or the same as in debian/changelog or the Date field in .changes files.
+# or the same format as `date -R`
+# To adhere to the specified format, the C or C.UTF-8 locale must be used.
+my $old_locale = setlocale(LC_TIME);
+setlocale(LC_TIME, "C.UTF-8");
+my $datestring = strftime "%a, %d %b %Y %H:%M:%S +0000", gmtime();
+setlocale(LC_TIME, $old_locale);
+
+open(my $releasefh, '>', 'Release') or die "cannot open Release for writing: $!";
+
+print $releasefh <<"END";
+Codename: invalid-sbuild-codename
+Date: $datestring
+Description: Sbuild Build Dependency Temporary Archive
+Label: sbuild-build-depends-archive
+Origin: sbuild-build-depends-archive
+Suite: invalid-sbuild-suite
+MD5Sum:
+ $packages_md5 $packages_size Packages
+ $sources_md5 $sources_size Sources
+SHA1:
+ $packages_sha1 $packages_size Packages
+ $sources_sha1 $sources_size Sources
+SHA256:
+ $packages_sha256 $packages_size Packages
+ $sources_sha256 $sources_size Sources
+END
+
+close $releasefh;
+
+SCRIPTEND
+
+ # Instead of using $(perl -e) and passing $packagessourcescmd as a command
+ # line argument, feed perl from standard input because otherwise the
+ # command line will be too long for certain backends (like the autopkgtest
+ # qemu backend).
+ my $pipe = $session->pipe_command(
+ { COMMAND => ['perl'],
+ USER => "root",
+ DIR => $dummy_archive_dir,
+ PIPE => 'out',
+ });
+ if (!$pipe) {
+ $self->log_error("cannot open pipe\n");
+ return 0;
+ }
+ print $pipe $packagessourcescmd;
+ close $pipe;
+ if ($? ne 0) {
+ $self->log_error("cannot create dummy archive\n");
+ return 0;
+ }
+
+ return 1;
+}
+
+sub get_apt_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ my $command = $options->{'COMMAND'};
+ my $apt_options = $self->get('APT Options');
+
+ debug2("APT Options: ", join(" ", @$apt_options), "\n")
+ if defined($apt_options);
+
+ my @aptcommand = ();
+ if (defined($apt_options)) {
+ push(@aptcommand, @{$command}[0]);
+ push(@aptcommand, @$apt_options);
+ if ($#$command > 0) {
+ push(@aptcommand, @{$command}[1 .. $#$command]);
+ }
+ } else {
+ @aptcommand = @$command;
+ }
+
+ debug2("APT Command: ", join(" ", @aptcommand), "\n");
+
+ $options->{'INTCOMMAND'} = \@aptcommand;
+}
+
+sub run_apt_command {
+ my $self = shift;
+ my $options = shift;
+
+ my $session = $self->get('Session');
+ my $host = $self->get('Host');
+
+ # Set modfied command
+ $self->get_apt_command_internal($options);
+
+ if ($self->get('Split')) {
+ return $host->run_command_internal($options);
+ } else {
+ return $session->run_command_internal($options);
+ }
+}
+
+sub pipe_apt_command {
+ my $self = shift;
+ my $options = shift;
+
+ my $session = $self->get('Session');
+ my $host = $self->get('Host');
+
+ # Set modfied command
+ $self->get_apt_command_internal($options);
+
+ if ($self->get('Split')) {
+ return $host->pipe_command_internal($options);
+ } else {
+ return $session->pipe_command_internal($options);
+ }
+}
+
+sub pipe_xapt_command {
+ my $self = shift;
+ my $options = shift;
+
+ my $session = $self->get('Session');
+ my $host = $self->get('Host');
+
+ # Set modfied command
+ $self->get_apt_command_internal($options);
+
+ if ($self->get('Split')) {
+ return $host->pipe_command_internal($options);
+ } else {
+ return $session->pipe_command_internal($options);
+ }
+}
+
+sub get_aptitude_command_internal {
+ my $self = shift;
+ my $options = shift;
+
+ my $command = $options->{'COMMAND'};
+ my $apt_options = $self->get('Aptitude Options');
+
+ debug2("Aptitude Options: ", join(" ", @$apt_options), "\n")
+ if defined($apt_options);
+
+ my @aptcommand = ();
+ if (defined($apt_options)) {
+ push(@aptcommand, @{$command}[0]);
+ push(@aptcommand, @$apt_options);
+ if ($#$command > 0) {
+ push(@aptcommand, @{$command}[1 .. $#$command]);
+ }
+ } else {
+ @aptcommand = @$command;
+ }
+
+ debug2("APT Command: ", join(" ", @aptcommand), "\n");
+
+ $options->{'INTCOMMAND'} = \@aptcommand;
+}
+
+sub run_aptitude_command {
+ my $self = shift;
+ my $options = shift;
+
+ my $session = $self->get('Session');
+ my $host = $self->get('Host');
+
+ # Set modfied command
+ $self->get_aptitude_command_internal($options);
+
+ if ($self->get('Split')) {
+ return $host->run_command_internal($options);
+ } else {
+ return $session->run_command_internal($options);
+ }
+}
+
+sub pipe_aptitude_command {
+ my $self = shift;
+ my $options = shift;
+
+ my $session = $self->get('Session');
+ my $host = $self->get('Host');
+
+ # Set modfied command
+ $self->get_aptitude_command_internal($options);
+
+ if ($self->get('Split')) {
+ return $host->pipe_command_internal($options);
+ } else {
+ return $session->pipe_command_internal($options);
+ }
+}
+
+sub get_sbuild_dummy_pkg_name {
+ my $self = shift;
+ my $name = shift;
+
+ return 'sbuild-build-depends-' . $name. '-dummy';
+}
+
+1;
diff --git a/lib/Sbuild/Sysconfig.pm.in b/lib/Sbuild/Sysconfig.pm.in
new file mode 100644
index 0000000..8f5cbb5
--- /dev/null
+++ b/lib/Sbuild/Sysconfig.pm.in
@@ -0,0 +1,84 @@
+#
+# Sysconfig.pm: system configuration for sbuild
+# Copyright © 2007-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/>.
+#
+#######################################################################
+
+package Sbuild::Sysconfig;
+
+use strict;
+use warnings;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT_OK);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT_OK = qw($version $release_date $compat_mode %paths %programs);
+}
+
+our $version = "@PACKAGE_VERSION@";
+our $release_date = "@RELEASE_DATE@";
+our $compat_mode = @SBUILD_COMPAT@;
+
+# Paths
+my $prefix = "@prefix@";
+my $exec_prefix = "@exec_prefix@";
+# Depend on prefix
+my $includedir = "@includedir@";
+my $localstatedir = "@localstatedir@";
+my $sharedstatedir = "@sharedstatedir@";
+my $sysconfdir = "@sysconfdir@";
+# Depend on exec_prefix
+my $bindir = "@bindir@";
+my $libdir = "@libdir@";
+my $libexecdir = "@libexecdir@";
+my $sbindir = "@sbindir@";
+# Data directories
+my $datarootdir = "@datarootdir@";
+my $datadir = "@datadir@";
+my $localedir = "@localedir@";
+my $mandir = "@mandir@";
+
+our %paths = (
+ 'PREFIX' => $prefix,
+ 'EXEC_PREFIX' => $exec_prefix,
+ 'INCLUDEDIR' => $includedir,
+ 'LOCALSTATEDIR' => $localstatedir,
+ 'SHAREDSTATEDIR' => $sharedstatedir,
+ 'SYSCONFDIR' => $sysconfdir,
+ 'BINDIR' => $bindir,
+ 'LIBDIR' => $libdir,
+ 'LIBEXECDIR' => $libexecdir,
+ 'SBINDIR' => $sbindir,
+ 'DATAROOTDIR' => $datarootdir,
+ 'DATADIR' => $datadir,
+ 'LOCALEDIR' => $localedir,
+ 'MANDIR' => $mandir,
+ 'BUILDD_CONF' => "@BUILDD_CONF@",
+ 'BUILDD_SYSCONF_DIR' => "@BUILDD_SYSCONF_DIR@",
+ 'SBUILD_CONF' => "@SBUILD_CONF@",
+ 'SBUILD_DATA_DIR' => "@SBUILD_DATA_DIR@",
+ 'SBUILD_LIBEXEC_DIR' => "@SBUILD_LIBEXEC_DIR@",
+ 'SBUILD_LOCALSTATE_DIR' => "$localstatedir/lib/sbuild",
+ 'SBUILD_SYSCONF_DIR' => "@SBUILD_SYSCONF_DIR@",
+ 'SCHROOT_CONF' => "@SCHROOT_CONF@",
+ 'SCHROOT_SYSCONF_DIR' => "@SCHROOT_SYSCONF_DIR@"
+);
+
+1;
diff --git a/lib/Sbuild/Utility.pm b/lib/Sbuild/Utility.pm
new file mode 100644
index 0000000..5a59b28
--- /dev/null
+++ b/lib/Sbuild/Utility.pm
@@ -0,0 +1,631 @@
+#
+# Utility.pm: library for sbuild utility programs
+# 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, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+#
+############################################################################
+
+# Import default modules into main
+package main;
+use Sbuild qw($devnull);
+use Sbuild::Sysconfig;
+
+$ENV{'LC_ALL'} = "C.UTF-8";
+$ENV{'SHELL'} = '/bin/sh';
+
+# avoid intermixing of stdout and stderr
+$| = 1;
+
+package Sbuild::Utility;
+
+use strict;
+use warnings;
+
+use Sbuild::Chroot;
+use File::Temp qw(tempfile);
+use Module::Load::Conditional qw(can_load); # Used to check for LWP::UserAgent
+use Time::HiRes qw ( time ); # Needed for high resolution timers
+
+sub get_dist ($);
+sub setup ($$$);
+sub cleanup ($);
+sub shutdown ($);
+sub get_unshare_cmd($);
+sub get_tar_compress_option($);
+
+my $current_session;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter);
+
+ @EXPORT = qw(setup cleanup shutdown check_url download get_unshare_cmd
+ read_subuid_subgid CLONE_NEWNS CLONE_NEWUTS CLONE_NEWIPC CLONE_NEWUSER
+ CLONE_NEWPID CLONE_NEWNET test_unshare get_tar_compress_options);
+
+ $SIG{'INT'} = \&shutdown;
+ $SIG{'TERM'} = \&shutdown;
+ $SIG{'ALRM'} = \&shutdown;
+ $SIG{'PIPE'} = \&shutdown;
+}
+
+sub get_dist ($) {
+ my $dist = shift;
+
+ $dist = "unstable" if ($dist eq "-u" || $dist eq "u");
+ $dist = "testing" if ($dist eq "-t" || $dist eq "t");
+ $dist = "stable" if ($dist eq "-s" || $dist eq "s");
+ $dist = "oldstable" if ($dist eq "-o" || $dist eq "o");
+ $dist = "experimental" if ($dist eq "-e" || $dist eq "e");
+
+ return $dist;
+}
+
+sub setup ($$$) {
+ my $namespace = shift;
+ my $distribution = shift;
+ my $conf = shift;
+
+ $conf->set('VERBOSE', 1);
+ $conf->set('NOLOG', 1);
+
+ $distribution = get_dist($distribution);
+
+ # TODO: Allow user to specify arch.
+ # Use require instead of 'use' to avoid circular dependencies when
+ # ChrootInfo modules happen to make use of this module
+ my $chroot_info;
+ if ($conf->get('CHROOT_MODE') eq 'schroot') {
+ require Sbuild::ChrootInfoSchroot;
+ $chroot_info = Sbuild::ChrootInfoSchroot->new($conf);
+ } elsif ($conf->get('CHROOT_MODE') eq 'autopkgtest') {
+ require Sbuild::ChrootInfoAutopkgtest;
+ $chroot_info = Sbuild::ChrootInfoAutopkgtest->new($conf);
+ } elsif ($conf->get('CHROOT_MODE') eq 'unshare') {
+ require Sbuild::ChrootInfoUnshare;
+ $chroot_info = Sbuild::ChrootInfoUnshare->new($conf);
+ } else {
+ require Sbuild::ChrootInfoSudo;
+ $chroot_info = Sbuild::ChrootInfoSudo->new($conf);
+ }
+
+ my $session;
+
+ $session = $chroot_info->create($namespace,
+ $distribution,
+ undef, # TODO: Add --chroot option
+ $conf->get('BUILD_ARCH'));
+
+ if (!defined $session) {
+ print STDERR "Error creating chroot info\n";
+ return undef;
+ }
+
+ $session->set('Log Stream', \*STDOUT);
+
+ my $chroot_defaults = $session->get('Defaults');
+ $chroot_defaults->{'DIR'} = '/';
+ $chroot_defaults->{'STREAMIN'} = $Sbuild::devnull;
+ $chroot_defaults->{'STREAMOUT'} = \*STDOUT;
+ $chroot_defaults->{'STREAMERR'} =\*STDOUT;
+
+ $Sbuild::Utility::current_session = $session;
+
+ if (!$session->begin_session()) {
+ print STDERR "Error setting up $distribution chroot\n";
+ return undef;
+ }
+
+ if (defined(&main::local_setup)) {
+ return main::local_setup($session);
+ }
+ return $session;
+}
+
+sub cleanup ($) {
+ my $conf = shift;
+
+ if (defined(&main::local_cleanup)) {
+ main::local_cleanup($Sbuild::Utility::current_session);
+ }
+ if (defined $Sbuild::Utility::current_session) {
+ $Sbuild::Utility::current_session->end_session();
+ }
+}
+
+sub shutdown ($) {
+ cleanup($main::conf); # FIXME: don't use global
+ exit 1;
+}
+
+# This method simply checks if a URL is valid.
+sub check_url {
+ my ($url) = @_;
+
+ # If $url is a readable plain file on the local system, just return true.
+ return 1 if (-f $url && -r $url);
+
+ # Load LWP::UserAgent if possible, else return 0.
+ if (! can_load( modules => { 'LWP::UserAgent' => undef, } )) {
+ warn "install the libwww-perl package to support downloading dsc files";
+ return 0;
+ }
+
+ # Setup the user agent.
+ my $ua = LWP::UserAgent->new;
+
+ # Determine if we need to specify any proxy settings.
+ $ua->env_proxy;
+ my $proxy = _get_proxy();
+ if ($proxy) {
+ $ua->proxy(['http', 'ftp'], $proxy);
+ }
+
+ # Dispatch a HEAD request, grab the response, and check the response for
+ # success.
+ my $res = $ua->head($url);
+ return 1 if ($res->is_success);
+
+ # URL wasn't valid.
+ return 0;
+}
+
+# This method is used to retrieve a file, usually from a location on the
+# Internet, but it can also be used for files in the local system.
+# $url is location of file, $file is path to write $url into.
+sub download {
+ # The parameters will be any URL and a location to save the file to.
+ my($url, $file) = @_;
+
+ # If $url is a readable plain file on the local system, just return the
+ # $url.
+ return $url if (-f $url && -r $url);
+
+ # Load LWP::UserAgent if possible, else return 0.
+ if (! can_load( modules => { 'LWP::UserAgent' => undef, } )) {
+ return 0;
+ }
+
+ # Filehandle we'll be writing to.
+ my $fh;
+
+ # If $file isn't defined, a temporary file will be used instead.
+ ($fh, $file) = tempfile( UNLINK => 0 ) if (! $file);
+
+ # Setup the user agent.
+ my $ua = LWP::UserAgent->new;
+
+ # Determine if we need to specify any proxy settings.
+ $ua->env_proxy;
+ my $proxy = _get_proxy();
+ if ($proxy) {
+ $ua->proxy(['http', 'ftp'], $proxy);
+ }
+
+ # Download the file.
+ print "Downloading $url to $file.\n";
+ my $expected_length; # Total size we expect of content
+ my $bytes_received = 0; # Size of content as it is received
+ my $percent; # The percentage downloaded
+ my $tick; # Used for counting.
+ my $start_time = time; # Record of the start time
+ open($fh, '>', $file); # Destination file to download content to
+ my $request = HTTP::Request->new(GET => $url);
+ my $response = $ua->request($request,
+ sub {
+ # Our own content callback subroutine
+ my ($chunk, $response) = @_;
+
+ $bytes_received += length($chunk);
+ unless (defined $expected_length) {
+ $expected_length = $response->content_length or undef;
+ }
+ if ($expected_length) {
+ # Here we calculate the speed of the download to print out later
+ my $speed;
+ my $duration = time - $start_time;
+ if ($bytes_received/$duration >= 1024 * 1024) {
+ $speed = sprintf("%.4g MB",
+ ($bytes_received/$duration) / (1024.0 * 1024)) . "/s";
+ } elsif ($bytes_received/$duration >= 1024) {
+ $speed = sprintf("%.4g KB",
+ ($bytes_received/$duration) / 1024.0) . "/s";
+ } else {
+ $speed = sprintf("%.4g B",
+ ($bytes_received/$duration)) . "/s";
+ }
+ # Calculate the percentage downloaded
+ $percent = sprintf("%d",
+ 100 * $bytes_received / $expected_length);
+ $tick++; # Keep count
+ # Here we print out a progress of the download. We start by
+ # printing out the amount of data retrieved so far, and then
+ # show a progress bar. After 50 ticks, the percentage is printed
+ # and the speed of the download is printed. A new line is
+ # started and the process repeats until the download is
+ # complete.
+ if (($tick == 250) or ($percent == 100)) {
+ if ($tick == 1) {
+ # In case we reach 100% from tick 1.
+ printf "%8s", sprintf("%d",
+ $bytes_received / 1024) . "KB";
+ print " [.";
+ }
+ while ($tick != 250) {
+ # In case we reach 100% before reaching 250 ticks
+ print "." if ($tick % 5 == 0);
+ $tick++;
+ }
+ print ".]";
+ printf "%5s", "$percent%";
+ printf "%12s", "$speed\n";
+ $tick = 0;
+ } elsif ($tick == 1) {
+ printf "%8s", sprintf("%d",
+ $bytes_received / 1024) . "KB";
+ print " [.";
+ } elsif ($tick % 5 == 0) {
+ print ".";
+ }
+ }
+ # Write the contents of the download to our specified file
+ if ($response->is_success) {
+ print $fh $chunk; # Print content to file
+ } else {
+ # Print message upon failure during download
+ print "\n" . $response->status_line . "\n";
+ return 0;
+ }
+ }
+ ); # End of our content callback subroutine
+ close $fh; # Close the destination file
+
+ # Print error message in case we couldn't get a response at all.
+ if (!$response->is_success) {
+ print $response->status_line . "\n";
+ return 0;
+ }
+
+ # Print out amount of content received before returning the path of the
+ # file.
+ print "Download of $url successful.\n";
+ print "Size of content downloaded: ";
+ if ($bytes_received >= 1024 * 1024) {
+ print sprintf("%.4g MB",
+ $bytes_received / (1024.0 * 1024)) . "\n";
+ } elsif ($bytes_received >= 1024) {
+ print sprintf("%.4g KB", $bytes_received / 1024.0) . "\n";
+ } else {
+ print sprintf("%.4g B", $bytes_received) . "\n";
+ }
+
+ return $file;
+}
+
+# This method is used to determine the proxy settings used on the local system.
+# It will return the proxy URL if a proxy setting is found.
+sub _get_proxy {
+ my $proxy;
+
+ # Attempt to acquire a proxy URL from apt-config.
+ if (open(my $apt_config_output, '-|', '/usr/bin/apt-config dump')) {
+ foreach my $tmp (<$apt_config_output>) {
+ if ($tmp =~ m/^.*Acquire::http::Proxy\s+/) {
+ $proxy = $tmp;
+ chomp($proxy);
+ # Trim the line to only the proxy URL
+ $proxy =~ s/^.*Acquire::http::Proxy\s+"|";$//g;
+ return $proxy;
+ }
+ }
+ close $apt_config_output;
+ }
+
+ # Attempt to acquire a proxy URL from the user's or system's wgetrc
+ # configuration.
+ # First try the user's wgetrc
+ if (open(my $wgetrc, '<', "$ENV{'HOME'}/.wgetrc")) {
+ foreach my $tmp (<$wgetrc>) {
+ if ($tmp =~ m/^[^#]*http_proxy/) {
+ $proxy = $tmp;
+ chomp($proxy);
+ # Trim the line to only the proxy URL
+ $proxy =~ s/^.*http_proxy\s*=\s*|\s+$//g;
+ return $proxy;
+ }
+ }
+ close($wgetrc);
+ }
+ # Now try the system's wgetrc
+ if (open(my $wgetrc, '<', '/etc/wgetrc')) {
+ foreach my $tmp (<$wgetrc>) {
+ if ($tmp =~ m/^[^#]*http_proxy/) {
+ $proxy = $tmp;
+ chomp($proxy);
+ # Trim the line to only the proxy URL
+ $proxy =~ s/^.*http_proxy\s*=\s*|\s+$//g;
+ return $proxy;
+ }
+ }
+ close($wgetrc);
+ }
+
+ # At this point there should be no proxy settings. Return undefined.
+ return 0;
+}
+
+# from sched.h
+use constant {
+ CLONE_NEWNS => 0x20000,
+ CLONE_NEWUTS => 0x4000000,
+ CLONE_NEWIPC => 0x8000000,
+ CLONE_NEWUSER => 0x10000000,
+ CLONE_NEWPID => 0x20000000,
+ CLONE_NEWNET => 0x40000000,
+};
+
+sub get_unshare_cmd($) {
+ my $options = shift;
+
+ my @idmap = @{$options->{'IDMAP'}};
+
+ my $unshare_flags = CLONE_NEWUSER;
+
+ if (defined($options->{'UNSHARE_FLAGS'})) {
+ $unshare_flags |= $options->{'UNSHARE_FLAGS'};
+ }
+
+ my $uidmapcmd = "";
+ my $gidmapcmd = "";
+ foreach (@idmap) {
+ my ($t, $hostid, $nsid, $range) = @{$_};
+ if ($t ne "u" and $t ne "g" and $t ne "b") {
+ die "invalid idmap type: $t";
+ }
+ if ($t eq "u" or $t eq "b") {
+ $uidmapcmd .= " $hostid $nsid $range";
+ }
+ if ($t eq "g" or $t eq "b") {
+ $gidmapcmd .= " $hostid $nsid $range";
+ }
+ }
+ my $idmapcmd = '';
+ if ($uidmapcmd ne "") {
+ $idmapcmd .= "0 == system \"newuidmap \$ppid $uidmapcmd\" or die \"newuidmap failed: \$!\";";
+ }
+ if ($gidmapcmd ne "") {
+ $idmapcmd .= "0 == system \"newgidmap \$ppid $gidmapcmd\" or die \"newgidmap failed: \$!\";";
+ }
+
+ my $command = <<"EOF";
+require 'syscall.ph';
+
+# Create a pipe for the parent process to signal the child process that it is
+# done with calling unshare() so that the child can go ahead setting up
+# uid_map and gid_map.
+pipe my \$rfh, my \$wfh;
+
+# We have to do this dance with forking a process and then modifying the
+# parent from the child because:
+# - new[ug]idmap can only be called on a process id after that process has
+# unshared the user namespace
+# - a process looses its capabilities if it performs an execve() with nonzero
+# user ids see the capabilities(7) man page for details.
+# - a process that unshared the user namespace by default does not have the
+# privileges to call new[ug]idmap on itself
+#
+# this also works the other way around (the child setting up a user namespace
+# and being modified from the parent) but that way, the parent would have to
+# stay around until the child exited (so a pid would be wasted). Additionally,
+# that variant would require an additional pipe to let the parent signal the
+# child that it is done with calling new[ug]idmap. The way it is done here,
+# this signaling can instead be done by wait()-ing for the exit of the child.
+my \$ppid = \$\$;
+my \$cpid = fork() // die "fork() failed: \$!";
+if (\$cpid == 0) {
+ # child
+
+ # Close the writing descriptor at our end of the pipe so that we see EOF
+ # when parent closes its descriptor.
+ close \$wfh;
+
+ # Wait for the parent process to finish its unshare() call by waiting for
+ # an EOF.
+ 0 == sysread \$rfh, my \$c, 1 or die "read() did not receive EOF";
+
+ # The program's new[ug]idmap have to be used because they are setuid root.
+ # These privileges are needed to map the ids from /etc/sub[ug]id to the
+ # user namespace set up by the parent. Without these privileges, only the
+ # id of the user itself can be mapped into the new namespace.
+ #
+ # Since new[ug]idmap is setuid root we also don't need to write "deny" to
+ # /proc/\$\$/setgroups beforehand (this is otherwise required for
+ # unprivileged processes trying to write to /proc/\$\$/gid_map since kernel
+ # version 3.19 for security reasons) and therefore the parent process
+ # keeps its ability to change its own group here.
+ #
+ # Since /proc/\$ppid/[ug]id_map can only be written to once, respectively,
+ # instead of making multiple calls to new[ug]idmap, we assemble a command
+ # line that makes one call each.
+ $idmapcmd
+ exit 0;
+}
+
+# parent
+
+# After fork()-ing, the parent immediately calls unshare...
+0 == syscall &SYS_unshare, $unshare_flags or die "unshare() failed: \$!";
+
+# .. and then signals the child process that we are done with the unshare()
+# call by sending an EOF.
+close \$wfh;
+
+# Wait for the child process to finish its setup by waiting for its exit.
+\$cpid == waitpid \$cpid, 0 or die "waitpid() failed: \$!";
+if (\$? != 0) {
+ die "child had a non-zero exit status: \$?";
+}
+
+# Currently we are nobody (uid and gid are 65534). So we become root user and
+# group instead.
+#
+# We are using direct syscalls instead of setting \$(, \$), \$< and \$> because
+# then perl would do additional stuff which we don't need or want here, like
+# checking /proc/sys/kernel/ngroups_max (which might not exist). It would also
+# also call setgroups() in a way that makes the root user be part of the
+# group unknown.
+0 == syscall &SYS_setgid, 0 or die "setgid failed: \$!";
+0 == syscall &SYS_setuid, 0 or die "setuid failed: \$!";
+0 == syscall &SYS_setgroups, 0, 0 or die "setgroups failed: \$!";
+EOF
+
+ if ($options->{'FORK'}) {
+ $command .= <<"EOF";
+# When the pid namespace is also unshared, then processes expect a master pid
+# to always be alive within the namespace. To achieve this, we fork() here
+# instead of exec() to always have one dummy process running as pid 1 inside
+# the namespace. This is also what the unshare tool does when used with the
+# --fork option.
+#
+# Otherwise, without a pid 1, new processes cannot be forked anymore after pid
+# 1 finished.
+my \$cpid = fork() // die "fork() failed: \$!";
+if (\$cpid != 0) {
+ # The parent process will stay alive as pid 1 in this namespace until
+ # the child finishes executing. This is important because pid 1 must
+ # never die or otherwise nothing new can be forked.
+ \$cpid == waitpid \$cpid, 0 or die "waitpid() failed: \$!";
+ exit (\$? >> 8);
+}
+EOF
+ }
+
+ $command .= 'exec { $ARGV[0] } @ARGV or die "exec() failed: $!";';
+ # remove code comments
+ $command =~ s/^\s*#.*$//gm;
+ # remove whitespace at beginning and end
+ $command =~ s/^\s+//gm;
+ $command =~ s/\s+$//gm;
+ # remove linebreaks
+ $command =~ s/\n//gm;
+ return ('perl', '-e', $command);
+}
+
+sub read_subuid_subgid() {
+ my $username = getpwuid $<;
+ my ($subid, $num_subid, $fh, $n);
+ my @result = ();
+
+ if (! -e "/etc/subuid") {
+ printf STDERR "/etc/subuid doesn't exist\n";
+ return;
+ }
+ if (! -r "/etc/subuid") {
+ printf STDERR "/etc/subuid is not readable\n";
+ return;
+ }
+
+ open $fh, "<", "/etc/subuid" or die "cannot open /etc/subuid for reading: $!";
+ while (my $line = <$fh>) {
+ ($n, $subid, $num_subid) = split(/:/, $line, 3);
+ last if ($n eq $username);
+ }
+ close $fh;
+
+ if ($n ne $username) {
+ printf STDERR "No entry for $username in /etc/subuid";
+ return;
+ }
+
+ push @result, ["u", 0, $subid, $num_subid];
+
+ open $fh, "<", "/etc/subgid" or die "cannot open /etc/subgid for reading: $!";
+ while (my $line = <$fh>) {
+ ($n, $subid, $num_subid) = split(/:/, $line, 3);
+ last if ($n eq $username);
+ }
+ close $fh;
+
+ if ($n ne $username) {
+ printf STDERR "No entry for $username in /etc/subgid";
+ return;
+ }
+
+ push @result, ["g", 0, $subid, $num_subid];
+
+ return @result;
+}
+
+sub test_unshare() {
+ # we spawn a new per process because if unshare succeeds, we would
+ # otherwise have unshared the sbuild process itself which we don't want
+ my $pid = fork();
+ if ($pid == 0) {
+ require "syscall.ph";
+ my $ret = syscall &SYS_unshare, CLONE_NEWUSER;
+ if (($ret >> 8) == 0) {
+ exit 0;
+ } else {
+ exit 1;
+ }
+ }
+ waitpid($pid, 0);
+ if (($? >> 8) != 0) {
+ printf STDERR "E: unshare failed: $!\n";
+ my $procfile = '/proc/sys/kernel/unprivileged_userns_clone';
+ open(my $fh, '<', $procfile) or die "failed to open $procfile";
+ chomp(my $content = do { local $/; <$fh> });
+ close($fh);
+ if ($content ne "1") {
+ print STDERR "I: /proc/sys/kernel/unprivileged_userns_clone is set to $content\n";
+ print STDERR "I: try running: sudo sysctl -w kernel.unprivileged_userns_clone=1\n";
+ print STDERR "I: or permanently enable unprivileged usernamespaces by putting the setting into /etc/sysctl.d/\n";
+ }
+ return 0;
+ }
+ return 1;
+}
+
+# tar cannot figure out the decompression program when receiving data on
+# standard input, thus we do it ourselves. This is copied from tar's
+# src/suffix.c
+sub get_tar_compress_options($) {
+ my $filename = shift;
+ if ($filename =~ /\.(gz|tgz|taz)$/) {
+ return ('--gzip');
+ } elsif ($filename =~ /\.(Z|taZ)$/) {
+ return ('--compress');
+ } elsif ($filename =~ /\.(bz2|tbz|tbz2|tz2)$/) {
+ return ('--bzip2');
+ } elsif ($filename =~ /\.lz$/) {
+ return ('--lzip');
+ } elsif ($filename =~ /\.(lzma|tlz)$/) {
+ return ('--lzma');
+ } elsif ($filename =~ /\.lzo$/) {
+ return ('--lzop');
+ } elsif ($filename =~ /\.lz4$/) {
+ return ('--use-compress-program', 'lz4');
+ } elsif ($filename =~ /\.(xz|txz)$/) {
+ return ('--xz');
+ } elsif ($filename =~ /\.zst$/) {
+ return ('--zstd');
+ }
+ return ();
+}
+
+1;
diff --git a/lib/Sbuild/XaptResolver.pm b/lib/Sbuild/XaptResolver.pm
new file mode 100644
index 0000000..e25f644
--- /dev/null
+++ b/lib/Sbuild/XaptResolver.pm
@@ -0,0 +1,107 @@
+# ResolverBase.pm: build library for sbuild
+# Copyright © 2005 Ryan Murray <rmurray@debian.org>
+# Copyright © 2005-2008 Roger Leigh <rleigh@debian.org>
+# 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 Sbuild::XaptResolver;
+
+use strict;
+use warnings;
+
+use Sbuild qw(debug);
+use Sbuild::Base;
+use Sbuild::ResolverBase;
+
+BEGIN {
+ use Exporter ();
+ our (@ISA, @EXPORT);
+
+ @ISA = qw(Exporter Sbuild::ResolverBase);
+
+ @EXPORT = qw();
+}
+
+sub new {
+ my $class = shift;
+ my $conf = shift;
+ my $session = shift;
+ my $host = shift;
+
+ my $self = $class->SUPER::new($conf, $session, $host);
+ bless($self, $class);
+
+ return $self;
+}
+
+sub install_deps {
+ my $self = shift;
+ my $name = shift;
+ my @pkgs = @_;
+
+ my $status = 0;
+ my $session = $self->get('Session');
+# my $dummy_pkg_name = 'sbuild-build-depends-' . $name. '-dummy';
+
+ # Call functions to setup an archive to install dummy package.
+# return 0 unless ($self->setup_apt_archive($dummy_pkg_name, @pkgs));
+# return 0 unless (!$self->update_archive());
+
+
+ $self->log_subsection("Install $name cross-build dependencies (xapt-based resolver)");
+
+ # Install the dummy package
+ my (@instd, @rmvd);
+ $self->log("Installing cross-build dependencies\n");
+ if (!$self->run_xapt("-a", $self->get_conf('HOST_ARCH'), @pkgs)) {
+ $self->log("Package installation failed\n");
+ if (defined ($self->get('Session')->get('Session Purged')) &&
+ $self->get('Session')->get('Session Purged') == 1) {
+ $self->log("Not removing build depends: cloned chroot in use\n");
+ } else {
+ $self->set_installed(@instd);
+ $self->set_removed(@rmvd);
+ goto package_cleanup;
+ }
+ return 0;
+ }
+ $self->set_installed(@instd);
+ $self->set_removed(@rmvd);
+ $status = 1;
+
+ package_cleanup:
+ if ($status == 0) {
+ if (defined ($session->get('Session Purged')) &&
+ $session->get('Session Purged') == 1) {
+ $self->log("Not removing installed packages: cloned chroot in use\n");
+ } else {
+ $self->uninstall_deps();
+ }
+ }
+
+ return $status;
+}
+
+sub purge_extra_packages {
+ my $self = shift;
+ my $name = shift;
+
+ $self->log_error('Xapt resolver doesn\'t implement purging of extra packages yet.\n');
+}
+
+1;
diff --git a/lisp/buildd-reply.el b/lisp/buildd-reply.el
new file mode 100644
index 0000000..5ba768d
--- /dev/null
+++ b/lisp/buildd-reply.el
@@ -0,0 +1,361 @@
+;;;!/bin/sh -e
+;;;
+;;; buildd-reply.el: Some utility functions to reply to buildd mails
+;;; 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/>.
+;;;
+;;; This file is not part of GNU Emacs.
+
+(defun buildd-prepare-mail (label send-now &rest ins)
+ (if label
+ (rmail-set-label label t))
+ (rmail-reply t)
+ (goto-char (point-max))
+ (while ins
+ (insert (car ins))
+ (setq ins (cdr ins)))
+ (if send-now
+ (mail-send-and-exit nil)))
+
+;; Reply to buildd logs with a signed .changes file (which must be
+;; extracted from the mail)
+(defun buildd-reply-ok ()
+ (interactive)
+ (save-excursion
+ (let (beg end str)
+ (goto-char (point-max))
+ (if (not (re-search-backward "^[^ ]*\\.changes:$"))
+ (error "Can't find .changes: line"))
+ (forward-line 1)
+ (beginning-of-line)
+ (setq beg (point))
+ (if (not (re-search-forward "^Files: $"))
+ (error "Can't find Files: line"))
+ (beginning-of-line)
+ (forward-line 1)
+ (while (looking-at "^ ")
+ (forward-line 1))
+ (setq end (point))
+ (setq str (buffer-substring beg end))
+ (rmail-set-label "ok" t)
+
+ (rmail-reply t)
+ (goto-char (point-max))
+ (setq beg (point))
+ (insert str)
+ (goto-char (point-max))
+ (setq end (point))
+ (mc-sign-region 0 beg end)
+ (mail-send-and-exit nil))))
+
+(defun buildd-reply-newversion ()
+ (interactive)
+ (save-excursion
+ (let (str)
+ (goto-char (point-min))
+ (if (not (re-search-forward "only different version \\([^ ]*\\) found"))
+ (error "Can't find version"))
+ (setq str (buffer-substring (match-beginning 1) (match-end 1)))
+ (buildd-prepare-mail "newv" t "newvers " str "\n"))))
+
+(defun buildd-reply-retry ()
+ (interactive)
+ (save-excursion
+ (buildd-prepare-mail "retry" t "retry\n")))
+
+(defun buildd-reply-depretry ()
+ (interactive)
+ (save-excursion
+ (let ((deps ""))
+ (goto-char (point-min))
+ (while (re-search-forward
+ "^E: Package \\([^ ]*\\) has no installation candidate$"
+ (point-max) t)
+ (if (> (length deps) 0) (setq deps (concat deps ", ")))
+ (setq deps (concat deps (buffer-substring (match-beginning 1)
+ (match-end 1)))))
+ (goto-char (point-min))
+ (while (re-search-forward
+ "^E: Couldn't find package \\([^ ]*\\)$" (point-max) t)
+ (if (> (length deps) 0) (setq deps (concat deps ", ")))
+ (setq deps (concat deps (buffer-substring (match-beginning 1)
+ (match-end 1)))))
+ (goto-char (point-min))
+ (if (re-search-forward
+ "^After installing, the following source dependencies"
+ (point-max) t)
+ (progn
+ (forward-line 1)
+ (while (re-search-forward
+ "\\([^ (]*\\)(inst [^ ]* ! \\([<>=]*\\) wanted \\([^ ]*\\))"
+ (point-max) t)
+ (if (> (length deps) 0) (setq deps (concat deps ", ")))
+ (setq deps (concat deps
+ (buffer-substring (match-beginning 1)
+ (match-end 1))
+ " ("
+ (buffer-substring (match-beginning 2)
+ (match-end 2))
+ " "
+ (buffer-substring (match-beginning 3)
+ (match-end 3))
+ ")")))))
+ (buildd-prepare-mail "dretry" nil "dep-retry " deps "\n")
+ (forward-line -1)
+ (end-of-line))))
+
+(defun buildd-reply-giveback ()
+ (interactive)
+ (save-excursion
+ (buildd-prepare-mail "giveback" t "giveback\n")))
+
+(defun buildd-reply-notforus ()
+ (interactive)
+ (save-excursion
+ (buildd-prepare-mail "not-for-us" t "not-for-us\n")))
+
+(defun buildd-reply-manual ()
+ (interactive)
+ (save-excursion
+ (buildd-prepare-mail "manual" t "manual\n")))
+
+(defun buildd-reply-purge ()
+ (interactive)
+ (save-excursion
+ (buildd-prepare-mail "purge" t "purge\n")))
+
+(defun buildd-reply-fail ()
+ (interactive)
+ (save-excursion
+ (buildd-prepare-mail "failed" nil "fail\n")
+ (goto-char (point-max))))
+
+(defvar buildd-log-base-addr "http://m68k.debian.org/buildd/logs/")
+
+(defun buildd-bug ()
+ (interactive)
+ (save-excursion
+ (let (pkgv pkg vers dist time)
+ (goto-char (point-min))
+ (if (not (re-search-forward "^Subject: Log for .* build of \\([^ ][^ ]*\\)_\\([^ ][^ ]*\\) (dist=\\([a-z]*\\))"))
+ (error "Can't find package+version in subject"))
+ (setq pkg (buffer-substring (match-beginning 1) (match-end 1))
+ vers (buffer-substring (match-beginning 2) (match-end 2))
+ dist (buffer-substring (match-beginning 3) (match-end 3)))
+ (setq pkgv (concat pkg "_" vers))
+ (if (not (re-search-forward "^Build started at \\([0-9-]*\\)"))
+ (error "Can't find package+version in subject"))
+ (setq time (buffer-substring (match-beginning 1) (match-end 1)))
+ (rmail-set-label "bug" t)
+
+ (rmail-mail)
+ (goto-char (point-min))
+ (end-of-line)
+ (insert "submit@bugs.debian.org")
+ (forward-line 1)
+ (end-of-line)
+ (insert (concat pkgv "(" dist "): "))
+ (goto-char (point-max))
+ (insert
+ (concat "Package: " pkg "\nVersion: " vers
+ "\nSeverity: important\n\n\n"
+ "A complete build log can be found at\n"
+ buildd-log-base-addr pkgv "_" time "\n"))
+ (goto-char (point-min))
+ (forward-line 1)
+ (end-of-line))))
+
+(defvar buildd-mail-addr "buildd")
+
+(defun buildd-bug-ack-append (edit-addr)
+ (interactive "P")
+ (save-excursion
+ (let (bugno pkgv pkg vers dist beg end)
+ (goto-char (point-min))
+ (if (not (re-search-forward "^Subject: Bug#\\([0-9]*\\): Acknowledgement (\\([^ ][^ ]*\\)_\\([^ ][^ ]*\\)(\\([a-z]*\\)): "))
+ (error "Can't find bug#, package+version, and/or dist in subject"))
+ (setq bugno (buffer-substring (match-beginning 1) (match-end 1))
+ pkg (buffer-substring (match-beginning 2) (match-end 2))
+ vers (buffer-substring (match-beginning 3) (match-end 3))
+ dist (buffer-substring (match-beginning 4) (match-end 4)))
+ (setq pkgv (concat pkg "_" vers))
+ (rmail-mail)
+ (goto-char (point-min))
+ (end-of-line)
+ (insert buildd-mail-addr)
+ (forward-line 1)
+ (end-of-line)
+ (insert (concat "Re: Log for failed build of " pkgv " (dist=" dist ")"))
+ (goto-char (point-max))
+ (insert (concat "fail\n(see #" bugno ")\n"))
+ (if (null edit-addr)
+ (mail-send-and-exit nil)
+ (progn (goto-char (point-min)) (forward-word 2) (forward-word -1))))))
+
+(defun buildd-bug-comment ()
+ (interactive)
+ (save-excursion
+ (let (pkgv pkg vers dist)
+ (goto-char (point-min))
+ (if (not (re-search-forward "^Subject: \\(Re:[ ]*\\)?Bug#\\([0-9]*\\): \\([^ ][^ ]*\\)_\\([^ ][^ ]*\\)(\\([a-z]*\\)"))
+ (error "Can't find bug#, package+version, and/or dist in subject"))
+ (setq pkg (buffer-substring (match-beginning 3) (match-end 3))
+ vers (buffer-substring (match-beginning 4) (match-end 4))
+ dist (buffer-substring (match-beginning 5) (match-end 5)))
+ (setq pkgv (concat pkg "_" vers))
+ (rmail-mail)
+ (goto-char (point-min))
+ (end-of-line)
+ (insert buildd-mail-addr)
+ (forward-line 1)
+ (end-of-line)
+ (insert (concat "Re: Log for failed build of " pkgv " (dist=" dist ")"))
+ (goto-char (point-max))
+ (insert "fail\n")
+ (call-process "date" nil t nil "+%m/%d/%y")
+ (forward-char -1)
+ (insert ": "))))
+
+(defun buildd-bug-change-category (edit-addr)
+ (interactive "P")
+ (save-excursion
+ (let (cat pkgv pkg vers dist)
+ (goto-char (point-min))
+ (if (not (re-search-forward "^Subject: \\(Re:[ ]*\\)?Bug#\\([0-9]*\\): \\([^ ][^ ]*\\)_\\([^ ][^ ]*\\)(\\([a-z]*\\)"))
+ (error "Can't find bug#, package+version, and/or dist in subject"))
+ (setq pkg (buffer-substring (match-beginning 3) (match-end 3))
+ vers (buffer-substring (match-beginning 4) (match-end 4))
+ dist (buffer-substring (match-beginning 5) (match-end 5)))
+ (setq pkgv (concat pkg "_" vers))
+ (setq cat
+ (completing-read "New bug category: "
+ (mapcar (function (lambda (x) (cons x t)))
+ '("fix-expected" "reminder-sent"
+ "nmu-offered" "easy" "medium" "hard"
+ "compiler-error" "uploaded-fixed-pkg"))
+ nil t nil))
+ (rmail-mail)
+ (goto-char (point-min))
+ (end-of-line)
+ (insert buildd-mail-addr)
+ (forward-line 1)
+ (end-of-line)
+ (insert (concat "Re: Log for failed build of " pkgv " (dist=" dist ")"))
+ (goto-char (point-max))
+ (insert (concat "fail\n[" cat "]\n"))
+ (if (null edit-addr)
+ (mail-send-and-exit nil)
+ (progn (goto-char (point-min)) (forward-word 2) (forward-word -1))))))
+
+(defun buildd-reopen-bug (bugno)
+ (interactive "nBug Number: ")
+ (save-excursion
+ (rmail-set-label "bug" t)
+ (rmail-mail)
+ (goto-char (point-min))
+ (end-of-line)
+ (insert (concat "control@bugs.debian.org, " bugno "@bugs.debian.org"))
+ (forward-line 1)
+ (end-of-line)
+ (insert (concat "Re: Bug#" bugno ": "))
+ (goto-char (point-max))
+ (insert (concat "reopen " bugno "\nstop\n\n"))))
+
+(defvar manual-source-deps-file-pattern
+ "wanna-build/andrea/madd_sd-")
+
+;; Used by buildd-edit-manual-source-deps.
+
+(defun buildd-find-place-for-new-source-dep (package)
+ (let ((this-package "")
+ (found-place nil))
+ (beginning-of-buffer)
+ ; Skip the comments and jump to the source deps
+ (re-search-forward "^[a-zA-Z0-9]* => ")
+ (beginning-of-line)
+ (forward-line -1)
+ ; Find the first higher (alphabetically later) package name
+ (while (and (< (point) (point-max)) (not found-place))
+ (progn
+ (re-search-forward "^\\([^=|]*[ \t]*|[ \t]*\\)?\\([a-zA-Z0-9.+-]*\\) => ")
+ (setq this-package (buffer-substring (match-beginning 2)
+ (match-end 2)))
+ (if (string-lessp package this-package)
+ (setq found-place t))))
+ ; Should never happen (assuming no source package is > `zephyr')
+ (if (not found-place)
+ (error "Couldn't find place for package %s" package))
+ ; Insert the package name, ready for the user to add the first source dep
+ (beginning-of-line)
+ (insert (format "%s => \n" package))
+ (forward-char -1)))
+
+;; Brings up a buffer with source-dependencies.manual file in it and
+;; jumps to the right place.
+
+(defun buildd-edit-manual-source-deps ()
+ (interactive)
+ (save-excursion
+ (goto-char (point-min))
+ (if (not (re-search-forward "Subject: Log for \\(failed\\|successful\\) build of \\([a-zA-Z0-9.+-]*\\)_[^ ]* (dist=\\([a-z]*\\)"))
+ (error "Can't find valid subject"))
+ (setq package (buffer-substring (match-beginning 2) (match-end 2)))
+ (setq dist (buffer-substring (match-beginning 3) (match-end 3))))
+ (setq buf (find-file-noselect (concat manual-source-deps-file-pattern dist)))
+ (pop-to-buffer buf)
+ (goto-char (point-min))
+ (if (re-search-forward (format "^\\([^=|]*[ \t]*|[ \t]*\\)?%s => " package) nil t)
+ (progn
+ (end-of-line)
+ (insert ", "))
+ (buildd-find-place-for-new-source-dep package)))
+
+
+(defvar manual-sd-map-file
+ "wanna-build/andrea/sd_map-unstable")
+
+(defun buildd-add-sd-map (package)
+ (interactive "sMap: ")
+ (if (not (string= (substring (buffer-name) 0 7) "sd_map-"))
+ (progn (setq buf (find-file-noselect manual-sd-map-file))
+ (pop-to-buffer buf)))
+ (goto-char (point-min))
+ (goto-char (point-min))
+ (if (re-search-forward (format "^\\([^=|]*[ \t]*|[ \t]*\\)?%s => " package) nil t)
+ (progn
+ (end-of-line)
+ (insert ", "))
+ (buildd-find-place-for-new-source-dep package)))
+
+(require 'rmail)
+(add-hook 'rmail-mode-hook
+ (lambda ()
+ (define-key rmail-mode-map "\C-c\C-o" 'buildd-reply-ok)
+ (define-key rmail-mode-map "\C-c\C-n" 'buildd-reply-newversion)
+ (define-key rmail-mode-map "\C-c\C-r" 'buildd-reply-retry)
+ (define-key rmail-mode-map "\C-c\C-d" 'buildd-reply-depretry)
+ (define-key rmail-mode-map "\C-c\C-m" 'buildd-reply-manual)
+ (define-key rmail-mode-map "\C-c\C-p" 'buildd-reply-purge)
+ (define-key rmail-mode-map "\C-c\C-f" 'buildd-reply-fail)
+ (define-key rmail-mode-map "\C-c\M-g" 'buildd-reply-giveback)
+ (define-key rmail-mode-map "\C-c\M-n" 'buildd-reply-notforus)
+ (define-key rmail-mode-map "\C-c\C-s" 'buildd-edit-manual-source-deps)
+ (define-key rmail-mode-map "\C-c\C-b" 'buildd-bug)
+ (define-key rmail-mode-map "\C-c\M-b" 'buildd-reopen-bug)
+ (define-key rmail-mode-map "\C-c\C-a\C-n" 'buildd-bug-ack-append)
+ (define-key rmail-mode-map "\C-c\C-a\C-a" 'buildd-bug-comment)
+ (define-key rmail-mode-map "\C-c\C-a\C-c" 'buildd-bug-change-category)))
+(global-set-key "\C-c\C-v" 'buildd-add-sd-map)
diff --git a/man/.gitignore b/man/.gitignore
new file mode 100644
index 0000000..f522167
--- /dev/null
+++ b/man/.gitignore
@@ -0,0 +1,7 @@
+*.1
+*.5
+*.7
+*.8
+sbuild.conf.man
+buildd.conf.man
+defs.man
diff --git a/man/Makefile.am b/man/Makefile.am
new file mode 100644
index 0000000..a210e1b
--- /dev/null
+++ b/man/Makefile.am
@@ -0,0 +1,102 @@
+# 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
+
+man_MANS = \
+ buildd.1 \
+ buildd.conf.5 \
+ buildd-mail.1 \
+ buildd-uploader.1 \
+ buildd-vlog.1 \
+ buildd-watcher.1 \
+ sbuild.1 \
+ sbuild.conf.5 \
+ sbuild-abort.1 \
+ sbuild-adduser.8 \
+ sbuild-apt.1 \
+ sbuild-checkpackages.1 \
+ sbuild-createchroot.8 \
+ sbuild-debian-developer-setup.1 \
+ sbuild-destroychroot.8 \
+ sbuild-hold.1 \
+ sbuild-qemu.1 \
+ sbuild-qemu-boot.1 \
+ sbuild-qemu-create.1 \
+ sbuild-qemu-update.1 \
+ sbuild-setup.7 \
+ sbuild-shell.1 \
+ sbuild-update.1
+
+sbuild.conf.man: $(abs_top_srcdir)/tools/sbuild-dumpconfig $(abs_top_srcdir)/lib/Sbuild/Conf.pm $(abs_top_srcdir)/lib/Sbuild/ConfBase.pm
+ PERL5LIB=$(abs_top_srcdir)/lib:$(abs_top_builddir)/lib \
+ $< sbuild man > $@
+
+buildd.conf.man: $(abs_top_srcdir)/tools/sbuild-dumpconfig $(abs_top_srcdir)/lib/Buildd/Conf.pm
+ PERL5LIB=$(abs_top_srcdir)/lib:$(abs_top_builddir)/lib \
+ $< buildd man > $@
+
+sbuild.conf.5: sbuild.conf.5.in defs.man sbuild.conf.man
+ soelim $< > $@
+
+buildd.conf.5: buildd.conf.5.in defs.man buildd.conf.man
+ soelim $< > $@
+
+%.1: %.1.in defs.man
+ soelim $< > $@
+
+%.5: %.5.in defs.man
+ soelim $< > $@
+
+%.7: %.7.in defs.man
+ soelim $< > $@
+
+%.8: %.8.in defs.man
+ soelim $< > $@
+
+CLEANFILES = \
+ sbuild.conf.man \
+ buildd.conf.man
+
+EXTRA_DIST = \
+ defs.man.in \
+ $(addsuffix .in, $(man_MANS)) \
+ sbuild-debuild.1.in
+
+install-data-hook:
+ ln -sf sbuild-hold.1 $(DESTDIR)$(man1dir)/sbuild-unhold.1
+ ln -sf sbuild-update.1 $(DESTDIR)$(man1dir)/sbuild-upgrade.1
+ ln -sf sbuild-update.1 $(DESTDIR)$(man1dir)/sbuild-distupgrade.1
+ ln -sf sbuild-update.1 $(DESTDIR)$(man1dir)/sbuild-clean.1
+ ln -sf buildd-mail.1 $(DESTDIR)$(man1dir)/buildd-mail-wrapper.1
+ ln -sf sbuild-abort.1 $(DESTDIR)$(man1dir)/buildd-abort.1
+ ln -sf sbuild-update.1 $(DESTDIR)$(man1dir)/buildd-update-chroots.1
+ ln -sf sbuild-createchroot.8 $(DESTDIR)$(man8dir)/buildd-make-chroot.8
+
+uninstall-hook:
+ $(RM) $(DESTDIR)$(man1dir)/sbuild-unhold.1
+ $(RM) $(DESTDIR)$(man1dir)/sbuild-upgrade.1
+ $(RM) $(DESTDIR)$(man1dir)/sbuild-distupgrade.1
+ $(RM) $(DESTDIR)$(man1dir)/sbuild-clean.1
+ $(RM) $(DESTDIR)$(man1dir)/buildd-mail-wrapper.1
+ $(RM) $(DESTDIR)$(man1dir)/buildd-abort.1
+ $(RM) $(DESTDIR)$(man1dir)/buildd-update-chroots.1
+ $(RM) $(DESTDIR)$(man8dir)/buildd-make-chroot.8
diff --git a/man/buildd-mail.1.in b/man/buildd-mail.1.in
new file mode 100644
index 0000000..4d57a37
--- /dev/null
+++ b/man/buildd-mail.1.in
@@ -0,0 +1,88 @@
+.\" 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/>.
+.so defs.man
+.TH BUILDD\-MAIL 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+buildd\-mail \- mail answer processor for buildd
+.SH SYNOPSIS
+.B buildd\-mail
+.br
+.B buildd\-mail\-wrapper
+.SH VERSION
+This man page documents the packaged version of buildd\-mail. This version
+is maintained by the \fBbuildd-tools\fP project developers on Alioth
+(\f[CR]https://alioth.debian.org/projects/buildd\-tools/\fP).
+.PP
+There is also a version maintained by Ryan Murray which is adapted to build
+daemon needs, with similar functionality. This version should be equally
+capable of working in a buildd setup, but has a number of enhancements aimed at
+making it suitable for use by end-users.
+.SH DESCRIPTION
+\fBbuildd\-mail\fR processes incoming mail for the buildd user.
+.PP
+\fBbuildd\-mail\-wrapper\fR was a setuid wrapper for buildd\-mail, which has
+been removed and is no longer required. It has been kept as a symbolic link to
+buildd\-mail for compatibility purposes, but is obsolete and will removed in a
+future release.
+.SH SETUP
+The buildd user should have a file \fI~/.forward\fP with the following
+contents:
+.PP
+\f[CR]|/usr/bin/buildd-mail-wrapper\fP
+.br
+.PP
+This will ensure all incoming mail will be processed using buildd\-mail.
+.SH FILES
+.TP
+.I /etc/buildd/buildd.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.builddrc
+User-specific configuration.
+.TP
+.I ~/.forward
+Mail forwarding configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2008 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR buildd (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/buildd-uploader.1.in b/man/buildd-uploader.1.in
new file mode 100644
index 0000000..85f0b64
--- /dev/null
+++ b/man/buildd-uploader.1.in
@@ -0,0 +1,70 @@
+.\" 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/>.
+.so defs.man
+.TH BUILDD\-UPLOADER 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+buildd\-uploader \- upload built packages for buildd
+.SH SYNOPSIS
+.B buildd\-uploader
+.SH VERSION
+This man page documents the packaged version of buildd\-uploader. This version
+is maintained by the \fBbuildd-tools\fP project developers on Alioth
+(\f[CR]https://alioth.debian.org/projects/buildd\-tools/\fP).
+.PP
+There is also a version maintained by Ryan Murray which is adapted to build
+daemon needs, with similar functionality. This version should be equally
+capable of working in a buildd setup, but has a number of enhancements aimed at
+making it suitable for use by end-users.
+.SH DESCRIPTION
+\fBbuildd\-uploader\fR uploads built packages.
+.SH FILES
+.TP
+.I /etc/buildd/buildd.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.builddrc
+User-specific configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2008 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR buildd (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/buildd-vlog.1.in b/man/buildd-vlog.1.in
new file mode 100644
index 0000000..d47944b
--- /dev/null
+++ b/man/buildd-vlog.1.in
@@ -0,0 +1,72 @@
+.\" 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/>.
+.so defs.man
+.TH BUILDD\-VLOG 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+buildd\-vlog \- view current build log
+.SH SYNOPSIS
+.B buildd\-vlog
+.SH VERSION
+This man page documents the packaged version of buildd\-vlog. This version
+is maintained by the \fBbuildd-tools\fP project developers on Alioth
+(\f[CR]https://alioth.debian.org/projects/buildd\-tools/\fP).
+.PP
+There is also a version maintained by Ryan Murray which is adapted to build
+daemon needs, with similar functionality. This version should be equally
+capable of working in a buildd setup, but has a number of enhancements aimed at
+making it suitable for use by end-users.
+.SH DESCRIPTION
+\fBbuildd\-vlog\fR monitors the build currently in progress and outputs the
+log file during the build in a manner similar to
+.BR tail (1) .
+.SH FILES
+.TP
+.I /etc/buildd/buildd.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.builddrc
+User-specific configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2008 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR buildd (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/buildd-watcher.1.in b/man/buildd-watcher.1.in
new file mode 100644
index 0000000..15ddbff
--- /dev/null
+++ b/man/buildd-watcher.1.in
@@ -0,0 +1,72 @@
+.\" 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/>.
+.so defs.man
+.TH BUILDD\-WATCHER 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+buildd\-watcher \- watch buildd activity
+.SH SYNOPSIS
+.B buildd\-watcher
+.SH VERSION
+This man page documents the packaged version of buildd\-watcher. This version
+is maintained by the \fBbuildd-tools\fP project developers on Alioth
+(\f[CR]https://alioth.debian.org/projects/buildd\-tools/\fP).
+.PP
+There is also a version maintained by Ryan Murray which is adapted to build
+daemon needs, with similar functionality. This version should be equally
+capable of working in a buildd setup, but has a number of enhancements aimed at
+making it suitable for use by end-users.
+.SH DESCRIPTION
+\fBbuildd\-watcher\fR monitors the activity of the buildd daemon and restarts
+it if required. It additionally cleans up old files, and rotates and archives
+log files.
+.SH FILES
+.TP
+.I /etc/buildd/buildd.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.builddrc
+User-specific configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2008 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR buildd (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/buildd.1.in b/man/buildd.1.in
new file mode 100644
index 0000000..26d7009
--- /dev/null
+++ b/man/buildd.1.in
@@ -0,0 +1,75 @@
+.\" 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/>.
+.so defs.man
+.TH BUILDD 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+buildd \- package autobuilder daemon
+.SH SYNOPSIS
+.B buildd
+.SH VERSION
+This man page documents the packaged version of buildd. This version
+is maintained by the \fBbuildd-tools\fP project developers on Alioth
+(\f[CR]https://alioth.debian.org/projects/buildd\-tools/\fP).
+.SH DESCRIPTION
+\fBbuildd\fR is a daemon used to automatically build Debian packages from
+source. wanna\-build is used to find and claim packages for building, while
+sbuild is used to build the packages. The helper programs buildd\-mail,
+buildd\-uploader and buildd\-watcher are used to perform various housekeeping
+tasks to keep the system running.
+.SH FILES
+.TP
+.I /etc/buildd/buildd.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.builddrc
+User-specific configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2008 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR buildd.conf (5),
+.BR buildd\-mail (1),
+.BR buildd\-uploader (1),
+.BR buildd\-watcher (1),
+.BR buildd\-mail (1),
+.BR wanna\-build (1),
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/buildd.conf.5.in b/man/buildd.conf.5.in
new file mode 100644
index 0000000..b00003d
--- /dev/null
+++ b/man/buildd.conf.5.in
@@ -0,0 +1,75 @@
+.\" Copyright © 2011 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/>.
+.so defs.man
+.TH BUILDD.CONF 5 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+buildd.conf \- configuration file for buildd
+.SH DESCRIPTION
+\fBbuildd.conf\fR is a Perl script which is sourced by buildd to permit
+configuration. It is run in a sandbox and restricted to setting a small number
+of variables, detailed below, which may be set to configure the behaviour of
+buildd. Each variable has an internal name, shown in all caps, and a perl
+variable showing the default setting. Note that the internal name is not
+accessible within buildd.conf; only the variable may be assigned. Also note
+that some of the defaults are set to be unique for each build, and so some of
+the defaults shown here are unique to the user and system used to build sbuild,
+and will be different for your system.
+.PP
+While buildd permits many aspects of its behaviour to be configured, this
+should not normally be required. The defaults should be adequate for most
+uses, and should only be changed if you are doing something more advanced, or
+have specialist requirements.
+.SH CONFIGURATION
+.so buildd.conf.man
+.SH FILES
+.TP
+.I /etc/buildd/buildd.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.builddrc
+User-specific configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2009 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR buildd (1),
+.BR sbuild (1),
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/defs.man.in b/man/defs.man.in
new file mode 100644
index 0000000..c553ecc
--- /dev/null
+++ b/man/defs.man.in
@@ -0,0 +1,19 @@
+.ds WANNA_BUILD_CONF @WANNA_BUILD_CONF@
+.ds SCHROOT_CONF @SCHROOT_CONF@
+.ds WANNA_BUILD_SYSCONF_DIR @WANNA_BUILD_SYSCONF_DIR@
+.ds SCHROOT_SYSCONF_DIR @SCHROOT_SYSCONF_DIR@
+.ds BUILDD_CONF @BUILDD_CONF@
+.ds BUILDD_SYSCONF_DIR @BUILDD_SYSCONF_DIR@
+.ds SBUILD_CONF @SBUILD_CONF@
+.ds SBUILD_SYSCONF_DIR @SBUILD_SYSCONF_DIR@
+.ds WANNA_BUILD_LIBEXEC_DIR @WANNA_BUILD_LIBEXEC_DIR@
+.ds SBUILD_LIBEXEC_DIR @SBUILD_LIBEXEC_DIR@
+.ds RELEASE_DATE @RELEASE_DATE@
+.ds VERSION @VERSION@
+.ds PACKAGE @PACKAGE@
+.ds PACKAGE_URL @PACKAGE_URL@
+.ds PACKAGE_BUGREPORT @PACKAGE_BUGREPORT@
+.ds PACKAGE_STRING @PACKAGE_STRING@
+.ds PACKAGE_VERSION @PACKAGE_VERSION@
+.ds PACKAGE_TARNAME @PACKAGE_TARNAME@
+.ds PACKAGE_NAME @PACKAGE_NAME@
diff --git a/man/sbuild-abort.1.in b/man/sbuild-abort.1.in
new file mode 100644
index 0000000..e2e87de
--- /dev/null
+++ b/man/sbuild-abort.1.in
@@ -0,0 +1,61 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-ABORT 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-abort \- abort the current sbuild build
+.SH SYNOPSIS
+.BR sbuild\-abort
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.br
+.BR buildd\-abort
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.SH DESCRIPTION
+\fBsbuild\-abort\fR kills the currently running \fIdpkg\-buildpackage\fP
+process. It will not kill if more than one process is found.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SH EXAMPLES
+To abort the current build:
+.PP
+\f[CR]% \f[CB]sbuild\-abort\fP\fP\[CR]
+.br
+.SH AUTHORS
+.nf
+Roman Hodek.
+Ryan Murray.
+Roger Leigh.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998 Roman Hodek <Roman.Hodek@informatik.uni-erlangen.de>.
+Copyright \[co] 2003 Ryan Murray <rmurray@debian.org>.
+Copyright \[co] 2006\[en]2008 Roger Leigh <rleigh@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-adduser.8.in b/man/sbuild-adduser.8.in
new file mode 100644
index 0000000..174d52a
--- /dev/null
+++ b/man/sbuild-adduser.8.in
@@ -0,0 +1,71 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-ADDUSER 8 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-adduser \- add an sbuild user
+.SH SYNOPSIS
+.B sbuild\-adduser
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.BR USER1 " [" USER2 " [" USER\f[BI]n\fP... ]]
+.SH DESCRIPTION
+\fBsbuild\-adduser\fR adds the specified users to the \fIsbuild\fP group,
+granting them the privilege to build packages with
+.BR sbuild (1).
+The
+.BR schroot (1)
+configuration must permit the user to obtain root permissions in the chroot via
+the \f[CI]root\-users\fP and \f[CI]root\-groups\fP options in
+\fIschroot.conf\fP, because this is required to install and remove
+build-dependencies inside the chroot. This is normally done using the
+\fIsbuild\fP group, but additional users or groups may be added in order to
+grant finer-grained permissions, for example to restrict individual users to
+particular chroots.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SS User selection
+.TP
+.B USER
+The user to be granted sbuild access.
+.SH EXAMPLES
+To allow user \fIrleigh\fP sbuild access:
+.PP
+\f[CR]% \f[CB]sudo sbuild\-adduser rleigh\fP\fP
+.br
+.SH AUTHORS
+.nf
+Roger Leigh.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2006\[en]2008 Roger Leigh <rleigh@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1),
+.BR sbuild\-setup (7),
+.BR schroot.conf (5).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-apt.1.in b/man/sbuild-apt.1.in
new file mode 100644
index 0000000..bb3eb34
--- /dev/null
+++ b/man/sbuild-apt.1.in
@@ -0,0 +1,84 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-APT 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-apt \- run apt-get or apt-cache in an sbuild chroot
+.SH SYNOPSIS
+.BR sbuild\-apt
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.B CHROOT
+.RB ( apt\-get \[or] apt\-cache )
+.RB "[" ARG1 " [" ARG2 " [" "ARG\f[BI]n\fP..." ]]]
+.SH DESCRIPTION
+\fBsbuild\-apt\fR runs
+.BR apt\-get (1)
+or
+.BR apt\-cache (1)
+inside the specified chroot, along with any additional arguments.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SS Chroot selection
+.TP
+.B CHROOT
+The chroot to run the command in. Note that \[oq]o\[cq], \[oq]s\[cq],
+\[oq]t\[cq], \[oq]u\[cq] and \[oq]e\[cq] may be used as abbreviations for
+\[oq]oldstable\[cq], \[oq]stable\[cq], \[oq]testing\[cq], \[oq]unstable\[cq]
+and \[oq]experimental\[cq], respectively.
+.SS APT options
+.TP
+.B apt\-get
+Run apt\-get.
+.TP
+.B apt\-cache
+Run apt\-cache.
+.TP
+.B ARG
+Additional arguments to pass to apt\-get or apt\-cache.
+.SH EXAMPLES
+To update the APT package lists in the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-apt unstable apt-get update\fP\fP
+.PP
+Note that
+.BR sbuild-update (1)
+is a more convenient method for doing the above.
+.SH AUTHORS
+.nf
+Roger Leigh.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2006\[en]2008 Roger Leigh <rleigh@debian.org>.
+.fi
+.SH SEE ALSO
+.BR apt\-cache (1),
+.BR apt\-get (1),
+.BR sbuild (1),
+.BR sbuild\-update (1),
+.BR sbuild\-upgrade (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-checkpackages.1.in b/man/sbuild-checkpackages.1.in
new file mode 100644
index 0000000..a393ef8
--- /dev/null
+++ b/man/sbuild-checkpackages.1.in
@@ -0,0 +1,81 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-CHECKPACKAGES 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-checkpackages \- check the package list in a sbuild chroot against a reference list
+.SH SYNOPSIS
+.BR sbuild\-checkpackages
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version
+.RB " \[or] " \-l \[or] \-\-list " \[or] " \-s \[or] \-\-set ]
+.B CHROOT
+.SH DESCRIPTION
+\fBsbuild\-checkpackages\fR compares the package list in an sbuild chroot (dpkg
+status file) against a reference list of packages. The differences between the
+two lists are displayed, showing the changes to remove and install in order to
+return the chroot to match the reference list.
+.PP
+The reference list may be set from the dpkg status file; this must be done
+initially in order to have a baseline reference for comparison. The chroot
+should only include \fBessential\fP and \fBbuild-essential\fP packages, plus
+the extra packages that sbuild needs such as \fBfakeroot\fP.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.TP
+.BR \-l ", " \-\-list
+List the differences between the installed packages and the reference file.
+.TP
+.BR \-s ", " \-\-set
+Set the reference file from the currently installed package list.
+.SS Chroot selection
+.TP
+.B CHROOT
+The chroot to use. Note that \[oq]o\[cq], \[oq]s\[cq],
+\[oq]t\[cq], \[oq]u\[cq] and \[oq]e\[cq] may be used as abbreviations for
+\[oq]oldstable\[cq], \[oq]stable\[cq], \[oq]testing\[cq], \[oq]unstable\[cq]
+and \[oq]experimental\[cq], respectively.
+.SH EXAMPLES
+To set the reference file in the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-checkpackages unstable \-\-set\fP\fP
+.br
+.PP
+To show the differences in the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-checkpackages unstable \-\-list\fP\fP
+.br
+.SH AUTHORS
+.nf
+Roger Leigh.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2006\[en]2008 Roger Leigh <rleigh@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-createchroot.8.in b/man/sbuild-createchroot.8.in
new file mode 100644
index 0000000..23b4b4d
--- /dev/null
+++ b/man/sbuild-createchroot.8.in
@@ -0,0 +1,365 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-CREATECHROOT 8 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-createchroot \- create sbuild chroot
+.SH SYNOPSIS
+.B sbuild\-createchroot
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.RB [ \-\-verbose ]
+.RB [ \-\-arch=\fIarch\fP ]
+.RB [ \-\-chroot-prefix=\fIprefix\fP ]
+.RB [ \-\-chroot-suffix=\fIsuffix\fP ]
+.RB [ \-\-chroot-mode=\fIschroot|sudo|unshare\fP ]
+.RB [ \-\-foreign ]
+.RB [ \-\-resolve-deps " \[or] " \-\-no-resolve-deps ]
+.RB [ \-\-merged-usr " \[or] " \-\-no-merged-usr " \[or] " \-\-auto-merged-usr ]
+.RB [ \-\-keep-debootstrap-dir ]
+.RB [ \-\-debootstrap=\fIdebootstrap\fP ]
+.RB [ "\-\-include=\fIpackage1[,package2,[packagen]]\fP" ]
+.RB [ "\-\-exclude=\fIpackage1[,package2,[packagen]]\fP" ]
+.RB [ "\-\-components=\fIcomponent1[,component2,[componentn]]\fP" ]
+.RB [ "\-\-keyring=\fIkeyring-file\fP" ]
+.RB [ "\-\-setup\-only" ]
+.RB [ "\-\-make\-sbuild\-tarball=\fIfile\fP" ]
+.RB [ "\-\-keep\-sbuild\-chroot\-dir" ]
+.RB [ "\-\-no\-deb\-src" ]
+.RB [ "\-\-alias=\fIalias\fP" ]
+.RB [ \-\-extra\-repository=\fIspec\fP ]
+.RB [ "\-\-command\-prefix=\fIprefix\fP" ]
+.B SUITE TARGET-DIRECTORY [DEBIAN-MIRROR-URI
+.RB [ SCRIPT ]]
+.PP
+.B buildd\-create\-chroot
+.B USER SUITE TARGET-DIRECTORY [DEBIAN-MIRROR-URI]
+.SH DESCRIPTION
+\fBsbuild\-createchroot\fR runs
+.BR debootstrap (1)
+to create a chroot suitable for building packages with sbuild. Note that while
+debootstrap may be used directly, sbuild\-createchroot performs additional
+setup tasks such as adding additional packages and configuring various files in
+the chroot. Invoking sbuild\-createchroot is functionally equivalent to
+running \f[CB]debootstrap\ \-\-variant=buildd
+\-\-include=fakeroot,build\-essential\fP, then editing
+\fI/etc/apt/sources.list\fP and \fI/etc/hosts\fP by hand.
+.PP
+The newly-created chroot is set up to work with
+.BR schroot (1)
+by creating a chroot definition file under \fI/etc/schroot/chroot.d\fP. This
+should be edited to finish the chroot configuration after
+sbuild\-createchroot has completed.
+.PP
+\fBbuildd\-create\-chroot\fP, like \fBsbuild\-createchroot\fR, runs
+.BR debootstrap (1)
+to create a chroot for package building. However, this creates a chroot
+specifically for
+.BR buildd (1)
+with an additional mandatory option to specify a user who will be granted sudo
+access inside the chroot. This script mainly differs in its additional
+customisation of \fI/etc/apt/sources.list\fP to use incoming.debian.org and
+security-master.debian.org. It also removes some packages from the
+newly-created chroot. Unless you are setting up an official Debian build
+daemon, \fBsbuild\-createchroot\fP should be used instead. The extra
+functionality will be merged into \fBsbuild\-createchroot\fP in the future.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SS General options
+.TP
+.BR \-v ", " \-\-verbose
+Print all messages.
+.SS Debootstrap options
+Note that
+.BR debootstrap (1)
+is the canonical reference for the meaning of the options; they are passed
+directly to debootstrap.
+.TP
+.BR \-\-arch=\fIarch\fP
+Set the target architecture. This may be used if dpkg is not already
+installed. See also \fI\-\-foreign\fP, below.
+.TP
+.BR \-\-chroot-suffix=\fIsuffix\fP
+Add a custom suffix to the chroot name. Defaults to '\-sbuild'.
+.TP
+.BR \-\-chroot\-prefix=\fIprefix\fP
+Add a custom prefix to the chroot name. Defaults to SUITE. This is useful to
+create variants of a base suite like stretch-backports to which the backports
+archive can be manually added after the base chroot was created using
+sbuild-shell. This way, a normal stretch chroot and a stretch-backports chroot
+can exist side-by-side. Besides this common use case, this functionality can
+also be used to create chroots with any other type of customization. It is a
+shortcut for creating a base chroot and then having to manually copy it and
+edit the configuration files.
+.TP
+.BR \-\-chroot-mode=\fIschroot|sudo|unshare\fP
+The sbuild chroot backend to generate the chroot for. The autopkgtest backend
+is not supported by this method because of the diversity of container types it
+supports. To generate a chroot for schroot and sudo, sbuild-createchroot must
+be executed with superuser privileges (for example by using sudo). Because of
+backwards compatibility, choosing schroot implies sudo (but not the other way
+round). Choosing unshare requires the \fI\-\-make\-sbuild\-tarball\fP option.
+Creating a chroot for the unshare backend can be done by a normal user (without
+sudo) but it requires Linux user namespaces to be enabled (via "sysctl -w
+kernel.unprivileged_userns_clone=1"). Chroot tarballs created for the unshare
+backend are also compatible with the schroot backend if a respective schroot
+config file is manually created by the user or by running sbuild-createchroot
+with the \fI--setup-only\fP option. Defaults to 'schroot'.
+.TP
+.BR \-\-foreign
+Only perform the initial unpack phase of bootstrapping. This is required if
+the target and host architectures do not match. Note that debootstrap requires
+running by hand to complete installation; run the debootstrap
+\fI/debootstrap/debootstrap\fP installed in \fITARGET-DIRECTORY\fP to complete
+the installation.
+.TP
+.BR \-\-resolve-deps
+Automatically resolve missing dependencies. This is the default.
+.TP
+.BR \-\-no-resolve-deps
+Do not automatically resolve missing dependencies.
+.TP
+.BR \-\-keep-debootstrap-dir
+Don't delete the \fI/debootstrap\fP directory in \fITARGET-DIRECTORY\fP after
+completing the installation.
+.TP
+.BR \-\-debootstrap=\fIdebootstrap\fP
+Define a custom debootstrap variant. Defaults to 'debootstrap'.
+.TP
+.BR "\-\-include=\fIpackage1[,package2,[packagen]]\fP"
+Comma separated list of packages which will be added to the list of packages to
+download and extract.
+.TP
+.BR "\-\-exclude=\fIpackage1[,package2,[packagen]]\fP"
+Comma separated list of packages which will be removed from the list of
+packages to download and extract. Note that this can remove essential packages,
+so use with extreme care.
+.TP
+.BR "\-\-components=\fIcomponent1[,component2,[componentn]]\fP"
+Comma separated list of archive components to use (e.g. \[oq]main\[cq],
+\[oq]contrib\[cq], \[oq]non-free\[cq]). Defaults to \[oq]main\[cq].
+.TP
+.BR "\-\-keyring=\fIkeyring-file\fP"
+Download signatures for retrieved \fIRelease\fP files and check them against
+\fIkeyring-file\fP. By default \fI/etc/apt/trusted.gpg\fP is used. Set to an
+empty string to disable signature checking.
+.TP
+.BR \-\-merged-usr
+Create a chroot in which \fI/bin\fP, \fI/sbin\fP and \fI/lib*\fP are
+symbolic links to their counterparts in \fI/usr\fP.
+.TP
+.BR \-\-no-merged-usr
+Create a chroot in which \fI/bin\fP, \fI/sbin\fP and \fI/lib*\fP are
+ordinary directories distinct from their counterparts in \fI/usr\fP.
+.TP
+.BR \-\-auto-merged-usr
+Do not specify whether \fI/bin\fP, \fI/sbin\fP and \fI/lib*\fP are
+symbolic links to their counterparts in \fI/usr\fP. In this case
+debootstrap will use its default behaviour (which is suite-specific).
+This is the default.
+.TP
+.B SUITE
+The distribution to bootstrap (e.g. \[oq]stretch[cq], \[oq]buster\[cq],
+\[oq]bullseye\[cq], \[oq]sid\[cq]). A complete list may be found in
+\fI/usr/share/debootstrap/scripts\fP.
+.TP
+.B TARGET-DIRECTORY
+The directory to create the chroot in. The directory will be created if it
+does not already exist.
+.TP
+.B DEBIAN-MIRROR-URI
+An \f[CR]http://\fP, \f[CR]file:///\fP, or \f[CR]ssh:///\fP URI
+pointing to a suitable archive mirror. Defaults to http://deb.debian.org/debian.
+.TP
+.B SCRIPT
+debootstrap script to run. Not typically required.
+.SS sbuild\-createchroot behaviour
+.TP
+.BR "\-\-setup\-only"
+Don't run debootstrap. Only perform the setup tasks on an already existing
+chroot. This is useful for converting an existing chroot for use with sbuild
+which has already been created using a tool such as debootstrap.
+Some configuration steps can only be carried out on a directory chroot and
+thus, this option is not useful with the unshare backend.
+.TP
+.BR \-\-make\-sbuild\-tarball=\fIfile\fP
+Create a bootstrapped file type chroot ready for use with sbuild and save it as
+\fIfile\fP. The compression format used for the tarball is dependent on the file
+extension used in \fIfile\fP. See the \fBTARBALL FILE\fP section for more
+details.
+.TP
+.BR \-\-keep\-sbuild\-chroot\-dir
+Don't delete the directory used for creating a file type chroot. This option
+does nothing if not creating a file type chroot.
+.TP
+.BR \-\-no\-deb\-src
+Don't add a deb-src line to the \fI/etc/apt/sources.list\fP file in the
+\fITARGET-DIRECTORY\fP after the debootstrap process. This is useful in
+situation when it is known that sbuild will never have to download the source
+package itself but is always given an already downloaded dsc. In that case,
+this option will help to save bandwidth and disk space because the source
+indices don't have to be downloaded and later continually updated.
+.TP
+.BR \-\-alias=\fIalias\fP
+Add an alternative name that the chroot will be known by. This option can be
+given multiple times to add more than one alias. Using an alias is useful for
+chroots of distributions that are known by more than one name. For example
+Debian unstable is also known as sid. Additionally, sbuild chooses the
+distribution by the latest changelog entry which could list \fIUNRELEASED\fP
+for packages that the maintainer is currently working on. For Debian it thus
+makes sense to add \fIUNRELEASED\fP as an alias for a Debian unstable chroot.
+This option is only allowed when choosing \fI--chroot-mode=schroot\fP.
+For similar functionality with \fI--chroot-mode=unshare\fP you can work with
+symlinks.
+See the EXAMPLES section for how to use this option in practice.
+.TP
+.BR \-\-extra\-repository=\fIspec\fP
+Add a repository to the list of apt sources. The repository specification is a
+line suitable for an apt
+.BR sources.list (5)
+file. For instance, you might use
+.nh
+.B \-\-extra\-repository="deb http://deb.debian.org/debian experimental main"
+.hy
+to allow packages in the experimental distribution to fulfill
+build-dependencies. Note that the build chroot must already trust the key of
+this repository. See the EXAMPLES section for how to combine this option with
+--chroot-prefix and --alias.
+.TP
+.BR \-\-command\-prefix=\fIprefix\fP
+Set the chroot \fIcommand-prefix\fP option as specified. A common use-case is
+to specify eatmydata, thereby preventing all commands executed in the chroot
+from syncing data to disk. This option is only allowed when choosing
+\fI--chroot-mode=schroot\fP. See
+.BR schroot.conf (5)
+for more details.
+.SH TARBALL FILE
+When creating an sbuild tarball \fIfile\fP, the compression format used to
+generate the tarball depends on the entension used in \fIfile\fP. Here is a
+listing of the extensions that will be detected and what format the tarball will
+be generated as.
+.TP
+.BR *.tar
+Generates an uncompressed tarball.
+.TP
+.BR *.tar.gz|*.tgz
+Generates a compressed tarball using gzip.
+.TP
+.BR *.tar.bz2|*.tbz
+Generates a compressed tarball using bzip2.
+.TP
+.BR *.tar.lz|*.tlz
+Generates a compressed tarball using lzip.
+.TP
+.BR *.tar.xz|*.txz
+Generates a compressed tarball using xz.
+.PP
+If no extension is specified, sbuild\-createchroot will rename \fIfile\fP to
+\fIfile.tar.gz\fP and generate \fIfile.tar.gz\fP as a compressed tarball using gzip.
+.SH EXAMPLES
+To create a bootstrapped file type sid (unstable) chroot ready for use with
+sbuild and schroot (the default backend), saved in
+\fI/srv/chroot/unstable-amd64.tar.gz\fP using the
+\fIdeb.debian.org\fP Debian http mirror redirector service and using a
+temporary directory as the target:
+.PP
+\f[CR]% \f[CB]sudo sbuild\-createchroot \fP\fP\\
+.br
+ \-\-make\-sbuild\-tarball=/srv/chroot/unstable-amd64.tar.gz \fP\fP\\
+.br
+ unstable `mktemp \-d` \fP\fP\\
+.br
+\f[CB] http://deb.debian.org/debian\fP\[CR]
+.PP
+To create a plain type sid (unstable) schroot chroot in
+\fI/srv/chroot/unstable\fP using the \fIdeb.debian.org\fP Debian http
+mirror redirector service and with aliases for \fIunstable\fP and
+\fIUNRELEASED\fP:
+.PP
+\f[CR]% \f[CB]sudo sbuild\-createchroot unstable /srv/chroot/unstable-amd64 \fP\fP\\
+.br
+ \-\-alias=sid \-\-alias=UNRELEASED \fP\fP\\
+.br
+\f[CB] http://deb.debian.org/debian\fP\[CR]
+.PP
+To create a schroot chroot to build for stretch backports:
+.PP
+\f[CR]% \f[CB]sudo sbuild\-createchroot \fP\fP\\
+.br
+ \-\-extra\-repository="deb http://deb.debian.org/debian stretch-backports main" \fP\fP\\
+.br
+ \-\-chroot-prefix=stretch-backports \fP\fP\\
+.br
+ stretch /srv/chroot/stretch-backports-amd64 \fP\fP\\
+.br
+\f[CB] http://deb.debian.org/debian\fP\[CR]
+.PP
+To create a schroot chroot to build for experimental using a custom chroot prefix:
+.PP
+\f[CR]% \f[CB]sudo sbuild\-createchroot \fP\fP\\
+.br
+ \-\-extra\-repository="deb http://deb.debian.org/debian experimental main" \fP\fP\\
+.br
+ \-\-chroot-prefix=experimental \fP\fP\\
+.br
+ unstable /srv/chroot/unstable-experimental-amd64 \fP\fP\\
+.br
+\f[CB] http://deb.debian.org/debian\fP\[CR]
+.PP
+Creating a chroot for the unshare backend does not require superuser privileges
+(no sudo). The path for the tarball is the default path expected by sbuild for
+the unshare backend:
+.PP
+\f[CR]% \f[CB]sbuild-createchroot --chroot-mode=unshare \\
+.br
+ --make-sbuild-tarball ~/.cache/sbuild/unstable-amd64.tar.gz \\
+.br
+ unstable `mktemp -d` http://deb.debian.org/debian/ \\
+.PP
+.SH HISTORY
+sbuild\-createchroot was previously known as \fBbuildd.chroot\fP.
+buildd.chroot performed exactly the same tasks, but additionally created a
+number of directories in the chroot for use by sbuild. This is now done by
+.BR schroot (1).
+.SH AUTHORS
+.nf
+Roger Leigh.
+Francesco P. Lovergine.
+Andres Mejia.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2004 Francesco P. Lovergine <frankie@debian.org>.
+Copyright \[co] 2007\-2008 Roger Leigh <rleigh@debian.org>.
+Copyright \[co] 2010 Andres Mejia <mcitadel@gmail.com>.
+.fi
+.SH "SEE ALSO"
+.BR debootstrap (8),
+.BR schroot (1),
+.BR sbuild (1),
+.BR sbuild\-setup (7).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
+
diff --git a/man/sbuild-debian-developer-setup.1.in b/man/sbuild-debian-developer-setup.1.in
new file mode 100644
index 0000000..85084ae
--- /dev/null
+++ b/man/sbuild-debian-developer-setup.1.in
@@ -0,0 +1,78 @@
+.\" Copyright © 2018 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/>.
+.so defs.man
+.TH SBUILD\-DEBIAN\-DEVELOPER\-SETUP 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-debian\-developer\-setup \- set up sbuild so that packages for Debian unstable can be built
+.SH SYNOPSIS
+.BR sbuild\-debian\-developer\-setup
+.RB [ \-h \[or] \-\-help ]
+.RB [ \-\-distribution=\fIdistribution\fP ]
+.RB [ \-\-suite=\fIsuite\fP ]
+.RB [ \-\-arch=\fIarch\fP ]
+.SH DESCRIPTION
+
+\fBsbuild-debian-developer-setup\fR is a convenience script to set up an sbuild
+environment for Debian Developers.
+
+When run, it adds the current user to the sbuild group, creates a schroot for
+building packages for Debian unstable in
+\fI/srv/chroot/unstable-$arch-sbuild\fP, and creates a cronjob which updates
+said schroot daily.
+
+The script assumes you are on an un-metered internet connection (daily schroot
+updates might be costly otherwise).
+.SH OPTIONS
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.TP
+.BR "\-\-distribution=\fIdistribution\fP"
+Set up an sbuild chroot for \fIdistribution\fP. Defaults to "debian".
+.TP
+.BR "\-\-suite=\fIsuite\fP"
+Set up an sbuild chroot for \fIsuite\fP. Defaults to "unstable".
+.TP
+.BR "\-\-arch=\fIarch\fP"
+Set up an sbuild chroot with architecture \fIarch\fP. Defaults to system arch.
+.SH EXAMPLES
+To set up sbuild and build the hello world Debian package, use:
+.PP
+\f[CR]% \f[CB]sudo sbuild\-debian\-developer\-setup\fP\fP\[CR]
+.br
+\f[CR]% \f[CB]newgrp sbuild\[CR]
+.br
+\f[CR]% \f[CB]sbuild -d unstable hello\[CR]
+.br
+.SH AUTHORS
+.nf
+Michael Stapelberg.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2018 Michael Stapelberg <stapelberg@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-debuild.1.in b/man/sbuild-debuild.1.in
new file mode 100644
index 0000000..fd28ecc
--- /dev/null
+++ b/man/sbuild-debuild.1.in
@@ -0,0 +1,196 @@
+.\" 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/>.
+.so defs.man
+.TH SBUILD-DEBUILD 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-debuild \- build debian packages from source in debuild\-like manner
+.SH SYNOPSIS
+.B sbuild\-debuild
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.RB [ \-\-dpkg\-buildpackage\-opts " " \fIstring\fP ]
+.RB [ \-\-dpkg\-buildpackage\-extra\-opts " " \fIstring\fP ]
+.RB [ \-\-sbuild\-opts " " \fIstring\fP ]
+.RB [ \-\-sbuild\-extra\-opts " " \fIstring\fP ]
+.RB [ \-\-lintian\-opts " " \fIstring\fP ]
+.RB [ \-\-lintian\-extra\-opts " " \fIstring\fP ]
+.RB [ \-\-no\-lintian ]
+.RB [ \-\-pre\-dpkg\-buildpackage\-commands " " \fIstring\fP ]
+.RB [ \-\-pre\-sbuild\-commands " " \fIstring\fP ]
+.RB [ \-\-pre\-lintian\-commands " " \fIstring\fP ]
+.RB [ \-\-pre\-exit\-commands " " \fIstring\fP ]
+.BR [PACKAGE1[.dsc] " [" PACKAGE2[.dsc] " [" PACKAGE\f[BI]n\fP[.dsc] ]]]
+.SH DESCRIPTION
+\fBsbuild\-debuild\fR rebuilds Debian binary packages from the corresponding
+Debian source in a manner similar to debuild.
+.PP
+sbuild\-debuild is given a list of packages to process as the arguments
+\fBPACKAGE\f[BI]i\fP[.dsc]\fR. These arguments are in the form of either
+debianized package source directories or a .dsc file.
+.PP
+For arguments given as source directories, dpkg-buildpackage is first run to
+produce a source .dsc file. Then, the package is built using the .dsc produced.
+For arguments given as .dsc files, running dpkg-buildpackage is skipped
+altogether. If no arguments are given, sbuild-debuild acts on the current
+working directory as a source directory to process.
+.PP
+Before a package is built, sbuild-debuild first changes into the directory where
+the corresponding .dsc file resides.
+.PP
+After a successful build, if lintian is installed on the system, lintian is run
+on the resulting .changes file produced after a build.
+.PP
+In between each run of dpkg-buildpackage, sbuild, and lintian for a package,
+a list of commands can be run, similar to running hooks, except that arguments
+can be given and "special" keywords can be given as an argument to these
+commands to have sbuild-debuild convert them into various arguments. See the
+section \fBEXTERNAL COMMANDS\fR for more on this.
+.PP
+The exit status from sbuild-debuild is either 0 or 1. When a package is
+processed, if dpkg-buildpackage, sbuild, or lintian fails, processing of that
+package is stopped immediately and processing of the next package is started
+instead. For external commands, if an external command fails, the next external
+command is run but processing of a package is not halted. If at any point the
+processing of a package failed or an external command failed, sbuild-debuild is
+marked to exit with value 1 (i.e. failure), otherwise it exits with value 0
+(i.e. success).
+.SH OPTIONS
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.TP
+.BR "\-\-dpkg\-buildpackage\-opts \fIstring\fP"
+Override the default options passed to dpkg-buildpackage.
+.br
+Default is \fB"-S -us -uc"\fR.
+.TP
+.BR "\-\-dpkg\-buildpackage\-extra\-opts \fIstring\fP"
+Supply extra options to dpkg-buildpackage.
+.TP
+.BR "\-\-sbuild\-opts \fIstring\fP"
+Override the default options passed to sbuild.
+.br
+Default is no options are given.
+.TP
+.BR "\-\-sbuild\-extra\-opts \fIstring\fP"
+Supply extra options to sbuild.
+.TP
+.BR "\-\-lintian\-opts \fIstring\fP"
+Override the default options passed to lintian.
+.br
+Default is no options are given.
+.TP
+.BR "\-\-lintian\-extra\-opts \fIstring\fP"
+Supply extra options to lintian.
+.TP
+.BR "\-\-no\-lintian"
+Do not run lintian.
+.TP
+.BR "\-\-pre\-dpkg\-buildpackage\-commands \fIstring\fP"
+String of command to run before running dpkg-buildpackage.
+.br
+This option can be supplied multiple times to add more commands.
+.TP
+.BR "\-\-pre\-sbuild\-commands \fIstring\fP"
+String of command to run before running sbuild.
+.br
+This option can be supplied multiple times to add more commands.
+.TP
+.BR "\-\-pre\-lintian\-commands \fIstring\fP"
+String of command to run before running lintian.
+.br
+This option can be supplied multiple times to add more commands.
+.TP
+.BR "\-\-pre\-exit\-commands \fIstring\fP"
+String of command to run before ending the processing of a package.
+.br
+This option can be supplied multiple times to add more commands.
+.SH CONFIG FILES
+.TP
+.I /etc/sbuild/sbuild-debuild.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.sbuild-debuildrc
+User-specific configuration.
+.SH EXTERNAL COMMANDS
+Support to run external commands during an sbuild-debuild run is provided. A set
+of external commands can be run before running dpkg-buildpackage, before running
+sbuild, before running lintian, and before ending processing of a package.
+Providing commands to run is done through the \fI\-\-pre\-*\-commands\fP options
+given on the command line and through the use of the configuration files. In the
+configuration file, the list of commands to run are placed in an array of
+strings corresponding to the commands to run.
+.PP
+Here's an example of how to edit the configuration files to run "foo" and "bar"
+with arguments before dpkg-buildpackage is run.
+.PP
+\f[CB]$pre_dpkg_buildpackage_commands = ['foo arg1 arg2',\fP
+.br
+\f[CB] 'bar arg1 arg2 arg3',\fP
+.br
+\f[CB] ];\fP
+.PP
+Here's an example of how to do the same with the previous example, except using
+the \fI\-\-pre\-dpkg\-buildpackage\-commands\fP option.
+.PP
+\f[CB]$ sbuild\-debuild \\\fP
+.br
+\f[CB] \-\-pre\-dpkg\-buildpackage\-commands 'foo arg1 arg2' \\\fP
+.br
+\f[CB] \-\-pre\-dpkg\-buildpackage\-commands 'bar arg1 arg2 arg3'\fP
+.PP
+Besides running external commands, sbuild\-debuild can also detect the use of
+certain keywords given as arguments. These keywords are used to allow for a
+command to be supplied with a certain argument depending on the keyword given.
+For example, it could be possible to have an external command be given the
+path to a .changes file.
+.PP
+Here is a listing of keywords and a description of what it's converted to.
+.IP \fB\@SBUILD_DEBUILD_DSC\@\fR
+This keyword is converted to the absolute path to a package's .dsc file.
+.IP \fB\@SBUILD_DEBUILD_SOURCE_CHANGES\@\fR
+This keyword is converted to the absolute path to a package's source .changes
+file.
+.IP \fB\@SBUILD_DEBUILD_BIN_CHANGES\@\fR
+This keyword is converted to the absolute path to a package's .changes file
+depending on architecture.
+.PP
+Here's an example of using a keyword to run piuparts on a .changes file before
+the processing of a package ends.
+.PP
+\f[CB]$ sbuild\-debuild \-\-pre\-exit\-commands \\\fP
+.br
+\f[CB] 'piuparts \@SBUILD_DEBUILD_BIN_CHANGES\@'\fP
+.PP
+One final note, external commands are processed in the order they are given.
+Also, the commands given in a configuration file are processed first, then the
+commands given through the command line options.
+.SH AUTHORS
+Andres Mejia.
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2009 Andres Mejia <mcitadel\@gmail.com>
+.fi
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-destroychroot.8.in b/man/sbuild-destroychroot.8.in
new file mode 100644
index 0000000..549b2ff
--- /dev/null
+++ b/man/sbuild-destroychroot.8.in
@@ -0,0 +1,57 @@
+.\" 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/>.
+.so defs.man
+.TH SBUILD\-DESTROYCHROOT 8 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-destroychroot \- destroy sbuild chroot
+.SH SYNOPSIS
+.BR sbuild\-destroychroot
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.B CHROOT
+.SH DESCRIPTION
+\fBsbuild\-destroychroot\fR instructs the user which commands to run to remove
+a chroot as it was created by \fBsbuild\-createchroot\fR. This command will not
+make any changes to the system by itself.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SS Chroot selection
+.TP
+.B CHROOT
+The chroot to use. Note that \[oq]o\[cq], \[oq]s\[cq],
+\[oq]t\[cq], \[oq]u\[cq] and \[oq]e\[cq] may be used as abbreviations for
+\[oq]oldstable\[cq], \[oq]stable\[cq], \[oq]testing\[cq], \[oq]unstable\[cq]
+and \[oq]experimental\[cq], respectively.
+.SH EXAMPLES
+To receive instructions on how to remove an \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-destroychroot unstable\fP\fP\[CR]
+.br
+.SH AUTHORS
+.nf
+Johannes Schauer Marin Rodrigues.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2016 Johannes Schauer Marin Rodrigues <josch@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
diff --git a/man/sbuild-hold.1.in b/man/sbuild-hold.1.in
new file mode 100644
index 0000000..c6b0399
--- /dev/null
+++ b/man/sbuild-hold.1.in
@@ -0,0 +1,71 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-HOLD 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-hold \- hold packages in an sbuild chroot
+.SH SYNOPSIS
+.BR sbuild\-hold
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.B CHROOT
+.RB "[" PACKAGE1 " [" PACKAGE2 " [" PACKAGE\f[BI]n\fP ]]]
+.br
+.BR sbuild\-unhold
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.B CHROOT
+.RB "[" PACKAGE1 " [" PACKAGE2 " [" PACKAGE\f[BI]n\fP ]]]
+.SH DESCRIPTION
+\fBsbuild\-hold\fR and \fBsbuild\-unhold\fR set and unset the dpkg hold status
+for the specified packages, respectively.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SS Chroot and package selection
+.TP
+.B CHROOT
+The chroot to use. Note that \[oq]o\[cq], \[oq]s\[cq],
+\[oq]t\[cq], \[oq]u\[cq] and \[oq]e\[cq] may be used as abbreviations for
+\[oq]oldstable\[cq], \[oq]stable\[cq], \[oq]testing\[cq], \[oq]unstable\[cq]
+and \[oq]experimental\[cq], respectively.
+.TP
+.B PACKAGE
+The packages for which the hold status is to be changed.
+.SH EXAMPLES
+To hold the libc6 package in the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-hold unstable libc6\fP\fP
+.br
+.SH AUTHORS
+.nf
+Roger Leigh.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2006\[en]2007 Roger Leigh <rleigh@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-qemu-boot.1.in b/man/sbuild-qemu-boot.1.in
new file mode 100644
index 0000000..e6e80dd
--- /dev/null
+++ b/man/sbuild-qemu-boot.1.in
@@ -0,0 +1,98 @@
+.\" Copyright © 2020-2022 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/>.
+.so defs.man
+.TH SBUILD\-QEMU\-BOOT "1" "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-qemu\-boot \- Boot a VM using a QEMU image
+.SH SYNOPSIS
+.B sbuild\-qemu\-boot
+.RB [ \-h ]
+.RB [ \-\-read-write ]
+.RB [ \-\-snapshot ]
+.RB [ \-\-shared-dir ]
+.RB [ \-\-arch=ARCH ]
+.RB [ \-\-ram\-size=MiB ]
+.RB [ \-\-cpus=CPUs ]
+.RB [ \-\-ssh\-port=PORT ]
+.RB [ \-\-noexec ]
+.RB IMAGE
+.SH DESCRIPTION
+\fBsbuild\-qemu\-boot\fR boots a QEMU VM using \fBIMAGE\fR into a console. The
+image is in read-only mode by default, as the goal is to support
+experimentation.
+.PP
+The architecture is auto-guessed by stripping any file suffix and then
+splitting the image name on dashes ``\-'', and finally by looking for a Debian
+architecture name. \fIsid\-amd64.img\fR, \fIamd64\-temporary.image\fR or even
+just \fIamd64\fR would all be assumed to be amd64 images, but not
+\fIamd64.rebuild\-tests.img\fR. If this guesses wrong, use the
+\fB\-\-arch\fR option.
+.PP
+Note that you might need to invoke stty(1) within the VM to match your
+host terminal's settings.
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show this help message and exit.
+.TP
+\fB\-\-read\-write\fR
+Write changes back to the image, instead of using the image in read\-only mode.
+.TP
+\fB\-\-snapshot\fR
+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.
+.TP
+\fB\-\-shared\-dir\fR
+Share this directory on the host with the guest. This will only work when the
+image was created with sbuild-qemu-create(1).
+.TP
+\fB\-\-arch\fR=ARCH
+Architecture to use (instead of attempting to auto\-guess).
+.TP
+\fB\-\-ram\-size\fR=MiB
+VM memory size in MB. Default: 2048
+.TP
+\fB\-\-cpus\fR=CPUs
+VM CPU count. Default: 2
+.TP
+\fB\-\-ssh\-port\fR=PORT
+Forward local port PORT to port 22 within the guest. Package
+\fBopenssh\-server\fR must be installed within the guest for this to be useful.
+.TP
+\fB\-\-boot\fR=auto,bios,efi,ieee1275,none
+How to boot the image. Default is BIOS on amd64 and i386, EFI on arm64 and
+armhf, and IEEE1275 on ppc64el.
+.TP
+\fB\-\-noexec\fR
+Don't actually do anything. Just print the command string that
+would be executed, and then exit.
+.SH EXAMPLES
+\fB$ sbuild\-qemu\-boot \-\-shared\-dir /tmp/foo unstable\-autopkgtest\-amd64.img\fR
+.PP
+This will update the image \fBunstable\-autopkgtest\-amd64.img\fR in
+the current directory. If no such file exists there, then
+\fB$IMAGEDIR/unstable\-autopkgtest\-amd64.img\fR is tried.
+.SH ENVIRONMENT
+If \fB$IMAGEDIR\fR is unset, then \fI~/.cache/sbuild\fR is used.
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2020\-2022 Christian Kastner <ckk@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1),
+.BR sbuild\-qemu (1),
+.BR sbuild\-qemu\-create (1).
diff --git a/man/sbuild-qemu-create.1.in b/man/sbuild-qemu-create.1.in
new file mode 100644
index 0000000..32e15c5
--- /dev/null
+++ b/man/sbuild-qemu-create.1.in
@@ -0,0 +1,161 @@
+.\" Copyright © 2020-2022 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/>.
+.so defs.man
+.TH SBUILD\-QEMU\-CREATE "1" "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-qemu\-create \- QEMU image creator for sbuild
+.SH SYNOPSIS
+.B sbuild\-qemu\-create
+.RB [ \-h ]
+.RB [ \-\-arch=ARCH ]
+.RB [ \-\-install\-packages=INSTALL_PACKAGES]
+.RB [ \-\-extra\-deb=EXTRA_DEB]
+.RB [ \-\-components=COMPONENTS ]
+.RB [ \-\-skel=SKEL ]
+.RB [ \-\-authorized\-keys=FILE ]
+.RB [ \-\-size=SIZE ]
+.RB [ \-o=OUT_FILE ]
+.RB [ \-\-noexec ]
+.RB [RELEASE]
+.RB [MIRROR]
+.PP
+Build an image for use with \fBsbuild-qemu\fR and \fBautopkgtest\fR.
+\fBRELEASE\fR will be debootstrapped from \fBMIRROR\fR. This mirror
+will also be used for the \fIsources.list\fR file within the VM.
+See MIRROR below.
+.PP
+Note that sbuild\-qemu\-create is just a simple wrapper around
+autopkgtest\-build\-qemu(1) that automates a few additional steps commonly
+performed for package\-building images.
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show this help message and exit.
+.TP
+\fB\-\-arch\fR=ARCH
+Architecture to use. Default is the host architecture. Currently supported
+architectures are: amd64, arm64, armhf, i386, ppc64el.
+.TP
+\fB\-\-install\-packages\fR=INSTALL_PACKAGES
+Comma\-separated list of additional packages to install using
+\fBapt\-get install\fR from within the image.
+.TP
+\fB\-\-extra\-deb\fR=EXTRA_DEB
+Package file (.deb) from the local filesystem to install. Can be specified more
+than once.
+.TP
+\fB\-\-components\fR=COMPONENTS
+Comma\-separated list of components to use with \fIsources.list\fR entries.
+Default: main.
+.TP
+\fB\-\-skel\fR=SKEL
+Skeleton directory to use for \fI\,/root\/\fP.
+.TP
+\fB\-\-authorized\-keys\fR=FILE
+Install this file as \fI/root/.ssh/authorized_keys\fR within the guest. This
+will automatically install the \fBopenssh-server\fR package. This supersedes
+any copying of this file by the \-\-skel option.
+.TP
+\fB\-\-size\fR=SIZE
+Image size to use. Note that the images will be created in qcow2 format, so
+they won't consume that space right away. Default: 10G.
+.TP
+\fB\-o\fR=OUT_FILE, \fB\-\-out\-file\fR=OUT_FILE
+Output filename. If not supplied, then DIST\-autopkgtest\-ARCH.img will be
+used.
+.TP
+\fB\-\-noexec\fR
+Don't actually do anything. Just print the autopkgtest\-build\-qemu(1) command
+string that would be executed, and then exit.
+.TP
+\fB\-\-boot\fR=auto,bios,efi,ieee1275,none
+How the image should boot. Default is BIOS on amd64 and i386, EFI on arm64 and
+armhf, and IEEE1275 on ppc64el.
+.SH "MIRROR"
+It is \fBhighly recommended\fR that you use an APT cache, like approx(8) or
+apt\-cacher\-ng(8), on the \fBlocal\fR machine (so that the VM can access
+that cache without much hassle) as a mirror. This will dramatically speed up the
+package build process. On the author's local machine, installing the build
+dependencies of even larger packages takes only a few seconds.
+.PP
+If you use a local cache, then ensure that the mirror is accessible from within
+the guest. Using \fIhttp://localhost:9999/debian\fR or similar will
+successfully build the image, but APT will fail within the running VM, because
+to the VM, ``localhost'' is something else. An easy workaround is to use an
+IP address instead, for example \fIhttp://192.168.0.123:9999/debian\fR,
+assuming the host system has the IP 192.168.0.123.
+.SH "SPECIAL CASES"
+If \fBRELEASE\fR is \fBexperimental\fR, \fIsources.list\fR will contain
+entries for both \fBexperimental\fR and \fBunstable\fR.
+.PP
+If \fBRELEASE\fR ends with \fB\-backports\fR, \fIsources.list\fR will contain
+entries for both \fBRELEASE\fR and for the distribution it is based upon. In
+other words, specifying \fIbullseye-backports\fR will also add an entry
+for \fIbullseye\fR.
+.SH "SHARING FILES"
+Among other things, autopkgtest\-virt\-qemu(1) has built-in support for sharing
+a directory on the host with the guest, so no further configuration should be
+necessary when accessing the VM using autopkgtest.
+.PP
+In additon to that, a mount point for a 9p filesystem is added to the
+VM's \fI/etc/fstab\fR. This is for cases where the VM is launched via QEMU
+directly, rather than going through autopkgtest\-virt\-qemu.
+.PP
+To share a directory on the host with the VM, QEMU should be started with
+the following additional options:
+.PP
+\fB \-virtfs local,path=/path/to/host/dir,id=sbuild-qemu,mount_tag=sbuild-qemu,security_model=none
+.SH "TTY GEOMETRY"
+sbuild\-qemu\-creates a file
+\fI/etc/profile.d/sbuild\-qemu\-terminal\-settings.sh\fR within the VM. This file
+reads terminal column and row geometry passed on through from the host, and
+sets the geometry within the guest using stty(1).
+.SH EXAMPLES
+\fB$ sudo sbuild\-qemu\-create unstable http://deb.debian.org/debian\fR
+.PP
+This will create an image \fBunstable\-autopkgtest\-amd64.img\fR (assuming
+that the host architecture is amd64) with the unstable distribution.
+.PP
+\fB$ sudo sbuild\-qemu\-create bullseye\-backports http://deb.debian.org/debian\fR
+.PP
+This will create an image \fBbullseye\-backports\-autopkgtest\-amd64.img\fR,
+with \fIsources.list\fR entries for both bullseye and bullseye-backports.
+.PP
+\fB$ sudo sbuild\-qemu\-create \-\-skel DIR unstable http://deb.debian.org/debian\fR
+.PP
+The files in \fBDIR\fR will be copied into /root (that is, root's $HOME). This
+can be used, for example, to copy an \fB.ssh/authorized_keys\fR file, so that
+one can connect to the running image using SSH (assuming openssh\-server is
+installed).
+.PP
+\fB$ sudo sbuild\-qemu\-create \-\-install\-packages openssh\-server unstable http://deb.debian.org/debian\fR
+.PP
+This would install openssh\-server. The package will be downloaded in the
+target environment using 'apt-get'.
+.PP
+A popular package to pre\-install this way would be \fBdebhelper\fR, as it is a
+build dependency of the vast majority of Debian packages.
+.PP
+\fB$ sudo sbuild\-qemu\-create \-\-extra\-deb FOO.deb unstable http://deb.debian.org/debian\fR
+.PP
+This would install the package \fBFOO.deb\fR from the local filesystem.
+Useful, for example, to install additional keyring packages.
+.SH COPYRIGHT
+Copyright \[co] 2020-2022 Christian Kastner <ckk@debian.org>
+.SH "SEE ALSO"
+.BR sbuild (1),
+.BR sbuild\-qemu (1),
+.BR sbuild\-qemu\-update (1).
diff --git a/man/sbuild-qemu-update.1.in b/man/sbuild-qemu-update.1.in
new file mode 100644
index 0000000..5695556
--- /dev/null
+++ b/man/sbuild-qemu-update.1.in
@@ -0,0 +1,77 @@
+.\" Copyright © 2020-2022 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/>.
+.so defs.man
+.TH SBUILD\-QEMU\-UPDATE "1" "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-qemu\-update \- sbuild\-update analog for QEMU images
+.SH SYNOPSIS
+.B sbuild\-qemu\-update
+.RB [ \-h ]
+.RB [ \-\-snapshot ]
+.RB [ \-\-arch=ARCH ]
+.RB [ \-\-noexec ]
+.RB IMAGE
+.SH DESCRIPTION
+\fBsbuild\-qemu\-update\fR boots a QEMU VM using \fBIMAGE\fR and then runs
+\fBapt-get\fR in it, performing \fBupdate\fR, \fBdist-upgrade\fR, \fBclean\fR
+and \fBautoremove\fR. It can optionally create a snapshot before updating.
+.PP
+The architecture is auto-guessed by stripping any file suffix and then
+splitting the image name on dashes ``\-'', and finally by looking for a Debian
+architecture name. \fIsid-amd64.img\fR, \fIamd64-temporary.image\fR or even
+just \fIamd64\fR would all be assumed to be amd64 images, but not
+\fIamd64.rebuild-tests.img\fR. If this guesses wrong, use the
+\fB\-\-arch\fR option.
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show this help message and exit.
+.TP
+\fB\-\-snapshot\fR
+Create a snapshot of the image before changing it. Useful for
+reproducibility purposes.
+.TP
+\fB\-\-arch\fR=ARCH
+Architecture to use (instead of attempting to auto\-guess).
+.TP
+\fB\-\-timeout\fR=SECS
+Maximum time to wait for command to finish with expected result. Mostly
+relevant for foreign architectures, where \fBapt-get update\fR can take
+quite a while. Default: 600s.
+.TP
+\fB\-\-noexec\fR
+Don't actually do anything. Just print the command string that
+would be executed, and then exit.
+.TP
+\fB\-\-boot\fR=auto,bios,efi,ieee1275,none
+How to boot the image. Default is BIOS on amd64 and i386, EFI on arm64 and
+armhf, and IEEE1275 on ppc64el.
+.SH EXAMPLES
+\fB$ sbuild\-qemu\-update unstable\-autopkgtest\-amd64.img\fR
+.PP
+This will update the image \fBunstable\-autopkgtest\-amd64.img\fR in
+the current directory. If no such file exists there, then
+\fB$IMAGEDIR/unstable\-autopkgtest\-amd64.img\fR is tried.
+.SH ENVIRONMENT
+If \fB$IMAGEDIR\fR is unset, then \fI~/.cache/sbuild\fR is used.
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2020-2022 Christian Kastner <ckk@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1),
+.BR sbuild\-qemu (1),
+.BR sbuild\-qemu\-create (1).
diff --git a/man/sbuild-qemu.1.in b/man/sbuild-qemu.1.in
new file mode 100644
index 0000000..cdb983c
--- /dev/null
+++ b/man/sbuild-qemu.1.in
@@ -0,0 +1,117 @@
+.\" Copyright © 2020-2022 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/>.
+.so defs.man
+.TH SBUILD\-QEMU "1" "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-qemu \- sbuild using QEMU images
+.SH SYNOPSIS
+.B sbuild\-qemu
+.RB [ \-h ]
+.RB [ \-\-image=IMAGE ]
+.RB [ \-\-ram\-size=MiB ]
+.RB [ \-\-cpus=CPUs ]
+.RB [ \-\-overlay\-dir=OVERLAY_DIR ]
+.RB [ \-\-noexec ]
+.RB [ \-\-autopkgtest\-debug ]
+.RB [ <sbuild-options> ]
+.PP
+Build Debian packages with \fBsbuild\fR using QEMU images, by using sbuild's
+\fB\-\-chroot\-mode=autopkgtest\fR.
+.PP
+All options other than the ones described below are passed on through to
+\fBsbuild\fR(1), though the options \fB\-\-dist\fR, \fB\-\-arch\fR
+and \fB\-\-build\fR are peeked at when looking for images. The image will be
+started in \fB\-snapshot\fR mode, so the image is never modified. Multiple
+processes can use the same image concurrently.
+.SH OPTIONS
+.TP
+\fB\-h\fR, \fB\-\-help\fR
+Show this help message and exit.
+.TP
+\fB\-\-image\fR=IMAGE
+QEMU image file to use for building. If not specified, sbuild-qemu will look
+for an image with the name \fBDIST\-autopkgtest\-ARCH.img\fR, where DIST is
+taken from \fB\-\-dist\fR if present, and ARCH is taken from \fB\-\-arch\fR
+or \fB\-\-build\fR 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 \fB$IMAGEDIR\fR. A
+suitable image can be created with sbuild\-qemu\-create(1).
+.TP
+\fB\-\-ram\-size\fR=MiB
+VM memory size in MB. Default: 2048
+.TP
+\fB\-\-cpus\fR=CPUs
+VM CPU count. Default: 2
+.TP
+\fB\-\-overlay\-dir\fR=OVERLAY_DIR
+Directory for the temporary image overlay instead of autopkgtest's default of
+\fI\,/tmp\/\fP (or $TMPDIR).
+.TP
+\fB\-\-noexec\fR
+Don't actually do anything. Just print the sbuild(1) command string that would
+be executed, and then exit.
+.TP
+\fB\-\-autopkgtest\-debug\fR
+Enable debug output for the autopkgtest\-virt\-qemu(1) driver.
+.TP
+\fB\-\-boot\fR=auto,bios,efi,ieee1275,none
+How to boot the image. Default is BIOS on amd64 and i386, EFI on arm64 and
+armhf, and IEEE1275 on ppc64el.
+.SH CONSIDERATIONS
+Using a fast temporary image overlay is key to faster build times. An overlay
+created on a \fItmpfs\fR would be ideal, and can speed up I/O by an order of
+magnitude, compared to a slow HDD. If \fI\,/tmp\fR or \fI$TMPDIR\fR are mountpoints
+for a tmpfs, then all should be fine by default.
+.PP
+However, tmpfs filesystems are backed by memory and swap on the host, so a build
+needing substantial disk space in the VM may fail. If this happens, the
+\-\-overlay\-dir option should be used to point to a host directory with more
+available disk space. Note that the base image itself must have been created
+with a suitable size, see the \fB\-\-size\fR option to sbuild\-qemu\-create(1).
+.SH LIMITATIONS
+Due to the nature of sbuild's \fB\-\-chroot\-mode=autopkgtest\fR, not all
+sbuild options are supported yet. In particular, getting an interactive
+shell, for example using \fB\-\-build\-failed\-command\fR is \fBNOT\fR possible.
+However, there are other ways to access the build environment (see below).
+.SH "VM ACCESS"
+sbuild's \fB\-\-chroot\-mode=autopkgtest\fR uses autopkgtest\-virt\-qemu under
+the hood, so you will find an SSH shell on port 10022 (or the first free port
+after that) if, and only if, the openssh\-server package is installed in the
+VM.
+.PP
+As a further consequence of this chroot mode, what is stated in
+autopkgtest\-virt\-qemu(1) generally also holds here.
+.PP
+.SH EXAMPLES
+\fB$ sbuild\-qemu \-d unstable \-s \-\-arch\-all FOO.dsc\fR
+.PP
+This will build the package \fBFOO.dsc\fR using \fBunstable\fR as the
+Distribution in the generated .changes file.
+.PP
+Because the \fB\-\-image\fR option was not specified, an image with the name
+\fBunstable\-autopkgtest\-amd64.img\fR will be looked for first in the current
+directory, and then in \fB$IMAGEDIR\fI.
+.PP
+The \fB\-s\fR and \fB\-\-arch\-all\fR aren't known to sbuild-qemu, and
+are therefore will be passed on through to sbuild for it to use.
+.SH ENVIRONMENT
+\fB$IMAGEDIR\fR defaults to \fI~/.cache/sbuild\fR.
+.SH COPYRIGHT
+Copyright \[co] 2020-2022 Christian Kastner <ckk@debian.org>
+.SH "SEE ALSO"
+.BR sbuild (1),
+.BR sbuild\-qemu\-create (1),
+.BR sbuild\-qemu\-update (1).
diff --git a/man/sbuild-setup.7.in b/man/sbuild-setup.7.in
new file mode 100644
index 0000000..102e4d7
--- /dev/null
+++ b/man/sbuild-setup.7.in
@@ -0,0 +1,303 @@
+.\" t
+.\"
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD-SETUP 7 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild-setup \- sbuild setup procedure
+.SH DESCRIPTION
+\fBsbuild\fP uses chroots to build packages within, to provide a minimal and
+consistent build environment. This man page describes the procedure to create
+a chroot by hand using debootstrap. These are only guidelines; depending upon
+the setup required, several of the steps may be omitted entirely.
+.SH QUICK START
+Simply running
+.B sbuild-createchroot
+will perform all the setup steps described in detail below. See the section
+\[lq]\fIsbuild-createchroot\fP\[rq] below, as well as
+.BR sbuild\-createchroot (8).
+.SH CHROOT SETUP
+This guide sets up a lenny chroot on a powerpc machine. Adjust the
+names for other suites and architectures.
+.SS 1. Run debootstrap to create the chroot
+\f[CR]# \f[CB]mkdir \-p /srv/chroot/lenny\fP\fP
+.br
+.PP
+The author has each chroot as a separate LVM logical volume (LV). Create and
+mount an LV here if required:
+.PP
+\f[CR]# \f[CB]lvcreate \-L 4G \-n lenny_chroot \-Z y volume-group\fP\fP
+.br
+.PP
+Add to /etc/fstab and mount (see next section for full fstab example).
+Finally, run debootstrap to create the chroot:
+.PP
+\f[CR]# \f[CB]debootstrap \-\-variant=buildd lenny /srv/chroot/lenny http://deb.debian.org/debian/\fP\fP
+.br
+.SS 2. Set up additional mounts
+An example \fI/etc/fstab\fP:
+.PP
+.TS
+lfCR s s s s s.
+/dev/volume-group/lenny_chroot \[rs]
+.T&
+lfCR lfCR lfCR lfCR lfCR lfCR.
+ /srv/chroot/lenny ext3 defaults 0 2
+/dev/pts /srv/chroot/lenny/dev/pts none rw,bind 0 0
+tmpfs /srv/chroot/lenny/dev/shm tmpfs defaults 0 0
+proc /srv/chroot/lenny/proc proc defaults 0 0
+.T&
+lfCR s s s s s.
+/dev/volume-group/home \[rs]
+.T&
+lfCR lfCR lfCR lfCR lfCR lfCR.
+ /srv/chroot/lenny/home ext3 quota 0 0
+/tmp /srv/chroot/lenny/tmp none rw,bind 0 0
+/etc/passwd /srv/chroot/lenny/etc/passwd none ro,bind 0 0
+/etc/shadow /srv/chroot/lenny/etc/shadow none ro,bind 0 0
+/etc/group /srv/chroot/lenny/etc/group none ro,bind 0 0
+/etc/gshadow /srv/chroot/lenny/etc/gshadow none ro,bind 0 0
+.T&
+lfCR s s s s s.
+/etc/resolv.conf \[rs]
+.T&
+lfCR lfCR s s s s.
+ /srv/chroot/lenny/etc/resolv.conf \[rs]
+.T&
+lfCR lfCR lfCR lfCR lfCR lfCR.
+ none ro,bind 0 0
+.TE
+.PP
+If the bind mountpoints don't exist in the chroot, touch them:
+.PP
+\f[CR]# \f[CB]touch /srv/chroot/lenny/etc/resolv.conf\fP\fP
+.PP
+Next, mount them all.
+.PP
+Depending on your kernel version and security considerations, you may wish to
+do this part slightly differently. With a Linux kernel, at least version 2.6
+is required for bind mounts, and devpts (CONFIG_UNIX98_PTYS) for
+\fI/dev/pts\fP. Other guides recommend copying the files, but this method
+keeps them up-to-date at no cost.
+.PP
+If using sbuild with schroot, \fIpasswd\fP, \fIshadow\fP, \fIgroup\fP,
+\fIgshadow\fP and \fIresolv.conf\fP can be updated automatically at the start
+of each build, so no action is required here. schroot can also automatically
+mount all of the extra filesystems, so all the other mounts may be omitted.
+.PP
+To disable networking, don't bind mount \fI/etc/resolv.conf\fP. This will
+prevent APT from working inside the chroot, but prevents package building from
+having working network access (no nameservers).
+.SS 3. Edit \f[BI]sources.list\fP
+Create or edit \fI/srv/chroot/lenny/etc/apt/sources.list\fP, and add all the
+APT sources required to obtain binary and source packages for your chosen
+distribution:
+.PP
+\f[CR]deb http://security.debian.org/ lenny/updates main\fP
+.br
+\f[CR]deb\-src http://security.debian.org/ lenny/updates main\fP
+.br
+\f[CR]\fP
+.br
+\f[CR]deb http://deb.debian.org/debian/ lenny main\fP
+.br
+\f[CR]deb\-src http://deb.debian.org/debian/ lenny main\fP
+.br
+.SS 4. Configure dchroot or schroot
+This is entirely optional, but will make the chroot environment easier to
+access and administer.
+.PP
+For dchroot, add the following line to \fI/etc/dchroot.conf\fP:
+.PP
+\f[CR]lenny /srv/chroot/lenny\fP
+.br
+.PP
+For schroot, add a group to \fI/etc/schroot/schroot.conf\fP (or a new file
+\fI/etc/schroot/chroot.d/lenny\fP), for example:
+.PP
+\f[CR][lenny]\fP
+.br
+\f[CR]type=directory\fP
+.br
+\f[CR]description=Debian lenny (stable)\fP
+.br
+\f[CR]location=/srv/chroot/lenny\fP
+.br
+\f[CR]priority=2\fP
+.br
+\f[CR]groups=root,sbuild\fP
+.br
+\f[CR]root\-groups=sbuild\fP
+.br
+\f[CR]aliases=stable\fP
+.br
+\f[CR]run\-setup\-scripts=true\fP
+.br
+\f[CR]run\-session\-scripts=true\fP
+.br
+.PP
+For sudo, add a symbolic link to the directory \fI/etc/sbuild/chroot\fP, for
+example:
+.PP
+\f[CR]# \f[CB]mkdir \-p /etc/sbuild/chroot\fP\fP
+.br
+\f[CR]# \f[CB]ln \-s /srv/chroot/lenny /etc/sbuild/chroot/lenny\fP\fP
+.br
+.SS 5. Log into chroot
+\f[CR]# \f[CB]dchroot \-c lenny\fP\fP
+.br
+.PP
+or
+.PP
+\f[CR]$ \f[CB]schroot \-c lenny \-u root\fP\fP
+.br
+.SS 6. Set up packages for sbuild
+While running as root inside the chroot:
+.PP
+\f[CR]# \f[CB]apt\-get update\fP\fP
+.br
+\f[CR]# \f[CB]apt\-get dist\-upgrade\fP\fP
+.br
+\f[CR]# \f[CB]apt\-get install debconf\fP\fP
+.br
+\f[CR]# \f[CB]dpkg\-reconfigure \-plow debconf\fP\fP
+.br
+.PP
+Answer the debconf questions as follows:
+.IP \fBinterface\fP
+choose \fI6/Noninteractive\fP
+.IP \fBpriority\fP
+choose \fI1/Critical\fP
+.PP
+You only need to run dpkg-reconfigure if you weren't asked the questions during
+the debconf install. Next, install the packages required for building
+packages:
+.PP
+\f[CR]# \f[CB]apt\-get install fakeroot build\-essential\fP\fP
+.br
+\f[CR]# \f[CB]apt\-get install makedev\fP\fP
+.br
+\f[CR]# \f[CB]cd /dev/\fP\fP
+.br
+\f[CR]# \f[CB]/sbin/MAKEDEV generic\fP\fP
+.br
+\f[CR]# \f[CB]touch /etc/mtab\fP\fP
+.br
+.PP
+For some security, we don't bind mount /dev, so it can't access e.g. USB
+devices
+.SS 7. sbuild setup
+While running as root inside the chroot:
+.PP
+\f[CR]# \f[CB]mkdir /build\fP\fP
+.br
+\f[CR]# \f[CB]chown root:sbuild /build\fP\fP
+.br
+\f[CR]# \f[CB]chmod 02775 /build\fP\fP
+.br
+\f[CR]# \f[CB]mkdir \-p /var/lib/sbuild/srcdep\-lock\fP\fP
+.br
+\f[CR]# \f[CB]chown \-R root:sbuild /var/lib/sbuild\fP\fP
+.br
+\f[CR]# \f[CB]chmod \-R 02775 /var/lib/sbuild\fP\fP
+.br
+.PP
+Note that when using sbuild with schroot, this setup is done at the start of
+each build, so is not required here.
+.SS 8. Finished
+Congratulations! You should now have a fully configured and operational
+chroot.
+.SH SBUILD-CREATECHROOT
+This script will automatically perform a number of the steps described above,
+including:
+.IP \[bu]
+Running debootstrap.
+.IP \[bu]
+Setting up APT sources in \fI/etc/apt/sources.list\fP.
+.IP \[bu]
+Setting up a minimal \fI/etc/passwd\fP
+.IP \[bu]
+Setting up /build and /var/lib/sbuild with appropriate ownership and
+permissions.
+.PP
+After it has done this, you do still need to do some manual setup, completing
+the steps it missed out above, for example.
+.SH USER SETUP
+.SS 1. Group membership
+As root, run:
+.PP
+\f[CR]# \f[CB]sbuild-adduser \f[CBI]user\fP\fP\fP
+.br
+.PP
+Alternatively, add the user to the \fIsbuild\fP group by hand:
+.PP
+\f[CR]# \f[CB]adduser \f[CBI]user\fP sbuild\fP\fP
+.SS 2. \f[BI]~/.sbuildrc\fP
+.br
+Configure the user's \fI~/.sbuildrc\fP:
+.PP
+\f[CR]$ \f[CB]cp /usr/share/doc/sbuild/examples/example.sbuildrc ~\f[CBI]user\fP/.sbuildrc\fP\fP
+.br
+.PP
+Edit to set the correct mail address to send log files to, and the correct
+maintainer name and/or uploader name.
+.PP
+.SS 3. Build directories
+Create directories to contain packages and log files. (.sbuildrc may have
+configured different locations; the default build directory is the current
+directory, and the default $log_dir is ~/logs):
+.PP
+\f[CR]$ \f[CB]mkdir ~/logs\fP\fP
+.br
+.SS 4. sudo setup
+This step not required if schroot is used (which is the default, set in
+\fIsbuild.conf\fP). If using sbuild with sudo (chroot_mode \[lq]split\[rq]),
+sudo needs configuring to give the user permission to install and remove
+packages in the chroot, which requires root privileges. Add the following
+lines to \fI/etc/sudoers\fP:
+.PP
+\f[CR]\f[CI]username\fP ALL=NOPASSWD: ALL\fP
+.br
+\f[CR]Defaults:\f[CI]username\fP env_keep+="APT_CONFIG DEBIAN_FRONTEND SHELL"\fP
+.br
+.PP
+where \fIusername\fP is the name of the user who will run sbuild.
+.SS 5. Finished
+The user should now be able to run sbuild.
+.PP
+\f[CR]$ \f[CB]sbuild ...\fP\fP
+.br
+.SH AUTHORS
+Roger Leigh.
+.SH COPYRIGHT
+Copyright \(co 2005-2008 Roger Leigh <rleigh@debian.org>
+.PP
+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.
+.SH SEE ALSO
+.BR debootstrap (1),
+.BR sbuild (1),
+.BR sbuild\-adduser (8),
+.BR sbuild\-createchroot (8).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-shell.1.in b/man/sbuild-shell.1.in
new file mode 100644
index 0000000..b0b51a7
--- /dev/null
+++ b/man/sbuild-shell.1.in
@@ -0,0 +1,63 @@
+.\" Copyright © 2005-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/>.
+.so defs.man
+.TH SBUILD\-SHELL 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-shell \- run a shell in an sbuild chroot
+.SH SYNOPSIS
+.BR sbuild\-shell
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.B CHROOT
+.SH DESCRIPTION
+\fBsbuild\-shell\fR runs an interactive shell (\fI/bin/sh\fP) inside the
+specified chroot. The changes will not be discarded, so this can be used to
+setup a build chroot.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.SS Chroot selection
+.TP
+.B CHROOT
+The chroot to use. Note that \[oq]o\[cq], \[oq]s\[cq],
+\[oq]t\[cq], \[oq]u\[cq] and \[oq]e\[cq] may be used as abbreviations for
+\[oq]oldstable\[cq], \[oq]stable\[cq], \[oq]testing\[cq], \[oq]unstable\[cq]
+and \[oq]experimental\[cq], respectively.
+.SH EXAMPLES
+To run a shell in the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-shell unstable\fP\fP\[CR]
+.br
+.SH AUTHORS
+.nf
+Roger Leigh.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2006\[en]2008 Roger Leigh <rleigh@debian.org>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild-update.1.in b/man/sbuild-update.1.in
new file mode 100644
index 0000000..bc859f4
--- /dev/null
+++ b/man/sbuild-update.1.in
@@ -0,0 +1,134 @@
+.\" 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/>.
+.so defs.man
+.TH SBUILD\-UPDATE 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild\-update \- update, upgrade, and clean an sbuild chroot with apt-get
+.SH SYNOPSIS
+.B sbuild\-update
+.RB [ \-h \[or] \-\-help ] " " [ \-V \[or] \-\-version ]
+.RB [ \-\-arch=\fIarchitecture\fP ]
+.RB [ \-u \[or] \-\-update ] " " [ \-g \[or] \-\-upgrade ] " "
+.RB [ \-d \[or] \-\-dist\-upgrade ] " " [ \-c \[or] \-\-clean ] " "
+.RB [ \-a \[or] \-\-autoclean ] " " [ \-r \[or] \-\-autoremove ]
+.RB [ \-\-chroot-mode=\fIschroot|sudo|unshare\fP ]
+.RB CHROOT1 " [" CHROOT2 " [" CHROOT\f[BI]n\fP... ]]
+.br
+.RB "(DEPRECATED) " sbuild\-upgrade
+.RB [ \-h \[or] \-\-help ] " " [ \-V \[or] \-\-version ]
+.RB CHROOT1 " [" CHROOT2 " [" CHROOT\f[BI]n\fP... ]]
+.br
+.RB "(DEPRECATED) " sbuild\-distupgrade
+.RB [ \-h \[or] \-\-help ] " " [ \-V \[or] \-\-version ]
+.RB CHROOT1 " [" CHROOT2 " [" CHROOT\f[BI]n\fP... ]]
+.br
+.RB "(DEPRECATED) " sbuild\-clean
+.RB [ \-h \[or] \-\-help ] " " [ \-V \[or] \-\-version ]
+.RB [ \-c \[or] \-\-clean ] " " [ \-a \[or] \-\-autoclean ] " "
+.RB [ \-r \[or] \-\-autoremove ]
+.RB CHROOT1 " [" CHROOT2 " [" CHROOT\f[BI]n\fP... ]]
+.SH DESCRIPTION
+\fBsbuild\-update\fR runs \f[CB]apt\-get\fP inside the specified chroot
+performing \f[CB]update\fP, \f[CB]upgrade\fP, \f[CB]dist\-upgrade\fP,
+\f[CB]clean\fP, \f[CB]autoclean\fP, and/or \f[CB]autoremove\fP depending on the
+options specified on the command line.
+.PP
+\fBsbuild\-upgrade\fR, \fBsbuild\-distupgrade\fR, and \fBsbuild\-clean\fR, are
+deprecated. They now simply run \fBsbuild\-update\fR with the appropriate
+options (\f[CB]\-\-upgrade\fP or \f[CB]\-\-dist\-upgrade\fP for
+\fBsbuild\-upgrade\fR or \fBsbuild\-distupgrade\fR respectively) along with
+whatever arguments were passed in the scripts.
+.SH OPTIONS
+.SS Actions
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.TP
+.BR "\-\-arch=\fIarchitecture\fP"
+Update chroot for the architecture specified.
+.TP
+.BR \-u ", " \-\-update
+Perform an \f[CB]apt\-get update\fP.
+.TP
+.BR \-g ", " \-\-upgrade
+Perform an \f[CB]apt\-get upgrade\fP.
+.TP
+.BR \-d ", " \-\-dist\-upgrade
+Perform an \f[CB]apt\-get dist\-upgrade\fP.
+.TP
+.BR \-c ", " \-\-clean
+Perform an \f[CB]apt\-get clean\fP.
+.TP
+.BR \-a ", " \-\-autoclean
+Perform an \f[CB]apt\-get autoclean\fP.
+.TP
+.BR \-r ", " \-\-autoremove
+Perform an \f[CB]apt\-get autoremove\fP.
+.SS Chroot selection
+.TP
+.BR \-\-chroot-mode=\fIschroot|sudo|unshare\fP
+The sbuild chroot backend of the chroot to update. The autopkgtest backend is
+not supported by this method because autopkgtest backends are always ephemeral
+and changes never persist. To update a chroot for schroot and sudo,
+sbuild-update must be executed with superuser privileges (for example by using
+sudo). Creating a chroot for the unshare backend can be done by a normal user
+(without sudo) but it requires Linux user namespaces to be enabled (via "sysctl
+-w kernel.unprivileged_userns_clone=1"). Defaults to 'schroot'.
+.TP
+.B CHROOT
+The chroot to use. Note that \[oq]o\[cq], \[oq]s\[cq],
+\[oq]t\[cq], \[oq]u\[cq] and \[oq]e\[cq] may be used as abbreviations for
+\[oq]oldstable\[cq], \[oq]stable\[cq], \[oq]testing\[cq], \[oq]unstable\[cq]
+and \[oq]experimental\[cq], respectively.
+.SH EXAMPLES
+To update the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-update \-\-update unstable\fP\fP
+.PP
+To upgrade the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-update \-\-upgrade unstable\fP\fP
+.PP
+To clean the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-update \-\-clean unstable\fP\fP
+.PP
+To perform an update, dist-upgrade, clean, autoclean, and autoremove for
+the \fIunstable\fP chroot:
+.PP
+\f[CR]% \f[CB]sbuild\-update \-udcar unstable\fP\fP
+.SH AUTHORS
+.nf
+Roger Leigh.
+Andres Mejia.
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 2006\[en]2009 Roger Leigh <rleigh@debian.org>.
+Copyright \[co] 2010 Andres Mejia <mcitadel@gmail.com>.
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1),
+.BR sbuild\-apt (1),
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild.1.in b/man/sbuild.1.in
new file mode 100644
index 0000000..71a11cd
--- /dev/null
+++ b/man/sbuild.1.in
@@ -0,0 +1,1678 @@
+.\" Copyright © 1998 James Troup <james@nocrew.org>
+.\" Copyright © 2005-2009 Roger Leigh <rleigh@debian.org>
+.\" 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/>.
+.so defs.man
+.TH SBUILD 1 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild \- build debian packages from source
+.SH SYNOPSIS
+.B sbuild
+.RB [ \-h \[or] \-\-help " \[or] " \-V \[or] \-\-version ]
+.RB [ \-v \[or] \-\-verbose " \[or] " \-q \[or] \-\-quiet ]
+.RB [ \-D \[or] \-\-debug ]
+.RB [ \-A \[or] \-\-arch\-all ]
+.RB [ \-\-archive=\fIarchive\fP ]
+.RB [ \-d \[or] \-\-dist=\fIdistribution\fP ]
+.RB [ \-c \[or] \-\-chroot=\fIchroot\fP ]
+.RB [ \-\-chroot\-mode=\fIschroot|sudo|autopkgtest|unshare\fP ]
+.RB [ \-\-arch=\fIarchitecture\fP ]
+.RB [ \-\-arch\-any " \[or] " \-\-no\-arch\-any ]
+.RB [ \-\-build=\fIarchitecture\fP ]
+.RB [ \-\-host=\fIarchitecture\fP ]
+.RB [ \-\-profiles=\fIprofile[,...]\fP ]
+.RB [ \-s \[or] \-\-source ]
+.RB [ \-\-force\-orig\-source ]
+.RB [ \-\-make\-binNMU=\fIchangelog-entry\fP ]
+.RB [ \-\-binNMU=\fINMU-version\fP ]
+.RB [ \-\-append\-to\-version=\fIstring\fP ]
+.RB [ \-\-binNMU\-timestamp=\fItimestamp\fP ]
+.RB [ \-\-binNMU\-changelog=\fIchangelog\fP ]
+.RB [ \-\-build\-dir=\fIdirectory\fP ]
+.RB [ \-\-add\-depends=\fIdependency\fP ]
+.RB [ \-\-add\-conflicts=\fIdependency\fP ]
+.RB [ \-\-add\-depends\-arch=\fIdependency\fP ]
+.RB [ \-\-add\-conflicts\-arch=\fIdependency\fP ]
+.RB [ \-\-add\-depends\-indep=\fIdependency\fP ]
+.RB [ \-\-add\-conflicts\-indep=\fIdependency\fP ]
+.RB [ \-m \[or] \-\-maintainer=\fImaintainer\fP ]
+.RB [ \-e \[or] \-\-uploader=\fIuploader\fP ]
+.RB [ \-k \[or] \-\-keyid=\fIkey-id\fP ]
+.RB [ \-\-source\-only\-changes ]
+.RB [ \-\-no\-source\-only\-changes ]
+.RB [ \-j \[or] \-\-jobs=\fIn\fP ]
+.RB [ \-\-debbuildopt=\fIoption\fP ]
+.RB [ \-\-debbuildopts=\fIoptions\fP ]
+.RB [ \-\-dpkg\-source\-opt=\fIoptions\fP ]
+.RB [ \-\-dpkg\-source\-opts=\fIoptions\fP ]
+.RB [ \-\-dpkg\-file\-suffix=\fIsuffix\fP ]
+.RB [ \-p \[or] \-\-purge=\fPpurge-mode\fP ]
+.RB [ \-\-purge\-build=\fPpurge-mode\fP ]
+.RB [ \-\-purge\-deps=\fPpurge-mode\fP ]
+.RB [ \-\-purge\-session=\fPpurge-mode\fP ]
+.RB [ \-b \[or] \-\-batch]
+.RB [ \-n \[or] \-\-nolog ]
+.RB [ \-\-clean\-source ]
+.RB [ \-\-no\-clean\-source ]
+.RB [ \-\-run\-lintian ]
+.RB [ \-\-no\-run\-lintian ]
+.RB [ \-\-lintian\-opt=\fIoptions\fP ]
+.RB [ \-\-lintian\-opts=\fIoptions\fP ]
+.RB [ \-\-run\-piuparts ]
+.RB [ \-\-no\-run\-piuparts ]
+.RB [ \-\-piuparts\-opt=\fIoptions\fP ]
+.RB [ \-\-piuparts\-opts=\fIoptions\fP ]
+.RB [ \-\-piuparts\-root\-arg=\fIoptions\fP ]
+.RB [ \-\-piuparts\-root\-args=\fIoptions\fP ]
+.RB [ \-\-run\-autopkgtest ]
+.RB [ \-\-no\-run\-autopkgtest ]
+.RB [ \-\-autopkgtest\-opt=\fIoptions\fP ]
+.RB [ \-\-autopkgtest\-opts=\fIoptions\fP ]
+.RB [ \-\-autopkgtest\-root\-arg=\fIoptions\fP ]
+.RB [ \-\-autopkgtest\-root\-args=\fIoptions\fP ]
+.RB [ \-\-pre\-build\-commands=\fIstring\fP ]
+.RB [ \-\-chroot\-setup\-commands=\fIstring\fP ]
+.RB [ \-\-chroot\-update\-failed\-commands=\fIstring\fP ]
+.RB [ \-\-build\-deps\-failed\-commands=\fIstring\fP ]
+.RB [ \-\-starting\-build\-commands=\fIstring\fP ]
+.RB [ \-\-finished\-build\-commands=\fIstring\fP ]
+.RB [ \-\-build\-failed\-commands=\fIstring\fP ]
+.RB [ \-\-chroot\-cleanup\-commands=\fIstring\fP ]
+.RB [ \-\-post\-build\-commands=\fIstring\fP ]
+.RB [ \-\-post\-build\-failed\-commands=\fIstring\fP ]
+.RB [ \-\-anything\-failed\-commands=\fIstring\fP ]
+.RB [ \-\-log\-external\-command\-output ]
+.RB [ \-\-log\-external\-command\-error ]
+.RB [ \-\-setup\-hook=\fIhook-script\fP ]
+.RB [ \-\-build\-dep\-resolver=\fIresolver\fP ]
+.RB [ \-\-resolve\-alternatives \[or] \-\-no\-resolve\-alternatives ]
+.RB [ \-\-extra\-package=\fIpackage.deb\fP ]
+.RB [ \-\-extra\-repository=\fIspec\fP ]
+.RB [ \-\-extra\-repository\-key=\fIfile.asc\fP ]
+.RB [ \-\-build\-path=\fIstring\fP ]
+.RB [ \-\-dsc\-dir=\fIstring\fP ]
+.RB [ \-\-autopkgtest\-virt\-server=\fIschroot|lxc|chroot|qemu|ssh\fP ]
+.RB [ \-\-autopkgtest\-virt\-server\-opt=\fIstring\fP ]
+.RB [ \-\-autopkgtest\-virt\-server\-opts=\fIoptions\fP ]
+.RB [ \-\-purge\-extra\-packages\fP ]
+.RB [ \-\-bd\-uninstallable\-explainer=\fIdose3|apt|none\fP ]
+.RB [ PACKAGE [ .dsc ]]
+.SH DESCRIPTION
+\fBsbuild\fR rebuilds Debian binary packages from the corresponding Debian
+source, installing any missing source dependencies. The build takes place in a
+dedicated clean build environment, rather than on the host system. For an
+overview of the supported chroot backends see the section \fBCHROOT MODES\fR.
+.PP
+\fBsbuild\fR can fetch the Debian source over a network, or it can use
+locally available sources.
+.PP
+sbuild is given a packages to process as the argument \fBPACKAGE[.dsc]\fR.
+This argument is in the form of either a debianized package source directory, a
+source package name along with a version in the form \fIpackage_version\fP, a
+source package name, or a .dsc file. If no arguments are given, the current
+working directory is passed as an argument.
+.PP
+For arguments given as source directories, dpkg\-source is first run to produce
+a source .dsc file. Then, the package is built using the .dsc produced. For
+arguments in the form \fIpackage_version\fP or \fIpackage\fP, apt is used to
+download the source package. For arguments given as a .dsc file, sbuild builds
+the source packages directly. For .dsc files in remote locations, the source
+packages are downloaded first, then built.
+.PP
+It is also possible to run external commands with sbuild. See the section
+\fBEXTERNAL COMMANDS\fR for more on this.
+.PP
+\fBsbuild\fR mails the build logs to a user. It is configured by the
+configuration files \fI/etc/sbuild/sbuild.conf\fP and \fI~/.sbuildrc\fP. An
+example sbuildrc is available in
+\fI/usr/share/doc/sbuild/examples/example.sbuildrc\fP.
+A custom path to a configuration file can also be specified through setting the
+\fBSBUILD_CONFIG\fP environment variable to the path of an additional
+configuration file.
+.PP
+You can build either using a local package with its .dsc file or a
+remote one by specifying an explicit dpkg version.
+.SH OPTIONS
+Options set on the command line overwrite settings made in the configuration
+file.
+.TP
+.BR \-h ", " \-\-help
+Display this manual.
+.TP
+.BR \-V ", " \-\-version
+Print version information.
+.TP
+.BR "\-\-add\-depends=\fIdependency\fP"
+.TP
+.BR "\-\-add\-conflicts=\fIdependency\fP"
+.TP
+.BR "\-\-add\-depends\-arch=\fIdependency\fP"
+.TP
+.BR "\-\-add\-conflicts\-arch=\fIdependency\fP"
+.TP
+.BR "\-\-add\-depends\-indep=\fIdependency\fP"
+.TP
+.BR "\-\-add\-conflicts\-indep=\fIdependency\fP"
+These options add a build dependencies to the source package being built, in
+addition to the build dependency information specified in debian/control.
+These dependencies will be concatenated directly to the Build\-Depends,
+Build\-Conflicts, Build\-Depends\-Arch, Build\-Conflicts\-Arch, Build\-Depends\-Indep
+and Build\-Conflicts\-Indep dependencies, respectively. The options may be used
+any number of times to add multiple dependencies. The format is identical to
+the format used in debian/control.
+These command line options append to the \fBMANUAL_DEPENDS\fP, \fBMANUAL_CONFLICTS\fP, \fBMANUAL_DEPENDS_ARCH\fP, \fBMANUAL_CONFLICTS_ARCH\fP, \fBMANUAL_DEPENDS_INDEP\fP and \fBMANUAL_CONFLICTS_INDEP\fP configuration variables, respectively. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-arch=\fIarchitecture\fP"
+Build using the architecture specified. A chroot named
+\fI$distribution\-$arch\-sbuild\fP or \fI$distribution\-arch\fP is searched for,
+in that order of preference. The chroot must be installed and configured
+appropriately to build as that architecture, e.g. using
+\fIpersonality=linux32\fP to build i386 packages on an amd64 system. Note that
+this option is equivalent to "\-\-host=architecture \-\-build=architecture".
+This command line option sets the \fBHOST_ARCH\fP and \fBBUILD_ARCH\fP configuration variables. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-host=\fIarchitecture\fP"
+Build using the host architecture specified. If $host and $build don't match, a
+chroot named \fI$distribution\-$build\-$host\-sbuild\fP or \fI$distribution\-$build\-$host\fP
+is searched for, falling back to \fI$distribution\-$build\-sbuild\fP or
+\fI$distribution\-$build\fP, in that order of preference. This option is only
+useful for cross-building when used together with \-\-build.
+This command line option sets the \fBHOST_ARCH\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-build=\fIarchitecture\fP"
+Build using the build architecture specified. This option is only useful for
+cross-building when used together with \-\-host. If \-\-build is not specified,
+the default system architecture is assumed.
+This command line option sets the \fBBUILD_ARCH\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-A ", " "\-\-arch\-all"
+Also build Architecture: all packages. This is the default behaviour for native builds. This option is the opposite of
+\-\-no\-arch\-all.
+See the section
+.BR "BUILD ARTIFACTS"
+for more information.
+This command line option sets the \fBBUILD_ARCH_ALL\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-no\-arch\-all"
+Do not build Architecture: all packages. This is the default behaviour for cross builds. This
+option is the opposite of \-\-arch\-all.
+See the section
+.BR "BUILD ARTIFACTS"
+for more information.
+This command line option sets the \fBBUILD_ARCH_ALL\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-arch\-any"
+Build Architecture: any packages. This is the default behavior. This option is
+the opposite of \-\-no\-arch\-any.
+See the section
+.BR "BUILD ARTIFACTS"
+for more information.
+This command line option sets the \fBBUILD_ARCH_ANY\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-no\-arch\-any"
+Do not build Architecture: any packages. This option is the opposite
+of \-\-arch\-any and only useful when used together with \-\-arch\-all
+or \-\-source.
+See the section
+.BR "BUILD ARTIFACTS"
+for more information.
+This command line option sets the \fBBUILD_ARCH_ANY\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-b ", " "\-\-batch"
+Operate in batchmode, i.e. write a build-progress file during execution
+and files on shutdown to facilitate a clean restart.
+This command line option sets the \fBBATCH_MODE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-c ", " "\-\-chroot=\fIchroot\fP"
+Specifies the chroot to use. The effect of this option depends on the selected
+chroot mode. With the \fBschroot\fP chroot mode, this option specifies the
+schroot name or alias to use. If not specified, the default is the first of
+schroot name or alias that matches \fI$distribution\-$arch\-sbuild\fP,
+\fI$distribution\-sbuild\fP, \fI$distribution\-$arch\fP or \fI$distribution\fP
+that exists. With the \fBsudo\fP chroot mode, this option specifies the chroot
+directory to use. The directory is either expected in /etc/sbuild/chroot (in
+buildd sbuild mode) or in the build directory (see \-\-build\-dir), prefixed with "chroot\-" (in
+user sbuild mode, the default). If not specified, the default is to search for
+a directory in the respective locations named in the same way as for the
+schroot mode. With the \fBunshare\fP chroot mode, if this option is a path,
+then it specifies the location of the chroot tarball directly. Otherwise, a
+tarball with equal basename from ~/.cache/sbuild will be used. If not
+specified, the default is to search for a tarball named in the same way as for
+the schroot mode under ~/.cache/sbuild. With the \fBautopkgtest\fP chroot mode
+this option has no effect. The \-\-autopkgtest\-virt\-server\-opts are used to pick
+the chroot in autopkgtest chroot mode.
+This command line option sets the \fBCHROOT\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-chroot\-mode=\fIschroot|sudo|autopkgtest|unshare\fP"
+Select the desired chroot mode. Four values are possible: schroot (the
+default), sudo (which uses sudo to execute chroot in a directory from
+/etc/sbuild/chroot or ./chroot), autopkgtest which uses the autopkgtest\-virt\-* binaries
+(selectable via the \-\-autopkgtest\-virt\-server option) and unshare (which uses linux
+namespaces for chroot and doesn't require superuser privileges).
+See the section
+.BR "CHROOT MODES"
+for more information.
+This command line option sets the \fBCHROOT_MODE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-d ", " "\-\-dist=\fIdistribution\fP"
+Explicitly set the distribution for the package build. This will be selecting
+the correct chroot to use and also sets the value of the Distribution field in
+the created .changes file. Setting this option is necessary when giving sbuild
+a .dsc file or a plain source package name to build. In the latter case it
+specifies the distribution the source package is fetched from.
+This command line option sets the \fBDISTRIBUTION\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-archive=\fIarchive\fP
+Communicate with specified archive.
+This command line option sets the \fBARCHIVE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-D ", " "\-\-debug"
+Enable debug output.
+.TP
+.BR "\-\-apt\-clean"
+.TQ
+.BR "\-\-no\-apt\-clean"
+Run (or do not run) apt\-get clean in the chroot before executing the build,
+overriding the default setting.
+This command line option sets the \fBAPT_CLEAN\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-apt\-update"
+.TQ
+.BR "\-\-no\-apt\-update"
+Run (or do not run) apt\-get update in the chroot before executing the build,
+overriding the default setting.
+This option has no effect on updating the internal sbuild apt repository, the
+repository for extra packages (see \-\-extra\-package) and the repositories given
+via \-\-extra\-repository. These are always updated. Thus, this option only
+influences updates of the default repositories of the chroot.
+This command line option sets the \fBAPT_UPDATE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-apt\-upgrade"
+.TQ
+.BR "\-\-no\-apt\-upgrade"
+Run (or do not run) apt\-get upgrade in the chroot before executing the build,
+overriding the default setting.
+This command line option sets the \fBAPT_UPGRADE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-apt\-distupgrade"
+.TQ
+.BR "\-\-no\-apt\-distupgrade"
+Run (or do not run) apt\-get distupgrade in the chroot before executing the build,
+overriding the default setting.
+This command line option sets the \fBAPT_DISTUPGRADE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-m ", " "\-\-maintainer=\fImaintainer\fP"
+Specify the identity to use for GPG signing packages, and also used as the
+maintainer for binary NMUs. This does not normally require setting (it
+defaults to the uploader).
+This command line option sets the \fBMAINTAINER_NAME\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-e ", " "\-\-uploader=\fIuploader\fP"
+Passed to dpkg\-genchanges and is used to set the Changed\-by: field in
+the .changes file(s).
+This command line option sets the \fBUPLOADER_NAME\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-k ", " "\-\-keyid=\fIkey-id\fP"
+Passed to debsign and is used to set the key to sign the .changes
+file(s). Default is not using any key and not signing the .changes file(s).
+This command line option sets the \fBKEY_ID\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-source\-only\-changes
+.TQ
+.BR "\-\-no\-source\-only\-changes
+In addition to the .changes file generated by dpkg\-buildpackage, also produce
+(or don't produce) a .changes file suitable for a source-only upload. If
+requested by \-\-keyid, this .changes file will also be signed by debsign.
+This command line option sets
+the \fBSOURCE_ONLY_CHANGES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-j ", " "\-\-jobs=\fIn\fP"
+Number of jobs to run simultaneously. Passed through to dpkg\-buildpackage.
+This command line option appends the appropriate \fB\-j\fP option to the \fBDPKG_BUILDPACKAGE_USER_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-debbuildopt=\fIoption\fP
+Pass the specified option directly to dpkg\-buildpackage in addition to the
+options already passed by sbuild. This option can be passed multiple times
+(once per dpkg\-buildpackage option) and can be freely mixed with the
+\-\-debbuildopts option. Options will be passed to dpkg\-buildpackage in the
+order that the \-\-debbuildopt and \-\-debbuildopts options are given on the
+command line.
+This command line option appends to the \fBDPKG_BUILDPACKAGE_USER_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-debbuildopts=\fIoptions\fP
+Pass the specified options directly to dpkg\-buildpackage in addition to the
+options already passed by sbuild. The argument will be split by whitespaces and
+the resulting array passed to the dpkg\-buildpackage invocation. If any options
+contain spaces, use \-\-debbuildopt for them. This option can be passed
+multiple times and can be freely mixed with the \-\-debbuildopt option. Options
+will be passed to dpkg\-buildpackage in the order that the \-\-debbuildopt and
+\-\-debbuildopts options are given on the command line.
+This command line option appends to the \fBDPKG_BUILDPACKAGE_USER_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-dpkg\-source\-opt=\fIoptions\fP
+Pass the specified option directly to dpkg\-source in addition to the options
+already passed by sbuild. This is only used when creating a source package from
+a Debianized source directory. This option can be passed multiple times (once
+per dpkg\-source option) and can be freely mixed with the \-\-dpkg\-source\-opts
+option. Options will be passed to dpkg\-source in the order that the
+\-\-dpkg\-source\-opt and \-\-dpkg\-source\-opts options are given on the
+command line.
+This command line option appends to the \fBDPKG_SOURCE_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.br
+\fBNOTE:\fR The '\fI\-b\fP', '\fI\-\-before\-build\fP' and '\fI\-\-after\-build\fP'
+options will always be passed to dpkg\-source, respectively.
+.TP
+.BR \-\-dpkg\-source\-opts=\fIoptions\fP
+Pass the specified options directly to dpkg\-source in addition to the options
+already passed by sbuild. The argument will be split by whitespaces and the
+resulting array passed to the dpkg\-source invocation. This is only used when
+creating a source package from a Debianized source directory. If any options
+contain spaces, use \-\-dpkg\-source\-opt for them. This option can be passed
+multiple times and can be freely mixed with the \-\-dpkg\-source\-opt option.
+Options will be passed to dpkg\-source in the order that the
+\-\-dpkg\-source\-opt and \-\-dpkg\-source\-opts options are given on the
+command line.
+This command line option appends to the \fBDPKG_SOURCE_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.br
+\fBNOTE:\fR The '\fI\-b\fP', '\fI\-\-before\-build\fP' and '\fI\-\-after\-build\fP'
+options will always be passed to dpkg\-source, respectively.
+.TP
+.BR "\-\-dpkg\-file\-suffix=\fIsuffix\fP"
+Add the suffix to the filename of the changes and buildinfo files generated by
+dpkg.
+.br
+\fBNOTE:\fR This option is ignored if dpkg\-dev in the build environment is too
+old to support it. At least dpkg\-dev 1.18.11 is required.
+.TP
+.BR "\-\-mail\-log\-to=\fIemail-address\fP"
+Send the build log to the specified email address.
+This command line option sets the \fBMAILTO\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-mailfrom=\fIemail-address\fP"
+Email address used as the sender address for build logs.
+This command line option sets the \fBMAILFROM\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-n ", " "\-\-nolog"
+Do not create a package log file in the \fI$log_dir\fP directory and no build
+log file, but print everything to stdout. Also do not send any log mails.
+This command line option sets the \fBNOLOG\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-profiles=\fIprofile[,...]\fP"
+Specify the profile(s) we build, as a comma-separated list. Defaults to the
+space separated list of profiles in the \fBDEB_BUILD_PROFILES\fP environment
+variable when building natively or the \fBcross\fP and \fBnocheck\fP profiles
+when cross-building.
+This command line option sets the \fBBUILD_PROFILES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-p ", " "\-\-purge=\fIpurge-mode\fP"
+Convenience option to set \fIpurge-mode\fR for build directory, build
+dependencies and session.
+This command line option sets the \fBPURGE_BUILD_DEPS\fP, \fBPURGE_BUILD_DIRECTORY\fP and \fBPURGE_SESSION\fP configuration variables. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-purge\-build=\fIpurge-mode\fP"
+\fIpurge-mode\fR determines if the build directory will be deleted after a
+build. Possible values are \fBalways\fR (default), \fBnever\fR, and
+\fBsuccessful\fR.
+This command line option sets the \fBPURGE_BUILD_DIRECTORY\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-purge\-deps=\fIpurge-mode\fP"
+\fIpurge-mode\fR determines if the build dependencies will be removed after a
+build. Possible values are \fBalways\fR (default), \fBnever\fR, and
+\fBsuccessful\fR.
+This command line option sets the \fBPURGE_BUILD_DEPS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-purge\-session=\fIpurge-mode\fP"
+Purge the schroot session following a build. This is useful in conjunction
+with the \fI\-\-purge\-build\fP and \fI\-\-purge\-deps\fP options when using
+snapshot chroots, since by default the snapshot will be deleted. Possible
+values are \fBalways\fR (default), \fBnever\fR, and \fBsuccessful\fR.
+This command line option sets the \fBPURGE_SESSION\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-s ", " "\-\-source"
+Build the source package in addition to the other requested build artifacts. By
+default, the dsc will not be rewritten because the source package is the input
+to sbuild, not its output. Even when running from an unpacked source tree
+sbuild will first build the source package using dpkg\-source and then pass that
+on to the sbuild machinery. Use this option only when you know what you are
+doing. This will rewrite the original dsc passed to sbuild.
+See the section
+.BR "BUILD ARTIFACTS"
+for more information.
+This command line option sets the \fBBUILD_SOURCE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-no\-source"
+Don't rebuild the source package. This is the default. It is the opposite of
+\-\-source.
+See the section
+.BR "BUILD ARTIFACTS"
+for more information.
+This command line option sets the \fBBUILD_SOURCE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-force\-orig\-source"
+When used with in conjunction with \-s, this option forces the inclusion of the
+orig.tar.gz file in the generated .changes file, even in cases where it would
+not normally be included, i.e. use dpkg\-buildpackage \-sa.
+This command line option sets the \fBFORCE_ORIG_SOURCE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-use\-snapshot"
+Installs the latest snapshot gcc compiler from the \fIgcc\-snapshot\fP package,
+and alters the build environment to use the snapshot compiler for the build.
+Specifically, this option appends \fI/usr/lib/gcc\-snapshot/lib\fP to the value
+of the \fBLD_LIBRARY_PATH\fP configuration variable and
+\fI/usr/lib/gcc\-snapshot/bin\fP to the value of the \fBPATH\fP configuration
+variable. It also sets the \fBGCC_SNAPSHOT\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-v ", " "\-\-verbose"
+Be verbose, i.e. all information goes to stdout as well as to the log files.
+.TP
+.BR \-q ", " "\-\-quiet"
+Be quiet. This is the opposite of \-\-verbose.
+.TP
+.BR "\-\-make\-binNMU=\fIchangelog-entry\fP"
+With this option, \fBsbuild\fR will create a new changelog entry in
+debian/changelog of every package built. The version number will be in the
+format for binary-only NMUs (see \-\-binNMU); the maintainer is set to the
+maintainer name configured for \fBsbuild\fR. \fIchangelog-entry\fR will be used
+as the changelog entry following \[lq]Binary-only non-maintainer upload for
+ARCH -- no source changes\[rq]. Please note that the versions in the
+\fIPACKAGE_VERSION[.dsc]\fR arguments still have to be the unmodified (non-NMU
+ones) so that the sources can be found. The version number in log files and
+mails will be modified by \fBsbuild\fR automatically.
+The \-\-append\-to\-version option has a similar effect but allows one to
+specify an arbitrary version suffix instead of a custom changelog entry. To
+have a custom version suffix and a custom changelog entry, use \-\-make\-binNMU
+and \-\-append\-to\-version at the same time with \-\-binNMU=0.
+This option is incompatible with \-\-binNMU\-changelog.
+This option implies \-\-no\-arch\-all.
+This command line option sets the \fBBIN_NMU\fP configuration variable and sets the \fBBIN_NMU_VERSION\fP configuration variable to 1 if it was not set yet, for example by the \-\-binNMU option. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-binNMU=\fINMU-version\fP"
+The version number of the binary NMU. This option only has an effect if
+combined with \-\-make\-binNMU and/or with \-\-append\-to\-version.
+\fIversion\fP is a single number for the (+b\fIn\fR) format
+used for binary NMUs.
+If the argument is the empty string or zero, then the +b\fIn\fR suffix will not
+be appended.
+The +b\fIn\fR suffix will be appended after the string given via \-\-append\-to\-version.
+This option is incompatible with \-\-binNMU\-changelog.
+This command line option sets the \fBBIN_NMU_VERSION\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-append\-to\-version=\fIstring\fP"
+This option is similar to \-\-make\-binNMU except that it allows the user to
+specify an arbitrary string to be appended to the version number (immediately
+before the '+' in the Debian revision if \-\-make\-binNMU is also provided).
+To pass an arbitrary changelog text as well, combine this option with
+\-\-make\-binNMU but be aware that this will also add the +b\fIn\fR suffix
+unless you also pass \-\-binNMU=0 to disable it.
+This option is incompatible with \-\-binNMU\-changelog.
+This option implies \-\-no\-arch\-all.
+This command line option sets the \fBAPPEND_TO_VERSION\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-binNMU\-timestamp=\fItimestamp\fP"
+Set the timestamp of the new binNMU changelog entry. By default, the time of
+the build will be used to generate the binNMU changelog timestamp. This option
+allows one to use a custom timestamp instead. The timestamp is either given as
+an integer in Unix time or as a string in the format compatible with Debian
+changelog entries (i.e. as it is generated by date \-R).
+This option is incompatible with \-\-binNMU\-changelog.
+This command line option sets the \fBBIN_NMU_TIMESTAMP\fP configuration
+variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-binNMU\-changelog=\fIchangelog\fP"
+Set the complete content of a binary-only changelog entry. This option allows
+full customization of the new changelog entry. It is up to the user to make
+sure that the changelog entry is well-formed. The argument has to include
+all necessary newlines. Leading and trailing newlines will be stripped.
+Sbuild will not interpret any backslash escapes.
+This option is incompatible with \-\-make\-binNMU, \-\-binNMU,
+\-\-append\-to\-version and \-\-binNMU\-timestamp.
+This command line option sets the \fBBIN_NMU_CHANGELOG\fP configuration
+variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-build\-dir=\fIdirectory\fP"
+Set the output directory for the build artifacts created by dpkg\-buildpackage
+and the log file. By default, the current directory is used or, when sbuild is
+executed from within an unpacked source directory, the parent directory.
+This command line option sets the \fBBUILD_DIR\fP configuration
+variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-clean\-source
+When executing sbuild from within an unpacked source tree, execute the
+debian/rules clean target. This is the default and might require some of the
+build dependencies installed on the host.
+This command line option sets the \fBCLEAN_SOURCE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-no\-clean\-source
+When executing sbuild from within an unpacked source tree, do not run the
+debian/rules clean target before building the source package. Only set this if
+you start from a clean checkout and you know what you are doing.
+This command line option sets the \fBCLEAN_SOURCE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-run\-lintian
+Run lintian after a successful build.
+This command line option sets the \fBRUN_LINTIAN\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-no\-run\-lintian
+Don't run lintian after a successful build. If sbuild is configured to run
+lintian by default, this option will prevent lintian being run.
+This command line option sets the \fBRUN_LINTIAN\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-lintian\-opt=\fIoptions\fP
+Pass the specified option directly to lintian in addition to the options
+already passed by sbuild. This option can be passed multiple times (once per
+lintian option) and can be freely mixed with the \-\-lintian\-opts option.
+Options will be passed to lintian in the order that the \-\-lintian\-opt and
+\-\-lintian\-opts options are given on the command line.
+This command line option appends to the \fBLINTIAN_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-lintian\-opts=\fIoptions\fP
+Pass the specified options directly to lintian in addition to the options
+already passed by sbuild. The argument will be split by whitespaces and the
+resulting array passed to the lintian invocation. If any options contain
+spaces, use \-\-lintian\-opt for them. This option can be passed multiple
+times and can be freely mixed with the \-\-lintian\-opts option. Options will
+be passed to lintian in the order that the \-\-lintian\-opt and
+\-\-lintian\-opts options are given on the command line.
+This command line option appends to the \fBLINTIAN_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-run\-piuparts
+Run piuparts after a successful build.
+This command line option sets the \fBRUN_PIUPARTS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-no\-run\-piuparts
+Don't run piuparts after a successful build. If sbuild is configured to run
+piuparts by default, this option will prevent piuparts being run.
+This command line option sets the \fBRUN_PIUPARTS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-piuparts\-opt=\fIoptions\fP
+Pass the specified option directly to piuparts in addition to the options
+already passed by sbuild. This option can be passed multiple times (once per
+piuparts option) and can be freely mixed with the \-\-piuparts\-opts option.
+Options will be passed to piuparts in the order that the \-\-piuparts\-opt and
+\-\-piuparts\-opts options are given on the command line.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.BR "OPTION STRING PERCENT ESCAPES"
+for more information.
+This command line option appends to the \fBPIUPARTS_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-piuparts\-opts=\fIoptions\fP
+Pass the specified options directly to piuparts in addition to the options
+already passed by sbuild. The argument will be split by whitespaces and the
+resulting array passed to the piuparts invocation. If any options contain
+spaces, use \-\-piuparts\-opt for them. This option can be passed multiple
+times and can be freely mixed with the \-\-piuparts\-opts option. Options will
+be passed to piuparts in the order that the \-\-piuparts\-opt and
+\-\-piuparts\-opts options are given on the command line.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.BR "OPTION STRING PERCENT ESCAPES"
+for more information.
+This command line option appends to the \fBPIUPARTS_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-piuparts\-root\-arg=\fIoptions\fP
+Add an argument that is used to launch piuparts as root. Without this option,
+the default is to use "sudo \-\-" to launch piuparts. If an empty string is
+supplied, then piuparts is launched without any prefixed command. This option
+can be specified multiple times. This command line option appends to the
+\fBPIUPARTS_ROOT_ARGS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-piuparts\-root\-args=\fIoptions\fP
+Add arguments that are used to launch piuparts as root. Without this option,
+the default is to use "sudo \-\-" to launch piuparts. If an empty string is
+supplied, then piuparts is launched without any prefixed command. The argument
+will be split by whitespaces. To pass options containing whitespaces use the
+option \-\-piuparts\-root\-arg. This command line option appends to the
+\fBPIUPARTS_ROOT_ARGS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-run\-autopkgtest
+Run autopkgtest after a successful build. This command line option sets the
+\fBRUN_AUTOPKGTEST\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-no\-run\-autopkgtest
+Don't run autopkgtest after a successful build. If sbuild is configured to run
+autopkgtest by default, this option will prevent autopkgtest being run. This
+command line option sets the \fBRUN_AUTOPKGTEST\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-autopkgtest\-opt=\fIoptions\fP
+Pass the specified option directly to autopkgtest in addition to the options
+already passed by sbuild. This option can be passed multiple times (once per
+autopkgtest option) and can be freely mixed with the \-\-autopkgtest\-opts
+option. Options will be passed to autopkgtest in the order that the
+\-\-autopkgtest\-opt and \-\-autopkgtest\-opts options are given on the command
+line.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.BR "OPTION STRING PERCENT ESCAPES"
+for more information.
+This command line option appends to the \fBAUTOPKGTEST_OPTIONS\fP
+configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-autopkgtest\-opts=\fIoptions\fP
+Pass the specified options directly to autopkgtest in addition to the options
+already passed by sbuild. The argument will be split by whitespaces and the
+resulting array passed to the autopkgtest invocation. If any options contain
+spaces, use \-\-autopkgtest\-opt for them. This option can be passed multiple
+times and can be freely mixed with the \-\-autopkgtest\-opts option. Options
+will be passed to autopkgtest in the order that the \-\-autopkgtest\-opt and
+\-\-autopkgtest\-opts options are given on the command line.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.BR "OPTION STRING PERCENT ESCAPES"
+for more information.
+This command line
+option appends to the \fBAUTOPKGTEST_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-autopkgtest\-root\-arg=\fIoptions\fP
+Add an argument that is used to launch autopkgtest as root. Without this
+option, the default is to use "sudo \-\-" to launch autopkgtest. If an empty
+string is supplied, then autopkgtest is launched without any prefixed command.
+This option can be specified multiple times. This command line option appends
+to the
+\fBAUTOPKGTEST_ROOT_ARGS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-autopkgtest\-root\-args=\fIoptions\fP
+Add arguments that are used to launch autopkgtest as root. Without this
+option, the default is to use "sudo \-\-" to launch autopkgtest. If an empty
+string is supplied, then autopkgtest is launched without any prefixed command.
+The argument will be split by whitespaces. To pass options containing
+whitespaces use the option \-\-autopkgtest\-root\-arg. This command line
+option appends to the \fBAUTOPKGTEST_ROOT_ARGS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-pre\-build\-commands=\fIstring\fP
+This is the earliest external command which is run right after the chroot
+session has been initialized and before anything else is done (like installing
+the build dependencies). The command is run outside of the chroot. This option
+can be used multiple times to add multiple commands. Certain percent escapes
+are supported. To write a literal percent sign, escape it with another percent
+sign. See the section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-chroot\-setup\-commands=\fIstring\fP
+Run these commands after the chroot and variables have been setup but before
+dependencies are installed. The command is run as root inside of the chroot.
+This option can be used multiple times to add multiple commands. Certain
+percent escapes are supported. To write a literal percent sign, escape it with
+another percent sign. See the
+section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-chroot\-update\-failed\-commands=\fIstring\fP
+Run these commands after any of 'apt\-get update', 'apt\-get upgrade' or 'apt\-get
+dist\-upgrade' failed.
+This hook is not run for updates of the internal sbuild apt repository, the
+repository for extra packages (see \-\-extra\-package) and the repositories given
+via \-\-extra\-repository.
+The environment is intact, and the failure can be
+investigated. Especially %SBUILD_SHELL is useful here. This option can be used
+multiple times to add multiple commands. Certain percent escapes are supported.
+To write a literal percent sign, escape it with another percent sign.See the
+section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-build\-deps\-failed\-commands=\fIstring\fP
+These commands are run if installing the build dependencies has failed directly
+after the failed attempt. The environment is intact, and the failure can be
+investigated. Especially %SBUILD_SHELL is useful here. The command is run as
+root inside the chroot. This option can be used multiple times to add multiple
+commands. Certain percent escapes are supported. To write a literal percent
+sign, escape it with another percent sign. See the section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-starting\-build\-commands=\fIstring\fP
+Run these commands after dependencies are installed, just before the package
+build with dpkg\-buildpackage starts. The command is run as the root user
+inside the chroot. This option can be used multiple times to add
+multiple commands. Certain percent escapes are supported. To write a literal
+percent sign, escape it with another percent sign. See the section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-finished\-build\-commands=\fIstring\fP
+Run these commands immediately after the timed package build finishes. The
+command is run as the root user inside the chroot. This
+option can be used multiple times to add multiple commands. Certain percent
+escapes are supported. To write a literal percent sign, escape it with another
+percent sign. See the section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-build\-failed\-commands=\fIstring\fP
+These commands are run if dpkg\-buildpackage has failed directly after the
+failed attempt. The environment is intact, and the failure can be investigated.
+Especially %SBUILD_SHELL is useful here. The command is run as the root
+user inside the chroot. This option can be used multiple times
+to add multiple commands. Certain percent escapes are supported. To write a
+literal percent sign, escape it with another percent sign. See the section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-chroot\-cleanup\-commands=\fIstring\fP
+Run these commands when a chroot is cleaned up, before build directory is
+purged. The command is run as root inside the chroot. This option can be used
+multiple times to add multiple commands. Certain percent escapes are supported.
+To write a literal percent sign, escape it with another percent sign. See the
+section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-post\-build\-commands=\fIstring\fP
+Run this command after a successful build. The command is run outside of the
+chroot. This option can be used multiple times to add multiple commands.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.TP
+.BR \-\-post\-build\-failed\-commands=\fIstring\fP
+Exactly like the above, but when a build fails.
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-anything\-failed\-commands=\fIstring\fP
+Run these commands for all the \fI\-\-xxx\-failed\-commands\fP options.
+Especially %SBUILD_SHELL is useful here. This option can be used multiple times
+to add multiple commands. Certain percent escapes are supported. To write a
+literal percent sign, escape it with another percent sign. See the section
+.BR "EXTERNAL COMMANDS"
+for more information.
+This command line option appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-log\-external\-command\-output
+Write output from external commands to the build log.
+This command line option sets the \fBLOG_EXTERNAL_COMMAND_OUTPUT\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-log\-external\-command\-error
+Write error output from external commands to the build log.
+This command line option sets the \fBLOG_EXTERNAL_COMMAND_ERROR\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-setup\-hook=\fIhook-script\fP" " " \fBDEPRECATED\fP
+This option is deprecated. Use of this option will add \fIhook-script\fP to the
+external commands to run via \fIchroot\-setup\-commands\fP.
+This command line option sets the \fBCHROOT_SETUP_SCRIPT\fP configuration variable and appends to the \fBEXTERNAL_COMMANDS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR "\-\-build\-dep\-resolver=\fIresolver\fP"
+Use the specified resolver to handle selecting the build dependencies.
+Supported resolvers are \fIapt\fP (the default), \fIaptitude\fP, \fIaspcud\fP,
+\fIxapt\fP, and \fInull\fP. The apt resolver is the most appropriate resolver
+for most users, for building for unstable, stable and other distributions. If
+alternative build dependencies are used (excluding architecture restrictions),
+only the first alternative will be used; the others will be ignored. The
+aptitude resolver is very similar, but smarter and slower, and it will consider
+all alternatives by default; it is suited to more complex situations, such as
+building packages for the experimental distribution, where packages need
+installing from multiple suites (\fIunstable\fP and \fIexperimental\fP). Due
+to performance issues, aptitude is not recommended for
+use by default. If the dependency situation is so complex that neither apt nor
+aptitude are able to find a solution, then you can use the aspcud resolver.
+This resolver uses apt\-cudf to ask aspcud, a real solver (in the math sense),
+to find a solution to the installation problem. Since aspcud uses a real solver
+(an ASP solver) it will always find a solution if one exists. The solution
+found by the aspcud resolver can be refined by changing the default
+optimization criteria through the \-\-aspcud\-criteria option. The xapt resolver
+is intended only for cross-building, and is a temporary transitional feature
+which will be removed following the complete introduction of multi-arch
+support. Finally, the null resolver is a dummy solver which does not install,
+upgrade or remove any packages. This allows one to completely control package
+installation via hooks.
+This command line option sets the \fBBUILD_DEP_RESOLVER\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-aspcud\-criteria=\fIcriteria\fP
+Optimization criteria in extended MISC 2012 syntax passed to aspcud through
+apt\-cudf. Optimization criteria are separated by commas, sorted by decreasing
+order of priority and are prefixed with a polarity (+ to maximize and - to
+minimize). The default criteria is \fB\-removed,\-changed,\-new\fP which first
+minimizes the number of removed packages, then the number of changed packages
+(up or downgrades) and then the number of new packages. A common task is to
+minimize the number of packages from experimental. To do this you can add a
+criteria like \fB\-count(solution,APT\-Release:=/a=experimental/)\fP to the
+default criteria. This will then minimize the number of packages in the
+solution which contain the string \fIa=experimental\fP in the \fIAPT\-Release\fP
+field of the EDSP output created by apt. For more help on how to write
+optimization criteria, see the
+.BR apt\-cudf (1)
+man page. Specifically the help on the \-\-criteria option.
+This command line option sets the \fBASPCUD_CRITERIA\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-resolve\-alternatives
+Allow the use of alternatives in Build\-Depends, Build\-Depends\-Arch and
+Build\-Depends\-Indep. This is the default for the aptitude dependency resolver.
+This command line option sets the \fBRESOLVE_ALTERNATIVES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-no\-resolve\-alternatives
+Do not allow the use of alternatives in Build\-Depends, Build\-Depends\-Arch and
+Build\-Depends\-Indep. Note that alternatives for the same package
+(e.g. different versions) are still allowed. This is the default for the
+apt and xapt dependency resolvers.
+This command line option sets the \fBRESOLVE_ALTERNATIVES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-extra\-package=\fIpackage.deb|directory\fP
+Make \fIpackage.deb\fP available for build-dependency resolution, by
+adding it to a temporary archive created by sbuild. This makes it
+easier to build packages against locally-built build dependencies,
+without waiting for those packages to enter the main archive, or going
+through the hassle of maintaining a local archive and making it
+accessible inside the chroot. \fIpackage.deb\fP is copied into the
+chroot, so it can refer to any path on the host system.
+If a directory is passed instead of a regular file, then all regular files
+inside that directory with a filename that ends in \fI.deb\fP will be added in
+the same fashion as it is done for individual packages.
+This option can be specified multiple times.
+This command line option appends to the \fBEXTRA_PACKAGES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-extra\-repository=\fIspec\fP
+Add a repository to the list of apt sources during the package build.
+The repository specification is a line suitable for an apt
+.BR sources.list (5)
+file. For instance, you might use
+.nh
+.B \-\-extra\-repository="deb http://deb.debian.org/debian experimental main"
+.hy
+to allow packages in the experimental distribution to fulfill
+build-dependencies. Note that the build chroot must already trust the
+key of this repository or a key must be given with the
+.nh
+.B \-\-extra\-repository\-key
+.hy
+flag (see
+.BR apt\-secure (8)).
+This command line option appends to the \fBEXTRA_REPOSITORIES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-extra\-repository\-key=\fIfile.asc\fP
+Add \fIfile.asc\fP to the list of trusted keys inside the chroot. The key is
+read from the filename given, and added to the trusted keys. For more
+information, see
+.BR apt\-secure (8).
+This flag is particularly useful if the target in
+.nh
+.B \-\-extra\-repository
+.hy
+is not signed
+with a key that's trusted by the base chroot.
+This command line option appends to the \fBEXTRA_REPOSITORY_KEYS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-build\-path=\fIstring\fP
+By default the package is built in a path of the following format
+/build/packagename\-XXXXXX/packagename\-version/ where XXXXXX is a random ascii
+string. This option allows one to specify a custom path where the package is
+built inside the chroot. The sbuild user in the chroot must have permissions to
+create the path. Common writable locations are subdirectories of /tmp or
+/build. Using /tmp might be dangerous, because (depending on the chroot
+mode) the /tmp inside the chroot might be a world writable location that can
+be accessed by processes outside the chroot. The directory /build can only be
+accessed by the sbuild user and group and should be a safe location. The
+buildpath must be an empty directory because the last component of the path
+will be removed after the build is finished. Notice that depending on the
+chroot mode (see \-\-chroot\-mode), some locations inside the chroot might be
+bind mounts that are shared with other sbuild instances. You must avoid using
+these shared locations as the build path or otherwise concurrent runs of sbuild
+will likely fail. With the default schroot chroot mode, the directory /build
+is shared between multiple schroot sessions. You can change this behaviour in
+/etc/schroot/sbuild/fstab. The behaviour of other chroot modes will vary.
+This command line option sets the \fBBUILD_PATH\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-dsc\-dir=\fIstring\fP
+By default the package is built in a path of the following format
+/build/packagename\-XXXXXX/packagename\-version/ where packagename\-version are
+replaced by the values in debian/changelog. This option allows one to specify a
+custom packagename\-version path where the package is built inside the chroot.
+This is useful to specify a static path for different versions for example for ccache.
+This command line option sets the \fBDSC_DIR\fP configuration variable. See
+.BR sbuild.conf (5) for more information.
+.TP
+.BR \-\-autopkgtest\-virt\-server=\fIschroot|lxc|chroot|qemu|ssh\fP
+The autopkgtest virtualization server. Can be specified with or without the autopkgtest\-virt\-
+prefix. For instance, the following set of command line options will use the
+autopkgtest\-virt\-schroot chroot mode for a package build:
+.nh
+.B \-\-chroot\-mode=autopkgtest \-\-autopkgtest\-virt\-server=schroot \-\-autopkgtest\-virt\-server\-opt=unstable\-amd64\-sbuild
+.hy
+This command line option sets the \fBAUTOPKGTEST_VIRT_SERVER\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-autopkgtest\-virt\-server\-opt=\fIstring\fP
+Pass the specified option directly to the respective autopkgtest\-virt\-* virtualization
+server in addition to the options already passed by sbuild. This option can be
+passed multiple times (once per autopkgtest\-virt\-* option) and can be freely mixed with
+the \-\-autopkgtest\-virt\-server\-opts option. Options will be passed to the respective
+autopkgtest\-virt\-* virtualization server in the order that the \-\-autopkgtest\-virt\-server\-opt
+and \-\-autopkgtest\-virt\-server\-opts options are given on the command line. See the
+manual pages of the respective autopkgtest\-virt\-* commands for more information.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.BR "OPTION STRING PERCENT ESCAPES"
+for more information.
+This command line option appends to the \fBAUTOPKGTEST_VIRT_SERVER_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-autopkgtest\-virt\-server\-opts=\fIoptions\fP
+Pass the specified options directly to the respective autopkgtest\-virt\-* virtualization
+server in addition to the options already passed by sbuild. The argument will
+be split by whitespaces and the resulting array passed to the autopkgtest\-virt\-*
+invocation. If any options contain spaces, use \-\-autopkgtest\-virt\-server\-opt for
+them. This option can be passed multiple times and can be freely mixed with
+the \-\-autopkgtest\-virt\-server\-opts option. Options will be passed to the
+respective autopkgtest\-virt\-* virtualization server in the order that the
+\-\-autopkgtest\-virt\-server\-opt and \-\-autopkgtest\-virt\-server\-opts options are given on
+the command line. See the manual pages of the respective autopkgtest\-virt\-* commands
+for more information.
+Certain percent escapes are supported. To write a literal percent sign, escape
+it with another percent sign. See the section
+.BR "OPTION STRING PERCENT ESCAPES"
+for more information.
+This command line option appends to the \fBAUTOPKGTEST_VIRT_SERVER_OPTIONS\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-sbuild\-mode=\fImode\fP
+Behaviour changes for use in a buildd environment.
+This command line option sets the \fBSBUILD_MODE\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-stats\-dir=\fIdirectory\fP
+Directory for writing build statistics to.
+This command line option sets the \fBSTATS_DIR\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-purge\-extra\-packages\fP
+This is an experimental option. Only use when you know what you are doing.
+Source packages must be buildable with only their build dependencies, all
+packages marked as Essential:yes, the build\-essential package and their
+transitive dependencies installed. But by default, most chroots will also
+include Priority:required packages and apt as well as their transitive
+dependencies. This option will try to remove all additional packages that are
+not strictly required for the build right after build dependencies were
+installed. This currently works best with the aspcud resolver. The apt resolver
+will not make as much effort to remove all unneeded packages and will keep all
+providers of a virtual package and all packages from any dependency alternative
+that happen to be installed. The aptitude and xapt resolver do not implement
+this feature yet. The removed packages are not (yet) added again after the
+build finished. This can have undesirable side effects like lintian not working
+(because there is no apt to install its dependencies) or bare chroots becoming
+totally unusable after apt was removed from them. Thus, this option should only
+be used with throw-away chroots like schroot provides them where the original
+state is automatically restored after each build. This command line option sets
+the \fBPURGE_EXTRA_PACKAGES\fP configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.TP
+.BR \-\-bd\-uninstallable\-explainer=\fIdose3|apt|none\fP
+If the build dependencies cannot be satisfied by the chosen resolver, sbuild
+will run the selected method to give a better explanation why the build
+dependencies cannot be installed. Possible arguments for this option are dose3
+(the default), apt and none. To disable this feature, pass none or the empty string.
+Depending on the resolver, the dose3 explainer might report a dependency
+situation as satisfiable even if the chosen resolver found it to be
+unsatisfiable. This is especially likely to happen if the apt resolver (the
+default) is used. Such disparities can have two reasons: either the
+understanding of the involved dependency situation of the apt and dose3 solver
+differs (this is a bug) or the apt solver was unable to find a solution if the
+dependency situation is not trivial (for example if it involves packages from
+multiple repositories). In the former case, please report the disparity as a
+bug against the resolvers. In the latter case, use a resolver that is more
+likely to find a solution like the aptitude or aspcud resolvers. Especially the
+aspcud resolver should find a solution if and only if the dose3 solver also
+finds one. This command line option sets the \fBBD_UNINSTALLABLE_EXPLAINER\fP
+configuration variable. See
+.BR sbuild.conf (5)
+for more information.
+.SH CHROOT MODES
+The main purpose of sbuild is to build Debian packages in a clean chroot
+environment. Provisioning and managing these chroot environments is not done by
+sbuild itself but by multiple backends. The default backend (or chroot mode) is
+schroot which is an suid binary that allows regular users to enter a chroot
+environment. But sbuild also allows one to build packages in a qemu virtual
+machine, lxc, lxd or on a remote host reached by ssh using the autopkgtest
+backend. The backend can be chosen using the \f[CB]\-\-chroot\-mode\fP command
+line argument or the \fI$chroot_mode\fP configuration parameter.
+.TP
+.BR schroot
+The default and recommended chroot mode. It is also used on Debian buildd
+machines. The easiest way to set up sbuild for use with the schroot backend is
+by using sbuild\-createchroot which will also write out the necessary schroot
+configuration files in /etc. To use the chroots, the current user has to be
+added to the sbuild group, for example by running sbuild\-adduser. Updating
+these schroot backends can be done using sbuild\-update. See the respective man
+pages for more information about how to use these programs. Schroot supports
+chroots from directories, tarballs, filesystem images and block devices.
+Schroot provides ephemeral chroots either by unpacking a tarball into a
+temporary directory, by using an overlay filesystem for directory chroots or by
+using btrfs or lvm snapshots. Chroots usable by schroot are defined by
+configuration files in /etc/schroot/chroot.d/. When building for a specific
+distribution and architecture, sbuild will choose the chroot that is named (or
+has the alias) \fI$distribution\-$arch\-sbuild\fP, \fI$distribution\-sbuild\fP,
+\fI$distribution\-$arch\fP or \fI$distribution\fP, in that order of preference.
+The used chroot name can be overridden using the \-c or \-\-chroot options.
+.TP
+.BR sudo
+This chroot mode is deprecated and only provided for backwards compatibility
+and testing purposes. It operates by plainly entering the chosen chroot
+directory using "sudo chroot". Thus, this backend also does not provide
+ephemeral chroots. The sudo chroot mode searches for a symlink or directory
+located at \fI/etc/sbuild/chroot/\fP or in the current directory, prefixed with
+\fIchroot\-\fP. The expected names are resolved in the same order as for the
+schroot chroot mode and can be overridden using the \-c or \-\-chroot options.
+.TP
+.BR autopkgtest
+This is an experimental chroot mode that allows one to build packages in any
+chroot supported by autopkgtest. This allows one to build packages in lxc or
+lxd containers, a qemu virtual machine or on a remote host via ssh. Which
+autopkgtest server to use is determined via the
+\f[CB]\-\-autopkgtest\-virt\-server\fP option. Since autopkgtest (in contrast to
+schroot) does not maintain a registry of available containers or (virtual)
+machines, it is necessary to manually specify them using the
+\f[CB]\-\-autopkgtest\-virt\-server\-opts=\fP command line argument. To avoid having
+to manually type the right container or machine name every time when sbuild is
+executed, percent escapes are permitted.
+.TP
+.BR unshare
+This backend allows one to build packages inside chroots provided by arbitrary
+tarballs without superuser privileges. This allows one to set up an arbitrary
+build environment without having to become root. Building packages with schroot
+also doesn't require sudo (schroot is suid root) but setting up and updating
+chroots requires superuser permissions. The unshare backend only makes use of
+two small suid binaries (newuidmap and newgidmap). This backend allows
+arbitrary tarballs containing chroot environments to be used for package
+building. The default tarball location is in ~/.cache/sbuild/. The expected
+names are resolved in the same order as for the schroot chroot mode and can be
+overridden using the \-c or \-\-chroot options.
+
+On buster and earlier Debian releases, unprivileged userns clones
+(/proc/sys/kernel/unprivileged_userns_clone) were disabled by default by means
+of a Debian-specific kernel patch. On these systems, root is required for
+enabling them. This can also done permanently by setting
+kernel.unprivileged_userns_clone=1 in /etc/sysctl.d/.
+.SH BUILD ARTIFACTS
+Sbuild is meant to be used to build architecture specific binary packages from
+a given source package. In addition, sbuild is also able to generate
+architecture independent binary packages as well as to rebuild the original
+source package that was used as input. In summary, sbuild is able to build
+architecture specific binary packages, architecture independent binary packages
+and source packages. What ends up being built is determined by the
+configuration variables \fBBUILD_ARCH_ANY\fR, \fBBUILD_ARCH_ALL\fR and
+\fBBUILD_SOURCE\fR, respectively. See
+.BR sbuild.conf (5)
+for a detailed explanation of these configuration variables.
+.PP
+By default, during native compilation, \fBBUILD_ARCH_ANY\fR and
+\fBBUILD_ARCH_ALL\fR are set to true while \fBBUILD_SOURCE\fR is set to false.
+During cross-compilation, \fBBUILD_ARCH_ALL\fR defaults to false. This
+behaviour can be changed either by using command line options or by modifying
+the configuration variables in your \fI~/.sbuildrc\fP. The relevant command
+line options to change the values of \fBBUILD_ARCH_ANY\fR, \fBBUILD_ARCH_ALL\fR
+and \fBBUILD_SOURCE\fR are \f[CB]\-\-arch\-any/\-\-no\-arch\-any\fP,
+\f[CB]\-\-arch\-all/\-\-no\-arch\-all\fP and \f[CB]\-\-source/\-\-no\-source\fP,
+respectively.
+.PP
+The values of \fBBUILD_ARCH_ANY\fR, \fBBUILD_ARCH_ALL\fR and \fBBUILD_SOURCE\fR
+change the parameter that dpkg\-buildpackage is called with. The following table
+displays the argument passed to dpkg\-buildpackage in the last column depending
+on the configuration options in the first three columns.
+.PP
+.if t \{\
+.ft CW
+\}
+.TS
+l l l l.
+\fBBUILD_ARCH_ANY\fR \fBBUILD_ARCH_ALL\fR \fBBUILD_SOURCE\fR dpkg\-buildpackage flag
+_
+false false false invalid
+false false true \-S
+false true false \-A
+false true true \-g
+true false false \-B
+true false true \-G
+true true false \-b
+true true true no option
+.TE
+.if t \{\
+.in
+.ft P
+\}
+.SH EXTERNAL COMMANDS
+Support to run external commands during an sbuild run is provided. A set of
+external commands can be run at various stages of a build. Providing commands to
+run is done through the appropriate options given on the command line and
+through the use of the configuration files. In the configuration file, the list
+of commands to run are placed in a hash of arrays of arrays of strings
+corresponding to the commands to run.
+.PP
+There are several sets of commands. All command are run inside the chroot as
+root except for the \fIpre/post\-build\-\fP commands which are run as the user
+running sbuild outside of the chroot. To run an external command as another
+user than the root user, prefix your command with \fIrunuser \-u sbuild \-\-\fP.
+.PP
+Here is a summary of the ordering, user, internal/external to chroot for each
+command hook
+.PP
+The following table shows each command hook in the context of the tasks sbuild
+performs. The column \fIroot\fP shows whether the command is run as root (yes)
+or not (no). The column \fIchroot\fP shows whether the command is run inside
+our outside the chroot. The working directory inside the chroot is the one
+marked with \f[CB]<<BUILDDIR>>\fR inside the log. By default, this is a
+directory of the format \f[CB]/build/packagename\-XXXXXX/\f[R] where
+\f[CB]XXXXXX\fR is a random ascii string. Otherwise, it is the directory set
+by \f[CB]\-\-build\-path\fR or by the \f[CB]BUILD_PATH\fR configuration option.
+The working directory outside of the chroot is $HOME. The remaining columns
+show the percent escapes that are defined in each command. Percent escapes
+that are available in all commands (\fB%%\fR, \fB%a\fR, \fB%b\fR, \fB%s\fR) are
+omitted. The value \fImaybe\fP in the column for the \fB%d\fR and \fB%p\fR
+escapes means that the value can not relied upon to be defined in these stages.
+More specifically, these escapes will not be defined at these points if the
+user specified a source package name without a version on the command line. In
+that case, the version will only become known after the source package has been
+retrieved in the "Fetch and unpack source package" stage.
+.PP
+.if t \{\
+.ft CW
+\}
+.TS
+l l l l l l.
+command/action root chroot %c %e %d,%p
+_
+Initialise chroot session
+\f[CB]\-\-pre\-build\-commands\fP no outside no yes maybe
+Setup the chroot and variables
+\f[CB]\-\-chroot\-setup\-commands\fP yes inside no no maybe
+Update and upgrade packages
+\f[CB]\-\-chroot\-update\-failed\-commands\fP yes inside no no maybe
+Fetch and unpack source package
+Install Dependencies
+\f[CB]\-\-build\-deps\-failed\-commands\fP yes inside no no yes
+\f[CB]\-\-starting\-build\-commands\fP yes inside no no yes
+Run dpkg\-buildpackage
+\f[CB]\-\-build\-failed\-commands\fP yes inside no no yes
+\f[CB]\-\-finished\-build\-commands\fP yes inside no no yes
+Run lintian (if configured)
+\f[CB]\-\-chroot\-cleanup\-commands\fP yes inside yes no yes
+Cleanup build files and dependencies
+Run piuparts (if configured)
+Run autopkgtest (if configured)
+Close schroot session
+\f[CB]\-\-post\-build\-commands\fP no outside yes yes yes
+\f[CB]\-\-post\-build\-failed\-commands\fP no outside yes yes yes
+.TE
+.if t \{\
+.in
+.ft P
+\}
+.PP
+The commands can be given in the configuration files. They can be given as
+strings or as a list of arguments. For example, to run "foo" and "bar" with
+arguments before a build starts, specifying the "foo" command as a list and
+"bar" as a string, one could do this:
+.PP
+\f[CB]$external_commands = {\fP
+.br
+\f[CB] "pre\-build\-commands" => [\fP
+.br
+\f[CB] ['foo', 'arg1', 'arg2'],\fP
+.br
+\f[CB] 'bar arg1 arg2 arg3',\fP
+.br
+\f[CB] ],
+.br
+\f[CB]};\fP
+.PP
+Hash keys for commands to run at other stages have the same name as their
+corresponding command-line option name without the preceding '\-\-'.
+.PP
+Here's an example of how to do the same with the previous example, except using
+the \fI\-\-pre\-build\-commands\fP option.
+.PP
+\f[CB]$ sbuild \\\fP
+.br
+\f[CB] \-\-pre\-build\-commands='foo arg1 arg2' \\\fP
+.br
+\f[CB] \-\-pre\-build\-commands='bar arg1 arg2 arg3'\fP
+.PP
+Note that all these commands are executed through the shell in "/bin/sh". If
+specifying the command as a list in the config file, very few shell facilities
+are supported: no redirection, no command concatenation with ; and so on. When
+passing a string (in the config file or on the commandline), the string is
+passed as-is to the shell. So all shell facilities are available, given that
+you escape everything properly, as you would in an interactive shell.
+.PP
+Besides running external commands, sbuild can also detect the use of certain
+percent escapes given as arguments. These are used to allow for a command to be
+supplied with a certain argument depending on the escape given.
+For example, it could be possible to have an external command be given the
+path to a .changes file.
+.PP
+Here is a listing of keywords and a description of what it's converted to.
+.TP
+\fB%%\fR
+Used to escape a '\fI%\fP'.
+.TP
+\fB%d\fR, \fB%SBUILD_DSC\fR
+These escapes are converted to the absolute path to a package's .dsc file.
+.TP
+\fB%c\fR, \fB%SBUILD_CHANGES\fR
+These escapes are converted to the absolute path to a package's source .changes
+file. This is the .changes file generated by the dpkg\-buildpackage invocation
+and not the source-only .changes file that might've been produced additionally
+via \-\-source\-only\-changes. This variable is only set after the build is
+finished, i.e in \f[CB]\-\-chroot\-cleanup\-commands\fP,
+\f[CB]\-\-post\-build\-commands\fP, and
+\f[CB]\-\-post\-build\-failed\-commands\fP.
+.TP
+\fB%a\fR, \fB%SBUILD_HOST_ARCH\fR
+These escapes are converted to the debian name of the architecture the build
+is being built for (e.g amd64, armhf).
+.TP
+\fB%e\fR, \fB%SBUILD_CHROOT_EXEC\fR
+These escapes are converted to a command which can be executed on a host and
+can be given arguments which will then be executed inside the chroot. Standard
+input and output of the process started inside the chroot are connected to the
+program executed on the host. Thus, this command can also be used to copy data
+into the chroot and out of the chroot. The working directory of the process
+started inside the chroot is the root directory of the chroot. The process is
+started as the root user. This variable is not set if the external command is
+run inside the chroot. Thus this escape is only available for
+\f[CB]\-\-pre\-build\-commands\fP,
+\f[CB]\-\-post\-build\-commands\fP, and
+\f[CB]\-\-post\-build\-failed\-commands\fP.
+.TP
+\fB%b\fR, \fB%SBUILD_BUILD_DIR\fR
+These escapes are converted to the absolute path to the build directory inside
+the chroot.
+.TP
+\fB%p\fR, \fB%SBUILD_PKGBUILD_DIR\fR
+These escapes are converted to the absolute path to the package build directory
+inside the chroot.
+.TP
+\fB%s\fR, \fB%SBUILD_SHELL\fR
+This is converted to a command to spawn an interactive "bash" shell
+.TP
+\fB%SBUILD_BUILD_ARCH\fR
+This escape is converted to the Debian name of the architecture that the build
+is being run on (e.g amd64, armhf).
+.PP
+Percent escapes are only substituted when an appropriate value is defined for
+them. At other times, it is left unchanged. In practice this means that there
+are only two escapes that are not available in all external commands: \fB%c\fR
+and \fB%e\fR. For example, a .changes file is only defined at the end of a
+build, so using \fI%c\fR will only be substituted for post\-build\-commands and post\-build\-failed\-commands.
+.PP
+Here's an example of using an escape to run a program foo on a .changes file
+after a build is done.
+.PP
+\f[CB]$ sbuild \-\-post\-build\-commands \\\fP
+.br
+\f[CB] 'foo %SBUILD_CHANGES'\fP
+.PP
+And here's an example that will spawn an interactive shell to investigate the
+problem whenever the build failed:
+.PP
+\f[CB]$ sbuild \-\-build\-failed\-commands '%SBUILD_SHELL'\fP
+.PP
+The following example would copy a file from the host into the chroot:
+.PP
+\f[CB]$ sbuild \-\-pre\-build\-commands \\\fP
+.br
+\f[CB] 'cat blub.txt | %SBUILD_CHROOT_EXEC sh \-c "cat > blub.txt"'\fP
+.PP
+One final note, external commands are processed in the order they are given.
+Also, the commands given in a configuration file are processed first, then the
+commands given through the command line options.
+.SH OPTION STRING PERCENT ESCAPES
+Besides for external command strings, percent escapes can also be used in
+custom options passed to piuparts, autopkgtest and the chosen autopkgtest\-virt server.
+This is for example useful for communicating the right chroot backend to
+piuparts or autopkgtest depending on the distribution or architecture the
+source package was built for.
+.PP
+Here is a listing of keywords and a description of what it's converted to.
+.TP
+\fB%%\fR
+Used to escape a '\fI%\fP'.
+.TP
+\fB%a\fR, \fB%SBUILD_HOST_ARCH\fR
+These escapes are converted to the debian name of the architecture the build
+is being built for (e.g amd64, armhf).
+.TP
+\fB%r\fR, \fB%SBUILD_DISTRIBUTION\fR
+The distribution that the source package was built for. This is the value
+recorded in debian/changelog or the value passed via the \-\-dist option.
+Mnemonic: the \fBr\fR is the first letter in "release".
+.PP
+Here is an example that will run piuparts with the right schroot chroot:
+.PP
+\f[CB]$ sbuild \-\-run\-piuparts \\\fP
+.br
+\f[CB] \-\-piuparts\-opts="\-\-schroot=%r\-%a\-sbuild"
+.PP
+Or an example of running autopkgtest with the right schroot chroot:
+.PP
+\f[CB]$ sbuild \-\-run\-autopkgtest \-\-autopkgtest\-root\-args= \\\fP
+.br
+\f[CB] \-\-autopkgtest\-opts="\-\- schroot %r\-%a\-sbuild"
+.PP
+To achieve the same effect via the configuration file, add the following:
+.PP
+\f[CB]$autopkgtest_root_args = '';\fP
+.br
+\f[CB]$piuparts_opts = [ '\-\-schroot=%r\-%a\-sbuild' ];\fP
+.br
+\f[CB]$autopkgtest_opts = [ '\-\-', 'schroot', '%r\-%a\-sbuild' ];\fP
+.PP
+The \-\-autopkgtest\-root\-args option and the $autopkgtest_root_args configuration
+variable are set to the empty string because the default is to run autopkgtest
+with "sudo \-\-" in front of it which is not needed with the schroot autopkgtest
+backend.
+.PP
+.SH LOCAL ARCHIVE
+The apt and aptitude resolvers create a local archive for installing build
+dependencies. This is an internal implementation detail of the build
+dependency resolver, which is not user configurable, and is intended to be
+entirely transparent to the user. The local archive exists only transiently
+during the package build. It does not persist across builds, and it is only
+used to store the dummy dependency packages created for a single build.
+.PP
+The dependency resolvers do the following:
+.IP \[bu]
+Create a dummy dependency package. This contains the Build\-Depends (and
+optionally Build\-Depends\-Arch and Build\-Depends\-Indep) as Depends, and
+Build\-Conflicts (and optionally Build\-Conflicts\-Arch and Build\-Conflicts\-Indep)
+as Conflicts.
+.IP \[bu]
+Install the dummy dependency package into the local archive,
+.IP \[bu]
+Generate the \fIPackages\fP, \fISources\fP and \fIRelease\fP files.
+.IP \[bu]
+Write a \fIsources.list\fP file for the local archive into
+\fI/etc/apt/sources.list.d\fP.
+.IP \[bu]
+Inject the lists directly into \fI/var/lib/apt/lists\fP. This step is to save
+running updating all apt sources which is undesirable during a build; apt and
+aptitude do not support updating a single source at present.
+.IP \[bu]
+Regenerate the apt caches to ensure everything is in sync.
+.IP \[bu]
+Install the dummy dependency package with apt or aptitude; the dummy package is
+pulled from the local apt archive, while all its dependencies are pulled from
+the regular configured apt sources.
+.PP
+At the end of the build, the local archive is removed, along with the rest of
+the build tree.
+.SH EXAMPLES
+Before you use sbuild for the first time, you have to do some setup depending
+on the chroot mode you are using. The default chroot mode is schroot. To use
+sbuild with the schroot backend, you need to add your user to the sbuild group
+and create a schroot chroot. The latter can be accomplished by using
+sbuild\-createchroot(8). After this one time setup,
+you can now use sbuild to build packages like this:
+.PP
+\f[CR]% \f[CB]sbuild \-d unstable bash\fP\fP
+.PP
+Or on a .dsc:
+.PP
+\f[CR]% \f[CB]sbuild \-d unstable bash.dsc\fP\fP
+.PP
+Or from within an unpacked source package (the \-d parameter is not necessary
+here because the distribution is inferred from debian/changelog):
+.PP
+\f[CR]% \f[CB]sbuild\fP\fP
+.SH ENVIRONMENT VARIABLES
+The following environment variables are used by \fBsbuild\fR:
+.IP "HOME"
+The home directory of the user.
+.IP "LOGNAME"
+Used in lockfiles.
+.IP "SBUILD_CONFIG"
+Path to an additional configuration file on top of the system wide and user
+specific ones.
+.SH FILES
+.TP
+.I /etc/sbuild/sbuild.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I /etc/sbuild/chroot
+Directory containing symbolic links to chroots. This is only used for sudo
+chroot access; schroot access uses the schroot chroot configuration.
+.TP
+.I ~/.sbuildrc
+User-specific configuration. A custom path to a configuration file can also be
+specified through setting the \fBSBUILD_CONFIG\fP environment variable to the
+path of an additional configuration file.
+.TP
+.I /var/lib/sbuild
+Build trees, archive signing keys, build statistics and lock files.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2009 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR sbuild.conf (5),
+.BR sbuild\-abort (1),
+.BR sbuild\-adduser (8),
+.BR sbuild\-apt (1),
+.BR sbuild\-checkpackages (1),
+.BR sbuild\-createchroot (8),
+.BR sbuild\-distupgrade (1),
+.BR sbuild\-hold (1),
+.BR sbuild\-setup (7).
+.BR sbuild\-shell (1),
+.BR sbuild\-unhold (1),
+.BR sbuild\-update (1),
+.BR sbuild\-upgrade (1),
+.BR schroot (1),
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/man/sbuild.conf.5.in b/man/sbuild.conf.5.in
new file mode 100644
index 0000000..03b7d80
--- /dev/null
+++ b/man/sbuild.conf.5.in
@@ -0,0 +1,74 @@
+.\" Copyright © 2011 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/>.
+.so defs.man
+.TH SBUILD.CONF 5 "\*[RELEASE_DATE]" "Version \*[VERSION]" "Debian sbuild"
+.SH NAME
+sbuild.conf \- configuration file for sbuild
+.SH DESCRIPTION
+\fBsbuild.conf\fR is a Perl script which is sourced by sbuild to permit
+configuration. It is run in a sandbox and restricted to setting a small number
+of variables, detailed below, which may be set to configure the behaviour of
+sbuild. Each variable has an internal name, shown in all caps, and a perl
+variable showing the default setting. Note that the internal name is not
+accessible within sbuild.conf; only the variable may be assigned. Also note
+that some of the defaults are set to be unique for each build, and so some of
+the defaults shown here are unique to the user and system used to build sbuild,
+and will be different for your system.
+.PP
+While sbuild permits many aspects of its behaviour to be configured, this
+should not normally be required. The defaults should be adequate for most
+uses, and should only be changed if you are doing something more advanced, or
+have specialist requirements.
+.SH CONFIGURATION
+.so sbuild.conf.man
+.SH FILES
+.TP
+.I /etc/sbuild/sbuild.conf
+Configuration, maintained by the system administrator. This may be used to
+override the defaults.
+.TP
+.I ~/.sbuildrc
+User-specific configuration.
+.SH AUTHORS
+Roman Hodek <Roman.Hodek@informatik.uni\-erlangen.de>.
+.PP
+\fBsbuild\fR is based on debbuild, written by James Troup
+<james@nocrew.org> and has been modified by
+.nf
+Ben Collins <bcollins@debian.org>,
+Ryan Murray <rmurray@debian.org>,
+Francesco Paolo Lovergine <frankie@debian.org>,
+Michael Banck <mbanck@debian.org>, and
+Roger Leigh <rleigh@debian.org>
+.fi
+.SH COPYRIGHT
+.nf
+Copyright \[co] 1998-2000 Roman Hodek <roman\@hodek.net>
+Copyright \[co] 1998-1999 James Troup <troup\@debian.org>
+Copyright \[co] 2003-2006 Ryan Murray <rmurray\@debian.org>
+Copyright \[co] 2001-2003 Rick Younie <younie\@debian.org>
+Copyright \[co] 2003-2004 Francesco Paolo Lovergine <frankie\@debian.org>
+Copyright \[co] 2005 Michael Banck <mbanck\@debian.org>
+Copyright \[co] 2005-2009 Roger Leigh <rleigh\@debian.org>
+.fi
+.SH "SEE ALSO"
+.BR sbuild (1).
+.\"#
+.\"# The following sets edit modes for GNU EMACS
+.\"# Local Variables:
+.\"# mode:nroff
+.\"# fill-column:79
+.\"# End:
diff --git a/scripts/.gitignore b/scripts/.gitignore
new file mode 100644
index 0000000..dd85664
--- /dev/null
+++ b/scripts/.gitignore
@@ -0,0 +1,5 @@
+install-sh
+missing
+config.guess
+config.sub
+depcomp
diff --git a/scripts/global.mk b/scripts/global.mk
new file mode 100644
index 0000000..e619215
--- /dev/null
+++ b/scripts/global.mk
@@ -0,0 +1,23 @@
+# sbuild Makefile template
+#
+#
+# Copyright © 2004-2007 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/>.
+#
+#####################################################################
+
+# Global options for use in all Makefiles.
+perlmoddir = $(PERL_MODULE_DIR) \ No newline at end of file
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644
index 0000000..e8f0b39
--- /dev/null
+++ b/test/.gitignore
@@ -0,0 +1,10 @@
+perl-syntax
+sbuild
+sbuild-checkpackages
+sbuild-clean
+sbuild-distupgrade
+sbuild-hold
+sbuild-stats
+sbuild-update
+sbuild-upgrade
+update
diff --git a/test/Makefile.am b/test/Makefile.am
new file mode 100644
index 0000000..ca1083b
--- /dev/null
+++ b/test/Makefile.am
@@ -0,0 +1,49 @@
+# 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
+
+TESTS = \
+ perl-syntax \
+ $(chroot_checks)
+
+if SBUILD_CHROOT_CHECKS
+chroot_checks = \
+ sbuild-hold \
+ sbuild-update \
+ sbuild-upgrade \
+ sbuild-distupgrade \
+ sbuild-clean \
+ sbuild-checkpackages \
+ sbuild
+endif
+
+EXTRA_DIST = \
+ logging \
+ perl-syntax.in \
+ sbuild-update.in \
+ sbuild-upgrade.in \
+ sbuild-distupgrade.in \
+ sbuild-clean.in \
+ sbuild-hold.in \
+ sbuild-checkpackages.in \
+ sbuild.in \
+ wanna-build
diff --git a/test/logging b/test/logging
new file mode 100644
index 0000000..ee2c8c5
--- /dev/null
+++ b/test/logging
@@ -0,0 +1,10 @@
+Logging test combinations:
+
+user mode with no log dir set
+user mode with log dir set
+buildd mode (log dir always set)
+
+direct download
+dsc
+source in cwd
+source in other directory
diff --git a/test/perl-syntax.in b/test/perl-syntax.in
new file mode 100755
index 0000000..5edb5e5
--- /dev/null
+++ b/test/perl-syntax.in
@@ -0,0 +1,30 @@
+#!/bin/sh
+#
+# perl-syntax: Check validity of perl syntax in programs
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for program in "@abs_top_srcdir@/bin/"*
+do
+ if grep '#!/usr/bin/perl' "$program" 2>&1 >/dev/null; then
+ echo "Checking $program"
+ PERL5LIB="@abs_top_srcdir@/lib" /usr/bin/perl -cw "$program"
+ fi
+done
diff --git a/test/sbuild-checkpackages.in b/test/sbuild-checkpackages.in
new file mode 100755
index 0000000..bd5c405
--- /dev/null
+++ b/test/sbuild-checkpackages.in
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# sbuild-checkpackages: Test sbuild-checkpackages
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ for opt in --list --set --list; do
+ PERL5LIB="@abs_top_srcdir@/lib" "@abs_top_srcdir@/bin/sbuild-checkpackages" "$opt" "$dist"
+ done
+done
diff --git a/test/sbuild-clean.in b/test/sbuild-clean.in
new file mode 100644
index 0000000..b0c25b5
--- /dev/null
+++ b/test/sbuild-clean.in
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# sbuild-clean: Test sbuild-clean
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ PATH="@abs_top_srcdir@/bin:$PATH" PERL5LIB="@abs_top_srcdir@/lib" "@abs_top_srcdir@/bin/sbuild-clean" "$dist"
+done
diff --git a/test/sbuild-distupgrade.in b/test/sbuild-distupgrade.in
new file mode 100644
index 0000000..12a373c
--- /dev/null
+++ b/test/sbuild-distupgrade.in
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# sbuild-distupgrade: Test sbuild-distupgrade
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ PATH="@abs_top_srcdir@/bin:$PATH" PERL5LIB="@abs_top_srcdir@/lib" "@abs_top_srcdir@/bin/sbuild-distupgrade" "$dist"
+done
diff --git a/test/sbuild-hold.in b/test/sbuild-hold.in
new file mode 100755
index 0000000..8097756
--- /dev/null
+++ b/test/sbuild-hold.in
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# sbuild-hold: Test sbuild-hold and sbuild-unhold
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ for prog in sbuild-hold sbuild-unhold; do
+ PERL5LIB="@abs_top_srcdir@/lib" "@abs_top_srcdir@/bin/$prog" "$dist" bash apt
+ done
+done
diff --git a/test/sbuild-update.in b/test/sbuild-update.in
new file mode 100755
index 0000000..e5ca8f6
--- /dev/null
+++ b/test/sbuild-update.in
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# sbuild-update: Test sbuild-update
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ for opt in --update --upgrade --dist-upgrade --clean --autoclean --autoremove -ugdcar; do
+ PERL5LIB="@abs_top_srcdir@/lib" "@abs_top_srcdir@/bin/sbuild-update" "$opt" "$dist"
+ done
+done
diff --git a/test/sbuild-upgrade.in b/test/sbuild-upgrade.in
new file mode 100755
index 0000000..8516451
--- /dev/null
+++ b/test/sbuild-upgrade.in
@@ -0,0 +1,26 @@
+#!/bin/sh
+#
+# sbuild-upgrade: Test sbuild-upgrade
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ PATH="@abs_top_srcdir@/bin:$PATH" PERL5LIB="@abs_top_srcdir@/lib" "@abs_top_srcdir@/bin/sbuild-upgrade" "$dist"
+done
diff --git a/test/sbuild.in b/test/sbuild.in
new file mode 100755
index 0000000..44b1869
--- /dev/null
+++ b/test/sbuild.in
@@ -0,0 +1,48 @@
+#!/bin/sh
+#
+# sbuild: Test package building
+# Copyright © 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/>.
+#
+#######################################################################
+
+set -e
+
+for dist in unstable; do
+ schrootdist="$dist-$(dpkg-architecture -qDEB_HOST_ARCH)-sbuild"
+
+ # General build checks
+ for pkg in bash dadadodo time pong2; do
+ ver="$(schroot --directory=/tmp -c "$schrootdist" -- apt-cache showsrc "$pkg" | grep ^Version: | head -n 1 | sed -e 's/^Version:[[:space:]]*\(.*\)$/\1/')"
+ PATH="@abs_top_srcdir@/bin:$PATH" PERL5LIB="@abs_top_srcdir@/lib" SBUILD_CONFIG="@abs_top_srcdir@/etc/example.sbuildrc" "@abs_top_srcdir@/bin/sbuild" -s -A -d"$dist" "${pkg}_${ver}"
+ done
+
+ # Test batch mode
+ for pkg in bash dadadodo; do
+ ver="$(schroot --directory=/tmp -c "$schrootdist" -- apt-cache showsrc "$pkg" | grep ^Version: | head -n 1 | sed -e 's/^Version:[[:space:]]*\(.*\)$/\1/')"
+ PATH="@abs_top_srcdir@/bin:$PATH" PERL5LIB="@abs_top_srcdir@/lib" SBUILD_CONFIG="@abs_top_srcdir@/etc/example.sbuildrc" "@abs_top_srcdir@/bin/sbuild" --batch -s -A -d"$dist" "${pkg}_${ver}"
+ done
+
+ # Test source building (note: no epoch or revision)
+ for pkg in debianutils; do
+ {
+ ver="$(apt-cache policy "$pkg" | grep "\*\*\*" | awk '{ print $2 }' | sed -e 's/^Version:[[:space:]]*\(.*\)$/\1/')"
+ apt-get source $pkg
+ cd "${pkg}-${ver}"
+ PATH="@abs_top_srcdir@/bin:$PATH" PERL5LIB="@abs_top_srcdir@/lib" SBUILD_CONFIG="@abs_top_srcdir@/etc/example.sbuildrc" "@abs_top_srcdir@/bin/sbuild" --batch -s -A -d"$dist"
+ }
+ done
+done
diff --git a/test/wanna-build b/test/wanna-build
new file mode 100755
index 0000000..d0ad6db
--- /dev/null
+++ b/test/wanna-build
@@ -0,0 +1,130 @@
+#!/usr/bin/perl
+#
+# This is a fake wanna-build. Its sole purpose is to provide a means
+# to test buildd in isolation, without a full wanna-build database and
+# associated intrastructure. This currently only permits testing of
+# buildd, and not buildd-mail or buildd-uploader.
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw(:config no_ignore_case auto_abbrev gnu_getopt);
+
+our $api = 1;
+our $arch = "amd64";
+our $list = undef;
+our $dist = "unstable";
+our $verbose = 0;
+our $command = "take";
+our $user = "buildd";
+our $database = "db";
+
+my @options = (
+ 'api=i' => \$api,
+ 'arch|A=s' => \$arch,
+ 'list|l=s' => sub { $command="list"; $list=$_[1]; },
+ 'dist|d=s' => \$dist,
+ 'verbose|v' => \$verbose,
+ 'building|take' => sub { $command="take" },
+ 'built' => sub { $command="built" },
+ 'attempted' => sub { $command="attempted" },
+ 'needs-build|give-back' => sub { $command="needs-build" },
+ 'info|i' => sub { $command="info" },
+ 'user=s' => \$user,
+ 'database=s' => \$database
+ );
+GetOptions(@options);
+
+my $output = "unknown output for action\n";
+if ($command eq "take") {
+ if ($verbose) {
+ $output = <<"EOF";
+wanna-build c9531211e54a03cff965eebc3fe617ea8539f7ba for sid on amd64
+- bash:
+ - status: ok
+ - pkg-ver: bash_4.2-1
+ - archive: ftp-master
+uupdate transactions: bash sid 4.1-3 --take Installed Building rleigh rleigh
+\$VAR1 = {
+ 'state_days' => '52',
+ 'priority' => 'source',
+ 'rel' => undef,
+ 'state_change' => '2012 Jan 21 16:19:15',
+ 'successtime' => 694,
+ 'permbuildpri' => undef,
+ 'extra_depends' => undef,
+ 'section' => 'shells',
+ 'failed' => undef,
+ 'buildpri' => undef,
+ 'state' => 'Building',
+ 'binary_nmu_changelog' => undef,
+ 'anytime' => 694,
+ 'bd_problem' => undef,
+ 'state_time' => '4510370',
+ 'build_arch_all' => 0,
+ 'version' => '4.1-3',
+ 'package' => 'bash',
+ 'distribution' => 'sid',
+ 'extra_conflicts' => undef,
+ 'installed_version' => '4.2-1',
+ 'notes' => 'out-of-date',
+ 'do_state_change' => 1,
+ 'builder' => 'rleigh',
+ 'old_failed' => undef,
+ 'binary_nmu_version' => undef,
+ 'previous_state' => 'Installed',
+ 'depends' => undef
+ };
+EOF
+ } else {
+ $output = <<"EOF";
+- bash:
+ - status: ok
+ - pkg-ver: bash_4.2-1
+ - archive: ftp-master
+update transactions: bash sid 4.2-1 --take Needs-Build Building rleigh rleigh
+EOF
+ }
+} elsif ($command eq "built") {
+ $output = <<"EOF";
+update transactions: bash sid 4.2-1 --built Building Built rleigh rleigh
+EOF
+} elsif ($command eq "attempted") {
+ $output = <<"EOF";
+update transactions: bash sid 4.2-1 --attempted Building Build-Attempted rleigh rleigh
+EOF
+} elsif ($command eq "needs-build") {
+ $output = <<"EOF";
+update transactions: bash sid 4.2-1 --give-back Building BD-Uninstallable rleigh rleigh
+EOF
+} elsif ($command eq "info") {
+ $output = <<"EOF";
+bash:
+ Package : bash
+ Version : 4.2-1
+ Builder : buildd_amd64-barber
+ State : Needs-Build
+ Section : shells
+ Priority : source
+ Installed-Version : 4.1-3
+ Previous-State : Installed
+ State-Change : 2011-11-30 11:26:24.40323
+ Build-time : 694
+ Build-Arch-All : 0
+ CalculatedPri : 52
+ component : main
+ Distribution : sid
+ Notes : out-of-date
+ State-Days : 52
+ State-Time : 4510444
+ Success-build-time : 694
+EOF
+} elsif ($command eq "list" && $list eq "needs-build") {
+ $output = <<"EOF";
+libs/bash_4.2-1 [optional:out-of-date:calprio{49}:days{0}]
+Total 1 package(s)
+EOF
+}
+
+print "$output";
diff --git a/tools/Makefile.am b/tools/Makefile.am
new file mode 100644
index 0000000..43ccb82
--- /dev/null
+++ b/tools/Makefile.am
@@ -0,0 +1,28 @@
+# 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
+
+noinst_SCRIPTS = \
+ sbuild-dumpconfig
+
+EXTRA_DIST = \
+ $(noinst_SCRIPTS)
diff --git a/tools/sbuild-dumpconfig b/tools/sbuild-dumpconfig
new file mode 100755
index 0000000..05dea19
--- /dev/null
+++ b/tools/sbuild-dumpconfig
@@ -0,0 +1,191 @@
+#!/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/>.
+#
+#######################################################################
+
+use strict;
+use warnings;
+
+use Sbuild::Conf qw();
+use Buildd::Conf qw();
+use Sbuild::Sysconfig;
+use Text::Wrap;
+use Data::Dumper;
+
+die "Usage: $0 sbuild|buildd config|man" if @ARGV != 2;
+
+my $program = $ARGV[0];
+my $output = $ARGV[1];
+
+$Sbuild::Sysconfig::paths{'SBUILD_CONF'} = '/invalid';
+$Sbuild::Sysconfig::paths{'BUILDD_CONF'} = '/invalid';
+
+$Data::Dumper::Sortkeys = 1;
+
+my $conf;
+if ($program eq "sbuild") {
+ $conf = Sbuild::Conf::new(CHECK=>0);
+} elsif ($program eq "buildd") {
+ $conf = Buildd::Conf::new(CHECK=>0);
+} else {
+ die "Unsupported configuration type $program";
+}
+exit 1 if !defined($conf);
+
+
+die "Unsupported output type $output"
+ if ($output ne "config" && $output ne "man");
+
+
+
+my @keys = sort $conf->get_keys();
+
+# print "KEYS: " . join(", ", @keys) . "\n";
+
+my %tmpgroups;
+
+foreach my $key (@keys) {
+# print "KEY: $key, GROUP: " . $conf->_get_group($key) . "\n";
+ $tmpgroups{$conf->_get_group($key)} = 1;
+}
+
+my @groups = sort keys %tmpgroups;
+
+# print "GROUPS: " . join(",\n", @groups) . "\n";
+
+
+my $header = "# ${program}.conf: ${program} settings. -*- Perl -*-\n";
+
+$header .= <<END;
+# Default settings are commented out.
+# Note that all possible settings are listed here. Many may be set on
+# the command-line, and do not normally need setting here,
+# e.g. \$verbose. Others need setting by each individual user in their
+# ~/.${program}rc, but are listed here for completeness.
+END
+
+if ($output eq "config") {
+ print "$header";
+}
+foreach my $group (@groups) {
+ # Don't print internal keys
+ next if $group =~ m/^__/;
+
+ if ($output eq "config") {
+ print "\n\n##\n## $group\n##\n\n";
+ } elsif ($output eq "man") {
+ print ".SS $group\n";
+ }
+
+ foreach my $key (@keys) {
+ if ($conf->_get_group($key) eq $group) {
+ my $type = $conf->_get_type($key);
+ my $varname = $conf->_get_varname($key);
+ my $help = $conf->_get_help($key);
+ my $default = $conf->_get_default($key);
+ my $ignore_default = $conf->_get_ignore_default($key);
+ my $cli_options = $conf->_get_cli_options($key);
+ my $example = $conf->_get_example($key);
+ if ($output eq "config") {
+ print "# $key\n";
+ print "# Type: $type\n";
+ if ($help) {
+ print wrap("# ", "# ", "$help\n");
+ }
+ if ($cli_options) {
+ print "# See also related command line options in sbuild(1):\n";
+ foreach my $opt (@{$cli_options}) {
+ print "# $opt\n";
+ }
+ }
+ if ($example) {
+ foreach my $line (split("\n", $example)) {
+ print "# $line\n";
+ }
+ }
+ if ($ignore_default) {
+ print wrap("#", "#", "$varname = ...;");
+ print("\n");
+ } else {
+ print wrap("#", "#", Data::Dumper->Dump([$default],
+ ["$varname"]));
+ }
+ print("\n");
+ } elsif ($output eq "man") {
+ print ".TP\n";
+ print ".BR $key\n";
+ print "$type type.\n";
+ if ($help) {
+ print "$help\n";
+# print wrap("", "", "$help\n");
+ }
+ if ($cli_options) {
+ print ".IP\n";
+ print "Related\n";
+ print ".BR sbuild (1)\n";
+ print "command line options:\n";
+ print ".PP\n";
+ print ".RS\n";
+ foreach my $opt (@{$cli_options}) {
+ print "\\f[CR]$opt\\fP\n";
+ print ".br\n";
+ }
+ print ".RE\n";
+ }
+ if ($example) {
+ print ".IP\n";
+ print "Example:\n";
+ print ".PP\n";
+ print ".RS\n";
+ foreach my $line (split("\n", $example)) {
+ print "\\f[CR]$line\\fP\n";
+ print ".br\n"
+ }
+ print ".RE\n";
+ }
+ if ($ignore_default) {
+ print ".PP\n";
+ print ".RS\n";
+ print "\\f[CR]$varname = ...;\\fP\n";
+ print ".br\n";
+ print ".RE\n";
+ } else {
+ print ".IP\n";
+ print "Default:\n";
+ print ".PP\n";
+ print ".RS\n";
+ foreach my $line (split("\n",
+ Data::Dumper->Dump([$default],
+ ["$varname"]))) {
+ print "\\f[CR]$line\\fP\n";
+ print ".br\n"
+ }
+ print ".RE\n";
+ }
+ }
+ }
+ }
+}
+if ($output eq "config") {
+ print "1;\n";
+}