1
# Copyright (C) 2008 Canonical, Ltd.
3
# This program is free software: you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation, either version 3 of the License, or
6
# (at your option) any later version.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
# GNU General Public License for more details.
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17
import subprocess, sys
22
from dbus.mainloop.glib import DBusGMainLoop
25
process = subprocess.Popen(cmd, stdout=subprocess.PIPE,
26
stderr=subprocess.PIPE, stdin=subprocess.PIPE, shell=True)
27
res = process.communicate()
31
stat = os.statvfs(dev)
32
return stat.f_bsize * stat.f_bavail
35
def __init__(self, frontend):
36
self.source_devices = {}
38
self.frontend = frontend
39
DBusGMainLoop(set_as_default=True)
40
self.bus = dbus.Bus(dbus.Bus.TYPE_SYSTEM)
41
hal_obj = self.bus.get_object('org.freedesktop.Hal',
42
'/org/freedesktop/Hal/Manager')
43
self.hal = dbus.Interface(hal_obj, 'org.freedesktop.Hal.Manager')
45
def set_install_source(self, source):
46
# TODO: Make more consistent with install_target
47
self.install_source = source
49
def set_install_target(self, target):
50
self.install_target = self.devices[target]
52
def device_added(self, udi):
53
dev_obj = self.bus.get_object("org.freedesktop.Hal", udi)
54
dev = dbus.Interface(dev_obj, "org.freedesktop.Hal.Device")
56
# Look for the volume first as it may not have appeared yet when you
58
if dev.PropertyExists('block.is_volume') and dev.GetProperty('block.is_volume'):
59
if (dev.PropertyExists('storage.bus') and
60
dev.GetProperty('storage.bus') == 'usb') and \
61
dev.GetProperty('storage.removable'):
64
p = dev.GetProperty('info.parent')
65
dev_obj = self.bus.get_object('org.freedesktop.Hal', p)
66
d = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
67
if (d.PropertyExists('storage.bus') and # necessary?
68
d.GetProperty('storage.bus') == 'usb') and \
69
d.GetProperty('storage.removable'):
73
dev.connect_to_signal(signal_name="PropertyModified", handler_function=self.property_modified)
75
def property_modified(self, num_changes, changes):
76
# FIXME: useless until we can pass in the udi of the device that's
78
print 'property modified:'
80
print 'change: %s' % str(c[0])
82
def device_removed(self, udi):
84
for device in self.devices.itervalues():
85
if device['udi'] == udi:
86
print 'removing %s' % udi
90
# TODO: Move into the frontend in a generic function.
92
m = self.frontend.dest_combo.get_model()
93
iterator = m.get_iter_first()
94
while iterator is not None:
95
if m.get_value(iterator, 0) == d:
97
iterator = m.iter_next(iterator)
98
if to_delete is not None:
100
def add_device(self, dev):
101
mountpoint = str(dev.GetProperty('volume.mount_point'))
102
device = str(dev.GetProperty('block.device'))
103
self.devices[device] = {
104
'label' : str(dev.GetProperty('volume.label')).replace(' ', '_'),
105
'fstype' : str(dev.GetProperty('volume.fstype')),
106
'uuid' : str(dev.GetProperty('volume.uuid')),
107
'mountpoint' : mountpoint,
108
'udi' : str(dev.GetProperty('info.udi')),
109
'free' : mountpoint and free_space(mountpoint) or 0,
112
print 'new device:\n%s' % self.devices[device]
113
self.frontend.add_dest(device)
115
def detect_devices(self):
116
# TODO: Handle devices with empty partition tables.
117
devices = self.hal.FindDeviceByCapability('storage')
118
for device in devices:
119
dev_obj = self.bus.get_object('org.freedesktop.Hal', device)
120
d = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
121
if d.GetProperty('storage.bus') == 'usb' and \
122
d.GetProperty('storage.removable'):
123
if d.GetProperty('block.is_volume'):
126
children = self.hal.FindDeviceStringMatch('info.parent', device)
127
for child in children:
128
dev_obj = self.bus.get_object('org.freedesktop.Hal', child)
129
child = dbus.Interface(dev_obj, 'org.freedesktop.Hal.Device')
130
if child.GetProperty('block.is_volume'):
131
self.add_device(child)
132
# TODO: should we break here?
134
self.bus.add_signal_receiver(self.device_added,
136
"org.freedesktop.Hal.Manager",
137
"org.freedesktop.Hal",
138
"/org/freedesktop/Hal/Manager")
139
self.bus.add_signal_receiver(self.device_removed,
141
"org.freedesktop.Hal.Manager",
142
"org.freedesktop.Hal",
143
"/org/freedesktop/Hal/Manager")
145
def mount_iso(self, filename):
146
# TODO: This should be automatically picked up by a modified
147
# device_added function that handles source devices in the form of
148
# CD-ROMs and mounted ISO files. It should probably call out to a
149
# separate function for this to ease readability.
150
# Actually, HAL doesn't seem to support loop mounted filesystems and
151
# indeed when manually mounted, the device does not appear in d-feet
152
# (searching the mountpoint). We'll probably have to just manually
153
# construct the addition to self.devices, which probably makes a fair
154
# amount of sense anyway.
156
print 'mounting %s' % filename
157
tmpdir = tempfile.mkdtemp()
158
res = popen('mount -o loop,ro %s %s' % (filename, tmpdir))
159
if res[0].returncode:
160
print 'unable to mount %s to %s' % (filename, tmpdir)
164
fp = open('%s/.disk/info' % tmpdir)
166
line = ' '.join(line.split(' ')[:2])
167
size = os.stat(filename).st_size
168
self.source_devices[filename] = { 'title' : line,
169
'filename' : filename,
171
self.frontend.add_source(filename)
173
print 'Unable to find %s/.disk/info, not using %s' % \
177
popen('umount %s' % tmpdir)
178
popen('rmdir %s' % tmpdir)
181
popen('umount %s' % tmpdir)
182
popen('rmdir %s' % tmpdir)
184
def install_bootloader(self):
185
# TODO: Needs to be moved into the generic install routine.
186
# TODO: mark install_target['device'] as bootable using sfdisk or similar.
187
print 'installing the bootloader to %s.' % self.install_target['device']
188
process = popen('syslinux -s %s' % self.install_target['device'])[0]
189
if process.returncode:
190
print 'Error installing the bootloader (syslinux -s %s)' % self.install_target['device']
192
def copy_files(self):
193
# TODO: md5 on copy as well?
196
# TODO: debate pulling the total_size bit in from ubiquity for more
198
# TODO: Fedora's program shells out to cp and uses another thread that
199
# checks the free space on the drive and updates the UI accordingly.
200
# Is this worth replicating?
201
#cmd = '/home/evan/usb-creator/install.py -s %s -t %s' % (tmpdir, self.install_target['mountpoint'])
202
cmd = 'cp -ra %s/. %s' % (tmpdir, self.install_target['mountpoint'])
205
self.pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
206
#gobject.io_add_watch(self.pipe.stdout,
207
# gobject.IO_IN | gobject.IO_HUP,
208
# self.data_available)
209
#gobject.io_add_watch(self.pipe.stderr,
210
# gobject.IO_IN | gobject.IO_HUP,
211
# self.data_available)
212
# Wait for the process to complete
213
gobject.child_watch_add(self.pipe.pid, self.on_end)
217
if self.install_source.lower().endswith('.iso'):
218
tmpdir = tempfile.mkdtemp()
219
popen('mount -o loop,ro %s %s' % (self.install_source, tmpdir))
221
print 'copying from ISO...'
224
popen('umount %s' % tmpdir)
225
popen('rmdir %s' % tmpdir)
227
tmpdir = tempfile.mkdtemp()
228
popen('mount -o ro %s %s' % (self.install_source, tmpdir))
230
print 'copying from CD...'
233
popen('umount %s' % tmpdir)
234
def on_end(self, pid, error_code):
235
# FIXME: move into install.py
236
popen('rm -rf %s/syslinux' % self.install_target['mountpoint'])
237
popen('mv %s/isolinux %s/syslinux' %
238
(self.install_target['mountpoint'], self.install_target['mountpoint']))
239
popen('mv %s/syslinux/isolinux.cfg %s/syslinux/syslinux.cfg' %
240
(self.install_target['mountpoint'], self.install_target['mountpoint']))
241
print 'error_code %d' % error_code
244
def data_available(self, source, condition):
245
#print 'data_available'
259
print 'data: %s' % data
260
self.frontend.progress(int(data.strip('\n')))
264
#text = source.readline()
266
# #print 'data: %s' % text.strip('\n')
267
# self.frontend.progress(int(text.strip('\n')))
272
def create_persistence_file(self):
273
# FIXME: Needs to get the count size from the frontend. Also needs to tell
274
# the frontend the max size for the count. The minimum should be static,
275
# something like 128 MB.
276
# FIXME: move into install.py
277
popen('dd if=/dev/zero of=%s/casper-rw bs=1M count=128' % self.install_target['mountpoint'])
278
popen('mkfs.ext3 -F %s/casper-rw' % self.install_target['mountpoint'])
279
# add persistence option to syslinux.cfg