2 # vim: ai ts=4 sts=4 et sw=4
4 # Copyright (c) 2007 Intel Corporation
6 # This program is free software; you can redistribute it and/or modify it
7 # under the terms of the GNU General Public License as published by the Free
8 # Software Foundation; version 2 of the License
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
12 # or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc., 59
17 # Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19 # This file contains utility functions which do not need to be inside any of
40 # this is for the copySourcesListFile() function
42 CONFIG_DIR
= os
.path
.expanduser("~/.image-creator")
43 if not os
.path
.isdir(CONFIG_DIR
):
44 print _("~/.image-creator/ directory did not exist. Creating")
45 os
.makedirs(CONFIG_DIR
)
47 sources_regex_file
= os
.path
.expanduser(os
.path
.join(CONFIG_DIR
, "sources_cfg"))
48 if os
.path
.isfile(sources_regex_file
):
49 f
= open(sources_regex_file
, "r")
50 mirrorSelectionLine
= f
.readline()
53 execfile(sources_regex_file
, global_dict
)
54 if mirrorSelectionLine
.find("=") != -1:
55 mirrorToUse
= mirrorSelectionLine
.split('=')[1]
56 mirrorToUse
= mirrorToUse
[1:-2]
57 if mirrorToUse
!= "no_mirror":
58 if mirrorToUse
in global_dict
:
59 src_regex
= global_dict
[mirrorToUse
]
61 if 'sources_regex' in global_dict
:
62 src_regex
= global_dict
['sources_regex']
64 if 'sources_regex' in global_dict
:
65 src_regex
= global_dict
['sources_regex']
67 print _("Creating sample %s file") % sources_regex_file
68 out_file
= open(sources_regex_file
, 'w')
69 print >> out_file
, """#!/usr/bin/python
71 # If you have a local mirror of the Ubuntu and/or Moblin.org APT repositories,
72 # then this configuration file will be useful to you.
74 # This file is used when copying the files that will go into
75 # /etc/apt/sources.list.d/ It consists of a list, which contains a search
76 # regular expression and a replacement string. When copying the files into the
77 # /etc/apt/sources.list.d/ , of the projects and targets, a search and replace
81 # source_archive, local mirror of source archive
83 # Edit the following and uncomment them to enable use of a local mirror server.
84 # NOTE: The trailing space is important in the strings!!!!
85 # (r'http://archive.ubuntu.com/ubuntu ', 'http://<PATH_TO_YOUR_LOCAL_MIRROR_OF_ARCHIVES_UBUNTU_COM/ '),
86 # (r'http://ports.ubuntu.com/ubuntu-ports ', 'http://<PATH_TO_YOUR_LOCAL_MIRROR_OF_PORTS_UBUNTU_COM/ '),
87 # (r'http://www.moblin.org/apt ', 'http://<PATH_TO_YOUR_LOCAL_MIRROR_OF_MOBLIN_ORG/ '),
92 # Make sure the file and directory are owned by the user if it is currently
94 userid
= int(mic_cfg
.config
.get("userinfo", "userid"))
95 groupid
= int(mic_cfg
.config
.get("userinfo", "groupid"))
96 for filename
in [ CONFIG_DIR
, sources_regex_file
]:
97 stat_val
= os
.stat(filename
)
98 st_uid
= stat_val
[stat
.ST_UID
]
99 st_gid
= stat_val
[stat
.ST_GID
]
101 if st_uid
== 0 and userid
!= 0:
103 elif st_gid
== 0 and groupid
!= 0:
106 os
.chown(filename
, userid
, groupid
)
109 # Add something to exercise this code
110 print _("USB devices: %s") % get_current_udisks()
113 """Figure out if we are running as root"""
114 sudo
= int(mic_cfg
.config
.get('userinfo', 'sudo'))
115 userid
= int(mic_cfg
.config
.get('userinfo', 'userid'))
116 if sudo
== 0 and userid
!= 0:
120 def get_current_udisks():
122 dirname
= os
.path
.realpath(os
.path
.abspath('/sys/bus/scsi'))
123 work_list
= getUsbDirTree(dirname
)
124 usb_list
= [ x
for x
in work_list
if re
.search(r
'usb', x
) ]
125 for filename
in usb_list
:
126 device_dir
= os
.path
.join('/sys/devices', filename
)
127 if os
.path
.isdir(device_dir
):
128 for device_file
in os
.listdir(device_dir
):
129 full_path
= os
.path
.join(device_dir
, device_file
)
130 result
= re
.search(r
'^block:(?P<dev>.*)', device_file
)
132 usb_dev
= os
.path
.join('/dev', result
.group('dev'))
133 if os
.path
.exists(usb_dev
):
134 usb_devices
.append(usb_dev
)
137 def getUsbDirTree(dirname
):
139 for filename
in os
.listdir(dirname
):
140 full_path
= os
.path
.join(dirname
, filename
)
141 if os
.path
.islink(full_path
):
142 file_set
.add(os
.path
.realpath(full_path
))
143 elif os
.path
.isdir(full_path
):
144 file_set
.update(getUsbDirTree(full_path
))
146 file_set
.add(full_path
)
149 def umountAllInPath(dirname
, directory_set
= None):
150 """Unmount all mounts that are found underneath the dirname specified. On
151 error returns a set containing the list of directories that failed to
153 # Have to add a '/' on the end to prevent /foo/egg and /foo/egg2 being
154 # treated as if both were under /foo/egg
155 if directory_set
== None:
156 directory_set
= set()
157 our_path
= os
.path
.realpath(os
.path
.abspath(dirname
)) + os
.sep
158 mounts
= getMountInfo()
159 for mpoint
in mounts
:
160 fs_type
=mounts
[mpoint
].fs_type
161 mpoint
= os
.path
.realpath(os
.path
.abspath(mpoint
)) + os
.sep
162 if our_path
== mpoint
[:len(our_path
)]:
163 # need save tmpfs to 'real' dir
164 if fs_type
== 'tmpfs':
165 tmp_path
= tempfile
.mkdtemp('','pdk-', '/tmp')
167 shutil
.copytree(mpoint
, tmp_path
)
168 result
= umount(mpoint
)
170 directory_set
.add(os
.path
.abspath(mpoint
))
171 if fs_type
== 'tmpfs':
172 shutil
.rmtree(tmp_path
)
173 elif fs_type
== 'tmpfs':
175 shutil
.move(tmp_path
, mpoint
)
179 """Helper function to un-mount a directory, returns back False if it failed
180 to unmount the directory"""
181 dirname
= os
.path
.abspath(os
.path
.expanduser(dirname
))
182 result
= os
.system("umount %s" % (dirname
))
184 # umount failed, see if the directory is actually mounted, if it isn't
185 # then we think it is okay
186 mounts
= getMountInfo()
187 if dirname
in mounts
:
191 def umount_device(device_file
):
192 """umount a device if it is mounted"""
193 search_file
= "%s " % os
.path
.realpath(os
.path
.abspath(device_file
))
194 mount_file
= open('/proc/mounts', 'r')
195 for line
in mount_file
:
197 if line
.find(search_file
) == 0:
198 print _("Umounting: %s") % device_file
199 return umount(device_file
)
203 """Function to parse the list of mounts and return back the data"""
205 mounts_file
= "/proc/mounts"
206 in_file
= open(mounts_file
, 'r')
209 mount_info
= MountInfo(line
)
210 output
[mount_info
.dirname
] = mount_info
214 """Function to see if a path is mounted, this is because os.path.ismount()
215 does not seem to detect --bind"""
216 path
= os
.path
.realpath(os
.path
.abspath(os
.path
.expanduser(path
)))
219 result
= execCommand(cmd
, quiet
= True, output
= output
)
221 result
= re
.search(r
'(?P<dev>.*) on (?P<mnt_point>.*) type', line
)
223 mnt_point
= result
.group('mnt_point')
224 if mnt_point
== path
:
229 def setblocking(f
, flag
):
230 " set/clear blocking mode"
231 # get the file descriptor
233 # get the file's current flag settings
234 fl
= fcntl
.fcntl(fd
, fcntl
.F_GETFL
)
236 # clear non-blocking mode from flags
237 fl
= fl
& ~os
.O_NONBLOCK
239 # set non-blocking mode from flags
240 fl
= fl | os
.O_NONBLOCK
241 # update the file's flags
242 fcntl
.fcntl(fd
, fcntl
.F_SETFL
, fl
)
244 def execCommand(cmd_line
, quiet
= False, output
= None, callback
= None):
245 if output
== None and callback
== None:
246 p
= subprocess
.Popen(cmd_line
.split())
248 p
= subprocess
.Popen(cmd_line
.split(), stdout
= subprocess
.PIPE
, stderr
= subprocess
.STDOUT
, stdin
= subprocess
.PIPE
, close_fds
= True)
249 # To get the callbacks to work, we need to capture the output
250 if callback
!= None and output
== None:
252 # Don't ever want the process waiting on stdin.
255 # Make the stdout of the subprocess non-blocking, so that we won't
256 # ever hang waiting for it. This way our callback function should
257 # always keep being called.
258 setblocking(p
.stdout
, False)
259 # This is so that we can keep calling our callback
262 poll
.register(p
.stdout
, select
.POLLIN
)
264 # As long as our subprocess returns None, then the subprocess is still
266 while p
.poll() == None:
268 # Only do this if we are capturing output
269 result
= poll
.poll(10)
271 buf
= p
.stdout
.read()
275 sys
.stdout
.write(buf
)
279 # Have to scan for anything remaining in the subprocess stdout
282 buf
= p
.stdout
.read()
287 sys
.stdout
.write(buf
)
288 # Now we have to package up our output
289 for line
in out_buffer
.splitlines():
291 result
= p
.returncode
294 def execChrootCommand(path
, cmd
, output
= None, callback
= None):
295 if not os
.path
.isfile(os
.path
.join(path
, 'bin/bash')):
296 print >> sys
.stderr
, _("Incomplete jailroot at %s") % (path
)
297 raise ValueError(_("Internal Error: Invalid buildroot at %s") % (path
))
300 cmd_line
= "/usr/sbin/chroot %s %s" % (path
, cmd
)
301 result
= execCommand(cmd_line
, output
= output
, callback
= callback
)
303 print _("Error in chroot. Result: %s") % result
304 print _("Command was: %s") % cmd_line
308 def copySourcesListFile(sourcefile
, destfile
):
309 """The purpose of this function is allow the user to be able to point at a
310 local repository for some of the sources, rather than going out over the
312 in_file
= open(sourcefile
, 'r')
313 out_file
= open(destfile
, 'w')
316 if type(src_regex
) == type([]):
317 for regex
, sub
in src_regex
:
318 line
= re
.sub(regex
, sub
, line
)
319 print >> out_file
, line
323 def touchFile(filename
):
324 """Work like the 'touch' command"""
325 if not os
.path
.exists(filename
):
326 basedir
= os
.path
.dirname(filename
)
327 if not os
.path
.exists(basedir
):
329 out_file
= open(filename
, 'w')
331 os
.utime(filename
, None)
333 def rmtree(path
, ignore_errors
=False, onerror
=None, callback
= None, count
= 0):
334 """Recursively delete a directory tree.
336 This function was copied from the shutil.py library in Python 2.5.1. The
337 license for Python is GPL compatible so it is okay to use it. The reason
338 for creating a custom version of this is so that we can have a callback
339 function. This way our throbber won't die for long periods of time when
340 deleting a large directory tree.
342 If ignore_errors is set, errors are ignored; otherwise, if onerror
343 is set, it is called to handle the error with arguments (func,
344 path, exc_info) where func is os.listdir, os.remove, or os.rmdir;
345 path is the argument to that function that caused it to fail; and
346 exc_info is a tuple returned by sys.exc_info(). If ignore_errors
347 is false and onerror is None, an exception is raised.
352 elif onerror
is None:
357 names
= os
.listdir(path
)
358 except os
.error
, err
:
359 onerror(os
.listdir
, path
, sys
.exc_info())
362 # For every 200 files deleted, lets call our callback
363 if count
% 200 == 0 and callback
:
365 fullname
= os
.path
.join(path
, name
)
367 mode
= os
.lstat(fullname
).st_mode
370 if stat
.S_ISDIR(mode
):
371 rmtree(fullname
, ignore_errors
, onerror
, callback
= callback
, count
= count
)
375 except os
.error
, err
:
376 onerror(os
.remove
, fullname
, sys
.exc_info())
380 onerror(os
.rmdir
, path
, sys
.exc_info())
382 def copy(src
, dst
, callback
= None):
384 timer
= gobject
.timeout_add(100, callback
, None)
388 shutil
.copy(src
, dst
)
391 pid
, exit_status
= os
.waitpid(pid
, os
.WNOHANG
)
396 gobject
.source_remove(timer
)
399 # Something failed, try again without the fork
400 shutil
.copy(src
, dst
)
402 shutil
.copy(src
, dst
)
404 def safeTextFileCopy(source_file
, destination_file
, force
= False):
405 """Routine which attempts to safely copy a text file. This means that if
406 we have the destination file and it has our signature text in it, then we
407 will overwrite it. But if the signature text is not in the destination
408 file then we will not overwrite it"""
409 id_string
= _("# ##-Created by Moblin Image Creator: if this line exists we will overwrite this file -##")
411 if os
.path
.isfile(source_file
):
412 if not os
.path
.isfile(destination_file
) or force
:
415 in_file
= open(destination_file
, 'r')
418 if re
.search(r
'^' + id_string
, line
):
423 in_file
= open(source_file
, 'r')
424 out_file
= open(destination_file
, 'w')
425 print >> out_file
, id_string
431 def mountList(mount_list
, chroot_dir
):
432 """Mount the items specified in the mount list. Return back a list of what
434 # We want to keep a list of everything we mount, so that we can use it in
437 mounts
= getMountInfo()
438 for mnt_type
, host_dirname
, target_dirname
, fs_type
, device
in mount_list
:
439 # If didn't specify target_dirname then use the host_dirname path,
440 # but we have to remove the leading '/'
441 if not target_dirname
and host_dirname
:
442 target_dirname
= re
.sub(r
'^' + os
.sep
, '', host_dirname
)
443 # Do the --bind mount types
444 if mnt_type
== "bind":
445 path
= os
.path
.join(chroot_dir
, target_dirname
)
446 mounted_list
.append(path
)
447 if not os
.path
.isdir(path
):
449 if not ismount(path
) and os
.path
.isdir(host_dirname
):
450 result
= os
.system('mount --bind %s %s' % (host_dirname
, path
))
452 raise OSError(_("Internal error while attempting to bind mount /%s!") % (host_dirname
))
453 # Mimic host mounts, if possible
454 elif mnt_type
== 'host':
455 if host_dirname
in mounts
:
456 mount_info
= mounts
[host_dirname
]
457 fs_type
= mount_info
.fs_type
458 device
= mount_info
.device
459 options
= "-o %s" % mount_info
.options
462 path
= os
.path
.join(chroot_dir
, target_dirname
)
463 mounted_list
.append(path
)
464 if not os
.path
.isdir(path
):
466 if not ismount(path
):
467 cmd
= 'mount %s -t %s %s %s' % (options
, fs_type
, device
, path
)
468 result
= execCommand(cmd
)
470 raise OSError(_("Internal error while attempting to mount %s %s!") % (host_dirname
, target_dirname
))
473 class MountInfo(object):
474 def __init__(self
, mount_line
):
475 """Input is in the form that is found in /etc/mtab (or /proc/mounts)"""
476 mount_line
= mount_line
.strip()
477 result
= mount_line
.split()
478 self
.device
= result
[0]
479 self
.dirname
= result
[1]
480 self
.fs_type
= result
[2]
481 self
.options
= result
[3]
483 return ("%s %s %s %s" % (self
.device
, self
.dirname
, self
.fs_type
, self
.options
))
485 return "MountInfo('%s')" % self
.__str
__()
487 # An exception class for Image Creator
488 class ImageCreatorError(exceptions
.Exception):
489 def __init__(self
, args
=None):
493 class ImageCreatorUmountError(ImageCreatorError
):
494 def __init__(self
, directory_set
= set()):
495 self
.directory_set
= directory_set
497 def getAllProcesses():
498 """Return back a dictionary of all the currently running processes on the
502 for filename
in sorted(os
.listdir(dirname
)):
503 full_path
= os
.path
.join(dirname
, filename
)
504 if os
.path
.isdir(full_path
) and re
.search(r
'^[0-9]+$', filename
):
506 in_file
= open(os
.path
.join(full_path
, "status"))
509 result
= re
.search(r
'^ppid:\t(?P<ppid>.*)', line
, re
.I
)
511 ppid
= int(result
.group('ppid'))
512 processes
.setdefault(ppid
, []).append(pid
)
517 def findChildren(pid
, process_dict
= None):
518 """Find all the children of PID (Process ID). Does not return back PID"""
521 process_dict
= getAllProcesses()
522 if pid
in process_dict
:
523 for child_pid
in process_dict
[pid
]:
524 output
.append(child_pid
)
525 output
.extend(findChildren(child_pid
, process_dict
))
529 def signalChildren(pid
, send_signal
, process_dict
= None, parent_first
= True):
530 """Send the signal 'send_signal' to the parent and all of its children
531 parent_first = Send the signal to the parent before signaling the
534 process_dict
= getAllProcesses()
535 if pid
in process_dict
:
537 print _("Sending signal: %s to PID: %s") % (send_signal
, pid
)
539 os
.kill(pid
, send_signal
)
542 for child_pid
in process_dict
[pid
]:
543 signalChildren(child_pid
, send_signal
= send_signal
,
544 process_dict
= process_dict
, parent_first
= parent_first
)
546 print _("Sending signal: %s to PID: %s") % (send_signal
, pid
)
548 os
.kill(pid
, send_signal
)
552 if '__main__' == __name__
: