aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPapoteur <papoteur@mageialinux-online.org>2015-03-14 14:52:17 +0100
committerPapoteur <papoteur@mageialinux-online.org>2015-03-14 14:52:17 +0100
commitf9b60a181d5f18eb7a49acbbe419a78a045a0504 (patch)
treee521c7d908cb95c67135e48737664941121af090
parent466ebd63db47c4abdfde7a014531b7273a5d2635 (diff)
downloadisodumper-f9b60a181d5f18eb7a49acbbe419a78a045a0504.tar
isodumper-f9b60a181d5f18eb7a49acbbe419a78a045a0504.tar.gz
isodumper-f9b60a181d5f18eb7a49acbbe419a78a045a0504.tar.bz2
isodumper-f9b60a181d5f18eb7a49acbbe419a78a045a0504.tar.xz
isodumper-f9b60a181d5f18eb7a49acbbe419a78a045a0504.zip
Add version with yui interface
-rw-r--r--yui/isodumper.py1041
1 files changed, 1041 insertions, 0 deletions
diff --git a/yui/isodumper.py b/yui/isodumper.py
new file mode 100644
index 0000000..2fc64a3
--- /dev/null
+++ b/yui/isodumper.py
@@ -0,0 +1,1041 @@
+#coding:utf-8
+
+#!/usr/bin/python
+#
+# Copyright (c) 2007-2009 Canonical Ltd.
+#
+# Author: Oliver Grawert <ogra@ubuntu.com>
+#
+# Modifications 2013 from papoteur <papoteur@mageialinux-online.org>
+# and Geiger David <david.david@mageialinux-online.org>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2 of the
+# License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software Foundation,
+# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
+
+# Requires python-parted
+
+# ensure we're using the latest build, if called from our build environment
+import sys
+
+sys.path.insert(0,'../../../build/swig/python')
+reload(sys)
+sys.setdefaultencoding("utf-8")
+###########
+# imports #
+###########
+import yui
+#import locale
+import time
+
+####################################
+# LOCALE (important for TERMINAL!) #
+####################################
+# set the locale to de/utf-8
+#locale.setlocale(locale.LC_ALL, "")
+#log = yui.YUILog.instance()
+#log.setLogFileName("debug.log")
+#log.enableDebugLogging( True )
+
+import os, re
+import io
+import gettext
+from gettext import gettext as _
+from subprocess import call, Popen, PIPE
+import dbus
+
+
+class NoUDisks2(Exception):
+ pass
+
+
+class UDisks2(object):
+
+ BLOCK = 'org.freedesktop.UDisks2.Block'
+ FILESYSTEM = 'org.freedesktop.UDisks2.Filesystem'
+ DRIVE = 'org.freedesktop.UDisks2.Drive'
+
+ def __init__(self):
+ self.bus = dbus.SystemBus()
+ try:
+ self.bus.get_object('org.freedesktop.UDisks2',
+ '/org/freedesktop/UDisks2')
+ except dbus.exceptions.DBusException as e:
+ if getattr(e, '_dbus_error_name', None) == 'org.freedesktop.DBus.Error.ServiceUnknown':
+ raise NoUDisks2()
+ raise
+
+ def node_mountpoint(self,node):
+
+ def de_mangle(raw):
+ return raw.replace('\\040', ' ').replace('\\011', '\t').replace('\\012',
+ '\n').replace('\\0134', '\\')
+
+ for line in open('/proc/mounts').readlines():
+ line = line.split()
+ if line[0] == node:
+ return de_mangle(line[1])
+ return None
+
+ def device(self, device_node_path):
+ device_node_path = os.path.realpath(device_node_path)
+ devname = device_node_path.split('/')[-1]
+
+ # First we try a direct object path
+ bd = self.bus.get_object('org.freedesktop.UDisks2',
+ '/org/freedesktop/UDisks2/block_devices/%s'%devname)
+ try:
+ device = bd.Get(self.BLOCK, 'Device',
+ dbus_interface='org.freedesktop.DBus.Properties')
+ device = bytearray(device).replace(b'\x00', b'').decode('utf-8')
+ except:
+ device = None
+
+ if device == device_node_path:
+ return bd
+
+ # Enumerate all devices known to UDisks2
+ devs = self.bus.get_object('org.freedesktop.UDisks2',
+ '/org/freedesktop/UDisks2/block_devices')
+ xml = devs.Introspect(dbus_interface='org.freedesktop.DBus.Introspectable')
+ for dev in re.finditer(r'name=[\'"](.+?)[\'"]', type(u'')(xml)):
+ bd = self.bus.get_object('org.freedesktop.UDisks2',
+ '/org/freedesktop/UDisks2/block_devices/%s2'%dev.group(1))
+ try:
+ device = bd.Get(self.BLOCK, 'Device',
+ dbus_interface='org.freedesktop.DBus.Properties')
+ device = bytearray(device).replace(b'\x00', b'').decode('utf-8')
+ except:
+ device = None
+ if device == device_node_path:
+ return bd
+
+ raise ValueError(_('%r not known to UDisks2')%device_node_path)
+
+ def find_devices(self):
+ proxy = self.bus.get_object("org.freedesktop.UDisks2", "/org/freedesktop/UDisks2")
+ iface = dbus.Interface(proxy, "org.freedesktop.UDisks2")
+ _udisks2_obj_manager = dbus.Interface(iface, "org.freedesktop.DBus.ObjectManager")
+ objects=_udisks2_obj_manager.GetManagedObjects()
+ re_drive = re.compile('(?P<path>.*?/drives/(?P<id>.*))')
+ re_block = re.compile('(?P<path>.*?/block_devices/(?P<id>.*))')
+ devs= [m.groupdict() for m in
+ [re_drive.match(path) for path in objects.keys()]
+ if m]
+ blocks = [m.groupdict() for m in
+ [re_block.match(path) for path in objects.keys()]
+ if m]
+ list=[]
+
+ for dev in devs:
+ dev_obj =objects[dev['path']]['org.freedesktop.UDisks2.Drive']
+ if (dev_obj['ConnectionBus'] == 'usb' or dev_obj['ConnectionBus'] == 'sdio') and \
+ (dev_obj['Removable'] == 1 or dev_obj['MediaRemovable'] == 1 ):
+ item=[]
+ vend = dev_obj['Vendor']
+ name = dev_obj['Model']
+ for block in blocks:
+ if dev['path'] == objects[block['path']]['org.freedesktop.UDisks2.Block']['Drive']:
+ path = '/dev/'+block['path'].split('/')[-1]
+ size = dev_obj['Size']
+ item.append(vend+" "+name)
+ item.append(path)
+ item.append(size)
+ list.append(item)
+ return list
+
+ def mount(self, device_node_path):
+ try:
+ d = self.device(device_node_path)
+ mount_options = ['rw', 'noexec', 'nosuid',
+ 'nodev', 'uid=%d'%os.geteuid(), 'gid=%d'%os.getegid()]
+ r=d.Mount(
+ {
+ 'auth.no_user_interaction':True,
+ 'options':','.join(mount_options)
+ },
+ dbus_interface=self.FILESYSTEM)
+ return unicode(r)
+ except:
+ # May be already mounted, check
+ mp = self.node_mountpoint(str(device_node_path))
+ print mp, sys.exc_info()[0]
+ if mp is None:
+ raise ValueError(sys.exc_info()[0])
+ return mp
+
+ def unmount(self, device_node_path):
+ d = self.device(device_node_path)
+ d.Unmount({'force':True, 'auth.no_user_interaction':True},
+ dbus_interface=self.FILESYSTEM)
+
+ def drive_for_device(self, device):
+ drive = device.Get(self.BLOCK, 'Drive',
+ dbus_interface='org.freedesktop.DBus.Properties')
+ return self.bus.get_object('org.freedesktop.UDisks2', drive)
+
+ def eject(self, device_node_path):
+ drive = self.drive_for_device(self.device(device_node_path))
+ drive.Eject({'auth.no_user_interaction':True},
+ dbus_interface=self.DRIVE)
+
+def countFiles(directory):
+ files = []
+ if os.path.isdir(directory):
+ for path, dirs, filenames in os.walk(directory):
+ files.extend(filenames)
+ return len(files)
+
+def makedirs(dest):
+ if not os.path.exists(dest):
+ os.makedirs(dest)
+
+class Info(object):
+ def __init__(self,title,richtext,text):
+ self.title=title
+ self.richtext=richtext
+ self.text=text
+
+
+class IsoDumper(object):
+
+# self.backup_select = self.wTree.get_widget("backup_select")
+# self.backup_name = self.wTree.get_widget("backup_name")
+# self.backup_button = self.wTree.get_widget("backup_button")
+# self.choose = self.wTree.get_widget("choose")
+# self.backup_bname = self.wTree.get_widget("bname")
+#
+
+ # set callbacks
+# dict = { "on_main_dialog_destroy_event" : self.confirm_close,
+# "on_main_dialog_delete_event" : self.confirm_close,
+# "on_cancel_button_clicked" : self.confirm_close,
+# "on_emergency_button_clicked" : self.restore,
+# "on_confirm_cancel_button_clicked": self.restore,
+# "on_filechooserbutton_file_set" : self.activate_devicelist,
+# "on_device_combobox_changed" : self.device_selected,
+# "on_nodev_close_clicked" : self.close,
+# "on_backup_button_clicked" : self.backup_go,
+# "on_backup_select_clicked" : self.backup_sel,
+# "on_select_clicked" : self.backup_choosed,
+# "on_choose_cancel_clicked" : self.backup_cancel,
+# "on_format_button_clicked" : self.format_dialog,
+# "on_format_cancel_clicked" : self.format_cancel,
+# "on_format_go_clicked" : self.do_format,
+# "on_write_button_clicked" : self.do_write,
+# "on_help_close_clicked": self.help_close,
+# "on_help_clicked": self.help_dialog,
+# }
+# self.wTree.signal_autoconnect(dict)
+#
+# self.window.show_all()
+ # make sure we have a target device
+
+ def update_list(self ):
+ self.devicelist.deleteAllItems()
+ self.get_devices()
+ self.restore()
+
+ def device_selected(self):
+ self.dev = self.devicelist.selectedItem().label()
+ if self.dev != None:
+ for name, path, size in self.list:
+ if self.dev.startswith(name):
+ self.deviceSize=size
+ self.device_name=name.rstrip().replace(' ', '')
+ break
+ self.backupbt.setEnabled()
+ self.formatbt.setEnabled()
+ self.ima.setEnabled()
+# if self.chooser.get_current_folder_uri() == None :
+# self.chooser.set_current_folder_uri('file:///home/'+self.user)
+ self.logger(_('Target Device: ')+ self.dev)
+
+ def backup_sel(self,widget):
+ if self.backup_bname.get_current_folder_uri() == None :
+ self.backup_bname.set_current_folder_uri('file:///home/'+self.user)
+ self.backup_bname.set_current_name(self.device_name+".img")
+ self.choose.run()
+
+ def backup_cancel(self,widget):
+ self.choose.hide()
+ # Unckeck the choice to backup
+ self.backupbt.setDisabled()
+
+ def backup_choosed(self, widget):
+ exit_dialog=self.backup_bname.get_filename()
+ if exit_dialog != None:
+ # Add .img if not specified
+ if not exit_dialog.lower().endswith('.img'):
+ exit_dialog=exit_dialog+".img"
+ head, tail = os.path.split(exit_dialog)
+ self.backup_dest=exit_dialog
+ self.backup_select.set_label(tail)
+ self.backup_button.setEnabled()
+ self.backup_select.set_tooltip_text(exit_dialog)
+ self.logger(_('Backup in: ')+ exit_dialog)
+ expander = self.wTree.get_widget("detail_expander")
+ expander.setEnabled()
+ self.choose.hide()
+
+ def format_dialog(self,widget):
+ self.backup_select.setDisabled()
+ format_button=self.wTree.get_widget("format_button")
+ format_button.setDisabled()
+ filechooserbutton=self.wTree.get_widget("filechooserbutton")
+ filechooserbutton.setDisabled()
+ write_button = self.wTree.get_widget("write_button")
+ write_button.setDisabled()
+ self.devicelist.setDisabled()
+ dialog=self.wTree.get_widget("format")
+ self.wTree.get_widget("format_device").set_text(self.dev)
+ self.wTree.get_widget("format_name").set_text(self.dev.split('(')[0])
+ exit_dialog=dialog.run()
+ if exit_dialog==0:
+ dialog.hide()
+
+ def do_format(self,format_type,name) :
+ target = self.dev.split('(')[1].split(')')[0]
+ info = Info(_("Formatting confirmation"),1,self.warning)
+ if self.ask_YesOrNo(info):
+ rc=self.raw_format(target, format_type, name)
+ self.operation=False
+ if rc == 0:
+ message = _('The device was formatted successfully.')
+ self.logger(message)
+ self.success()
+ elif rc == 5:
+ message = _("An error occurred while creating a partition.")
+ self.logger(message)
+ self.emergency(message)
+ elif rc == 127:
+ message = _('Authentication error.')
+ self.logger(message)
+ self.emergency(message)
+ else:
+ message = _('An error occurred.')
+ self.emergency(message)
+
+ def restore(self):
+ print "restore"
+ self.backup_select.setEnabled()
+ self.backuptbt.setDisabled()
+ self.formatbt.setEnabled()
+ self.ima.setEnabled()
+ self.devicelist.setEnabled()
+ self.progress.setLabel("")
+ self.progress.setValue(0)
+ self.progress.setDisabled()
+ print "restored"
+
+ def raw_format(self, usb_path, fstype, label):
+ self.operation=True
+ if os.geteuid() > 0:
+ launcher='pkexec'
+ self.process = Popen([launcher,'/usr/bin/python', '-u', '/usr/lib/isodumper/raw_format.py','-d',usb_path,'-f',fstype, '-l', label, '-u', str(os.geteuid()), '-g', str(os.getgid())], shell=False, stdout=PIPE, preexec_fn=os.setsid)
+ else:
+ self.process = Popen(['/usr/bin/python', '-u', '/usr/lib/isodumper/raw_format.py','-d',usb_path,'-f',fstype, '-l', label, '-u', str(os.geteuid()), '-g', str(os.getgid())], shell=False, stdout=PIPE, preexec_fn=os.setsid)
+ working=True
+ while working:
+ time.sleep(0.5)
+ self.process.poll()
+ rc=self.process.returncode
+ if rc is None:
+ working=True
+ else:
+ self.process = None
+ working= False
+ return rc
+
+ def format_cancel(self, widget):
+ dialog=self.wTree.get_widget("format")
+ dialog.hide()
+ self.backup_select.setEnabled()
+ format_button=self.wTree.get_widget("format_button")
+ filechooserbutton=self.wTree.get_widget("filechooserbutton")
+ format_button.setEnabled()
+ filechooserbutton.setEnabled()
+ self.devicelist.setEnabled()
+
+ def backup_go(self):
+ dest = self.backup_img_name
+ info = Info(_("Backup confirmation"),1,_("Do you want to overwrite the file?"))
+ if os.path.exists(dest):
+ if not(self.ask_YesOrNo(info)):
+ self.returncode = 1
+ return True
+ st = os.statvfs(os.path.dirname(dest))
+ free = st.f_bavail * st.f_frsize
+ if free<self.deviceSize :
+ sizeM=str(self.deviceSize/(1024*1024))
+ message=_("The destination directory is too small to receive the backup (%s Mb needed)")%(sizeM)
+ self.logger(message)
+ self.emergency(message)
+ else:
+ self.returncode=0
+ source = self.dev.split('(')[1].split(')')[0]
+ self.logger(_('Backup in:')+' '+dest)
+ self.raw_write(source, dest, self.deviceSize)
+ if self.returncode==0:
+ self.success()
+
+ def do_write(self):
+# write_button = self.wTree.get_widget("write_button")
+ self.writebt.setDisabled()
+ self.devicelist.setDisabled()
+ self.formatbt.setDisabled()
+ self.backupbt.setDisabled()
+ self.backup_select.setDisabled()
+ self.progress.setEnabled()
+ source = self.img_name
+ target = self.dev.split('(')[1].split(')')[0]
+ self.logger(_('Image: ')+source)
+ self.logger(_('Target Device: ')+self.dev)
+ b = os.path.getsize(source)
+ if b > (self.deviceSize):
+ message = _('The device is too small to contain the ISO file.')
+ self.logger(message)
+ self.emergency(message)
+ else:
+ info = Info(_("Writing confirmation"),1,self.warning)
+ if self.ask_YesOrNo(info):
+ if self.deviceSize> 1024*1024*1024*32 :
+ info = Info(_("Warning"),2,_('The device is bigger than 32 Gbytes.\
+ Are you sure you want use it?'))
+ if self.ask_YesOrNo(info):
+ pass
+ else:
+ self.emergency(message)
+ return
+ self.ima.setDisabled()
+ self.writebt.setDisabled()
+ self.do_umount(target)
+ # Writing step
+ # Iso dump or Uefi preparation
+# uefi_checkbox=self.wTree.get_widget("uefi_check")
+ if self.uefi_check.isChecked():
+ #uefi mode : formats FAT32, names MGALIVE, copies the ISO
+ target = self.dev.split('(')[1].split(')')[0]
+ dev_name="MGALIVE"
+ rc=self.raw_format(target, 'fat32', dev_name)
+ if rc == 5:
+ message = _("An error occurred while creating a partition.")
+ self.logger(message)
+ self.emergency(message)
+ elif rc == 127:
+ message = _('Authentication error.')
+ self.logger(message)
+ self.emergency(message)
+ elif rc == 0:
+ message = _('The device was formatted successfully.')
+ self.logger(message)
+# time.sleep(2)
+ seen=False
+ part=target+'1'
+ while(not seen):
+ try:
+ self.u.device(part)
+ seen=True
+ except:
+ seen=False
+ time.sleep(.5)
+ try:
+ dest=self.u.mount(part)
+ except:
+ self.logger(_("Error mounting the partition %s")%part)
+ self.emergency(message)
+ return
+ if dest!="":
+ self.logger(_("Mounted in: "))
+ self.returncode=0
+ self.files_write(source, dest)
+ self.u.unmount(part)
+ self.operation=False
+ if self.returncode==0:
+ self.success()
+ else:
+ self.logger(_("Error copying files"))
+ self.emergency(message)
+ else:
+ self.operation=False
+ self.logger(_("Error mounting the partition %s")%part)
+ self.emergency(message)
+ else:
+ message = _('An error occurred.')
+ self.emergency(message)
+ else:
+ #Dump mode
+ self.returncode=0
+ b=os.path.getsize(source)
+ print "Launch write", source, target, b
+ self.raw_write(source, target, b)
+# gobject.idle_add(task.next)
+# while gtk.events_pending():
+# gtk.main_iteration(True)
+ self.check_write(target, b)
+# gobject.idle_add(task.next)
+# while gtk.events_pending():
+# gtk.main_iteration(True)
+ if self.returncode == 0:
+ self.success()
+ else:
+ self.devicelist.setEnabled()
+ self.writebt.setEnabled()
+ self.formatbt.setEnabled()
+ self.backup_select.setEnabled()
+ def do_umount(self, target):
+ mounts = self.get_mounted(target)
+ if mounts:
+ self.logger(_('Unmounting all partitions of ')+target+':')
+ for mount in mounts:
+ self.logger(_('Trying to unmount ')+mount[0]+'...')
+# while gtk.events_pending():
+# gtk.main_iteration(True)
+ try:
+ retcode = call('umount '+mount[0], shell=True)
+ if retcode < 0:
+ message = _('Error, umount ')+mount[0]+_(' was terminated by signal ')+str(retcode)
+ self.logger(message)
+ self.emergency(message)
+ else:
+ if retcode == 0:
+ self.logger(mount[0]+_(' successfully unmounted'))
+ else:
+ message = _('Error, umount ')+mount[0]+_(' returned ')+str(retcode)
+ self.logger(message)
+ self.emergency(message)
+ except OSError, e:
+ message = _('Execution failed: ')+str(e)
+ self.logger(message)
+ self.emergency(message)
+
+ def get_mounted(self, target):
+ try:
+ lines = [line.strip("\n").split(" ") for line in open ("/etc/mtab", "r").readlines()]
+ return [mount for mount in lines if mount[0].startswith(target)]
+ except:
+ message = _('Could not read mtab !')
+ self.logger(message)
+ self.emergency(message)
+
+ def raw_write(self, source, target, b):
+ self.operation=True
+ bs=4096*128
+ try:
+ ifc=io.open(source, "rb",1)
+ except:
+ message = _('Reading error.')+ source
+ self.logger(message)
+ self.emergency(message)
+ return
+ else:
+ try:
+ ofc= io.open(target, 'wb',0)
+ except:
+ message = _('You have not the rights for writing on the device')
+ self.logger(message)
+ self.emergency(message)
+ self.close('dummy')
+ else:
+ self.progress.setEnabled()
+ self.progress.setLabel(_('Writing ')+source.split('/')[-1]+_(' to ')+target.split('/')[-1])
+ self.logger(_('Executing copy from ')+source+_(' to ')+target)
+ steps=range(0, b+1, b/100)
+ steps.append(b)
+ indice=1
+ written=0
+ ncuts=b/bs
+ while ncuts <= 100:
+ bs=bs/2
+ ncuts=b/bs
+ for i in xrange(0,ncuts+1):
+ try:
+ buf=ifc.read(bs)
+ except:
+ message = _("Reading error.")
+ self.logger(message)
+ self.emergency(message)
+ return
+ try:
+ ofc.write(buf)
+ except:
+ message = _("Writing error.")
+ self.logger(message)
+ self.emergency(message)
+ return
+ written+=len(buf)
+ if written > steps[indice]:
+ if indice%1==0:
+ self.logger(_('Wrote: ')+str(indice)+'% '+str(written)+' bytes')
+ self.progress.setValue(indice)
+ indice +=1
+ try:
+ os.fsync(ofc)
+ except:
+ message = _("Writing error.")
+ self.logger(message)
+ self.emergency(message)
+ return
+ self.progress.setValue(100)
+ self.logger(_('Image ')+source.split('/')[-1]+_(' successfully written to ')+target)
+ self.logger(_('Bytes written: ')+str(written))
+ try:
+ ofc.close()
+ except:
+ message = _("Writing error.")
+ self.logger(message)
+ self.emergency(message)
+ ifc.close()
+
+ def check_write(self, target, b):
+ import hashlib
+ self.progress.setEnabled()
+ self.progress.setLabel(_('Checking ')+target.split('/')[-1])
+ self.progress.setValue(0)
+ steps=range(0, b+1, b/100)
+ steps.append(b)
+ indice=0
+ checked=0
+ sha1func=hashlib.sha1()
+ md5func=hashlib.md5()
+ ncuts=b/1024
+ try:
+ with open(target, 'rb') as f:
+ for x in xrange(0,ncuts):
+ block = f.read(1024)
+ sha1func.update(block)
+ md5func.update(block)
+ if checked > steps[indice]:
+ self.progress.setValue(indice)
+ indice +=1
+ checked+=1024
+ block = f.read(b-ncuts*1024)
+ sha1func.update(block)
+ md5func.update(block)
+ sha1sumcalc=sha1func.hexdigest()
+ md5sumcalc=md5func.hexdigest()
+ self.logger(_('SHA1 sum: ')+sha1sumcalc)
+ self.logger(_('MD5 sum: ')+md5sumcalc)
+ f.close()
+ except:
+ pass
+ self.progress.setValue(100)
+
+ def files_write(self, source, dest):
+ self.operation=True
+ temp_dir='/mnt/MGALIVE'
+ makedirs(temp_dir)
+ self.process=Popen(['mount', '-o', 'loop',source,temp_dir ], shell=False, stdout=PIPE)
+ working=True
+ while working:
+ time.sleep(0.5)
+ self.process.poll()
+ rc=self.process.returncode
+ if rc is None:
+ working=True
+ else:
+ self.process = None
+ working=False
+ self.logger(_('ISO image mounted in ')+temp_dir)
+ self.progress.setEnabled()
+ self.progress.setLabel(_('Writing ')+source.split('/')[-1]+_(' to ')+dest.encode())
+ self.logger(_('Executing copy from ')+source+_(' to ')+dest.encode())
+ total_files=countFiles(temp_dir)
+ self.logger(_("%s file(s) to copy."%total_files))
+ cumuled_size=0
+ if total_files > 0:
+ for path, dirs, filenames in os.walk(temp_dir):
+ for directory in dirs:
+ destDir = path.replace(temp_dir,dest)
+ makedirs(os.path.join(destDir, directory))
+ for sfile in filenames:
+ srcFile = os.path.join(path, sfile)
+ destFile = os.path.join(path.replace(temp_dir, dest), sfile)
+ try:
+ with open(srcFile, 'rb') as fsrc:
+ with open(destFile, 'wb') as fdst:
+ while 1:
+ buf = fsrc.read(256*1024)
+ if not buf:
+ os.fsync(fdst)
+ break
+ fdst.write(buf)
+ cumuled_size += len(buf)
+ self.progress.setValue(int(cumuled_size*100/total_files))
+ except:
+ self.returncode=1
+ return
+ self.process = Popen(['umount', temp_dir ], shell=False, stdout=PIPE)
+ while True :
+ self.process.poll()
+ if self.process.returncode != None:
+ break
+ self.logger(_('Image ')+source.split('/')[-1]+_(' successfully written to ')+dest.encode())
+ else:
+ self.returncode=1
+
+ def success(self):
+ self.operation=False
+ self.final_unsensitive()
+ info = Info(_("Success"),1,_("The operation was successfully performed.\n\
+ You are free to unplug it now, a log isodumper.log\n\
+ of the operation will be saved in your homedir/.isodumper/ when\n\
+ you close the application."))
+ if self.ask_OK(info) :
+ return
+
+
+ def confirm_close(self, *args):
+ if self.operation==False: # no writing , backup nor format running
+ self.close('dummy')
+ else: # writing , backup or format running
+ info = Info(_("Success"),1,_("Writing is in progress. Exiting during writing \n\
+ will occur that the device or the backup will be unusable.\n\
+ Are you sure you want to quit during writing?"))
+ if self.ask_YesOrNo(info) :
+ return True
+ else:
+ return False
+
+ def emergency(self,message):
+ self.operation=False
+ self.returncode=1
+ self.final_unsensitive()
+# expander = self.wTree.get_widget("detail_expander")
+# expander.set_expanded(True)
+# mark = self.log.create_mark("end", self.log.get_end_iter(), False)
+# self.logview.scroll_to_mark(mark, 0.05, True, 0.0, 1.0)
+ info = Info(_("Error"),1,message)
+ self.ask_OK(info)
+# self.close()
+
+ def final_unsensitive(self):
+ self.ima.setDisabled()
+ self.devicelist.setDisabled()
+ self.writebt.setDisabled()
+ self.progress.setDisabled()
+ self.backup_select.setDisabled()
+
+ def close(self, widget):
+ self.write_logfile()
+# gtk.main_quit()
+ exit(0)
+
+ def write_logfile(self):
+ start = self.log.get_start_iter()
+ end = self.log.get_end_iter()
+ import pwd
+ pw = pwd.getpwnam(self.user)
+ uid = pw.pw_uid
+ gid=pw.pw_gid
+ if (self.user != 'root') and (self.user !=''):
+ logpath='/home/'+self.user+'/.isodumper'
+ os.setgid(gid)
+ os.setuid(uid)
+ if not(os.path.isdir(logpath)):
+ os.mkdir(logpath)
+ else:
+ logpath='/root'
+ logfile=open(logpath+'/isodumper.log',"w")
+ logfile.write(self.log.get_text(start, end, False))
+ logfile.close()
+
+ print self.log.get_text(start, end, False)
+
+ def logger(self, text):
+ self.logview.appendLines(text+"\n")
+
+ def activate_devicelist(self, widget):
+# label = self.wTree.get_widget("to_label")
+ expander = self.wTree.get_widget("detail_expander")
+# self.devicelist.setEnabled()
+ expander.setEnabled()
+# label.setEnabled()
+# self.img_name = self.chooser.get_filename()
+ write_button = self.wTree.get_widget("write_button")
+ write_button.setEnabled()
+ self.logger(_('Image ')+": "+ self.img_name)
+ self.chooser.set_tooltip_text(self.img_name)
+
+ def activate_backup(self, widget):
+ self.backup_img_name = self.backup_dir.get_filename()
+
+# def expander_control(self, widget):
+# # this is darn ugly but still better than the UI behavior of
+# # the unexpanded expander which doesnt reset the window size
+# if widget.get_expanded():
+# gobject.timeout_add(130, lambda: self.window.reshow_with_initial_size())
+
+ def help_dialog(self, widget):
+ dialog = self.wTree.get_widget("help_dialog")
+ dialog.run()
+
+ def help_close(self, widget):
+ dialog = self.wTree.get_widget("help_dialog")
+ dialog.hide()
+
+ def about(self, widget):
+ about_button = self.wTree.get_widget("about_button")
+ about_button.setEnabled()
+ dialog = self.wTree.get_widget("about_dialog")
+ resp = dialog.run()
+ if resp:
+ dialog.hide()
+
+ def __init__(self,user):
+ APP="isodumper"
+ DIR="/usr/share/locale"
+ self.RELEASE="v0.40"
+
+ gettext.bindtextdomain(APP, DIR)
+ gettext.textdomain(APP)
+
+ # for the localisation of log file
+ self.user = user
+
+ # define size of the selected device
+ self.deviceSize=0
+
+ # Operation running
+ self.operation=False
+
+ self.warning = _("Warning\nThis will destroy all data on the target device,\n\
+ are you sure you want to proceed?\n\
+ If you say ok here, please <b>do not unplug</b>\
+ the device during the following operation.")
+ """
+ Init/Constructor for the 'widgets'
+ """
+ self.atelier = yui.YUI.widgetFactory()
+ self.dialog = self.atelier.createMainDialog()
+ # create the main gui
+ # +---+-----------------+
+ # + banner +
+ # +---+-----------------+
+ # | L | device |
+ # +---+-----+------+----+
+ # + W | ima | u | writebt |
+ # +---+-----+------+----+
+ # + B | backup_select | backupbt |
+ # +---+-----+------+----+
+ # + F | bf |
+ # +----------------+----+
+ # | progress |
+ # +---------------------+
+ # | report |
+ # +---------------------+
+ # | ba | bp | bh | bq |
+ # +---------------------+
+ self.ancrage = self.atelier.createReplacePoint(self.dialog)
+ self.box = self.atelier.createVBox(self.ancrage)
+ self.bannerbox = self.atelier.createHBox(self.box)
+# self.bannerbox.setWeight(1,15)
+ self.banner=self.atelier.createImage(self.bannerbox,"/usr/share/isodumper/header.png")
+# self.atelier.createLabel( self.hautbox, "ATELIER ANNUITÉS")
+ self.devicebox = self.atelier.createHBox(self.box)
+ self.devicebox.setWeight(1,10)
+ self.devicelist=self.atelier.createComboBox(self.devicebox,_("Device to use:"),False)
+ self.devicelist.setStretchable(0,True)
+ self.writebox = self.atelier.createHBox(self.box)
+ self.writebox.setWeight(1,10)
+ self.deviceLabel=self.atelier.createLabel(self.writebox,_("Write Image:"))
+ # add file picker
+ self.ima=self.atelier.createPushButton(self.writebox,"Choose an image")
+ self.ima.setStretchable(0,True)
+ self.uefi_check=self.atelier.createCheckBox(self.writebox,_("For UEFI"))
+ self.writebt = self.atelier.createPushButton(self.writebox, _("&Write" ))
+ self.writebt.setStretchable(0,True)
+ self.writebt.setDisabled()
+ self.backupbox = self.atelier.createHBox(self.box)
+ self.backupbox.setWeight(1,10)
+ self.atelier.createLabel(self.backupbox,_("Backup to:"))
+ self.backup_select=self.atelier.createPushButton(self.backupbox,"Choose an image")
+ self.backup_select.setStretchable(0,True)
+ # add file picker
+ #
+ self.backupbt = self.atelier.createPushButton(self.backupbox, _("&Backup" ))
+ self.backupbt.setStretchable(0,True)
+ self.backupbt.setDisabled()
+ self.formatbox = self.atelier.createHBox(self.box)
+ self.formatbox.setWeight(1,10)
+ self.atelier.createLabel(self.formatbox,_("Format the device in FAT, NTFS or ext:"))
+ self.formatbt = self.atelier.createPushButton(self.formatbox, _("&Format" ))
+ self.formatbt.setStretchable(0,True)
+ self.progressbox = self.atelier.createHBox(self.box)
+ self.progressbox.setWeight(1,10)
+ self.progress = self.atelier.createProgressBar(self.progressbox,"Progression",100)
+ self.reportbox = self.atelier.createHBox(self.box)
+ self.reportbox.setWeight(1,10)
+ self.logview = self.atelier.createLogView(self.reportbox,"Report", 10)
+ self.reportbox.setWeight(1,20)
+ self.buttonsbox = self.atelier.createHBox(self.box)
+ self.buttonsbox.setWeight(1,10)
+# self.cadregauche= self.atelier.createFrame(self.mainhbox,"Traitements par lots")
+ self.refreshbt = self.atelier.createPushButton(self.buttonsbox, _("&Refresh" ))
+ self.refreshbt.setStretchable(0,True)
+ self.aboutbt = self.atelier.createPushButton(self.buttonsbox, _("&About" ))
+ self.aboutbt.setStretchable(0,True)
+ self.helpbt = self.atelier.createPushButton(self.buttonsbox, _("&Help" ))
+ self.helpbt.setStretchable(0,True)
+ self.quitbt = self.atelier.createPushButton(self.buttonsbox, _("&Quit" ))
+ self.quitbt.setStretchable(0,True)
+ #self.display = self.factory.createReplacePoint(self.framedisplayminsize) # here we change the widget
+
+ self.u = None
+ try:
+ self.u = UDisks2()
+ except :
+ message = _('UDisks2 is not available on your system')
+ self.logger(message)
+ self.emergency(message)
+ self.get_devices()
+ self.device_selected()
+ self.dialog.recalcLayout()
+ self.ancrage.showChild()
+
+ def ask_format(self):
+ atelier = yui.YUI.widgetFactory()
+ dialog = atelier.createPopupDialog()
+# dialog.setTitle(_("Choose format"))
+ vb=atelier.createVBox(dialog)
+ label = atelier.createInputField(vb,_("Label for the device:"))
+ cr = atelier.createRadioButtonGroup(vb)
+ vb_cr = atelier.createVBox(cr)
+ format_fat = atelier.createRadioButton(vb_cr,_("FAT 32 (Windows)"))
+ format_ntfs = atelier.createRadioButton((vb_cr),_("NTFS (Windows)"))
+ format_ext = atelier.createRadioButton((vb_cr),_("ext4 (Linux)"))
+ bb = atelier.createHBox(vb)
+ executebt = atelier.createPushButton(bb,_("Execute"))
+ cancelbt = atelier.createPushButton(bb,_("Cancel"))
+ dialog.open()
+ while True:
+ event = dialog.waitForEvent()
+ if event.eventType() == yui.YEvent.CancelEvent:
+ dialog.destroy()
+ return "no", "no"
+ if event.widget() == executebt:
+ if format_fat.value():
+ return 'fat32', label.value().upper()[:11]
+ if format_ntfs.value():
+ #dialog.destroy()
+ return 'ntfs', label.value()[:32]
+ if format_ext.value():
+ #dialog.destroy()
+ return 'ext4', label.value()
+ if event.widget() == cancelbt:
+ return "no","no"
+
+
+ def ask_OK(self, info):
+ yui.YUI.widgetFactory
+ mgafactory = yui.YMGAWidgetFactory.getYMGAWidgetFactory(yui.YExternalWidgets.externalWidgetFactory("mga"))
+ dlg = mgafactory.createDialogBox(yui.YMGAMessageBox.B_ONE)
+ dlg.setTitle(info.title)
+ dlg.setText(info.text, info.richtext)
+ dlg.setButtonLabel("OK", yui.YMGAMessageBox.B_ONE)
+ dlg.setMinSize(50, 5);
+ return dlg.show() == yui.YMGAMessageBox.B_ONE
+
+ def ask_YesOrNo(self, info):
+ yui.YUI.widgetFactory
+ mgafactory = yui.YMGAWidgetFactory.getYMGAWidgetFactory(yui.YExternalWidgets.externalWidgetFactory("mga"))
+ dlg = mgafactory.createDialogBox(yui.YMGAMessageBox.B_TWO)
+ dlg.setTitle(info.title)
+ dlg.setText(info.text, info.richtext)
+ dlg.setButtonLabel("Yes", yui.YMGAMessageBox.B_ONE)
+ dlg.setButtonLabel("No", yui.YMGAMessageBox.B_TWO)
+ dlg.setMinSize(70, 8);
+ return dlg.show() == yui.YMGAMessageBox.B_ONE
+
+ def aboutDialog(self):
+ yui.YUI.widgetFactory;
+ mgafactory = yui.YMGAWidgetFactory.getYMGAWidgetFactory(yui.YExternalWidgets.externalWidgetFactory("mga"))
+ dlg = mgafactory.createAboutDialog("About Isodumper", self.RELEASE, "GPLv2",
+ "Oliver Grawert\nPapoteur", "A tool for writing ISO images to a device", "")
+ dlg.show();
+
+ def nodevDialog(self):
+ yui.YUI.widgetFactory
+ mgafactory = yui.YMGAWidgetFactory.getYMGAWidgetFactory(yui.YExternalWidgets.externalWidgetFactory("mga"))
+ dlg = mgafactory.createDialogBox(yui.YMGAMessageBox.B_TWO)
+ dlg.setTitle("IsoDumper")
+ dlg.setText("Warning\nNo target devices were found.\nYou need to plug in a USB Key to which the image can be written.","")
+ dlg.setButtonLabel("Refresh", yui.YMGAMessageBox.B_ONE)
+ dlg.setButtonLabel("Cancel", yui.YMGAMessageBox.B_TWO)
+ dlg.setMinSize(70, 8);
+ return dlg.show() == yui.YMGAMessageBox.B_ONE
+
+ def handleevent(self):
+ self.traitement=None
+ while True:
+ event = self.dialog.waitForEvent()
+ if event.eventType() == yui.YEvent.CancelEvent:
+ self.dialog.destroy()
+ break
+ if event.widget() == self.quitbt:
+ self.dialog.destroy()
+ break
+ if event.widget() == self.ima:
+ self.img_name=yui.YUI.app().askForExistingFile('file:///home/'+self.user,"*.iso *.img","Choose an image")
+ self.ima.setLabel(self.img_name)
+ self.writebt.setEnabled()
+ if event.widget() == self.writebt:
+ self.do_write()
+ if event.widget() == self.backupbt:
+ self.backup_go()
+ if event.widget() == self.backup_select:
+ self.backup_img_name=yui.YUI.app().askForSaveFileName('file:///home/'+self.user,"*.img","Backup in")
+ self.backup_select.setLabel(self.backup_img_name)
+ self.backupbt.setEnabled()
+ if event.widget() == self.formatbt:
+ format_type,name = self.ask_format()
+ self.dialog.activate()
+ self.do_format(format_type,name)
+ try:
+ if event.widget() == self.refreshbt:
+ self.update_list()
+ except:
+ pass
+ try:
+ if event.widget() == self.helpbt:
+ self.help_dialog()
+ except:
+ pass
+ try:
+ if event.widget() == self.quitbt:
+ self.confirm_close()
+ except:
+ pass
+ if event.widget() == self.aboutbt:
+ self.aboutDialog()
+ def get_devices(self):
+# dialog = self.wTree.get_widget("nodev_dialog")
+ self.list=self.u.find_devices()
+ while len(self.list)==0:
+ if self.nodevDialog():
+ self.list = self.u.find_devices()
+ else:
+ self.dialog.destroy()
+ break
+ for name, path, size in self.list:
+ # convert in Mbytes
+ sizeM=str(int(size)/(1024*1024))
+ self.devicelist.addItem(str(name+' ('+path.lstrip()+') '+sizeM+_('Mb')))
+
+if __name__ == "__main__":
+ import sys
+ user=sys.argv[1]
+ app = IsoDumper(user)
+ app.handleevent()
+
+