-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Hi everyone,

While auditing the code related to bug 117988, I've noticed that it calls 
portageexit() directly because the normal atexit hooks do not work with the 
os.execv call when portage restarts itself.  Currently, several other atexit 
hooks exist but never get called when portage restarts itself.

Unfortunately, python's atexit module does not have a public interface for 
anything but the register() function (_exithandlers and _run_exitfuncs are 
accessible but undocumented).  In the attached patch, I've copied the private 
code (GPL compatible) directly from /usr/lib/python2.4/atexit.py so that the 
functionality is duplicated in our portage_exec module.  Then I've wrapped all 
relevant access to the atexit module in calls to the new 
portage_exec.atexit_register() function.  The last hunk uses the new function 
portage_exec.run_exitfuncs() to manually call the registered functions prior to 
the os.execv call. Feedback would be appreciated.

Zac
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.2 (GNU/Linux)

iD8DBQFD5vaC/ejvha5XGaMRAmvFAJ90D3b5N6t8YiVjy4Qp0woo/tFm9wCeNwYH
QDCajTK1JPea2fg+AT7WDEs=
=8DuV
-----END PGP SIGNATURE-----
Index: pym/portage_locks.py
===================================================================
--- pym/portage_locks.py	(revision 2671)
+++ pym/portage_locks.py	(working copy)
@@ -4,7 +4,6 @@
 # $Id: /var/cvsroot/gentoo-src/portage/pym/portage_locks.py,v 1.18.2.2 2005/01/16 02:35:33 carpaski Exp $
 
 
-import atexit
 import errno
 import os
 import stat
@@ -12,6 +11,7 @@
 import time
 import types
 import portage_exception
+import portage_exec
 import portage_file
 import portage_util
 import portage_data
@@ -30,7 +30,7 @@
 	if os.path.isdir(mypath):
 		hardlock_path_list = mypath[:]
 
-atexit.register(clean_my_hardlocks)
+portage_exec.atexit_register(clean_my_hardlocks)
 
 def lockdir(mydir):
 	return lockfile(mydir,wantnewlockfile=1)
Index: pym/portage.py
===================================================================
--- pym/portage.py	(revision 2671)
+++ pym/portage.py	(working copy)
@@ -19,7 +19,7 @@
 	raise SystemExit, 127
 
 try:
-	import os,string,types,atexit,signal,fcntl
+	import os,string,types,signal,fcntl
 	import time,cPickle,traceback,copy
 	import re,pwd,grp,commands
 	import shlex,shutil
@@ -6901,7 +6901,7 @@
 		close_portdbapi_caches()
 		commit_mtimedb()
 
-atexit.register(portageexit)
+portage_exec.atexit_register(portageexit)
 
 if (secpass==2) and (not os.environ.has_key("SANDBOX_ACTIVE")):
 	if settings["PORTAGE_CALLER"] in ["emerge","fixpackages"]:
Index: pym/portage_exec.py
===================================================================
--- pym/portage_exec.py	(revision 2671)
+++ pym/portage_exec.py	(working copy)
@@ -39,6 +39,41 @@
 	args.append(mycommand)
 	return spawn(args, opt_name=opt_name, **keywords)
 
+_exithandlers = []
+def atexit_register(func, *args, **kargs):
+	"""Wrapper around atexit.register that is needed in order to track
+	what is registered.  For example, when portage restarts itself via
+	os.execv, the atexit module does not work so we have to do it
+	manually by calling the run_exitfuncs() function in this module."""
+	_exithandlers.append((func, args, kargs))
+
+def run_exitfuncs():
+	"""This is should behave identically to the routine performed by
+	the atexit module at exit time.  It's only necessary to call this
+	function when atexit will not work (because of os.execv, for
+	example)."""
+
+	# This function is a copy of the private atexit._run_exitfuncs()
+	# from the python 2.4.2 sources.  The only difference from the
+	# original function is the string in the print statement.
+	exc_info = None
+	while _exithandlers:
+		func, targs, kargs = _exithandlers.pop()
+		try:
+			func(*targs, **kargs)
+		except SystemExit:
+			exc_info = sys.exc_info()
+		except:
+			import traceback
+			print >> sys.stderr, "Error in portage_exec.run_exitfuncs:"
+			traceback.print_exc()
+			exc_info = sys.exc_info()
+
+	if exc_info is not None:
+		raise exc_info[0], exc_info[1], exc_info[2]
+
+atexit.register(run_exitfuncs)
+
 # We need to make sure that any processes spawned are killed off when
 # we exit. spawn() takes care of adding and removing pids to this list
 # as it creates and cleans up processes.
@@ -55,7 +90,7 @@
 			# of spawn().
 			pass
 
-atexit.register(cleanup)
+atexit_register(cleanup)
 
 def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
           uid=None, gid=None, groups=None, umask=None, logfile=None,
Index: bin/emerge
===================================================================
--- bin/emerge	(revision 2672)
+++ bin/emerge	(working copy)
@@ -9,7 +9,7 @@
 
 import portage
 
-import emergehelp,xpak,string,re,commands,time,shutil,traceback,atexit,signal,socket,types
+import emergehelp,xpak,string,re,commands,time,shutil,traceback,signal,socket,types
 from stat import *
 from output import *
 
@@ -492,7 +492,7 @@
 		emergelog(" *** terminating.")
 	if "notitles" not in portage.features:
 		xtermTitleReset()
-atexit.register(emergeexit)
+portage.portage_exec.atexit_register(emergeexit)
 
 def emergeexitsig(signum, frame):
 	signal.signal(signal.SIGINT, signal.SIG_IGN)
@@ -2119,7 +2119,7 @@
 							if len(mymergelist) > mergecount:
 								emergelog(" *** RESTARTING emerge via exec() after change of portage version.")
 								del portage.mtimedb["resume"]["mergelist"][0]
-								portage.portageexit()
+								portage.portage_exec.run_exitfuncs()
 								mynewargv=["/usr/lib/portage/bin/emerge","--resume"]
 								badlongopts = ("--ask","--tree","--changelog","--skipfirst","--resume")
 								for arg in myopts:

Reply via email to