1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
# Copyright 2010-2011 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import signal
import traceback
import errno
import fcntl
import portage
from portage import os, StringIO
import portage.elog.messages
from _emerge.PollConstants import PollConstants
from _emerge.SpawnProcess import SpawnProcess
class MergeProcess(SpawnProcess):
"""
Merge packages in a subprocess, so the Scheduler can run in the main
thread while files are moved or copied asynchronously.
"""
__slots__ = ('dblink', 'mycat', 'mypkg', 'settings', 'treetype',
'vartree', 'scheduler', 'blockers', 'pkgloc', 'infloc', 'myebuild',
'mydbapi', 'prev_mtimes', '_elog_reader_fd', '_elog_reg_id',
'_buf')
def _elog_output_handler(self, fd, event):
output = None
if event & PollConstants.POLLIN:
try:
output = os.read(fd, self._bufsize)
except OSError as e:
if e.errno not in (errno.EAGAIN, errno.EINTR):
raise
if output:
lines = output.split('\n')
if len(lines) == 1:
self._buf += lines[0]
else:
lines[0] = self._buf + lines[0]
self._buf = lines.pop()
out = StringIO()
for line in lines:
funcname, phase, key, msg = line.split(' ', 3)
reporter = getattr(portage.elog.messages, funcname)
reporter(msg, phase=phase, key=key, out=out)
def _spawn(self, args, fd_pipes, **kwargs):
"""
Fork a subprocess, apply local settings, and call
dblink.merge().
"""
files = self._files
elog_reader_fd, elog_writer_fd = os.pipe()
fcntl.fcntl(elog_reader_fd, fcntl.F_SETFL,
fcntl.fcntl(elog_reader_fd, fcntl.F_GETFL) | os.O_NONBLOCK)
mylink = self.dblink(self.mycat, self.mypkg, settings=self.settings,
treetype=self.treetype, vartree=self.vartree,
blockers=self.blockers, scheduler=self.scheduler,
pipe=elog_writer_fd)
fd_pipes[elog_writer_fd] = elog_writer_fd
self._elog_reg_id = self.scheduler.register(elog_reader_fd,
self._registered_events, self._elog_output_handler)
pid = os.fork()
if pid != 0:
self._elog_reader_fd = elog_reader_fd
self._buf = ""
portage.process.spawned_pids.append(pid)
return [pid]
os.close(elog_reader_fd)
portage.process._setup_pipes(fd_pipes)
# Use default signal handlers since the ones inherited
# from the parent process are irrelevant here.
signal.signal(signal.SIGINT, signal.SIG_DFL)
signal.signal(signal.SIGTERM, signal.SIG_DFL)
portage.output.havecolor = self.settings.get('NOCOLOR') \
not in ('yes', 'true')
# In this subprocess we want mylink._display_merge() to use
# stdout/stderr directly since they are pipes. This behavior
# is triggered when mylink._scheduler is None.
mylink._scheduler = None
# In this subprocess we don't want PORTAGE_BACKGROUND to
# suppress stdout/stderr output since they are pipes. We
# also don't want to open PORTAGE_LOG_FILE, since it will
# already be opened by the parent process, so we set the
# "subprocess" value for use in conditional logging code
# involving PORTAGE_LOG_FILE.
self.settings["PORTAGE_BACKGROUND"] = "subprocess"
self.settings.backup_changes("PORTAGE_BACKGROUND")
rval = 1
try:
rval = mylink.merge(self.pkgloc, self.infloc,
myebuild=self.myebuild, mydbapi=self.mydbapi,
prev_mtimes=self.prev_mtimes)
except SystemExit:
raise
except:
traceback.print_exc()
finally:
# Call os._exit() from finally block, in order to suppress any
# finally blocks from earlier in the call stack. See bug #345289.
os._exit(rval)
def _unregister(self):
"""
Unregister from the scheduler and close open files.
"""
if self._elog_reg_id is not None:
self.scheduler.unregister(self._elog_reg_id)
self._elog_reg_id = None
if self._elog_reader_fd:
os.close(self._elog_reader_fd)
self._elog_reader_fd = None
super(MergeProcess, self)._unregister()
|