Use the cached portage.getpid() function to avoid unnecessary syscalls,
and update the cache after each call to os.fork() where the fork may
invoke portage APIs.

Bug: https://bugs.gentoo.org/739540
Signed-off-by: Zac Medico <zmed...@gentoo.org>
---
 bin/quickpkg                                     |  2 +-
 lib/_emerge/EbuildBinpkg.py                      |  4 +++-
 lib/_emerge/actions.py                           |  2 +-
 lib/portage/_emirrordist/FetchTask.py            |  4 ++--
 lib/portage/cache/metadata.py                    |  4 +++-
 lib/portage/elog/mod_mail_summary.py             |  5 ++---
 lib/portage/elog/mod_save_summary.py             |  2 +-
 lib/portage/locks.py                             |  2 +-
 lib/portage/package/ebuild/doebuild.py           |  2 +-
 lib/portage/process.py                           | 16 +++++++++++-----
 lib/portage/tests/locks/test_lock_nonblock.py    |  1 +
 .../futures/asyncio/test_wakeup_fd_sigchld.py    |  2 +-
 lib/portage/util/__init__.py                     |  4 ++--
 lib/portage/util/_eventloop/EventLoop.py         |  6 +++---
 .../util/_eventloop/asyncio_event_loop.py        |  4 ++--
 lib/portage/util/_eventloop/global_event_loop.py |  7 +++----
 lib/portage/util/locale.py                       |  1 +
 lib/portage/util/movefile.py                     |  2 +-
 lib/portage/util/socks5.py                       |  2 +-
 lib/portage/xpak.py                              |  2 +-
 20 files changed, 42 insertions(+), 32 deletions(-)

diff --git a/bin/quickpkg b/bin/quickpkg
index be7d1d7af..a171b3bd5 100755
--- a/bin/quickpkg
+++ b/bin/quickpkg
@@ -111,7 +111,7 @@ def quickpkg_atom(options, infos, arg, eout):
                                vardb.aux_update(cpv, update_metadata)
                        xpdata = xpak.xpak(dblnk.dbdir)
                        binpkg_tmpfile = os.path.join(bintree.pkgdir,
-                               cpv + ".tbz2." + str(os.getpid()))
+                               cpv + ".tbz2." + str(portage.getpid()))
                        ensure_dirs(os.path.dirname(binpkg_tmpfile))
                        binpkg_compression = settings.get("BINPKG_COMPRESS", 
"bzip2")
                        try:
diff --git a/lib/_emerge/EbuildBinpkg.py b/lib/_emerge/EbuildBinpkg.py
index 6e098eb8a..879b3a9aa 100644
--- a/lib/_emerge/EbuildBinpkg.py
+++ b/lib/_emerge/EbuildBinpkg.py
@@ -3,6 +3,8 @@
 
 from _emerge.CompositeTask import CompositeTask
 from _emerge.EbuildPhase import EbuildPhase
+
+import portage
 from portage import os
 
 class EbuildBinpkg(CompositeTask):
@@ -17,7 +19,7 @@ class EbuildBinpkg(CompositeTask):
                root_config = pkg.root_config
                bintree = root_config.trees["bintree"]
                binpkg_tmpfile = os.path.join(bintree.pkgdir,
-                       pkg.cpv + ".tbz2." + str(os.getpid()))
+                       pkg.cpv + ".tbz2." + str(portage.getpid()))
                bintree._ensure_dir(os.path.dirname(binpkg_tmpfile))
 
                self._binpkg_tmpfile = binpkg_tmpfile
diff --git a/lib/_emerge/actions.py b/lib/_emerge/actions.py
index 063f5d4a0..a4ecfe43d 100644
--- a/lib/_emerge/actions.py
+++ b/lib/_emerge/actions.py
@@ -2623,7 +2623,7 @@ def ionice(settings):
        if not ionice_cmd:
                return
 
-       variables = {"PID" : str(os.getpid())}
+       variables = {"PID" : str(portage.getpid())}
        cmd = [varexpand(x, mydict=variables) for x in ionice_cmd]
 
        try:
diff --git a/lib/portage/_emirrordist/FetchTask.py 
b/lib/portage/_emirrordist/FetchTask.py
index 457ca2ac6..41f96b962 100644
--- a/lib/portage/_emirrordist/FetchTask.py
+++ b/lib/portage/_emirrordist/FetchTask.py
@@ -415,7 +415,7 @@ class FetchTask(CompositeTask):
                        self._fetch_tmp_dir_info = 'distfiles'
                        distdir = self.config.options.distfiles
 
-               tmp_basename = self.distfile + '._emirrordist_fetch_.%s' % 
os.getpid()
+               tmp_basename = self.distfile + '._emirrordist_fetch_.%s' % 
portage.getpid()
 
                variables = {
                        "DISTDIR": distdir,
@@ -622,7 +622,7 @@ class FetchTask(CompositeTask):
 
                head, tail = os.path.split(dest)
                hardlink_tmp = os.path.join(head, 
".%s._mirrordist_hardlink_.%s" % \
-                       (tail, os.getpid()))
+                       (tail, portage.getpid()))
 
                try:
                        try:
diff --git a/lib/portage/cache/metadata.py b/lib/portage/cache/metadata.py
index db81b8ba1..bd1b70fa0 100644
--- a/lib/portage/cache/metadata.py
+++ b/lib/portage/cache/metadata.py
@@ -6,6 +6,8 @@ import errno
 import re
 import stat
 from operator import attrgetter
+
+import portage
 from portage import os
 from portage import _encodings
 from portage import _unicode_encode
@@ -122,7 +124,7 @@ class database(flat_hash.database):
 
                s = cpv.rfind("/")
                fp = os.path.join(self.location,cpv[:s],
-                       ".update.%i.%s" % (os.getpid(), cpv[s+1:]))
+                       ".update.%i.%s" % (portage.getpid(), cpv[s+1:]))
                try:
                        myf = open(_unicode_encode(fp,
                                encoding=_encodings['fs'], errors='strict'), 
'wb')
diff --git a/lib/portage/elog/mod_mail_summary.py 
b/lib/portage/elog/mod_mail_summary.py
index ac260880e..789f55f4d 100644
--- a/lib/portage/elog/mod_mail_summary.py
+++ b/lib/portage/elog/mod_mail_summary.py
@@ -6,7 +6,6 @@ import portage
 from portage.exception import AlarmSignal, PortageException
 from portage.localization import _
 from portage.util import writemsg
-from portage import os
 from portage import _encodings
 from portage import _unicode_decode
 
@@ -22,7 +21,7 @@ def process(mysettings, key, logentries, fulltext):
                time.strftime("%Y%m%d-%H%M%S %Z", time.localtime(time.time())),
                encoding=_encodings['content'], errors='replace')
        header = _(">>> Messages generated for package %(pkg)s by process 
%(pid)d on %(time)s:\n\n") % \
-               {"pkg": key, "pid": os.getpid(), "time": time_str}
+               {"pkg": key, "pid": portage.getpid(), "time": time_str}
        config_root = mysettings["PORTAGE_CONFIGROOT"]
 
        # Copy needed variables from the config instance,
@@ -66,7 +65,7 @@ def _finalize(mysettings, items):
        mysubject = mysubject.replace("${HOST}", socket.getfqdn())
 
        mybody = _("elog messages for the following packages generated by "
-               "process %(pid)d on host %(host)s:\n") % {"pid": os.getpid(), 
"host": socket.getfqdn()}
+               "process %(pid)d on host %(host)s:\n") % {"pid": 
portage.getpid(), "host": socket.getfqdn()}
        for key in items:
                mybody += "- %s\n" % key
 
diff --git a/lib/portage/elog/mod_save_summary.py 
b/lib/portage/elog/mod_save_summary.py
index 946a1ad4c..6e24b608f 100644
--- a/lib/portage/elog/mod_save_summary.py
+++ b/lib/portage/elog/mod_save_summary.py
@@ -79,7 +79,7 @@ def process(mysettings, key, logentries, fulltext):
                encoding=_encodings['content'], errors='replace')
        elogfile.write(_(">>> Messages generated by process "
                "%(pid)d on %(time)s for package %(pkg)s:\n\n") %
-               {"pid": os.getpid(), "time": time_str, "pkg": key})
+               {"pid": portage.getpid(), "time": time_str, "pkg": key})
        elogfile.write(_unicode_decode(fulltext))
        elogfile.write("\n")
        elogfile.close()
diff --git a/lib/portage/locks.py b/lib/portage/locks.py
index 701093024..1073343be 100644
--- a/lib/portage/locks.py
+++ b/lib/portage/locks.py
@@ -501,7 +501,7 @@ def unlockfile(mytuple):
 def hardlock_name(path):
        base, tail = os.path.split(path)
        return os.path.join(base, ".%s.hardlock-%s-%s" %
-               (tail, portage._decode_argv([os.uname()[1]])[0], os.getpid()))
+               (tail, portage._decode_argv([os.uname()[1]])[0], 
portage.getpid()))
 
 def hardlink_is_mine(link, lock):
        try:
diff --git a/lib/portage/package/ebuild/doebuild.py 
b/lib/portage/package/ebuild/doebuild.py
index 7bb942966..3b1991b28 100644
--- a/lib/portage/package/ebuild/doebuild.py
+++ b/lib/portage/package/ebuild/doebuild.py
@@ -1178,7 +1178,7 @@ def doebuild(myebuild, mydo, _unused=DeprecationWarning, 
settings=None, debug=0,
                                        bintree = 
portage.db[mysettings['EROOT']]['bintree']
                                        mysettings["PORTAGE_BINPKG_TMPFILE"] = \
                                                
bintree.getname(mysettings.mycpv) + \
-                                               ".%s" % (os.getpid(),)
+                                               ".%s" % (portage.getpid(),)
                                        bintree._ensure_dir(os.path.dirname(
                                                
mysettings["PORTAGE_BINPKG_TMPFILE"]))
                                else:
diff --git a/lib/portage/process.py b/lib/portage/process.py
index 8d4cf164e..48548bacc 100644
--- a/lib/portage/process.py
+++ b/lib/portage/process.py
@@ -79,11 +79,11 @@ if _fd_dir is not None:
                                        raise
                                return range(max_fd_limit)
 
-elif os.path.isdir("/proc/%s/fd" % os.getpid()):
+elif os.path.isdir("/proc/%s/fd" % portage.getpid()):
        # In order for this function to work in forked subprocesses,
        # os.getpid() must be called from inside the function.
        def get_open_fds():
-               return (int(fd) for fd in os.listdir("/proc/%s/fd" % 
os.getpid())
+               return (int(fd) for fd in os.listdir("/proc/%s/fd" % 
portage.getpid())
                        if fd.isdigit())
 
 else:
@@ -363,12 +363,13 @@ def spawn(mycommand, env=None, opt_name=None, 
fd_pipes=None, returnpid=False,
        # fork, so that the result is cached in the main process.
        bool(groups)
 
-       parent_pid = os.getpid()
+       parent_pid = portage.getpid()
        pid = None
        try:
                pid = os.fork()
 
                if pid == 0:
+                       portage._ForkWatcher.hook(portage._ForkWatcher)
                        try:
                                _exec(binary, mycommand, opt_name, fd_pipes,
                                        env, gid, groups, uid, umask, cwd, 
pre_exec, close_fds,
@@ -386,7 +387,9 @@ def spawn(mycommand, env=None, opt_name=None, 
fd_pipes=None, returnpid=False,
                                sys.stderr.flush()
 
        finally:
-               if pid == 0 or (pid is None and os.getpid() != parent_pid):
+               # Don't used portage.getpid() here, due to a race with the above
+               # portage._ForkWatcher cache update.
+               if pid == 0 or (pid is None and _os.getpid() != parent_pid):
                        # Call os._exit() from a finally block in order
                        # to suppress any finally blocks from earlier
                        # in the call stack (see bug #345289). This
@@ -603,7 +606,7 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
        # it is done before we start forking children
        if cgroup:
                with open(os.path.join(cgroup, 'cgroup.procs'), 'a') as f:
-                       f.write('%d\n' % os.getpid())
+                       f.write('%d\n' % portage.getpid())
 
        # Unshare (while still uid==0)
        if unshare_net or unshare_ipc or unshare_mount or unshare_pid:
@@ -640,6 +643,9 @@ def _exec(binary, mycommand, opt_name, fd_pipes,
                                                if unshare_pid:
                                                        main_child_pid = 
os.fork()
                                                        if main_child_pid == 0:
+                                                               # The 
portage.getpid() cache may need to be updated here,
+                                                               # in case the 
pre_exec function invokes portage APIs.
+                                                               
portage._ForkWatcher.hook(portage._ForkWatcher)
                                                                # pid namespace 
requires us to become init
                                                                binary, myargs 
= portage._python_interpreter, [
                                                                        
portage._python_interpreter,
diff --git a/lib/portage/tests/locks/test_lock_nonblock.py 
b/lib/portage/tests/locks/test_lock_nonblock.py
index 02ba16ad9..3448b84f6 100644
--- a/lib/portage/tests/locks/test_lock_nonblock.py
+++ b/lib/portage/tests/locks/test_lock_nonblock.py
@@ -19,6 +19,7 @@ class LockNonblockTestCase(TestCase):
                        lock1 = portage.locks.lockfile(path)
                        pid = os.fork()
                        if pid == 0:
+                               portage._ForkWatcher.hook(portage._ForkWatcher)
                                portage.locks._close_fds()
                                 # Disable close_fds since we don't exec
                                 # (see _setup_pipes docstring).
diff --git a/lib/portage/tests/util/futures/asyncio/test_wakeup_fd_sigchld.py 
b/lib/portage/tests/util/futures/asyncio/test_wakeup_fd_sigchld.py
index e5b104e0f..c37a6338b 100644
--- a/lib/portage/tests/util/futures/asyncio/test_wakeup_fd_sigchld.py
+++ b/lib/portage/tests/util/futures/asyncio/test_wakeup_fd_sigchld.py
@@ -39,7 +39,7 @@ proc = 
loop.run_until_complete(asyncio.create_subprocess_exec('sleep', '0', loop
 loop.run_until_complete(proc.wait())
 
 for i in range(8192):
-       os.kill(os.getpid(), signal.SIGCHLD)
+       os.kill(portage.getpid(), signal.SIGCHLD)
 
 # Verify that the child watcher still works correctly
 # (this will hang if it doesn't).
diff --git a/lib/portage/util/__init__.py b/lib/portage/util/__init__.py
index c14c15fe8..0412b2b59 100644
--- a/lib/portage/util/__init__.py
+++ b/lib/portage/util/__init__.py
@@ -1266,7 +1266,7 @@ class atomic_ofstream(ObjectProxy):
                if follow_links:
                        canonical_path = os.path.realpath(filename)
                        object.__setattr__(self, '_real_name', canonical_path)
-                       tmp_name = "%s.%i" % (canonical_path, os.getpid())
+                       tmp_name = "%s.%i" % (canonical_path, portage.getpid())
                        try:
                                object.__setattr__(self, '_file',
                                        open_func(_unicode_encode(tmp_name,
@@ -1281,7 +1281,7 @@ class atomic_ofstream(ObjectProxy):
                                # new error if necessary.
 
                object.__setattr__(self, '_real_name', filename)
-               tmp_name = "%s.%i" % (filename, os.getpid())
+               tmp_name = "%s.%i" % (filename, portage.getpid())
                object.__setattr__(self, '_file',
                        open_func(_unicode_encode(tmp_name,
                                encoding=_encodings['fs'], errors='strict'),
diff --git a/lib/portage/util/_eventloop/EventLoop.py 
b/lib/portage/util/_eventloop/EventLoop.py
index 94e637853..ff2b73255 100644
--- a/lib/portage/util/_eventloop/EventLoop.py
+++ b/lib/portage/util/_eventloop/EventLoop.py
@@ -188,7 +188,7 @@ class EventLoop:
                self._sigchld_read = None
                self._sigchld_write = None
                self._sigchld_src_id = None
-               self._pid = os.getpid()
+               self._pid = portage.getpid()
                self._asyncio_wrapper = _PortageEventLoop(loop=self)
                self._asyncio_child_watcher = _PortageChildWatcher(self)
 
@@ -431,7 +431,7 @@ class EventLoop:
                # If this signal handler was not installed by the
                # current process then the signal doesn't belong to
                # this EventLoop instance.
-               if os.getpid() == self._pid:
+               if portage.getpid() == self._pid:
                        os.write(self._sigchld_write, b'\0')
 
        def _sigchld_io_cb(self, fd, events):
@@ -1026,7 +1026,7 @@ class EventLoop:
                        log_lines.append('{}: {}'.format(key, value))
 
                logging.error('\n'.join(log_lines), exc_info=exc_info)
-               os.kill(os.getpid(), signal.SIGTERM)
+               os.kill(portage.getpid(), signal.SIGTERM)
 
        def call_exception_handler(self, context):
                """
diff --git a/lib/portage/util/_eventloop/asyncio_event_loop.py 
b/lib/portage/util/_eventloop/asyncio_event_loop.py
index 605e7243b..836f1c30a 100644
--- a/lib/portage/util/_eventloop/asyncio_event_loop.py
+++ b/lib/portage/util/_eventloop/asyncio_event_loop.py
@@ -69,7 +69,7 @@ class AsyncioEventLoop(_AbstractEventLoop):
                        # in order to ensure that emerge exits immediately 
(though
                        # uncleanly).
                        signal.signal(signal.SIGTERM, signal.SIG_DFL)
-                       os.kill(os.getpid(), signal.SIGTERM)
+                       os.kill(portage.getpid(), signal.SIGTERM)
 
        def _create_future(self):
                """
@@ -117,7 +117,7 @@ class AsyncioEventLoop(_AbstractEventLoop):
                        self._wakeup_fd = -1
                        # Account for any signals that may have arrived between
                        # set_wakeup_fd calls.
-                       os.kill(os.getpid(), signal.SIGCHLD)
+                       os.kill(portage.getpid(), signal.SIGCHLD)
                try:
                        return self._loop.run_until_complete(future)
                finally:
diff --git a/lib/portage/util/_eventloop/global_event_loop.py 
b/lib/portage/util/_eventloop/global_event_loop.py
index 1db958d2e..21a1d1970 100644
--- a/lib/portage/util/_eventloop/global_event_loop.py
+++ b/lib/portage/util/_eventloop/global_event_loop.py
@@ -1,13 +1,12 @@
 # Copyright 2012-2020 Gentoo Authors
 # Distributed under the terms of the GNU General Public License v2
 
-import os
-
+import portage
 from .EventLoop import EventLoop
 from portage.util._eventloop.asyncio_event_loop import AsyncioEventLoop
 
 
-_MAIN_PID = os.getpid()
+_MAIN_PID = portage.getpid()
 _instances = {}
 
 
@@ -17,7 +16,7 @@ def global_event_loop():
        belongs exclusively to the current process.
        """
 
-       pid = os.getpid()
+       pid = portage.getpid()
        instance = _instances.get(pid)
        if instance is not None:
                return instance
diff --git a/lib/portage/util/locale.py b/lib/portage/util/locale.py
index 99c8f7ae7..58c687139 100644
--- a/lib/portage/util/locale.py
+++ b/lib/portage/util/locale.py
@@ -102,6 +102,7 @@ def check_locale(silent=False, env=None):
 
        pid = os.fork()
        if pid == 0:
+               portage._ForkWatcher.hook(portage._ForkWatcher)
                try:
                        if env is not None:
                                try:
diff --git a/lib/portage/util/movefile.py b/lib/portage/util/movefile.py
index 4f8054f29..a251d369d 100644
--- a/lib/portage/util/movefile.py
+++ b/lib/portage/util/movefile.py
@@ -209,7 +209,7 @@ def movefile(src, dest, newmtime=None, sstat=None, 
mysettings=None,
        if hardlink_candidates:
                head, tail = os.path.split(dest)
                hardlink_tmp = os.path.join(head, ".%s._portage_merge_.%s" % \
-                       (tail, os.getpid()))
+                       (tail, portage.getpid()))
                try:
                        os.unlink(hardlink_tmp)
                except OSError as e:
diff --git a/lib/portage/util/socks5.py b/lib/portage/util/socks5.py
index 9f22c1dbe..a76d1a741 100644
--- a/lib/portage/util/socks5.py
+++ b/lib/portage/util/socks5.py
@@ -42,7 +42,7 @@ class ProxyManager:
                portage.util.ensure_dirs(tmpdir, **ensure_dirs_kwargs)
 
                self.socket_path = os.path.join(tmpdir,
-                               '.portage.%d.net.sock' % os.getpid())
+                               '.portage.%d.net.sock' % portage.getpid())
                server_bin = os.path.join(settings['PORTAGE_BIN_PATH'], 
'socks5-server.py')
                spawn_kwargs = {}
                # The portage_uid check solves EPERM failures in Travis CI.
diff --git a/lib/portage/xpak.py b/lib/portage/xpak.py
index 2a4bcda21..9063c4c56 100644
--- a/lib/portage/xpak.py
+++ b/lib/portage/xpak.py
@@ -325,7 +325,7 @@ class tbz2:
                self.scan() # Don't care about condition... We'll rewrite the 
data anyway.
 
                if break_hardlinks and self.filestat and self.filestat.st_nlink 
> 1:
-                       tmp_fname = "%s.%d" % (self.file, os.getpid())
+                       tmp_fname = "%s.%d" % (self.file, portage.getpid())
                        copyfile(self.file, tmp_fname)
                        try:
                                portage.util.apply_stat_permissions(self.file, 
self.filestat)
-- 
2.25.3


Reply via email to