<?xml version='1.0' encoding='UTF-8'?>
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
	"http://www.oasis-open.org/docbook/xml/4.5/docbookx.dtd">
<book id="tug">
  <bookinfo>
    <title>The Transactional Update Guide</title>
    <authorgroup>
      <author>
        <firstname>Thorsten</firstname>
        <surname>Kukuk</surname>
        <email>kukuk@thkukuk.de</email>
      </author>
      <author>
        <firstname>Ignaz</firstname>
        <surname>Forster</surname>
        <email>iforster@suse.com</email>
      </author>
    </authorgroup>
    <releaseinfo>Version 0.4, 28. September 2021</releaseinfo>
    <abstract>
      <para>
	This is the documentation for transactional-update and is intended for
	users, administrators and packagers.
      </para>
      <para> It describes how transactional-update with Btrfs works by giving
	an overview of the design, what an administrator needs to know about
	setting up and operating such a system and what a packager needs to
	know for creating compatible packages.
      </para>
      <para>
	For specific usage see the
	<ulink url="https://kubic.opensuse.org/documentation/man-pages/transactional-update.8.html">transactional-update man page</ulink>
	or the
	<ulink url="https://kubic.opensuse.org/documentation/man-pages/">list of Kubic related commands</ulink>.
      </para>
    </abstract>
  </bookinfo>

  <chapter id="tu-introduction">
    <title>Introduction</title>
    <section id="tu-introduction-description">
      <title>Description</title>
      <para>
        <emphasis remap='B'>transactional-update</emphasis>
	is an application that allows to update a Linux system and its
	applications in an <emphasis>atomic</emphasis> way: The update will
	be performed in the background, not influencing the currently
	running system. The update will be activated by	a reboot. This concept
	is similar to
	<ulink url="https://rpm-ostree.readthedocs.io/en/latest/">rpm-ostree</ulink>
	or CoreOS' previous Container OS. However
	<emphasis remap='B'>transactional-update</emphasis> is not another
	package manager, but is reusing the existing system tools such as
	<emphasis>RPM</emphasis> as the packaging format and
	<emphasis>zypper</emphasis> as the package manager. It depends on
	<emphasis>Btrfs</emphasis> due to its snapshotting and
	copy-on-write features.
      </para>
      <para>
	The idea and reason to build up on existing tools is the ability
	to continue using existing packages and tool chains for delivery and
	application of updates. While currently only implemented for (open)SUSE
	environments the concept is vendor independent and may also be
	implemented for other package managers, package formats and file
	systems. It consists of the (open)SUSE specific
	<emphasis remap='B'>transactional-update</emphasis> script and the
	generic <emphasis>tukit</emphasis> library.
      </para>
      <para>
	Conceptually <emphasis>transactional-update</emphasis>
	creates a new snapshot with <emphasis>btrfs</emphasis> before
	performing any update and uses that snapshot for modifications.
	Since btrfs snapshots contain only the difference between two versions
	and thus are usually very small, updates done with
	<emphasis remap='B'>transactional-update</emphasis> are very space
	efficient.
	This also means several snapshots can be installed at the same time
	without a problem.
      </para>
    </section>

    <section id="tu-introduction-definition">
      <title>Definition</title>
      <para>
	A <emphasis>transactional update</emphasis> (also known as
	<emphasis>atomic upgrade</emphasis>) is an update that
      </para>
      <itemizedlist>
	<listitem>
	  <para>
	    is atomic:
	  </para>
	  <itemizedlist>
	    <listitem>
	      <para>
		The update does not influence the running system.
	      </para>
	    </listitem>
	    <listitem>
	      <para>
		The machine can be powered off at any time. When powered on
		again either the unmodified old state or the new state is
		active. It is not possible to have a running system in an
                intermediate state.
	      </para>
	    </listitem>
	  </itemizedlist>
	</listitem>
	<listitem>
	  <para>
	    can be rolled back:
	  </para>
	  <itemizedlist>
	    <listitem>
	      <para>
		If the upgrade fails or if a newer software version turns out
		to not be compatible with your infrastructure, the system can
		quickly be restored to a previous state.
	      </para>
	    </listitem>
	  </itemizedlist>
	</listitem>
      </itemizedlist>
    </section>

    <section id="tu-introduction-reason">
      <title>Use Cases</title>
      <para>
	As Linux distributions are evolving, new concepts are
	emerging, such rolling releases, containers, embedded systems or long
	time support releases. While the classical update mechanisms are
	probably perfectly fine for a regular desktop users or a conventional
	server system, the following example use cases may give an indication
	why an even more error-proof system may be desirable:
      </para>
      <para>
	Distributions with <emphasis role="bold">rolling updates</emphasis> face
	the problem: how should intrusive updates be applied to a running system
	- without breaking the update mechanism itself? Examples like the
	migration from SysV init to systemd, a major version update of a desktop
	environment while the desktop is still running or even only a small
	update to D-Bus may give a good idea of the problem. The desktop
	environment may simply terminate, killing the update process and leaving
	the system in a broken, undefined state. If any update breaks such a
	system there needs to be a quick way to roll back the system to the last
	working state.
      </para>
      <para>
	On <emphasis role="bold">mission critical systems</emphasis> or
	<emphasis role="bold">embedded systems</emphasis> one will usually want
	to make sure that no service or user behaviour interferes with the
	update of the system. Moreover the update should not modify the system,
	e.g. by uncontrolled restarts of services or unexpected modifications to
	the system in post scripts. Potential interruptions are deferred to a
	defined maintenance window instead. For really critical systems the
	update can be verified (e.g. using <command>snapper diff</command>) or
	discarded before actually booting into the new system. If an update
	encounters an error the new snapshot will be discarded automatically.
      </para>
      <para>
	For <emphasis role="bold">cluster nodes</emphasis> it is important that
	the system is always in a consistent state, requires no manual
	interaction and is able to recover itself from error conditions.
	For these systems transactional-updates provides automatic
	updates; snapshots with failed updates will be automatically removed.
	Automatic reboots can be triggered using a variety of different reboot
	methods (e.g. rebootmgr, notify, kured or systemd), making the
        application of the updates cluster aware.
      </para>
      <para>
	To summarize: The update should only be applied if there were no
	errors during the update. If it turns out that the update is
	causing errors (e.g. because of a new kernel version incompatible
	with the hardware) there should be a quick and easy way to roll back
	to the state before the update was applied.
      </para>
    </section>
  </chapter>

  <chapter id="tu-components">
    <title>Components</title>
    <para>
	transactional-update is split into two parts: the (open)SUSE specific
	<emphasis>transactional-update</emphasis> shell script, and the
	generic <emphasis>tukit</emphasis> (including libtukit, the tukit
	command line application and the D-Bus bindings).
    </para>
    <section id="tu-libtukit">
      <title>libtukit.so</title>
      <para>
	libtukit is a C++ library implementing the core functionality of
	<emphasis remap='B'>transactional-update</emphasis>. It is responsible
	for snapshot management, preparing the environment (including
	overlay handling, see <xref linkend="tu-setup-etc" />) and executing
	the command to run within the update environment.
      </para>
      <para>
	The library is designed to be a general purpose library for handling
	transactional systems. It provides methods to create, modify and close
	transactions as well as execute commands within a transaction.
	Currently <emphasis>snapper</emphasis> is the only implemented snapshot
	management option.
      </para>
      <para>
	Applications such as package managers are expected to use this library
	for easily supporting transactional systems. DNF for example is
	supporting transactional systems via the
	<ulink url="https://code.opensuse.org/microos/libdnf-plugin-txnupd">libdnf-plugin-txnupd</ulink>
	plugin.
      </para>
      <para>
	The library also provides C bindings with the same functionality as the
	C++ library.
      </para>
    </section>
    <section id="tu-tukit">
      <title>tukit</title>
      <para>
	tukit is a utility application to call <emphasis>libtukit</emphasis>
	functionality from the command line. Applications which do not support
	<emphasis>libtukit</emphasis> directly may use this application as
	a wrapper. This command is not yet intended to be called by the user
	directly, as it does not perform maintenance tasks such as marking a
	snapshot for deletion for now.
      </para>
    </section>
    <section id="tu-tukitd">
      <title>D-Bus Bindings</title>
      <para>
	The <emphasis>libtukit</emphasis> functionality is also available via
	D-Bus interface <emphasis>org.opensuse.tukit</emphasis>. Commands are
	executed asynchronously, returning a signal when the command execution
	is finished.
      </para>
    </section>
    <section id="tu-transactional-update">
      <title>transactional-update</title>
      <para>
	This shell script is an (open)SUSE specific wrapper for handling the
	tasks typical on a transactional system, e.g. installing packages,
	updating the system or updating the bootloader. To do so it is using
	the tukit wrapper to call applications such as
	<emphasis>zypper</emphasis> for package management.
      </para>
    </section>
  </chapter>

  <chapter id="tu-howitworks">
    <title>High Level Concept</title>
    <section id="tu-concept">
      <title>The root file system</title>
      <para>
	This chapter describes the handling of the root file system. In general
	<emphasis remap='B'>transactional-update</emphasis> will not modify any
	other subvolumes or file systems: Information stored on these mounts
	(such as <filename class='directory'>/var</filename> or
	<filename class='directory'>/home</filename>) is usually not supposed
	to be rolled back. See <xref linkend="tu-setup" /> for a real world
	setup.
	There are exceptions for a few dedicated subvolumes such as
	<filename class='directory'>/boot/grub2/x86_64-efi</filename> which
	also have to be available to be	able to update the bootloader; these
	directories will be bind mounted into the update environment.
      </para>
      <para>
	<emphasis remap='B'>transactional-update</emphasis> is based around
	several concepts of the <emphasis>Btrfs</emphasis> file system, a
	general purpose Copy-on-Write (Cow) filesystem with snapshot and
	subvolume support, though support for other file systems is possible
	(see <xref linkend="tu-porting" /> for requirements and porting
	information).
      </para>
    </section>
    <section id="tu-howitworks-chroot">
      <title>Updating the correct snapshot</title>
      <para>
	transactional-update is wrapping all binaries which will modify the
	file system with <command>tukit</command>, which will in turn use
	<command>chroot</command> to execute the command in the new snapshot.
	That way e.g. <emphasis>zypper</emphasis> will install packages
	into the new snapshot only.
      </para>
    </section>
    <section id="tu-howitworks-example">
      <title>Workflow</title>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-Start.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots</phrase>
        </textobject>
	<caption>
	  <para>
	    In the beginning there is a list of old snapshots, each one based
	    on the previous one, and the newest one is the current root
	    file system.
	  </para>
	</caption>
      </mediaobject>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-Step2.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots with a read-write clone of the current root file system.</phrase>
        </textobject>
	<caption>
	  <para>
	    In the first step, a new snapshot of the current root
	    file system will be created. This snapshot is set read write.
	  </para>
	</caption>
      </mediaobject>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-Step3.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots with a read-write clone of current root
	  file system, which will be updated with zypper.</phrase>
        </textobject>
	<caption>
	  <para>
	    In the second step the snapshot will be updated. This can be
	    <emphasis>zypper up</emphasis> or <emphasis>zypper dup</emphasis>,
	    the installation or removal of a package or any other modification
	    to the root file system.
	  </para>
	</caption>
      </mediaobject>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-Step4.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots with the clone set read-only.</phrase>
        </textobject>
	<caption>
	  <para>
	    In the third step the snapshot will be changed back to read-only,
	    so that the data cannot be modified anymore.
	  </para>
	</caption>
      </mediaobject>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-Step5.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots with the read-only clone set as the new default.</phrase>
        </textobject>
	<caption>
	  <para>
	    The last step is to mark the updated snapshot as new root
	    file system. This is the atomic step: If the power would have
	    been pulled before, the unchanged old system would have been
	    booted. Now the new, updated system will boot.
	  </para>
	</caption>
      </mediaobject>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-After-Reboot.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots with the current root file system as newest
	  at the end.</phrase>
        </textobject>
	<caption>
	  <para>
	    After reboot, the newly prepared snapshot is the new root
	    file system. In case anything goes wrong a rollback to any of
	    the older snapshots can be performed.
	  </para>
	</caption>
      </mediaobject>
      <mediaobject>
        <imageobject>
          <imagedata fileref="png/Workflow-Without-Reboot.png"/>
        </imageobject>
        <textobject>
          <phrase>List of snapshots with a read-write clone of current root
	  filesystem, which will be updated with zypper.</phrase>
        </textobject>
	<caption>
	  <para>
	    If the system is not rebooted and
	    <emphasis>transactional-update</emphasis> is called again a new
	    snapshot will be created and updated. This new snapshot is based
	    on the <emphasis>current</emphasis> running root filesystem again,
	    <emphasis>not</emphasis> on the new default snapshot!
	    For stacking changes (i.e. if several commands are supposed to be
	    combined in one single snapshot) the <option>shell</option> command
	    can be used to perform any number of operations, or the
	    <option>--continue</option> can be used to continue the latest
	    snapshot while preserving a separate snapshot for each step.
	  </para>
	</caption>
      </mediaobject>
    </section>
    <section id="tu-howitworks-simplified">
      <title>Simplified workflow</title>
      <para>
	In essence the logic of transactional-update can be summarized as
	follows:
      </para>
      <itemizedlist>
	<listitem>
	  <programlisting>
SNAPSHOT_ID=`snapper create --read-write -p -d "Snapshot Update"`
	  </programlisting>
	</listitem>
	<listitem>
	  <programlisting>
zypper -R ${SNAPSHOT_DIR} up|patch|dup|...
	  </programlisting>
	</listitem>
	<listitem>
	  <programlisting>
btrfs property set ${SNAPSHOT_DIR} ro true
	  </programlisting>
	</listitem>
	<listitem>
	  <programlisting>
btrfs subvol set-default ${SNAPSHOT_DIR}
	  </programlisting>
	</listitem>
	<listitem>
	  <programlisting>
systemctl reboot
	  </programlisting>
	</listitem>
      </itemizedlist>
    </section>
  </chapter>

  <chapter id="tu-setup">
    <title>System setup</title>
    <section id="tu-setup-readonly">
      <title>Read-only file system</title>
      <para>
	<emphasis remap='B'>transactional-update</emphasis> is typically used
	on a read-only root file system, even though it also supports
	regular read-write systems.
      </para>
    </section>
    <section id="tu-setup-var">
      <title>/var</title>
      <para>
	On a system with snapshot support
	<filename class='directory'>/var</filename> should not be part of the
	root file system, otherwise doing a rollback to a previous state would
	also roll back the <filename class='directory'>/var</filename>
	contents. On a read-only system this directory has to be writable in any
	case, variable data is stored inside.
      </para>
      <para>
	Due to the volatile nature of
	<filename class='directory'>/var</filename> the directory will
	<emphasis>not</emphasis> be mounted into the new snapshot during the
	<emphasis remap='B'>transactional-update</emphasis> run, as this would
	break atomicity: The currently running system depends on the old state
	of the data (imagine a database migration was triggered by a package).
	Any modifications to <filename class='directory'>/var</filename>
	therefore have to be in the new system, i.e. modifying the contents of
	<filename class='directory'>/var</filename> as part of the packaging
	scripts is not allowed.
      </para>
      <para>
	The only exception to this rule are directories: Those will be
	recreated during the first boot into the updated system by the
	<emphasis>create-dirs-from-rpmdb.service</emphasis> helper service. For
	all other cases please use one of the options described in
	<ulink url="https://en.opensuse.org/openSUSE:Packaging_for_transactional-updates">Packaging for transactional-updates</ulink>
	and
	<ulink url="https://en.opensuse.org/openSUSE:Packaging_guidelines#Migration_.2F_Upgrades">Migration / Upgrade</ulink>
	in the Packaging guidelines. If a package is breaking this rule by
	installing files into a directory which is not part of the root file
	system, then a warning message indicating the affected file is
	printed at the end of the
	<emphasis remap='B'>transactional-update</emphasis> run.
      </para>
    </section>
    <section id="tu-setup-etc">
      <title>/etc</title>
      <para>
	<emphasis remap='B'>transactional-update</emphasis> also supports
	write operations to <filename class='directory'>/etc</filename> on an
	otherwise read-only file system. To do so an
	<emphasis>overlayfs</emphasis> layer is put on top of the system's
	<filename class='directory'>/etc</filename> directory. All modified
	configuration files will end up in the current snapshot's overlay in
	<filename class='directory'>/var/lib/overlay/&lt;snapshotnum&gt;/etc</filename>,
	with each snapshot having one associated overlay directory.
      </para>
      <para>
	The mounted <filename class='directory'>/etc</filename> directory will
	typically consist of three (or more) layers. In
	<filename>/etc/fstab</filename> this may look similar as the following
	entry:
	<programlisting>overlay  /etc  overlay  defaults,upperdir=/sysroot/var/lib/overlay/82/etc,lowerdir=/sysroot/var/lib/overlay/81/etc:/sysroot/var/lib/overlay/76/etc:/sysroot/etc,workdir=/sysroot/var/lib/overlay/work-etc,x-systemd.requires-mounts-for=/var,x-systemd.requires-mounts-for=/var/lib/overlay,x-systemd.requires-mounts-for=/sysroot/var,x-systemd.requires-mounts-for=/sysroot/var/lib/overlay,x-initrd.mount  0  0</programlisting>
      </para>
      <para>
	The important options are <option>upperdir</option> and
	<option>lowerdir</option>. <option>upperdir</option> references the
	write layer, i.e. the directory the system is currently writing to. If
	a file in <filename class='directory'>/etc</filename> is opened for
	read access, it will first try to open the file from this directory.
	If the file is not present in this layer, then overlayfs will search
	in the <option>lowerdir</option>s from left to right until it finds
	the file, i.e. the layers are <emphasis>transparent</emphasis>. The
	last layer is the reference layer and always has to have a complete
	copy of <filename class='directory'>/etc</filename>, all the layers
	above just contain changes to that base layer.
      </para>
      <example>
	<title>/etc layers</title>
	<programlisting>
upperdir  (/var/lib/overlay/x/etc)   |       | file2 | file3 | file4 |       |
lowerdir1 (/var/lib/overlay/x-1/etc) |       |       | file3 |       | file5 | file6
lowerdir2 (/etc of snapshot x-2)     | file1 | file2 | file3 |       |       | file6
	</programlisting>
        <para>
	  Let's have another look from the perspective while an update is
	  running. <replaceable>x</replaceable> is the id of the new snapshot
	  where the update is currently being performed.
	  <replaceable>x-1</replaceable> is the id of the currently running
	  snapshot, and <replaceable>x-2</replaceable> is the id of the
	  snapshot before the currently running one.
      </para>
	<para>
	  <filename>file1</filename> is an old file that hasn't been
	  changed in the last two snapshots. Therefore it is only present in the
	  base / lowest overlay. As the file cannot be found in the first two
	  layers, the version from the base layer will be used.</para>
	  <para><filename>file2</filename> has been changed during the update.
	  The currently running system still sees the old version of the file
	  from the base layer, the updated system will see the new version of
	  the file.
	</para>
	<para>
	  <filename>file3</filename> has been changed during the update,
	  but there is also a modification in the currently running system. See
	  the Warning below for details.
	</para>
	<para>
	  <filename>file4</filename> was only added in the new snapshot
	  and will only be available there. The currently running system doesn't
	  know anything about it.
	</para>
	<para>
	  <filename>file5</filename> was added in the current snapshot.
	  It will also be visible in the new snapshot.
	</para>
	<para>
	  <filename>file6</filename> was modified in the current snapshot.
	  It will also be visible in the new snapshot. Basically the same case
	  as <filename>file5</filename>.
	</para>
      </example>
      <para>
	To reduce the number of overlays the contents of the overlays are
	merged into the base layer if a new snapshot is created. Caveat:
	the state of <filename class='directory'>/etc</filename> that is
	synced into the new snapshot is not the	one from the currently
	running system, but the previous one: the files in the current
	system could still be changed before the reboot, and we want these
	changes (e.g. by the admin or a configuration management software)
	to be visible in the new system.
      </para>
      <warning>
	<para>
	  If a file has been modified both in the new snapshot and in the
	  currently running system <emphasis>after the snapshot was
	  created</emphasis>, then the changes done in the currently running
	  system will be lost in the new snapshot (see the
	  <filename>file3</filename> case from the example above).
	</para>
	<para>
	  When the new snapshot is booted for the first time the systemd
	  service <command>transactional-update-etc-cleaner</command> will
	  check for these conflicts and print the results to the journal.
	</para>
      </warning>
      <para>
	If the <option>--continue</option> option is used multiple times to
        extend a
	new snapshot while the system has not been rebooted, and if that
	snapshot is based on the currently active system, then the
	merge will only run for the first snapshot; the additional
	snapshot layers will be added to <option>lowerdir</option>. This is
	to make sure that changes to the running system will still be
	visible after booting into the new system.
      </para>
      <para>
	The above <filename>/etc/fstab</filename> example is showing such a
	case: <option>lowerdir</option> contains two numbered overlay
	directories. The later directory with number <emphasis>76</emphasis>
	indicates the snapshot which was used as a base. As the
	<option>lowerdir</option> contains a second entry with number
	<emphasis>81</emphasis> it means that the <option>--continue</option>
	option has been used before the system was rebooted. Gaps in the
        numbers such
	as seen here may indicate that those snapshots were discarded or a
	rollback to snapshot 76 was performed. The lowest snapshot is always
	<filename class='directory'>/sysroot/etc</filename>, containing the
	root file system's contents.
      </para>
      <para>
	As <filename class='directory'>/etc</filename> is mounted by
	<application>dracut</application> during early boot the options have to
	be prefixed with <emphasis>/sysroot</emphasis>. The
	<command>x-systemd</command> options are setting up the volume's
	<application>systemd</application> dependencies correctly.
      </para>
      <para>
	Overlays no longer referenced by any snapshots will be deleted during
	the <command>transactional-update cleanup-overlays</command> run.
      </para>
    </section>
<!--
    <para>
      Read-only root filesystem or Read-Write filesystem? Requirements for
      RPMs, what is allowed and what not. Config files in /etc with
      overlayfs. Special handling for passwd, shadow, group. Rollback. Strict
      seperation from data and applications.
    </para>
-->
  </chapter>

  <chapter id='tu-files'>
    <title>Files</title>
    <variablelist>
      <varlistentry>
        <term><filename>/usr/etc/transactional-update.conf</filename></term>
        <listitem>
          <para>
	    This is the reference configuration file for
	    <command>transactional-update</command>, containing distribution
	    default values. This file should not be changed by the
	    administrator.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term><filename>/etc/transactional-update.conf</filename></term>
        <listitem>
          <para>
	    To change the default configuration for
	    <command>transactional-update</command> copy or create this file
	    and change the options accordingly. See
	    <ulink url="https://kubic.opensuse.org/documentation/man-pages/transactional-update.conf.5.html">transactional-update.conf(5)</ulink>
	    for a description of the configuration options. Values from this
	    file will overwride the distribution default values.
          </para>
        </listitem>
      </varlistentry>
      <varlistentry>
        <term><filename>/var/lib/overlay/</filename></term>
        <listitem>
          <para>
	    See <xref linkend="tu-setup-etc" /> for an explanation of this
	    directory's contents.
          </para>
        </listitem>
      </varlistentry>
    </variablelist>
  </chapter>

  <chapter id='tu-porting'>
    <title>Porting to other systems</title>
    <para>
      Currently <emphasis>snapper</emphasis> is the only supported snapshot
      implementation, however the code base is prepared to support other
      (file) systems
      as long as they provide snapshot functionality and the ability to boot
      from specific snapshots.
    </para>
  </chapter>

  <chapter id='tu-author'>
    <title>Author/acknowledgments</title>
    <para>
      This document was written by Thorsten Kukuk &lt;kukuk@suse.com&gt;
      with many contributions from Ignaz Forster &lt;iforster@suse.com&gt;.
    </para>
  </chapter>

  <chapter id='tu-copyright'>
    <title>Copyright information for this document</title>
    <para>
    </para>
  </chapter>
</book>
