summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Bronder <jsbronder@gentoo.org>2014-06-19 20:31:24 +0000
committerJustin Bronder <jsbronder@gentoo.org>2014-06-19 20:31:24 +0000
commitd0b3f15f028db26d6e9ce0281d7e9332d958e480 (patch)
tree35ee1201be49c8dfe9f69e5fda8704660d5e5c41 /sys-cluster/torque
parentRemove old. (diff)
downloadgentoo-2-d0b3f15f028db26d6e9ce0281d7e9332d958e480.tar.gz
gentoo-2-d0b3f15f028db26d6e9ce0281d7e9332d958e480.tar.bz2
gentoo-2-d0b3f15f028db26d6e9ce0281d7e9332d958e480.zip
Bump 4.1.7, add fix for CVE-2013-4495 (#491270)
(Portage version: 2.2.8-r1/cvs/Linux x86_64, signed Manifest commit with key 4D7043C9)
Diffstat (limited to 'sys-cluster/torque')
-rw-r--r--sys-cluster/torque/ChangeLog8
-rw-r--r--sys-cluster/torque/files/CVE-2013-4495.4.1.patch343
-rw-r--r--sys-cluster/torque/torque-4.1.7.ebuild219
3 files changed, 569 insertions, 1 deletions
diff --git a/sys-cluster/torque/ChangeLog b/sys-cluster/torque/ChangeLog
index 6ab81f3c494f..e7e5b6d2a33a 100644
--- a/sys-cluster/torque/ChangeLog
+++ b/sys-cluster/torque/ChangeLog
@@ -1,6 +1,12 @@
# ChangeLog for sys-cluster/torque
# Copyright 1999-2014 Gentoo Foundation; Distributed under the GPL v2
-# $Header: /var/cvsroot/gentoo-x86/sys-cluster/torque/ChangeLog,v 1.167 2014/06/19 19:55:00 jsbronder Exp $
+# $Header: /var/cvsroot/gentoo-x86/sys-cluster/torque/ChangeLog,v 1.168 2014/06/19 20:31:24 jsbronder Exp $
+
+*torque-4.1.7 (19 Jun 2014)
+
+ 19 Jun 2014; Justin Bronder <jsbronder@gentoo.org> +torque-4.1.7.ebuild,
+ +files/CVE-2013-4495.4.1.patch:
+ Bump 4.1.7, add fix for CVE-2013-4495 (#491270)
*torque-2.5.13 (19 Jun 2014)
diff --git a/sys-cluster/torque/files/CVE-2013-4495.4.1.patch b/sys-cluster/torque/files/CVE-2013-4495.4.1.patch
new file mode 100644
index 000000000000..810a4f0944a5
--- /dev/null
+++ b/sys-cluster/torque/files/CVE-2013-4495.4.1.patch
@@ -0,0 +1,343 @@
+From 2aad72c3d2ac612ecbb66828ac6ed5ab51eff5f3 Mon Sep 17 00:00:00 2001
+From: David Beer <dbeer@adaptivecomputing.com>
+Date: Mon, 11 Nov 2013 11:55:58 -0700
+Subject: [PATCH] Fix CVE 2013-4495. Note: this patch has been verified as
+ fixing this security hole but has not received other regression testing.
+ Could not cherry-pick as 2.5 and 4.1 are very different.
+
+---
+ src/server/svr_mail.c | 265 ++++++++++++++++++++++++++++++++------------------
+ 1 file changed, 170 insertions(+), 95 deletions(-)
+
+diff --git a/src/server/svr_mail.c b/src/server/svr_mail.c
+index b269e82..52f2f1f 100644
+--- a/src/server/svr_mail.c
++++ b/src/server/svr_mail.c
+@@ -89,6 +89,7 @@
+ #include <stdio.h>
+ #include <stdlib.h>
+ #include <string.h>
++#include <unistd.h>
+ #include "list_link.h"
+ #include "attribute.h"
+ #include "server_limits.h"
+@@ -136,6 +137,77 @@ void free_mail_info(
+
+
+
++void add_body_info(
++
++ char *bodyfmtbuf /* I */,
++ mail_info *mi /* I */)
++
++ {
++ char *bodyfmt = NULL;
++ bodyfmt = strcpy(bodyfmtbuf, "PBS Job Id: %i\n"
++ "Job Name: %j\n");
++ if (mi->exec_host != NULL)
++ {
++ strcat(bodyfmt, "Exec host: %h\n");
++ }
++
++ strcat(bodyfmt, "%m\n");
++
++ if (mi->text != NULL)
++ {
++ strcat(bodyfmt, "%d\n");
++ }
++ }
++
++
++/*
++ * write_email()
++ *
++ * In emailing, the mail body is written to a pipe connected to
++ * standard input for sendmail. This function supplies the body
++ * of the message.
++ *
++ */
++void write_email(
++
++ FILE *outmail_input,
++ mail_info *mi)
++
++ {
++ char *bodyfmt = NULL;
++ char *subjectfmt = NULL;
++
++ /* Pipe in mail headers: To: and Subject: */
++ fprintf(outmail_input, "To: %s\n", mi->mailto);
++
++ /* mail subject line formating statement */
++ get_svr_attr_str(SRV_ATR_MailSubjectFmt, (char **)&subjectfmt);
++ if (subjectfmt == NULL)
++ {
++ subjectfmt = "PBS JOB %i";
++ }
++
++ fprintf(outmail_input, "Subject: ");
++ svr_format_job(outmail_input, mi, subjectfmt);
++ fprintf(outmail_input, "\n");
++
++ /* Set "Precedence: bulk" to avoid vacation messages, etc */
++ fprintf(outmail_input, "Precedence: bulk\n\n");
++
++ /* mail body formating statement */
++ get_svr_attr_str(SRV_ATR_MailBodyFmt, &bodyfmt);
++ if (bodyfmt == NULL)
++ {
++ char bodyfmtbuf[MAXLINE];
++ add_body_info(bodyfmtbuf, mi);
++ bodyfmt = bodyfmtbuf;
++ }
++
++ /* Now pipe in the email body */
++ svr_format_job(outmail_input, mi, bodyfmt);
++
++ } /* write_email() */
++
+
+
+ void *send_the_mail(
+@@ -143,15 +215,19 @@ void *send_the_mail(
+ void *vp)
+
+ {
+- mail_info *mi = (mail_info *)vp;
+-
+- int i;
+- char *mailfrom = NULL;
+- char *subjectfmt = NULL;
+- char *bodyfmt = NULL;
+- char *cmdbuf = NULL;
+- char bodyfmtbuf[MAXLINE];
+- FILE *outmail;
++ mail_info *mi = (mail_info *)vp;
++
++ int status = 0;
++ int numargs = 0;
++ int pipes[2];
++ int counter;
++ pid_t pid;
++ char *mailptr;
++ char *mailfrom = NULL;
++ char tmpBuf[LOG_BUF_SIZE];
++ // We call sendmail with cmd_name + 2 arguments + # of mailto addresses + 1 for null
++ char *sendmail_args[100];
++ FILE *stream;
+
+ /* Who is mail from, if SRV_ATR_mailfrom not set use default */
+ get_svr_attr_str(SRV_ATR_mailfrom, &mailfrom);
+@@ -173,124 +249,123 @@ void *send_the_mail(
+ mailfrom = PBS_DEFAULT_MAIL;
+ }
+
+- /* mail subject line formating statement */
+- get_svr_attr_str(SRV_ATR_MailSubjectFmt, &subjectfmt);
+- if (subjectfmt == NULL)
+- {
+- subjectfmt = "PBS JOB %i";
+- }
++ sendmail_args[numargs++] = (char *)SENDMAIL_CMD;
++ sendmail_args[numargs++] = (char *)"-f";
++ sendmail_args[numargs++] = (char *)mailfrom;
+
+- /* mail body formating statement */
+- get_svr_attr_str(SRV_ATR_MailBodyFmt, &bodyfmt);
+- if (bodyfmt == NULL)
++ /* Add the e-mail addresses to the command line */
++ mailptr = strdup(mi->mailto);
++ sendmail_args[numargs++] = mailptr;
++ for (counter=0; counter < (int)strlen(mailptr); counter++)
+ {
+- bodyfmt = strcpy(bodyfmtbuf, "PBS Job Id: %i\n"
+- "Job Name: %j\n");
+- if (mi->exec_host != NULL)
++ if (mailptr[counter] == ',')
+ {
+- strcat(bodyfmt, "Exec host: %h\n");
+- }
+-
+- strcat(bodyfmt, "%m\n");
+-
+- if (mi->text != NULL)
+- {
+- strcat(bodyfmt, "%d\n");
++ mailptr[counter] = '\0';
++ sendmail_args[numargs++] = mailptr + counter + 1;
++ if (numargs >= 99)
++ break;
+ }
+ }
+
+- /* setup sendmail command line with -f from_whom */
+- i = strlen(SENDMAIL_CMD) + strlen(mailfrom) + strlen(mi->mailto) + 6;
+-
+- if ((cmdbuf = calloc(1, i + 1)) == NULL)
++ sendmail_args[numargs] = NULL;
++
++ /* Create a pipe to talk to the sendmail process we are about to fork */
++ if (pipe(pipes) == -1)
+ {
+- char tmpBuf[LOG_BUF_SIZE];
+-
+- snprintf(tmpBuf,sizeof(tmpBuf),
+- "Unable to popen() command '%s' for writing: '%s' (error %d)\n",
+- SENDMAIL_CMD,
+- strerror(errno),
+- errno);
++ snprintf(tmpBuf, sizeof(tmpBuf), "Unable to pipes for sending e-mail\n");
+ log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
+ PBS_EVENTCLASS_JOB,
+ mi->jobid,
+ tmpBuf);
+-
+- free_mail_info(mi);
+
++ free_mail_info(mi);
++ free(mailptr);
+ return(NULL);
+ }
+
+- sprintf(cmdbuf, "%s -f %s %s",
+- SENDMAIL_CMD,
+- mailfrom,
+- mi->mailto);
+-
+- outmail = popen(cmdbuf, "w");
+-
+- if (outmail == NULL)
++ if ((pid=fork()) == -1)
+ {
+- char tmpBuf[LOG_BUF_SIZE];
+-
+- snprintf(tmpBuf,sizeof(tmpBuf),
+- "Unable to popen() command '%s' for writing: '%s' (error %d)\n",
+- cmdbuf,
+- strerror(errno),
+- errno);
++ snprintf(tmpBuf, sizeof(tmpBuf), "Unable to fork for sending e-mail\n");
+ log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
+ PBS_EVENTCLASS_JOB,
+ mi->jobid,
+ tmpBuf);
+
+ free_mail_info(mi);
+- free(cmdbuf);
+-
++ free(mailptr);
++ close(pipes[0]);
++ close(pipes[1]);
+ return(NULL);
+ }
++ else if (pid == 0)
++ {
++ /* CHILD */
+
+- /* Pipe in mail headers: To: and Subject: */
+- fprintf(outmail, "To: %s\n", mi->mailto);
++ /* Make stdin the read end of the pipe */
++ dup2(pipes[0], 0);
+
+- fprintf(outmail, "Subject: ");
+- svr_format_job(outmail, mi, subjectfmt);
+- fprintf(outmail, "\n");
++ /* Close the rest of the open file descriptors */
++ int numfds = sysconf(_SC_OPEN_MAX);
++ while (--numfds > 0)
++ close(numfds);
+
+- /* Set "Precedence: bulk" to avoid vacation messages, etc */
+- fprintf(outmail, "Precedence: bulk\n\n");
++ execv(SENDMAIL_CMD, sendmail_args);
++ /* This never returns, but if the execv fails the child should exit */
++ exit(1);
++ }
++ else
++ {
++ /* This is the parent */
+
+- /* Now pipe in the email body */
+- svr_format_job(outmail, mi, bodyfmt);
++ /* Close the read end of the pipe */
++ close(pipes[0]);
+
+- errno = 0;
+- if ((i = pclose(outmail)) != 0)
+- {
+- char tmpBuf[LOG_BUF_SIZE];
++ /* Write the body to the pipe */
++ stream = fdopen(pipes[1], "w");
++ write_email(stream, mi);
+
+- snprintf(tmpBuf,sizeof(tmpBuf),
+- "Email '%c' to %s failed: Child process '%s' %s %d (errno %d:%s)\n",
+- mi->mail_point,
+- mi->mailto,
+- cmdbuf,
+- ((WIFEXITED(i)) ? ("returned") : ((WIFSIGNALED(i)) ? ("killed by signal") : ("croaked"))),
+- ((WIFEXITED(i)) ? (WEXITSTATUS(i)) : ((WIFSIGNALED(i)) ? (WTERMSIG(i)) : (i))),
+- errno,
+- strerror(errno));
+- log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
+- PBS_EVENTCLASS_JOB,
+- mi->jobid,
+- tmpBuf);
+- }
+- else if (LOGLEVEL >= 4)
+- {
+- log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
+- PBS_EVENTCLASS_JOB,
+- mi->jobid,
+- "Email sent successfully\n");
+- }
++ fflush(stream);
++
++ /* Close and wait for the command to finish */
++ if (fclose(stream) != 0)
++ {
++ snprintf(tmpBuf,sizeof(tmpBuf),
++ "Piping mail body to sendmail closed: errno %d:%s\n",
++ errno, strerror(errno));
++
++ log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
++ PBS_EVENTCLASS_JOB,
++ mi->jobid,
++ tmpBuf);
++ }
++
++ // we aren't going to block in order to find out whether or not sendmail worked
++ if ((waitpid(pid, &status, WNOHANG) != 0) &&
++ (status != 0))
++ {
++ snprintf(tmpBuf,sizeof(tmpBuf),
++ "Sendmail command returned %d. Mail may not have been sent\n",
++ status);
++
++ log_event(PBSEVENT_ERROR | PBSEVENT_ADMIN | PBSEVENT_JOB,
++ PBS_EVENTCLASS_JOB,
++ mi->jobid,
++ tmpBuf);
++ }
+
+- free_mail_info(mi);
+- free(cmdbuf);
++ // don't leave zombies
++ while (waitpid(-1, &status, WNOHANG) != 0)
++ {
++ // zombie reaped, NO-OP
++ }
++
++ free_mail_info(mi);
++ free(mailptr);
++ return(NULL);
++ }
+
++ /* NOT REACHED */
++
+ return(NULL);
+ } /* END send_the_mail() */
+
+--
+1.8.3.2
+
diff --git a/sys-cluster/torque/torque-4.1.7.ebuild b/sys-cluster/torque/torque-4.1.7.ebuild
new file mode 100644
index 000000000000..896e03f6edf9
--- /dev/null
+++ b/sys-cluster/torque/torque-4.1.7.ebuild
@@ -0,0 +1,219 @@
+# Copyright 1999-2014 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+# $Header: /var/cvsroot/gentoo-x86/sys-cluster/torque/torque-4.1.7.ebuild,v 1.1 2014/06/19 20:31:24 jsbronder Exp $
+
+EAPI=4
+inherit flag-o-matic eutils linux-info
+
+DESCRIPTION="Resource manager and queuing system based on OpenPBS"
+HOMEPAGE="http://www.adaptivecomputing.com/products/open-source/torque"
+# TODO: hopefully moving to github tags soon
+# http://www.supercluster.org/pipermail/torquedev/2013-May/004519.html
+SRC_URI="http://www.adaptivecomputing.com/index.php?wpfb_dl=1690 -> ${P}.tar.gz"
+LICENSE="torque-2.5"
+
+SLOT="0"
+KEYWORDS="~alpha ~amd64 ~hppa ~ia64 ~mips ~ppc ~ppc64 ~sparc ~x86"
+IUSE="cpusets +crypt doc drmaa kernel_linux munge nvidia server +syslog tk"
+
+DEPEND_COMMON="sys-libs/ncurses
+ sys-libs/readline
+ cpusets? ( sys-apps/hwloc )
+ munge? ( sys-auth/munge )
+ nvidia? ( >=x11-drivers/nvidia-drivers-275 )
+ tk? ( dev-lang/tk )
+ syslog? ( virtual/logger )
+ !games-util/qstat"
+
+DEPEND="${DEPEND_COMMON}
+ !sys-cluster/slurm"
+
+RDEPEND="${DEPEND_COMMON}
+ crypt? ( net-misc/openssh )
+ !crypt? ( net-misc/netkit-rsh )"
+
+pkg_setup() {
+ PBS_SERVER_HOME="${PBS_SERVER_HOME:-/var/spool/torque}"
+
+ # Find a Torque server to use. Check environment, then
+ # current setup (if any), and fall back on current hostname.
+ if [ -z "${PBS_SERVER_NAME}" ]; then
+ if [ -f "${ROOT}${PBS_SERVER_HOME}/server_name" ]; then
+ PBS_SERVER_NAME="$(<${ROOT}${PBS_SERVER_HOME}/server_name)"
+ else
+ PBS_SERVER_NAME=$(hostname -f)
+ fi
+ fi
+
+ USE_CPUSETS="--disable-cpuset"
+ if use cpusets; then
+ if ! use kernel_linux; then
+ einfo
+ elog " Torque currently only has support for cpusets in linux."
+ elog "Assuming you didn't really want this USE flag."
+ einfo
+ else
+ linux-info_pkg_setup
+ if ! linux_config_exists || ! linux_chkconfig_present CPUSETS; then
+ einfo
+ elog " Torque support for cpusets will require that you recompile"
+ elog "your kernel with CONFIG_CPUSETS enabled."
+ einfo
+ fi
+ USE_CPUSETS="--enable-cpuset"
+ fi
+ fi
+}
+
+src_prepare() {
+ # Unused and causes breakage when switching from glibc to tirpc.
+ # https://github.com/adaptivecomputing/torque/pull/148
+ sed -i '/rpc\/rpc\.h/d' src/lib/Libnet/net_client.c || die
+
+ # We install to a valid location, no need to muck with ld.so.conf
+ # --without-loadlibfile is supposed to do this for us...
+ sed -i '/mk_default_ld_lib_file || return 1/d' buildutils/pbs_mkdirs.in || die
+
+ epatch "${FILESDIR}"/torque-4.1.5.1-tcl8.6.patch
+
+ # 491270
+ epatch "${FILESDIR}"/CVE-2013-4495.4.1.patch
+}
+
+src_configure() {
+ local myconf="--with-rcp=mom_rcp"
+
+ use crypt && myconf="--with-rcp=scp"
+
+ econf \
+ $(use_enable tk gui) \
+ $(use_enable syslog) \
+ $(use_enable server) \
+ $(use_enable drmaa) \
+ $(use_enable munge munge-auth) \
+ $(use_enable nvidia nvidia-gpus) \
+ --with-server-home=${PBS_SERVER_HOME} \
+ --with-environ=/etc/pbs_environment \
+ --with-default-server=${PBS_SERVER_NAME} \
+ --disable-gcc-warnings \
+ --with-tcp-retry-limit=2 \
+ --without-loadlibfile \
+ ${USE_CPUSETS} \
+ ${myconf}
+}
+
+src_install() {
+ local dir
+
+ emake DESTDIR="${D}" install || die "make install failed"
+
+ dodoc CHANGELOG README.* Release_Notes || die "dodoc failed"
+ if use doc; then
+ dodoc doc/admin_guide.ps doc/*.pdf || die "dodoc failed"
+ fi
+
+ # The build script isn't alternative install location friendly,
+ # So we have to fix some hard-coded paths in tclIndex for xpbs* to work
+ for file in `find "${D}" -iname tclIndex`; do
+ sed -e "s/${D//\// }/ /" "${file}" > "${file}.new"
+ mv "${file}.new" "${file}" || die
+ done
+
+ for dir in $(find "${D}/${PBS_SERVER_HOME}" -type d); do
+ keepdir "${dir#${D}}"
+ done
+
+ if use server; then
+ newinitd "${FILESDIR}"/pbs_server-init.d-munge pbs_server || die
+ newinitd "${FILESDIR}"/pbs_sched-init.d pbs_sched || die
+ fi
+ newinitd "${FILESDIR}"/pbs_mom-init.d-munge pbs_mom || die
+ newconfd "${FILESDIR}"/torque-conf.d-munge torque || die
+ newinitd "${FILESDIR}"/trqauthd-init.d trqauthd || die
+ newenvd "${FILESDIR}"/torque-env.d 25torque || die
+}
+
+pkg_preinst() {
+ if [[ -f "${ROOT}etc/pbs_environment" ]]; then
+ cp "${ROOT}etc/pbs_environment" "${D}"/etc/pbs_environment || die
+ fi
+
+ if [[ -f "${ROOT}${PBS_SERVER_HOME}/server_priv/nodes" ]]; then
+ cp "${ROOT}${PBS_SERVER_HOME}/server_priv/nodes" \
+ "${D}"/${PBS_SERVER_HOME}/server_priv/nodes || die
+ fi
+
+ echo "${PBS_SERVER_NAME}" > "${D}${PBS_SERVER_HOME}/server_name" || die
+
+ # Fix up the env.d file to use our set server home.
+ sed -i \
+ "s:/var/spool/torque:${PBS_SERVER_HOME}:g" "${D}"/etc/env.d/25torque \
+ || die
+
+ if use munge; then
+ sed -i 's,\(PBS_USE_MUNGE=\).*,\11,' "${D}"etc/conf.d/torque || die
+ fi
+}
+
+pkg_postinst() {
+ elog " If this is the first time torque has been installed, then you are not"
+ elog "ready to start the server. Please refer to the documentation located at:"
+ elog "http://www.clusterresources.com/wiki/doku.php?id=torque:torque_wiki"
+
+ elog " For a basic setup, you may use emerge --config ${PN}"
+
+ elog "Important 4.0+ updates"
+ elog " - The on-wire protocol version has been changed."
+ elog " Versions of Torque before 4.0.0 are no longer able to communicate."
+ elog " - pbs_iff has been replaced by trqauthd, you will now need to add"
+ elog " trqauthd to your default runlevel."
+}
+
+# root will be setup as the primary operator/manager, the local machine
+# will be added as a node and we'll create a simple queue, batch.
+pkg_config() {
+ local h="$(echo "${ROOT}/${PBS_SERVER_HOME}" | sed 's:///*:/:g')"
+ local rc=0
+
+ ebegin "Configuring Torque"
+ einfo "Using ${h} as the pbs homedir"
+ einfo "Using ${PBS_SERVER_NAME} as the pbs_server"
+
+ # Check for previous configuration and bail if found.
+ if [ -e "${h}/server_priv/acl_svr/operators" ] \
+ || [ -e "${h}/server_priv/nodes" ] \
+ || [ -e "${h}/mom_priv/config" ]; then
+ ewarn "Previous Torque configuration detected. Press Enter to"
+ ewarn "continue or Control-C to abort now"
+ read
+ fi
+
+ # pbs_mom configuration.
+ echo "\$pbsserver ${PBS_SERVER_NAME}" > "${h}/mom_priv/config" || die
+ echo "\$logevent 255" >> "${h}/mom_priv/config" || die
+
+ if use server; then
+ local qmgr="${ROOT}/usr/bin/qmgr -c"
+ # pbs_server bails on repeated backslashes.
+ if ! "${ROOT}"/usr/sbin/pbs_server -f -d "${h}" -t create; then
+ eerror "Failed to start pbs_server"
+ rc=1
+ else
+ ${qmgr} "set server operators = root@$(hostname -f)" ${PBS_SERVER_NAME} \
+ && ${qmgr} "create queue batch" ${PBS_SERVER_NAME} \
+ && ${qmgr} "set queue batch queue_type = Execution" ${PBS_SERVER_NAME} \
+ && ${qmgr} "set queue batch started = True" ${PBS_SERVER_NAME} \
+ && ${qmgr} "set queue batch enabled = True" ${PBS_SERVER_NAME} \
+ && ${qmgr} "set server default_queue = batch" ${PBS_SERVER_NAME} \
+ && ${qmgr} "set server resources_default.nodes = 1" ${PBS_SERVER_NAME} \
+ && ${qmgr} "set server scheduling = True" ${PBS_SERVER_NAME} \
+ || die
+
+ "${ROOT}"/usr/bin/qterm -t quick ${PBS_SERVER_NAME} || rc=1
+
+ # Add the local machine as a node.
+ echo "$(hostname -f) np=1" > "${h}/server_priv/nodes" || die
+ fi
+ fi
+ eend ${rc}
+}