backup_utils.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. #!/usr/bin/python2.6
  2. #
  3. # The Qubes OS Project, http://www.qubes-os.org
  4. #
  5. # Copyright (C) 2012 Agnieszka Kostrzewa <agnieszka.kostrzewa@gmail.com>
  6. #
  7. # This program is free software; you can redistribute it and/or
  8. # modify it under the terms of the GNU General Public License
  9. # as published by the Free Software Foundation; either version 2
  10. # of the License, or (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, write to the Free Software
  19. # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  20. #
  21. #
  22. import sys
  23. import os
  24. from PyQt4.QtCore import *
  25. from PyQt4.QtGui import *
  26. from pyinotify import WatchManager, Notifier, ThreadedNotifier, EventsCodes, ProcessEvent
  27. import subprocess
  28. import time
  29. from thread_monitor import *
  30. from datetime import datetime
  31. from string import replace
  32. mount_for_backup_path = '/usr/libexec/qubes-manager/mount_for_backup.sh'
  33. def check_if_mounted(dev_path):
  34. mounts_file = open("/proc/mounts")
  35. for m in list(mounts_file):
  36. if m.startswith(dev_path):
  37. return m.split(" ")[1]
  38. return None
  39. def mount_device(dev_path):
  40. try:
  41. mount_dir_name = "backup" + replace(str(datetime.now()),' ', '-').split(".")[0]
  42. pmount_cmd = [mount_for_backup_path, dev_path, mount_dir_name]
  43. res = subprocess.check_call(pmount_cmd)
  44. except Exception as ex:
  45. QMessageBox.warning (None, "Error mounting selected device!", "<b>Could not mount {0}.</b><br><br>ERROR: {1}".format(dev_path, ex))
  46. return None
  47. if res == 0:
  48. dev_mount_path = "/media/"+mount_dir_name
  49. return dev_mount_path
  50. return None
  51. def umount_device(dev_mount_path):
  52. while True:
  53. try:
  54. pumount_cmd = ["pumount", "--luks-force", dev_mount_path]
  55. res = subprocess.check_call(pumount_cmd)
  56. if res == 0:
  57. dev_mount_path = None
  58. return dev_mount_path
  59. except Exception as ex:
  60. title = "Error unmounting backup device!"
  61. text = "<b>Could not unmount {0}.</b><br>\
  62. <b>Please retry or unmount it manually using</b><br> pumount {0}.<br><br>\
  63. ERROR: {1}".format(dev_mount_path, ex)
  64. button = QMessageBox.warning (None, title, text, QMessageBox.Ok | QMessageBox.Retry, QMessageBox.Retry)
  65. if button == QMessageBox.Ok:
  66. return dev_mount_path
  67. def fill_devs_list(dialog):
  68. dialog.dev_combobox.clear()
  69. dialog.dev_combobox.addItem("None")
  70. dialog.blk_manager.blk_lock.acquire()
  71. for a in dialog.blk_manager.attached_devs:
  72. if dialog.blk_manager.attached_devs[a]['attached_to']['vm'] == dialog.vm.name :
  73. att = a + " " + unicode(dialog.blk_manager.attached_devs[a]['size']) + " " + dialog.blk_manager.attached_devs[a]['desc']
  74. dialog.dev_combobox.addItem(att, QVariant(a))
  75. for a in dialog.blk_manager.free_devs:
  76. att = a + " " + unicode(dialog.blk_manager.free_devs[a]['size']) + " " + dialog.blk_manager.free_devs[a]['desc']
  77. dialog.dev_combobox.addItem(att, QVariant(a))
  78. dialog.blk_manager.blk_lock.release()
  79. dialog.dev_combobox.setCurrentIndex(0) #current selected is null ""
  80. dialog.prev_dev_idx = 0
  81. dialog.dir_line_edit.clear()
  82. enable_dir_line_edit(dialog, True)
  83. def enable_dir_line_edit(dialog, boolean):
  84. dialog.dir_line_edit.setEnabled(boolean)
  85. dialog.select_path_button.setEnabled(boolean)
  86. def dev_combobox_activated(dialog, idx):
  87. if idx == dialog.prev_dev_idx: #nothing has changed
  88. return
  89. #there was a change
  90. dialog.dir_line_edit.setText("")
  91. dialog.backup_dir = None
  92. if dialog.dev_mount_path != None:
  93. dialog.dev_mount_path = umount_device(dialog.dev_mount_path)
  94. if dialog.dev_mount_path != None:
  95. dialog.dev_combobox.setCurrentIndex(dialog.prev_dev_idx)
  96. return
  97. if dialog.dev_combobox.currentText() != "None": #An existing device chosen
  98. dev_name = str(dialog.dev_combobox.itemData(idx).toString())
  99. dialog.blk_manager.blk_lock.acquire()
  100. if dev_name in dialog.blk_manager.free_devs:
  101. if dev_name.startswith(dialog.vm.name): # originally attached to dom0
  102. dev_path = "/dev/"+dev_name.split(":")[1]
  103. else: # originally attached to another domain, eg. usbvm
  104. #attach it to dom0, then treat it as an attached device
  105. dialog.blk_manager.attach_device(dialog.vm, dev_name)
  106. if dev_name in dialog.blk_manager.attached_devs: #is attached to dom0
  107. assert dialog.blk_manager.attached_devs[dev_name]['attached_to']['vm'] == dialog.vm.name
  108. dev_path = "/dev/" + dialog.blk_manager.attached_devs[dev_name]['attached_to']['frontend']
  109. dialog.blk_manager.blk_lock.release()
  110. #check if device mounted
  111. dialog.dev_mount_path = check_if_mounted(dev_path)
  112. if dialog.dev_mount_path == None:
  113. dialog.dev_mount_path = mount_device(dev_path)
  114. if dialog.dev_mount_path == None:
  115. dialog.dev_combobox.setCurrentIndex(0) #if couldn't mount - set current device to "None"
  116. dialog.prev_dev_idx = 0
  117. return
  118. dialog.prev_dev_idx = idx
  119. dialog.select_dir_page.emit(SIGNAL("completeChanged()"))
  120. def select_path_button_clicked(dialog):
  121. dialog.backup_dir = dialog.dir_line_edit.text()
  122. file_dialog = QFileDialog()
  123. file_dialog.setReadOnly(True)
  124. if dialog.dev_mount_path != None:
  125. new_path = file_dialog.getExistingDirectory(dialog, "Select backup directory.", dialog.dev_mount_path)
  126. else:
  127. new_path = file_dialog.getExistingDirectory(dialog, "Select backup directory.", "~")
  128. if new_path:
  129. dialog.dir_line_edit.setText(new_path)
  130. dialog.backup_dir = new_path
  131. dialog.select_dir_page.emit(SIGNAL("completeChanged()"))
  132. def simulate_long_lasting_proces(period, progress_callback):
  133. for i in range(period):
  134. progress_callback((i*100)/period)
  135. time.sleep(1)
  136. progress_callback(100)
  137. return 0