chiark / gitweb /
chiark-utils (4.2.0) unstable; urgency=low
authorIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 10 Jun 2012 20:40:32 +0000 (21:40 +0100)
committerIan Jackson <ijackson@chiark.greenend.org.uk>
Sun, 10 Jun 2012 20:40:32 +0000 (21:40 +0100)
  * Rename `xacpi-simple' to `xbatmon-simple':
    - rename source file
    - change program name in Makefiles, .gitignore, rules, etc.
    - change program's idea of its own name for usage message
    - look for X resources under both old ane new names
    - provide a compatibility symlink (using dh_link)

# imported from the archive

136 files changed:
COPYING [new file with mode: 0644]
TODO [new file with mode: 0644]
backup/Makefile [new file with mode: 0644]
backup/backuplib.pl [new file with mode: 0644]
backup/bringup [new file with mode: 0755]
backup/checkallused [new file with mode: 0755]
backup/driver [new file with mode: 0755]
backup/examples/chiark/SYMLINKS.tar [new file with mode: 0644]
backup/examples/chiark/expected-diffs [new file with mode: 0644]
backup/examples/chiark/fsys.all [new file with mode: 0644]
backup/examples/chiark/fsys.pt0 [new file with mode: 0644]
backup/examples/chiark/fsys.pt1 [new file with mode: 0644]
backup/examples/chiark/settings.pl [new file with mode: 0644]
backup/examples/chiark/settings.sh [new file with mode: 0755]
backup/examples/chiark/tape.a0 [new file with mode: 0644]
backup/examples/chiark/tape.b0 [new file with mode: 0644]
backup/examples/chiark/tape.b1 [new file with mode: 0644]
backup/examples/chiark/tape.c0 [new file with mode: 0644]
backup/examples/chiark/tape.c1 [new file with mode: 0644]
backup/examples/chiark/tape.d0 [new file with mode: 0644]
backup/examples/chiark/tape.d1 [new file with mode: 0644]
backup/examples/chiark/tape.ka [new file with mode: 0644]
backup/examples/chiark/tape.kb [new file with mode: 0644]
backup/examples/chiark/warnings.now [new file with mode: 0755]
backup/examples/chiark/warnings.soon [new file with mode: 0755]
backup/examples/relativity/bringup-hook [new file with mode: 0755]
backup/examples/relativity/expected-diffs [new file with mode: 0644]
backup/examples/relativity/fsys.all [new file with mode: 0644]
backup/examples/relativity/fsys.pt0 [new file with mode: 0644]
backup/examples/relativity/fsys.pt1 [new file with mode: 0644]
backup/examples/relativity/fsys.pt2 [new file with mode: 0644]
backup/examples/relativity/ifsys.prefixes [new file with mode: 0644]
backup/examples/relativity/ifsys.pt0 [new file with mode: 0644]
backup/examples/relativity/ifsys.pt1 [new file with mode: 0644]
backup/examples/relativity/ifsys.pt2 [new file with mode: 0644]
backup/examples/relativity/settings.pl [new file with mode: 0644]
backup/examples/relativity/tape.ks [new file with mode: 0644]
backup/examples/relativity/tape.kt [new file with mode: 0644]
backup/examples/relativity/tape.s0 [new file with mode: 0644]
backup/examples/relativity/tape.s1 [new file with mode: 0644]
backup/examples/relativity/tape.s2 [new file with mode: 0644]
backup/examples/relativity/tape.t0 [new file with mode: 0644]
backup/examples/relativity/tape.t1 [new file with mode: 0644]
backup/examples/relativity/tape.t2 [new file with mode: 0644]
backup/examples/relativity/tape.u0 [new file with mode: 0644]
backup/examples/relativity/tape.u1 [new file with mode: 0644]
backup/examples/relativity/tape.u2 [new file with mode: 0644]
backup/examples/relativity/tape.v0 [new file with mode: 0644]
backup/examples/relativity/tape.v1 [new file with mode: 0644]
backup/examples/relativity/tape.v2 [new file with mode: 0644]
backup/examples/relativity/warnings.now [new file with mode: 0755]
backup/examples/relativity/warnings.soon [new file with mode: 0755]
backup/full [new file with mode: 0755]
backup/increm [new file with mode: 0755]
backup/iwjbackup.txt [new file with mode: 0644]
backup/labeltape [new file with mode: 0755]
backup/loaded [new file with mode: 0755]
backup/lvm [new file with mode: 0755]
backup/man/checkallused.1 [new file with mode: 0644]
backup/man/driver.1 [new file with mode: 0644]
backup/man/labeltape.1 [new file with mode: 0644]
backup/man/loaded.1 [new file with mode: 0644]
backup/man/takedown.1 [new file with mode: 0644]
backup/man/whatsthis.1 [new file with mode: 0644]
backup/nosnap [new file with mode: 0755]
backup/remount [new file with mode: 0755]
backup/remountrocp [new file with mode: 0755]
backup/snap-common [new file with mode: 0644]
backup/snap-drop [new file with mode: 0755]
backup/snaprsync [new file with mode: 0755]
backup/takedown [new file with mode: 0755]
backup/whatsthis [new file with mode: 0755]
cprogs/Makefile [new file with mode: 0644]
cprogs/dlist.h [new file with mode: 0644]
cprogs/mcastsoundd.c [new file with mode: 0644]
cprogs/myopt.c [new file with mode: 0644]
cprogs/myopt.h [new file with mode: 0644]
cprogs/rcopy-repeatedly.c [new file with mode: 0644]
cprogs/readbuffer.1 [new file with mode: 0644]
cprogs/readbuffer.c [new file with mode: 0644]
cprogs/really.8 [new file with mode: 0644]
cprogs/really.c [new file with mode: 0644]
cprogs/really.testcases [new file with mode: 0755]
cprogs/rwbuffer.c [new file with mode: 0644]
cprogs/rwbuffer.h [new file with mode: 0644]
cprogs/smtpallow.c [new file with mode: 0644]
cprogs/summer.1 [new file with mode: 0644]
cprogs/summer.c [new file with mode: 0644]
cprogs/trivsoundd-start [new file with mode: 0755]
cprogs/trivsoundd.8 [new file with mode: 0644]
cprogs/trivsoundd.c [new file with mode: 0644]
cprogs/usernice.c [new file with mode: 0644]
cprogs/usr-local-src-misc-Makefile [new file with mode: 0644]
cprogs/watershed.c [new file with mode: 0644]
cprogs/with-lock-ex.1 [new file with mode: 0644]
cprogs/with-lock-ex.c [new file with mode: 0644]
cprogs/wrbufcore.c [new file with mode: 0644]
cprogs/writebuffer.1 [new file with mode: 0644]
cprogs/writebuffer.c [new file with mode: 0644]
cprogs/xbatmon-simple.c [new file with mode: 0644]
debian/changelog [new file with mode: 0644]
debian/chiark-backup/conffiles [new file with mode: 0644]
debian/chiark-utils-bin.links [new file with mode: 0644]
debian/compat [new file with mode: 0644]
debian/control [new file with mode: 0644]
debian/copyright [new file with mode: 0644]
debian/rules [new file with mode: 0755]
scripts/ChiarkNNTP.pm [new file with mode: 0644]
scripts/Makefile [new file with mode: 0644]
scripts/cvs-adjustroot [new file with mode: 0755]
scripts/cvs-repomove [new file with mode: 0755]
scripts/cvsweb-list [new file with mode: 0755]
scripts/expire-iso8601 [new file with mode: 0755]
scripts/genspic2gnuplot [new file with mode: 0755]
scripts/gnucap2genspic [new file with mode: 0755]
scripts/hexterm [new file with mode: 0755]
scripts/named-conf [new file with mode: 0755]
scripts/named-conf.8 [new file with mode: 0644]
scripts/ngspice2genspic [new file with mode: 0755]
scripts/nntpid [new file with mode: 0755]
scripts/palm-datebook-reminders [new file with mode: 0755]
scripts/palm-datebook-reminders.1 [new file with mode: 0644]
scripts/random-word [new file with mode: 0755]
scripts/remountresizereiserfs [new file with mode: 0755]
scripts/summarise-mailbox-preserving-privacy [new file with mode: 0755]
settings.make [new file with mode: 0644]
sync-accounts/Makefile [new file with mode: 0644]
sync-accounts/grab-account [new file with mode: 0755]
sync-accounts/grab-account.8 [new file with mode: 0644]
sync-accounts/sync-accounts [new file with mode: 0755]
sync-accounts/sync-accounts-createuser [new file with mode: 0755]
sync-accounts/sync-accounts-createuser.8 [new file with mode: 0644]
sync-accounts/sync-accounts.5 [new file with mode: 0644]
sync-accounts/sync-accounts.8 [new file with mode: 0644]
sync-accounts/sync-accounts.example-bsd [new file with mode: 0644]
sync-accounts/sync-accounts.example-linux [new file with mode: 0644]

diff --git a/COPYING b/COPYING
new file mode 100644 (file)
index 0000000..4432540
--- /dev/null
+++ b/COPYING
@@ -0,0 +1,676 @@
+
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                      TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+  
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                    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
+state 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 3 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/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<http://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<http://www.gnu.org/philosophy/why-not-lgpl.html>.
+
diff --git a/TODO b/TODO
new file mode 100644 (file)
index 0000000..e67e14d
--- /dev/null
+++ b/TODO
@@ -0,0 +1,17 @@
+BACKUP
+======
+
+would be nice someday
+---------------------
+increms after fulls
+replace loaded with idformat
+read/writebuffer setuid --mlock
+whatsthis no cloneandhack
+increm no cloneandhack
+whatsthis listing for zafio and dump archives
+configuration files autogenerator
+
+
+SCRIPTS
+=======
+manpage for gnucap2gnuplot
diff --git a/backup/Makefile b/backup/Makefile
new file mode 100644 (file)
index 0000000..9ca10ba
--- /dev/null
@@ -0,0 +1,70 @@
+# Makefile
+# simple make settings
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+us=    chiark-backup
+
+include ../settings.make
+
+BINSCRIPTS=    checkallused loaded driver takedown whatsthis labeltape \
+               snaprsync
+SHARESCRIPTS=  bringup full increm snap-drop
+SHAREFILES=    backuplib.pl snap-common
+SNAPKINDS=     lvm remount remountrocp nosnap
+
+EXAMPLES=      relativity chiark
+
+all:
+
+install:               all
+               $(INSTALL_DIRECTORY) $(confdir) $(confdir)/snap $(bindir) \
+                       $(sharedir) $(vardir) $(man1dir)
+               set -e; for s in $(BINSCRIPTS); do \
+                       $(INSTALL_SCRIPT) $$s $(bindir)/backup-$$s; done
+               $(INSTALL_SHARE) $(SHAREFILES) $(sharedir)
+               $(INSTALL_SCRIPT) $(SHARESCRIPTS) $(sharedir)
+               set -e; for s in $(SNAPKINDS); do \
+                       d=$(confdir)/snap/$$s; \
+                       test ! -f $$d || d=$$d.dist; \
+                       $(INSTALL_SCRIPT) $$s $$d; done
+
+install-docs:
+               $(INSTALL_DIRECTORY) $(txtdocdir)
+               $(INSTALL_SHARE) iwjbackup.txt $(txtdocdir)/README
+
+install-examples:
+               set -e; for e in $(EXAMPLES); do \
+                       cd examples/$$e; \
+                       $(INSTALL_DIRECTORY) $(exampledir)/$$e; \
+                       $(INSTALL_SHARE) [!A-Z]*[!~] $(exampledir)/$$e; \
+                       if test -f SYMLINKS.tar; then \
+                               exec <SYMLINKS.tar; \
+                               (set -e; cd $(exampledir)/$$e && tar -xf -); \
+                       fi; \
+                       cd ../..; \
+               done
+
+clean:
+               rm -f *~ ./#*# *.o
+
+distclean realclean:   clean
diff --git a/backup/backuplib.pl b/backup/backuplib.pl
new file mode 100644 (file)
index 0000000..cbbe6f4
--- /dev/null
@@ -0,0 +1,285 @@
+# backuplib.pl
+# core common routines
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+require IO::File;
+
+$nice='nice ' if !defined $nice;
+
+sub printdate () {
+    print scalar(localtime),"\n";
+}
+
+# Set status info -- we write the current status to a file 
+# so if we hang or crash the last thing written to the file
+# will tell us where we were when things went pear-shaped.
+sub setstatus ($) {
+    open S, ">this-status.new" or die $!;
+    print S $_[0],"\n" or die $!;
+    close S or die $!;
+    rename "this-status.new","this-status" or die $!;
+}
+
+# startprocess, endprocesses, killprocesses are 
+# used to implement the funky pipeline stuff.
+sub startprocess ($$$) {
+    my ($i,$o,$c) = @_;
+    pboth("  $c\n");
+    defined($p= fork) or die $!;
+    if ($p) { $processes{$p}= $c; return; }
+    open STDIN,"$i" or die "$c stdin $i: $!";
+    open STDOUT,"$o" or die "$c stdout $o: $!";
+    &closepipes;
+    exec $c; die "$c: $!";
+}
+
+sub rewind_raw () {
+    runsystem("mt -f $tape rewind");
+}
+
+sub readtapeid_raw () {
+    open T, ">>TAPEID" or die $!; close T;
+    unlink 'TAPEID' or die $!;
+    rewind_raw();
+    system "mt -f $tape setblk $blocksizebytes"; $? and die $?;
+    system "dd if=$tape bs=${blocksize}b count=10 ".
+          "| tar -b$blocksize -vvxf - TAPEID";
+}
+
+sub runsystem ($) {
+    pboth("    $_[0]\n");
+    system $_[0];
+    $? and die $?;
+}
+
+sub pboth ($) {
+    my ($str) = @_;
+    print LOG $str or die $!;
+    print $str or die $!;
+}
+
+sub nexttapefile ($) {
+    my ($what) = @_;
+    $currenttapefilenumber++;
+    $currenttapefilename= $what;
+    pboth(sprintf "writing tape file #%d (mt fsf %d): %s\n",
+         $currenttapefilenumber, $currenttapefilenumber-1, $what);
+}
+
+sub writetapeid ($$) {
+    open T, ">TAPEID" or die $!;
+    print T "$_[0]\n$_[1]\n" or die $!;
+    close T or die $!;
+
+    $currenttapefilenumber= 0;
+    nexttapefile('TAPEID');
+
+    system "tar -b$blocksize -vvcf TAPEID.tar TAPEID"; $? and die $?;
+    system "dd if=TAPEID.tar of=$ntape bs=${blocksize}b count=10";
+    $? and die $?;
+}
+
+sub endprocesses () {
+    while (keys %processes) {
+       $p= waitpid(-1,0) or die "wait: $!";
+       if (!exists $processes{$p}) { warn "unknown pid exited: $p, code $?\n"; next; }
+       $c= $processes{$p};
+       delete $processes{$p};
+       $? && die "error: command gave code $?: $c\n";
+    }
+    pboth("  ok\n");
+}
+
+sub killprocesses {
+    for $p (keys %processes) {
+       kill 15,$p or warn "kill process $p: $!";
+    }
+    undef %processes;
+}
+
+# Read a fsys.foo filesystem group definition file.
+# Syntax is: empty lines and those beginning with '#' are ignored.
+# Trailing whitespace is ignored. Lines of the form 'prefix foo bar'
+# are handled specially, as arex lines 'exclude regexp'; otherwise 
+# we just shove the line into @fsys and let parsefsys deal with it.
+
+sub readfsysfile ($) {
+    my ($fn) = @_;
+    my ($fh,$sfn);
+    $fh= new IO::File "$fn", "r" or die "cannot open fsys file $fn ($!).\n";
+    for (;;) {
+       $!=0; $_= <$fh> or die "unexpected EOF in $fn ($!)\n";
+       chomp; s/\s*$//;
+       last if m/^end$/;
+       next unless m/\S/;
+       next if m/^\#/;
+       if (m/^prefix\s+(\w+)\s+(\S.*\S)$/) {
+           $prefix{$1}= $2;
+       } elsif (m/^prefix\-df\s+(\w+)\s+(\S.*\S)$/) {
+           $prefixdf{$1}= $2;
+       } elsif (m/^snap(?:\=(\w+))?\s+(\w+)\s+(\w+)$/) {
+            push @excldir,$1;
+       } elsif (m/^excludedir\s+(\S.*\S)$/) {
+            push @excldir,$1;
+        } elsif (m/^exclude\s+(\S.*\S)$/) {
+            push @excl,$1;
+       } elsif (m/^include\s+(\S.*\S)$/) {
+           $sfn = $1;
+           $sfn =~ s/^\./fsys./;
+           $sfn = "$etc/$sfn" unless $sfn =~ m,^/,;
+           readfsysfile($sfn);
+        } else {
+           push @fsys,$_;
+       }
+    }
+    close $fh or die $!;
+}
+
+sub readfsys ($) {
+    my ($fsnm) = @_;
+    my ($fsf);
+    $fsf= "$etc/fsys.$fsnm";
+    stat $fsf or die "Filesystems $fsnm unknown ($!).\n";
+    readfsysfile($fsf);
+}
+
+# Parse a line from a filesystem definition file. We expect the line
+# to be in $tf.
+sub parsefsys () {
+    my ($dopts,$dopt);
+    if ($tf =~ m#^(/\S*)\s+(\w+)([,=0-9a-z]*)$#) {
+        # Line of form '[/device:]/file/system dumptype[,options]'
+       $atf= $1;
+       $tm= $2;
+       $dopts= $3;
+       $prefix= '<local>';
+       $pcstr= '';
+       $rstr= '';
+    } elsif ($tf =~ m#^(/\S*)\s+(\w+)([,=0-9a-z]*)\s+(\w+)$#) {
+        # Line of form '[/device:]/file/system dumptype[,options] prefix'
+        # (used for remote backups)
+       $atf= $1;
+       $tm= $2;
+       $dopts= $3;
+       $prefix= $4;
+       $pcstr= "$prefix:";
+       defined($prefix{$prefix}) or die "prefix $prefix in $tf ?\n";
+       $rstr= $prefix{$prefix}.' ';
+    } else {
+       die "fsys $tf ?";
+    }
+
+    $fsidstr= $pcstr.$atf;
+    $fsidstr =~ s/[,+]/+$&/g;
+    $fsidstr =~ s#/#,#g;
+    $fsidfile= "/var/lib/chiark-backup/incstamp,$fsidstr";
+
+    $dev = $atf =~ s,^(.*)\:,, ? $1 : '';
+
+    if (!length $pcstr) {
+       stat $atf or die "stat $atf: $!";
+       -d _ or die "not a dir: $atf";
+    }
+
+    undef %dopt;
+    foreach $dopt (split /\,/,$dopts) {
+       if (grep { $dopt eq $_ } qw(gz noinc)) {
+           $dopt{$dopt}= 'y';
+       } elsif (grep { $dopt eq $_ } qw(snap)) {
+           $dopt{$dopt}= $dopt;
+       } elsif ($dopt =~ m/\=/ && grep { $` eq $_ } qw(gz snap)) {
+           $dopt{$`}= $';
+       } elsif (length $dopt) {
+           die "unknown option $dopt (in $dopts $tf)";
+       }
+    }
+
+    my ($gzo);
+    foreach $gzo (qw(gz gzi)) {
+       if ($dopt{$gzo} eq 'y') {
+           $$gzo= '1';
+       } elsif ($dopt{$gzo} =~ m/^\d$/) {
+           $$gzo= $dopt{$gzo};
+       } elsif (defined $dopt{$gzo}) {
+           die "$tf bad $gzo";
+       } else {
+           $$gzo= '';
+       }
+    }
+
+    if (length $dopt{'snap'}) {
+       length $dev or die "$pcstr:$atf no device but needed for snap";
+    }
+}
+
+sub execute ($) {
+    pboth("  $_[0]\n");
+    system $_[0]; $? and die "$_[0] $?";
+}
+
+sub prepfsys () {
+    $dev_print= $dev;
+    $atf_print= $atf;
+    
+    if (length $dopt{'snap'}) {
+       
+       system('snap-drop'); $? and die $?;
+       
+       $snapscripts= '/etc/chiark-backup/snap';
+       $snapbase= "$rstr $snapscripts/$dopt{'snap'}";
+       $snapargs= "/var/lib/chiark-backup";
+
+       $snapsnap= "$snapbase snap $snapargs $dev $atf";
+       $snapdrop= "$snapbase drop $snapargs";
+
+       open SD, ">snap-drop.new" or die $!;
+       print SD $snapdrop,"\n" or die $!;
+       close SD or die $!;
+       rename "snap-drop.new","snap-drop" or die $!;
+
+       execute($snapsnap);
+
+       $dev_nosnap= $dev;
+       $atf_nosnap= $atf;
+       $dev= "/var/lib/chiark-backup/snap-device";
+       $atf= "/var/lib/chiark-backup/snap-mount";
+    }
+}
+
+sub finfsys () {
+    if (length $dopt{'snap'}) {
+       system('snap-drop'); $? and die $?;
+    }
+}
+
+sub openlog () {
+    unlink 'log';
+    $u= umask(007);
+    open LOG, ">log" or die $!;
+    umask $u;
+    select(LOG); $|=1; select(STDOUT);
+}
+
+$SIG{'__DIE__'}= 'killprocesses';
+
+1;
diff --git a/backup/bringup b/backup/bringup
new file mode 100755 (executable)
index 0000000..b88a720
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+# bringup
+# script for going back to defaultrunlevel
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# Very simple: extract the default runlevel from /etc/inittab
+# and change to it with telinit.
+
+runlevel=`sed -ne '/^id:/ s/.*:\([0-9]\):.*/\1/p' /etc/inittab`
+telinit $runlevel
+
+test ! -f /etc/chiark-backup/bringup-hook
diff --git a/backup/checkallused b/backup/checkallused
new file mode 100755 (executable)
index 0000000..c0079d0
--- /dev/null
@@ -0,0 +1,182 @@
+#!/usr/bin/perl
+# checkallused
+# check that the configuration is sane and backs up everything it should
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# All filesystems must either be backed up in both full and
+# incremental dumps or listed as exceptions.
+
+BEGIN {
+    $etc= '/etc/chiark-backup';
+    require "$etc/settings.pl";
+    require 'backuplib.pl';
+}
+
+$|=1;
+
+open X,'last-tape' or die $!;
+chomp($tape= <X>);
+close X or die $!;
+
+while (!defined $tapedone{$tape}) {
+    open X,"$etc/tape.$tape" or die "$tape $!";
+    $fsg=''; $next='';
+    for (;;) {
+       $_= <X> or die $1; chomp; s/\s*$//;
+       last if m/^end$/;
+       next unless m/\S/;
+       next if m/^\#/;
+       if (m/^filesystems (\w+)$/) { $fsg= $1; }
+        elsif (m/^next (\w+)$/) { $next=$1; }
+       else { die "$tape $_ ?"; }
+    }
+    length $fsg or die "$tape $!";
+    length $next or die "$tape $!";
+    push @{$fsgdone{$fsg}}, $tape;
+    $tapedone{$tape}=1;
+    $tape= $next;
+}
+
+sub checkdevspec ($$$) {
+    my ($atf,$devspec,$why) = @_;
+    push @{ $devspec{$atf}{$devspec} }, $why;
+}
+
+for $fsg (sort keys %fsgdone) {
+    print "filesystem group $fsg: ".join(' ',@{$fsgdone{$fsg}}).":\n ";
+    @fsys= ();
+    readfsys($fsg);
+    for $tf (@fsys) {
+       parsefsys();
+       $pstr= "$pcstr$atf";
+       &e("dumped twice ($backed{$pstr}, $fsg): $pstr")
+           if defined $backed{$pstr};
+       $backed{$pstr}= $fsg;
+       checkdevspec($pstr,"$pcstr$dev","filesystem group $fsg")
+           if length $dev;
+       print " $pstr";
+    }
+    print "\n";
+}
+
+print "incremental group:\n ";
+@fsys= ();
+readfsys('all');
+for $tf (@fsys) {
+    parsefsys();
+    $pstr= "$pcstr$atf";
+    $incrd{$pstr}= $fsg;
+    checkdevspec($pstr,"$pcstr$dev","incremental group") if length $dev;
+    print " $pstr";
+}
+print "\n";
+
+for $pfx ('', sort keys %prefix) {
+    $rstr= length($pfx) ? $prefix{$pfx}.' ' : '';
+    $dfstr= exists($prefixdf{$pfx}) ? $prefixdf{$pfx} :
+       'df -P --no-sync -xiso9660 -xnfs -xproc -xtmpfs';
+    $cmd= "$rstr $dfstr";
+    open X, "$cmd |" or die $!;
+    $_= <X>; m/^Filesystem/ or die "$cmd => $_ ?";
+    $prefix= length($pfx) ? $pfx : '<local>';
+    $pcstr= length($pfx) ? "$pfx:" : '';
+    print "mount points: $prefix:";
+    while (<X>) {
+       chomp;
+       next if m,^procfs\s,;
+       m,^/dev/(\S+)\s.*\s(/\S*)\s*$, or die "$_ ?";
+       ($dev,$mp) = ($1,$2);
+       checkdevspec("$pcstr$mp","$pcstr/dev/$dev","df");
+       $mounted{"$pcstr$mp"}="$pcstr$dev"; print " $1-$2";
+       if (defined($backto= $backed{"$pcstr$mp"})) {
+           if (m,^/dev/\S+\s+\d+\s+(\d+)\s,) {
+               $usedkb{$backto} += $1;
+               $countedkb{"$pcstr$mp"}++;
+           }
+       }
+    }
+    print "\n";
+    $!=0; close(X); $? and die "$? $!";
+}
+
+foreach $fsg (keys %usedkb) {
+    print "filesystem group $fsg: $usedkb{$fsg} 1K-blocks raw accounted\n";
+}
+
+foreach $fsg (keys %backed) {
+    next if $countedkb{$fsg};
+    print "unaccounted filesystem: $fsg\n";
+}
+
+foreach $dsk (keys %devspec) {
+    if (keys %{ $devspec{$dsk} } != 1) {
+       foreach $devspec (keys %{ $devspec{$dsk} }) {
+           &e("inconsistent devices for $dsk: $devspec (".
+               join(', ', @{ $devspec{$dsk}{$devspec} }).")");
+       }
+    }
+}
+
+# We check that all mounted filesystems are dumped and all
+# filesystems to be dumped are mounted. The expected-diffs
+# config file allows us to make exceptions.
+# eg: 
+# #expect disk2 to be mounted but not dumped
+# !/disk2
+# # CD may or may not be mounted but should not be dumped in either case
+# ?/cdrom
+
+open Z,"$etc/expected-diffs" or die $!;
+for (;;) {
+    $_= <Z> or die; chomp; s/\s*$//;
+    last if m/^end$/;
+    next unless m/^\S/;
+    next if m/^\#/;
+    if (s/^\?//) {
+        print "non-permanent filesystem expected not to be dumped: $_\n";
+        if (defined($mounted{$_})) {
+            delete $mounted{$_};
+        }
+    } elsif (s/^\!//) {
+       &e("expected not to be dumped, but not a mount point: $_")
+           unless defined($mounted{$_});
+        print "filesystem expected not to be dumped: $_\n";
+        delete $mounted{$_};
+    } else {
+       &e("non-filesystem expected to be dumped is mounted: $_ on $mounted{$_}")
+           if defined($mounted{$_});
+        $mounted{$_}= 'expected-diffs';
+        print "non-filesystem expected to be dumped: $_\n";
+    }
+}
+    
+for $fs (sort keys %backed) { length($mounted{$fs}) || &e("dumped ($backed{$fs}), not a mount point: $fs"); }
+for $fs (sort keys %incrd) { length($mounted{$fs}) || &e("increm'd ($incrd{$fs}), not a mount point: $fs"); }
+for $fs (sort keys %mounted) { length($backed{$fs}) || &e("mount point ($mounted{$fs}), not dumped: $fs"); }
+for $fs (sort keys %mounted) { length($incrd{$fs}) || &e("mount point ($mounted{$fs}), not increm'd: $fs"); }
+
+$emsg.= "configuration ok\n" unless $e;
+print STDERR $emsg;
+exit($e);
+
+sub e { $emsg.="** @_\n"; $e=1; }
diff --git a/backup/driver b/backup/driver
new file mode 100755 (executable)
index 0000000..5dab3eb
--- /dev/null
@@ -0,0 +1,60 @@
+#!/bin/sh
+# driver
+# entry point for inittab (or perhaps cron) to run the backups.
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+cd /var/lib/chiark-backup
+PATH=/usr/share/chiark-backup:$PATH export PATH
+
+if [ "x$1" != test ]; then
+       stty sane
+       stty -isig
+fi
+
+rm -f this-status p p2
+echo 'FAILED to start dump script' >this-status
+
+# Here we go : run 'full', which (name notwithstanding) handles
+# both full and incremental backups, according to the ID of the
+# tape in the drive.
+(full; snap-drop) 2>&1 | tee this-log
+
+status=`cat this-status 2>/dev/null`
+
+# Mail a report to somewhere appropriate; -odq removed (means just 
+# queue message, don't try to deliver) because it just delays the
+# message (you might want that if mail was one of the services turned
+# off for the duration of the backup, though).
+cat <<END - this-log | /usr/lib/sendmail -oi -om -oee -t
+To: dump-reports
+Subject: Dump Report: $status
+
+END
+
+rm -f /TAPEID
+
+if [ "x$1" != test ]; then
+        # Bring the system up as multiuser again
+       bringup
+       stty isig
+fi
diff --git a/backup/examples/chiark/SYMLINKS.tar b/backup/examples/chiark/SYMLINKS.tar
new file mode 100644 (file)
index 0000000..e7db3fe
Binary files /dev/null and b/backup/examples/chiark/SYMLINKS.tar differ
diff --git a/backup/examples/chiark/expected-diffs b/backup/examples/chiark/expected-diffs
new file mode 100644 (file)
index 0000000..644ef8b
--- /dev/null
@@ -0,0 +1,4 @@
+/var/spool/news/chiark
+!/var/spool/news
+!/tmp
+end
diff --git a/backup/examples/chiark/fsys.all b/backup/examples/chiark/fsys.all
new file mode 100644 (file)
index 0000000..3de4e4a
--- /dev/null
@@ -0,0 +1,3 @@
+include fsys.pt0
+include fsys.pt1
+end
diff --git a/backup/examples/chiark/fsys.pt0 b/backup/examples/chiark/fsys.pt0
new file mode 100644 (file)
index 0000000..1412887
--- /dev/null
@@ -0,0 +1,4 @@
+/                      dump
+/var                   dump
+/u                     dump
+end
diff --git a/backup/examples/chiark/fsys.pt1 b/backup/examples/chiark/fsys.pt1
new file mode 100644 (file)
index 0000000..6afe7ef
--- /dev/null
@@ -0,0 +1,4 @@
+/u2                    dump
+/usr                   dump
+/var/spool/news/chiark cpio
+end
diff --git a/backup/examples/chiark/settings.pl b/backup/examples/chiark/settings.pl
new file mode 100644 (file)
index 0000000..2f14cdf
--- /dev/null
@@ -0,0 +1,13 @@
+#
+chdir '/var/lib/chiark-backup' or die $!;
+push(@INC,'/usr/share/chiark-backup');
+$ENV{'PATH'} =~ s,^/usr/share/chiark-backup:,,;
+$ENV{'PATH'}= '/usr/share/chiark-backup:'.$ENV{'PATH'};
+$blocksize= 1;
+$blocksizebytes= 512*$blocksize;
+$softblocksizekb= 1;
+$softblocksizebytes= 1024*$softblocksizekb;
+$tapename= 'st0';
+$tape= "/dev/$tapename";
+$ntape= "/dev/n$tapename";
+1;
diff --git a/backup/examples/chiark/settings.sh b/backup/examples/chiark/settings.sh
new file mode 100755 (executable)
index 0000000..4fad8c5
--- /dev/null
@@ -0,0 +1,10 @@
+# shell script fragment setting options
+# defaults for currently implemented parameters are
+#lvm_vg=
+#lvm_lv=chiark_backup
+#lvm_lvsize_opts=
+# -l <min(no. of free extents in vg, 1.1x total lv size)> (lvm)
+# -l <min(no. of free extents in vg, 1.5x total space used)> (remountrocp)
+#lvm_lvtools_opts='-A n'
+#lvm_lvcreate_opts=
+#lvm_lvcreate_args=
diff --git a/backup/examples/chiark/tape.a0 b/backup/examples/chiark/tape.a0
new file mode 100644 (file)
index 0000000..219e728
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems all
+next b0
+end
diff --git a/backup/examples/chiark/tape.b0 b/backup/examples/chiark/tape.b0
new file mode 100644 (file)
index 0000000..9b0d81f
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems all
+next c0
+end
diff --git a/backup/examples/chiark/tape.b1 b/backup/examples/chiark/tape.b1
new file mode 100644 (file)
index 0000000..0eab3f9
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next c0
+end
diff --git a/backup/examples/chiark/tape.c0 b/backup/examples/chiark/tape.c0
new file mode 100644 (file)
index 0000000..cb23647
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems all
+next d0
+end
diff --git a/backup/examples/chiark/tape.c1 b/backup/examples/chiark/tape.c1
new file mode 100644 (file)
index 0000000..44f2b50
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next d0
+end
diff --git a/backup/examples/chiark/tape.d0 b/backup/examples/chiark/tape.d0
new file mode 100644 (file)
index 0000000..6fbdc69
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems all
+next a0
+end
diff --git a/backup/examples/chiark/tape.d1 b/backup/examples/chiark/tape.d1
new file mode 100644 (file)
index 0000000..4856464
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next a0
+end
diff --git a/backup/examples/chiark/tape.ka b/backup/examples/chiark/tape.ka
new file mode 100644 (file)
index 0000000..cad1fa7
--- /dev/null
@@ -0,0 +1,2 @@
+incremental
+end
diff --git a/backup/examples/chiark/tape.kb b/backup/examples/chiark/tape.kb
new file mode 100644 (file)
index 0000000..cad1fa7
--- /dev/null
@@ -0,0 +1,2 @@
+incremental
+end
diff --git a/backup/examples/chiark/warnings.now b/backup/examples/chiark/warnings.now
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/backup/examples/chiark/warnings.soon b/backup/examples/chiark/warnings.soon
new file mode 100755 (executable)
index 0000000..14d7bd2
--- /dev/null
@@ -0,0 +1,2 @@
+T 45 "in 1 minute"
+T 15 "in 15 seconds"
diff --git a/backup/examples/relativity/bringup-hook b/backup/examples/relativity/bringup-hook
new file mode 100755 (executable)
index 0000000..2d57080
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh
+set -e
+chvt 11
diff --git a/backup/examples/relativity/expected-diffs b/backup/examples/relativity/expected-diffs
new file mode 100644 (file)
index 0000000..5609903
--- /dev/null
@@ -0,0 +1,7 @@
+#!sfere:/opt/bigdisc
+!davenant:/export/mirror
+davenant:/export/mirror/work
+!davenant:/export/mp3
+!kadath:/tmp
+#!elmyra:/mnt/fat/data
+end
diff --git a/backup/examples/relativity/fsys.all b/backup/examples/relativity/fsys.all
new file mode 100644 (file)
index 0000000..cf611fb
--- /dev/null
@@ -0,0 +1,5 @@
+include ifsys.prefixes
+include        ifsys.pt0
+include ifsys.pt1
+include ifsys.pt2
+end
diff --git a/backup/examples/relativity/fsys.pt0 b/backup/examples/relativity/fsys.pt0
new file mode 100644 (file)
index 0000000..3cd925c
--- /dev/null
@@ -0,0 +1,3 @@
+include ifsys.prefixes
+include ifsys.pt0
+end
diff --git a/backup/examples/relativity/fsys.pt1 b/backup/examples/relativity/fsys.pt1
new file mode 100644 (file)
index 0000000..a9210e7
--- /dev/null
@@ -0,0 +1,3 @@
+include ifsys.prefixes
+include ifsys.pt1
+end
diff --git a/backup/examples/relativity/fsys.pt2 b/backup/examples/relativity/fsys.pt2
new file mode 100644 (file)
index 0000000..2e786f9
--- /dev/null
@@ -0,0 +1,3 @@
+include ifsys.prefixes
+include ifsys.pt2
+end
diff --git a/backup/examples/relativity/ifsys.prefixes b/backup/examples/relativity/ifsys.prefixes
new file mode 100644 (file)
index 0000000..4e317aa
--- /dev/null
@@ -0,0 +1,9 @@
+prefix davenant ssh -o 'BatchMode yes' -c blowfish -o 'Compression yes' davenant 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH'
+prefix xenophobe ssh -o 'BatchMode yes' -c blowfish -o 'Compression no' xenophobe 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH'
+
+prefix khem ssh -o 'BatchMode yes' -c blowfish -o 'Compression yes' -o 'CompressionLevel 1' khem 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH'
+
+prefix kadath ssh -o 'BatchMode yes' -c blowfish -o 'Compression yes' -o 'CompressionLevel 1' kadath -l ian 'PATH=/usr/local/sbin:/usr/local/bin:/sbin:/usr/sbin:$PATH really'
+prefix-df kadath /bin/df -t noprocfs,nfs
+
+end
diff --git a/backup/examples/relativity/ifsys.pt0 b/backup/examples/relativity/ifsys.pt0
new file mode 100644 (file)
index 0000000..2305e5f
--- /dev/null
@@ -0,0 +1,13 @@
+/usr/src               dump    davenant
+
+/var                   dump    davenant
+/u                     dump    davenant
+
+/                      dump    davenant
+
+/export/mirror/work    cpio    davenant
+
+/usr                   dump    davenant
+/boot                  dump    davenant
+
+end
diff --git a/backup/examples/relativity/ifsys.pt1 b/backup/examples/relativity/ifsys.pt1
new file mode 100644 (file)
index 0000000..7ec91b9
--- /dev/null
@@ -0,0 +1,13 @@
+/                      dump    kadath
+
+/usr                   dump    kadath
+/var                   dump    kadath
+/home                  dump    kadath
+
+/var                   dump    khem
+/home                  dump    khem
+/                      dump    khem
+
+/usr                   dump    khem
+
+end
diff --git a/backup/examples/relativity/ifsys.pt2 b/backup/examples/relativity/ifsys.pt2
new file mode 100644 (file)
index 0000000..ac447bb
--- /dev/null
@@ -0,0 +1,8 @@
+/                      dump    xenophobe
+
+/                      dump
+
+/dos/c                 cpio
+/dos/d                 cpio
+
+end
diff --git a/backup/examples/relativity/settings.pl b/backup/examples/relativity/settings.pl
new file mode 100644 (file)
index 0000000..9aa7eb4
--- /dev/null
@@ -0,0 +1,20 @@
+# configuration, for putting in /etc/chiark-backup
+
+chdir '/var/lib/chiark-backup' or die $!;
+push(@INC,'/usr/share/chiark-backup');
+$ENV{'PATH'} =~ s,^/usr/share/chiark-backup,,;
+$ENV{'PATH'}= '/usr/share/chiark-backup:'.$ENV{'PATH'};
+
+# This sets both blocksizes to 512b. Note that both must be the
+# same if using the zftape floppy tape driver, since that requires
+# blocks to be the right size, but dd with the obs=10k option
+# doesn't pad the final block to the blocksize...
+
+$blocksize= 1;
+$blocksizebytes= 512*$blocksize;
+$softblocksizekb= 1;
+$softblocksizebytes= 1024*$softblocksizekb;
+$tapename= 'st0';
+$tape= "/dev/$tapename";
+$ntape= "/dev/n$tapename";
+1;
diff --git a/backup/examples/relativity/tape.ks b/backup/examples/relativity/tape.ks
new file mode 100644 (file)
index 0000000..cad1fa7
--- /dev/null
@@ -0,0 +1,2 @@
+incremental
+end
diff --git a/backup/examples/relativity/tape.kt b/backup/examples/relativity/tape.kt
new file mode 100644 (file)
index 0000000..cad1fa7
--- /dev/null
@@ -0,0 +1,2 @@
+incremental
+end
diff --git a/backup/examples/relativity/tape.s0 b/backup/examples/relativity/tape.s0
new file mode 100644 (file)
index 0000000..a2a30dd
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt0
+next s1
+end
diff --git a/backup/examples/relativity/tape.s1 b/backup/examples/relativity/tape.s1
new file mode 100644 (file)
index 0000000..7d333f5
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next s2
+end
diff --git a/backup/examples/relativity/tape.s2 b/backup/examples/relativity/tape.s2
new file mode 100644 (file)
index 0000000..81499eb
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt2
+next t0
+end
diff --git a/backup/examples/relativity/tape.t0 b/backup/examples/relativity/tape.t0
new file mode 100644 (file)
index 0000000..3da59fe
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt0
+next t1
+end
diff --git a/backup/examples/relativity/tape.t1 b/backup/examples/relativity/tape.t1
new file mode 100644 (file)
index 0000000..2e5b4f5
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next t2
+end
diff --git a/backup/examples/relativity/tape.t2 b/backup/examples/relativity/tape.t2
new file mode 100644 (file)
index 0000000..627dbd9
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt2
+next u0
+end
diff --git a/backup/examples/relativity/tape.u0 b/backup/examples/relativity/tape.u0
new file mode 100644 (file)
index 0000000..0c47152
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt0
+next u1
+end
diff --git a/backup/examples/relativity/tape.u1 b/backup/examples/relativity/tape.u1
new file mode 100644 (file)
index 0000000..f570303
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next u2
+end
diff --git a/backup/examples/relativity/tape.u2 b/backup/examples/relativity/tape.u2
new file mode 100644 (file)
index 0000000..44dcec4
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt2
+next v0
+end
diff --git a/backup/examples/relativity/tape.v0 b/backup/examples/relativity/tape.v0
new file mode 100644 (file)
index 0000000..b9f3ccb
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt0
+next v1
+end
diff --git a/backup/examples/relativity/tape.v1 b/backup/examples/relativity/tape.v1
new file mode 100644 (file)
index 0000000..bfbc0e0
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt1
+next v2
+end
diff --git a/backup/examples/relativity/tape.v2 b/backup/examples/relativity/tape.v2
new file mode 100644 (file)
index 0000000..b85219a
--- /dev/null
@@ -0,0 +1,3 @@
+filesystems pt2
+next s0
+end
diff --git a/backup/examples/relativity/warnings.now b/backup/examples/relativity/warnings.now
new file mode 100755 (executable)
index 0000000..e69de29
diff --git a/backup/examples/relativity/warnings.soon b/backup/examples/relativity/warnings.soon
new file mode 100755 (executable)
index 0000000..14d7bd2
--- /dev/null
@@ -0,0 +1,2 @@
+T 45 "in 1 minute"
+T 15 "in 15 seconds"
diff --git a/backup/full b/backup/full
new file mode 100755 (executable)
index 0000000..61b6266
--- /dev/null
@@ -0,0 +1,337 @@
+#!/usr/bin/perl
+# full
+# Main backup script - does a full dump or execs increm.  Do NOT run directly!
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+BEGIN {
+    $etc= '/etc/chiark-backup';
+    require "$etc/settings.pl";
+    require 'backuplib.pl';
+}
+
+$|=1;
+
+while (@ARGV) {
+    $_= shift @ARGV;
+    if (m/^\-\-no\-reten$/) {
+       $noreten=1;
+    } elsif (m/^\-\-no\-config\-check$/) {
+       $nocheck=1;
+    } else {
+       die "unknown option/argument \`$_'\n";
+    }
+}
+
+# Check to see whether the tape.nn and fsys.nn files are sane.
+# checkallused checks that all the filesystems mounted are in fact
+# dumped in both full and incremental dumps.
+
+openlog();
+
+if (!$nocheck) {
+    setstatus "FAILED configuration check";
+    print "Configuration check ...\n" or die $!;
+    system 'backup-checkallused'; $? and die $?;
+} else {
+    setstatus "FAILED rewinding";
+    rewind_raw();
+}
+
+printdate();
+
+setstatus "FAILED reading TAPEID";
+# Try to read the tape ID from the tape into the file TAPEID
+
+readtapeid_raw();
+
+setstatus "FAILED during startup";
+
+# We need some ID; if the tape has one already that takes precedence;
+# otherwise the user might have set a tape ID that this should be
+# by creating really-TAPEID.
+if (open T, "TAPEID") {
+    unlink 'really-TAPEID';
+} elsif (open T, "really-TAPEID") {
+} else {
+    die "No TAPEID.\n";
+}
+
+# read the ID; it had better be a non-empty string of alphanumeric chars.
+chomp($tapeid= <T>);
+$tapeid =~ m/[^0-9a-zA-Z]/ and die "Bad TAPEID ($&).\n";
+$tapeid =~ m/[0-9a-zA-Z]/ or die "Empty TAPEID.\n";
+close T;
+
+setstatus "FAILED at tape identity check";
+
+# We don't let the user overwrite the tape used for the last backup.
+if (open L, "last-tape") {
+    chomp($lasttape= <L>);
+    close L;
+} else {
+    undef $lasttape;
+}
+
+die "Tape $tapeid same as last time.\n" if $tapeid eq $lasttape;
+
+# $tapeid identifies the individual tape; $tapedesc is its current
+# identity and function, for printing in messages.  You can make these
+# namespaces the same if you like, or you can make the tape.<tapeid>
+# files be links to tape.<tapedesc> files.
+if (defined($tapedesc= readlink "$etc/tape.$tapeid")) {
+    $tapedesc =~ s/^.*\.//;
+    $tapedesc .= "($tapeid)";
+} else {
+    $tapedesc = $tapeid;
+}
+
+# Parse the appropriate tape.nn file.
+# Format is: empty lines and lines starting '#' are ignored. Trailing
+# whitespace is ignored. File must end with 'end' on a line by itself.
+# Either there should be a line 'incremental' to indicate that this is
+# a tape for incremental backups, or a pair of lines 'filesystems fsg'
+# and 'next tapeid', indicating that this tape is part of a full 
+# backup, containing the filesystem group fsg. 
+undef $fsys;
+open D, "$etc/tape.$tapeid" or die "Unknown tape $tapeid ($!).\n";
+for (;;) {
+    $_= <D> or die; chomp; s/\s+$//;
+    last if m/^end$/;
+    next unless m/\S/;
+    next if m/^\#/;
+    if (m/^filesystems (\w+)$/) {
+       $fsys= $1;
+    } elsif (m/^next (\w+)$/) {
+       $next= $1;
+    } elsif (m/^incremental$/) {
+       $incremental= 1;
+    } else {
+       die "unknown entry in tape $tapeid at line $.: $_\n";
+    }
+}
+close D or die $!;
+
+# Incremental backups are handled by increm, not us.
+if ($incremental) {
+    die "incremental tape $tapeid has next or filesystems\n"
+       if defined($next) || defined($fsys);
+    print STDERR "Incremental tape $tapeid.\n\n";
+    setstatus "FAILED during incremental startup";
+    exec "increm",$tapeid,$tapedesc;
+    die $!;
+}
+
+# Read the filesystem group definition (file fsys.nnn)
+readfsys("$fsys");
+
+$doing= "dump of $fsys to tape $tapedesc in drive $tape";
+print LOG "$doing:\n" or die $!;
+
+if (!$noreten) {
+    setstatus "FAILED retensioning";
+    runsystem("mt -f $tape reten");
+}
+
+setstatus "FAILED writing tape ID";
+# First write the tape ID to this tape.
+
+writetapeid($tapeid,$tapedesc);
+
+unlink 'this-md5sums';
+
+print "Doing $doing ...\n" or die $!;
+
+unlink 'p';
+system 'mknod -m600 p p'; $? and die $?;
+
+setstatus "FAILED during dump";
+
+sub closepipes () {
+    close(DUMPOR); close(TEEOR); close(BUFOR); close(FINDOR);
+    close(DUMPOW); close(TEEOW); close(BUFOW); close(FINDOW);
+    close(GZOR); close(GZOW);
+    close(DDERRR); close(DDERRW);
+}
+
+# work out a find option string that will exclude the required files    
+# Note that dump pays no attention to exclude options.
+$exclopt = '';
+foreach $exc (@excldir) {
+    $exclopt .= "-regex $exc -prune -o ";
+}
+foreach $exc (@excl) {
+    $exclopt .= "-regex $exc -o ";
+}
+
+# For each filesystem to be put on this tape:
+for $tf (@fsys) {
+    printdate();
+    parsefsys();
+    prepfsys();
+
+    pipe(FINDOR,FINDOW) or die $!;
+    pipe(DUMPOR,DUMPOW) or die $!;
+    pipe(TEEOR,TEEOW) or die $!;
+    pipe(TEEOR,TEEOW) or die $!;
+    pipe(BUFOR,BUFOW) or die $!;
+    pipe(DDERRR,DDERRW) or die $!;
+    
+    $bufir='TEEOR';
+    $ddcmd= "dd ibs=$softblocksizebytes obs=$blocksizebytes of=$ntape 2>&1";
+
+    if ($gz) {
+       $bufir='GZOR';
+       pipe(GZOR,GZOW) or die $!;
+       $ddcmd .= " conv=sync";
+    }
+    
+    nexttapefile("full $prefix:$atf_print");
+
+    # We can back up via dump or cpio or zafio
+    $dumpin= '</dev/null';
+    if ($tm eq 'dump') {
+       $dumplabel= $pcstr.$atf_print.'$';
+       $dumpcmd= "dump 0Lbfu $dumplabel $softblocksizekb - $atf";
+    } elsif ($tm eq 'cpio') {
+       startprocess '</dev/null','>&FINDOW',$rstr."find $atf -xdev -noleaf -print0";
+       $dumpcmd= "cpio -Hustar -o0C$softblocksizebytes";
+       $dumpin= '<&FINDOR';
+    } elsif ($tm eq 'zafio') {
+        # compress-each-file-then-archive using afio
+        startprocess '</dev/null','>&FINDOW',$rstr."find $atf -xdev -noleaf $exclopt -print";
+        # don't use verbose flag as this generates 2MB report emails :->
+        $dumpcmd = "afio -b $softblocksizebytes -Zo -";
+        $dumpin = '<&FINDOR';
+    } elsif ($tm eq 'ntfsimage') {
+       $dumpcmd= "ntfsimage -svvf --dirty $dev";
+    } elsif ($tm eq 'gtar') {
+       execute("$rstr touch $fsidfile+new");
+       $dumpcmd= "tar Ccfl $atf - .";
+    } else {
+       die "unknown method $tm for $prefix:$atf_print\n";
+    }
+    # This is a funky way of doing a pipeline which pays attention
+    # to the exit status of all the commands in the pipeline.
+    # It is roughly equivalent to:
+    #    md5sum <p >>this-md5sums
+    #    dump <$dumpin | tee p [| gzip] | writebuffer | dd >/dev/null
+
+    startprocess '<p','>>this-md5sums',"$nice md5sum";
+    startprocess $dumpin,'>&DUMPOW',"$nice ".$rstr.$dumpcmd;
+    startprocess '<&DUMPOR','>&TEEOW',"$nice tee p";
+    if ($gz) {
+       startprocess '<&TEEOR','>&GZOW',"$nice gzip -v$gz";
+    }
+    startprocess "<&$bufir",'>&BUFOW',"$nasty writebuffer";
+    startprocess '<&DDERRR','>/dev/null',"$nice tee dderr >&2";
+    startprocess '<&BUFOR','>&DDERRW',"$nasty $ddcmd";
+    closepipes();
+    endprocesses();
+
+    open DDERR, "dderr" or die $!;
+    defined(read DDERR,$_,1023) or die $!;
+    close DDERR;
+    m/\n(\d+)\+0 records out\n/ or die ">$dderr< ?";
+    push @tapefilesizes, [ $1, $currenttapefilename ];
+    $totalrecords += $1;
+    pboth("total blocks written so far: $totalrecords\n");
+
+    if ($tm eq 'gtar') {
+       execute("$rstr mv -f $fsidfile+new $fsidfile");
+    }  
+    
+    finfsys();
+}
+
+# The backup should now be complete; verify it
+
+setstatus "FAILED during check";
+
+# Rewind the tape and skip the TAPEID record
+runsystem("mt -f $tape rewind");
+runsystem("mt -f $ntape fsf 1");
+
+# Check the md5sums match for each filesystem on the tape
+open S,"this-md5sums" or die $!;
+for $tf (@fsys) {
+    printdate();
+    parsefsys();
+    chomp($orgsum= <S>); $orgsum =~ s/\ +\-?$//;
+    $orgsum =~ m/^[0-9a-fA-F]{32}$/i or die "orgsum \`$orgsum' ?";
+    $cmd= "$nasty dd if=$ntape ibs=$blocksizebytes";
+    $cmd .= " | $nasty readbuffer";
+    $cmd .= " | $nice gzip -vd" if $gz;
+    $cmd .= " | $nice md5sum";
+    pboth("  $cmd\n");
+    chomp($csum= `$cmd`);
+    $csum =~ s/\ +\-?$//;
+    $orgsum eq $csum or die "MISMATCH $tf $csum $orgsum\n";
+    print "checksum ok $csum\t$tf\n" or die $!;
+    print LOG "checksum ok $csum\t$tf\n" or die $!;
+}
+printdate();
+runsystem("mt -f $tape rewind");
+
+setstatus "FAILED during cleanup";
+
+$summary= '';
+foreach $tfs (@tapefilesizes) {
+    $summary .= sprintf "    %10d blocks for %s\n", $tfs->[0], $tfs->[1]
+}
+$summary .=
+    sprintf "    %10d blocks total (of %d bytes) plus TAPEID and headers\n",
+    $totalrecords, $blocksizebytes;
+
+pboth("size-summary:\n");
+pboth($summary);
+
+open SS, ">size-summary..new" or die $!;
+print SS $summary or die $!;
+close SS or die $!;
+rename 'size-summary..new',"size-summary.$fsys" or die $!;
+
+# Write to some status files to indicate what the backup system
+# ought to do when next invoked.
+# reset incremental backup count to 1.
+open IAN,">increm-advance.new" or die $!;
+print IAN "1\n" or die $!;
+close IAN or die $!;
+
+# Next full backup is whatever the next link in the tape description
+# file says it ought to be.
+open TN,">next-full.new" or die $!;
+print TN "$next\n" or die $!;
+close TN or die $!;
+
+unlink 'last-tape','next-full';
+# We are the last tape to have been backed up
+rename 'TAPEID','last-tape' or die $!;
+rename 'this-md5sums',"md5sums.$fsys" or die $!;
+rename 'log',"log.$fsys" or die $!;
+rename 'next-full.new',"next-full" or die $!;
+rename 'increm-advance.new',"increm-advance" or die $!;
+
+print "$doing completed.\nNext dump tape is $next.\n" or die $!;
+
+setstatus "Successful: $tapedesc $fsys, next $next";
+exit 0;
diff --git a/backup/increm b/backup/increm
new file mode 100755 (executable)
index 0000000..c9d52ca
--- /dev/null
@@ -0,0 +1,151 @@
+#!/usr/bin/perl
+# increm
+# Do an incremental backup; ONLY for invocation by full !
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# We are invoked by full if the tape description file says that it is
+# for an incremental backup.  We expect two commandline argument which
+# is the ID string of the tape to use, and the description (which
+# includes ID and function).
+
+BEGIN {
+    $etc= '/etc/chiark-backup';
+    require "$etc/settings.pl";
+    require 'backuplib.pl';
+}
+
+$|=1;
+
+@ARGV==2 or die;
+($tapeid,$tapedesc)= @ARGV;
+
+print "Running incremental onto $tapedesc ...\n" or die $!;
+
+# Just check that we weren't passed a bogus ID (the tape description
+# file for incrementals is just 'incremental\nend\n')
+open T,"$etc/tape.$tapeid" or die "Tape $tapeid not found: $!\n";
+close T;
+
+# This is only used for the 'next FULL backup is X' message at the end.
+open NF,"next-full" or die $!;
+chomp($next= <NF>);
+close NF or die $!;
+
+setstatus "FAILED during incremental";
+
+# Read the number of the incremental involved
+# (ie, (how many files are already on the tape) - 1)
+open A,"increm-advance" or die $!;
+chomp($advance= <A>);
+close A or die $!;
+
+# better be a decimal number
+$advance =~ m/^\d+$/ or die "$advance ?";
+
+# Get a list of all filesystems
+readfsys('all');
+openlog();
+
+# Rewind the tape and if we are the first incremental on the tape then
+# write the TAPEID record, otherwise skip forward to the correct point.
+# (full will already have checked that this is the right tape before
+# it invoked us, so no need to read the existing TAPEID record first.)
+runsystem("mt -f $ntape rewind");
+if ($advance == 1) {
+    writetapeid($tapeid,$tapedesc);
+} else {
+    runsystem("mt -f $ntape fsf $advance");
+    $currenttapefilenumber= $advance;
+}
+
+sub closepipes () {
+    close(DUMPOR); close(BUFOR);
+    close(DUMPOW); close(BUFOW);
+    close(GZOR); close(GZOW);
+}
+
+setstatus "PROBLEMS during incremental dump";
+
+for $tf (@fsys) {
+
+    parsefsys();
+    prepfsys();
+    
+    $bufir='DUMPOR';
+    $ddcmd= "$nasty dd ibs=$softblocksizebytes obs=$blocksizebytes of=$ntape";
+
+    pipe(DUMPOR,DUMPOW) or die $!;
+    pipe(BUFOR,BUFOW) or die $!;
+
+    $gz= $gzi if length $gzi;
+    if ($gz) {
+       $bufir='GZOR';
+       pipe(GZOR,GZOW) or die $!;
+       $ddcmd .= " conv=sync";
+    }
+
+    if ($dopt{'noinc'}) {
+       pboth("Incrementals of $atf_print ($prefix) suppressed in config.\n");
+    }
+
+    if ($tm eq 'dump') {
+       $dumplabel= $pcstr.$atf_print.'$';
+       $dumpcmd= "dump 1Lbfu $dumplabel $softblocksizekb - $atf";
+    } elsif ($tm eq 'gtar') {
+       $dumpcmd= "tar NCcfl $fsidfile $atf - .";
+    } else {
+       pboth("Not dumping $atf_print ($prefix) - not supported.\n");
+       next;
+    }
+
+    nexttapefile("inc $prefix:$atf_print");
+    
+    # Same trick as full uses to do a pipeline whilst keeping track
+    # of all exit statuses:
+    #   dump </dev/null | writebuffer | dd >/dev/null
+    startprocess '</dev/null','>&DUMPOW',"$nice ".$rstr.$dumpcmd;
+    if ($gz) {
+       startprocess '<&DUMPOR','>&GZOW',"$nice gzip -v$gz";
+    }
+    startprocess "<&$bufir",'>&BUFOW',"$nasty writebuffer";
+    startprocess '<&BUFOR','>/dev/null',"$nasty $ddcmd";
+    closepipes();
+    endprocesses();
+    # advance is a file counter, so it needs to be updated for each 
+    # dump we do to tape.
+    $advance++;
+
+    finfsys();
+}
+
+# Rewind the tape, and increment the counter of incremental backups.
+runsystem("mt -f $tape rewind");
+open IAN,">increm-advance.new" or die $!;
+print IAN "$advance\n" or die $!;
+close IAN or die $!;
+rename 'increm-advance.new','increm-advance' or die $!;
+
+pboth("Next FULL dump tape is $next\n");
+
+setstatus "INCREMENTAL successful: $tapedesc, next full is $next";
+exit 0;
diff --git a/backup/iwjbackup.txt b/backup/iwjbackup.txt
new file mode 100644 (file)
index 0000000..a353e75
--- /dev/null
@@ -0,0 +1,204 @@
+iwjbackup.txt
+documentation file
+Copyright - AND NO WARRANTY - see notes at bottom of file for details.
+
+This is a quick summary of the backup scripts, and some comments on
+some of the config files: it's a bit patchy and might have the odd
+ommission. The canonical source is the sources, as always :->
+
+
+To run, the contents of /etc/chiark-backup should be:
+
+warnings.*: files defining how many warnings you get as the system is
+brought down to do backups. The defaults are fine.
+
+settings.pl: generic config file: in particular, the name of the tape
+device is set here.
+
+settings.sh: generic config file for shell scripts.  Currently only
+contains some options for the lvm snapshotter.
+
+tape.*: conventionally, each tape you're going to use in the backup
+cycle has a tape number, a name and a config file.  The tape numbers
+in use at Relativity are digit strings like `512'.  The name is a
+combination of rotation set and volume number; rotation sets are
+typically a single letter (`s', `t', `u', `v') at Relativity and
+volumes a single digit (`0', `1', `2') at Relativity.  You need at
+least two tapes as the system won't write a backup on the same tape it
+wrote the last one to.
+
+There are also conventionally incremental tapes whose names are a
+fixed letter (`k' in the current scheme) followed by a rotation
+letter.  At Relativity we have two of these, `ks' and `kt'.
+
+Syntax of the tape.* files for full dump tapes:
+  filesystems X
+  next N
+  end
+
+where N is the name of the next tape in the *full dump* sequence
+(which should be circular; eg
+v0->v1->v2->s0->s1->s1->t0->t1->t2->u0->u1->u2->v0->...
+and X is a filesystem group name (typically the same as the volume
+number).
+
+Each defined filesystem group has a name and a config file
+fsys.<name>.  These files define what is backed up and how.  The
+filesystem `all' must also exist; it's used for incremental backups
+(and it must exist even if you don't do incrementals).
+
+In the fsys.* files:
+  Empty lines and lines starting '#' are comments and ignored.
+  Lines starting `excludedir' given regexps of things to exclude
+   (temp dirs, Netscape's cache, etc).
+  Lines starting `include' say to include another file when reading
+   this one.
+  Lines starting `prefix' give a command prefix necessary to
+   run things on a remote machine:
+     prefix <prefix-name> <command-part>
+  Other lines should be of the form
+    [<device name>:]<directory name> <backup-type>[,<options>]
+  for local backups, or
+    [<device name>:]<directory name> <backup-type>[,<options>] <prefix-name>
+  for remote backups.
+The file (including any included files) must end with the word 'end'
+on a line of its own.
+
+Valid values for <backup-type> are
+  cpio
+    uses cpio to produce tar-format backups
+  dump
+    uses dump to dump entire filesystems
+    <directory name> should be a mount-point
+  gtar
+    uses GNU tar to produce GNU tar format backups and -N-based
+    semi-incrementals (not --incremental or --listed-incremental)
+  zafio
+    uses afio to compress each file as it is backed up
+  ntfsimage
+    for NTFS volumes, requires device name
+Only `dump' and `gtar' type backups perform any kind of incremental
+backups.
+
+<options> is a comma-separated list of <option> or <option>=<value>.
+Options supported:
+
+  gz[i][=<compressionlevel>]
+    Indicates that the whole stream should be compressed with gzip.
+    The compression level defaults to 1 if gz is specified by the
+    level isn't.  gzi appliies only to the incrementals; gz applies to
+    both unless gzi is also specified.  compression level 0 means not
+    to run gzip at all and is the default if gz[i] is not mentioned.
+
+  snap=<snapkind>
+    Indicates that the filesystem should be frozen before the backup
+    by using /etc/chiark-backup/snap/<snapkind>.  See the head comment
+    in /etc/chiark-backup/snap/lvm for details of how this works.
+    When snap= is used, the block device must be specified.
+
+  noinc
+    Suppress incrementals.
+
+expected-diffs is a config file to indicate which 
+filesystems should *not* be backed up. The scripts do a config
+check which involves checking that:
+ * all filesystems to be backed up are present
+ * all filesystems that are present are backed up
+expected-diffs allows you to make exceptions to this; backing 
+up your CDROM drive is a bit pointless, frex.
+The format here is:
+<prefixchar><mountpoint>
+
+where <prefixchar> is ?, ! or nothing, and 
+<mountpoint> is <prefix>:<mountpoint> for a remote fs or
+<mountpoint> for a local one
+(examples: "mnementh:/cdrom", "/cdrom").
+If <prefixchar> is nothing, the scripts will complain if the fs
+is mounted. If it is !, they will complain if it is not mounted.
+If ? they won't complain either way (useful for devices that are
+not always mounted, like /cdrom).
+
+
+You may also create `bringup-hook', a script (or program) which will
+be run by `bringup' at the end.
+
+
+Useful scripts (all in /usr/bin):
+
+backup-checkallused: this only does a check of the configuration
+files.  It should give a cryptic summary of the configuration and
+print 'configuration ok'. If not, fix your config files :->
+You have to create the file /var/lib/chiark-backup/last-tape
+containing the id of a tape; this helps backup-checkallused know where
+to start iterating over tapes.  Any tapeid will do.  (But don't make
+it the same as the one you want to back up to first.)
+
+backup-loaded: this tells the scripts that a currently unlabelled tape
+should be treated as tape X: eg:
+    backup-loaded b3
+will cause it to treat it as tape `b3'.  NB: this won't override the
+TAPEID label written on the tape; it's just for use with previously
+unused tapes.  This applies only to the next time the backup scripts
+are invoked.  You can say just
+    backup-loaded
+to go back to the default behaviour, which is to fail if the tape has
+no TAPEID.
+
+backup-driver: this is the script to actually run to do a backup.  If
+run from the command line, give it the argument 'test' - otherwise it
+will attempt to run bringup to change runlevel, on the assumption that
+it was run from inittab (see below).  The status report email will be
+sent to whatever the unqualified local-part `dump-reports' points to.
+
+backup-takedown: This is for running a reduced level of system
+services during backups.  Usage: takedown <freq> where <freq> can be
+`now', `soon' or nothing depending on number of warning messages
+desired - these correspond to warnings.* files.
+
+To use this you'll need to configure init:
+ * set up runlevel 5 to provide the level of services you want
+   (by tweaking the symlinks in /etc/rc5.d or equivalent)
+ * Add the following to /etc/inittab (tweak paths and VC number
+   if desired):
+
+  # Runlevel 5 is set up to run a reduced level of services during
+  # backups. (currently this means: no squid, no webserver, no newsserver)
+  # We also run the backup script automatically on entering runlevel 5:
+  dm:5:once:backup-driver </dev/tty8 >/dev/tty8 2>&1
+
+ * takedown can be run from the command line or via cron.
+
+backup-whatsthis: a simple script to display the TAPEID of the current
+tape and optionally list its contents.  This script is a bit of a hack
+and may not be fully reliable:
+
+ Usage:
+ whatsthis [--list [n]]
+
+WARNING: it's currently hardwired to assume `cpio' type backups
+when listing; it could be trivially hardwired to assume `zafio' 
+or with slightly more effort it could be done properly :->.
+
+
+COPYRIGHT and LACK OF WARRANTY information
+
+This file is part of chiark backup, a system for backing up GNU/Linux and
+other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+
+chiark backup is:
+ Copyright (C) 1997-1998,2000-2001,2007
+                    Ian Jackson <ian@chiark.greenend.org.uk>
+ Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+
+This 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, or (at your option) any later version.
+
+This 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, consult the Free Software Foundation's
+website at www.fsf.org, or the GNU Project website at www.gnu.org.
diff --git a/backup/labeltape b/backup/labeltape
new file mode 100755 (executable)
index 0000000..fc3d2b3
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+
+use POSIX;
+
+$etc= '/etc/chiark-backup';
+require "$etc/settings.pl";
+require 'backuplib.pl';
+
+while ($ARGV[0] =~ m/^-/) {
+    $_= shift @ARGV;
+    last if m/^\-$/;
+    s/^\-//;
+    while (length) {
+       if (s/^f//) {
+           $force=1;
+       } else {
+           die "$0: unknown option -$_\n";
+       }
+    }
+}
+
+@ARGV==1 or die "$0: need 1 arg, new TAPEID\n";
+($newid)= @ARGV;
+
+open LOG, ">/dev/null" or die $!;
+
+readtapeid_raw();
+
+if (!open T,'TAPEID') {
+    $!==&ENOENT or die $!;
+} else {
+    chomp($oldid= <T>);
+    close T or die $!;
+    print "Tape is currently labelled \`$oldid'\n" or die $!;
+    die "$0: use -f to force relabelling\n" unless $force;
+}
+
+open T,'>TAPEID' or die $!;
+print T "$newid\n" or die $!;
+close T or die $!;
+
+writetapeid($newid,'tapeid set manually');
+rewind_raw();
+
+print "Labelled tape \`$newid'\n" or die $!;
+exit 0;
diff --git a/backup/loaded b/backup/loaded
new file mode 100755 (executable)
index 0000000..7670e06
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+# loaded
+# Entry point for sysadmin to state that we've loaded a tape
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+set -e
+cd /var/lib/chiark-backup
+
+if test -f "/etc/chiark-backup/tape.$1"
+then
+       echo "$1" >really-TAPEID
+       echo "Will assume tape is $1 unless I discover otherwise."
+else
+       if [ "x$1" != x ]; then echo "Tape $1 not found."; fi
+       echo "Will only use tape if it has a TAPEID."
+       rm -f really-TAPEID
+fi
diff --git a/backup/lvm b/backup/lvm
new file mode 100755 (executable)
index 0000000..dd503e7
--- /dev/null
@@ -0,0 +1,50 @@
+#!/bin/bash
+# invoked by backup scripts as
+#    lvm snap $vardir $device $mountpoint
+#              creates and mounts on $vardir/snap-mount
+#              creates $vardir/snap-device -> device
+#    lvm drop $vardir
+
+set -e
+snapkind=lvm
+. /usr/share/chiark-backup/snap-common
+
+#---------- clean up anything
+
+lvmdropcore
+
+if test "$opmode" = drop; then
+       echo 'lvm snap dropped'
+       exit 0
+fi
+
+#---------- create snapshot
+
+fstype="$(mount | sed -n \
+ "s,^$device on $mountpoint type \([a-z0-9][a-z0-9]*\) .*,-t \1 ,p")"
+
+lvmunmapperdevice
+lvmdevice2vgroup
+
+if [ -z "$lvm_lvsize_opts" ]; then
+       lvmextentscore1
+
+       lvdisplay_out="$(really lvdisplay -c "$device")"
+       extents2="$(printf "%s" "$lvdisplay_out" | awk -F: '{print $8}')"
+       extents2=$(( $extents2 + ($extents2+9)/10 - 1 ))
+
+       lvmextentscore2
+fi
+
+lvmcreatecore1
+
+lvcreate -s \
+       $lvm_lvtools_opts \
+       $lvm_lvsize_opts \
+       -n $lvm_lv \
+       $lvm_lvcreate_opts "$device" $lvm_lvcreate_args
+
+mkdir -- "$snmnt"
+mount -v -r $fstype $lvm_mount_opts "$lvpath" "$snmnt"
+
+echo 'lvm snap activated'
diff --git a/backup/man/checkallused.1 b/backup/man/checkallused.1
new file mode 100644 (file)
index 0000000..6224eeb
--- /dev/null
@@ -0,0 +1,33 @@
+.TH BACKUP-CHECKALLUSED "1" "July 2003" "Debian" "Chiark-backup"
+.SH NAME
+backup-checkallused \- check chiark-backup configuration
+.SH SYNOPSIS
+.B backup-checkallused 
+.br
+.SH DESCRIPTION
+`backup-checkallused' does a check of the configuration
+files.  It should give a cryptic summary of the configuration and
+print 'configuration ok'. If not, fix your config files :->
+You have to create the file 
+.br
+/var/lib/chiark-backup/last-tape
+containing the id of a tape; this helps backup-checkallused know where
+to start iterating over tapes.  Any tapeid will do.  (But don't make
+it the same as the one you want to back up to first.)
+.SH OPTIONS
+None
+.SH FILES
+.TP
+.I /etc/chiark-backup/settings.pl
+Configuration file for the whole of chiark-backup
+.P
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org> but 
+may be used by anyone.
+.SH COPYRIGHT
+Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+.br
+Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/backup/man/driver.1 b/backup/man/driver.1
new file mode 100644 (file)
index 0000000..96cf062
--- /dev/null
@@ -0,0 +1,31 @@
+.TH BACKUP-DRIVER "1" "July 2003" "Debian" "Chiark-backup"
+.SH NAME
+backup-driver \- entry point for cron or inittab to start backups
+.SH SYNOPSIS
+.B backup-driver
+.I [-test]
+.br
+.SH DESCRIPTION
+`backup-driver' is the script to actually run to do a backup. It
+assumes that it is being run from cron or inittab unless passed the
+`test' option
+.SH OPTIONS
+.TP
+.BR -test
+Do not attempt to use backup-bringup to change runlevel. Use this
+argument when running backup-driver from the command line.
+.SH FILES
+.TP
+.I /etc/chiark-backup/settings.pl
+Configuration file for the whole of chiark-backup
+.P
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org> but 
+may be used by anyone.
+.SH COPYRIGHT
+Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+.br
+Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/backup/man/labeltape.1 b/backup/man/labeltape.1
new file mode 100644 (file)
index 0000000..0585899
--- /dev/null
@@ -0,0 +1,34 @@
+.TH BACKUP-LABELTAPE "1" "July 2003" "Debian" "Chiark-backup"
+.SH NAME
+backup-labeltape \- display or change tape label
+.SH SYNOPSIS
+.B backup-labeltape
+.I [-force] TAPEID
+.br
+.SH DESCRIPTION
+`backup-labeltape' will display the tape label; it will also label the
+tape with TAPEID unless the tape already has a label (the -force
+option will over-write the current label)
+`test' option
+.SH OPTIONS
+.TP
+.BR -force
+Over-write an existing tape label
+.TP
+.BR TAPEID
+The new label to write to the tape
+.SH FILES
+.TP
+.I /etc/chiark-backup/settings.pl
+Configuration file for the whole of chiark-backup
+.P
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org> but 
+may be used by anyone.
+.SH COPYRIGHT
+Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+.br
+Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/backup/man/loaded.1 b/backup/man/loaded.1
new file mode 100644 (file)
index 0000000..c88603c
--- /dev/null
@@ -0,0 +1,41 @@
+.TH BACKUP-LOADED "1" "July 2003" "Debian" "Chiark-backup"
+.SH NAME
+backup-loaded \- tell the chiark-backup system what to do with a new tape
+.SH SYNOPSIS
+.B backup-loaded
+.I [TAPEID]
+.br
+.SH DESCRIPTION
+backup-loaded: this tells the scripts that a currently unlabelled tape
+should be treated as tape X: eg:
+.br
+\fBbackup-loaded b3\fP
+.br
+will cause it to treat it as tape `b3'.  NB: this won't override the
+TAPEID label written on the tape; it's just for use with previously
+unused tapes.  This applies only to the next time the backup scripts
+are invoked.  You can say just
+.br
+\fBbackup-loaded\fP
+.br
+to go back to the default behaviour, which is to fail if the tape has
+no TAPEID.
+.SH OPTIONS
+.TP
+.BR TAPEID
+Treat the tape as label TAPEID
+.SH FILES
+.TP
+.I /etc/chiark-backup/settings.pl
+Configuration file for the whole of chiark-backup
+.P
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org> but 
+may be used by anyone.
+.SH COPYRIGHT
+Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+.br
+Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/backup/man/takedown.1 b/backup/man/takedown.1
new file mode 100644 (file)
index 0000000..e435932
--- /dev/null
@@ -0,0 +1,35 @@
+.TH BACKUP-TAKEDOWN "1" "July 2003" "Debian" "Chiark-backup"
+.SH NAME
+backup-takedown \- Bring some system services down for backups
+.SH SYNOPSIS
+.B backup-takedown
+.I [freq]
+.br
+.SH DESCRIPTION
+`backup-takedown' is for running a reduced level of system
+services during backups. To use this command you need to configure
+init by setting up runlevel 5 to provide the level of services you
+want, and to run backup-driver automatically on entering runlevel 5.
+.SH OPTIONS
+.TP
+.BR freq
+`freq' may be `now', `soon' or nothing depending on the number of
+warning messages desired - these correspond to warnings.* files.
+.SH FILES
+.TP
+.I /etc/chiark-backup/warnings.*
+Files specifying what number and frequency of warnings will be produced.
+.TP
+.I /etc/chiark-backup/settings.pl
+Configuration file for the whole of chiark-backup
+.P
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org> but 
+may be used by anyone.
+.SH COPYRIGHT
+Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+.br
+Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/backup/man/whatsthis.1 b/backup/man/whatsthis.1
new file mode 100644 (file)
index 0000000..32e7366
--- /dev/null
@@ -0,0 +1,37 @@
+.TH BACKUP-WHATSTHIS "1" "July 2003" "Debian" "Chiark-backup"
+.SH NAME
+backup-whatsthis \- read an id off a tape and display it
+.SH SYNOPSIS
+.B backup-whatsthis
+.RB [\| --list
+.RI [\| n \|]]
+.br
+.SH DESCRIPTION
+`backup-whatsthis' is a simple script to display the TAPEID of the current
+tape and optionally list its contents.  This script is a bit of a hack
+and may not be fully reliable.
+.SH OPTIONS
+.TP
+.B \--list
+.RI [\| n \|]
+Print TAPEID then list archive n (default 0). Note that archives are
+numbered from zero.
+.SH FILES
+.TP
+.I /etc/chiark-backup/settings.pl
+Configuration file for the whole of chiark-backup
+.P
+.SH BUGS
+`backup-whatsthis' is currently hardwired to assume `cpio' type backups
+when listing; it could be trivially hardwired to assume `zafio' 
+or with slightly more effort it could be done properly :->.
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org> but 
+may be used by anyone.
+.SH COPYRIGHT
+Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+.br
+Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/backup/nosnap b/backup/nosnap
new file mode 100755 (executable)
index 0000000..06187fa
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+set -e
+
+removes () {
+       if test -L "$vardir/snap-mount"; then
+               rm -f -- "$vardir/snap-mount"
+       elif test -d "$vardir/snap-mount"; then
+               rmdir -- "$vardir/snap-mount"
+       fi
+       rm -f -- "$vardir/snap-device"
+}
+
+vardir="$2"
+
+case "$#.$1" in
+2.drop)
+       removes
+       ;;
+4.snap)
+       removes
+       ln -s -- "$3" "$vardir/snap-device"
+       ln -s -- "$4" "$vardir/snap-mount"
+       ;;
+*)
+       cat >&2 <<'END'
+usage: .../nosnap snap VARDIR DEV MOUNT
+       .../nosnap drop VARDIR
+END
+       exit 1
+       ;;
+esac
diff --git a/backup/remount b/backup/remount
new file mode 100755 (executable)
index 0000000..ce7a4bb
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+set -e
+
+removes () {
+       rm -f -- "$vardir/snap-mount" "$vardir/snap-device"
+}
+
+vardir="$2"
+
+case "$#.$1" in
+2.drop)
+       fs="$(readlink "$vardir/snap-mount")"
+       removes
+       mount -vo remount,rw "$fs" || true
+       ;;
+4.snap)
+       removes
+       mount -vo remount,ro "$4"
+       ln -s -- "$3" "$vardir/snap-device"
+       ln -s -- "$4" "$vardir/snap-mount"
+       ;;
+*)
+       cat >&2 <<'END'
+usage: .../remount snap VARDIR DEV MOUNT
+       .../remount drop VARDIR
+END
+       exit 1
+       ;;
+esac
diff --git a/backup/remountrocp b/backup/remountrocp
new file mode 100755 (executable)
index 0000000..04ddd27
--- /dev/null
@@ -0,0 +1,82 @@
+#!/bin/bash
+# invoked by backup scripts as
+#    remountrocp snap $vardir $device $mountpoint
+#              remounts $mountpoint readonly
+#              copies data to $vardir/snap-mount
+#              remounts $mountpoint readwrite
+#    remountrocp drop $vardir
+#              deletes $vardir/snap-mount
+
+set -e
+snapkind=remountrocp
+: ${lvm_vg:=}
+remountrocp_fs=ext2
+. ${CHIARK_BACKUP_SHAREDIR:-/usr/share/chiark-backup}/snap-common
+
+#---------- clean up anything
+
+vgroup=$lvm_vg
+lvmdropcore
+
+lastsettings="$vardir/remountrocp-settings"
+test ! -f $lastsettings || . $lastsettings
+
+if test "$opmode" = drop; then
+       test -z "$last_mountpoint" || mount -o remount,rw $last_mountpoint
+       rm -f $lastsettings
+       echo 'remountrocp snap dropped'
+       exit 0
+fi
+
+#---------- create snapshot
+
+if [ -z "$lvm_lvsize_opts" ]; then
+       lvmextentscore1
+
+       df_out="$(really df -P --block-size=$extsize $mountpoint)"
+       extents2="$(printf "%s" "$df_out" | awk '/^\// {print $3}')"
+       extents2=$(( ($extents2*150+102399)/102400 + 4 ))
+
+       lvmextentscore2
+fi
+
+lvmcreatecore1
+
+cat >$lastsettings.new <<END
+last_mountpoint=$mountpoint
+END
+mv -f $lastsettings.new $lastsettings
+
+lvcreate \
+       $lvm_lvtools_opts \
+       $lvm_lvsize_opts \
+       -n $lvm_lv \
+       $lvm_lvcreate_opts \
+       $vgroup \
+       $lvm_lvcreate_args
+
+mkfs -t $remountrocp_fs -q "$lvpath"
+
+mkdir -- "$snmnt"
+mount -t $remountrocp_fs $lvm_mount_opts "$lvpath" "$snmnt"
+echo ' copy filesystem created and mounted'
+
+attempts=10
+while true; do
+       if mount -o remount,ro "$mountpoint"; then break; fi
+       attempts=$(( $attempts - 1 ))
+       if [ $attempts = 0 ]; then
+               echo >&2 'cannot remount readonly'
+               exit 1
+       fi
+       sleep 1
+done
+trap "set +e; mount -o remount,rw $mountpoint; exit 12" 0
+echo ' source remounted readonly, copying...'
+cp -ax -- "$mountpoint/." "$snmnt/."
+echo ' finalising...'
+mount -o remount,rw "$mountpoint"
+trap '' 0
+mount -o remount,ro "$lvpath"
+
+echo 'remountrocp snap activated'
diff --git a/backup/snap-common b/backup/snap-common
new file mode 100644 (file)
index 0000000..52a2c41
--- /dev/null
@@ -0,0 +1,89 @@
+# sourced by snap/lvm and snap/remountrocp
+
+#---------- common arg parsing
+
+nargs=$#
+opmode="$1"
+vardir="$2"
+device="$3"
+mountpoint="$4"
+
+lvm_lv=chiark-backup
+lvm_lvtools_opts='-A n'
+lvm_lvcreate_opts=
+lvm_lvcreate_args=
+
+test ! -f /etc/chiark-backup/settings.sh || . /etc/chiark-backup/settings.sh
+
+case "$nargs.$opmode" in
+4.snap|2.drop)
+       ;;
+*)
+       cat >&2 <<END
+usage: .../$snapkind snap VARDIR DEV MOUNT
+       .../$snapkind drop VARDIR
+END
+       exit 1
+       ;;
+esac
+
+#---------- common functions
+
+lvmunmapperdevice () {
+       # turns device=/dev/mapper/... into /dev/<group>/<volume>
+       case "$device" in
+       /dev/mapper/*)
+               device="`printf '%s' "$device" | perl -pe '
+                       s,^/dev/mapper/,,;
+                       die if m,/,;
+                       s,\-\-,!,g;
+                       s,\-,/,g;
+                       s,\!,-,g;
+                       s,^,/dev/,;
+               '`"
+               ;;
+       esac
+}
+
+lvmdevice2vgroup () {
+       vgroup="${device#/dev/}"
+       vgroup="${vgroup%/*}"
+}
+
+lvmdropcore () {
+       snmnt="$vardir/snap-mount"
+       umount -v "$snmnt" || true
+       test ! -d "$snmnt" || rmdir -- "$snmnt" || rm -f "$snmnt"
+
+       set +e
+       old_lv_dev="$(readlink $vardir/snap-device)"
+       rc=$?
+       set -e
+
+       if [ $rc = 0 ]; then
+               set +e
+               lvchange    $lvm_lvtools_opts -a n $old_lv_dev
+               lvremove -f $lvm_lvtools_opts      $old_lv_dev
+               set -e
+               rm $vardir/snap-device
+       fi
+}
+
+lvmextentscore1 () {
+       # vgroup must be set
+       vgdisplay_out="$(really vgdisplay -c "$vgroup")"
+       extents="$(printf "%s" "$vgdisplay_out" | awk -F: '{print $16}')"
+       extsize="$(printf "%s" "$vgdisplay_out" | awk -F: '{print $13}')"
+}
+
+lvmextentscore2 () {
+       if [ $extents2 -lt $extents ]; then extents=$extents2; fi
+       lvm_lvsize_opts="-l $extents"
+}
+
+lvmcreatecore1 () {
+       # vgroup must be set
+       lvpath="/dev/$vgroup/$lvm_lv"
+       ln -s -- "$lvpath" "$vardir"/snap-device
+       sync
+}
diff --git a/backup/snap-drop b/backup/snap-drop
new file mode 100755 (executable)
index 0000000..85c443d
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+set -e
+vd=/var/lib/chiark-backup
+if [ "x$1" != "x" ]; then
+       vd="$1"; shift
+fi
+cd "$vd"
+test -f snap-drop || exit 0
+sh -x snap-drop || true
+rm snap-drop
diff --git a/backup/snaprsync b/backup/snaprsync
new file mode 100755 (executable)
index 0000000..82ea5d4
--- /dev/null
@@ -0,0 +1,163 @@
+#!/bin/bash
+#
+# usage: snaprsync <setting>... <positionals>
+#  <setting> is --<name>=<value>
+#  <positionals> are assigned to unused mandatory values in order
+# mandatory:
+#   rhost device mountpoint localarea 
+# optional:
+       localprevious=
+       snapkind=lvm
+       rsharedir=/usr/share/chiark-backup 
+       retcdir=/etc/chiark-backup
+       rvardir=/var/lib/chiark-backup
+       bwlimit=
+       subdir=.
+       rsyncopts=
+       rsynccompress=z
+       sshopts=
+       summer=summer
+
+
+# snaprsync
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+
+set -e
+
+badusage () { echo >&2 "snaprsync: bad usage: $1"; exit 12; }
+nb_echo () { (echo "$@"); } # See Debian #382798
+x () { nb_echo "+ $@"; "$@"; }
+xspawned () { eval "${1}pid=$!; nb_echo \"+[$!] ($1) &\";"; }
+xwait () { eval "nb_echo \"+[\$${1}pid] ($1)...\"; wait \$${1}pid;"; }
+
+while true; do
+       case "$1" in
+       --?*=*)
+               name=${1#--}; name=${name%%=*}
+               value=${1#--*=}
+               case "$name" in
+               rhost|device|mountpoint|localarea);;
+               localprevious|snapkind|rsharedir|retcdir|rvardir|bwlimit);;
+               subdir|rsyncopts|rsynccompress|sshopts|summer);;
+               *) badusage "unknown setting $name";;
+               esac
+               eval "$name=\$value"
+               ;;
+       --)     shift; break ;;
+       -*)     badusage "unknown option $1" ;;
+       *)      break ;;
+       esac
+       shift
+done
+
+for name in rhost device mountpoint localarea; do
+       eval "value=\$$name"
+       if [ "x$value" != x ]; then continue; fi
+       if [ $# = 0 ]; then badusage "no value for setting $name"; fi
+       eval "$name=$1"
+       shift
+done
+
+datefmt='%Y-%m-%d %H:%M:%S Z'
+rsync="rsync ${bwlimit:+--bwlimit} $bwlimit"
+export RSYNC_RSH="ssh -o compression=no $sshopts"
+sshpfx='PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin; export PATH; '
+
+ssh $sshopts $rhost "$sshpfx date -u '+$rhost $datefmt start'"
+ssh $sshopts $rhost "$sshpfx id"
+ssh $sshopts $rhost "$sshpfx ls -d $rsharedir"
+ssh $sshopts $rhost "$sshpfx ls -d $rvardir"
+
+test -d $localarea || x mkdir $localarea
+ournode=`uname -n`
+rsumsfile=for-$ournode.sums
+summer="$summer -ACDBtqfx"
+
+td=/dev/enoent
+rc=12
+trap 'rm -rf $td; exit $rc' 0
+td=`mktemp -td`
+
+mkfifo -m 600 $td/sentinel
+exec 4<>$td/sentinel
+
+x ssh $sshopts $rhost "$sshpfx $rsharedir/snap-drop $rvardir"
+x ssh $sshopts $rhost "
+       $sshpfx
+       set -e
+       cd $rvardir
+       echo '$retcdir/snap/$snapkind drop $rvardir' >snap-drop.new
+       mv snap-drop.new snap-drop
+"
+x ssh $sshopts $rhost "$sshpfx $retcdir/snap/$snapkind snap $rvardir $device $mountpoint"
+ssh $sshopts $rhost <$td/sentinel 4<&- "
+  $sshpfx
+  set -e
+  date -u '+$rhost $datefmt main'
+  exec 3<&0 0</dev/null
+  (set +e; read x <&3; kill 0) &
+  cd $rvardir
+  umask 077
+  exec 3>$rsumsfile
+  cd snap-mount
+  $summer . >&3
+  date -u '+$rhost $datefmt sumsdone'
+  cd ..
+" &
+xspawned rsum
+x $rsync -aHSx$rsynccompress --numeric-ids --delete $rsyncopts \
+       ${localprevious:+--link-dest} $localprevious \
+       $rhost:$rvardir/snap-mount/$subdir $localarea/.
+date -u "+ $datefmt rsyncdone"
+
+exec 3>$localarea,lsums
+(cd $localarea && \
+ $summer . >&3) &
+xspawned lsum
+exec 3>&-
+
+xwait rsum
+exec 4<&-
+date -u "+ $datefmt sumsdone"
+x ssh $sshopts $rhost "$sshpfx $rsharedir/snap-drop $rvardir"
+
+if [ "x${localprevious}" != x ] && test -f "$localprevious,rsums"; then
+       cp "$localprevious,rsums" "$localarea,rsums"
+fi
+x $rsync -pI \
+       $rhost:$rvardir/$rsumsfile \
+       "$localarea,rsums"
+
+xwait $lsum
+date -u "+ $datefmt checking"
+
+set +e
+diff -u <(sed -e 's/^mountpoint/dir       /' "$localarea,rsums") \
+  "$localarea,lsums" >"$localarea,sumsdiff"
+diffrc=$?
+set -e
+test $diffrc = 0 || test $diffrc = 1
+
+date -u "+ $datefmt checked $diffrc"
+rc=$diffrc
diff --git a/backup/takedown b/backup/takedown
new file mode 100755 (executable)
index 0000000..97a2a9f
--- /dev/null
@@ -0,0 +1,70 @@
+#!/bin/sh
+# takedown
+# Entry point for cron to take the system down for backups
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# Expects a single (possibly empty) argument X which is used to select
+# a file /etc/chiark-backup/warnings.X. This file will contain lines like:
+# T 300 "in 10 minutes"
+# T 240 "in 5 minutes"
+# T 45 "in 1 minute"
+# T 15 "in 15 seconds"
+# configuring the frequency of warning messages. If you call the 
+# files 'warnings.soon', 'warnings.now' and 'warnings.' then
+# you can invoke this as:
+#   takedown                     lots of warnings
+#   takedown soon                not so many warnings
+#   takedown now                 no warning at all
+
+set -e
+cd /etc/chiark-backup
+
+host="`hostname`" || true
+
+T () {
+       (
+               exec wall <<END &
+ *** WARNING - SYSTEM GOING DOWN FOR BACKUPS ***
+ $host will shut down automatically $2.
+
+END
+       ) &
+       sleep $1
+}
+
+. "warnings.$1"
+
+(
+       exec wall <<END &
+ *** WARNING - SYSTEM GOING DOWN FOR BACKUPS ***
+
+  $host is shutting down IMMEDIATELY.
+
+END
+) &
+sleep 1
+
+# We assume that runlevel 5 is set up suitably for doing backups
+# (ie non-essential services turned off in an effort to get the
+# tape to stream.)
+telinit 5
diff --git a/backup/whatsthis b/backup/whatsthis
new file mode 100755 (executable)
index 0000000..26315f8
--- /dev/null
@@ -0,0 +1,143 @@
+#!/usr/bin/perl
+# whatsthis
+# read an id off the tape and display it to the user
+
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001,2007
+#                     Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+# First rough hack; mostly just code nabbed from full. 
+# --list assumes the dump type was 'zafio', which is a bit bogus.
+
+# whatsthis   : no args => just print tapeid
+# whatsthis --list [n] : print tapeid then list archive n (if n omitted,
+# 0 is assumed.) Note that archives are numbered from zero!
+
+sub rewind();
+sub stopandsay(@);
+
+$etc='/etc/chiark-backup';
+require "$etc/settings.pl";
+require 'backuplib.pl';
+
+$| = 1;
+
+# This isn't intended to be run automatically, so don't bother 
+# with setting status.
+
+# If we are run with the argument --list then list the backup to 
+# stdout. Otherwise just print the tape ID.
+$listing = 0;  # default : don't list
+$listing = 1 if ($ARGV[0] eq '--list');
+$listarchive = 0;
+$listarchive = $ARGV[1] if defined $ARGV[1];
+
+print "Trying to read tape ID from currently inserted tape...\n";
+
+unlink 'TAPEID';
+system "mt -f $ntape setblk $blocksizebytes"; $? and die $?;
+system "dd if=$ntape bs=${blocksize}b count=10 | tar -b$blocksize -vvxf - TAPEID";
+$? and stopandsay "Failed to read TAPEID.\n";
+
+if (!open(T, "TAPEID"))
+{
+  stopandsay "Tape has no ID label.\n";
+}
+
+# OK, there's a TAPEID file, read the ID and check for sanity.
+chomp($tapeid= <T>);
+if ($tapeid =~ m/[^0-9a-zA-Z]/)
+{
+   stopandsay "Tape has a bad (non-alphanumeric) TAPEID ($&).\n";
+}
+elsif (! $tapeid =~ m/[0-9a-zA-Z]/)
+{
+   stopandsay "Empty TAPEID.\n";
+}
+
+print "TAPEID is $tapeid.\n";
+close T;
+
+# If we aren't listing the tape contents, we can just rewind the tape
+# and exit.
+if (!$listing)
+{
+   rewind();
+   exit;
+}
+
+# List the contents of archive $listarchive on the tape.
+# We are already at the right place for the first archive
+# (after the TAPEID). 
+# For any other archive, we skip forwards to the start of that archive.
+if ($listarchive)
+{
+   system "mt -f $ntape fsf $listarchive";
+   $? and stopandsay "Couldn't skip forward to archive $listarchive.";
+}
+
+# Use file to figure out what the archive type is
+# This doesn't seem to work too well, so I'm disabling it -- PMM 
+#$ftype = `dd if=$ntape ibs=$blocksizebytes | file -`;
+#$? and stopandsay "couldn't determine file type: $?";
+$ftype = 'POSIX tar';
+
+# What we want to do here is roughly:
+# dd if=$ntape ibs=$blocksizebytes | readbuffer | afio ???
+#
+# where the afio options are such as to list an archive created with
+# afio -b $softblocksizebytes -Zvo
+
+if ($ftype =~ /POSIX tar/) {
+   # POSIX tar archive; we read it with cpio
+   $reader = "cpio -it -C$softblocksizebytes -Hustar";
+} elsif ($ftype =~ /ASCII cpio/) {
+   $reader = "afio -b $softblocksizebytes -Zt -";
+} elsif ($ftype =~ /dump file/) {
+   stopandsay "sorry: can't list dump files yet";
+} else {
+   stopandsay "listing failed: unknown archive type";
+}
+
+# Now back up so we can read the file again properly
+#system "mt -f $ntape bsf 1"; $? and stopandsay "couldn't backspace tape: $?";
+
+system "dd if=$ntape ibs=$blocksizebytes | readbuffer | $reader";
+$? and stopandsay "listing failed: $?";
+
+# All's well, stop here.
+print "Listing complete.\n";
+rewind();
+exit;
+
+
+# Rewind the tape.
+sub rewind ()
+{
+   system "mt -f $tape rewind"; $? and die $?;
+}
+
+# Print the given message, rewind the tape and exit failure.
+sub stopandsay(@)
+{
+   print @_;
+   rewind();
+   exit(1);
+}
diff --git a/cprogs/Makefile b/cprogs/Makefile
new file mode 100644 (file)
index 0000000..d328bf5
--- /dev/null
@@ -0,0 +1,85 @@
+# Makefile
+# simple make settings
+#
+# This file is part of chiark backup, a system for backing up GNU/Linux and
+# other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+#
+# chiark backup is:
+#  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+#  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+#
+# This 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, or (at your option) any later version.
+#
+# This 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, consult the Free Software Foundation's
+# website at www.fsf.org, or the GNU Project website at www.gnu.org.
+
+us=    chiark-utils-bin
+
+include ../settings.make
+
+RWBUFFER_SIZE_MB=16
+
+PROGRAMS=              readbuffer writebuffer with-lock-ex xbatmon-simple \
+                       summer watershed rcopy-repeatedly
+SUIDSBINPROGRAMS=      really
+DAEMONS=               trivsoundd
+MAN1PAGES=             readbuffer.1 writebuffer.1 with-lock-ex.1
+MAN8PAGES=             trivsoundd.8 really.8
+BUILTTXTDOCS=          watershed.txt
+TXTDOCS=               $(BUILTTXTDOCS)
+
+TARGETS=       $(PROGRAMS) $(SUIDSBINPROGRAMS) $(DAEMONS) $(BUILTTXTDOCS)
+
+all:           $(TARGETS)
+
+readbuffer:                    readbuffer.o                    rwbuffer.o
+writebuffer:                   writebuffer.o   wrbufcore.o     rwbuffer.o 
+trivsoundd:                    trivsoundd.o    wrbufcore.o     rwbuffer.o 
+really:                                really.o myopt.o
+
+really.o myopt.o rcopy-repeatedly.o: myopt.h
+readbuffer.o writebuffer.o rwbuffer.o wrbufcore.o trivsoundd.o:        rwbuffer.h
+
+xbatmon-simple:        xbatmon-simple.o
+               $(CC) -o $@ $< -L/usr/X11R6/lib -lX11 -lm
+
+summer:                summer.o
+               $(CC) -o $@ $< -lnettle -lgmp
+
+rcopy-repeatedly: rcopy-repeatedly.o myopt.o
+               $(CC) -o $@ $^ -lm -lrt
+
+watershed:     watershed.o
+               $(CC) -o $@ $< -lnettle -lgmp
+
+watershed.txt: watershed.c
+               sed '/^$$/,$$d' <$^ >$@.new && mv -f $@.new $@
+
+install:               all
+               $(INSTALL_DIRECTORY) $(bindir) $(sbindir)
+               $(INSTALL_PROGRAM) $(PROGRAMS) $(bindir)
+               $(INSTALL_PROGRAM) $(DAEMONS) $(sbindir)
+               $(INSTALL) -m 4774 -o root -g $(SYSTEM_GROUP) \
+                       $(SUIDSBINPROGRAMS) $(sbindir)
+
+install-docs:  watershed.txt
+               $(INSTALL_DIRECTORY) $(man1dir) $(man8dir) $(txtdocdir)
+               $(INSTALL) -m 644 $(MAN1PAGES) ${man1dir}/.
+               $(INSTALL) -m 644 $(MAN8PAGES) ${man8dir}/.
+               $(INSTALL) -m 644 $(TXTDOCS) ${txtdocdir}/.
+
+install-examples:
+
+clean:
+               rm -f *~ ./#*# *.o
+
+distclean realclean:   clean
+               rm -f $(TARGETS)
diff --git a/cprogs/dlist.h b/cprogs/dlist.h
new file mode 100644 (file)
index 0000000..5c11e4d
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * dlist.h
+ * - macros for handling doubly linked lists
+ */
+/*
+ *  This file is
+ *    Copyright (C) 1997-1999 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ *  It is part of adns, which is
+ *    Copyright (C) 1997-2000 Ian Jackson <ian@davenant.greenend.org.uk>
+ *    Copyright (C) 1999 Tony Finch <dot@dotat.at>
+ *  
+ *  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 3, 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, consult the Free Software Foundation,
+ *  Inc., website at www.fsf.org, or the GNU Project website at www.gnu.org. 
+ */
+
+#ifndef ADNS_DLIST_H_INCLUDED
+#define ADNS_DLIST_H_INCLUDED
+
+#define LIST_INIT(list) ((list).head= (list).tail= 0)
+#define LINK_INIT(link) ((link).next= (link).back= 0)
+
+#define LIST_UNLINK_PART(list,node,part) \
+  do { \
+    if ((node)->part back) (node)->part back->part next= (node)->part next; \
+      else                                  (list).head= (node)->part next; \
+    if ((node)->part next) (node)->part next->part back= (node)->part back; \
+      else                                  (list).tail= (node)->part back; \
+  } while(0)
+
+#define LIST_LINK_TAIL_PART(list,node,part) \
+  do { \
+    (node)->part next= 0; \
+    (node)->part back= (list).tail; \
+    if ((list).tail) (list).tail->part next= (node); else (list).head= (node); \
+    (list).tail= (node); \
+  } while(0)
+
+#define LIST_UNLINK(list,node) LIST_UNLINK_PART(list,node,)
+#define LIST_LINK_TAIL(list,node) LIST_LINK_TAIL_PART(list,node,)
+
+#endif
diff --git a/cprogs/mcastsoundd.c b/cprogs/mcastsoundd.c
new file mode 100644 (file)
index 0000000..8a8c670
--- /dev/null
@@ -0,0 +1,326 @@
+/*
+ * http://www.ibiblio.org/pub/Linux/docs/HOWTO/Multicast-HOWTO
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+#include <assert.h>
+#include <unistd.h>
+#include <signal.h>
+
+#include <endian.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "myopt.h"
+
+typedef unsigned char Byte;
+
+static int ov_mode= 'r';
+static const char *ov_requ=  "127.0.0.1";
+static const char *ov_mcast= "239.193.27.221";
+
+static int ov_port_requ= 4101;
+static int ov_port_ctrl= 4101;
+static int ov_port_data= -1;
+
+static const struct cmdinfo cmdinfos[]= {
+  { "server",     0, &ov_mode,0,0, 's' },
+  { "player",     0, &ov_mode,0,0, 'p' },
+  { "request",    0, &ov_mode,0,0, 'r' },
+  { "mcast-addr", 1, 0,&ov_mcast },
+  { "requ-addr",  1, 0,&ov_requ },
+  { "requ-port",  1, &ov_port_requ },
+  { "ctrl-port",  1, &ov_port_ctrl },
+  { "data-port",  1, &ov_port_data },
+  0
+};
+
+
+static int mcast_fd, requ_fd;
+static struct sockaddr_in requ_sa, ctrl_sa, data_sa;
+
+static void sysfail(const char *m) { perror(m); exit(16); }
+
+static Byte packet[1024];
+static int packet_len;
+
+/*---------- marshalling ----------*/
+
+static uint64_t htonll(uint64_t v) {
+#if LITTLE_ENDIAN
+  return (v >> 32) | (v << 32);
+#endif
+#if BIG_ENDIAN
+  return v;
+#endif
+}
+
+#define OP_CTRL_PLAY 1
+#define OP_CTRL_STOP 2
+#define OP_CTRL_DATA 3
+
+#define MAR_CTRL_PLAY                          \
+  FI8(operation)                               \
+  FI8(reserved)                                        \
+  FI8(generation)                              \
+  FI8(counter)                                 \
+  FI64(totallen)                               \
+  FI64(startts)                                        \
+  FI32(starttns)                               \
+  FI32(txrate)                                 \
+  FR(trackfn,char,256)
+  
+#define MAR_CTRL_STOP                          \
+  FI8(operation)                               \
+  FI8(reserved)                                        \
+  FR0
+
+#define MAR_DATA                               \
+  FI8(operation)                               \
+  FI8(reserved)                                        \
+  FI8(generation)                              \
+  FI8(counter)                                 \
+  FI64(offset)                                 \
+  FR(data,Byte,1024)
+     
+#define FI8(f)      F(f, uint8_t,  v)
+#define FI32(f)     F(f, uint32_t, htonl(v))
+#define FI64(f)     F(f, uint64_t, htonll(v))
+
+#define MARS                                   \
+  MAR(CTRL_PLAY)                               \
+  MAR(CTRL_STOP)                               \
+  MAR(DATA)
+
+#define F(f,t,c) t f;
+#define FR(f,t,l) t f[(l)]; int f##_l;
+#define FR0 /* */
+#define MAR(m) typedef struct Mar_##m { MAR_##m } Mar_##m;
+MARS
+#undef F
+#undef FR
+#undef FR0
+#undef MAR
+
+#define F(f,t,c) { t v= d->f; *(t*)p= c; p += sizeof(t); };
+#define FR(f,t,l) assert(d->f##_l<=l); memcpy(p,d->f,d->f##_l); p+=d->f##_l;
+#define FR0 /* */
+#define MAR(m)                                 \
+  static void mar_##m(const Mar_##m *d) {      \
+    Byte *p= packet;                           \
+    MAR_##m                                    \
+    packet_len= p - packet;                    \
+    assert(packet_len < sizeof(packet));       \
+  }
+MARS
+#undef F
+#undef FR
+#undef FR0
+#undef MAR
+
+#define F(f,t,c) {                             \
+    t v;                                       \
+    if (lr < sizeof(t)) return -1;             \
+    v= *(const t*)p;                           \
+    p += sizeof(t);  lr -= sizeof(t);          \
+    d->f= c;                                   \
+  };
+#define FR(f,t,l) {                            \
+    if (lr > l) return -1;                     \
+    memcpy(d->f, p, lr);                       \
+    d->f##_l= lr;                              \
+  };
+#define FR0                                    \
+    if (lr) return -1;
+#define MAR(m)                                 \
+  static int unmar_##m(Mar_##m *d) {           \
+    const Byte *p= packet;                     \
+    int lr= packet_len;                                \
+    MAR_##m                                    \
+    return 0;                                  \
+  }
+MARS
+#undef F
+#undef FR
+#undef FR0
+#undef MAR
+
+/*---------- general stuff ----------*/
+
+static void nonblock(int fd) {
+  int r;
+  r= fcntl(fd,F_GETFL);  if (r<0) sysfail("nonblock fcntl F_GETFL");
+  r |= O_NONBLOCK;
+  r= fcntl(fd,F_SETFL,r);  if (r<0) sysfail("nonblock fcntl F_GETFL");
+}
+
+static void blocksignals(int how) {
+  sigset_t set;
+  int r;
+
+  sigemptyset(&set);
+  sigaddset(&set,SIGCHLD);
+  r= sigprocmask(how,&set,0);
+  if (r) sysfail("sigprocmask");
+}
+
+static int mksocket(int type, int proto,
+                   const struct sockaddr_in *sa, const char *what) {
+  int fd, r;
+
+  fd= socket(PF_INET, type, proto);
+  if (fd<0) sysfail("socket %s",what);
+
+  r= bind(fd, (struct sockaddr*)&mcast_sa, sizeof(*sa));
+  if (r) sysfail("bind %s",what);
+
+  return fd;
+}
+
+static void mkmcastrecv(const struct sockaddr_in *sa, const char *what) {
+  struct ip_mreq mreq;
+  int r;
+
+  mcast_fd= mksocket(SOCK_DGRAM, IPPROTO_UDP, sa, what);
+
+  mreq.imr_multiaddr= sa->sin_addr;
+  mreq.imr_interface.s_addr= INADDR_ANY;
+  r= setsockopt(mcast_fd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq));
+  if (r) sysfail("add mcast membership %s", what);
+}
+
+/*---------- player ----------*/
+
+static void recvd_play(void) {
+  Mar_CTRL_PLAY pkt;
+  int r;
+
+  r= unmar_CTRL_PLAY(&pkt);
+  if (r) { fprintf(stderr,"bad PLAY packet\n"); return; }
+  
+}
+
+static void recvd_stop(void) {
+  Mar_CTRL_STOP pkt;
+  int r;
+
+  r= unmar_CTRL_STOP(&pkt);
+  if (r) { fprintf(stderr,"bad STOP packet\n"); return; }
+}
+  
+static void player(void) {
+  struct sockaddr_in peer_sa, old_peer_sa;
+  socklen_t peer_salen;
+  int r;
+
+  mkmcastrecv(&ctrl_sa, "ctrl");
+  
+  memset(&old_peer_sa, 0, sizeof(old_peer_sa));
+
+  for (;;) {
+    peer_salen= sizeof(peer_sa);
+    memset(&peer_sa, 0, sizeof(peer_sa));
+    
+    blocksignals(SIG_UNBLOCK);
+    packet_len= recvfrom(mcast_fd, packet, sizeof(packet),
+                        MSG_TRUNC, (struct sockaddr*)&peer_sa, &peer_salen);
+    blocksignals(SIG_BLOCK);
+
+    if (packet_len<0) {
+      if (errno==EINTR) continue;
+      perror("mcast_fd recvfrom");
+      continue;
+    }
+    if (peer_salen != sizeof(peer_sa)) {
+      fprintf(stderr,"mcast_fd recvfrom salen %ld not %ld\n",
+             (unsigned long)peer_salen, (unsigned long)sizeof(peer_sa));
+      continue;
+    }
+    if (packet_len > sizeof(packet)) {
+      fprintf(stderr,"mcast_fd recvfrom packet len %ld longer than max %ld\n",
+             (unsigned long)packet_len, (unsigned long)sizeof(packet));
+      continue;
+    }
+    if (memcmp(&old_peer_sa, &peer_sa, sizeof(old_peer_sa))) {
+      char *p= inet_ntoa(peer_sa.sin_addr);
+      fprintf(stderr,"receiving from %s:%d\n",p,ntohs(peer_sa.sin_port));
+      memcpy(&old_peer_sa, &peer_sa, sizeof(old_peer_sa));
+    }
+    if (packet_len==0) {
+      fprintf(stderr,"empty packet!\n");
+      continue;
+    }
+    switch (packet[0]) {
+    case OP_CTRL_PLAY:
+      recvd_play();
+      break;
+    case OP_CTRL_STOP:
+      recvd_stop();
+      break;
+    default:
+      fprintf(stderr,"unknown opcode %d\n",packet[0]);
+    }
+  }
+}
+
+/*---------- server ----------*/
+
+void server(void) {
+  requ_fd= mksocket(SOCK_STREAM, IPPROTO_TCP, &requ_sa, "requ");
+  
+
+/*---------- main ----------*/
+
+static void argaddr(struct sin_addr *sa, const char *addr_name, int port) {
+  memset(sa,0,sizeof(*sa));
+  sa->sin_family= AF_INET;
+  
+  r= inet_aton(ov_mcast, &mcast_sa.sin_addr);
+  if (!r) badusage("invalid addr `%s'", addr_name);
+
+  if (port<0 || port>65536) badusage("invalid port %d",port);
+
+  sa->sin_port= htons(port);
+}
+
+int main(int argc, const char **argv) {
+  int r;
+
+  if (ov_port_data < 0) ov_port_data= ov_port_ctrl+1;
+  myopt(&argv, cmdinfos);
+
+  argaddr(&requ_sa, ov_requ,  ov_requ_port);
+  argaddr(&ctrl_sa, ov_mcast, ov_ctrl_port);
+  argaddr(&data_sa, ov_data,  ov_data_port);
+
+  if (argv[1] && ov_mode != 'p')
+    badusage("mode takes no non-option arguments");
+
+  switch (ov_mode) {
+  case 'p':
+    player();
+    break;
+  case 's':
+    server();
+    break;
+  case 'r':
+    if (!argv[1] || argv[2])
+      badusage("play-requester takes one non-option argument");
+    request(argv[1]);
+    break;
+  default:
+    abort();
+  }
+
+  nonblock(0);
+  mar_CTRL_PLAY(0);
+  mar_CTRL_STOP(0);
+  mar_DATA(0);
+  unmar_DATA(0);
+  return 0;
+}
diff --git a/cprogs/myopt.c b/cprogs/myopt.c
new file mode 100644 (file)
index 0000000..c4d5a86
--- /dev/null
@@ -0,0 +1,97 @@
+/*
+ * libdpkg - Debian packaging suite library routines
+ * myopt.c - my very own option parsing
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+#include "myopt.h"
+
+void badusage(const char *fmt, ...) {
+  va_list al;
+  
+  va_start(al,fmt);
+  vfprintf(stderr,fmt,al);
+  va_end(al);
+  fputc('\n',stderr);
+  usagemessage();
+  exit(-1);
+}
+
+void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos) {
+  const struct cmdinfo *cip;
+  const char *p, *value;
+  int l;
+
+  ++(*argvp);
+  while ((p= **argvp) && *p == '-') {
+    ++(*argvp);
+    if (!strcmp(p,"--")) break;
+    if (*++p == '-') {
+      ++p; value=0;
+      for (cip= cmdinfos;
+           cip->olong || cip->oshort;
+           cip++) {
+        if (!cip->olong) continue;
+        if (!strcmp(p,cip->olong)) break;
+        l= strlen(cip->olong);
+        if (!strncmp(p,cip->olong,l) &&
+            (p[l]== ((cip->takesvalue==2) ? '-' : '='))) { value=p+l+1; break; }
+      }
+      if (!cip->olong) badusage("unknown option --%s",p);
+      if (cip->takesvalue) {
+        if (!value) {
+          value= *(*argvp)++;
+          if (!value) badusage("--%s option takes a value",cip->olong);
+        }
+        if (cip->call) cip->call(cip,value);
+        else *cip->sassignto= value;
+      } else {
+        if (value) badusage("--%s option does not take a value",cip->olong);
+        if (cip->call) cip->call(cip,0);
+        else *cip->iassignto= cip->arg;
+      }
+    } else {
+      while (*p) {
+        for (cip= cmdinfos; (cip->olong || cip->oshort) && *p != cip->oshort; cip++);
+        if (!cip->oshort) badusage("unknown option -%c",*p);
+        p++;
+        if (cip->takesvalue) {
+          if (!*p) {
+            value= *(*argvp)++;
+            if (!value) badusage("-%c option takes a value",cip->oshort);
+          } else {
+            value= p; p="";
+            if (*value == '=') value++;
+          }
+          if (cip->call) cip->call(cip,value);
+          else *cip->sassignto= value;
+        } else {
+          if (*p == '=') badusage("-%c option does not take a value",cip->oshort);
+          if (cip->call) cip->call(cip,0);
+          else *cip->iassignto= cip->arg;
+        }
+      }
+    }
+  }
+}
diff --git a/cprogs/myopt.h b/cprogs/myopt.h
new file mode 100644 (file)
index 0000000..664fcca
--- /dev/null
@@ -0,0 +1,44 @@
+/*
+ * myopt.h - declarations for my very own option parsing
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+#ifndef MYOPT_H
+#define MYOPT_H
+
+extern void usagemessage(void); /* supply this */
+
+typedef void (*voidfnp)(void);
+
+struct cmdinfo {
+  const char *olong;
+  char oshort;
+  int takesvalue; /* 0 = normal   1 = standard value   2 = option string cont */
+  int *iassignto;
+  const char **sassignto;
+  void (*call)(const struct cmdinfo*, const char *value);
+  int arg;
+  void *parg;
+  voidfnp farg;
+};
+
+void myopt(const char *const **argvp, const struct cmdinfo *cmdinfos);
+void badusage(const char *fmt, ...);
+
+#endif /* MYOPT_H */
diff --git a/cprogs/rcopy-repeatedly.c b/cprogs/rcopy-repeatedly.c
new file mode 100644 (file)
index 0000000..fc2741d
--- /dev/null
@@ -0,0 +1,722 @@
+/*
+ * rcopy-repeatedly
+ *
+ *   You say  rcopy-repeatedly local-file user@host:remote-file
+ *   and it polls for changes to local-file and copies them to
+ *   remote-file.  rcopy-repeatedly must be installed at the far end.
+ *   You can copy in either direction but not between two remote
+ *   locations.
+ *
+ *   Limitations:
+ *    * Cannot cope with files which are modified between us opening
+ *      and statting them for the first time; if the file shrinks
+ *      we may bomb out.  Workaround: use rename-in-place.
+ *    * When transferring large files, bandwidth limiter will
+ *      be `lumpy' as the whole file is transferred and then we
+ *      sleep.
+ *    * Cannot copy between two local files.  Workaround: a symlink
+ *      (but presumably there was some reason you weren't doing that)
+ *    * No ability to synchronise more than just exactly one file
+ *    * Polls.  It would be nice to use inotify or something.
+ *
+ *   Inherent limitations:
+ *    * Can only copy plain files.
+ *
+ *   See the --help for options.
+ */     
+
+/*
+ * rcopy-repeatedly is
+ *  Copyright (C) 2008 Ian Jackson <ian@davenant.greenend.org.uk>
+ * and the option parser we use is
+ *  Copyright (C) 1994,1995 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+/*
+ * protocol is:
+ *   server sends banner
+ *    - "#rcopy-repeatedly#\n"
+ *    - length of declaration, as 4 hex digits, zero prefixed,
+ *      and a newline [5 bytes].  In this protocol version this
+ *      will be "0002" but client _must_ parse it.
+ *   server sends declaration
+ *    - one of "u " or "d" [1 byte]
+ *    - optionally, some more ascii text, reserved for future use
+ *      must be ignored by declaree (but not sent by declarer)
+ *    - a newline [1 byte]
+ *   client sends
+ *    - 0x02   START
+ *        n    2 bytes big endian declaration length
+ *        ...  client's declaration (ascii text, including newline)
+ 8             see above
+ * then for each update
+ *   sender sends one of
+ *    - 0x03   destination file should be deleted
+ *             but note that contents must be retained by receiver
+ *             as it may be used for rle updates
+ *    - 0x04   complete new destination file follows, 64-bit length
+ *        l    8 bytes big endian length
+ *        ...  l bytes data
+ *             receiver must then reply with 0x01 ACK
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <time.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <assert.h>
+#include <math.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "myopt.h"
+
+#define REPLMSG_ACK    0x01
+#define REPLMSG_START  0x02
+#define REPLMSG_RM     0x03
+#define REPLMSG_FILE64 0x04
+
+static const char banner[]= "#rcopy-repeatedly#\n";
+
+static FILE *commsi, *commso;
+
+static double max_bw_prop= 0.2;
+static int txblocksz= INT_MAX, verbose=1;
+static int min_interval_usec= 100000; /* 100ms */
+
+static int nsargs;
+static const char **sargs;
+
+static const char *rsh_program= 0;
+static const char *rcopy_repeatedly_program= "rcopy-repeatedly";
+static int server_upcopy=-1; /* -1 means not yet known; 0 means download */
+  /* `up' means towards the client,
+   * since we regard the subprocess as `down' */
+
+static int udchar;
+
+static char mainbuf[65536]; /* must be at least 2^16 */
+
+#define NORETURN __attribute__((noreturn))
+
+static void vdie(int ec, const char *pfx, int eno,
+                const char *fmt, va_list al) NORETURN;
+static void vdie(int ec, const char *pfx, int eno,
+                const char *fmt, va_list al) {
+  fputs("rcopy-repeatedly: ",stderr);
+  if (server_upcopy>=0) fputs("server: ",stderr);
+  if (pfx) fprintf(stderr,"%s: ",pfx);
+  vfprintf(stderr,fmt,al);
+  if (eno!=-1) fprintf(stderr,": %s",strerror(eno));
+  fputc('\n',stderr);
+  exit(ec);
+}
+static void die(int ec, const char *pfx, int eno,
+               const char *fmt, ...) NORETURN;
+static void die(int ec, const char *pfx, int eno,
+               const char *fmt, ...) {
+  va_list al;
+  va_start(al,fmt);
+  vdie(ec,pfx,eno,fmt,al);
+}
+
+static void diem(void) NORETURN;
+static void diem(void) { die(16,0,errno,"malloc failed"); }
+static void *xmalloc(size_t sz) {
+  assert(sz);
+  void *p= malloc(sz);
+  if (!p) diem();
+  return p;
+}
+static void *xrealloc(void *p, size_t sz) {
+  assert(sz);
+  p= realloc(p,sz);
+  if (!p) diem();
+  return p;
+}
+
+static void diee(const char *fmt, ...) NORETURN;
+static void diee(const char *fmt, ...) {
+  va_list al;
+  va_start(al,fmt);
+  vdie(12,0,errno,fmt,al);
+}
+static void die_protocol(const char *fmt, ...) NORETURN;
+static void die_protocol(const char *fmt, ...) {
+  va_list al;
+  va_start(al,fmt);
+  vdie(10,"protocol error",-1,fmt,al);
+}
+
+static void die_badrecv(const char *what) NORETURN;
+static void die_badrecv(const char *what) {
+  if (ferror(commsi)) diee("communication failed while receiving %s", what);
+  if (feof(commsi)) die_protocol("receiver got unexpected EOF in %s", what);
+  abort();
+}
+static void die_badsend(void) NORETURN;
+static void die_badsend(void) {
+  diee("transmission failed");
+}
+
+static void send_flush(void) {
+  if (ferror(commso) || fflush(commso))
+    die_badsend();
+}
+static void sendbyte(int c) {
+  if (putc(c,commso)==EOF)
+    die_badsend();
+}
+
+static void mfreadcommsi(void *buf, int l, const char *what) {
+  int r= fread(buf,1,l,commsi);  if (r!=l) die_badrecv(what);
+}
+static void mfwritecommso(const void *buf, int l) {
+  int r= fwrite(buf,1,l,commso);  if (r!=l) die_badsend();
+}
+
+static void mpipe(int p[2]) { if (pipe(p)) diee("could not create pipe"); }
+static void mdup2(int fd, int fd2) {
+  if (dup2(fd,fd2)!=fd2) diee("could not dup2(%d,%d)",fd,fd2);
+}
+
+typedef void copyfile_die_fn(FILE *f, const char *xi);
+
+struct timespec ts_sendstart;
+
+static void mgettime(struct timespec *ts) {
+  int r= clock_gettime(CLOCK_MONOTONIC, ts);
+  if (r) diee("clock_gettime failed");
+}
+
+static void bandlimit_sendstart(void) {
+  mgettime(&ts_sendstart);
+}
+
+static double mgettime_elapsed(struct timespec ts_base,
+                              struct timespec *ts_ret) {
+  mgettime(ts_ret);
+  return (ts_ret->tv_sec - ts_base.tv_sec) +
+         (ts_ret->tv_nsec - ts_base.tv_nsec)*1e-9;
+}
+
+static void flushstderr(void) {
+  if (ferror(stderr) || fflush(stderr))
+    diee("could not write progress to stderr");
+}
+
+static void verbosespinprintf(const char *fmt, ...) {
+  static const char spinnerchars[]= "/-\\";
+  static int spinnerchar;
+
+  if (!verbose)
+    return;
+
+  va_list al;
+  va_start(al,fmt);
+  fprintf(stderr,"      %c ",spinnerchars[spinnerchar]);
+  spinnerchar++; spinnerchar %= sizeof(spinnerchars)-1;
+  vfprintf(stderr,fmt,al);
+  putc('\r',stderr);
+  flushstderr();
+}
+
+static void bandlimit_sendend(uint64_t bytes, int *interval_usec_update) {
+  struct timespec ts_buf;
+  double elapsed= mgettime_elapsed(ts_sendstart, &ts_buf);
+  double secsperbyte_observed= elapsed / bytes;
+
+  double min_update= elapsed / max_bw_prop;
+  if (min_update > 1e3) min_update= 1e3;
+  int min_update_usec= min_update * 1e6;
+
+  if (*interval_usec_update < min_update_usec)
+    *interval_usec_update= min_update_usec;
+
+  verbosespinprintf("%12lluby %10.3fs %13.2fkby/s %8dms",
+                   (unsigned long long)bytes, elapsed,
+                   1e-3/secsperbyte_observed, *interval_usec_update/1000);
+}
+static void copyfile(FILE *sf, copyfile_die_fn *sdie, const char *sxi,
+                    FILE *df, copyfile_die_fn *ddie, const char *dxi,
+                    uint64_t lstart, int amsender) {
+  struct timespec ts_last;
+  int now, r;
+  uint64_t l=lstart, done=0;
+
+  ts_last= ts_sendstart;
+
+  while (l>0) {
+    now= l < sizeof(mainbuf) ? l : sizeof(mainbuf);
+    if (now > txblocksz) now= txblocksz;
+
+    r= fread(mainbuf,1,now,sf);  if (r!=now) sdie(sf,sxi);
+    r= fwrite(mainbuf,1,now,df);  if (r!=now) ddie(df,dxi);
+    l -= now;
+    done += now;
+
+    if (verbose) {
+      fprintf(stderr," %3d%% \r",
+             (int)(done*100.0/lstart));
+      flushstderr();
+    }
+  }
+}
+
+static void copydie_inputfile(FILE *f, const char *filename) {
+  diee("read failed on source file `%s'", filename);
+}
+static void copydie_tmpwrite(FILE *f, const char *tmpfilename) {
+  diee("write failed to temporary receiving file `%s'", tmpfilename);
+}
+static void copydie_commsi(FILE *f, const char *what) {
+  die_badrecv(what);
+}
+static void copydie_commso(FILE *f, const char *what) {
+  die_badsend();
+}
+  
+static int generate_declaration(void) {
+  /* returns length; declaration is left in mainbuf */
+  char *p= mainbuf;
+  *p++= udchar;
+  *p++= '\n';
+  return p - mainbuf;
+}
+
+static void read_declaration(int decllen) {
+  assert(decllen <= sizeof(mainbuf));
+  if (decllen<2) die_protocol("declaration too short");
+  mfreadcommsi(mainbuf,decllen,"declaration");
+  if (mainbuf[decllen-1] != '\n')
+    die_protocol("declaration missing final newline");
+  if (mainbuf[0] != udchar)
+    die_protocol("declaration incorrect direction indicator");
+}
+
+static void receiver(const char *filename) {
+  FILE *newfile;
+  char *tmpfilename;
+  int r, c;
+
+  char *lastslash= strrchr(filename,'/');
+  if (!lastslash)
+    r= asprintf(&tmpfilename, ".rcopy-repeatedly.#%s#", filename);
+  else
+    r= asprintf(&tmpfilename, "%.*s/.rcopy-repeatedly.#%s#",
+               (int)(lastslash-filename), filename, lastslash+1);
+  if (r==-1) diem();
+  
+  r= unlink(tmpfilename);
+  if (r && errno!=ENOENT)
+    diee("could not remove temporary receiving file `%s'", tmpfilename);
+  
+  for (;;) {
+    send_flush();
+    c= fgetc(commsi);
+
+    switch (c) {
+
+    case EOF:
+      if (ferror(commsi)) die_badrecv("transfer message code");
+      assert(feof(commsi));
+      return;
+
+    case REPLMSG_RM:
+      r= unlink(filename);
+      if (r && errno!=ENOENT)
+       diee("source file removed but could not remove destination file `%s'",
+            filename);
+      break;
+      
+    case REPLMSG_FILE64:
+      newfile= fopen(tmpfilename, "wb");
+      if (!newfile) diee("could not create temporary receiving file `%s'",
+                        tmpfilename);
+      uint8_t lbuf[8];
+      mfreadcommsi(lbuf,8,"FILE64 l");
+
+      uint64_t l=
+       (lbuf[0] << 28 << 28) |
+       (lbuf[1] << 24 << 24) |
+       (lbuf[2] << 16 << 24) |
+       (lbuf[3] <<  8 << 24) |
+       (lbuf[4]       << 24) |
+       (lbuf[5]       << 16) |
+       (lbuf[6]       <<  8) |
+       (lbuf[7]            ) ;
+
+      copyfile(commsi, copydie_commsi,"FILE64 file data",
+              newfile, copydie_tmpwrite,tmpfilename,
+              l, 0);
+
+      if (fclose(newfile)) diee("could not flush and close temporary"
+                               " receiving file `%s'", tmpfilename);
+      if (rename(tmpfilename, filename))
+       diee("could not install new version of destination file `%s'",
+            filename);
+
+      sendbyte(REPLMSG_ACK);
+      break;
+
+    default:
+      die_protocol("unknown transfer message code 0x%02x",c);
+
+    }
+  }
+}
+
+static void sender(const char *filename) {
+  FILE *f, *fold;
+  int interval_usec, r, c;
+  struct stat stabtest, stab;
+  enum { told_nothing, told_file, told_remove } told;
+
+  interval_usec= 0;
+  fold= 0;
+  told= told_nothing;
+  
+  for (;;) {
+    if (interval_usec) {
+      send_flush();
+      usleep(interval_usec);
+    }
+    interval_usec= min_interval_usec;
+
+    r= stat(filename, &stabtest);
+    if (r) {
+      f= 0;
+    } else {
+      if (told == told_file &&
+         stabtest.st_mode  == stab.st_mode  &&
+         stabtest.st_dev   == stab.st_dev   &&
+         stabtest.st_ino   == stab.st_ino   &&
+         stabtest.st_mtime == stab.st_mtime &&
+         stabtest.st_size  == stab.st_size)
+       continue;
+      f= fopen(filename, "rb");
+    }
+    
+    if (!f) {
+      if (errno!=ENOENT) diee("could not access source file `%s'",filename);
+      if (told != told_remove) {
+       verbosespinprintf
+         (" ENOENT                                                    ");
+       sendbyte(REPLMSG_RM);
+       told= told_remove;
+      }
+      continue;
+    }
+
+    if (fold) fclose(fold);
+    fold= 0;
+
+    r= fstat(fileno(f),&stab);
+    if (r) diee("could not fstat source file `%s'",filename);
+
+    if (!S_ISREG(stab.st_mode))
+      die(8,0,-1,"source file `%s' is not a plain file",filename);
+
+    uint8_t hbuf[9]= {
+      REPLMSG_FILE64,
+      stab.st_size >> 28 >> 28,
+      stab.st_size >> 24 >> 24,
+      stab.st_size >> 16 >> 24,
+      stab.st_size >>  8 >> 24,
+      stab.st_size       >> 24,
+      stab.st_size       >> 16,
+      stab.st_size       >>  8,
+      stab.st_size
+    };
+
+    bandlimit_sendstart();
+
+    mfwritecommso(hbuf,9);
+
+    copyfile(f, copydie_inputfile,filename,
+            commso, copydie_commso,0,
+            stab.st_size, 1);
+
+    send_flush();
+
+    c= fgetc(commsi);  if (c==EOF) die_badrecv("ack");
+    if (c!=REPLMSG_ACK) die_protocol("got %#02x instead of ACK",c);
+
+    bandlimit_sendend(stab.st_size, &interval_usec);
+
+    fold= f;
+    told= told_file;
+  }
+}
+
+typedef struct {
+  const char *userhost, *path;
+} FileSpecification;
+
+static FileSpecification srcspec, dstspec;
+
+static void of__server(const struct cmdinfo *ci, const char *val) {
+  int ncount= nsargs + 1 + !!val;
+  sargs= xrealloc(sargs, sizeof(*sargs) * ncount);
+  sargs[nsargs++]= ci->olong;
+  if (val)
+    sargs[nsargs++]= val;
+}
+
+static int of__server_int(const struct cmdinfo *ci, const char *val) {
+  of__server(ci,val);
+  long v;
+  char *ep;
+  errno= 0; v= strtol(val,&ep,10);
+  if (!*val || *ep || errno || v<INT_MIN || v>INT_MAX)
+    badusage("bad integer argument `%s' for --%s",val,ci->olong);
+  return v;
+}
+
+static void of_help(const struct cmdinfo *ci, const char *val) {
+  usagemessage();
+  if (ferror(stdout)) diee("could not write usage message to stdout");
+  exit(0);
+}
+
+static void of_bw(const struct cmdinfo *ci, const char *val) {
+  int pct= of__server_int(ci,val);
+  if (pct<1 || pct>100)
+    badusage("bandwidth percentage must be between 1 and 100 inclusive");
+  *(double*)ci->parg= pct * 0.01;
+}
+
+static void of_server_int(const struct cmdinfo *ci, const char *val) {
+  *(int*)ci->parg= of__server_int(ci,val);
+}
+
+void usagemessage(void) {
+  printf(
+        "usage: rcopy-repeatedly [<options>] <file> <file>\n"
+        "  <file> may be <local-file> or [<user>@]<host>:<file>\n"
+        "  exactly one of each of the two forms must be provided\n"
+        "  a file is taken as remote if it has a : before the first /\n"
+        "general options:\n"
+        "  --help\n"
+        "  --quiet | -q\n"
+        "options for bandwidth (and cpu time) control:\n"
+        "  --max-bandwidth-percent  (default %d)\n"
+        "  --tx-block-size      (default/max %d)\n"
+        "  --min-interval-usec  (default %d)\n"
+        "options for finding programs:\n"
+        "  --rcopy-repeatedly  (default: rcopy-repeatedly)\n"
+        "  --rsh-program       (default: $RCOPY_REPEATEDLY_RSH or $RSYNC_RSH or ssh)\n"
+        "options passed to server side via ssh:\n"
+        "  --receiver --sender, bandwidth control options\n",
+         (int)(max_bw_prop*100), (int)sizeof(mainbuf), min_interval_usec);
+}
+
+static const struct cmdinfo cmdinfos[]= {
+  { "help",     .call= of_help },
+  { "max-bandwidth-percent", 0,1,.call=of_bw,.parg=&max_bw_prop            },
+  { "tx-block-size",0,     1,.call=of_server_int, .parg=&txblocksz         },
+  { "min-interval-usec",0, 1,.call=of_server_int, .parg=&min_interval_usec },
+  { "rcopy-repeatedly",0,  1, .sassignto=&rcopy_repeatedly_program         },
+  { "rsh-program",0,       1, .sassignto=&rsh_program                      },
+  { "quiet",'q',  .iassignto= &verbose,       .arg=0                       },
+  { "receiver",   .iassignto= &server_upcopy, .arg=0                       },
+  { "sender",     .iassignto= &server_upcopy, .arg=1                       },
+  { 0 }
+};
+
+static void server(const char *filename) {
+  int c, l;
+  char buf[2];
+
+  udchar= server_upcopy?'u':'d';
+
+  commsi= stdin;
+  commso= stdout;
+  l= generate_declaration();
+  fprintf(commso, "%s%04x\n", banner, l);
+  mfwritecommso(mainbuf, l);
+  send_flush();
+
+  c= fgetc(commsi);
+  if (c==EOF) {
+    if (feof(commsi)) exit(14);
+    assert(ferror(commsi));  die_badrecv("initial START message");
+  }
+  if (c!=REPLMSG_START) die_protocol("initial START was %#02x instead",c);
+
+  mfreadcommsi(buf,2,"START l");
+  l= (buf[0] << 8) | buf[1];
+
+  read_declaration(l);
+
+  if (server_upcopy)
+    sender(filename);
+  else
+    receiver(filename);
+}
+
+static void client(void) {
+  int uppipe[2], downpipe[2], r;
+  pid_t child;
+  FileSpecification *remotespec;
+  const char *remotemode;
+
+  mpipe(uppipe);
+  mpipe(downpipe);
+
+  if (srcspec.userhost) {
+    udchar= 'u';
+    remotespec= &srcspec;
+    remotemode= "--sender";
+  } else {
+    udchar= 'd';
+    remotespec= &dstspec;
+    remotemode= "--receiver";
+  }
+
+  sargs= xrealloc(sargs, sizeof(*sargs) * (7 + nsargs));
+  memmove(sargs+5, sargs, sizeof(*sargs) * nsargs);
+  sargs[0]= rsh_program;
+  sargs[1]= remotespec->userhost;
+  sargs[2]= rcopy_repeatedly_program;
+  sargs[3]= remotemode;
+  sargs[4]= "--";
+  sargs[5+nsargs]= remotespec->path;
+  sargs[6+nsargs]= 0;
+    
+  child= fork();
+  if (child==-1) diee("fork failed");
+  if (!child) {
+    mdup2(downpipe[0],0);
+    mdup2(uppipe[1],1);
+    close(uppipe[0]); close(downpipe[0]);
+    close(uppipe[1]); close(downpipe[1]);
+
+    execvp(rsh_program, (char**)sargs);
+    diee("failed to execute rsh program `%s'",rsh_program);
+  }
+
+  commso= fdopen(downpipe[1],"wb");
+  commsi= fdopen(uppipe[0],"rb");
+  if (!commso || !commsi) diee("fdopen failed");
+  close(downpipe[0]);
+  close(uppipe[1]);
+  
+  char banbuf[sizeof(banner)-1 + 5 + 1];
+  r= fread(banbuf,1,sizeof(banbuf)-1,commsi);
+  if (ferror(commsi)) die_badrecv("read banner");
+
+  if (r!=sizeof(banbuf)-1 ||
+      memcmp(banbuf,banner,sizeof(banner)-1) ||
+      banbuf[sizeof(banner)-1 + 4] != '\n') {
+    const char **sap;
+    int count=0;
+    for (count=0, sap=sargs; *sap; sap++) count+= strlen(*sap)+1;
+    char *cmdline= xmalloc(count+1);
+    cmdline[0]=' ';
+    for (sap=sargs; *sap; sap++) {
+      strcat(cmdline," ");
+      strcat(cmdline,*sap);
+    }
+    
+    die(8,0,-1,"did not receive banner as expected -"
+       " shell dirty? ssh broken?\n"
+       " try running\n"
+       "  %s\n"
+       " and expect the first line to be\n"
+       "  %s",
+       cmdline, banner);
+  }
+  
+  banbuf[sizeof(banbuf)-1]= 0;
+  char *ep;
+  long decllen= strtoul(banbuf + sizeof(banner)-1, &ep, 16);
+  if (ep != banbuf + sizeof(banner)-1 + 4)
+    die_protocol("declaration length syntax error");
+
+  read_declaration(decllen);
+
+  int l= generate_declaration();
+  sendbyte(REPLMSG_START);
+  sendbyte((l >> 8) & 0x0ff);
+  sendbyte( l       & 0x0ff);
+  mfwritecommso(mainbuf,l);
+
+  if (remotespec==&srcspec)
+    receiver(dstspec.path);
+  else
+    sender(srcspec.path);
+}
+
+static void parse_file_specification(FileSpecification *fs, const char *arg,
+                                    const char *what) {
+  const char *colon;
+  
+  if (!arg) badusage("too few arguments - missing %s\n",what);
+
+  for (colon=arg; ; colon++) {
+    if (!*colon || *colon=='/') {
+      fs->userhost=0;
+      fs->path= arg;
+      return;
+    }
+    if (*colon==':') {
+      char *uh= xmalloc(colon-arg + 1);
+      memcpy(uh,arg, colon-arg);  uh[colon-arg]= 0;
+      fs->userhost= uh;
+      fs->path= colon+1;
+      return;
+    }
+  }
+}
+
+int main(int argc, const char *const *argv) {
+  setvbuf(stderr,0,_IOLBF,BUFSIZ);
+
+  myopt(&argv, cmdinfos);
+
+  if (!rsh_program) rsh_program= getenv("RCOPY_REPEATEDLY_RSH");
+  if (!rsh_program) rsh_program= getenv("RSYNC_RSH");
+  if (!rsh_program) rsh_program= "ssh";
+
+  if (txblocksz<1) badusage("transmit block size must be at least 1");
+  if (min_interval_usec<0) badusage("minimum update interval may not be -ve");
+
+  if (server_upcopy>=0) {
+    if (!argv[0] || argv[1])
+      badusage("server mode must have just the filename as non-option arg");
+    server(argv[0]);
+  } else {
+    parse_file_specification(&srcspec, argv[0], "source");
+    parse_file_specification(&dstspec, argv[1], "destination");
+    if (argv[2]) badusage("too many non-option arguments");
+    if (!!srcspec.userhost == !!dstspec.userhost)
+      badusage("need exactly one remote file argument");
+    client();
+  }
+  return 0;
+}
diff --git a/cprogs/readbuffer.1 b/cprogs/readbuffer.1
new file mode 100644 (file)
index 0000000..18f6214
--- /dev/null
@@ -0,0 +1,28 @@
+.TH readbuffer 1 2001-10-21 chiark-backup
+.SH NAME
+readbuffer \- read input from devices which don't like constant stopping and starting
+.SH SYNOPSIS
+.B readbuffer
+.RB [ --mlock ]
+.RI [ size ]
+.SH DESCRIPTION
+.B readbuffer
+reads data on standard input and writes it to standard output.  It
+will internally buffer up to \fIsize\fR megabytes of data, and will
+only read more data when the buffer is at least 75% empty.
+.PP
+\fIsize\fR may also be suffixed with
+.BR m ", " k ", or " b
+to indicate that it is in megabytes (2^20), kilobytes (2^10) or bytes.
+.PP
+It is intended for use in situations where many small
+reads are undesirable for performance reasons, e.g. tape drives.
+.SH OPTIONS
+.TP
+.B --mlock
+Calls
+.BR mlock (2)
+to lock the buffer into memory.
+.SH "SEE ALSO"
+.BR writebuffer (1),
+.BR mlock (2)
diff --git a/cprogs/readbuffer.c b/cprogs/readbuffer.c
new file mode 100644 (file)
index 0000000..00df636
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * readbuffer.c
+ *
+ * A program for reading input from devices which don't like constant
+ * stopping and starting, such as tape drives.  readbuffer is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * readbuffer is part of chiark backup, a system for backing up GNU/Linux and
+ * other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+ * chiark backup is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+
+#include "rwbuffer.h"
+
+const char *progname= "readbuffer";
+
+static size_t waitempty;
+
+int main(int argc, const char *const *argv) {
+  int r,reading;
+
+  startup(argv);
+  waitempty= (buffersize*1)/4;
+  reading=1;
+  maxselfd=2;
+  
+  while (!seeneof || used) {
+    
+    FD_ZERO(&readfds);
+    if (reading) {
+      if (used<buffersize-1) {
+       FD_SET(0,&readfds);
+      } else {
+       reading=0;
+      }
+    }
+    FD_ZERO(&writefds); if (used) FD_SET(1,&writefds);
+
+    callselect();
+
+    if (FD_ISSET(0,&readfds)) {
+      r= read(0,rp,min(buffersize-1-used,buf+buffersize-rp));
+      if (!r) {
+        seeneof=1; reading=0;
+      } else if (r<0) {
+        if (!(errno == EAGAIN || errno == EINTR)) { perror("read"); exit(1); }
+      } else {
+        used+= r;
+        rp+= r;
+        if (rp == buf+buffersize) rp=buf;
+      }
+    }
+
+    if (FD_ISSET(1,&writefds)) {
+      assert(used);
+      r= write(1,wp,min(used,buf+buffersize-wp));
+      if (r<=0) {
+        if (!(errno == EAGAIN || errno == EINTR)) { perror("write"); exit(1); }
+      } else {
+        used-= r;
+        wp+= r;
+        if (wp == buf+buffersize) wp=buf;
+      }
+      if (used < waitempty && !seeneof) {
+       reading=1;
+      }
+    }
+  }
+  exit(0);
+}
diff --git a/cprogs/really.8 b/cprogs/really.8
new file mode 100644 (file)
index 0000000..45bca20
--- /dev/null
@@ -0,0 +1,161 @@
+.TH really 8 2001-10-21 chiark-backup
+.SH NAME
+really \- gain privilege or run commands a different user
+.SH SYNOPSIS
+.B really
+.RI [ options ]
+.RI [ "command args... " ]
+.SH DESCRIPTION
+.B really
+checks whether the caller is allowed, and if it is it changes its uids
+and gids according to the command line options and executes the
+specified command.
+.PP
+If no options are specified, the uid will be set to 0 and the gids
+will be left unchanged.
+.PP
+If no command is specified,
+.B really
+will run
+.BR "$SHELL -i" .
+.PP
+A caller is allowed if it has write access to
+.BR /etc/inittab .
+This is most easily achieved by creating or using a suitable group,
+containing all the appropriate users, and making
+.B /etc/inittab
+group-owned by that group and group-writeable.
+.SH OPTIONS
+.TP
+\fB-u\fR \fIusername\fR | \fB--user\fR \fIusername\fR
+Sets the uid, gid, and supplementary group list, according to
+.IR username 's
+entry in the password and group databases.
+.TP
+\fB-i\fR \fIusername\fR | \fB--useronly\fR \fIusername\fR
+Sets only the uid according to
+.IR username 's
+entry in the password database.
+.TP
+\fB-I\fR \fIuid\fR | \fB--uidonly\fR \fIuid\fR
+Sets the uid to the numeric value
+.I uid
+(which need not correspond to any existing user in the password
+database).
+.TP
+\fB-g\fR \fIgroupname\fR | \fB--group\fR \fIgroupname\fR
+.I groupname
+is looked up in the group database and its gid is appended to the
+process's supplementary groups list.  If this is the first gid
+specified it will also be set as the primary gid.
+.TP
+\fB-G\fR \fIgid\fR | \fB--gid\fR \fIgid\fR
+.I gid
+is appended to the process's supplementary groups list.
+.RI ( gid
+need not correspond to any existing group in the group database.)  If
+this is the first gid specified it will also be set as the primary
+gid.
+.TP
+\fB-z\fR | \fB--groupsclear\fR
+Clears the process's supplementary groups list.  When using this
+option you must also specify
+.B -g
+or
+.BR -G .
+The process's groups will then be exactly those specified.  The
+relative position of
+.B -z
+in the argument list is not relevant.
+.TP
+.B \-\-
+Indicates the end of the options.  The next argument (if present) will
+be interpreted as the command name, even if it starts with a hyphen.
+.SH SECURITY CONSIDERATIONS
+.B really
+is designed so that installing it setuid root is extremely unlikely to
+compromise the security of any system.  It will check using
+.BR access (2)
+whether the real user is allowed to write to
+.B /etc/inittab
+and if this check fails
+.B really
+will exit without even attempting to parse its command line.
+.PP
+.B really
+is
+.B not
+designed to be resistant to malicious command line arguments.  Do not
+allow untrusted processes to pass options to really, or to specify the
+command to be run.  Whether it is safe to allow relatively untrusted
+processes to pass options to the command which is to be run depends on
+the behaviour of that command and its security status.
+.PP
+Attempting to use
+.B really
+to drop privilege is dangerous unless the calling environment is very
+well understood.  There are many inherited process properties and
+resources which might be used by the callee to escalate its privilege
+to that of the (root-equivalent) caller.  For this function, it is
+usually better to use
+.B userv
+if possible.
+.SH ENVIRONMENT
+.B really
+does not manipulate the environment at all.  The calling program is
+run in exactly the same environment as the caller passes to
+.BR really .
+In particular,
+.B really
+will not add
+.B sbin
+directories to
+.B PATH
+so
+.BR really -enabled
+accounts will usually need to have these directories on their
+configured
+.B PATH
+to start with.
+.PP
+.B SHELL
+is used to find the default shell to use in interactive mode (ie, when
+no command is specified).
+.SH AUTHOR
+This version of
+.B really
+was written by Ian Jackson <ian@chiark.greenend.org.uk>.
+.PP
+It and this manpage are Copyright (C) 1992-5,2003 Ian Jackson
+<ian@chiark.greenend.org.uk>.
+.PP
+.B really
+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,
+or (at your option) any later version.
+.PP
+.B really
+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.
+.PP
+You should have received a copy of the GNU General Public
+License along with this file; if not, consult the Free Software
+Foundation's website at www.fsf.org, or the GNU Project website at
+www.gnu.org.
+.SH AVAILABILITY
+.B really
+is currently part of
+.B chiark-utils
+and is available for download from
+ftp.chiark.greenend.org.uk in /users/ian/chiark-utils/,
+in source and pre-compiled binary form, and also from Ian Jackson's
+cvsweb.
+.SH "SEE ALSO"
+.BR userv (1),
+.BR access (2),
+.BR setresuid (2),
+.BR setresgid (2),
+.BR setgroups (2)
diff --git a/cprogs/really.c b/cprogs/really.c
new file mode 100644 (file)
index 0000000..46db574
--- /dev/null
@@ -0,0 +1,247 @@
+/*
+ * really.c - program for gaining privilege
+ *
+ * Copyright (C) 1992-3 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/types.h>
+#include <errno.h>
+
+#include "myopt.h"
+
+void usagemessage(void) {
+  if (fputs("usage: really [<really-option> ...] [--]"
+            " [<command> [<argument/option> ...]]\n"
+            "really-options specifying the user:\n"
+            " if no options given, set the uid to 0;\n"
+            " -u|--user <username>     also sets their default group list\n"
+            " -i|--useronly <username> } set the uid\n"
+            " -I|--uidonly <uid>       }  but inherits the group list\n"
+            "really-options specifying the group:\n"
+            " -z|--groupsclear         only groups specified are to be used\n"
+            " -g|--group <groupname>   } add this to\n"
+            " -G|--gid <gid>           }  the group list\n"
+            "other really-options:\n"
+           " -h|--help                display this message\n"
+           " -R|--chroot <dir>        chroot (but *not* chdir)\n",
+            stderr) == EOF) { perror("write usage"); exit(-1); }
+}
+
+static const char *opt_user, *opt_useronly, *opt_chroot;
+static int opt_groupsclear= 0, opt_ngids= 0, opt_uidonly= -1;
+static int opt_gids[512];
+
+static void af_uidonly(const struct cmdinfo *cip, const char *value) {
+  unsigned long ul;
+  char *ep;
+
+  ul= strtoul(value,&ep,10);
+  if (*ep) { fprintf(stderr,"bad uid `%s'\n",value); exit(-1); }
+  opt_uidonly= ul;
+}
+
+static void af_group(const struct cmdinfo *cip, const char *value) {
+  struct group *gr;
+
+  if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
+    badusage("too many groups specified");
+  gr= getgrnam(value);
+  if (!gr) { fprintf(stderr,"unknown group `%s'\n",value); exit(-1); }
+  opt_gids[opt_ngids++]= gr->gr_gid;
+}
+
+static void af_gid(const struct cmdinfo *cip, const char *value) {
+  char *ep;
+  unsigned long ul;
+
+  if (opt_ngids >= sizeof(opt_gids)/sizeof(opt_gids[0]))
+    badusage("too many gids specified");
+  ul= strtoul(value,&ep,0);
+  if ((*ep) || (uid_t)ul != ul || ul>INT_MAX) badusage("bad gid `%s'",value);
+  opt_gids[opt_ngids++]= ul;
+}
+
+static void af_help(const struct cmdinfo *cip, const char *value) {
+  usagemessage(); exit(0);
+}
+
+static const struct cmdinfo cmdinfos[]= {
+  { "user",         'u',  1,  0, &opt_user,          0,           },
+  { "useronly",     'i',  1,  0, &opt_useronly,      0            },
+  { "uidonly",      'I',  1,  0, 0,                  af_uidonly   },
+  { "groupsclear",  'z',  0,  &opt_groupsclear, 0,   0,        1  },
+  { "group",        'g',  1,  0, 0,                  af_group     },
+  { "gid",          'G',  1,  0, 0,                  af_gid       },
+  { "chroot",       'R',  1,  0, &opt_chroot,        0            },
+  { "help",         'h',  0,  0, 0,                  af_help      },
+  {  0,              0                                            }
+};
+
+#ifdef REALLY_CHECK_FILE
+static int checkroot(void) {
+  int r;
+  r= access(REALLY_CHECK_FILE,W_OK);
+  if (r) return -1;
+  return 0;
+}
+#endif
+#ifdef REALLY_CHECK_GID
+static int checkroot(void) {
+  gid_t groups[512];
+  int r, i;
+
+  r= getgid(); if (r==REALLY_CHECK_GID) return 0;
+  if (r<0) { perror("getgid check"); exit(-1); }
+  r= getgroups(sizeof(groups)/sizeof(groups[0]),groups);
+  if (r<0) { perror("getgroups check"); exit(-1); }
+  for (i=0; i<r; i++)
+    if (groups[i] == REALLY_CHECK_GID) return 0;
+  return -1;
+}
+#endif
+#ifdef REALLY_CHECK_NONE
+static int checkroot(void) {
+  return 0;
+}
+#endif
+
+int main(int argc, const char *const *argv) {
+  struct passwd *pw= 0;
+  gid_t groups[512];
+  int i, j, ngroups, ngroups_in, maingid, orgmaingid, mainuid, orgmainuid, r;
+  const char *cp;
+  
+  orgmainuid= getuid();
+  if (orgmainuid && checkroot()) { perror("sorry"); exit(-1); }
+  myopt(&argv,cmdinfos);
+
+  if (opt_groupsclear && !opt_ngids)
+    badusage("-z|--groupsclear must be accompanied by some groups");
+  if (opt_user && (opt_useronly || opt_uidonly!=-1))
+    badusage("-u|--user may not be used with -i|--useronly or -I|--uidonly");
+  if (opt_user && opt_groupsclear)
+    badusage("-u|--user may not be used with -z|--groupsclear");
+  if (opt_uidonly != -1 && (uid_t)opt_uidonly != opt_uidonly)
+    badusage("-I|--uidonly value %d is out of range for a uid",opt_uidonly);
+
+  if (!opt_user && !opt_useronly && opt_uidonly==-1 && !opt_ngids) {
+    opt_uidonly= 0;
+  }
+  if (opt_user || opt_useronly) {
+    cp= opt_user ? opt_user : opt_useronly;
+    pw= getpwnam(cp);
+    if (!pw) { fprintf(stderr,"unknown user `%s'\n",cp); exit(-1); }
+    opt_uidonly= pw->pw_uid;
+  }
+  if (opt_chroot) {
+    if (chroot(opt_chroot)) { perror("chroot failed"); exit(-1); }
+  }
+  orgmaingid= getgid();
+  if (orgmaingid<0) { perror("getgid failed"); exit(-1); }
+  if (opt_user) {
+    r= initgroups(opt_user,pw->pw_gid);
+    if (r) { perror("initgroups failed"); exit(-1); }
+    maingid= pw->pw_gid;
+  } else {
+    maingid= -1;
+  }
+  if (opt_groupsclear) {
+    ngroups= 0;
+    if (opt_ngids > sizeof(groups)/sizeof(groups[0])) {
+      fputs("too many groups to set\n",stderr);
+      exit(-1);
+    }
+  } else {
+    ngroups= getgroups(0,0);
+    if (ngroups<0) { perror("getgroups(0,0) failed"); exit(-1); }
+    if (ngroups+opt_ngids > sizeof(groups)/sizeof(groups[0])) {
+      fputs("too many groups already set for total to fit\n",stderr);
+      exit(-1);
+    }
+    ngroups= getgroups(ngroups,groups);
+    if (ngroups<0) { perror("getgroups failed"); exit(-1); }
+  }
+  if (opt_ngids) {
+    maingid= opt_gids[0];
+  }
+  if (opt_ngids || opt_groupsclear) {
+    ngroups_in= ngroups; ngroups= 0;
+    for (i=0; i<ngroups_in; i++) {
+      for (j=0; j<ngroups && groups[j] != groups[i]; j++);
+      if (j<ngroups) continue;
+      groups[ngroups++]= groups[i];
+    }
+    for (i=0; i<opt_ngids; i++) {
+      for (j=0; j<ngroups && groups[j] != opt_gids[i]; j++);
+      if (j<ngroups) continue;
+      groups[ngroups++]= opt_gids[i];
+    }
+    r= setgroups(ngroups,groups);
+    if (r) { perror("setgroups failed"); exit(-1); }
+  }
+  if (maingid != -1) {
+    r= setgid(maingid); if (r) { perror("setgid failed"); exit(-1); }
+    r= setgid(maingid); if (r) { perror("2nd setgid failed"); exit(-1); }
+  }
+  if (opt_uidonly != -1) {
+    mainuid= opt_uidonly;
+  } else {
+    mainuid= orgmainuid;
+  }
+  r= setuid(mainuid); if (r) { perror("setuid failed"); exit(-1); }
+  r= setuid(mainuid); if (r) { perror("2nd setuid failed"); exit(-1); }
+  if (mainuid != 0) {
+    r= seteuid(0); if (r>=0) { fputs("could seteuid 0",stderr); exit(-1); }
+    if (errno != EPERM) {
+      perror("unexpected failure mode for seteuid 0"); exit(-1);
+    }
+  }
+  r= getuid(); if (r<0) { perror("getuid failed"); exit(-1); }
+  if (r != mainuid) { fputs("getuid mismatch",stderr); exit(-1); }
+  r= geteuid(); if (r<0) { perror("geteuid failed"); exit(-1); }
+  if (r != mainuid) { fputs("geteuid mismatch",stderr); exit(-1); }
+  if (maingid != -1) {
+    for (i=0; i<ngroups && maingid != groups[i]; i++);
+    if (i>=ngroups && maingid != orgmaingid) {
+      r= setgid(orgmaingid);
+      if (r>=0) { fputs("could setgid back",stderr); exit(-1); }
+      if (errno != EPERM) {
+        perror("unexpected failure mode for setgid back"); exit(-1);
+      }
+    }
+    r= getgid(); if (r<0) { perror("getgid failed"); exit(-1); }
+    if (r != maingid) { fputs("getgid mismatch",stderr); exit(-1); }
+    r= getegid(); if (r<0) { perror("getegid failed"); exit(-1); }
+    if (r != maingid) { fputs("getegid mismatch",stderr); exit(-1); }
+  }
+  if (!*argv) {
+    cp= getenv("SHELL");
+    if (!cp) cp= "sh";
+    execlp(cp,cp,"-i",(const char*)0);
+  } else {
+    execvp(argv[0],(char**)argv);
+  }
+  perror("exec failed");
+  exit(-1);
+}
diff --git a/cprogs/really.testcases b/cprogs/really.testcases
new file mode 100755 (executable)
index 0000000..153e47c
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/perl
+
+$testuser=   'testac';
+$testgroup=  'testac';
+$testuid=    1000;
+$testgid=    1000;
+@testxgids=  qw(1000);
+$numgid=     50008;
+$othergroup= 'daemon';
+$othergid=   1;
+
+sub parseid ($) {
+    my ($id) = @_;
+    my $orgid= $id;
+    my $out= '';
+    my $part;
+    chomp($id);
+    $id =~ s/^uid=// or return $id =~ m/\n/ ? "ERROR: $`" : "ERROR: $id";
+    $id =~ s/^(\d+)// or return $orgid;
+    $out= $1;
+    $id =~ s/^\([^\)]+\)//; $id =~ s/^\s+// or return $orgid;
+    $id =~ s/^gid=(\d+)// or return $orgid;
+    $out.= " $1";
+    $id =~ s/^\([^\)]+\)//; $id =~ s/^\s+// or return $orgid;
+    $id =~ s/^groups=// or return $orgid;
+    for $part (split(/,/,$id)) {
+        $part =~ s/^(\d+)// or return $orgid;
+        $out.= " $1";
+        $part =~ s/^\([^\)]+\)//; $part eq '' or return $orgid;
+    }
+    return $out;
+}
+
+$org= `id`;
+$org =~ m/^uid=\d+\(([^\)]+)\) gid=\d+\(([^\)]+)\) / or die "$org ?";
+$orguser= $1; $orggroup= $2;
+$org= parseid($org);
+$org =~ m/^\d+ \d+ / or die $org;
+($orguid,$orggid,@orgxgids)= split(/ /,$org);
+
+$tests= <<END;
+-u $testuser
+$testuid $testgid @testxgids
+
+-u $testuser -z
+ERROR: -z|--groupsclear must be accompanied by some groups
+
+-u $testuser -g $othergroup
+$testuid $othergid @testxgids $othergid
+
+-u $testuser -z -g $othergroup
+ERROR: -u|--user may not be used with -z|--groupsclear
+
+-u $testuser -G $numgid -g $othergroup
+$testuid $numgid @testxgids $numgid $othergid
+
+-u $testuser -z -G $numgid -g $othergroup
+ERROR: -u|--user may not be used with -z|--groupsclear
+
+-u $testuser -g $testgroup -G $testgid
+$testuid $testgid @testxgids
+
+-u $testuser -z -g $testgroup -G $testgid
+ERROR: -u|--user may not be used with -z|--groupsclear
+
+-u $testuser -g $othergroup -g $testgroup -G $testgid
+$testuid $othergid @testxgids $othergid
+
+-u $testuser -z -g $othergroup -g $testgroup -G $testgid
+ERROR: -u|--user may not be used with -z|--groupsclear
+
+-u $testuser -G $numgid -g $othergroup -g $testgroup -G $testgid
+$testuid $numgid @testxgids $numgid $othergid
+
+-u $testuser -z -G $numgid -g $othergroup -g $testgroup -G $testgid
+ERROR: -u|--user may not be used with -z|--groupsclear
+
+
+0 $orggid @orgxgids
+
+-i $testuser
+$testuid $orggid @orgxgids
+
+-i $testuser -z
+ERROR: -z|--groupsclear must be accompanied by some groups
+
+-i $testuser -g $othergroup
+$testuid $othergid @orgxgids $othergid
+
+-i $testuser -z -g $othergroup
+$testuid $othergid $othergid
+
+-i $testuser -G $numgid -g $othergroup
+$testuid $numgid @orgxgids $numgid $othergid
+
+-i $testuser -z -G $numgid -g $othergroup
+$testuid $numgid $numgid $othergid
+
+-i $testuser -g $orggroup -G $orggid
+$testuid $orggid @orgxgids
+
+-i $testuser -z -g $orggroup -G $orggid
+$testuid $orggid $orggid
+
+-i $testuser -g $othergroup -g $orggroup -G $orggid
+$testuid $othergid @orgxgids $othergid
+
+-i $testuser -z -g $othergroup -g $orggroup -G $orggid
+$testuid $othergid $othergid $orggid
+
+-i $testuser -G $numgid -g $othergroup -g $orggroup -G $orggid
+$testuid $numgid @orgxgids $numgid $othergid
+
+-i $testuser -z -G $numgid -g $othergroup -g $orggroup -G $orggid
+$testuid $numgid $numgid $othergid $orggid
+
+
+0 $orggid @orgxgids
+
+-z
+ERROR: -z|--groupsclear must be accompanied by some groups
+
+-g $othergroup
+$orguid $othergid @orgxgids $othergid
+
+-z -g $othergroup
+$orguid $othergid $othergid
+
+-G $numgid -g $othergroup
+$orguid $numgid @orgxgids $numgid $othergid
+
+-z -G $numgid -g $othergroup
+$orguid $numgid $numgid $othergid
+
+-g $orggroup -G $orggid
+$orguid $orggid @orgxgids
+
+-z -g $orggroup -G $orggid
+$orguid $orggid $orggid
+
+-g $othergroup -g $orggroup -G $orggid
+$orguid $othergid @orgxgids $othergid
+
+-z -g $othergroup -g $orggroup -G $orggid
+$orguid $othergid $othergid $orggid
+
+-G $numgid -g $othergroup -g $orggroup -G $orggid
+$orguid $numgid @orgxgids $numgid $othergid
+
+-z -G $numgid -g $othergroup -g $orggroup -G $orggid
+$orguid $numgid $numgid $othergid $orggid
+
+
+ERROR: sorry: Permission denied
+./really-test -u $testuser -g staff
+END
+
+@tests= split(/\n/,$tests);
+for ($i=0; $i<$#tests; $i+=3) {
+    $out= `$tests[$i+2] ./really-test $tests[$i] id 2>&1`;
+    $newout= parseid($out);
+    print("OK $tests[$i] ($tests[$i+2])\n"), next if $newout eq $tests[$i+1];
+    die "$newout != $tests[$i+1] ($tests[$i]) $i";
+}
diff --git a/cprogs/rwbuffer.c b/cprogs/rwbuffer.c
new file mode 100644 (file)
index 0000000..4d8503c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * rwbuffer.c
+ * common definitions for readbuffer/writebuffer
+ *
+ * readbuffer and writebuffer are:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * readbuffer is part of chiark backup, a system for backing up GNU/Linux and
+ * other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+ * chiark backup is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+
+#include "rwbuffer.h"
+
+#ifndef RWBUFFER_SIZE_MB_DEF
+#define RWBUFFER_SIZE_MB_DEF 16
+#endif
+
+#ifndef RWBUFFER_SIZE_MB_MAX
+#define RWBUFFER_SIZE_MB_MAX 512
+#endif
+
+unsigned char *buf, *wp, *rp;
+int used, seeneof, maxselfd;
+size_t buffersize= RWBUFFER_SIZE_MB_DEF*1024*1024;
+fd_set readfds;
+fd_set writefds;
+
+static int opt_mlock=0;
+
+int min(int a, int b) { return a<=b ? a : b; }
+
+static void usage(FILE *f) {
+  if (fprintf(f,"usage: %s [--mlock] [<megabytes>]\n",progname) < 0)
+    { perror("print usage"); exit(16); }
+}
+
+static void usageerr(const char *what) {
+  fprintf(stderr,"%s: bad usage: %s\n",progname,what);
+  usage(stderr);
+  exit(12);
+}
+
+void nonblock(int fd, int yesno) {
+  int r;
+  r= fcntl(fd,F_GETFL,0); if (r == -1) { perror("fcntl getfl"); exit(8); }
+  if (yesno) r |= O_NDELAY;
+  else r &= ~O_NDELAY;
+  if (fcntl(fd,F_SETFL,r) == -1) { perror("fcntl setfl"); exit(8); }
+}
+
+static void unnonblock(void) {
+  nonblock(0,0); nonblock(1,0);
+}
+
+void startupcore(void) {
+  buf= xmalloc(buffersize);
+
+  if (opt_mlock) {
+    if (mlock(buf,buffersize)) { perror("mlock"); exit(2); }
+  }
+
+  used=0; wp=rp=buf; seeneof=0;
+  if (atexit(unnonblock)) { perror("atexit"); exit(16); }
+}
+
+void startup(const char *const *argv) {
+  const char *arg;
+  char *ep;
+  int shift=-1;
+  
+  assert(argv[0]);
+  
+  while ((arg= *++argv)) {
+    if (!strcmp(arg,"--mlock")) {
+      opt_mlock= 1;
+    } else if (isdigit((unsigned char)arg[0])) {
+      buffersize= strtoul(arg,&ep,0);
+      if (ep[0] && ep[1]) usageerr("buffer size spec. invalid");
+      switch (ep[0]) {
+      case 0: case 'm':  shift= 20;  break;
+      case 'k':          shift= 10;  break;
+      case 'b':          shift= 0;   break;
+      default: usageerr("buffer size unit unknown");
+      }
+      if (buffersize > ((RWBUFFER_SIZE_MB_MAX << 20) >> shift))
+       usageerr("buffer size too big");
+      buffersize <<= shift;
+    } else {
+      usageerr("invalid option");
+    }
+  }
+
+  startupcore();
+  nonblock(0,1); nonblock(1,1);
+}
+
+void *xmalloc(size_t sz) {
+  void *r= malloc(sz); if (!r) { perror("malloc"); exit(6); }; return r;
+}
+
+void callselect(void) {
+  int r;
+  
+  for (;;) {
+    r= select(maxselfd,&readfds,&writefds,0,0);
+    if (r != -1) return;
+    if (errno != EINTR) {
+      perror("select"); exit(4);
+    }
+  }
+}
diff --git a/cprogs/rwbuffer.h b/cprogs/rwbuffer.h
new file mode 100644 (file)
index 0000000..f49b30b
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * rwbuffer.h
+ * common declarations for readbuffer/writebuffer
+ *
+ * readbuffer and writebuffer are:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * readbuffer is part of chiark backup, a system for backing up GNU/Linux and
+ * other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+ * chiark backup is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+
+#ifndef RWBUFFER_H
+#define RWBUFFER_H
+
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <assert.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include "dlist.h"
+
+
+int min(int a, int b);
+void callselect(void);
+void startup(const char *const *argv);
+void startupcore(void);
+void *xmalloc(size_t sz);
+void nonblock(int fd, int yesno);
+
+extern const char *progname; /* must be defined by main .c file */
+
+extern unsigned char *buf, *wp, *rp;
+extern int used, seeneof, maxselfd;
+extern size_t buffersize;
+extern fd_set readfds;
+extern fd_set writefds;
+
+
+void wrbufcore_startup(void);
+void wrbufcore_prepselect(int rdfd, int wrfd);
+void wrbufcore_afterselect(int rdfd, int wrfd);
+void fdsetset(int fd, fd_set *set);
+void wrbuf_report(const char *m);
+
+
+#endif /*RWBUFFER_H*/
diff --git a/cprogs/smtpallow.c b/cprogs/smtpallow.c
new file mode 100644 (file)
index 0000000..b4f8765
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * smtpallow.c - ld_preload for hacking with connect() !
+ *
+ * Copyright (C) 1994,1995 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+#include <syscall.h>
+#include <sys/socketcall.h>
+#include <netinet/in.h>
+#include <string.h>
+
+_syscall2(long,socketcall,int,call,unsigned long *,args);
+
+int real_connect(int sockfd, const struct sockaddr *saddr, int addrlen)
+{
+       unsigned long args[3];
+
+       args[0] = sockfd;
+       args[1] = (unsigned long)saddr;
+       args[2] = addrlen;
+       return socketcall(SYS_CONNECT, args);
+}
+
+int connect(int fd, const struct sockaddr *them_any, int addrlen) {
+  struct sockaddr_in *them= (struct sockaddr_in*)them_any;
+  int r,l,i;
+  struct sockaddr_in us;
+  
+  if (addrlen == sizeof(us) &&
+      them->sin_family == AF_INET &&
+      them->sin_port == htons(25)) {
+    memset(&us,0,sizeof(us));
+    us.sin_port= 0;
+    us.sin_family= AF_INET;
+    us.sin_addr.s_addr= INADDR_ANY;
+    r= getsockname(fd,(struct sockaddr*)&us,&l);
+    if (r<0 && errno != EINVAL) return r;
+    if (!ntohs(us.sin_port)) {
+      for (i=1023; i>0; i--) {
+        us.sin_port= htons(i);
+        if (!bind(fd,(struct sockaddr*)&us,sizeof(us))) break;
+        if (errno != EADDRINUSE) return -1;
+      }
+      if (!i) return -1;
+    } else if (r<0) return r;
+  }
+  return real_connect(fd,them_any,addrlen);
+}
diff --git a/cprogs/summer.1 b/cprogs/summer.1
new file mode 100644 (file)
index 0000000..a77b8c9
--- /dev/null
@@ -0,0 +1,157 @@
+.TH SUMMER "1" "December 2006" "Debian" "Chiark-utils-bin"
+.SH NAME
+summer \- print checksum and system metainformation for files
+.SH SYNOPSIS
+.B summer -ACDbfqtx
+.RI [\| startpoint ...]
+.br
+.SH DESCRIPTION
+.B summer
+prints the MD5 checksum of the contents, and the system
+metainformation (ownership, permissions, timestamps, etc.), for a
+file, or recursively for a whole directory tree.
+
+Each command line argument should be a file or directory to be processed;
+if it is a directory then it will be processed and then its contents will
+also be processed, recursively. If no
+.IR startpoint s
+are specified on the command line then
+.B summer
+will read a list of newline-separated startpoints from standard input.
+
+Since
+.B summer
+correctly handles devices, FIFOs and other non-regular files it is useful
+for generating and comparing summaries of arbitrary directory trees where
+md5sum alone would not be.
+.SH OUTPUT FORMAT
+.B summer
+prints one line of information for each filesystem object it processes.
+Each line has the following columns:
+.TS
+tab (@);
+l l.
+@MD5 checksum (in hex) or file type information
+@Size of file in bytes
+@File access rights (in octal)
+@User ID of owner (in decimal)
+@Group ID of owner (in decimal)
+@atime (time of last access, decimal time_t)
+@mtime (time of last modification)
+@ctime (time of last status change)
+@Filename
+.TE
+
+For regular files, the first column is the md5sum. For directories, pipes,
+symlinks and sockets it is the literal string \fBdir\fR, \fBmountpoint\fR, \fBpipe\fR, \fBsymlink\fR or \fBsocket\fR
+as appropriate. For devices it begins with \fBc\fR for character or \fBb\fR for block
+devices, followed by the device number as a single 32 bit hex number and as
+four separate 8 bit decimal numbers (most significant first).
+
+Note that any bytes in the filename other than printing 7-bit ASCII
+are escaped using
+.B \\\x\c
+.I NN
+syntax, where
+.I NN
+are two hex digits; backslashes are also escaped in this way.
+This makes the output unambiguous.  Filenames will be relative
+if the relevant
+.I startpoint
+was relative, and absolute if it was absolute.
+
+For symlinks the filename column is followed by `\fB -> \fR' (note the
+spaces) and the target of the link, again escaped, as above.
+.SH OPTIONS
+.TP
+.B \-A
+Do not print the atime (time of last access). The atime column will be omitted.
+.TP
+.B \-C
+Do not print the ctime (time of last status change). The ctime column will be omitted.
+.TP
+.B \-M
+Do not print the mtime (time of last modification). The mtime column will be omitted.
+.TP
+.B \-D
+Do not print directory sizes. The size column for directories will read \fBdir\fR.
+.TP
+.B \-b
+Do not print mtime (time of last modification) for symbolic links. The mtime field
+for symbolic links will read \fBlink\fR.
+.TP
+.B \-B
+Do not print any times for special files, symlinks,
+sockets, or fifos.  The atime, mtime and ctime fields
+for these objects will read
+.BR char ", " block ", " link  ", " sock " or " pipe
+instead.
+.TP
+.B \-f
+Include information about errors encountered (for example, unreadable files)
+in the output, and continue processing. The default is to print error information
+to standard error and stop immediately an error is encountered.
+.TP
+.B \-x
+Do not cross mountpoints while recursing into subdirectories.  
+Startpoints which are mountpoints \fIare\fR descended into.
+.TP
+.B \-q
+Suppress the progress information which
+.B summer
+normally prints to standard error.
+.TP
+.B \-t
+Set the field separator between the information and the filename to a
+tab character (default is space).
+.TP
+.B \-f
+Normally any errors (problems accessing files including nonexistent
+startpoings, and the like) are fatal; an error message is reported to
+stderr.
+
+With
+.B -f
+errors are nonfatal and the problems are reported inline.  The
+filesystem object with the problem is reported in the normal way
+except that instead of the checksum, the string
+\fB\\[\fR\fIproblem\fR[\fB:\fR\ \fIdetails\fR]\fB]\fR
+appears.  Fields whose value could not be determined are printed
+as \fB?\fR.
+.TP
+.B \-h
+Print a brief usage message to stderr (and do nothing else, exiting nonzero).
+.SH PARSING THE OUTPUT
+If the first character in the line is \fB\\[\fR, then the first
+(checksum or type) field is everything until the first subsequent
+\fB]\fR; this may be of variable length and will be followed by one or
+more spaces.  Otherwise the first field has a fixed width: 64
+characters, the size of an MD5 checksum represented in hex, and is
+followed by a single space.
+
+The metadata fields are space-separated but are also space-padded to a
+minimum width: 10 characters for sizes and times and ids; 4 characters
+for the mode.
+
+The filename field, and optional link target information, are of
+variable length, but they are escaped so that they do not contain
+spaces.
+.SH AUTHOR
+.B summer
+is
+.br
+Copyright (C) 2003-2007 Ian Jackson <ian@chiark.greenend.org.uk>
+
+This manpage was written by Peter Maydell
+and subsequently improved by Ian Jackson.  It is
+.br
+Copyright (C) 2006 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+.br
+Copyright (C) 2007 Ian Jackson <ian@chiark.greenend.org.uk>
+
+This is free software, distributed under the GNU General Public
+Licence, version 3 or (at your option) any later version; see
+/usr/share/doc/chiark-utils-bin/copyright or
+/usr/share/common-licenses/GPL-3
+for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/cprogs/summer.c b/cprogs/summer.c
new file mode 100644 (file)
index 0000000..762b5d6
--- /dev/null
@@ -0,0 +1,450 @@
+/*
+ * summer - program for summarising (with md5 checksums) filesystem trees
+ *
+ * usage:
+ *    cat startpoints.list | summer >data.list
+ *    summer startpoints... >data.list
+ *  prints md5sum of data-list to stderr
+ */
+/*
+ * Copyright (C) 2003,2006-2007 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+#define _GNU_SOURCE
+
+#include <search.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <limits.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "nettle/md5-compat.h"
+
+#define MAXFN 2048
+#define MAXDEPTH 1024
+#define CSUMXL 32
+
+static int quiet=0, hidectime=0, hideatime=0, hidemtime=0;
+static int hidedirsize=0, hidelinkmtime=0, hidextime=0, onefilesystem=0;
+static int filenamefieldsep=' ';
+static FILE *errfile;
+
+#define nodeflag_fsvalid       1u
+
+static void malloc_fail(void) { perror("summer: alloc failed"); exit(12); }
+
+static void *mmalloc(size_t sz) {
+  void *r;
+  r= malloc(sz);  if (!r) malloc_fail();
+  return r;
+}
+
+static void *mrealloc(void *p, size_t sz) {
+  void *r;
+  r= realloc(p, sz);  if (!r && sz) malloc_fail();
+  return r;
+}
+
+static void fn_escaped(FILE *f, const char *fn) {
+  int c;
+  while ((c= *fn++)) {
+    if (c>=33 && c<=126 && c!='\\') putc(c,f);
+    else fprintf(f,"\\x%02x",(int)(unsigned char)c);
+  }
+}
+
+static void add_pr(int *pr, int printf_ret) {
+  if (printf_ret == EOF) return;
+  *pr += printf_ret;
+}
+
+static void vproblemx(const char *path, int padto, int per,
+                     const char *fmt, va_list al) {
+  int e=errno, pr=0;
+  
+  if (errfile==stderr) fputs("summer: error: ",stderr);
+  else add_pr(&pr, fprintf(errfile,"\\["));
+  
+  add_pr(&pr, vfprintf(errfile,fmt,al));
+  if (per) add_pr(&pr, fprintf(errfile,": %s",strerror(e)));
+
+  if (errfile==stderr) {
+    fputs(": ",stderr);
+    fn_escaped(stderr,path);
+    fputc('\n',stderr);
+    exit(2);
+  }
+
+  add_pr(&pr, printf("]"));
+
+  while (pr++ < padto)
+    putchar(' ');
+}  
+
+static void problem_e(const char *path, int padto, const char *fmt, ...) {
+  va_list(al);
+  va_start(al,fmt);
+  vproblemx(path,padto,1,fmt,al);
+  va_end(al);
+}
+
+static void problem(const char *path, int padto, const char *fmt, ...) {
+  va_list(al);
+  va_start(al,fmt);
+  vproblemx(path,padto,0,fmt,al);
+  va_end(al);
+}
+
+static void csum_file(const char *path) {
+  FILE *f;
+  MD5_CTX mc;
+  char db[65536];
+  unsigned char digest[16];
+  size_t r;
+  int i;
+
+  f= fopen(path,"rb");
+  if (!f) { problem_e(path,sizeof(digest)*2,"open"); return; }
+  
+  MD5Init(&mc);
+  for (;;) {
+    r= fread(db,1,sizeof(db),f);
+    if (ferror(f)) {
+      problem_e(path,sizeof(digest)*2,"read");
+      fclose(f); return;
+    }
+    if (!r) { assert(feof(f)); break; }
+    MD5Update(&mc,db,r);
+  }
+  MD5Final(digest,&mc);
+  if (fclose(f)) { problem_e(path,sizeof(digest)*2,"close"); return; }
+
+  for (i=0; i<sizeof(digest); i++)
+    printf("%02x", digest[i]);
+}
+
+static void csum_dev(int cb, const struct stat *stab) {
+  printf("%c 0x%08lx %3lu %3lu %3lu %3lu    ", cb,
+        (unsigned long)stab->st_rdev,
+        ((unsigned long)stab->st_rdev & 0x0ff000000U) >> 24,
+        ((unsigned long)stab->st_rdev & 0x000ff0000U) >> 16,
+        ((unsigned long)stab->st_rdev & 0x00000ff00U) >> 8,
+        ((unsigned long)stab->st_rdev & 0x0000000ffU) >> 0);
+}
+
+static void csum_str(const char *s) {
+  printf("%-*s", CSUMXL, s);
+}
+
+static void linktargpath(const char *linktarg) {
+  printf(" -> ");
+  fn_escaped(stdout, linktarg);
+}
+
+static void pu10(void) { printf(" %10s", "?"); }
+
+#define PTIME(stab, memb)  ((stab) ? ptime((stab), (stab)->memb) : pu10())
+
+static void ptime(const struct stat *stab, unsigned long val) {
+  const char *instead;
+
+  if (!hidextime) goto justprint;
+  else if (S_ISCHR(stab->st_mode)) instead= "char";
+  else if (S_ISBLK(stab->st_mode)) instead= "block";
+  else if (S_ISLNK(stab->st_mode)) instead= "link";
+  else if (S_ISSOCK(stab->st_mode)) instead= "sock";
+  else if (S_ISFIFO(stab->st_mode)) instead= "pipe";
+  else {
+  justprint:
+    printf(" %10lu", val);
+    return;
+  }
+
+  printf(" %10s",instead);
+}
+
+struct hardlink {
+  dev_t dev;
+  ino_t ino;
+  char path[1];
+};
+static void *hardlinks;
+
+static int hardlink_compar(const void *av, const void *bv) {
+  const struct hardlink *a=av, *b=bv;
+  if (a->ino != b->ino) return b->ino - a->ino;
+  return b->dev - a->dev;
+}
+
+static void recurse(const char *path, unsigned nodeflags, dev_t fs);
+
+static void node(const char *path, unsigned nodeflags, dev_t fs) {
+  char linktarg[MAXFN+1];
+  struct hardlink *foundhl;
+  const struct stat *stab;
+  struct stat stabuf;
+  int r, mountpoint=0;
+
+  r= lstat(path, &stabuf);
+  stab= r ? 0 : &stabuf;
+
+  foundhl= 0;
+  if (stab && stab->st_nlink>1) {
+    struct hardlink *newhl, **foundhl_node;
+    newhl= mmalloc(sizeof(*newhl) + strlen(path));
+    newhl->dev= stab->st_dev;
+    newhl->ino= stab->st_ino;
+    foundhl_node= tsearch(newhl, &hardlinks, hardlink_compar);
+    if (!foundhl_node) malloc_fail();
+    foundhl= *foundhl_node;
+    if (foundhl!=newhl) {
+      free(newhl); /* hardlink to an earlier object */
+    } else {
+      foundhl= 0; /* new object with link count>1 */
+      strcpy(newhl->path, path);
+    }
+  }
+
+  if (stab) {
+    if ((nodeflags & nodeflag_fsvalid) && stab->st_dev != fs)
+      mountpoint= 1;
+    fs= stab->st_dev;
+    nodeflags |= nodeflag_fsvalid;
+  }
+
+  if (!stab) problem_e(path,CSUMXL,"inaccessible");
+  else if (foundhl) csum_str("hardlink");
+  else if (S_ISREG(stab->st_mode)) csum_file(path);
+  else if (S_ISCHR(stab->st_mode)) csum_dev('c',stab);
+  else if (S_ISBLK(stab->st_mode)) csum_dev('b',stab);
+  else if (S_ISFIFO(stab->st_mode)) csum_str("pipe");
+  else if (S_ISLNK(stab->st_mode)) csum_str("symlink");
+  else if (S_ISSOCK(stab->st_mode)) csum_str("sock");
+  else if (S_ISDIR(stab->st_mode)) csum_str(mountpoint ? "mountpoint" : "dir");
+  else problem(path,CSUMXL,"badobj: 0x%lx", (unsigned long)stab->st_mode);
+
+  if (stab && S_ISLNK(stab->st_mode)) {
+    r= readlink(path, linktarg, sizeof(linktarg)-1);
+    if (r==sizeof(linktarg)) { problem(path,-1,"readlink too big"); r=-1; }
+    else if (r<0) { problem_e(path,-1,"readlink"); }
+    else assert(r<sizeof(linktarg));
+
+    if (r<0) strcpy(linktarg,"\\?");
+    else linktarg[r]= 0;
+  }
+
+  if (stab) {
+    if (S_ISDIR(stab->st_mode) && hidedirsize)
+      printf(" %10s","dir");
+    else
+      printf(" %10lu", 
+            (unsigned long)stab->st_size);
+
+    printf(" %4o %10ld %10ld",
+          (unsigned)stab->st_mode & 07777U,
+          (unsigned long)stab->st_uid,
+          (unsigned long)stab->st_gid);
+  } else {
+    printf(" %10s %4s %10s %10s", "?","?","?","?");
+  }
+
+  if (!hideatime)
+    PTIME(stab, st_atime);
+
+  if (!hidemtime) {
+    if (stab && S_ISLNK(stab->st_mode) && hidelinkmtime)
+      printf(" %10s","link");
+    else
+      PTIME(stab, st_mtime);
+  }
+
+  if (!hidectime)
+    PTIME(stab, st_ctime);
+
+  putchar(filenamefieldsep);
+  fn_escaped(stdout, path);
+
+  if (foundhl) linktargpath(foundhl->path);
+  if (stab && S_ISLNK(stab->st_mode)) linktargpath(linktarg);
+
+  putchar('\n');
+
+  if (ferror(stdout)) { perror("summer: stdout"); exit(12); }
+
+  if (stab && S_ISDIR(stab->st_mode) && !(mountpoint && onefilesystem))
+    recurse(path, nodeflags, fs);
+}
+
+static void process(const char *startpoint) {
+  if (!quiet)
+    fprintf(stderr,"summer: processing: %s\n",startpoint);
+  node(startpoint, 0,0);
+  tdestroy(hardlinks,free);
+  hardlinks= 0;
+}
+
+static int recurse_maxlen;
+
+static int recurse_filter(const struct dirent *de) {
+  int l;
+  if (de->d_name[0]=='.' &&
+      (de->d_name[1]==0 ||
+       (de->d_name[1]=='.' &&
+       de->d_name[2]==0)))
+    return 0;
+  l= strlen(de->d_name);
+  if (l > recurse_maxlen) recurse_maxlen= l;
+  return 1;
+}
+
+static int recurse_compar(const struct dirent **a, const struct dirent **b) {
+  return strcmp((*a)->d_name, (*b)->d_name);
+}
+
+static void recurse(const char *path_or_buf, unsigned nodeflags, dev_t fs) {
+  static char *buf;
+  static int buf_allocd;
+  
+  struct dirent **namelist, *const *de;
+  const char *path_or_0= path_or_buf==buf ? 0 : path_or_buf;
+  int nentries, pathl, esave, buf_want, i;
+
+  pathl= strlen(path_or_buf);
+  recurse_maxlen= 2;
+  nentries= scandir(path_or_buf, &namelist, recurse_filter, recurse_compar);
+  esave= errno;
+  
+  buf_want= pathl+1+recurse_maxlen+1;
+  if (buf_want > buf_allocd) {
+    buf= mrealloc(buf, buf_want);
+    buf_allocd= buf_want;
+  }
+  /* NOTE that path_or_buf is invalid after this point because
+   * it might have been realloc'd ! */
+  if (path_or_0) strcpy(buf,path_or_0);
+
+  buf[pathl]= '/';
+  pathl++;
+  if (nentries < 0) {
+    buf[pathl]= 0;  errno= esave;
+    problem_e(buf,CSUMXL+72,"scandir failed");
+    fn_escaped(stdout,buf);  putchar('\n');
+    return;
+  }
+  for (i=0, de=namelist; i<nentries; i++, de++) {
+    strcpy(buf+pathl, (*de)->d_name);
+    node(buf, nodeflags, fs);
+    free(*de);
+  }
+  free(namelist);
+}
+
+static void from_stdin(void) {
+  char buf[MAXFN+2];
+  char *s;
+  int l;
+
+  if (!quiet)
+    fprintf(stderr, "summer: processing stdin lines as startpoints\n");
+  for (;;) {
+    s= fgets(buf,sizeof(buf),stdin);
+    if (ferror(stdin)) { perror("summer: stdin"); exit(12); }
+    if (!s) { if (feof(stdin)) return; else abort(); }
+    l= strlen(buf);
+    assert(l>0);
+    if (buf[l-1]!='\n') { fprintf(stderr,"summer: line too long\n"); exit(8); }
+    buf[l-1]= 0;
+    process(buf);
+  }
+}
+
+int main(int argc, const char *const *argv) {
+  const char *arg;
+  int c;
+
+  errfile= stderr;
+  
+  while ((arg=argv[1]) && *arg++=='-') {
+    while ((c=*arg++)) {
+      switch (c) {
+      case 'h':
+       fprintf(stderr,
+               "summer: usage: summer startpoint... >data.list\n"
+               "               cat startpoints.list | summer >data.list\n");
+       exit(8);
+      case 'q':
+       quiet= 1;
+       break;
+      case 't':
+       filenamefieldsep= '\t';
+       break;
+      case 'D':
+       hidedirsize= 1;
+       break;
+      case 'b':
+       hidelinkmtime= 1;
+       break;
+      case 'B':
+       hidextime= 1;
+       break;
+      case 'x':
+       onefilesystem= 1;
+       break;
+      case 'C':
+       hidectime= 1;
+       break;
+      case 'A':
+       hideatime= 1;
+       break;
+      case 'M':
+       hidemtime= 1;
+       break;
+      case 'f':
+       errfile= stdout;
+       break;
+      default:
+       fprintf(stderr,"summer: bad usage, try -h\n");
+       exit(8);
+      }
+    }
+    argv++;
+  }
+
+  if (!argv[1]) {
+    from_stdin();
+  } else {
+    if (!quiet)
+      fprintf(stderr, "summer: processing command line args as startpoints\n");
+    while ((arg=*++argv)) {
+      process(arg);
+    }
+  }
+  if (ferror(stdout) || fclose(stdout)) {
+    perror("summer: stdout (at end)"); exit(12);
+  }
+  if (!quiet)
+    fputs("summer: done.\n", stderr);
+  return 0;
+}
diff --git a/cprogs/trivsoundd-start b/cprogs/trivsoundd-start
new file mode 100755 (executable)
index 0000000..9b50700
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/bash
+set -e
+pf=`tempfile`
+rm -f $pf
+mknod -m600 $pf p
+exec 3<>$pf
+exec >$pf 4<$pf
+rm -f $pf
+exec 3>&-
+(exec logger -p user.info -t trivsoundd <&4 >/dev/null 2>&1) &
+exec 4<&- 2>&1 </dev/null
+exec ${0%%-start}
diff --git a/cprogs/trivsoundd.8 b/cprogs/trivsoundd.8
new file mode 100644 (file)
index 0000000..796ba93
--- /dev/null
@@ -0,0 +1,25 @@
+.TH writebuffer 1 2001-10-21 chiark-backup
+.SH NAME
+writebuffer \- write output to devices which don't like constant stopping and starting
+.SH SYNOPSIS
+.B writebuffer
+.RB [ --mlock ]
+.RI [ size ]
+.SH DESCRIPTION
+.B writebuffer
+reads data on standard input and writes it to standard output.  It
+will buffer internally up to \fIsize\fR megabytes and will only write
+data when the buffer is at least 75% full or when there is no more
+input to fill the buffer.
+.PP
+It is intended for use in situations where many small writes are
+undesirable for performance reasons, e.g. tape drives.
+.SH OPTIONS
+.TP
+.B --mlock
+Calls
+.BR mlock (2)
+to lock the buffer into memory.
+.SH "SEE ALSO"
+.BR readbuffer (1),
+.BR mlock (2)
diff --git a/cprogs/trivsoundd.c b/cprogs/trivsoundd.c
new file mode 100644 (file)
index 0000000..d1e6636
--- /dev/null
@@ -0,0 +1,233 @@
+/*
+ * triv-sound-d.c
+ * writebuffer adapted for sound-playing
+ *
+ * readbuffer and writebuffer are:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * readbuffer is part of chiark backup, a system for backing up GNU/Linux and
+ * other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+ * chiark backup is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+
+#include "rwbuffer.h"
+
+const char *progname= "trivsoundd";
+
+static int maxstartdelay=60, maxbadaccept=10;
+
+struct inqnode {
+  struct inqnode *next, *back;
+  time_t accepted;
+  int fd;
+};
+
+static struct { struct inqnode *head, *tail; } inq;
+static int master, sdev;
+static time_t now;
+
+static void usageerr(const char *m) {
+  fprintf(stderr,"bad usage: %s\n",m);
+  exit(12);
+}
+
+static void bindmaster(const char *bindname) {
+  union {
+    struct sockaddr sa;
+    struct sockaddr_in sin;
+    struct sockaddr_un sun;
+  } su;
+  socklen_t sulen;
+  const char *colon;
+  char *copy, *ep;
+  int r;
+  unsigned long portul;
+  struct hostent *he;
+  struct servent *se;
+
+  memset(&su,0,sizeof(su));
+
+  if (bindname[0]=='/' || bindname[0]=='.') {
+
+    if (strlen(bindname) >= sizeof(su.sun.sun_path))
+      usageerr("AF_UNIX bind path too long");
+    sulen= sizeof(su.sun);
+    su.sun.sun_family= AF_UNIX;
+    strcpy(su.sun.sun_path, bindname);
+
+  } else if (bindname[0] != ':' && (colon= strrchr(bindname,':'))) {
+
+    sulen= sizeof(su.sin);
+    su.sin.sin_family= AF_INET;
+
+    copy= xmalloc(colon - bindname + 1);
+    memcpy(copy,bindname, colon - bindname + 1);
+    copy[colon - bindname]= 0;
+    portul= strtoul(colon+1,&ep,0);
+
+    if (!*ep) {
+      if (!portul || portul>=65536) usageerr("invalid port number");
+      su.sin.sin_port= htons(portul);
+    } else {
+      se= getservbyname(colon+1, "tcp");
+      if (!se) { fprintf(stderr,"unknown service `%s'\n",colon+1); exit(4); }
+      su.sin.sin_port= htons(se->s_port);
+    }
+
+    if (!strcmp(copy,"any")) {
+      su.sin.sin_addr.s_addr= INADDR_ANY;
+    } else if (!inet_aton(copy,&su.sin.sin_addr)) {
+      he= gethostbyname(copy);
+      if (!he) { herror(copy); exit(4); }
+      if (he->h_addrtype != AF_INET ||
+         he->h_length != sizeof(su.sin.sin_addr) ||
+         !he->h_addr_list[0] ||
+         he->h_addr_list[1]) {
+       fprintf(stderr,"hostname lookup `%s' did not yield"
+               " exactly one IPv4 address\n",copy);
+       exit(4);
+      }
+      memcpy(&su.sin.sin_addr, he->h_addr_list[0], sizeof(su.sin.sin_addr));
+    }
+
+  } else {
+    usageerr("unknown bind name");
+    exit(12);
+  }
+
+  master= socket(su.sa.sa_family,SOCK_STREAM,0);
+  if (master<0) { perror("socket"); exit(8); }
+
+  r= bind(master, &su.sa, sulen);
+  if (r) { perror("bind"); exit(8); }
+
+  r= listen(master, 5);
+  if (r) { perror("listen"); exit(8); }
+}
+
+static void opensounddevice(void) {
+  int r;
+  char cbuf[200];
+  
+  sdev= open("/dev/dsp", O_WRONLY);
+  if (sdev<0) { perror("open sound device"); exit(8); }
+
+  snprintf(cbuf, sizeof(cbuf), "sox -t raw -s -w -r 44100 -c 2"
+          " - </dev/null -t ossdsp - >&%d", sdev);
+  r= system(cbuf);  if (r) { fprintf(stderr,"sox gave %d\n",r); exit(5); }
+}
+
+void wrbuf_report(const char *m) {
+  printf("writing %s\n", m);
+}
+
+static void selectcopy(void) {
+  int slave= inq.head ? inq.head->fd : -1;
+  wrbufcore_prepselect(slave, sdev);
+  fdsetset(master,&readfds);
+  callselect();
+  wrbufcore_afterselect(slave, sdev);
+}
+
+static void expireoldconns(void) {
+  struct inqnode *searchold, *nextsearchold;
+      
+  for (searchold= inq.head ? inq.head->next : 0;
+       searchold;
+       searchold= nextsearchold) {
+    nextsearchold= searchold->next;
+    if (searchold->accepted < now-maxstartdelay) {
+      printf("expired %p\n",searchold);
+      LIST_UNLINK(inq,searchold);
+      free(searchold);
+    }
+  }
+}
+
+static void acceptnewconns(void) {
+  static int bad;
+  
+  int slave;
+  struct inqnode *new;
+
+  if (!FD_ISSET(master,&readfds)) return;
+
+  slave= accept(master,0,0);
+  if (slave < 0) {
+    if (!(errno == EINTR ||
+         errno == EAGAIN ||
+         errno == EWOULDBLOCK)) {
+      perror("accept");
+      bad++;
+      if (bad > maxbadaccept) {
+       fprintf(stderr,"accept failures repeating\n");
+       exit(4);
+      }
+    }
+    /* any transient error will just send us round again via select */
+    return;
+  }
+
+  bad= 0;
+  new= xmalloc(sizeof(struct inqnode));
+  new->accepted= now;
+  new->fd= slave;
+  LIST_LINK_TAIL(inq,new);
+
+  printf("accepted %p\n",new);
+}
+
+static void switchinput(void) {
+  struct inqnode *old;
+  if (!seeneof) return;
+  old= inq.head;
+  assert(old);
+  printf("finished %p\n",old);
+  close(old->fd);
+  LIST_UNLINK(inq,old);
+  free(old);
+  seeneof= 0;
+}  
+
+int main(int argc, const char *const *argv) {
+  assert(argv[0]);
+  if (!argv[1] || argv[2] || argv[1][0]=='-')
+    usageerr("no options allowed, must have one argument (bindname)");
+
+  buffersize= 44100*4* 5/*seconds*/;
+
+  opensounddevice();
+  bindmaster(argv[1]);
+  nonblock(sdev,1);
+  nonblock(master,1);
+
+  startupcore();
+  wrbufcore_startup();
+  
+  printf("started\n");
+  for (;;) {
+    selectcopy();
+    if (time(&now)==(time_t)-1) { perror("time(2)"); exit(4); }
+    expireoldconns();
+    acceptnewconns();
+    switchinput();
+  }
+}
diff --git a/cprogs/usernice.c b/cprogs/usernice.c
new file mode 100644 (file)
index 0000000..1f05e19
--- /dev/null
@@ -0,0 +1,67 @@
+/**/
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <pwd.h>
+#include <sys/resource.h>
+
+int main(int argc,char **argv) {
+  long l;
+  int mrenice,wrenice,newprio,eflag;
+  uid_t ruid;
+  char *ep;
+  struct passwd *pw;
+
+  mrenice=0;
+  if (argc < 3) {
+    fputs("usernice: too few args\n"
+          " usage: usernice <nicelevel> <command> <arguments>\n"
+          "        usernice <nicelevel>p <pid> <pid> ...\n"
+          "        usernice <nicelevel>u <username|uid> ...\n",
+          stderr);
+    exit(-1);
+  }
+  l= strtol(*++argv,&ep,10);
+  if (*ep == 'p' || *ep == 'u') { mrenice= *ep++; }
+  if (*ep) { fputs("usernice: priority not numeric or bad flags\n",stderr); exit(-1); }
+  if (l<-20 || l>20)
+    { fputs("usernice: priority must be -20 .. 20\n",stderr); exit(-1); }
+  newprio= l;
+  if (mrenice) {
+    eflag=0;
+    while (*++argv) {
+      if (mrenice == 'p') {
+        wrenice= PRIO_PROCESS;
+        l= strtoul(*argv,&ep,10);
+        if (*ep) {
+          fprintf(stderr,"usernice: pid `%s' not numeric\n",*argv); eflag=2;
+          continue;
+        }
+      } else {
+        wrenice= PRIO_USER;
+        l= strtoul(*argv,&ep,10);
+        if (*ep) {
+          pw= getpwnam(*argv);
+          if (!pw) {
+            fprintf(stderr,"usernice: unknown user `%s'\n",*argv); eflag=2;
+            continue;
+          }
+          l= pw->pw_uid;
+        }
+      }
+      if (setpriority(wrenice,l,newprio)) {
+        perror(*argv); if (!eflag) eflag=1;
+      }
+    }
+    exit(eflag);
+  } else {
+    if (setpriority(PRIO_PROCESS,0,newprio))
+      { perror("usernice: setpriority"); exit(-1); }
+    ruid= getuid(); if (ruid == (uid_t)-1) { perror("usernice: getuid"); exit(-1); }
+    if (setreuid(ruid,ruid)) { perror("usernice: setreuid"); exit(-1); }
+    execvp(argv[1],argv+1); perror("usernice: exec"); exit(-1);
+  }
+}
diff --git a/cprogs/usr-local-src-misc-Makefile b/cprogs/usr-local-src-misc-Makefile
new file mode 100644 (file)
index 0000000..c2bfd7a
--- /dev/null
@@ -0,0 +1,22 @@
+CFLAGS=        -Wall -Wwrite-strings -Wmissing-prototypes -Wstrict-prototypes \
+       -Wpointer-arith -O2 -g -DREALLY_CHECK_FILE='"/etc/inittab"'
+LDFLAGS=
+
+TARGETS=really ucgi ucgitarget
+
+all:           $(TARGETS)
+
+ucgi:          ucgi.o ucgicommon.o
+
+ucgitarget:    ucgitarget.o ucgicommon.o
+
+really:                really.o myopt.o
+
+really-test:   really Makefile
+               rm -f really-test
+               cp really really-test
+               really chown root.staff really-test
+               really chmod 4770 really-test
+
+really-check:  really-test really.testcases
+               ./really.testcases
diff --git a/cprogs/watershed.c b/cprogs/watershed.c
new file mode 100644 (file)
index 0000000..3bf9b07
--- /dev/null
@@ -0,0 +1,488 @@
+/*
+ * watershed - an auxiliary verb for optimising away
+ *             unnecessary runs of idempotent commands
+ *
+ * watershed is Copyright 2007 Canonical Ltd
+ * written by Ian Jackson <ian@davenant.greenend.org.uk>
+ * and this version now maintained as part of chiark-utils
+ *
+ *
+ * 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 3 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+/*
+ *  NB a different fork of this code exists in Ubuntu's udev.
+ */
+/*
+ *
+ * usage: watershed [<options>] <command> [<arg>...]
+ *
+ * options:
+ *   -d|--state-dir <state-dir>
+ *        default is /var/run/watershed for uid 0
+ *                   $HOME/.watershed for others
+ *   -i|--command-id <command-id>
+ *
+ * files used:
+ *    <state-dir>/<command-id>.lock            lockfile
+ *    <state-dir>/<command-id>.cohort          cohort
+ *
+ * default <command-id> is
+ *    hex(sha256(argv[0]+'\0' + argv[1]+'\0' ... argv[argc-1]+'\0')
+ *    '='
+ *    mangled argv[0] (all chars [^-+_0-9A-Za-z] replaced with ?
+ *                     and max 32 chars)
+ *
+ * exit status:
+ *  127      - something went wrong, or process died with some other signal
+ *  SIGPIPE  - process died with SIGPIPE
+ *  x        - process called _exit(x)
+ *
+ * stdin/stdout/stderr:
+ *
+ *  If watershed exits 127 due to some unexpected problem, a message
+ *  is printed to stderr explaining why (obviously).
+ *
+ *  If a watershed invocation ends up running the process, the process
+ *  simply inherits stdin/out/err.  Otherwise stdin/stdout are not used.
+ *
+ *  If the process run for us by another invocation of watershed exits
+ *  zero, or watershed die with the same signal as the process
+ *  (currently just SIGPIPE), nothing is printed to stderr.  Otherwise
+ *  (ie, failure of the actual process, in another invocation),
+ *  watershed prints a description of the wait status to stderr, much
+ *  as the shell might.
+ *
+ */
+/*
+ * gcc -Wall -Wwrite-strings -Wmissing-prototypes watershed.c -o watershed /usr/lib/libnettle.a
+ */
+/*
+ *
+ * Theory:
+ *
+ *  We consider only invocations with a specific command id (and state
+ *  directory), since other invocations are completely independent by
+ *  virtue of having different state file pathnames and thus different
+ *  state files.  Normally, a command id corresponds to invocations
+ *  with a particular set of command line arguments and a state
+ *  directory corresponds to a particular euid; environment variable
+ *  settings and other inherited process properties are disregarded.
+ *
+ *  A `cohort' is a set of invocations which can be coalesced into one
+ *  run of the command.  For each cohort there is a file, the cohort
+ *  file (which may not yet exist, may exist and have a name, or may
+ *  be unliked).
+ *
+ *  An `invocation' is an invocation of the `watershed' program.  A
+ *  `process' is an invocation of the requested command.
+ *
+ *  There is always one current cohort, in one of the following
+ *  two states:
+ *
+ *   * Empty
+ *     No invocations are in this cohort yet.
+ *     The cohort filename is ENOENT.
+ *     This is the initial state for a cohort, and the legal next
+ *     state is Accumulating.
+ *
+ *   * Accumulating
+ *     The process for this run has not yet started, so that new
+ *     invocations arriving would be satisfied if this cohort were to
+ *     run.
+ *     The cohort filename refers to this cohort's file.
+ *     The legal next state for the cohort is Ready.
+ *
+ *  Additionally, there may be older cohorts in the following states:
+ *
+ *   * Ready
+ *     The command for this cohort has not yet been run.
+ *     The cohort file has no name and is empty.
+ *     Only one cohort, the lockholder's, may be in this state.
+ *     The next legal states are Running, or exceptionally Forgotten
+ *     (if the lockholder crashes and is the only invocation in the
+ *     cohort).
+ *
+ *   * Running
+ *     The lockholder is running the command for this cohort.
+ *     This state is identical to Ready from the point of view
+ *     of all invocations except the lockholder.
+ *     The legal next states are Done (the usual case), or (if the
+ *     lockholder crashes) Ready or Forgotten.
+ *
+ *   * Done
+ *     The main process for this run has finished.
+ *     The cohort file has no name and contains sizeof(int)
+ *     bytes, the `status' value from waitpid.
+ *     The legal next state is Forgotten.
+ *
+ *   * Forgotten
+ *     All invocations have finished and the cohort file no longer
+ *     exists.  This is the final state.
+ *
+ *  Only the lockholder may move a cohort between states, except that
+ *  any invocation may make the current Empty cohort become
+ *  Accumulating, and that the kernel will automatically move a cohort
+ *  from Running to Ready or from Done to Forgotten, when appropriate.
+ *
+ * 
+ * Algorithm:
+ *
+ *   1. Open the cohort file (O_CREAT|O_RDWR)   so our cohort is
+ *                                                 Accumulating/Ready/
+ *                                                    Running/Done
+ *                              
+ *   2. Acquire lock (see below)                so lockholder's cohort is
+ *                                                Accumulating/Ready/Done
+ *   3. fstat the open cohort file
+ *         If it is nonempty:                      Done
+ *          Read status from it and exit.
+ *         Otherwise, if nonzero link count:       Accumulating
+ *          Unlink the cohort filename
+ *         Otherwise:                              Ready
+ *
+ *   4. Fork and run the command                   Running
+ *       and wait for it
+ *
+ *   5. Write the wait status to the cohort file   Done
+ *
+ *                      
+ *   6. Release the lock                        so we are no longer lockholder
+ *                                              but our cohort is still
+ *                                                 Done
+ *
+ *   8. Exit                                       Done/Forgotten
+ *
+ *  If an invocation crashes (ie, if watershed itself fails, rather
+ *  than if the command does) then that invocation's caller will be
+ *  informed of the error.
+ *
+ *  If the lockholder crashes with the cohort in:
+ *
+ *     Accumulating:
+ *       The cohort remains in Accumulating and another invocation can
+ *       become the lockholder.  If there are never any other
+ *       invocations then the lockfile and cohort file will not be
+ *       cleaned up (see below).
+ *
+ *     Running/Ready:
+ *       The cohort goes from Running back to Ready (see above) and
+ *       another invocation in the same cohort will become the
+ *       lockholder and run it.  If there is no other invocation in
+ *       the cohort the cohort goes to Forgotten although the lockfile
+ *       will not be cleaned up - see below.
+ *
+ *     Done:
+ *       If there are no more invocations, the cohort is Forgotten but
+ *       the lockfile is not cleaned up.
+ *
+ * Lockfile:
+ *
+ *  There is one lock for all cohorts.  The lockholder is the
+ *  invocation which holds the fcntl lock on the file whose name is
+ *  the lockfile.  The lockholder (and no-one else) may unlink the
+ *  lockfile.
+ *
+ *  To acquire the lock:
+ *
+ *   1. Open the lockfile (O_CREAT|O_RDWR)
+ *   2. Acquire fcntl lock (F_SETLKW)
+ *   3. fstat the open lockfile and stat the lockfile filenmae
+ *      If inode numbers disagree, close lockfile and start
+ *      again from the beginning.
+ *
+ *  To release the lock, unlink the lockfile and then either close it
+ *  or exit.  Crashing will also release the lock but leave the
+ *  lockfile lying around (which is slightly untidy but not
+ *  incorrect); if this is a problem a cleanup task could periodically
+ *  acquire and release the lock for each lockfile found:
+ *
+ * Cleanup:
+ *
+ *  As described above and below, stale cohort files and lockfiles can
+ *  result from invocations which crashed if the same command is never
+ *  run again.  Such cohorts are always in Empty or Accumulating.
+ *
+ *  If it became necessary to clean up stale cohort files and
+ *  lockfiles resulting from crashes, the following algorithm should
+ *  be executed for each lockfile found, as a cleanup task:
+ *
+ *   1. Acquire the lock.
+ *      This makes us the lockholder.           and the current cohort is in
+ *                                                 Empty/Accumulating
+ *
+ *                                              so now that cohort is
+ *   2. Unlink the cohort file, ignoring ENOENT.   Ready/Forgotten
+ *   3. Release the lock.                          Ready/Forgotten
+ *   4. Exit.                                      Ready/Forgotten
+ *
+ *  This consists only of legal transitions, so if current cohort
+ *  wasn't stale, it will have been moved to Ready and some other
+ *  invocation in this cohort will become the lockholder and as normal
+ *  from step 4 of the main algorithm.  If the cohort was stale it
+ *  will go to Forgotten straight away.
+ *
+ *  A suitable cleanup script, on a system with with-lock-ex, is: */
+ //     #!/bin/sh
+ //     set -e
+ //     if [ $# != 1 ]; echo >&2 'usage: cleanup <statedir>'; exit 1; fi
+ //     cd "$1"
+ //     for f in ./*.lock; do
+ //       with-lock-ex -w rm -f "${f%.lock}.cohort"
+ //     done
+/*
+ */
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <getopt.h>
+#include <locale.h>
+#include <libintl.h>
+
+#include <nettle/sha.h>
+
+static const struct option os[]= {
+  { "--state-dir", 1,0,'d' },
+  { "--command-id",1,0,'i' },
+  { 0 }
+};
+
+static const char *state_dir, *command_id, *command;
+static const char *lock_path, *cohort_path;
+
+static int cohort_fd, lock_fd;
+
+
+#define _(x) gettext(x)
+
+#define NOEINTR_TYPED(type,assign) do{                 \
+    while ((assign)==(type)-1 && errno==EINTR) {}      \
+  }while(0)
+
+#define NOEINTR(assign) \
+    NOEINTR_TYPED(int,(assign))
+
+#define CHECKED(value,what) do{                        \
+    NOEINTR(r= (value));                       \
+    if (r<0) diee((what));                     \
+  }while(0)
+
+
+static void badusage(void) {
+  fputs(_("usage: watershed [<options>] <command>...\n"
+         "options: -d|--state-dir <directory>  -i|--command-id <id>\n"
+         "see /usr/share/doc/chiark-utils-bin/watershed.txt\n"),
+         stderr);
+  exit(127);
+}
+static void die(const char *m) {
+  fprintf(stderr,_("watershed: error: %s\n"), m);
+  exit(127);
+}
+static void diee(const char *m) {
+  fprintf(stderr,_("watershed: error: %s failed: %s\n"), m, strerror(errno));
+  exit(127);
+}
+static void dieep(const char *action, const char *path) {
+  fprintf(stderr,_("watershed: error: could not %s `%s': %s\n"),
+         action, path, strerror(errno));
+  exit(127);
+}
+
+static char *m_vasprintf(const char *fmt, va_list al) {
+  char *s;  int r;
+  r= vasprintf(&s,fmt,al);
+  if (r==-1) diee("vasprintf");
+  return s;
+}
+static char *m_asprintf(const char *fmt, ...) {
+  char *s;  va_list al;
+  va_start(al,fmt); s= m_vasprintf(fmt,al); va_end(al);
+  return s;
+}
+
+static void parse_args(int argc, char *const *argv) {
+  int o;
+  for (;;) {
+    o= getopt_long(argc, argv, "+d:i:", os,0);
+    if (o==-1) break;
+    switch (o) {
+    case 'd': state_dir= optarg; break;
+    case 'i': command_id= optarg; break;
+    default: badusage();
+    }
+  }
+  command= argv[optind];
+  if (!command) badusage();
+  if (!state_dir) state_dir= getenv("WATERSHED_STATEDIR");
+  if (!state_dir) {
+    uid_t u= geteuid();  if (u==(uid_t)-1) diee("getuid");
+    if (u) {
+      const char *home= getenv("HOME");
+      if (!home) die(_("HOME not set, no --state-dir option"
+                      " supplied, not root"));
+      state_dir= m_asprintf("%s/.watershed", home);
+    } else {
+      state_dir= "/var/run/watershed";
+    }
+  }
+  if (!command_id) {
+    char *const *ap;
+    struct sha256_ctx sc;
+    unsigned char dbuf[SHA256_DIGEST_SIZE], *p;
+    char *construct, *q;
+    int i, c;
+    
+    sha256_init(&sc);
+    for (ap= argv+optind; *ap; ap++) sha256_update(&sc,strlen(*ap)+1,*ap);
+    sha256_digest(&sc,sizeof(dbuf),dbuf);
+
+    construct= m_asprintf("%*s#%.32s", (int)sizeof(dbuf)*2,"", command);
+    for (i=sizeof(dbuf), p=dbuf, q=construct; i; i--,p++,q+=2)
+      sprintf(q,"%02x",*p);
+    *q++= '=';
+    while ((c=*q++)) {
+      if (!(c=='-' || c=='+' || c=='_' || isalnum((unsigned char)c)))
+       q[-1]= '?';
+    }
+    command_id= construct;
+  }
+
+  lock_path= m_asprintf("%s/%s.lock", state_dir, command_id);
+  cohort_path= m_asprintf("%s/%s.cohort", state_dir, command_id);
+}
+
+static void acquire_lock(void) {
+  struct stat current_stab, our_stab;
+  struct flock fl;
+  int r;
+
+  for (;;) {
+    NOEINTR( lock_fd= open(lock_path, O_CREAT|O_RDWR, 0600) );
+    if (lock_fd<0) diee("open lock");
+
+    memset(&fl,0,sizeof(fl));
+    fl.l_type= F_WRLCK;
+    fl.l_whence= SEEK_SET;
+    CHECKED( fcntl(lock_fd, F_SETLKW, &fl), "acquire lock" );
+    
+    CHECKED( fstat(lock_fd, &our_stab), "fstat our lock");
+
+    NOEINTR( r= stat(lock_path, &current_stab) );
+    if (!r &&
+       our_stab.st_ino == current_stab.st_ino &&
+       our_stab.st_dev == current_stab.st_dev) break;
+    if (r && errno!=ENOENT) diee("fstat current lock");
+    
+    close(lock_fd);
+  }
+}
+static void release_lock(void) {
+  int r;
+  CHECKED( unlink(lock_path), "unlink lock");
+}
+
+static void report(int status) {
+  int v;
+  if (WIFEXITED(status)) {
+    v= WEXITSTATUS(status);
+    if (v) fprintf(stderr,_("watershed: `%s' failed with error exit status %d"
+                           " (in another invocation)\n"), command, v);
+    exit(status);
+  }
+  if (WIFSIGNALED(status)) {
+    v= WTERMSIG(status); assert(v);
+    if (v == SIGPIPE) raise(v);
+    fprintf(stderr,
+           WCOREDUMP(status)
+           ? _("watershed: `%s' died due to fatal signal %s (core dumped)\n")
+           : _("watershed: `%s' died due to fatal signal %s\n"),
+           command, strsignal(v));
+  } else {
+    fprintf(stderr, _("watershed: `%s' failed with"
+                     " crazy wait status 0x%x\n"), command, status);
+  }
+  exit(127);
+}
+
+int main(int argc, char *const *argv) {
+  int status, r, dir_created=0, l;
+  unsigned char *p;
+  struct stat cohort_stab;
+  pid_t c, c2;
+  
+  setlocale(LC_MESSAGES,""); /* not LC_ALL, see use of isalnum below */
+  parse_args(argc,argv);
+
+  for (;;) {
+    NOEINTR( cohort_fd= open(cohort_path, O_CREAT|O_RDWR, 0644) );
+    if (cohort_fd>=0) break;
+    if (errno!=ENOENT) dieep(_("open/create cohort state file"), cohort_path);
+    if (dir_created++) die("open cohort state file still ENOENT after mkdir");
+    NOEINTR( r= mkdir(state_dir,0700) );
+    if (r && errno!=EEXIST) dieep(_("create state directory"), state_dir);
+  }
+
+  acquire_lock();
+
+  CHECKED( fstat(cohort_fd, &cohort_stab), "fstat our cohort");
+  if (cohort_stab.st_size) {
+    if (cohort_stab.st_size < sizeof(status))
+      die(_("cohort status file too short (disk full?)"));
+    else if (cohort_stab.st_size != sizeof(status))
+      die("cohort status file too long");
+    NOEINTR( r= read(cohort_fd,&status,sizeof(status)) );
+    if (r==-1) diee("read cohort");
+    if (r!=sizeof(status)) die("cohort file read wrong length");
+    release_lock(); report(status);
+  }
+
+  if (cohort_stab.st_nlink)
+    CHECKED( unlink(cohort_path), "unlink our cohort");
+
+  NOEINTR_TYPED(pid_t, c= fork() );  if (c==(pid_t)-1) diee("fork");
+  if (!c) {
+    close(cohort_fd); close(lock_fd);
+    execvp(command, argv+optind);
+    fprintf(stderr,_("watershed: failed to execute `%s': %s\n"),
+           command, strerror(errno));
+    exit(127);
+  }
+
+  NOEINTR( c2= waitpid(c, &status, 0) );
+  if (c2==(pid_t)-1) diee("waitpid");
+  if (c2!=c) die("waitpid gave wrong pid");
+
+  for (l=sizeof(status), p=(void*)&status; l>0; l-=r, p+=r)
+    CHECKED( write(cohort_fd,p,l), _("write result status"));
+
+  release_lock();
+  if (!WIFEXITED(status)) report(status);
+  exit(WEXITSTATUS(status));
+}
diff --git a/cprogs/with-lock-ex.1 b/cprogs/with-lock-ex.1
new file mode 100644 (file)
index 0000000..941d1d9
--- /dev/null
@@ -0,0 +1,147 @@
+.TH WITH-LOCK-EX "1" "July 2003" "Debian" "Chiark-utils-bin"
+.SH NAME
+with-lock-ex \- file locker
+.SH SYNOPSIS
+.B with-lock-ex
+.BR \-w \||\| \-q \||\| \-f
+.I lockfile command 
+.IR args \ \|.\|.\|.
+.br
+.SH DESCRIPTION
+with-lock-ex will open and lock the lockfile for writing and then feed
+the remainder of its arguments to
+.BR exec (2);
+when that process terminates the fd will be closed and the file
+unlocked automatically by the kernel.
+.PP
+If the file does not exist it is created, with permissions
+.B rw
+for each user class for which the umask has
+.BR w .
+.SH OPTIONS
+.TP
+.B \-w
+Wait for the lock to be available.
+.TP
+.B \-f
+Fail (printing a message to stderr and exiting 255) if the lock cannot
+be acquired immediately because another process has it.
+.TP
+.B \-q
+Silently do nothing (ie, exit 0 instead of executing the specified
+process) if the lock cannot be acquired immediately because another
+process has it.
+.SH STALE LOCKS
+The locking protocol used does not suffer from stale locks.  If the
+lock cannot be acquired, one or more running processes must currently
+hold the lock; if the lock needs to be freed those processes should be
+killed.
+.PP
+Under no circumstances should `stale lock cleaner' cron jobs, or the
+like, be instituted.  In systems where a great many locks may exist,
+old lockfiles may be removed from cron but only if each lock is
+acquired before the lockfile is removed, for example with
+.IP
+.B with-lock-ex -q
+.I lockfile
+.B rm
+.I lockfile
+.SH DEADLOCKS
+There is no deadlock detection.  In a system with several locks, a
+lock hierarchy should be established, such that for every pair of
+locks
+.I A
+and
+.I B
+which a process might lock simultaneously, either
+.IR A > B
+or
+.IR B > A
+where the relation > is transitive and noncyclic.
+.PP
+Then, for any two locks
+.I X
+and
+.I Y
+with
+.IR X > Y
+it is forbidden to acquire
+.I X
+while holding
+.IR Y .
+Instead, acquire
+.I X
+first, or release
+.I Y
+before (re)acquiring
+.I X
+and
+.I Y
+in that order.
+.PP
+(There are more complicated ways of avoiding deadlocks, but a lock
+hierarchy is simple to understand and implement.  If it does not meet
+your needs, consult the literature.)
+.SH LOCKING PROTOCOL
+The locking protocol used by
+.B with-lock-ex
+is as follows:
+.PP
+The lock is held by a process (or group of processes) which holds an
+fcntl exclusive lock on the first byte of the plain file which has the
+specified name.  A holder of the lock (and only a holder of the lock)
+may delete the file or change the inode to which the name refers, and
+as soon as it does so it ceases to hold the lock.
+.PP
+Any process may create the file if it does not exist.  There is no
+need for the file to contain any actual data.  Indeed, actually using
+the file for data storage is strongly disrecommended, as this will
+foreclose most strategies for reliable update.  Use a separate
+lockfile instead.
+.PP
+Ability to obtain the lock corresponds to write permission on the file
+(and of course permission to create the  file, if it does not already
+exist).  However, processes with only read permission on the file can
+prevent the lock being acquired at all; therefore lockfiles should not
+usually be world-readable.
+.PP
+When a (group of) processes wishes to acquire the lock, it should open
+the file
+(with
+.BR O_CREAT )
+and lock it with
+.BR fcntl (2)
+.BR F_RWLCK ,
+operation
+.B F_SETLK
+or
+.BR F_SETLKW .
+If this succeeds it should fstat the file descriptor it has, and the
+file by its path.  If the device and inode match then the lock has
+been acquired and remains acquired until that group of processes
+changes which file the name refers to, deletes the file, or releases
+the fcntl lock.  If they do not then another process acquired the lock
+and deleted the file in the meantime; you must now close your
+filedescriptor and start again.
+.B with-lock-ex
+follows this specification.
+.PP
+Note that
+.BR flock (2)
+is a different kind of lock to
+.BR fcntl (2).
+.B with-lock-ex
+uses
+.BR fcntl .
+.SH AUTHOR
+This Manual page was written by Matthew Vernon <matthew@debian.org>
+and enhanced by Ian Jackson <ian@chiark.greenend.org.uk>, in 2003, but
+may be used by anyone.
+.SH COPYRIGHT
+with-lock-ex was written by Ian Jackson <ian@chiark.greenend.org.uk>
+in 1993, 1994, 1995, 1996, 1998, 1999. He has placed it in the public
+domain. 
+.SH "SEE ALSO"
+.BR fcntl (2),
+.BR flock (2),
+.BR chmod (2)
diff --git a/cprogs/with-lock-ex.c b/cprogs/with-lock-ex.c
new file mode 100644 (file)
index 0000000..1850d1f
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * File locker
+ *
+ * Usage: with-lock-ex -<mode> <lockfile> <command> <args>...
+ *
+ * modes are
+ *  w    wait for the lock
+ *  f    fail if the lock cannot be acquired
+ *  q    silently do nothing if the lock cannot be acquired
+ *
+ * with-lock-ex will open and lock the lockfile for writing and
+ * then feed the remainder of its arguments to exec(2); when
+ * that process terminates the fd will be closed and the file
+ * unlocked automatically by the kernel.
+ *
+ * If invoked as with-lock, behaves like with-lock-ex -f (for backward
+ * compatibility with an earlier version).
+ *
+ * This file written by me, Ian Jackson, in 1993, 1994, 1995, 1996,
+ * 1998, 1999.  I hereby place it in the public domain.
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+
+static const char *cmd;
+
+static void fail(const char *why) __attribute__((noreturn));
+
+static void fail(const char *why) {
+  fprintf(stderr,"with-lock-ex %s: %s: %s\n",cmd,why,strerror(errno));
+  exit(255);
+}
+
+int main(int argc, char **argv) {
+  int fd, mode, um;
+  struct stat stab, fstab;
+  long cloexec;
+  struct flock fl;
+  const char *p;
+
+  if (argc >= 3 && !strcmp((p= strrchr(argv[0],'/')) ? ++p : argv[0], "with-lock")) {
+    mode= 'f';
+  } else if (argc < 4 || argv[1][0] != '-' || argv[1][2] ||
+            ((mode= argv[1][1]) != 'w' && mode != 'q' && mode != 'f')) {
+    fputs("usage: with-lock-ex -w|-q|-f <lockfile> <command> <args>...\n"
+         "       with-lock             <lockfile> <command> <args>...\n",
+         stderr);
+    exit(255);
+  } else {
+    argv++; argc--;
+  }
+  cmd= argv[2];
+  um= umask(0777); if (um==-1) fail("find umask");
+  if (umask(um)==-1) fail("reset umask");
+
+  for (;;) {
+  
+    fd= open(argv[1],O_RDWR|O_CREAT,0666&~(um|((um&0222)<<1)));
+    if (fd<0) fail(argv[1]);
+  
+    for (;;) {
+      fl.l_type= F_WRLCK;
+      fl.l_whence= SEEK_SET;
+      fl.l_start= 0;
+      fl.l_len= 1;
+      if (fcntl(fd, mode=='w' ? F_SETLKW : F_SETLK, &fl) != -1) break;
+      if (mode=='q' &&
+         (errno == EAGAIN || errno == EWOULDBLOCK || errno == EBUSY))
+       exit(0);
+      if (errno != EINTR) fail("could not acquire lock");
+    }
+
+    if (fstat(fd, &fstab)) fail("could not fstat lock fd");
+    if (stat(argv[1], &stab)) {
+      if (errno != ENOENT) fail("could not stat lockfile");
+    } else {
+      if (stab.st_dev == fstab.st_dev &&
+         stab.st_ino == fstab.st_ino) break;
+    }
+    close(fd);
+  }
+
+  cloexec= fcntl(fd, F_GETFD); if (cloexec==-1) fail("fcntl F_GETFD");
+  cloexec &= ~1;
+  if (fcntl(fd, F_SETFD, cloexec)==-1) fail("fcntl F_SETFD");
+
+  execvp(cmd,argv+2);
+  fail("unable to execute command");
+}
diff --git a/cprogs/wrbufcore.c b/cprogs/wrbufcore.c
new file mode 100644 (file)
index 0000000..65fc2be
--- /dev/null
@@ -0,0 +1,95 @@
+/*
+ * wrbufcore.c
+ *
+ * Core of algorithm for writing output to devices which don't like
+ * constant stopping and starting, such as tape drives.  This is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * writebuffer is part of chiark backup, a system for backing up GNU/Linux
+ * and other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+ * chiark backup is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+
+#include "rwbuffer.h"
+
+static size_t waitfill;
+
+int writing;
+
+void wrbufcore_startup(void) {
+  waitfill= (buffersize*3)/4;
+  writing=0;
+  maxselfd=0;
+}
+
+void fdsetset(int fd, fd_set *set) {
+  FD_SET(fd,set);
+  if (fd >= maxselfd) maxselfd= fd+1;
+}
+
+void wrbufcore_prepselect(int rdfd, int wrfd) {
+  FD_ZERO(&readfds);
+  if (rdfd>=0 && !seeneof && used+1<buffersize) fdsetset(rdfd,&readfds);
+  
+  FD_ZERO(&writefds);
+  if (writing) fdsetset(wrfd,&writefds);
+}
+
+void wrbufcore_afterselect(int rdfd, int wrfd) {
+  int r;
+    
+  if (FD_ISSET(wrfd,&writefds) &&
+      !(rdfd>=0 && FD_ISSET(rdfd,&readfds)) &&
+      !used) {
+    wrbuf_report("stopping");
+    writing= 0;
+    FD_CLR(wrfd,&writefds);
+  }
+
+  if (rdfd>=0 && FD_ISSET(rdfd,&readfds)) {
+    r= read(rdfd,rp,min(buffersize-1-used,buf+buffersize-rp));
+    if (!r) {
+      seeneof=1; writing=1;
+      wrbuf_report("seeneof");
+    } else if (r<0) {
+      if (!(errno == EAGAIN || errno == EINTR)) { perror("read"); exit(1); }
+    } else {
+      used+= r;
+      rp+= r;
+      if (rp == buf+buffersize) rp=buf;
+    }
+    if (used > waitfill) {
+      if (!writing) wrbuf_report("starting");
+      writing=1;
+    }
+  }
+
+  if (FD_ISSET(wrfd,&writefds) && used) {
+    r= write(wrfd,wp,min(used,buf+buffersize-wp));
+    if (r<=0) {
+      if (!(errno == EAGAIN || errno == EINTR)) { perror("write"); exit(1); }
+    } else {
+      used-= r;
+      wp+= r;
+      if (wp == buf+buffersize) wp=buf;
+    }
+  }
+}
diff --git a/cprogs/writebuffer.1 b/cprogs/writebuffer.1
new file mode 100644 (file)
index 0000000..b3f28ab
--- /dev/null
@@ -0,0 +1,29 @@
+.TH writebuffer 1 2001-10-21 chiark-backup
+.SH NAME
+writebuffer \- write output to devices which don't like constant stopping and starting
+.SH SYNOPSIS
+.B writebuffer
+.RB [ --mlock ]
+.RI [ size ]
+.SH DESCRIPTION
+.B writebuffer
+reads data on standard input and writes it to standard output.  It
+will buffer internally up to \fIsize\fR megabytes and will only write
+data when the buffer is at least 75% full or when there is no more
+input to fill the buffer.
+.PP
+\fIsize\fR may also be suffixed with
+.BR m ", " k ", or " b
+to indicate that it is in megabytes (2^20), kilobytes (2^10) or bytes.
+.PP
+It is intended for use in situations where many small writes are
+undesirable for performance reasons, e.g. tape drives.
+.SH OPTIONS
+.TP
+.B --mlock
+Calls
+.BR mlock (2)
+to lock the buffer into memory.
+.SH "SEE ALSO"
+.BR readbuffer (1),
+.BR mlock (2)
diff --git a/cprogs/writebuffer.c b/cprogs/writebuffer.c
new file mode 100644 (file)
index 0000000..da47cea
--- /dev/null
@@ -0,0 +1,46 @@
+/*
+ * writebuffer.c
+ *
+ * A program for writing output to devices which don't like constant
+ * stopping and starting, such as tape drives.  writebuffer is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *
+ * writebuffer is part of chiark backup, a system for backing up GNU/Linux
+ * and other UN*X-compatible machines, as used on chiark.greenend.org.uk.
+ * chiark backup is:
+ *  Copyright (C) 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ *  Copyright (C) 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ *
+ */
+
+#include "rwbuffer.h"
+
+const char *progname= "writebuffer";
+
+void wrbuf_report(const char *m) { }
+
+int main(int argc, const char *const *argv) {
+  startup(argv);
+  wrbufcore_startup();
+  while (!seeneof || used) {
+    wrbufcore_prepselect(0,1);
+    callselect();
+    wrbufcore_afterselect(0,1);
+  }
+  exit(0);
+}
diff --git a/cprogs/xbatmon-simple.c b/cprogs/xbatmon-simple.c
new file mode 100644 (file)
index 0000000..6ca3294
--- /dev/null
@@ -0,0 +1,856 @@
+/*
+ * display outputs, per line:
+ *
+ *   Remaining:         | Empty:       | Degraded:
+ *     blue     |  black       |  dimgrey      discharging
+ *     green    |  black       |  dimgrey      charging
+ *     cyan     |  black       |  dimgrey      charged
+ *     grey     |  black       |  dimgrey      charging&discharching!
+ *     lightgrey |  black      |  dimgrey      none of the above
+ *     blue     |  red         |  dimgrey      discharging - low!
+ *     green    |  red         |  dimgrey      charging - low
+ *     cyan     |  red         |  dimgrey      charged - low [1]
+ *     grey     |  red         |  dimgrey      charging&discharching, low [1]
+ *       ...  darkgreen  ...                   no batteries present
+ *       ...  yellow  ...                      error
+ *
+ * [1] battery must be quite badly degraded
+ */
+/*
+ * Copyright (C) 2004 Ian Jackson <ian@davenant.greenend.org.uk>
+ *
+ * This 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,
+ * or (at your option) any later version.
+ *
+ * This 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 file; if not, consult the Free Software
+ * Foundation's website at www.fsf.org, or the GNU Project website at
+ * www.gnu.org.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <stdint.h>
+#include <limits.h>
+#include <inttypes.h>
+
+#include <sys/poll.h>
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <X11/Xlib.h>
+#include <X11/Xutil.h>
+#include <X11/Xresource.h>
+
+#define TOP      60
+#define BOTTOM 3600
+
+#define TIMEOUT         5000 /* milliseconds */
+#define TIMEOUT_ONERROR 3333 /* milliseconds */
+
+static const char program_name[]= "xbatmon-simple";
+static int debug=-1, alarmlevel;
+
+/*---------- general utility stuff and declarations ----------*/
+
+static void fail(const char *m) {
+  fprintf(stderr,"error: %s\n", m);
+  exit(-1);
+}
+static void badusage(void) { fail("bad usage"); }
+
+typedef uint64_t value;
+#define VAL_NOTFOUND (~(value)0)
+
+typedef struct fileinfo fileinfo;
+typedef int parser(const fileinfo*);
+
+static parser parse_uevent;
+
+struct fileinfo {
+  const char *filename;
+  parser *parse;
+  const void *extra;
+};
+
+/*---------- structure of and results from /sys/class/power/... ----------*/
+/* variables this_... are the results from readbattery();
+ * if readbattery() succeeds the appropriate ones are all valid
+ * and not VAL_NOTFOUND
+ */
+
+typedef struct batinfo_field {
+  const char *label;
+  value *valuep;
+  const char *enumarray[10];
+} batinfo_field;
+
+#define BAT_QTYS(_, _ec, EC_, PC_)                             \
+  _(design_capacity##_ec,    BATTERY,  EC_##FULL_DESIGN )      \
+  _(last_full_capacity##_ec, BATTERY,  EC_##FULL        )      \
+  _(remaining_capacity##_ec, BATTERY,  EC_##NOW         )      \
+  _(present_rate##_ec,       BATTERY,  PC_##NOW         )
+ /* ENERGY [mWh]; POWER [mW]; CHARGE [uAh]; CURRENT [uA] */
+
+#define UEVENT_ESSENTIAL_QUANTITY_FIELDS(_)                    \
+  _(present,                 BATTERY,  PRESENT /* bool */ )    \
+  _(online,                  MAINS,    ONLINE  /* bool */ )
+
+#define UEVENT_FUNKY_QUANTITY_FIELDS(_)                \
+  BAT_QTYS(_,_energy,ENERGY_,POWER_)           \
+  BAT_QTYS(_,_charge,CHARGE_,CURRENT_)
+
+#define UEVENT_OPTIONAL_QUANTITY_FIELDS(_)                     \
+  _(voltage,                 BATTERY,  VOLTAGE_NOW /* uV */ )
+
+#define UEVENT_ENUM_FIELDS(_)                                          \
+  _(state,   BATTERY,  STATUS,  "Discharging","Charging","Full","Unknown" ) \
+  _(type,    BOTH,     TYPE,    "Mains",       "Battery"              )
+
+#define CHGST_DISCHARGING 0 /* Reflects order in _(state,...) above     */
+#define CHGST_CHARGING    1 /* Also, much code assumes exactly          */
+#define CHGST_CHARGED     2 /* these three possible states.             */
+#define CHGST_UNKNOWN     3 /* these three possible states.             */
+#define CHGST_ERROR       8 /* Except that this one is an extra bit.    */
+
+#define TYPE_MAINS        0 /* Reflects order in _(type,...) above        */
+#define TYPE_BATTERY      1 /* Also, much code assumes exactly these two  */
+#define TYPE_BOTH       100 /* Except this is a magic invalid value.      */
+
+#define SEPARATE_QUANTITY_FIELDS(_)            \
+  /* See commit ec6f5f0be800bc5f2a27046833dba04e0c67ffac for
+     the code needed to use this */
+
+
+#define ALL_DIRECT_VARS(_)                     \
+  UEVENT_ESSENTIAL_QUANTITY_FIELDS(_)          \
+  UEVENT_FUNKY_QUANTITY_FIELDS(_)              \
+  UEVENT_OPTIONAL_QUANTITY_FIELDS(_)           \
+  UEVENT_ENUM_FIELDS(_)                                \
+  SEPARATE_QUANTITY_FIELDS(_)
+
+#define ALL_VARS(_)                            \
+  ALL_DIRECT_VARS(_)                           \
+  BAT_QTYS(_,,,)
+
+#define ALL_NEEDED_FIELDS(_)                   \
+  UEVENT_ESSENTIAL_QUANTITY_FIELDS(_)          \
+  UEVENT_ENUM_FIELDS(_)                                \
+  SEPARATE_QUANTITY_FIELDS(_)
+
+#define ALL_PLAIN_ACCUMULATE_FIELDS(_)         \
+  UEVENT_ESSENTIAL_QUANTITY_FIELDS(_)          \
+  SEPARATE_QUANTITY_FIELDS(_)
+
+#define ALL_ACCUMULATE_FIELDS(_)               \
+  ALL_PLAIN_ACCUMULATE_FIELDS(_)               \
+  BAT_QTYS(_,,,)
+
+
+#define F_VAR(f,...) \
+static value this_##f;
+ALL_VARS(F_VAR)
+
+#define Q_FLD(f,t,l)        { "POWER_SUPPLY_" #l, &this_##f },
+#define E_FLD(f,t,l,vl...)  { "POWER_SUPPLY_" #l, &this_##f, { vl } },
+
+static const batinfo_field uevent_fields[]= {
+  UEVENT_ESSENTIAL_QUANTITY_FIELDS(Q_FLD)
+  UEVENT_FUNKY_QUANTITY_FIELDS(Q_FLD)
+  UEVENT_OPTIONAL_QUANTITY_FIELDS(Q_FLD)
+  UEVENT_ENUM_FIELDS(E_FLD)
+  { 0 }
+};
+
+#define S_FLD(f,t,fn,vl...)                                            \
+static const batinfo_field bif_##f = { 0, &this_##f, { vl } };
+  SEPARATE_QUANTITY_FIELDS(S_FLD)
+
+#define S_FILE(f,t,fn,vl...) { fn, parse_separate, &bif_##f },
+
+static const fileinfo files[]= {
+  { "uevent",  parse_uevent,  uevent_fields },
+  SEPARATE_QUANTITY_FIELDS(S_FILE)
+  { 0 }
+};
+
+/*---------- parsing of one thingx in /sys/class/power/... ----------*/
+
+/* variables private to the parser and its error handlers */
+static char batlinebuf[1000];
+static FILE *batfile;
+static const char *batdirname;
+static const char *batfilename;
+static const char *batlinevalue;
+
+static int batfailf(const char *why) {
+  if (batlinevalue) {
+    fprintf(stderr,"%s/%s: %s value `%s': %s\n",
+           batdirname,batfilename, batlinebuf,batlinevalue,why);
+  } else {
+    fprintf(stderr,"%s/%s: %s: `%s'\n",
+           batdirname,batfilename, why, batlinebuf);
+  }
+  return -1;
+}
+
+static int batfailc(const char *why) {
+  fprintf(stderr,"%s/%s: %s\n",
+         batdirname,batfilename, why);
+  return -1;
+}
+
+static int batfaile(const char *syscall, const char *target) {
+  fprintf(stderr,"%s: failed to %s %s: %s\n",
+         batdirname ? batdirname : "*", syscall, target, strerror(errno));
+  return -1;
+}
+
+static int chdir_base(void) {
+  int r;
+  
+  r= chdir("/sys/class/power_supply");
+  if (r) return batfaile("chdir","/sys/class/power_supply");
+
+  return 0;
+}
+
+static void tidybattery(void) {
+  if (batfile) { fclose(batfile); batfile=0; }
+}
+
+static int parse_value(const fileinfo *cfile, const batinfo_field *field) {
+  if (*field->valuep != VAL_NOTFOUND)
+    return batfailf("value specified multiple times");
+
+  if (!field->enumarray[0]) {
+
+    char *ep;
+    *field->valuep= strtoull(batlinevalue,&ep,10);
+    if (*ep)
+      batfailf("value number syntax incorrect");
+
+  } else {
+       
+    const char *const *enumsearch;
+    for (*field->valuep=0, enumsearch=field->enumarray;
+        *enumsearch && strcmp(*enumsearch,batlinevalue);
+        (*field->valuep)++, enumsearch++);
+    if (!*enumsearch)
+      batfailf("unknown enum value");
+
+  }
+  return 0;
+}
+
+static int parse_uevent(const fileinfo *cfile) {
+  char *equals= strchr(batlinebuf,'=');
+  if (!equals)
+    return batfailf("line without a equals");
+  *equals= 0;
+  batlinevalue = equals+1;
+
+  const batinfo_field *field;
+  for (field=cfile->extra; field->label; field++) {
+    if (!strcmp(field->label,batlinebuf))
+      goto found;
+  }
+  return 0;
+
+ found:
+  return parse_value(cfile, field);
+}
+
+static int readbattery(void) { /* 0=>ok, -1=>couldn't */
+  
+  const fileinfo *cfile;
+  char *sr;
+  int r, l;
+  
+  r= chdir_base();
+  if (r) return r;
+
+  r= chdir(batdirname);
+  if (r) return batfaile("chdir",batdirname);
+
+#define V_NOTFOUND(f,...) \
+  this_##f = VAL_NOTFOUND;
+ALL_VARS(V_NOTFOUND)
+
+  for (cfile=files;
+       (batfilename= cfile->filename);
+       cfile++) {
+    batfile= fopen(batfilename,"r");
+    if (!batfile) {
+      if (errno == ENOENT) continue;
+      return batfaile("open",batfilename);
+    }
+
+    for (;;) {
+      batlinevalue= 0;
+      
+      sr= fgets(batlinebuf,sizeof(batlinebuf),batfile);
+      if (ferror(batfile)) return batfaile("read",batfilename);
+      if (!sr && feof(batfile)) break;
+      l= strlen(batlinebuf);
+      assert(l>0);
+      if (batlinebuf[l-1] != '\n')
+       return batfailf("line too long");
+      batlinebuf[l-1]= 0;
+
+      if (cfile->parse(cfile))
+       return -1;
+    }
+
+    fclose(batfile);
+    batfile= 0;
+  }
+
+  if (debug) {
+    printf("%s:\n",batdirname);
+#define V_PRINT(f,...)                                 \
+    printf(" %-30s = %20"PRId64"\n", #f, (int64_t)this_##f);
+ALL_DIRECT_VARS(V_PRINT)
+  }
+
+  int needsfields_MAINS   = this_type == TYPE_MAINS;
+  int needsfields_BATTERY = this_type == TYPE_BATTERY;
+  int needsfields_BOTH    = 1;
+
+  int missing = 0;
+
+#define V_NEEDED(f,t,...)                              \
+  if (needsfields_##t && this_##f == VAL_NOTFOUND) {   \
+    fprintf(stderr,"%s: %s: not found\n",              \
+           batdirname, #f);                            \
+    missing++;                                         \
+  }
+ALL_NEEDED_FIELDS(V_NEEDED)
+
+  if (missing) return -1;
+
+  return 0;
+}   
+
+/*---------- data collection and analysis ----------*/
+
+/* These next three variables are the results of the charging state */
+static unsigned charging_mask; /* 1u<<CHGST_* | ... */
+static double nondegraded_norm, fill_norm, ratepersec_norm;
+static int alarmed;
+
+#define Q_VAR(f,t,...) \
+static double total_##f;
+  ALL_ACCUMULATE_FIELDS(Q_VAR)
+
+static void acquiredata(void) {
+  DIR *di = 0;
+  struct dirent *de;
+  int r;
+  
+  charging_mask= 0;
+  alarmed = 0;
+
+  if (debug) printf("\n");
+
+#define Q_ZERO(f,t,...) \
+  total_##f= 0;
+ALL_ACCUMULATE_FIELDS(Q_ZERO)
+
+  r = chdir_base();
+  if (r) goto bad;
+
+  di= opendir(".");  if (!di) { batfaile("opendir","battery"); goto bad; }
+  while ((de= readdir(di))) {
+    if (de->d_name[0]==0 || de->d_name[0]=='.') continue;
+
+    batdirname= de->d_name;
+    r= readbattery();
+    tidybattery();
+
+    if (r) {
+    bad:
+      charging_mask |= (1u << CHGST_ERROR);
+      break;
+    }
+
+    if (this_type == TYPE_BATTERY) {
+      if (!this_present)
+       continue;
+
+      charging_mask |= 1u << this_state;
+
+#define QTY_SUPPLIED(f,...)   this_##f != VAL_NOTFOUND &&
+#define QTY_USE_ENERGY(f,...) this_##f = this_##f##_energy;
+#define QTY_USE_CHARGE(f,...) this_##f = this_##f##_charge;
+
+      double funky_multiplier;
+      if (BAT_QTYS(QTY_SUPPLIED,_energy,,) 1) {
+       if (debug) printf(" using energy\n");
+       BAT_QTYS(QTY_USE_ENERGY,,,);
+       funky_multiplier = 1.0;
+      } else if (BAT_QTYS(QTY_SUPPLIED,_charge,,)
+                this_voltage != VAL_NOTFOUND) {
+       if (debug) printf(" using charge\n");
+       BAT_QTYS(QTY_USE_CHARGE,,,);
+       funky_multiplier = this_voltage * 1e-6;
+      } else {
+       batfailc("neither complete set of energy nor charge");
+       continue;
+      }
+      if (this_state == CHGST_DISCHARGING)
+       /* negate it */
+       total_present_rate -= 2.0 * this_present_rate * funky_multiplier;
+
+#define Q_ACCUMULATE_FUNKY(f,...)                      \
+      total_##f += this_##f * funky_multiplier;
+BAT_QTYS(Q_ACCUMULATE_FUNKY,,,)
+    }
+
+#define Q_ACCUMULATE_PLAIN(f,t,...)                    \
+    if (this_type == TYPE_##t)                 \
+      total_##f += this_##f;
+ALL_PLAIN_ACCUMULATE_FIELDS(Q_ACCUMULATE_PLAIN)
+
+      
+  }
+  if (di) closedir(di);
+
+  if (debug) {
+    printf("TOTAL:\n");
+    printf(" %-30s = %#20x\n", "mask", charging_mask);
+#define T_PRINT(f,...)                                 \
+    printf(" %-30s = %20.6f\n", #f, total_##f);
+BAT_QTYS(T_PRINT,,,)
+ALL_PLAIN_ACCUMULATE_FIELDS(T_PRINT)
+  }
+
+  if ((charging_mask & (1u<<CHGST_DISCHARGING)) &&
+      !total_online/*mains*/) {
+    double time_remaining =
+      -total_remaining_capacity * 3600.0 / total_present_rate;
+    if (debug) printf(" %-30s = %20.6f\n", "time remaining", time_remaining);
+    if (time_remaining < alarmlevel)
+      alarmed = 1;
+  }
+
+  if (total_design_capacity < 0.5)
+    total_design_capacity= 1.0;
+
+  if (total_last_full_capacity < total_remaining_capacity)
+    total_last_full_capacity= total_remaining_capacity;
+  if (total_design_capacity < total_last_full_capacity)
+    total_design_capacity= total_last_full_capacity;
+
+  nondegraded_norm= total_last_full_capacity / total_design_capacity;
+  fill_norm= total_remaining_capacity / total_design_capacity;
+  ratepersec_norm=  total_present_rate
+    / (3600.0 * total_design_capacity);
+}
+
+static void initacquire(void) {
+}  
+
+/*---------- argument parsing ----------*/
+
+#define COLOURS                                        \
+  C(blue,           discharging)               \
+  C(green,         charging)                   \
+  C(cyan,           charged)                   \
+  C(lightgrey,      notcharging)               \
+  C(grey,           confusing)                 \
+  C(black,          normal)                    \
+  C(red,            low)                       \
+  C(dimgrey,        degraded)                  \
+  C(darkgreen,      absent)                    \
+  C(yellow,         error)                     \
+  C(white,          equilibrium)               \
+  GC(remain)                                   \
+  GC(white)                                    \
+  GC(empty)
+
+static XrmDatabase xrm;
+static Display *disp;
+static int screen;
+static const char *parentwindow;
+
+static const char defaultresources[]=
+#define GC(g)
+#define C(c,u)                                 \
+  "*" #u "Color: " #c "\n"
+  COLOURS
+#undef GC
+#undef C
+  ;
+
+#define S(s) ((char*)(s))
+static const XrmOptionDescRec optiontable[]= {
+  { S("-debug"),        S("*debug"),        XrmoptionIsArg },
+  { S("-warningTime"),  S("*warningTime"),  XrmoptionSepArg },
+  { S("-display"),      S("*display"),      XrmoptionSepArg },
+  { S("-geometry"),     S("*geometry"),     XrmoptionSepArg },
+  { S("-into"),         S("*parentWindow"), XrmoptionSepArg },
+  { S("-iconic"),       S("*iconic"),       XrmoptionIsArg },
+  { S("-withdrawn"),    S("*withdrawn"),    XrmoptionIsArg },
+#define GC(g)
+#define C(c,u)                                                 \
+  { S("-" #u "Color"),  S("*" #u "Color"),  XrmoptionSepArg }, \
+  { S("-" #u "Colour"), S("*" #u "Color"),  XrmoptionSepArg },
+  COLOURS
+#undef GC
+#undef C
+};
+
+static const char *getresource(const char *want) {
+  char name_buf[256], class_buf[256];
+  XrmValue val;
+  char *rep_type_dummy;
+  int r;
+
+  assert(strlen(want) < 128);
+
+  sprintf(name_buf,"xbatmon-simple.%s",want);
+  sprintf(class_buf,"Xbatmon-Simple.%s",want);
+  
+  r= XrmGetResource(xrm, name_buf,class_buf, &rep_type_dummy, &val);
+  if (r) return val.addr;
+
+  sprintf(name_buf,"xacpi-simple.%s",want);
+  sprintf(class_buf,"Xacpi-Simple.%s",want);
+  
+  r= XrmGetResource(xrm, name_buf,class_buf, &rep_type_dummy, &val);
+  if (r) return val.addr;
+  
+  return 0;
+}
+
+static int getresource_bool(const char *want, int def, int *cache) {
+  /* *cache should be initialised to -1 and will be set to !!value
+   * alternatively cache==0 is allowed */
+
+  if (cache && *cache >= 0) return *cache;
+
+  const char *str= getresource(want);
+  int result = def;
+  if (str && str[0]) {
+    char *ep;
+    long l= strtol(str,&ep,0);
+    if (!*ep) {
+      result = l > 0;
+    } else {
+      switch (str[0]) {
+      case 't': case 'T': case 'y': case 'Y':         result= 1;  break;
+      case 'f': case 'F': case 'n': case 'N':         result= 0;  break;
+      case '-': /* option name from XrmoptionIsArg */ result= 1;  break;
+      }
+    }
+  }
+
+  if (cache) *cache= result;
+  return result;
+}
+
+static void more_resources(const char *str, const char *why) {
+  XrmDatabase more;
+
+  if (!str) return;
+
+  more= XrmGetStringDatabase((char*)str);
+  if (!more) fail(why);
+  XrmCombineDatabase(more,&xrm,0);
+}
+
+static void parseargs(int argc, char **argv) {
+  Screen *screenscreen;
+  
+  XrmInitialize();
+
+  XrmParseCommand(&xrm, (XrmOptionDescRec*)optiontable,
+                 sizeof(optiontable)/sizeof(*optiontable),
+                 program_name, &argc, argv);
+
+  if (argc>1) badusage();
+
+  getresource_bool("debug",0,&debug);
+
+  const char *alarmlevel_string= getresource("alarmLevel");
+  alarmlevel = alarmlevel_string ? atoi(alarmlevel_string) : 300;
+
+  parentwindow = getresource("parentWindow");
+
+  disp= XOpenDisplay(getresource("display"));
+  if (!disp) fail("could not open display");
+
+  screen= DefaultScreen(disp);
+
+  screenscreen= ScreenOfDisplay(disp,screen);
+  if (!screenscreen) fail("screenofdisplay");
+  more_resources(XScreenResourceString(screenscreen), "screen resources");
+  more_resources(XResourceManagerString(disp), "display resources");
+  more_resources(defaultresources, "default resources");
+} 
+
+/*---------- display ----------*/
+
+static Window win;
+static int width, height;
+static Colormap cmap;
+static unsigned long lastbackground;
+
+typedef struct {
+  GC gc;
+  unsigned long lastfg;
+} Gcstate;
+
+#define C(c,u) static unsigned long pix_##u;
+#define GC(g) static Gcstate gc_##g;
+  COLOURS
+#undef C
+#undef GC
+
+static void refresh(void);
+
+#define CHGMASK_CHG_DIS ((1u<<CHGST_CHARGING) | (1u<<CHGST_DISCHARGING))
+
+static void failr(const char *m, int r) {
+  fprintf(stderr,"error: %s (code %d)\n", m, r);
+  exit(-1);
+}
+
+static void setbackground(unsigned long newbg) {
+  int r;
+  
+  if (newbg == lastbackground) return;
+  r= XSetWindowBackground(disp,win,newbg);
+  if (!r) fail("XSetWindowBackground");
+  lastbackground= newbg;
+}
+
+static void setforeground(Gcstate *g, unsigned long px) {
+  XGCValues gcv;
+  int r;
+  
+  if (g->lastfg == px) return;
+  
+  memset(&gcv,0,sizeof(gcv));
+  g->lastfg= gcv.foreground= px;
+  r= XChangeGC(disp,g->gc,GCForeground,&gcv);
+  if (!r) fail("XChangeGC");
+}
+
+static void show_solid(unsigned long px) {
+  setbackground(px);
+  XClearWindow(disp,win);
+}
+
+static void show(void) {
+  double elap, then;
+  int i, leftmost_lit, leftmost_nondeg, beyond, first_beyond;
+
+  if (!charging_mask)
+    return show_solid(pix_absent);
+
+  if (charging_mask & (1u << CHGST_ERROR))
+    return show_solid(pix_error);
+
+  setbackground(pix_degraded);
+  XClearWindow(disp,win);
+  
+  setforeground(&gc_remain,
+               !(charging_mask & CHGMASK_CHG_DIS) ?
+               (~charging_mask & (1u << CHGST_CHARGED) ?
+                pix_notcharging : pix_charged) :
+               !(~charging_mask & CHGMASK_CHG_DIS) ? pix_confusing :
+               charging_mask & (1u<<CHGST_CHARGING)
+               ? pix_charging : pix_discharging);
+               
+  setforeground(&gc_empty, alarmed ? pix_low : pix_normal);
+
+  for (i=0, first_beyond=1; i<height; i++) {
+    elap= !i ? 0 :
+      height==2 ? BOTTOM :
+      TOP * exp( (double)i / (height-2) * log( (double)BOTTOM/TOP ) );
+    
+    then= fill_norm + ratepersec_norm * elap;
+
+    beyond=
+      ((charging_mask & (1u<<CHGST_DISCHARGING) && then <= 0.0) ||
+       (charging_mask & (1u<<CHGST_CHARGING) && then>=nondegraded_norm));
+
+    if (then <= 0.0) then= 0.0;
+    else if (then >= nondegraded_norm) then= nondegraded_norm;
+
+    leftmost_lit= width * then;
+    leftmost_nondeg= width * nondegraded_norm;
+
+    if (beyond && first_beyond) {
+      XDrawLine(disp, win, gc_white.gc, 0,i, leftmost_nondeg,i);
+      first_beyond= 0;
+    } else {
+      if (leftmost_lit < leftmost_nondeg)
+       XDrawLine(disp, win, gc_empty.gc,
+                 leftmost_lit,i, leftmost_nondeg,i);
+      if (leftmost_lit >= 0)
+       XDrawLine(disp, win, gc_remain.gc, 0,i, leftmost_lit,i);
+    }
+  }
+}
+
+static void initgc(Gcstate *gc_r) {
+  XGCValues gcv;
+
+  memset(&gcv,0,sizeof(gcv));
+  gcv.function= GXcopy;
+  gcv.line_width= 1;
+  gc_r->lastfg= gcv.foreground= pix_equilibrium;
+  gc_r->gc= XCreateGC(disp,win, GCFunction|GCLineWidth|GCForeground, &gcv);
+}
+
+static void colour(unsigned long *pix_r, const char *whichcolour) {
+  XColor xc;
+  const char *name;
+  Status st;
+
+  name= getresource(whichcolour);
+  if (!name) fail("get colour resource");
+  
+  st= XAllocNamedColor(disp,cmap,name,&xc,&xc);
+  if (!st) fail(name);
+  
+  *pix_r= xc.pixel;
+}
+
+static void initgraphics(int argc, char **argv) {
+  int xwmgr, r;
+  const char *geom_string;
+  XSizeHints *normal_hints;
+  XWMHints *wm_hints;
+  XClassHint *class_hint;
+  int pos_x, pos_y, gravity;
+  char *program_name_silly;
+  
+  program_name_silly= (char*)program_name;
+
+  normal_hints= XAllocSizeHints();
+  wm_hints= XAllocWMHints();
+  class_hint= XAllocClassHint();
+
+  if (!normal_hints || !wm_hints || !class_hint)
+    fail("could not alloc hint(s)");
+
+  geom_string= getresource("geometry");
+
+  xwmgr= XWMGeometry(disp,screen, geom_string,"128x32", 0,
+                normal_hints,
+                &pos_x, &pos_y,
+                &width, &height,
+                &gravity);
+
+  unsigned long parentwindowid;
+  if (parentwindow)
+    parentwindowid = strtoul(parentwindow,0,0);
+  else
+    parentwindowid = DefaultRootWindow(disp);
+
+  win= XCreateSimpleWindow(disp,parentwindowid,
+                          pos_x,pos_y,width,height,0,0,0);
+  cmap= DefaultColormap(disp,screen);
+  
+#define C(c,u) colour(&pix_##u, #u "Color");
+#define GC(g) initgc(&gc_##g);
+  COLOURS
+#undef C
+#undef GC
+
+  r= XSetWindowBackground(disp,win,pix_degraded);
+  if (!r) fail("init set background");
+  lastbackground= pix_degraded;
+
+  normal_hints->flags= PWinGravity;
+  normal_hints->win_gravity= gravity;
+  normal_hints->x= pos_x;
+  normal_hints->y= pos_y;
+  normal_hints->width= width;
+  normal_hints->height= height;
+  if ((xwmgr & XValue) || (xwmgr & YValue))
+    normal_hints->flags |= USPosition;
+
+  wm_hints->flags= InputHint;
+  wm_hints->input= False;
+  wm_hints->initial_state=
+    (getresource_bool("withdrawn",0,0) ? WithdrawnState :
+     getresource_bool("iconic",0,0) ? IconicState
+     : NormalState);
+
+  class_hint->res_name= program_name_silly;
+  class_hint->res_class= program_name_silly;
+
+  XmbSetWMProperties(disp,win, program_name,program_name,
+                    argv,argc, normal_hints, wm_hints, class_hint);
+
+  XSelectInput(disp,win, ExposureMask|StructureNotifyMask);
+  XMapWindow(disp,win);
+}
+static void refresh(void) {
+  acquiredata();
+  show();
+}
+
+static void newgeometry(void) {
+  int dummy;
+  Window dummyw;
+  
+  XGetGeometry(disp,win, &dummyw,&dummy,&dummy, &width,&height, &dummy,&dummy);
+}
+
+static void eventloop(void) {
+  XEvent ev;
+  struct pollfd pfd;
+  int r, timeout;
+  
+  newgeometry();
+  refresh();
+
+  for (;;) {
+    XFlush(disp);
+
+    pfd.fd= ConnectionNumber(disp);
+    pfd.events= POLLIN|POLLERR;
+
+    timeout= !(charging_mask & (1u << CHGST_ERROR)) ? TIMEOUT : TIMEOUT_ONERROR;
+    r= poll(&pfd,1,timeout);
+    if (r==-1 && errno!=EINTR) failr("poll",errno);
+
+    while (XPending(disp)) {
+      XNextEvent(disp,&ev);
+      if (ev.type == ConfigureNotify) {
+       XConfigureEvent *ce= (void*)&ev;
+       width= ce->width;
+       height= ce->height;
+      }
+    }
+    refresh();
+  }
+}
+
+int main(int argc, char **argv) {
+  parseargs(argc,argv);
+  initacquire();
+  initgraphics(argc,argv);
+  eventloop();
+  return 0;
+}
diff --git a/debian/changelog b/debian/changelog
new file mode 100644 (file)
index 0000000..02b4972
--- /dev/null
@@ -0,0 +1,500 @@
+chiark-utils (4.2.0) unstable; urgency=low
+
+  * Rename `xacpi-simple' to `xbatmon-simple':
+    - rename source file
+    - change program name in Makefiles, .gitignore, rules, etc.
+    - change program's idea of its own name for usage message
+    - look for X resources under both old ane new names
+    - provide a compatibility symlink (using dh_link)
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Sun, 10 Jun 2012 21:40:32 +0100
+
+chiark-utils (4.1.32) unstable; urgency=medium
+
+  xacpi-simple improvements:
+  * Better parsing of boolean -debug option.
+  * Support -into, -iconic and -withdrawn options.
+
+  Packaging fix:
+  * debian/rules clean removes various sv-* and dh log files.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Thu, 07 Jun 2012 19:22:46 +0100
+
+chiark-utils (4.1.31) unstable; urgency=medium
+
+  Bugfixes to programs:
+  * xacpi-simple updated:
+    - Now works with recent kernels by reading /sys/class/power_supply
+      (so is now misnamed, since that may or may not come from acpi).
+      Closes: #635242.
+    - Copes both with batteries which report charge/current and ones which
+      report energy/power.
+    - Copes with batteries which are not full, charging or discharging
+      (eg, if the system is on a/c but battery charging is suppressed).
+    - Shows red when time remaining is less than 5 minutes (configurable)
+      rather than trying to use "alarm" value from /sys/class/power_supply
+      (which is not very predictable or reliable).
+    - Option -debug produces some debugging output.
+    - In the code, rename pix_... variables to be named after the meaning
+      rather than the default colour.
+  * chiark-backup's /etc/chiark-backup/snap/nosnap is now more careful
+    about rm vs. rmdir of its link/mountpoint/whatever.
+
+  Documentation and description improvements:
+  * watershed now installs its head doc comment in
+    /usr/share/doc/chiark-utils-bin/watershed.txt.
+    This is apropos of #659989 but is of course not a proper fix.
+  * Mention rcopy-repeatedly in the Description.
+
+  User-visible packaging improvements:
+  * chiark-backup Suggests chiark-utils-bin, not the nonexistent
+    chiark-cprogs (for `summer').
+
+  Other packaging and source code improvements:
+  * Provide build-arch and build-indep debian/rules targets.
+  * Fix the type of a callback function passed to scandir, to expect
+    struct dirent**'s rather than void*'s.
+  * Add -Wno-pointer-sign to gcc warning options.
+  * Add ${misc:Depends} to Depends: lines.  Causes no change to the .debs.
+  * Switch to git.  Move .cvsignores to .gitignore, etc.
+  * Update my email address.
+
+ -- Ian Jackson <ijackson@chiark.greenend.org.uk>  Wed, 06 Jun 2012 02:30:50 +0100
+
+chiark-utils (4.1.30) unstable; urgency=low
+
+  * rcopy-repeatedly: new utility
+  * cvs-repomove: work with Solaris's shoddy sed.  (Closes: #497670)
+  * backup/snap-drop: honour argument specifying vardir.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Wed,  8 Oct 2008 21:42:17 +0100
+
+chiark-utils (4.1.29) unstable; urgency=low
+
+  * backup-snaprsync: pass $vardir as argument to snap-drop
+  * backup-snaprsync: add a missing x prefix to an ssh invocation
+  * chiark-backup: really cope properly with nosnap removal
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Thu, 28 Aug 2008 20:02:47 +0100
+
+chiark-utils (4.1.28+nmu2) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Fix "file-in-etc-not-marked-as-conffile /etc/chiark-backup/snap/nosnap"
+    by adding /etc/chiark-backup/snap/nosnap to debian/chiark-
+    backup/conffiles (closes: #553560).
+
+ -- gregor herrmann <gregoa@debian.org>  Sat, 05 Dec 2009 13:27:32 +0100
+
+chiark-utils (4.1.28+nmu1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * change build dependency from libnettle-dev to nettle-dev
+    (Closes: #543123).
+
+ -- Magnus Holmgren <holmgren@debian.org>  Wed, 02 Sep 2009 08:55:36 +0200
+
+chiark-utils (4.1.28) unstable; urgency=high
+
+  chiark-named-conf:
+  * Remove obsolete +nodebug option from calls to dig;
+    required for BIND9 compatibility.  Thanks to Ross Younger.
+  * Foreign zones work properly using the configuration
+    prevailing at the end of the config file.
+  * Warn if user specifies a zone with the trailing `.'.
+
+  Other:
+  * hexterm: Declare and document the dependency of hexterm on tcl8.4.
+  * chiark-backup: remove snap-mount properly whether it's a dir or a leaf
+    node (such as a symlink).
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Wed, 27 Aug 2008 23:26:55 +0100
+
+chiark-utils (4.1.24) unstable; urgency=low
+
+  * Change my email address in the Maintainer field so that
+    when I say Closes: in the changelog it doesn't say "fixed in NMU".
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 23 Sep 2007 16:39:48 +0100
+
+chiark-utils (4.1.23) unstable; urgency=low
+
+  * chiark-named-conf improvements:
+    - set $|=1 to make output better with eg  2>&1 |less
+    - new allow-indirect-glue directive
+    - new forbid-slave directive
+    - remove incorrect references to SOA ORIGIN - should be MNAME
+    - allow config file to not exist.  Closes: #274528.
+    - do not warn about glueful nameservers in serverless-glueless
+      areas (since these can't cause problems).  Thanks to Ben Harris
+      for report and subsequent discussion.
+  * chiark-backup Depends on chiark-utils-bin.  Closes: #401611.
+  * summer manpage (thanks to Peter Maydell for initial contribution).
+    (Closes: #401640.)
+  * summer has new -B option for suppressing times of unusual node types.
+  * backup-snaprsync passes -B to summer.
+  * copyright notices for various scripts.
+  * many scripts mentioned in Description and debian/copyright.
+  * use dh_strip rather than doing it ourselves.  Closes #436624.
+  (4.1.22 was an internal unreleased version.)
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sat, 22 Sep 2007 02:29:55 +0100
+
+chiark-utils (4.1.21) unstable; urgency=low
+
+  * backup-snaprsync: pass -I to rsync when copying rsums.
+  * update licence to GPLv3 or later (was GPLv2 or later).
+  * remove FSF's paper address from debian/copyright
+    and refer to the FSF and GNU websites instead.
+  * update copyright dates in backup/.
+  * Fix some out of date email addresses.
+  * summer and watershed's shlibdeps go into Recommends since
+    bits of the package are useable without them.
+  * seddery libgmp3 dependencies to libgmp3 | libgmp3c2 since
+    we are C and not C++ and care nothing for C++ ABI transitions.
+  * Document the dependency relationships in the Description.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Fri, 21 Sep 2007 21:58:33 +0100
+
+chiark-utils (4.1.20) unstable; urgency=low
+
+  * Fix bashism in upstream backup/Makefile.  Closes: #379541.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Fri, 13 Jul 2007 09:49:17 +0100
+
+chiark-utils (4.1.19) unstable; urgency=low
+
+  * Mention summer, xacpi-simple, watershed in chiark-utils-bin's description.
+  * Set SHELL=/bin/bash in debian/rules.  Closes: #379541.
+  * Remove Matthew from the Uploaders.  Closes: #280012.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Thu, 12 Jul 2007 17:25:11 +0100
+
+chiark-utils (4.1.18) unstable; urgency=low
+
+  * summer works even if passed multiple option-containing args.
+  * workaround for nettle bug, supply -lgmp if -lnettle.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Wed, 23 May 2007 19:42:05 +0100
+
+chiark-utils (4.1.17) unstable; urgency=low
+
+  * summer has new -M (do not print mtimes) option.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Wed, 23 May 2007 18:56:53 +0100
+
+chiark-utils (4.1.16) unstable; urgency=low
+
+  * chiark-backup's snaprsync has new rsynccompress option.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Thu, 19 Apr 2007 23:23:32 +0100
+
+chiark-utils (4.1.15) unstable; urgency=low
+
+  * chiark-backup's snaprsync sedderies mountpoint out before sums diff.
+  * chiark-backup's snaprsync has new sshopts option.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Thu, 19 Apr 2007 20:12:37 +0100
+
+chiark-utils (4.1.14) unstable; urgency=low
+
+  * summer has new -x (one file system) option.
+  * summer produces better output with -f when scandir fails.
+  * chiark-backup's snaprsync uses summer -x.
+  * chiark-backup suggests chiark-cprogs with summer with -x.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 18 Feb 2007 13:02:21 +0000
+
+chiark-utils (4.1.13) unstable; urgency=low
+
+  * New `nosnap' no-op snap kind.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Fri, 16 Feb 2007 19:10:23 +0000
+
+chiark-utils (4.1.12) unstable; urgency=low
+
+  * New `watershed' program.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Thu, 15 Feb 2007 01:12:05 +0000
+
+chiark-utils (4.1.11) unstable; urgency=low
+
+  * remountresizereiserfs version copied from chiark.  Works with LVM1
+    on 2.6.x, but not with 2.4.x due to stupid stupid LVM bug where
+    sizes are reported in 512-byte kilobytes but only sometimes.  Also
+    fixed to cope better with dm-based LVM volumes.
+  * New script `summarise-mailbox-preserving-privacy'.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Wed, 22 Nov 2006 19:53:17 +0000
+
+chiark-utils (4.1.10) unstable; urgency=low
+
+  * New script `hexterm', `terminal emulator' for binary data,
+    display/entry in ASCII and hex.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Mon,  2 Oct 2006 22:21:52 +0100
+
+chiark-utils (4.1.9) unstable; urgency=low
+
+  * New better algorithm for expire-iso8601.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Mon,  2 Oct 2006 18:11:56 +0100
+
+chiark-utils (4.1.8) unstable; urgency=low
+
+  * remountresizereiserfs: New script for calculating resize= parameter.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Tue, 15 Aug 2006 15:46:19 +0100
+
+chiark-utils (4.1.7) unstable; urgency=low
+
+  * backup: new `noinc' fs line option.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Mon, 14 Aug 2006 11:04:30 +0100
+
+chiark-utils (4.1.6) unstable; urgency=low
+
+  * backup/snaprsync: work around bug in bash (Debian #382798).
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Mon, 14 Aug 2006 00:43:00 +0100
+
+chiark-utils (4.1.5) unstable; urgency=low
+
+  * expire-iso8601 - new script.
+  * backup/snaprsync: allow setting of program to use for summer; allow
+    setting of rsync options; allow settings to be set to empty strings on
+    the command line; do not fail if localprevious,rsums is missing.
+  * cprogs/summer.c: properly fix for reentrancy of buf.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 13 Aug 2006 17:04:08 +0100
+
+chiark-utils (4.1.4) unstable; urgency=low
+
+  summer bugfix:
+  * `buf' is used reentrantely by recurse() and sometimes is the `path'
+    argument to recurse; cope appropriately.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sat,  5 Aug 2006 15:34:03 +0100
+
+chiark-utils (4.1.3) unstable; urgency=low
+
+  backup snap remountrocp fixes:
+  * Size of snapshot volume properly calculated (now we divide by 1k
+    since df reports in bytes and vgdisplay in kbytes).
+  * Restrict copy to same file system (oops!).
+  * Nicer messages from remountrocp (-q to mkfs; various echoes).
+
+  backup snaprsync fixes:
+  * Support --subdir= option, defaults to `.' (root of source fs).
+  * Correctly set up /var/lib/chiark-backup/snap-drop so drop works.
+  * Send output of local summer to fd 3 as required.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 30 Jul 2006 15:41:07 +0100
+
+chiark-utils (4.1.2) unstable; urgency=low
+
+  * Translate /dev/mapper devices from mount table into LVM device names.
+  * Use `current logical extents associated to logical volume' (field 8)
+    rather than `allocated logical extents of logical volume' (field 9)
+    for size, as the latter is sometimes -1 for some reason.
+  * Attempt 10 times to mount readonly.
+  * Comments stating that lvm{create,extents}core1 need vgroup.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 30 Jul 2006 14:44:23 +0100
+
+chiark-utils (4.1.1) unstable; urgency=low
+
+  * summer sorts the output and identifies hardlink
+    targets instead of printing link count.
+  * backup: new `remountrocp' snapshot type.
+  * backup: snaprsync script shipped.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 30 Jul 2006 13:19:27 +0100
+
+chiark-utils (4.1.0) unstable; urgency=low
+
+  New facilities:
+  * New `cvs-repomove' and `cvs-adjustroot' programs,
+        for moving CVS repositories about safely and easily.
+  * New `random-word' script (no documentation).
+  * New `xacpi-simple' utility (no documentation).
+  * New `summer' checksumming program with no manpage.
+  * Spice and gnuplot scripts `ngspice2genspic', `gnucap2genspec'
+    `genspic2gnuplot'.
+
+  chiark-backup:
+  * Pass -P option to df (to make new df not break things).
+  * Pass -xtmpfs option to df.
+  * Fix critical driver script lost output bug.
+  * Fix pipe mode by passing -m600 when creating it.
+  * Don't mind devices on local filesystems (was failed stat bug).
+  * New lvm/remount-ro snapshotting feature.
+  * New `snaprsync' script which is still a work in progress.
+  * New gtar backup type.
+  * Use (set) dump label field.
+  * Say `(C)' in nroff pages rather than `\(c0' (typo for `\(co').
+    (Closes: #208299 - Debian bug, thanks to Colin Watson.)
+  * Move tape.* symlinks for chiark example into chiark directory.
+    (Closes: #208300 - Debian bug, thanks to Colin Watson.)
+
+  Other fixes and improvements:
+  * Size for readbuffer/writebuffer may be in k or bytes.
+  * sync-accounts works properly with groups with - + . in names
+  * really has -R (chroot) option, and usage message improved.
+  * Typo in copyright statement in with-lock-ex.1 fixed.
+  * Mention cvs-* and palm-datebook-reminders in debian/control.
+  * Make myself (Ian Jackson) the Maintainer.
+  * Fix manpage titles for palm-datebook-reminders(1) and really(8).
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Tue, 27 Jun 2006 19:40:57 +0100
+
+chiark-utils (4.0.0) unstable; urgency=medium
+
+  New programs and utilities:
+  * trivsoundd (cprogs, no .deb).  Largely untested.
+  * palm-datebook-reminders (scripts, chiark-scripts.deb)
+  * really (cprogs, chiark-really.deb)
+  * with-lock-ex (cprogs, chiark-utils-bin.deb)
+  * usernice, cvsweb-list, smtpallow.  (Source only, not built.)
+    
+  chiark-named-conf:
+  * remove warnings by adding complete set of function prototypes
+  * check foreign zones from delegation by default (-mforeign!*)
+
+  chiark-rwbuffer:
+  * fix writebuffer's progname.
+
+  chiark-backup:
+  * full dumps have tapedesc in TAPEID
+  * fix reporting of unaccountable filesystems.
+  * compression support (gz as a style option).
+  * ntfsimage support.
+  * Count and display on-tape file numbers.
+  * Count output blocks for each tape file and in total.
+  * $nice and $nasty in settings.pl.
+  * Show mt runes when we execute them.
+  * A little less clone-and-hack.
+  * Manpages.
+
+  Build system improvements:
+  * Make manpages non-executable (!) (ie install with INSTALL_SHARE)
+  * Makefile commonality moved into settings.make.
+  * readbuffer/writebuffer moved into new cprogs directory.
+  
+  Debian-specific changes:
+  * sync-accounts and chiark-named-conf merged into single
+    chiark-scripts package.
+  * Add section and priority fields
+  * Update to a sensible standards-version
+  * Correct location of the GPL
+
+ -- Matthew Vernon <matthew@debian.org>  Wed, 16 Jul 2003 12:01:01 +0100
+
+chiark-utils (3.0.3) unstable; urgency=low
+
+  * chiark-backup: compatibility with md5sum from dpkg 1.10.4.
+  * chiark-named-conf: manpage gluelessness section improved.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 20 Oct 2002 17:42:53 +0100
+
+chiark-utils (3.0.2) unstable; urgency=low
+
+  * Enhanced portability: cope with (and preserve) comments and 
+    blank lines in local database files.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 14 Jul 2002 23:21:31 +0100
+
+chiark-utils (3.0.1) unstable; urgency=low
+
+  * Enhanced portability: -g root => -g $(SYSTEM_GROUP)
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 14 Jul 2002 21:52:46 +0100
+
+chiark-utils (3.0.0) unstable; urgency=low
+
+  sync-accounts:
+  * Moved all files from old CVS repository into chiark-utils.
+  * Manpages for everything.
+  * /etc/sync-accounts comments may be indented.
+  * Copyright notices sorted out.
+  * sync-accounts.linux uses vipw, vigr.
+  * Removed sync-accounts-mktar (eewwgh).
+  * Makefile and Debian control stuff added.
+
+  Other changes:
+  * debian/copyright file improved.
+  * named-conf script has copyright notice at top.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 14 Jul 2002 21:18:35 +0100
+
+chiark-utils (2.2.6) unstable; urgency=low
+
+  * chiark-named-conf --mail-* works without zone list as args.
+  * chiark-named-conf --mail-* prints better output.
+
+ -- Ian Jackson <ian@chiark.greenend.org.uk>  Sat,  1 Jun 2002 01:12:00 +0100
+
+chiark-utils (2.2.4) unstable; urgency=low
+
+  * chiark-named-conf can send mail reports, and is generally better.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 26 May 2002 21:03:11 +0100
+
+chiark-utils (2.2.3) unstable; urgency=low
+
+  * Allow invocation on 2nd-level domains by coping with superzone `.'
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sun, 27 Jan 2002 19:38:14 +0000
+
+chiark-utils (2.2.2) unstable; urgency=low
+
+  * chiark-named-conf does checks on all slaves even for `*' zones.
+  * chiark-named-conf glueless-serverless works properly.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sat, 12 Jan 2002 20:19:32 +0000
+
+chiark-utils (2.2.1) unstable; urgency=low
+
+  * chiark-named-conf gets list of delegated servers right when doing
+    simple checks (ie -l).
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sat, 12 Jan 2002 00:58:22 +0000
+
+chiark-utils (2.2.0) unstable; urgency=low
+
+  * Add new chiark-named-conf script with manpage etc., and package it.
+  * Add new scripts directory with Makefile.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Sat, 12 Jan 2002 00:47:10 +0000
+
+chiark-utils (2.1.1) unstable; urgency=low
+
+  * add call to mt reten to full.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Wed, 28 Nov 2001 00:05:11 +0000
+
+chiark-utils (2.1.0) experimental; urgency=low
+
+  * man pages for readbuffer, writebuffer from Richard Kettlewell.
+  * add info re last-tape and checkallused to iwjbackup.txt.
+  * new `backup-labeltape' utility.
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Fri,  9 Nov 2001 21:12:25 +0000
+
+chiark-utils (2.0.0) experimental; urgency=low
+  
+  * Initial prepackaged release of chiark backup.
+
+  Significant changes since merge chiark/anarres/mnemeth:
+  * readbuffer/writebuffer command line arg for buffer size.
+  * readbuffer/writebuffer share common code.
+  * example config files from Relativity and chiark shipped.
+  * Improved documentation.
+  * bringup-hook created.
+  * bugfixes (all of bugs introduced by merge, AFAICT).
+
+ -- Ian Jackson <ian@davenant.greenend.org.uk>  Mon,  8 Oct 2001 01:52:21 +0100
+
+# Local variables:
+# mode: debian-changelog
+# End:
diff --git a/debian/chiark-backup/conffiles b/debian/chiark-backup/conffiles
new file mode 100644 (file)
index 0000000..c38afbf
--- /dev/null
@@ -0,0 +1,5 @@
+/etc/chiark-backup/settings.sh
+/etc/chiark-backup/snap/lvm
+/etc/chiark-backup/snap/remount
+/etc/chiark-backup/snap/remountrocp
+/etc/chiark-backup/snap/nosnap
diff --git a/debian/chiark-utils-bin.links b/debian/chiark-utils-bin.links
new file mode 100644 (file)
index 0000000..becff8f
--- /dev/null
@@ -0,0 +1 @@
+/usr/bin/xbatmon-simple /usr/bin/xacpi-simple
diff --git a/debian/compat b/debian/compat
new file mode 100644 (file)
index 0000000..7ed6ff8
--- /dev/null
@@ -0,0 +1 @@
+5
diff --git a/debian/control b/debian/control
new file mode 100644 (file)
index 0000000..f99a656
--- /dev/null
@@ -0,0 +1,130 @@
+Source: chiark-utils
+Section: admin
+Priority: extra
+Maintainer: Ian Jackson <ijackson@chiark.greenend.org.uk>
+Build-Depends: libx11-dev, nettle-dev, debhelper (>= 5)
+Standards-Version: 3.9.1
+
+Package: chiark-backup
+Section: utils
+Priority: extra
+Architecture: all
+Depends: chiark-rwbuffer, chiark-utils-bin, ${misc:Depends}
+Suggests: chiark-utils-bin (>= 4.1.14)
+Description: backup system for small systems and networks
+ These are the backup scripts used by chiark.greenend.org.uk and other
+ systems belonging to the Sinister Greenend Organisation.  Features:
+  * Suitable for single systems and small networks.
+  * Reasonably simple; they do what you tell it to.
+  * Hard failures when individual systems fail, to encourage fixing !
+ If you have a larger site you may wish to look at Amanda.
+
+Package: chiark-scripts
+Section: admin
+Priority: extra
+Conflicts: chiark-named-conf, sync-accounts
+Replaces: chiark-named-conf, sync-accounts
+Depends: ${misc:Depends}
+Suggests: tcl8.4
+Architecture: all
+Description: chiark system administration scripts
+ This package contains a number of small administration scripts used
+ by chiark.greenend.org.uk and other systems belonging to the Sinister
+ Greenend Organisation.  Featuring:
+ .
+ chiark-named-conf: a tool for managing nameserver configurations
+ and checking for suspected DNS problems.  Its main functions are to
+ check that delegations are appropriate and working, that secondary
+ zones are slaved from the right places, and to generate a
+ configuration for BIND, from its own input file.
+ .
+ sync-accounts: a simple but flexible account info synchroniser.
+ sync-accounts is a tool for copying un*x account data from remote
+ systems and installing it locally.  It is flexible and reasonably
+ straightforward, but lacks integration with other distributed
+ databases such as NIS.
+ .
+ cvs-repomove and cvs-adjustroot: tools for moving CVS repositories
+ and adjusting working trees.
+ .
+ palm-datebook-reminders: a program which emails mails you reminders
+ about the appointments in your Palm's Datebook.
+ .
+ cvsweb-list: cgi program to list ucgi (userv-utils) cvsweb repos
+ .
+ expire-iso8601: keep or expire backup trees named after their dates
+ .
+ gnucap2genspic, ngspice2genspic, genspic2gnuplot: convert gnucap
+ files and ngspice output files to genspic and genspic files to
+ gnuplot input so they can be plotted.
+ .
+ hexterm: connects to serial port and allows the user interact in
+ ASCII and hex.  Ie, a hex "terminal" program which lets you speak a
+ serial port protocol directly.  (Needs tcl8.4 to be installed.)
+ .
+ random-word, remountresizereiserfs,
+ summarise-mailbox-preserving-privacy
+
+Package: chiark-rwbuffer
+Section: utils
+Priority: extra
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: readbuffer/writebuffer: prevents tape drive seesawing, etc.
+ readbuffer and writebuffer: programs for reading input from devices,
+ and writing output to, which don't like constant stopping and
+ starting, such as tape drives and audio playback devices.
+
+Package: chiark-utils-bin
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Recommends: ${shlibs:Recommends}
+Suggests: ${shlibs:Suggests}
+Section: utils
+Priority: extra
+Description: chiark system administration utilities
+ This package contains a number of small administration scripts used
+ by chiark.greenend.org.uk and other systems belonging to the Sinister
+ Greenend Organisation.  Currently featuring only:
+ .
+ with-lock-ex: a simple tool for acquiring a lockfile before running
+ another program or script.
+ .
+ summer: a tool for reporting complete details about a filesystem tree
+ in a parseable format, including checksums.
+ .
+ xbatmon-simple: a very simple X client for displaying battery
+ charge status.
+ .
+ watershed: a utility for saving on superfluous executions of an
+ idempotent command.  (This is the same utility as shipped separately
+ in Ubuntu's udev, but with slightly different defaults and a
+ different install location.)
+ .
+ rcopy-repeatedly: a utility for repeatedly copying a file from one
+ host to another, to keep a copy constantly up to date.
+ .
+ summer and watershed require the installation of the Recommended
+ crypto libraries; xbatmon-simple needs the Suggested X libraries.
+
+Package: chiark-really
+Section: admin
+Priority: extra
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: really - a tool for gaining privilege (simple, realistic sudo)
+ really is a program that allows certain users to become whatever user
+ they like on request.  It is a bit like sudo in that respect.
+ However, really is simpler than sudo, and doesn't give the system
+ administrator any false security promises.  So really is less of a
+ general security risk to the system.
+ .
+ Unlike sudo it does not pretend that the called account can be any
+ more secure than the calling account. so there is never a need for a
+ password.  If you wanted to restrict which commands and functions the
+ called user can perform, use userv, not really or sudo.
+ .
+ Also unlike sudo, really only works if the calling user is supposed
+ to be equivalent to root.  But, really can also be used by
+ root-equivalent users to become any user, not just root; in this way
+ it can be a replacement for certain uses of su.
diff --git a/debian/copyright b/debian/copyright
new file mode 100644 (file)
index 0000000..2d0cbc9
--- /dev/null
@@ -0,0 +1,79 @@
+This package contains some the utilities from Ian Jackson's private
+system `chiark.greenend.org.uk'.
+
+This package, containing the moderately portable sources and Debian
+packaging information, and the resulting Debian binary packages, was
+put together by Ian, but with assistance from other chiark users.  For
+both upstream and Debian packaging questions, please contact
+chiark-utils-maint@chiark.greenend.org.uk.
+
+
+The software included is:
+
+chiark backup, for small systems or networks.  chiark backup is
+ Copyright 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+ Copyright 1999 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+
+readbuffer/writebuffer (in the backup directory), filters for keeping
+tape drives streaming and audio output devices playing.  They are:
+ Copyright 1997-1998,2000-2001 Ian Jackson <ian@chiark.greenend.org.uk>
+
+sync-accounts, a tool for synchronising UN*X password data.  It's
+ Copyright 1999-2000,2002 Ian Jackson <ian@davenant.greenend.org.uk>
+ Copyright 2000-2001 nCipher Corporation Ltd
+
+chiark-named-conf (in the scripts directory) checks and generates
+nameserver configurations.  It is:
+ Copyright 2002 Ian Jackson <ian@chiark.greenend.org.uk>
+
+watershed, a tool for optimising runs of idempotent commands, is
+ Copyright 2007 Canonical Ltd
+
+xbatmon-simple, a simple X client for displaying ACPI battery status
+ Copyright 2004,2012 Ian Jackson <ian@chiark.greenend.org.uk>
+
+summer, a tool for reporting complete details about a filesystem tree
+ Copyright 2003-2007 Ian Jackson <ian@chiark.greenend.org.uk>
+ manpage Copyright 2006 Peter Maydell <pmaydell@chiark.greenend.org.uk>
+
+cvs-repomove and cvs-adjustroot: tools for moving CVS repositories
+ Copyright 2004-2006 Ian Jackson <ian@chiark.greenend.org.uk>
+
+cvsweb-list: cgi program to list ucgi (userv-utils) cvsweb repos
+ Copyright 2001 Ian Jackson <ian@chiark.greenend.org.uk>
+
+expire-iso8601: keeps or expires backup trees named after their dates
+ Copyright 2006 Ian Jackson <ian@chiark.greenend.org.uk>
+
+gnucap2genspic, ngspice2genspic, genspic2gnuplot:
+Tools for converting gnucap files and ngspice output files to
+genspic and genspic files to gnuplot input so they can be plotted.
+ Copyright 2004 Ian Jackson <ian@chiark.greenend.org.uk>
+
+hexterm: connect to serial port and interact in ASCII and hex (hex terminal)
+ Copyright 2005 Ian Jackson <ian@chiark.greenend.org.uk>
+palm-datebook-reminders: for mailing reminders about Palm PDA appointments
+ Copyright 2003 Ian Jackson <ian@chiark.greenend.org.uk>
+
+random-word, remountresizereiserfs, summarise-mailbox-preserving-privacy
+ Miscellaneous utilities.
+ Copyright 2004,2006 Ian Jackson <ian@chiark.greenend.org.uk>
+
+
+The chiark utilities are all free software; you can redistribute them
+and/or modify them 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.
+
+These programs are distributed in the hope that they 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 with
+your Debian GNU/Linux system, in /usr/share/common-licenses/GPL-3, or
+with the chiark-utils source package as the file COPYING; if not,
+email me at one of the addresses above or consult the Free Software
+Foundation's website at www.fsf.org, or the GNU Project website at
+www.gnu.org.
diff --git a/debian/rules b/debian/rules
new file mode 100755 (executable)
index 0000000..62d84cf
--- /dev/null
@@ -0,0 +1,157 @@
+#!/usr/bin/make -f
+
+SHELL=/bin/bash
+
+subdirs_build_arch=    cprogs
+subdirs_nobuild=backup sync-accounts scripts
+package=       chiark-utils
+packages_indep=        chiark-backup chiark-scripts
+packages_arch= chiark-rwbuffer chiark-really chiark-utils-bin
+packages=      $(packages_indep) $(packages_arch)
+
+cwd=   $(shell pwd)
+d=     $(cwd)/debian
+t=     $d/tmp
+
+build:
+       $(checkdir)
+       set -e; for s in $(subdirs_build_arch); do $(MAKE) -C $$s all; done
+       touch build
+
+build-indep: build
+build-arch: build
+
+clean:
+       $(checkdir)
+       rm -f build
+       set -e; for s in $(subdirs_build_arch); do \
+               $(MAKE) -C $$s -i distclean || \
+               $(MAKE) -C $$s -f Makefile.in distclean; \
+       done
+       rm -rf *~ debian/tmp debian/*~ debian/files* debian/substvars*
+       rm -rf debian/sv-* debian/*.debhelper.log
+
+binary-prep:
+       $(checkdir)
+       rm -rf debian/tmp*
+       #
+       set -e; for s in $(subdirs_build_arch) $(subdirs_nobuild); do \
+               $(MAKE) -C $$s install install-docs install-examples \
+                       prefix=$t/$$s/usr \
+                       et