1 # Copyright 1999-2011 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
4 from _emerge.SubProcess import SubProcess
6 from portage.cache.mappings import slot_dict_class
8 from portage import _encodings
9 from portage import _unicode_encode
10 from portage import os
11 from portage.const import BASH_BINARY
16 class SpawnProcess(SubProcess):
19 Constructor keyword args are passed into portage.process.spawn().
20 The required "args" keyword argument will be passed as the first
24 _spawn_kwarg_names = ("env", "opt_name", "fd_pipes",
25 "uid", "gid", "groups", "umask", "logfile",
26 "path_lookup", "pre_exec")
28 __slots__ = ("args",) + \
29 _spawn_kwarg_names + ("_selinux_type",)
31 _file_names = ("log", "process", "stdout")
32 _files_dict = slot_dict_class(_file_names, prefix="")
39 if self.fd_pipes is None:
41 fd_pipes = self.fd_pipes
42 fd_pipes.setdefault(0, sys.stdin.fileno())
43 fd_pipes.setdefault(1, sys.stdout.fileno())
44 fd_pipes.setdefault(2, sys.stderr.fileno())
46 # flush any pending output
47 for fd in fd_pipes.values():
48 if fd == sys.stdout.fileno():
50 if fd == sys.stderr.fileno():
53 self._files = self._files_dict()
56 master_fd, slave_fd = self._pipe(fd_pipes)
57 fcntl.fcntl(master_fd, fcntl.F_SETFL,
58 fcntl.fcntl(master_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
61 if self._can_log(slave_fd):
62 logfile = self.logfile
65 fd_pipes_orig = fd_pipes.copy()
67 # TODO: Use job control functions like tcsetpgrp() to control
68 # access to stdin. Until then, use /dev/null so that any
69 # attempts to read from stdin will immediately return EOF
70 # instead of blocking indefinitely.
71 null_input = open('/dev/null', 'rb')
72 fd_pipes[0] = null_input.fileno()
74 fd_pipes[0] = fd_pipes_orig[0]
76 # WARNING: It is very important to use unbuffered mode here,
77 # in order to avoid issue 5380 with python3.
78 files.process = os.fdopen(master_fd, 'rb', 0)
79 if logfile is not None:
81 fd_pipes[1] = slave_fd
82 fd_pipes[2] = slave_fd
84 files.log = open(_unicode_encode(logfile,
85 encoding=_encodings['fs'], errors='strict'), mode='ab')
86 if logfile.endswith('.gz'):
87 files.log = gzip.GzipFile(filename='', mode='ab',
90 portage.util.apply_secpass_permissions(logfile,
91 uid=portage.portage_uid, gid=portage.portage_gid,
94 if not self.background:
95 files.stdout = os.fdopen(os.dup(fd_pipes_orig[1]), 'wb')
97 output_handler = self._output_handler
101 # Create a dummy pipe so the scheduler can monitor
102 # the process from inside a poll() loop.
103 fd_pipes[self._dummy_pipe_fd] = slave_fd
105 fd_pipes[1] = slave_fd
106 fd_pipes[2] = slave_fd
107 output_handler = self._dummy_handler
110 for k in self._spawn_kwarg_names:
115 kwargs["fd_pipes"] = fd_pipes
116 kwargs["returnpid"] = True
117 kwargs.pop("logfile", None)
119 self._reg_id = self.scheduler.register(files.process.fileno(),
120 self._registered_events, output_handler)
121 self._registered = True
123 retval = self._spawn(self.args, **kwargs)
126 if null_input is not None:
129 if isinstance(retval, int):
132 self._set_returncode((self.pid, retval))
137 portage.process.spawned_pids.remove(self.pid)
139 def _can_log(self, slave_fd):
142 def _pipe(self, fd_pipes):
145 @param fd_pipes: pipes from which to copy terminal size if desired.
149 def _spawn(self, args, **kwargs):
150 spawn_func = portage.process.spawn
152 if self._selinux_type is not None:
153 spawn_func = portage.selinux.spawn_wrapper(spawn_func,
155 # bash is an allowed entrypoint, while most binaries are not
156 if args[0] != BASH_BINARY:
157 args = [BASH_BINARY, "-c", "exec \"$@\"", args[0]] + args
159 return spawn_func(args, **kwargs)
161 def _output_handler(self, fd, event):
164 buf = self._read_buf(files.process, event)
169 if not self.background:
170 write_successful = False
174 if not write_successful:
175 buf.tofile(files.stdout)
176 write_successful = True
180 if e.errno != errno.EAGAIN:
185 # Avoid a potentially infinite loop. In
186 # most cases, the failure count is zero
187 # and it's unlikely to exceed 1.
190 # This means that a subprocess has put an inherited
191 # stdio file descriptor (typically stdin) into
192 # O_NONBLOCK mode. This is not acceptable (see bug
193 # #264435), so revert it. We need to use a loop
194 # here since there's a race condition due to
195 # parallel processes being able to change the
196 # flags on the inherited file descriptor.
197 # TODO: When possible, avoid having child processes
198 # inherit stdio file descriptors from portage
199 # (maybe it can't be avoided with
200 # PROPERTIES=interactive).
201 fcntl.fcntl(files.stdout.fileno(), fcntl.F_SETFL,
202 fcntl.fcntl(files.stdout.fileno(),
203 fcntl.F_GETFL) ^ os.O_NONBLOCK)
206 buf.tofile(files.log)
208 # array.tofile() doesn't work with GzipFile
209 files.log.write(buf.tostring())
215 self._unregister_if_appropriate(event)
217 def _dummy_handler(self, fd, event):
219 This method is mainly interested in detecting EOF, since
220 the only purpose of the pipe is to allow the scheduler to
221 monitor the process from inside a poll() loop.
224 buf = self._read_buf(self._files.process, event)
234 self._unregister_if_appropriate(event)