summaryrefslogtreecommitdiffstats
path: root/debian/slapd.scripts-common
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:33 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 16:35:33 +0000
commit1f641814b2bfbbf2dc3347e11a3fe901bfdf1efc (patch)
tree77094b19bf9091ceb6f6ab7d0267f70fef9c8323 /debian/slapd.scripts-common
parentAdding upstream version 2.5.13+dfsg. (diff)
downloadopenldap-1f641814b2bfbbf2dc3347e11a3fe901bfdf1efc.tar.xz
openldap-1f641814b2bfbbf2dc3347e11a3fe901bfdf1efc.zip
Adding debian version 2.5.13+dfsg-5.debian/2.5.13+dfsg-5debian
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'debian/slapd.scripts-common')
-rw-r--r--debian/slapd.scripts-common713
1 files changed, 713 insertions, 0 deletions
diff --git a/debian/slapd.scripts-common b/debian/slapd.scripts-common
new file mode 100644
index 0000000..8f803f3
--- /dev/null
+++ b/debian/slapd.scripts-common
@@ -0,0 +1,713 @@
+# -*- sh -*-
+# This file can be included with #SCRIPTSCOMMON#
+
+
+# ===== Dumping and reloading using LDIF files ========================= {{{
+#
+# If incompatible changes are done to the database underlying a LDAP
+# directory we need to dump the contents and reload the data into a newly
+# created database after the new server was installed. The following
+# functions deal with this functionality.
+
+
+# ----- Configuration of this component -------------------------------- {{{
+#
+# Dumping the database can have negative effects on the system we are
+# running on. If there is a lot of data dumping it might fill a partition
+# for example. Therefore we must give the user exact control over what we
+# are doing.
+
+database_dumping_enabled() { # {{{
+# Check if the user has enabled database dumping for the current situation.
+# Return success if yes.
+# Usage: if database_dumping_enabled; then ... fi
+
+ # If the package is being removed, dump unconditionally as we
+ # don't know whether the next version will require reload.
+ [ "$MODE" = remove ] && return 0
+
+ db_get slapd/dump_database
+ case "$RET" in
+ always)
+ ;;
+ "when needed")
+ database_format_changed || return 1
+ ;;
+ never)
+ return 1
+ ;;
+ *)
+ echo >&2 "Unknown value for slapd/dump_database: $RET"
+ echo >&2 "Please report!"
+ exit 1
+ ;;
+ esac
+}
+
+# }}}
+database_format_changed() { # {{{
+# Check if the database format has changed since the old installed version
+# Return success if yes.
+# Usage: if database_format_changed; then
+
+ if dpkg --compare-versions "$OLD_VERSION" lt-nl 2.5; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# }}}
+database_dumping_destdir() { # {{{
+# Figure out the directory we are dumping the database to and create it
+# if it does not exist.
+# Usage: destdir=`database_dumping_destdir`
+
+ local dir
+ db_get slapd/dump_database_destdir
+ dir=`echo "$RET"|sed -e "s/VERSION/$OLD_VERSION/"`
+ mkdir -p -m 700 "$dir"
+ echo $dir
+}
+
+# }}}
+create_new_user() { # {{{
+ if [ -z "`getent group openldap`" ]; then
+ addgroup --quiet --system openldap
+ fi
+ if [ -z "`getent passwd openldap`" ]; then
+ echo -n " Creating new user openldap... " >&2
+ adduser --quiet --system --home /var/lib/ldap --shell /bin/false \
+ --ingroup openldap --disabled-password --disabled-login \
+ --gecos "OpenLDAP Server Account" openldap
+ echo "done." >&2
+ fi
+}
+# }}}
+create_ldap_directories() { # {{{
+ if [ ! -d /var/lib/ldap ]; then
+ mkdir -m 0700 /var/lib/ldap
+ fi
+ if [ ! -d /var/run/slapd ]; then
+ mkdir -m 0755 /var/run/slapd
+ fi
+ update_permissions /var/lib/ldap
+ update_permissions /var/run/slapd
+}
+# }}}
+update_permissions() { # {{{
+ local dir
+ dir="$1"
+ if [ -d "$dir" ]; then
+ [ -z "$SLAPD_USER" ] || chown -R -H "$SLAPD_USER" "$dir"
+ [ -z "$SLAPD_GROUP" ] || chgrp -R -H "$SLAPD_GROUP" "$dir"
+ fi
+}
+# }}}
+update_databases_permissions() { # {{{
+ get_suffix | while read -r suffix; do
+ dbdir=`get_directory "$suffix"`
+ update_permissions "$dbdir"
+ done
+}
+# }}}
+# }}}
+# ----- Dumping and loading the data ------------------------------------ {{{
+
+dump_config() { # {{{
+# Dump the cn=config database to the backup directory.
+# This is not the same as backup_config_once, which copies the slapd.d
+# directory verbatim.
+ local dir
+
+ [ -d "$SLAPD_CONF" ] || return 0
+
+ dir="$(database_dumping_destdir)"
+ echo "Saving current slapd configuration to $dir..." >&2
+ slapcat -F "$SLAPD_CONF" -n0 -l "$dir/cn=config.ldif"
+}
+# }}}
+dump_databases() { # {{{
+# If the user wants us to dump the databases they are dumped to the
+# configured directory.
+
+ local db suffix file dir failed slapcat_opts
+
+ database_dumping_enabled || return 0
+
+ dir=`database_dumping_destdir`
+ echo >&2 " Dumping to $dir: "
+ (get_suffix | while read -r suffix; do
+ dbdir=`get_directory "$suffix"`
+ if [ -n "$dbdir" ]; then
+ file="$dir/$suffix.ldif"
+ printf ' - directory %s... ' "$suffix" >&2
+ # Need to support slapd.d migration from preinst
+ if [ -f "${SLAPD_CONF}" ]; then
+ slapcat_opts="-g -f ${SLAPD_CONF}"
+ else
+ slapcat_opts="-g -F ${SLAPD_CONF}"
+ fi
+ slapcat ${slapcat_opts} -b "$suffix" > "$file" || failed=1
+ if [ "$failed" ]; then
+ rm -f "$file"
+ echo "failed." >&2
+ db_subst slapd/upgrade_slapcat_failure location "$dir" <&5
+ db_input critical slapd/upgrade_slapcat_failure <&5 || true
+ db_go <&5 || true
+ exit 1
+ fi
+ echo "done." >&2
+ fi
+ done) 5<&0 </dev/null
+}
+
+# }}}
+load_databases() { # {{{
+ local dir file db dbdir backupdir slapadd_opts
+
+ dir=`database_dumping_destdir`
+ echo >&2 " Loading from $dir: "
+ # restore by increasing suffix length due to possibly glued databases
+ get_suffix | awk '{ print length, $0 }' | sort -n | cut -d ' ' -f 2- \
+ | while read -r suffix; do
+ dbdir=`get_directory "$suffix"`
+ if [ -z "$dbdir" ]; then
+ continue
+ fi
+ if ! is_empty_dir "$dbdir"; then
+ echo >&2 \
+ " Directory $dbdir for $suffix not empty, aborting."
+ exit 1
+ fi
+
+ file="$dir/$suffix.ldif"
+ printf ' - directory %s... ' "$suffix" >&2
+
+ # If there is an old DB_CONFIG file, restore it before
+ # running slapadd
+ backupdir="$(compute_backup_path -n "$suffix")"
+ if [ -e "$backupdir"/DB_CONFIG ]; then
+ cp -a "$backupdir"/DB_CONFIG "$dbdir"/
+ fi
+
+ if [ -f "${SLAPD_CONF}" ]; then
+ slapadd_opts="-g -f ${SLAPD_CONF}"
+ else
+ slapadd_opts="-g -F ${SLAPD_CONF}"
+ fi
+ capture_diagnostics slapadd ${slapadd_opts} \
+ -q -b "$suffix" -l "$file" || failed=1
+ if [ "$failed" ]; then
+ rm -f "$dbdir"/*
+ echo "failed." >&2
+ echo >&2
+ cat <<-EOF
+ Loading the database from the LDIF dump failed with the following
+ error while running slapadd:
+EOF
+ release_diagnostics " "
+ return 1
+ fi
+ echo "done." >&2
+
+ if [ -n "$SLAPD_USER" ] || [ -n "$SLAPD_GROUP" ]; then
+ echo -n " - chowning database directory ($SLAPD_USER:$SLAPD_GROUP)... "
+ update_permissions "$dbdir"
+ echo "done";
+ fi
+ done
+}
+
+# }}}
+move_incompatible_databases_away() { # {{{
+ echo >&2 " Moving old database directories to /var/backups:"
+ (get_suffix | while read -r suffix; do
+ dbdir=`get_directory "$suffix"`
+ move_old_database_away "$dbdir" "$suffix" <&5
+ done) 5<&0 </dev/null
+}
+# }}}
+# }}}
+# }}}
+
+# ===== Parsing the slapd configuration file ============================ {{{
+#
+# For some operations we have to know the slapd configuration. These
+# functions are for parsing the slapd configuration file.
+
+# The following two functions need to support slapd.conf installations
+# as long as upgrading from slapd.conf environment is supported.
+# They're used to dump database in preinst which may have a slapd.conf file.
+get_suffix() { # {{{
+ if [ -f "${SLAPD_CONF}" ]; then
+ for f in `get_all_slapd_conf_files`; do
+ sed -n -e '/^suffix[[:space:]]/ { s/^suffix[[:space:]]\+"*\([^"]\+\)"*/\1/; s/\\\\/\\/g; p }' $f
+ done
+ else
+ grep -h ^olcSuffix ${SLAPD_CONF}/cn\=config/olcDatabase*.ldif | cut -d: -f 2
+ fi | sort -u
+}
+# }}}
+get_directory() { # {{{
+# Returns the db directory for a given suffix
+ if [ -d "${SLAPD_CONF}" ] && get_suffix | grep -Fq "$1" ; then
+ sed -n 's/^olcDbDirectory: *//p' `grep -Flx "olcSuffix: $1" ${SLAPD_CONF}/cn\=config/olcDatabase*.ldif`
+ elif [ -f "${SLAPD_CONF}" ]; then
+ # Extract the directory for the given suffix ($1)
+ # Quote backslashes once for slapd.conf parser, again for awk
+ quoted="$(printf '%s' "$1" | sed 's/\\/\\\\\\\\/g')"
+ for f in `get_all_slapd_conf_files`; do
+ awk ' BEGIN { DB=0; SUF=""; DIR="" } ;
+ /^database/ { DB=1; SUF=""; DIR="" } ;
+ DB==1 && /^suffix[ \t]+"?'"$quoted"'"?$/ { SUF=$2 ; } ;
+ DB==1 && /^directory/ { DIR=$2 ;} ;
+ DB==1 && SUF!="" && DIR!="" { sub(/^"/,"",DIR) ; sub(/"$/,"",DIR) ; print DIR; SUF=""; DIR="" }' "${f}" | \
+ sed -e's/\([^\\]\|^\)"/\1/g; s/\\"/"/g; s/\\\\/\\/g'
+
+ done
+ else
+ return 1
+ fi
+}
+# }}}
+get_all_slapd_conf_files() { # {{{
+# Returns the list of all the config files: slapd.conf and included files.
+ echo ${SLAPD_CONF}
+ awk '
+BEGIN { I=0 }
+/^include/ {
+ sub(/include/," ");
+ I=1;
+}
+I==1 && /^[ \t]+/ {
+ split($0,F) ;
+ for (f in F)
+ if (!match(F[f],/schema/)) {
+ print F[f]
+ } ;
+ next;
+}
+I==1 { I=0 }
+' ${SLAPD_CONF}
+}
+# }}}
+# }}}
+
+compute_backup_path() { # {{{
+# Compute the path to backup a database directory
+# This is used when backing up actual database files, either just before
+# reloading a dump, or before generating a new configuration.
+# The directory used for dumping and reloading LDIF across upgrades, as
+# well as for backing up the configuration before upgrading, is computed
+# by database_dumping_destdir().
+# Usage: compute_backup_path [-n] <basedn>
+
+# XXX: should ask the user via debconf
+# or maybe just use database_dumping_destdir
+
+ local basedn ok_exists
+ if [ "$1" = "-n" ]; then
+ ok_exists=yes
+ shift
+ fi
+ basedn="$1"
+
+ local id target
+ if [ "$MODE" = reconfigure ] || [ "$DEBCONF_RECONFIGURE" ]; then
+ # reconfigure: use OLD_VERSION plus a timestamp.
+ id="${OLD_VERSION:+$OLD_VERSION-}$(date +%Y%m%d-%H%M%S)"
+ else
+ # install/upgrade: use OLD_VERSION or a timestamp, not both.
+ id="${OLD_VERSION:-$(date +%Y%m%d-%H%M%S)}"
+ fi
+ target="/var/backups/$basedn-$id.ldapdb"
+ if [ -e "$target" ] && [ -z "$ok_exists" ]; then
+ echo >&2
+ echo >&2 " Backup path $target exists. Giving up..."
+ exit 1
+ fi
+
+ printf '%s' "$target"
+}
+
+# }}}
+move_old_database_away() { # {{{
+# Move the old database away if it is still there
+#
+# In fact this function makes sure that the database directory is empty
+# with the exception of any DB_CONFIG file
+# and can be populated with a new database. If something is in the way
+# it is moved to a backup directory if the user accepted the debconf
+# option slapd/move_old_database. Otherwise we output a warning and let
+# the user fix it himself.
+# Usage: move_old_database_away <dbdir> [<basedn>]
+
+ local databasedir backupdir
+ databasedir="$1"
+ suffix="${2:-unknown}"
+
+ if [ ! -e "$databasedir" ] || is_empty_dir "$databasedir"; then
+ return 0
+ fi
+
+ # Note that we can't just move the database dir as it might be
+ # a mount point. Instead me move the content which might
+ # include mount points as well anyway, but it's much less likely.
+ db_get slapd/move_old_database
+ if [ "$RET" = true ]; then
+ backupdir="$(compute_backup_path "$suffix")"
+ printf ' - directory %s... ' "$suffix" >&2
+ mkdir -p "$backupdir"
+ find -H "$databasedir" -mindepth 1 -maxdepth 1 -type f \
+ -exec mv {} "$backupdir" \;
+ echo done. >&2
+ else
+ cat >&2 <<EOF
+ There are leftover files in $databasedir. This will probably break
+ creating the initial directory. If that's the case please move away
+ stuff in there and retry the configuration.
+EOF
+ fi
+}
+# }}}
+manual_configuration_wanted() { # {{{
+# Check if the user wants to configure everything himself (queries debconf)
+# Returns success if yes.
+
+ db_get slapd/no_configuration
+ if [ "$RET" = "true" ]; then
+ return 0
+ else
+ return 1
+ fi
+}
+# }}}
+create_new_configuration() { # {{{
+# Create a new configuration and directory
+
+ local basedn dc
+
+ # For the domain really.argh.org we create the basedn
+ # dc=really,dc=argh,dc=org with the dc entry dc: really
+ db_get slapd/domain
+ basedn="dc=`echo $RET | sed 's/^\.//; s/\.$//; s/\./,dc=/g'`"
+ dc="`echo $RET | sed 's/^\.//; s/\..*$//'`"
+
+ backup_config_once
+ if [ -e "/var/lib/ldap" ] && ! is_empty_dir /var/lib/ldap; then
+ echo >&2 " Moving old database directory to /var/backups:"
+ move_old_database_away /var/lib/ldap
+ fi
+ create_ldap_directories
+ create_new_slapd_conf "$basedn"
+ create_new_directory "$basedn" "$dc"
+
+ # Put the right permissions on this directory.
+ update_permissions /var/lib/ldap
+
+ # Now that we created the new directory we don't need the passwords in the
+ # debconf database anymore. So wipe them.
+ wipe_admin_pass
+}
+# }}}
+create_new_slapd_conf() { # {{{
+# Create the new slapd.d directory (configuration)
+# Usage: create_new_slapd_conf <basedn>
+
+ local initldif failed basedn adminpass
+
+ # Fetch configuration
+ basedn="$1"
+ db_get slapd/internal/adminpw
+ adminpass="$RET"
+
+ echo -n " Creating initial configuration... " >&2
+
+ # Create the slapd.d directory.
+ rm -rf ${SLAPD_CONF}/cn=config ${SLAPD_CONF}/cn=config.ldif
+ mkdir -p ${SLAPD_CONF}
+ initldif=`mktemp -t slapadd.XXXXXX`
+ cat /usr/share/slapd/slapd.init.ldif > ${initldif}
+
+ # Change some defaults
+ sed -i -e "s|@SUFFIX@|$basedn|g" ${initldif}
+ sed -i -e "s|@PASSWORD@|$adminpass|g" ${initldif}
+
+ capture_diagnostics slapadd -F "${SLAPD_CONF}" -b "cn=config" \
+ -l "${initldif}" || failed=1
+ if [ "$failed" ]; then
+ cat <<-EOF
+Loading the initial configuration from the ldif file (${init_ldif}) failed with
+the following error while running slapadd:
+EOF
+ release_diagnostics " "
+ exit 1
+ fi
+
+ update_permissions "${SLAPD_CONF}"
+ rm -f "${initldif}"
+ echo "done." >&2
+}
+# }}}
+create_new_directory() { # {{{
+# Create a new directory. Takes the basedn and the dc value of that entry.
+# Other information is extracted from debconf.
+# Usage: create_new_directory <basedn> <dc>
+
+ local basedn dc organization adminpass
+ basedn="$1"
+ dc="$2"
+
+ db_get shared/organization
+ organization="$RET"
+ db_get slapd/internal/adminpw
+ adminpass="$RET"
+
+ echo -n " Creating LDAP directory... " >&2
+
+ initldif=`mktemp -t slapadd.XXXXXX`
+ cat <<-EOF > "${initldif}"
+ dn: $basedn
+ objectClass: top
+ objectClass: dcObject
+ objectClass: organization
+ o: $organization
+ dc: $dc
+
+ EOF
+
+ capture_diagnostics slapadd -F "${SLAPD_CONF}" -b "${basedn}" \
+ -l "${initldif}" || failed=1
+ if [ "$failed" ]; then
+ rm -f ${initldif}
+ echo "failed." >&2
+ cat <<-EOF
+Loading the initial configuration from the ldif file (${init_ldif}) failed with
+the following error while running slapadd:
+EOF
+ release_diagnostics " "
+ exit 1
+ fi
+
+ rm -f ${initldif}
+ echo "done." >&2
+}
+# }}}
+backup_config_once() { # {{{
+# Create a backup of the current configuration files.
+# Usage: backup_config_once
+
+ local backupdir
+
+ if [ -z "$FLAG_CONFIG_BACKED_UP" ]; then
+ if [ -e "$SLAPD_CONF" ]; then
+ backupdir=`database_dumping_destdir`
+ echo -n " Backing up $SLAPD_CONF in ${backupdir}... " >&2
+ cp -a "$SLAPD_CONF" "$backupdir"
+ echo done. >&2
+ fi
+ FLAG_CONFIG_BACKED_UP=yes
+ fi
+}
+
+# }}}
+
+
+set_defaults_for_unseen_entries() { # {{{
+# Set up the defaults for our templates
+ DOMAIN=`hostname -d 2>/dev/null` || true
+ if [ -z "$DOMAIN" ]; then DOMAIN='nodomain'; fi
+
+ db_fget slapd/domain seen
+ if [ "$RET" = false ]; then
+ db_set slapd/domain "$DOMAIN"
+ fi
+
+ db_fget shared/organization seen
+ if [ "$RET" = false ]; then
+ db_set shared/organization "$DOMAIN"
+ fi
+}
+# }}}
+crypt_admin_pass() { # {{{
+# Store the encrypted admin password into the debconf db
+# Usage: crypt_admin_pass
+
+ local adminpw;
+
+ db_get slapd/password1
+ if [ ! -z "$RET" ]; then
+ db_set slapd/internal/adminpw "$(create_password_hash "$RET")"
+ else
+
+ # Set the password.
+ adminpw="$(generate_admin_pass)"
+ db_set slapd/internal/generated_adminpw "$adminpw"
+ db_set slapd/internal/adminpw "$(create_password_hash "$adminpw")"
+ fi
+}
+
+generate_admin_pass() {
+# Generate a password, if no password given then generate one.
+# Usage: generate_admin_pass
+
+ # 15 bytes of /dev/urandom provide 120 random bits, assuming the entropy pool is full enough.
+ # Coding these 15 bytes in base64 returns a 20 characters long password.
+ head -c 15 /dev/urandom | base64 | tr -d '[:space:]'
+}
+
+wipe_admin_pass() {
+# Remove passwords after creating the initial ldap database.
+# Usage: wipe_admin_pass
+ db_set slapd/password1 ""
+ db_set slapd/password2 ""
+ db_set slapd/internal/adminpw ""
+ db_set slapd/internal/generated_adminpw ""
+}
+
+# }}}
+create_password_hash() { # {{{
+# Create the password hash for the given password
+# Usage: hash=`create_password_hash "$password"`
+
+ slappasswd -s "$1"
+}
+
+# }}}
+previous_version_older() { # {{{
+# Check if the previous version is newer than the reference version passed.
+# If we are not upgrading the previous version is assumed to be newer than
+# any reference version.
+# Usage: previous_version_older <package version>
+
+ if dpkg --compare-versions "$OLD_VERSION" lt-nl "$1"; then
+ return 0
+ else
+ return 1
+ fi
+}
+
+# }}}
+previous_version_newer() { # {{{
+# Check if the previous version is newer than the reference version passed.
+# If we are not upgrading the previous version is assumed to be newer than
+# any reference version.
+# Usage: previous_version_newer <package version>
+
+ if dpkg --compare-versions "$OLD_VERSION" gt-nl "$1"; then
+ return 0
+ else
+ return 1
+ fi
+} # }}}
+
+is_initial_configuration() { # {{{
+# Check if this is the initial configuration and not an upgrade of an
+# existing configuration
+# Usage: if is_initial_configuration "$@"; then ... fi from top level
+
+ # Plain installation
+ if [ "$1" = configure ] && [ -z "$2" ]; then
+ return 0
+ fi
+ # Configuration via dpkg-reconfigure
+ if [ "$1" = reconfigure ] || [ "$DEBCONF_RECONFIGURE" ]; then
+ return 0
+ fi
+ # Upgrade but slapd.conf doesn't exist. If the user is doing this
+ # intentionally because they want to put it somewhere else, they
+ # should select manual configuration in debconf.
+ if [ "$1" = configure ] && [ ! -e "${SLAPD_CONF}" ]; then
+ return 0
+ fi
+ return 1
+}
+
+# }}}
+is_empty_dir() { # {{{
+# Check if a path refers to a directory that is "empty" from the POV of slapd
+# (i.e., contains no files except for an optional DB_CONFIG).
+# Usage: if is_empty_dir "$dir"; then ... fi
+
+ output=`find -H "$1" -mindepth 1 -maxdepth 1 -type f \! -name DB_CONFIG 2>/dev/null`
+ if [ -n "$output" ]; then
+ return 1
+ else
+ return 0
+ fi
+}
+
+# }}}
+
+# ===== Global variables ================================================ {{{
+#
+# At some points we need to know which version we are upgrading from if
+# any. More precisely we only care about the configuration and data we
+# might have laying around. Some parts also want to know which mode the
+# script is running in.
+
+MODE="$1" # install, upgrade, etc. - see debian-policy
+OLD_VERSION="$2"
+
+# Source the init script configuration
+# See example file debian/slapd.default for variables defined here
+if [ -f "/etc/default/slapd" ]; then
+ . /etc/default/slapd
+fi
+
+# Load the default location of the slapd config file
+if [ -z "$SLAPD_CONF" ]; then
+ if [ -f "/etc/ldap/slapd.conf" ] && \
+ [ ! -e "/etc/ldap/slapd.d" ]
+ then
+ SLAPD_CONF="/etc/ldap/slapd.conf"
+ else
+ SLAPD_CONF="/etc/ldap/slapd.d"
+ fi
+fi
+
+# }}}
+
+# ----- Handling diagnostic output ------------------------------------ {{{
+#
+# Often you want to run a program while you are showing progress
+# information to the user. If the program you are running outputs some
+# diagnostics it will mess up your screen.
+#
+# This is what the following functions are designed for. When running the
+# program, use capture_diagnostics to store what the program outputs to
+# stderr and use release_diagnostics to write out the captured output.
+
+
+capture_diagnostics() { # {{{
+# Run the command passed and capture the diagnostic output in a temporary
+# file. You can dump that file using release_diagnostics.
+
+ # Create the temporary file
+ local tmpfile
+ tmpfile=`mktemp`
+ exec 7<>"$tmpfile"
+ rm "$tmpfile"
+
+ # Run the program and capture stderr. If the program fails the
+ # function fails with the same status.
+ "$@" 2>&7 || return $?
+}
+
+# }}}
+release_diagnostics() { # {{{
+# Dump the diagnostic output captured via capture_diagnostics, optionally
+# prefixing each line.
+# Usage: release_diagnostics "prefix"
+
+ { exec < /dev/stdin ; sed -e "s/^/$1/" ; } <&7
+}
+
+# }}}
+
+
+# }}}
+
+# vim: set sw=8 foldmethod=marker:
+