]> git.ipfire.org Git - pakfire.git/blame - python/pakfire/chroot.py
Add cgroup support.
[pakfire.git] / python / pakfire / chroot.py
CommitLineData
93bd0aa4 1#!/usr/bin/python
b792d887
MT
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###############################################################################
93bd0aa4 21
93bd0aa4
MT
22import fcntl
23import os
24import select
25import subprocess
26import time
27
ed4a9455 28from _pakfire import PERSONALITY_LINUX, PERSONALITY_LINUX32
90a630f9 29
ed4a9455
MT
30import pakfire.util as util
31from errors import *
93bd0aa4
MT
32
33def logOutput(fds, logger, returnOutput=1, start=0, timeout=0):
34 output=""
35 done = 0
36
37 # set all fds to nonblocking
38 for fd in fds:
39 flags = fcntl.fcntl(fd, fcntl.F_GETFL)
40 if not fd.closed:
41 fcntl.fcntl(fd, fcntl.F_SETFL, flags| os.O_NONBLOCK)
42
43 tail = ""
44 while not done:
17fef974 45 if (time.time() - start) > timeout and timeout != 0:
93bd0aa4
MT
46 done = 1
47 break
48
17fef974
MT
49 i_rdy, o_rdy, e_rdy = select.select(fds,[],[],1)
50
93bd0aa4
MT
51 for s in i_rdy:
52 # slurp as much input as is ready
53 input = s.read()
17fef974 54
93bd0aa4
MT
55 if input == "":
56 done = 1
57 break
17fef974 58
93bd0aa4
MT
59 if logger is not None:
60 lines = input.split("\n")
61 if tail:
62 lines[0] = tail + lines[0]
17fef974 63
93bd0aa4
MT
64 # we may not have all of the last line
65 tail = lines.pop()
17fef974 66
93bd0aa4 67 for line in lines:
93bd0aa4 68 logger.info(line)
17fef974 69
93bd0aa4
MT
70 for h in logger.handlers:
71 h.flush()
17fef974 72
93bd0aa4
MT
73 if returnOutput:
74 output += input
17fef974 75
93bd0aa4
MT
76 if tail and logger is not None:
77 logger.info(tail)
17fef974 78
93bd0aa4
MT
79 return output
80
81
2b6cc06d 82def do(command, shell=False, chrootPath=None, cwd=None, timeout=0, raiseExc=True, returnOutput=0, personality=None, logger=None, env=None, cgroup=None, *args, **kargs):
93bd0aa4
MT
83 # Save the output of command
84 output = ""
85
86 # Save time when command was started
87 start = time.time()
88
89 # Create preexecution thingy for command
90 preexec = ChildPreExec(personality, chrootPath, cwd)
91
92 if logger:
93 logger.debug("Executing command: %s in %s" % (command, chrootPath or "/"))
94
95 try:
93bd0aa4
MT
96 # Create new child process
97 child = subprocess.Popen(
c07a3ca7 98 command,
93bd0aa4
MT
99 shell=shell,
100 bufsize=0, close_fds=True,
101 stdin=open("/dev/null", "r"),
102 stdout=subprocess.PIPE,
103 stderr=subprocess.PIPE,
104 preexec_fn = preexec,
105 env=env
106 )
107
2b6cc06d
MT
108 # If cgroup is given, attach the subprocess.
109 if cgroup:
110 cgroup.attach_task(child.pid)
111
93bd0aa4
MT
112 # use select() to poll for output so we dont block
113 output = logOutput([child.stdout, child.stderr], logger, returnOutput, start, timeout)
114
115 except:
116 # kill children if they aren't done
117 if child and child.returncode is None:
118 os.killpg(child.pid, 9)
119 try:
120 if child:
121 os.waitpid(child.pid, 0)
122 except:
123 pass
124 raise
125
126 # wait until child is done, kill it if it passes timeout
127 niceExit=1
128 while child.poll() is None:
129 if (time.time() - start) > timeout and timeout != 0:
130 niceExit = 0
131 os.killpg(child.pid, 15)
132 if (time.time() - start) > (timeout+1) and timeout != 0:
133 niceExit = 0
134 os.killpg(child.pid, 9)
135
136 if not niceExit:
137 raise commandTimeoutExpired, ("Timeout(%s) expired for command:\n # %s\n%s" % (timeout, command, output))
138
139 if logger:
140 logger.debug("Child returncode was: %s" % str(child.returncode))
141
142 if raiseExc and child.returncode:
143 if returnOutput:
144 raise Error, ("Command failed: \n # %s\n%s" % (command, output), child.returncode)
145 else:
146 raise Error, ("Command failed. See logs for output.\n # %s" % (command,), child.returncode)
147
148 return output
149
150class ChildPreExec(object):
151 def __init__(self, personality, chrootPath, cwd):
152 self._personality = personality
153 self.chrootPath = chrootPath
154 self.cwd = cwd
155
156 @property
157 def personality(self):
158 """
159 Return personality value if supported.
160 Otherwise return None.
161 """
93bd0aa4 162 personality_defs = {
ed4a9455
MT
163 "linux64": PERSONALITY_LINUX,
164 "linux32": PERSONALITY_LINUX32,
93bd0aa4
MT
165 }
166
167 try:
168 return personality_defs[self._personality]
169 except KeyError:
170 pass
171
172 def __call__(self, *args, **kargs):
173 # Set a new process group
174 os.setpgrp()
175
176 # Set new personality if we got one.
177 if self.personality:
ed4a9455 178 util.personality(self.personality)
93bd0aa4
MT
179
180 # Change into new root.
181 if self.chrootPath:
182 os.chdir(self.chrootPath)
183 os.chroot(self.chrootPath)
184
185 # Change to cwd.
186 if self.cwd:
c07a3ca7
MT
187 if not os.path.exists(self.cwd):
188 os.makedirs(self.cwd)
189
93bd0aa4 190 os.chdir(self.cwd)