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