]> git.ipfire.org Git - pakfire.git/blob - pakfire/chroot.py
macros: Move make_*_targets into build section.
[pakfire.git] / pakfire / chroot.py
1 #!/usr/bin/python
2 ###############################################################################
3 # #
4 # Pakfire - The IPFire package management system #
5 # Copyright (C) 2011 Pakfire development team #
6 # #
7 # This program is free software: you can redistribute it and/or modify #
8 # it under the terms of the GNU General Public License as published by #
9 # the Free Software Foundation, either version 3 of the License, or #
10 # (at your option) any later version. #
11 # #
12 # This program is distributed in the hope that it will be useful, #
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of #
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
15 # GNU General Public License for more details. #
16 # #
17 # You should have received a copy of the GNU General Public License #
18 # along with this program. If not, see <http://www.gnu.org/licenses/>. #
19 # #
20 ###############################################################################
21
22 import ctypes
23 import fcntl
24 import os
25 import select
26 import subprocess
27 import time
28
29 from errors import *
30
31 _libc = ctypes.cdll.LoadLibrary(None)
32 _libc.personality.argtypes = [ctypes.c_ulong]
33 _libc.personality.restype = ctypes.c_int
34
35 def logOutput(fds, logger, returnOutput=1, start=0, timeout=0):
36 output=""
37 done = 0
38
39 # set all fds to nonblocking
40 for fd in fds:
41 flags = fcntl.fcntl(fd, fcntl.F_GETFL)
42 if not fd.closed:
43 fcntl.fcntl(fd, fcntl.F_SETFL, flags| os.O_NONBLOCK)
44
45 tail = ""
46 while not done:
47 if (time.time() - start)>timeout and timeout!=0:
48 done = 1
49 break
50
51 i_rdy,o_rdy,e_rdy = select.select(fds,[],[],1)
52 for s in i_rdy:
53 # slurp as much input as is ready
54 input = s.read()
55 if input == "":
56 done = 1
57 break
58 if logger is not None:
59 lines = input.split("\n")
60 if tail:
61 lines[0] = tail + lines[0]
62 # we may not have all of the last line
63 tail = lines.pop()
64 for line in lines:
65 if line == '': continue
66 logger.info(line)
67 for h in logger.handlers:
68 h.flush()
69 if returnOutput:
70 output += input
71 if tail and logger is not None:
72 logger.info(tail)
73 return output
74
75
76 def do(command, shell=False, chrootPath=None, cwd=None, timeout=0, raiseExc=True, returnOutput=0, personality=None, logger=None, env=None, *args, **kargs):
77 # Save the output of command
78 output = ""
79
80 # Save time when command was started
81 start = time.time()
82
83 # Create preexecution thingy for command
84 preexec = ChildPreExec(personality, chrootPath, cwd)
85
86 if logger:
87 logger.debug("Executing command: %s in %s" % (command, chrootPath or "/"))
88
89 try:
90 child = None
91
92 # Create new child process
93 child = subprocess.Popen(
94 command,
95 shell=shell,
96 bufsize=0, close_fds=True,
97 stdin=open("/dev/null", "r"),
98 stdout=subprocess.PIPE,
99 stderr=subprocess.PIPE,
100 preexec_fn = preexec,
101 env=env
102 )
103
104 # use select() to poll for output so we dont block
105 output = logOutput([child.stdout, child.stderr], logger, returnOutput, start, timeout)
106
107 except:
108 # kill children if they aren't done
109 if child and child.returncode is None:
110 os.killpg(child.pid, 9)
111 try:
112 if child:
113 os.waitpid(child.pid, 0)
114 except:
115 pass
116 raise
117
118 # wait until child is done, kill it if it passes timeout
119 niceExit=1
120 while child.poll() is None:
121 if (time.time() - start) > timeout and timeout != 0:
122 niceExit = 0
123 os.killpg(child.pid, 15)
124 if (time.time() - start) > (timeout+1) and timeout != 0:
125 niceExit = 0
126 os.killpg(child.pid, 9)
127
128 if not niceExit:
129 raise commandTimeoutExpired, ("Timeout(%s) expired for command:\n # %s\n%s" % (timeout, command, output))
130
131 if logger:
132 logger.debug("Child returncode was: %s" % str(child.returncode))
133
134 if raiseExc and child.returncode:
135 if returnOutput:
136 raise Error, ("Command failed: \n # %s\n%s" % (command, output), child.returncode)
137 else:
138 raise Error, ("Command failed. See logs for output.\n # %s" % (command,), child.returncode)
139
140 return output
141
142 class ChildPreExec(object):
143 def __init__(self, personality, chrootPath, cwd):
144 self._personality = personality
145 self.chrootPath = chrootPath
146 self.cwd = cwd
147
148 @property
149 def personality(self):
150 """
151 Return personality value if supported.
152 Otherwise return None.
153 """
154 # taken from sys/personality.h
155 personality_defs = {
156 "linux64": 0x0000,
157 "linux32": 0x0008,
158 }
159
160 try:
161 return personality_defs[self._personality]
162 except KeyError:
163 pass
164
165 def __call__(self, *args, **kargs):
166 # Set a new process group
167 os.setpgrp()
168
169 # Set new personality if we got one.
170 if self.personality:
171 res = _libc.personality(self.personality)
172 if res == -1:
173 raise OSError, "Could not set personality"
174
175 # Change into new root.
176 if self.chrootPath:
177 os.chdir(self.chrootPath)
178 os.chroot(self.chrootPath)
179
180 # Change to cwd.
181 if self.cwd:
182 if not os.path.exists(self.cwd):
183 os.makedirs(self.cwd)
184
185 os.chdir(self.cwd)