handle shell redirects
[bootmanager.git] / source / utils.py
1 #!/usr/bin/python
2 #
3 # $Id$
4 # $URL$
5 #
6 # Copyright (c) 2003 Intel Corporation
7 # All rights reserved.
8 #
9 # Copyright (c) 2004-2006 The Trustees of Princeton University
10 # All rights reserved.
11 # expected /proc/partitions format
12
13 import os, sys, shutil
14 import subprocess
15 import shlex
16 import socket
17 import fcntl
18 import string
19 import exceptions
20
21 from Exceptions import *
22
23
24 ### handling breakpoints in the startup process
25 import select, sys, string
26
27 ### global debug settings
28 # NOTE. when BREAKPOINT_MODE turns out enabled,
29 # you have to attend the boot phase, that would hang otherwise 
30
31 # enabling this will cause the node to ask for breakpoint-mode at startup
32 # production code should read False/False
33 PROMPT_MODE=False
34 # default for when prompt is turned off, or it's on but the timeout triggers
35 BREAKPOINT_MODE=False
36 VERBOSE_MODE=False
37 VERBOSE_MODE=True
38 # in seconds : if no input, proceed
39 PROMPT_TIMEOUT=5
40
41 def prompt_for_breakpoint_mode ():
42
43     global BREAKPOINT_MODE
44     if PROMPT_MODE:
45         default_answer=BREAKPOINT_MODE
46         answer=''
47         if BREAKPOINT_MODE:
48             display="[y]/n"
49         else:
50             display="y/[n]"
51         sys.stdout.write ("Want to run in breakpoint mode ? %s "%display)
52         sys.stdout.flush()
53         r,w,e = select.select ([sys.stdin],[],[],PROMPT_TIMEOUT)
54         if r:
55             answer = string.strip(sys.stdin.readline())
56         else:
57             sys.stdout.write("\nTimed-out (%d s)"%PROMPT_TIMEOUT)
58         if answer:
59             BREAKPOINT_MODE = ( answer == "y" or answer == "Y")
60         else:
61             BREAKPOINT_MODE = default_answer
62     label="Off"
63     if BREAKPOINT_MODE:
64         label="On"
65     sys.stdout.write("\nCurrent BREAKPOINT_MODE is %s\n"%label)
66
67 def breakpoint (message, cmd = None):
68
69     if BREAKPOINT_MODE:
70
71         if cmd is None:
72             cmd="/bin/sh"
73             message=message+" -- Entering bash - type ^D to proceed"
74
75         print message
76         os.system(cmd)
77
78
79 ########################################
80 def makedirs( path ):
81     """
82     from python docs for os.makedirs:
83     Throws an error exception if the leaf directory
84     already exists or cannot be created.
85
86     That is real useful. Instead, we'll create the directory, then use a
87     separate function to test for its existance.
88
89     Return 1 if the directory exists and/or has been created, a BootManagerException
90     otherwise. Does not test the writability of said directory.
91     """
92     try:
93         os.makedirs( path )
94     except OSError:
95         pass
96     try:
97         os.listdir( path )
98     except OSError:
99         raise BootManagerException, "Unable to create directory tree: %s" % path
100     
101     return 1
102
103
104
105 def removedir( path ):
106     """
107     remove a directory tree, return 1 if successful, a BootManagerException
108     if failure.
109     """
110     try:
111         os.listdir( path )
112     except OSError:
113         return 1
114
115     try:
116         shutil.rmtree( path )
117     except OSError, desc:
118         raise BootManagerException, "Unable to remove directory tree: %s" % path
119     
120     return 1
121
122
123
124 def sysexec( cmd, log= None, fsck = False ):
125     """
126     execute a system command, output the results to the logger
127     if log <> None
128
129     return 1 if command completed (return code of non-zero),
130     0 if failed. A BootManagerException is raised if the command
131     was unable to execute or was interrupted by the user with Ctrl+C
132     """
133     if VERBOSE_MODE:
134         print ("sysexec >>> %s" % cmd)
135
136     try:
137         if cmd.__contains__(">"):
138             prog = subprocess.Popen(shlex.split(cmd), shell=True)
139         else:
140             prog = subprocess.Popen(shlex.split(cmd), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
141     except OSError:
142         raise BootManagerException, \
143               "Unable to create instance of subprocess.Popen " \
144               "for command: %s" % cmd
145     try:
146         (stdoutdata, stderrdata) = prog.communicate()
147     except KeyboardInterrupt:
148         raise BootManagerException, "Interrupted by user"
149
150     if log is not None:
151         log.write(stdoutdata)
152
153     returncode = prog.wait()
154
155     if fsck:
156        # The exit code returned by fsck is the sum of the following conditions:
157        #      0    - No errors
158        #      1    - File system errors corrected
159        #      2    - System should be rebooted
160        #      4    - File system errors left uncorrected
161        #      8    - Operational error
162        #      16   - Usage or syntax error
163        #      32   - Fsck canceled by user request
164        #      128  - Shared library error
165        if returncode != 0 and returncode != 1:
166             raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode)
167     else:
168         if returncode != 0:
169             raise BootManagerException, "Running %s failed (rc=%d)" % (cmd,returncode)
170
171     prog = None
172     return 1
173
174
175 def sysexec_chroot( path, cmd, log= None ):
176     """
177     same as sysexec, but inside a chroot
178     """
179     preload = ""
180     release = os.uname()[2]
181     # 2.6.12 kernels need this
182     if release[:5] == "2.6.1":
183         library = "%s/lib/libc-opendir-hack.so" % path
184         if not os.path.exists(library):
185             shutil.copy("./libc-opendir-hack.so", library)
186         preload = "/bin/env LD_PRELOAD=/lib/libc-opendir-hack.so"
187     sysexec("chroot %s %s %s" % (path, preload, cmd), log)
188
189
190 def sysexec_chroot_noerr( path, cmd, log= None ):
191     """
192     same as sysexec_chroot, but capture boot manager exceptions
193     """
194     try:
195         rc= 0
196         rc= syexec_chroot( cmd, log )
197     except BootManagerException, e:
198         pass
199
200     return rc
201
202
203 def sysexec_noerr( cmd, log= None ):
204     """
205     same as sysexec, but capture boot manager exceptions
206     """
207     try:
208         rc= 0
209         rc= sysexec( cmd, log )
210     except BootManagerException, e:
211         pass
212
213     return rc
214
215
216
217 def chdir( dir ):
218     """
219     change to a directory, return 1 if successful, a BootManagerException if failure
220     """
221     try:
222         os.chdir( dir )
223     except OSError:
224         raise BootManagerException, "Unable to change to directory: %s" % dir
225
226     return 1
227
228
229
230 def removefile( filepath ):
231     """
232     removes a file, return 1 if successful, 0 if failure
233     """
234     try:
235         os.remove( filepath )
236     except OSError:
237         raise BootManagerException, "Unable to remove file: %s" % filepath
238
239     return 1
240
241
242
243 # from: http://forums.devshed.com/archive/t-51149/
244 #              Ethernet-card-address-Through-Python-or-C
245
246 def hexy(n):
247     return "%02x" % (ord(n))
248
249 def get_mac_from_interface(ifname):
250     """
251     given a device name, like eth0, return its mac_address.
252     return None if the device doesn't exist.
253     """
254     
255     SIOCGIFHWADDR = 0x8927 # magic number
256
257     s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
258     ifname = string.strip(ifname)
259     ifr = ifname + '\0'*(32-len(ifname))
260
261     try:
262         r= fcntl.ioctl(s.fileno(),SIOCGIFHWADDR,ifr)
263         addr = map(hexy,r[18:24])
264         ret = (':'.join(map(str, addr)))
265     except IOError, e:
266         ret = None
267         
268     return ret
269
270 def check_file_hash(filename, hash_filename):
271     """Check the file's integrity with a given hash."""
272     return sha1_file(filename) == open(hash_filename).read().split()[0].strip()
273
274 def sha1_file(filename):
275     """Calculate sha1 hash of file."""
276     try:
277         try:
278             import hashlib
279             m = hashlib.sha1()
280         except:
281             import sha
282             m=sha.new()
283         f = file(filename, 'rb')
284         while True:
285             # 256 KB seems ideal for speed/memory tradeoff
286             # It wont get much faster with bigger blocks, but
287             # heap peak grows
288             block = f.read(256 * 1024)
289             if len(block) == 0:
290                 # end of file
291                 break
292             m.update(block)
293             # Simple trick to keep total heap even lower
294             # Delete the previous block, so while next one is read
295             # we wont have two allocated blocks with same size
296             del block
297         return m.hexdigest()
298     except IOError:
299         raise BootManagerException, "Cannot calculate SHA1 hash of %s" % filename