aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xbin/ebuild.sh14
-rw-r--r--bin/isolated-functions.sh7
-rwxr-xr-xbin/misc-functions.sh4
-rw-r--r--pym/_emerge/AbstractEbuildProcess.py97
-rw-r--r--pym/portage/package/ebuild/_ipc/ExitCommand.py27
-rw-r--r--pym/portage/package/ebuild/_ipc/IpcCommand.py9
-rw-r--r--pym/portage/package/ebuild/_ipc/__init__.py2
-rw-r--r--pym/portage/package/ebuild/config.py4
-rw-r--r--pym/portage/package/ebuild/doebuild.py67
-rw-r--r--pym/portage/package/ebuild/prepare_build_dirs.py19
-rw-r--r--pym/portage/tests/ebuild/test_ipc_daemon.py23
11 files changed, 156 insertions, 117 deletions
diff --git a/bin/ebuild.sh b/bin/ebuild.sh
index f9bfb3400..8b458705d 100755
--- a/bin/ebuild.sh
+++ b/bin/ebuild.sh
@@ -289,10 +289,6 @@ register_success_hook() {
if ! hasq "$EBUILD_PHASE" clean cleanrm depend help ; then
cd "$PORTAGE_BUILDDIR" || \
die "PORTAGE_BUILDDIR does not exist: '$PORTAGE_BUILDDIR'"
-else
- # Don't try to create this when it's parent
- # directory doesn't necessarily exist.
- unset EBUILD_EXIT_STATUS_FILE
fi
#if no perms are specified, dirs/files will have decent defaults
@@ -749,9 +745,10 @@ dyn_clean() {
fi
if [[ $EMERGE_FROM = binary ]] || ! hasq keepwork $FEATURES; then
- rm -f "$PORTAGE_BUILDDIR"/.{ebuild_changed,exit_status,logid,unpacked,prepared} \
+ rm -f "$PORTAGE_BUILDDIR"/.{ebuild_changed,logid,unpacked,prepared} \
"$PORTAGE_BUILDDIR"/.{configured,compiled,tested,packaged} \
- "$PORTAGE_BUILDDIR"/.die_hooks
+ "$PORTAGE_BUILDDIR"/.die_hooks \
+ "$PORTAGE_BUILDDIR"/.ipc_{in,out,lock}
rm -rf "${PORTAGE_BUILDDIR}/build-info"
rm -rf "${WORKDIR}"
@@ -2206,10 +2203,6 @@ ebuild_main() {
exit 1
;;
esac
- if [ -n "$EBUILD_EXIT_STATUS_FILE" ] ; then
- > "$EBUILD_EXIT_STATUS_FILE" || \
- die "failed to create '$EBUILD_EXIT_STATUS_FILE'"
- fi
}
if [[ $EBUILD_PHASE = depend ]] ; then
@@ -2230,6 +2223,7 @@ elif [[ -n $EBUILD_SH_ARGS ]] ; then
chown portage:portage "$T/environment" &>/dev/null
chmod g+w "$T/environment" &>/dev/null
fi
+ [[ -n $PORTAGE_IPC_DAEMON ]] && "$PORTAGE_BIN_PATH"/ebuild-ipc exit 0
exit 0
)
exit $?
diff --git a/bin/isolated-functions.sh b/bin/isolated-functions.sh
index ddcf8f0d7..14ba58cc8 100644
--- a/bin/isolated-functions.sh
+++ b/bin/isolated-functions.sh
@@ -189,7 +189,7 @@ die() {
fi
eerror "S: '${S}'"
- [ -n "$EBUILD_EXIT_STATUS_FILE" ] && > "$EBUILD_EXIT_STATUS_FILE"
+ [[ -n $PORTAGE_IPC_DAEMON ]] && "$PORTAGE_BIN_PATH"/ebuild-ipc exit 1
# subshell die support
[[ $BASHPID = $EBUILD_MASTER_PID ]] || kill -s SIGTERM $EBUILD_MASTER_PID
@@ -558,7 +558,7 @@ save_ebuild_env() {
# portage config variables and variables set directly by portage
unset ACCEPT_LICENSE BAD BRACKET BUILD_PREFIX COLS \
DISTCC_DIR DISTDIR DOC_SYMLINKS_DIR \
- EBUILD_EXIT_STATUS_FILE EBUILD_FORCE_TEST EBUILD_MASTER_PID \
+ EBUILD_FORCE_TEST EBUILD_MASTER_PID \
ECLASSDIR ECLASS_DEPTH ENDCOL FAKEROOTKEY \
GOOD HILITE HOME \
LAST_E_CMD LAST_E_LEN LD_PRELOAD MISC_FUNCTIONS_ARGS MOPREFIX \
@@ -569,7 +569,8 @@ save_ebuild_env() {
PORTAGE_COLORMAP PORTAGE_CONFIGROOT PORTAGE_DEBUG \
PORTAGE_DEPCACHEDIR PORTAGE_GID \
PORTAGE_GRPNAME PORTAGE_INST_GID \
- PORTAGE_INST_UID PORTAGE_LOG_FILE PORTAGE_MASTER_PID \
+ PORTAGE_INST_UID PORTAGE_IPC_DAEMON \
+ PORTAGE_LOG_FILE PORTAGE_MASTER_PID \
PORTAGE_NONFATAL PORTAGE_QUIET \
PORTAGE_REPO_NAME PORTAGE_RESTRICT PORTAGE_UPDATE_ENV \
PORTAGE_USERNAME PORTAGE_VERBOSE PORTAGE_WORKDIR_MODE PORTDIR \
diff --git a/bin/misc-functions.sh b/bin/misc-functions.sh
index 10d193124..9777c9954 100755
--- a/bin/misc-functions.sh
+++ b/bin/misc-functions.sh
@@ -853,9 +853,7 @@ if [ -n "${MISC_FUNCTIONS_ARGS}" ]; then
${x}
done
unset x
+ [[ -n $PORTAGE_IPC_DAEMON ]] && "$PORTAGE_BIN_PATH"/ebuild-ipc exit 0
fi
-[ -n "${EBUILD_EXIT_STATUS_FILE}" ] && \
- touch "${EBUILD_EXIT_STATUS_FILE}" &>/dev/null
-
:
diff --git a/pym/_emerge/AbstractEbuildProcess.py b/pym/_emerge/AbstractEbuildProcess.py
index f4a87f68b..913a32d12 100644
--- a/pym/_emerge/AbstractEbuildProcess.py
+++ b/pym/_emerge/AbstractEbuildProcess.py
@@ -1,19 +1,65 @@
# Copyright 1999-2009 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
+import textwrap
from _emerge.SpawnProcess import SpawnProcess
-import portage
-portage.proxy.lazyimport.lazyimport(globals(),
- 'portage.package.ebuild.doebuild:_doebuild_exit_status_check_and_log'
-)
+from _emerge.EbuildIpcDaemon import EbuildIpcDaemon
+from portage.elog.messages import eerror
+from portage.localization import _
+from portage.package.ebuild._ipc.ExitCommand import ExitCommand
from portage import os
from portage.util._pty import _create_pty_or_pipe
class AbstractEbuildProcess(SpawnProcess):
- __slots__ = ('settings',)
+ __slots__ = ('settings',) + \
+ ('_ipc_daemon', '_exit_command',)
_phases_without_builddir = ('clean', 'cleanrm', 'depend', 'help',)
+ def _get_phase(self):
+ phase = getattr(self, 'phase', None)
+ if not phase:
+ phase = self.settings.get("EBUILD_PHASE")
+ if not phase:
+ phase = 'other'
+ return phase
+
+ def _start(self):
+
+ if self._get_phase() not in self._phases_without_builddir:
+ envs = [self.settings]
+ if self.env is not None:
+ envs.append(self.env)
+ for env in envs:
+ env['PORTAGE_IPC_DAEMON'] = "1"
+ self._exit_command = ExitCommand()
+ self._exit_command.reply_hook = self._exit_command_callback
+ input_fifo = os.path.join(
+ self.settings['PORTAGE_BUILDDIR'], '.ipc_in')
+ output_fifo = os.path.join(
+ self.settings['PORTAGE_BUILDDIR'], '.ipc_out')
+ commands = {'exit' : self._exit_command}
+ self._ipc_daemon = EbuildIpcDaemon(commands=commands,
+ input_fifo=input_fifo,
+ output_fifo=output_fifo,
+ scheduler=self.scheduler)
+ self._ipc_daemon.start()
+
+ SpawnProcess._start(self)
+
+ def _exit_command_callback(self):
+ if self._registered:
+ # Let the process exit naturally, if possible. This
+ # doesn't really do any harm since it can return
+ # long before the timeout expires.
+ self.scheduler.schedule(self._reg_id, timeout=1000)
+ if self._registered:
+ # If it doesn't exit naturally in a reasonable amount
+ # of time, kill it (solves bug #278895). We try to avoid
+ # this when possible since it makes sandbox complain about
+ # being killed by a signal.
+ self.cancel()
+
def _pipe(self, fd_pipes):
stdout_pipe = fd_pipes.get(1)
got_pty, master_fd, slave_fd = \
@@ -27,11 +73,40 @@ class AbstractEbuildProcess(SpawnProcess):
return not ('sesandbox' in self.settings.features \
and self.settings.selinux_enabled()) or os.isatty(slave_fd)
+ def _unexpected_exit(self):
+
+ phase = self._get_phase()
+
+ msg = _("The ebuild phase '%s' has exited "
+ "unexpectedly. This type of behavior "
+ "is known to be triggered "
+ "by things such as failed variable "
+ "assignments (bug #190128) or bad substitution "
+ "errors (bug #200313). Normally, before exiting, bash should "
+ "have displayed an error message above. If bash did not "
+ "produce an error message above, it's possible "
+ "that the ebuild has called `exit` when it "
+ "should have called `die` instead. This behavior may also "
+ "be triggered by a corrupt bash binary or a hardware "
+ "problem such as memory or cpu malfunction. If the problem is not "
+ "reproducible or it appears to occur randomly, then it is likely "
+ "to be triggered by a hardware problem. "
+ "If you suspect a hardware problem then you should "
+ "try some basic hardware diagnostics such as memtest. "
+ "Please do not report this as a bug unless it is consistently "
+ "reproducible and you are sure that your bash binary and hardware "
+ "are functioning properly.") % phase
+
+ for l in textwrap.wrap(msg, 72):
+ eerror(l, phase=phase, key=self.settings.mycpv)
+
def _set_returncode(self, wait_retval):
SpawnProcess._set_returncode(self, wait_retval)
- phase = self.settings.get("EBUILD_PHASE")
- if not phase:
- phase = 'other'
- if phase not in self._phases_without_builddir:
- self.returncode = _doebuild_exit_status_check_and_log(
- self.settings, phase, self.returncode)
+
+ if self._ipc_daemon is not None:
+ self._ipc_daemon.cancel()
+ if self._exit_command.exitcode is not None:
+ self.returncode = self._exit_command.exitcode
+ else:
+ self.returncode = 1
+ self._unexpected_exit()
diff --git a/pym/portage/package/ebuild/_ipc/ExitCommand.py b/pym/portage/package/ebuild/_ipc/ExitCommand.py
new file mode 100644
index 000000000..f14050b91
--- /dev/null
+++ b/pym/portage/package/ebuild/_ipc/ExitCommand.py
@@ -0,0 +1,27 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+from portage.package.ebuild._ipc.IpcCommand import IpcCommand
+
+class ExitCommand(IpcCommand):
+
+ __slots__ = ('exitcode', 'reply_hook',)
+
+ def __init__(self):
+ IpcCommand.__init__(self)
+ self.reply_hook = None
+ self.exitcode = None
+
+ def __call__(self, argv):
+
+ if self.exitcode is not None:
+ # Ignore all but the first call, since if die is called
+ # then we certainly want to honor that exitcode, even
+ # the ebuild process manages to send a second exit
+ # command.
+ self.reply_hook = None
+ else:
+ self.exitcode = int(argv[1])
+
+ # (stdout, stderr, returncode)
+ return ('', '', 0)
diff --git a/pym/portage/package/ebuild/_ipc/IpcCommand.py b/pym/portage/package/ebuild/_ipc/IpcCommand.py
new file mode 100644
index 000000000..efb27f0a2
--- /dev/null
+++ b/pym/portage/package/ebuild/_ipc/IpcCommand.py
@@ -0,0 +1,9 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+class IpcCommand(object):
+
+ __slots__ = ()
+
+ def __call__(self, argv):
+ raise NotImplementedError(self)
diff --git a/pym/portage/package/ebuild/_ipc/__init__.py b/pym/portage/package/ebuild/_ipc/__init__.py
new file mode 100644
index 000000000..21a391aee
--- /dev/null
+++ b/pym/portage/package/ebuild/_ipc/__init__.py
@@ -0,0 +1,2 @@
+# Copyright 2010 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
diff --git a/pym/portage/package/ebuild/config.py b/pym/portage/package/ebuild/config.py
index a97dd33ac..3b8ee10b5 100644
--- a/pym/portage/package/ebuild/config.py
+++ b/pym/portage/package/ebuild/config.py
@@ -168,7 +168,7 @@ class config(object):
_environ_whitelist += [
"ACCEPT_LICENSE", "BASH_ENV", "BUILD_PREFIX", "D",
"DISTDIR", "DOC_SYMLINKS_DIR", "EAPI", "EBUILD",
- "EBUILD_EXIT_STATUS_FILE", "EBUILD_FORCE_TEST",
+ "EBUILD_FORCE_TEST",
"EBUILD_PHASE", "ECLASSDIR", "ECLASS_DEPTH", "ED",
"EMERGE_FROM", "EPREFIX", "EROOT",
"FEATURES", "FILESDIR", "HOME", "NOCOLOR", "PATH",
@@ -183,7 +183,7 @@ class config(object):
"PORTAGE_CONFIGROOT", "PORTAGE_DEBUG", "PORTAGE_DEPCACHEDIR",
"PORTAGE_GID", "PORTAGE_GRPNAME",
"PORTAGE_INST_GID", "PORTAGE_INST_UID",
- "PORTAGE_IUSE",
+ "PORTAGE_IPC_DAEMON", "PORTAGE_IUSE",
"PORTAGE_LOG_FILE", "PORTAGE_MASTER_PID",
"PORTAGE_PYM_PATH", "PORTAGE_QUIET",
"PORTAGE_REPO_NAME", "PORTAGE_RESTRICT",
diff --git a/pym/portage/package/ebuild/doebuild.py b/pym/portage/package/ebuild/doebuild.py
index 61eea40ee..7405b6f12 100644
--- a/pym/portage/package/ebuild/doebuild.py
+++ b/pym/portage/package/ebuild/doebuild.py
@@ -213,8 +213,6 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings,
mysettings["PORTAGE_CONFIGROOT"], EBUILD_SH_ENV_FILE)
mysettings["PM_EBUILD_HOOK_DIR"] = os.path.join(
mysettings["PORTAGE_CONFIGROOT"], EBUILD_SH_ENV_DIR)
- mysettings["EBUILD_EXIT_STATUS_FILE"] = os.path.join(
- mysettings["PORTAGE_BUILDDIR"], ".exit_status")
#set up KV variable -- DEP SPEEDUP :: Don't waste time. Keep var persistent.
if not eapi_exports_KV(eapi):
@@ -240,61 +238,6 @@ def doebuild_environment(myebuild, mydo, myroot, mysettings,
(c, style_to_ansi_code(c)))
mysettings["PORTAGE_COLORMAP"] = "\n".join(mycolors)
-def _doebuild_exit_status_check(mydo, settings):
- """
- Returns an error string if the shell appeared
- to exit unsuccessfully, None otherwise.
- """
- exit_status_file = settings.get("EBUILD_EXIT_STATUS_FILE")
- if not exit_status_file or \
- os.path.exists(exit_status_file):
- return None
- msg = _("The ebuild phase '%s' has exited "
- "unexpectedly. This type of behavior "
- "is known to be triggered "
- "by things such as failed variable "
- "assignments (bug #190128) or bad substitution "
- "errors (bug #200313). Normally, before exiting, bash should "
- "have displayed an error message above. If bash did not "
- "produce an error message above, it's possible "
- "that the ebuild has called `exit` when it "
- "should have called `die` instead. This behavior may also "
- "be triggered by a corrupt bash binary or a hardware "
- "problem such as memory or cpu malfunction. If the problem is not "
- "reproducible or it appears to occur randomly, then it is likely "
- "to be triggered by a hardware problem. "
- "If you suspect a hardware problem then you should "
- "try some basic hardware diagnostics such as memtest. "
- "Please do not report this as a bug unless it is consistently "
- "reproducible and you are sure that your bash binary and hardware "
- "are functioning properly.") % mydo
- return msg
-
-def _doebuild_exit_status_check_and_log(settings, mydo, retval):
- msg = _doebuild_exit_status_check(mydo, settings)
- if msg:
- if retval == os.EX_OK:
- retval = 1
- for l in wrap(msg, 72):
- eerror(l, phase=mydo, key=settings.mycpv)
- return retval
-
-def _doebuild_exit_status_unlink(exit_status_file):
- """
- Double check to make sure it really doesn't exist
- and raise an OSError if it still does (it shouldn't).
- OSError if necessary.
- """
- if not exit_status_file:
- return
- try:
- os.unlink(exit_status_file)
- except OSError:
- pass
- if os.path.exists(exit_status_file):
- os.unlink(exit_status_file)
-
-
_doebuild_manifest_cache = None
_doebuild_broken_ebuilds = set()
_doebuild_broken_manifests = set()
@@ -721,8 +664,6 @@ def doebuild(myebuild, mydo, myroot, mysettings, debug=0, listonly=0,
elog_process(mysettings.mycpv, mysettings)
return 1
del env_file, env_stat, saved_env
- else:
- mysettings.pop("EBUILD_EXIT_STATUS_FILE", None)
# if any of these are being called, handle them -- running them out of
# the sandbox -- and stop now.
@@ -1172,14 +1113,6 @@ def spawn(mystring, mysettings, debug=0, free=0, droppriv=0, sesandbox=0, fakero
spawn_func = selinux.spawn_wrapper(spawn_func,
mysettings["PORTAGE_SANDBOX_T"])
- phase = env.get('EBUILD_PHASE')
- if phase not in EbuildSpawnProcess._phases_without_builddir:
- # Don't try to unlink for phases that don't require
- # PORTAGE_BUILDDIR, since the directory may not
- # even belong to this process in that case.
- _doebuild_exit_status_unlink(
- env.get("EBUILD_EXIT_STATUS_FILE"))
-
if keywords.get("returnpid"):
return spawn_func(mystring, env=env, **keywords)
diff --git a/pym/portage/package/ebuild/prepare_build_dirs.py b/pym/portage/package/ebuild/prepare_build_dirs.py
index dc29eeeb8..15e087121 100644
--- a/pym/portage/package/ebuild/prepare_build_dirs.py
+++ b/pym/portage/package/ebuild/prepare_build_dirs.py
@@ -85,6 +85,25 @@ def prepare_build_dirs(myroot, mysettings, cleanup):
writemsg(_("File Not Found: '%s'\n") % str(e), noiselevel=-1)
return 1
+ for x in ('.ipc_in', '.ipc_out'):
+ p = os.path.join(mysettings['PORTAGE_BUILDDIR'], x)
+ st = None
+ try:
+ st = os.lstat(p)
+ except OSError:
+ os.mkfifo(p)
+ else:
+ if not stat.S_ISFIFO(st.st_mode):
+ st = None
+ try:
+ os.unlink(p)
+ except OSError:
+ pass
+ os.mkfifo(p)
+ apply_secpass_permissions(p,
+ uid=portage_uid, gid=portage_gid,
+ mode=0o770, mask=0o2, stat_cached=st)
+
# Reset state for things like noauto and keepwork in FEATURES.
for x in ('.die_hooks',):
try:
diff --git a/pym/portage/tests/ebuild/test_ipc_daemon.py b/pym/portage/tests/ebuild/test_ipc_daemon.py
index 48412a1c3..de548c6f1 100644
--- a/pym/portage/tests/ebuild/test_ipc_daemon.py
+++ b/pym/portage/tests/ebuild/test_ipc_daemon.py
@@ -8,30 +8,11 @@ from portage.tests import TestCase
from portage.const import PORTAGE_BIN_PATH
from portage.const import PORTAGE_PYM_PATH
from portage.const import BASH_BINARY
+from portage.package.ebuild._ipc.ExitCommand import ExitCommand
from _emerge.SpawnProcess import SpawnProcess
from _emerge.EbuildIpcDaemon import EbuildIpcDaemon
from _emerge.TaskScheduler import TaskScheduler
-class ExitCommand(object):
-
- def __init__(self):
- self.reply_hook = None
- self.exitcode = None
-
- def __call__(self, argv):
-
- if self.exitcode is not None:
- # Ignore all but the first call, since if die is called
- # then we certainly want to honor that exitcode, even
- # the ebuild process manages to send a second exit
- # command.
- self.reply_hook = None
- else:
- self.exitcode = int(argv[1])
-
- # (stdout, stderr, returncode)
- return ('', '', 0)
-
class IpcDaemonTestCase(TestCase):
def testIpcDaemon(self):
@@ -58,8 +39,8 @@ class IpcDaemonTestCase(TestCase):
'"$PORTAGE_BIN_PATH"/ebuild-ipc exit %d' % exitcode],
env=env, scheduler=task_scheduler.sched_iface)
def exit_command_callback():
- daemon.cancel()
proc.cancel()
+ daemon.cancel()
exit_command.reply_hook = exit_command_callback
task_scheduler.add(daemon)
task_scheduler.add(proc)