dell-recovery/ 0000775 0000000 0000000 00000000000 12704013124 010514 5 ustar dell-recovery/ubiquity/ 0000775 0000000 0000000 00000000000 12704013124 012367 5 ustar dell-recovery/ubiquity/stepRecoveryMedia.ui 0000664 0000000 0000000 00000043523 12670505071 016400 0 ustar
dell-recovery/ubiquity/dell-bootstrap.py 0000664 0000000 0000000 00000155012 12704013037 015703 0 ustar #!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# «dell-bootstrap» - Ubiquity plugin for Dell Factory Process
#
# Copyright (C) 2010-2014, Dell Inc.
#
# Author:
# - Mario Limonciello
#
# This 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 application; if not, write to the Free Software Foundation, Inc., 51
# Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
################################################################################
from ubiquity.plugin import InstallPlugin, Plugin, PluginUI
from ubiquity import misc
from threading import Thread
import time
from Dell.recovery_threading import ProgressBySize
import debconf
import Dell.recovery_common as magic
from Dell.recovery_xml import BTOxml
import subprocess
import os
import re
import shutil
import dbus
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
import syslog
import glob
import zipfile
import tarfile
import gi
gi.require_version('UDisks', '2.0')
from gi.repository import GLib, UDisks
NAME = 'dell-bootstrap'
BEFORE = 'language'
WEIGHT = 12
OEM = False
#Partition Definitions
EFI_ESP_PARTITION = '1'
EFI_RP_PARTITION = '2'
EFI_OS_PARTITION = '3'
EFI_SWAP_PARTITION = '4'
TYPE_NTFS = '07'
TYPE_NTFS_RE = '27'
TYPE_VFAT = '0b'
TYPE_VFAT_LBA = '0c'
#Continually Reused ubiquity templates
RECOVERY_TYPE_QUESTION = 'dell-recovery/recovery_type'
SWAP_QUESTION = 'dell-recovery/swap'
RP_FILESYSTEM_QUESTION = 'dell-recovery/recovery_partition_filesystem'
DRIVER_INSTALL_QUESTION = 'dell-recovery/disable-driver-install'
no_options = GLib.Variant('a{sv}', {})
#######################
# Noninteractive Page #
#######################
class PageNoninteractive(PluginUI):
"""Non-Interactive frontend for the dell-bootstrap ubiquity plugin"""
def __init__(self, controller, *args, **kwargs):
self.controller = controller
PluginUI.__init__(self, controller, *args, **kwargs)
def get_type(self):
'''For the noninteractive frontend, get_type always returns an empty str
This is because the noninteractive frontend always runs in "factory"
mode, which expects such a str""'''
return ""
def set_type(self, value, stage):
"""Empty skeleton function for the non-interactive UI"""
pass
def show_dialog(self, which, data = None):
"""Empty skeleton function for the non-interactive UI"""
pass
def get_selected_device(self):
"""Empty skeleton function for the non-interactive UI"""
pass
def populate_devices(self, devices):
"""Empty skeleton function for the non-interactive UI"""
pass
def set_advanced(self, item, value):
"""Empty skeleton function for the non-interactive UI"""
pass
def get_advanced(self, item):
"""Empty skeleton function for the non-interactive UI"""
return ''
############
# GTK Page #
############
class PageGtk(PluginUI):
"""GTK frontend for the dell-bootstrap ubiquity plugin"""
#OK, so we're not "really" a language page
#We are just cheating a little bit to make sure our widgets are translated
plugin_is_language = True
def __init__(self, controller, *args, **kwargs):
self.plugin_widgets = None
oem = 'UBIQUITY_OEM_USER_CONFIG' in os.environ
self.efi = False
self.genuine = magic.check_vendor()
if not oem:
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
builder = Gtk.Builder()
builder.add_from_file('/usr/share/ubiquity/gtk/stepDellBootstrap.ui')
builder.connect_signals(self)
self.controller = controller
self.controller.add_builder(builder)
self.plugin_widgets = builder.get_object('stepDellBootstrap')
self.automated_recovery = builder.get_object('automated_recovery')
self.automated_recovery_box = builder.get_object('automated_recovery_box')
self.automated_combobox = builder.get_object('hard_drive_combobox')
self.interactive_recovery = builder.get_object('interactive_recovery')
self.interactive_recovery_box = builder.get_object('interactive_recovery_box')
self.hdd_recovery = builder.get_object('hdd_recovery')
self.hdd_recovery_box = builder.get_object('hdd_recovery_box')
self.hidden_radio = builder.get_object('hidden_radio')
self.info_box = builder.get_object('info_box')
self.info_spinner = Gtk.Spinner()
builder.get_object('info_spinner_box').add(self.info_spinner)
self.restart_box = builder.get_object('restart_box')
self.err_dialog = builder.get_object('err_dialog')
#advanced page widgets
icon = builder.get_object('dell_image')
icon.set_tooltip_markup("Dell Recovery Advanced Options")
self.advanced_page = builder.get_object('advanced_window')
self.advanced_table = builder.get_object('advanced_table')
self.version_detail = builder.get_object('version_detail')
self.mount_detail = builder.get_object('mountpoint_detail')
self.memory_detail = builder.get_object('memory_detail')
self.proprietary_combobox = builder.get_object('disable_proprietary_driver_combobox')
self.rp_filesystem_combobox = builder.get_object('recovery_partition_filesystem_checkbox')
self.swap_combobox = builder.get_object('swap_behavior_combobox')
self.ui_combobox = builder.get_object('default_ui_combobox')
if not (self.genuine and 'UBIQUITY_AUTOMATIC' in os.environ):
builder.get_object('error_box').show()
PluginUI.__init__(self, controller, *args, **kwargs)
def plugin_get_current_page(self):
"""Called when ubiquity tries to realize this page.
* Disable the progress bar
* Check whether we are on genuine hardware
"""
#are we real?
if not (self.genuine and 'UBIQUITY_AUTOMATIC' in os.environ):
self.advanced_table.set_sensitive(False)
self.interactive_recovery_box.hide()
self.automated_recovery_box.hide()
self.automated_recovery.set_sensitive(False)
self.interactive_recovery.set_sensitive(False)
self.controller.allow_go_forward(False)
self.toggle_progress()
return self.plugin_widgets
def toggle_progress(self):
"""Toggles the progress bar for RP build"""
if 'UBIQUITY_AUTOMATIC' in os.environ and \
hasattr(self.controller, 'toggle_progress_section'):
self.controller.toggle_progress_section()
def get_type(self):
"""Returns the type of recovery to do from GUI"""
if self.automated_recovery.get_active():
return "automatic"
elif self.interactive_recovery.get_active():
return "interactive"
else:
return ""
def get_selected_device(self):
"""Returns the selected device from the GUI"""
device = size = ''
model = self.automated_combobox.get_model()
iterator = self.automated_combobox.get_active_iter()
if iterator is not None:
device = model.get_value(iterator, 0)
size = model.get_value(iterator, 1)
return (device, size)
def set_type(self, value, stage):
"""Sets the type of recovery to do in GUI"""
if not self.genuine:
return
self.hidden_radio.set_active(True)
if value == "automatic":
self.automated_recovery.set_active(True)
elif value == "interactive":
self.interactive_recovery.set_active(True)
elif value == "factory":
if stage == 2:
self.plugin_widgets.hide()
else:
self.controller.allow_go_forward(False)
if value == "hdd":
self.advanced_table.set_sensitive(False)
self.hdd_recovery_box.show()
self.interactive_recovery_box.hide()
self.automated_recovery_box.hide()
self.interactive_recovery.set_sensitive(False)
self.automated_recovery.set_sensitive(False)
def toggle_type(self, widget):
"""Allows the user to go forward after they've made a selection'"""
self.controller.allow_go_forward(True)
self.automated_combobox.set_sensitive(self.automated_recovery.get_active())
def show_dialog(self, which, data = None):
"""Shows a dialog"""
if which == "info":
self.controller._wizard.quit.set_label(
self.controller.get_string('ubiquity/imported/cancel'))
self.controller.allow_go_forward(False)
self.automated_recovery_box.hide()
self.interactive_recovery_box.hide()
self.info_box.show_all()
self.info_spinner.start()
self.toggle_progress()
elif which == "forward":
self.automated_recovery_box.hide()
self.interactive_recovery_box.hide()
self.toggle_progress()
else:
self.info_spinner.stop()
if which == "exception":
self.err_dialog.format_secondary_text(str(data))
self.err_dialog.run()
self.err_dialog.hide()
return
def populate_devices(self, devices):
"""Feeds a selection of devices into the GUI
devices should be an array of 3 column arrays
"""
#populate the devices
liststore = self.automated_combobox.get_model()
for device in devices:
liststore.append(device)
#default to the first item active (it should be sorted anyway)
self.automated_combobox.set_active(0)
## ##
## Advanced GUI options ##
## ##
def toggle_advanced(self, widget, data = None):
"""Shows the advanced page"""
self.plugin_widgets.set_sensitive(False)
self.advanced_page.run()
self.advanced_page.hide()
self.plugin_widgets.set_sensitive(True)
def _map_combobox(self, item):
"""Maps a combobox to a question"""
combobox = None
if item == DRIVER_INSTALL_QUESTION:
combobox = self.proprietary_combobox
elif item == RP_FILESYSTEM_QUESTION:
combobox = self.rp_filesystem_combobox
elif item == SWAP_QUESTION:
combobox = self.swap_combobox
return combobox
def set_advanced(self, item, value):
"""Populates the options that should be on the advanced page"""
if item == 'efi' and value:
self.efi = True
elif item == "mem" and value:
self.memory_detail.set_markup("Total Memory: %f GB" % value)
elif item == "version":
self.version_detail.set_markup("Version: %s" % value)
elif item == "mount":
self.mount_detail.set_markup("Mounted From: %s" % value)
else:
if type(value) is bool:
if value:
value = 'true'
else:
value = 'false'
combobox = self._map_combobox(item)
if combobox:
iterator = find_item_iterator(combobox, value)
if iterator is not None:
combobox.set_active_iter(iterator)
else:
syslog.syslog("DEBUG: setting %s to %s failed" % \
(item, value))
combobox.set_active(0)
def get_advanced(self, item):
"""Returns the value in an advanced key"""
combobox = self._map_combobox(item)
if combobox:
model = combobox.get_model()
iterator = combobox.get_active_iter()
return model.get_value(iterator, 0)
else:
return ""
def advanced_callback(self, widget, data = None):
"""Callback when an advanced widget is toggled"""
if widget == self.proprietary_combobox:
#nothing changes if we change proprietary drivers currently
pass
elif widget == self.rp_filesystem_combobox:
#nothing changes if we change RP filesystem currently
pass
elif widget == self.swap_combobox:
#nothing change if we change swap currently
pass
################
# Debconf Page #
################
class Page(Plugin):
"""Debconf driven page for the dell-bootstrap ubiquity plugin"""
def __init__(self, frontend, db=None, ui=None):
self.device = None
self.device_size = 0
self.efi = False
self.preseed_config = ''
self.rp_builder = None
self.disk_size = None
self.rp_filesystem = None
self.swap = None
self.stage = 1
Plugin.__init__(self, frontend, db, ui)
def log(self, error):
"""Outputs a debugging string to /var/log/installer/debug"""
self.debug("%s: %s" % (NAME, error))
def delete_swap(self):
"""Disables any swap partitions in use"""
udisks = UDisks.Client.new_sync(None)
manager = udisks.get_object_manager()
for item in manager.get_objects():
swap = item.get_swapspace()
if not swap:
continue
part = item.get_partition()
if not part:
continue
#Check if the swap is active or not
swap_active = swap.get_cached_property("Active").get_boolean()
if not swap_active:
continue
block = item.get_block()
if not block:
continue
device = block.get_cached_property('Device').get_bytestring().decode('utf-8')
swap.call_stop_sync(no_options)
# Only delete the swap partitions on the target
if device.startswith(self.device):
part.call_delete_sync(no_options)
def sleep_network(self):
"""Requests the network be disabled for the duration of install to
prevent conflicts"""
bus = dbus.SystemBus()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
try:
backend_iface = dbus.Interface(bus.get_object(magic.DBUS_BUS_NAME, '/RecoveryMedia'), magic.DBUS_INTERFACE_NAME)
backend_iface.force_network(False)
backend_iface.request_exit()
except Exception:
pass
def test_swap(self):
"""Tests what to do with swap"""
if not self.swap or (self.swap == "dynamic" and \
(self.mem >= 32 or self.disk_size <= 64)):
return True
else:
return False
def clean_recipe(self):
"""Cleans up the recipe to remove swap if we have a small drive"""
#If we are in dynamic (dell-recovery/swap=dynamic) and small drive
# or we explicitly disabled (dell-recovery/swap=false)
if self.test_swap():
self.log("Performing swap recipe fixup (%s, hdd: %i, mem: %f)" % \
(self.swap, self.disk_size, self.mem))
try:
recipe = self.db.get('partman-auto/expert_recipe')
self.db.set('partman-auto/expert_recipe',
' . '.join(recipe.split('.')[0:-2])+' .')
except debconf.DebconfError as err:
self.log(str(err))
def remove_extra_partitions(self):
"""Removes partitions we are installing on for the process to start"""
#check for small disks.
#on small disks or big mem, don't look for extended or delete swap.
swap_part = EFI_SWAP_PARTITION
os_part = EFI_OS_PARTITION
# check dual boot or not
try:
if self.db.get('dell-recovery/dual_boot')=='true':
##dual boot get the partition number of OS and swap
os_label = self.db.get('dell-recovery/os_partition')
os_part,swap_part = self.dual_partition_num(os_label)
except debconf.DebconfError as err:
self.log(str(err))
if self.test_swap():
swap_part = ''
#remove extras
for number in (os_part,swap_part):
if number.isdigit():
remove = misc.execute_root('parted', '-s', self.device, 'rm', number)
if remove is False:
self.log("Error removing partition number: %s on %s (this may be normal)'" % (number, self.device))
refresh = misc.execute_root('partx', '-d', '--nr', number, self.device)
if refresh is False:
self.log("Error updating partition %s for kernel device %s (this may be normal)'" % (number, self.device))
def dual_partition_num(self,label):
#remove UBUNTU patition for dual boot
##OS num
os_part = ''
swap_part = ''
digits = re.compile('\d+')
try:
os_path = magic.fetch_output(['readlink','/dev/disk/by-label/'+label]).split('\n')
except Exception as err:
self.log('os_path command is executed failed, the error is %s'%str(err))
os_part = digits.search(os_path[0].split('/')[-1]).group()
with misc.raised_privileges():
partitions = magic.fetch_output(['parted','-s',self.device,'print']).split('\n')
for line in partitions:
if 'linux-swap' in line:
swap_part = line.split()[0]
return os_part,swap_part
def explode_sdr(self):
'''Explodes all content explicitly defined in an SDR
If no SDR was found, don't change drive at all
'''
sdr_file = glob.glob(magic.CDROM_MOUNT + "/*SDR")
if not sdr_file:
sdr_file = glob.glob(magic.ISO_MOUNT + "/*SDR")
if not sdr_file:
return
#RP Needs to be writable no matter what
if not os.path.exists(magic.ISO_MOUNT):
cd_mount = misc.execute_root('mount', '-o', 'remount,rw', magic.CDROM_MOUNT)
if cd_mount is False:
raise RuntimeError("Error remounting RP to explode SDR.")
#Parse SDR
srv_list = []
dest = ''
with open(sdr_file[0], 'r') as rfd:
sdr_lines = rfd.readlines()
for line in sdr_lines:
if line.startswith('SI'):
columns = line.split()
if len(columns) > 2:
#always assume lower case (in case case sensitive FS)
srv_list.append(columns[2].lower())
if line.startswith('HW'):
columns = line.split()
if len(columns) > 2 and columns[1] == 'destination':
dest = columns[2]
#Explode SRVs that match SDR
for srv in srv_list:
fname = os.path.join(os.path.join(magic.CDROM_MOUNT, 'srv', '%s' % srv))
if os.path.exists('%s.tgz' % fname):
archive = tarfile.open('%s.tgz' % fname)
elif os.path.exists('%s.zip' % fname):
archive = zipfile.ZipFile('%s.zip' % fname)
else:
self.log("Skipping SRV %s. No file on filesystem." % srv)
continue
with misc.raised_privileges():
self.log("Extracting SRV %s onto filesystem" % srv)
archive.extractall(path=magic.CDROM_MOUNT)
archive.close()
#if the destination is somewhere special, change the language
if dest:
self.preseed('dell-recovery/destination', dest)
if dest == 'CN':
self.preseed('debian-installer/locale', 'zh_CN.UTF-8')
self.ui.controller.translate('zh_CN.UTF-8')
def usb_boot_preseeds(self, more_keys=None):
"""Sets/unsets preseeds that are common to a USB boot scenario.
This can either happen if booted from USB stick while in stage 2
or if booted from USB stick in stage 1 and choosing to only restore
the linux partition
"""
keys = ['ubiquity/poweroff', 'ubiquity/reboot']
if more_keys:
keys += more_keys
for key in keys:
self.db.fset(key, 'seen', 'false')
self.db.set(key, '')
self.db.set('ubiquity/partman-skip-unmount', 'false')
self.db.set('partman/filter_mounted', 'true')
def unset_drive_preseeds(self):
"""Unsets any preseeds that are related to setting a drive"""
keys = [ 'partman-auto/init_automatically_partition',
'partman-auto/disk',
'partman-auto/expert_recipe',
'partman-basicfilesystems/no_swap',
'grub-installer/only_debian',
'grub-installer/with_other_os',
'grub-installer/bootdev',
'grub-installer/make_active',
'oem-config/early_command',
'oem-config/late_command']
self.usb_boot_preseeds(keys)
def fixup_recovery_devices(self):
"""Discovers the first hard disk to install to"""
disks = []
udisks = UDisks.Client.new_sync(None)
manager = udisks.get_object_manager()
drive = None
for item in manager.get_objects():
loop = item.get_loop()
block = item.get_block()
partition = item.get_partition()
if loop or \
partition or \
not block or \
not block.get_cached_property("HintPartitionable").get_boolean() or \
block.get_cached_property("ReadOnly").get_boolean():
continue
#Check the disk is type of NVME SSD
device_path = block.get_cached_property("Device").get_bytestring().decode('utf-8')
if device_path.startswith('/dev/nvme'):
with misc.raised_privileges():
getdisk_subp = subprocess.Popen(['smartctl', '-d', 'scsi', '--all', device_path], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = getdisk_subp.communicate()[0]
vendor = ""
model = ""
for prop in output.decode('utf-8').split('\n'):
if prop.startswith('Vendor:'):
vendor = prop.split(':')[1].strip()
elif prop.startswith('Product:'):
model = prop.split(':')[1].strip()
else:
continue
nvme_dev_size = block.get_cached_property("Size").unpack()
nvme_size_gb = "%i" % (nvme_dev_size / 1000000000)
disks.append([device_path, nvme_dev_size, "%s GB %s %s (%s)" % (nvme_size_gb, vendor, model, device_path)])
continue
drive_obj = block.get_cached_property("Drive").get_string()
if drive_obj == '/':
continue
drive = udisks.get_object(drive_obj).get_drive()
if drive:
bus = drive.get_cached_property("ConnectionBus").get_string()
if bus == 'usb':
continue
elif bus == 'sdio':
media = drive.get_cached_property("Media").get_string()
if not media:
continue
else:
continue
devicesize = drive.get_cached_property("Size").unpack()
if devicesize == 0:
continue
devicefile = block.get_cached_property("Device").get_bytestring().decode('utf-8')
devicemodel = drive.get_cached_property("Model").get_string()
devicevendor = drive.get_cached_property("Vendor").get_string()
devicesize_gb = "%i" % (devicesize / 1000000000)
disks.append([devicefile, devicesize, "%s GB %s %s (%s)" % (devicesize_gb, devicevendor, devicemodel, devicefile)])
if len(disks) == 0:
raise RuntimeError("Unable to find and candidate hard disks to install to.")
# Search for the recovery partition on the same disk first
the_same_disk = None
for disk in disks:
device = disk[0]
with open('/proc/mounts', 'r') as mounts:
for line in mounts.readlines():
if device in line:
self.device = device
the_same_disk = disk
break
if the_same_disk:
break
if the_same_disk:
disks.remove(the_same_disk)
disks.insert(0, the_same_disk)
else:
disks.sort()
self.device = disks[0][0]
#If multiple candidates were found, record in the logs
if len(disks) > 1:
self.log("Multiple disk candidates were found: %s" % disks)
self.log("Initially selected candidate disk: %s" % self.device)
#populate UI
self.ui.populate_devices(disks)
def fixup_factory_devices(self, rec_part):
"""Find the factory recovery partition, and re-adjust preseeds to use that data"""
#Ignore any EDD settings - we want to just plop on the same drive with
#the right FS label (which will be valid right now)
#Don't you dare put a USB stick in the system with that label right now!
self.device = rec_part["slave"]
if os.path.exists(magic.ISO_MOUNT):
location = magic.ISO_MOUNT
else:
location = magic.CDROM_MOUNT
early = '/usr/share/dell/scripts/oem_config.sh early %s' % location
self.db.set('oem-config/early_command', early)
self.db.set('partman-auto/disk', self.device)
#EFI install finds ESP
self.db.set('grub-installer/bootdev', self.device)
if rec_part["fs"] == "ntfs":
self.rp_filesystem = TYPE_NTFS_RE
elif rec_part["fs"] == "vfat":
self.rp_filesystem = TYPE_VFAT_LBA
else:
raise RuntimeError("Unknown filesystem on recovery partition: %s" % rec_part["fs"])
self.disk_size = rec_part["size_gb"]
self.log("Detected device we are operating on is %s" % self.device)
self.log("Detected a %s filesystem on the %s recovery partition" % (rec_part["fs"], rec_part["label"]))
def prepare(self, unfiltered=False):
"""Prepare the Debconf portion of the plugin and gather all data"""
#version
with misc.raised_privileges():
version = magic.check_version()
self.log("version %s" % version)
#mountpoint
mount = find_boot_device()
self.log("mounted from %s" % mount)
#recovery type
rec_type = None
try:
rec_type = self.db.get(RECOVERY_TYPE_QUESTION)
except debconf.DebconfError as err:
self.log(str(err))
rec_type = 'dynamic'
self.db.register('debian-installer/dummy', RECOVERY_TYPE_QUESTION)
self.db.set(RECOVERY_TYPE_QUESTION, rec_type)
#If we were preseeded to dynamic, look for an RP
rec_part = magic.find_factory_partition_stats()
if "slave" in rec_part:
self.stage = 2
if rec_type == 'dynamic':
# we rebooted with no USB stick or DVD in drive and have the RP
# mounted at /cdrom
if self.stage == 2 and rec_part["slave"] in mount:
self.log("Detected RP at %s, setting to factory boot" % mount)
rec_type = 'factory'
else:
self.log("No (matching) RP found. Assuming media based boot")
rec_type = 'dvd'
#Media boots should be interrupted at first screen in --automatic mode
if rec_type == 'factory':
self.db.fset(RECOVERY_TYPE_QUESTION, 'seen', 'true')
else:
self.db.set(RECOVERY_TYPE_QUESTION, '')
self.db.fset(RECOVERY_TYPE_QUESTION, 'seen', 'false')
#Support cases where the recovery partition isn't a linux partition
try:
self.rp_filesystem = self.db.get(RP_FILESYSTEM_QUESTION)
except debconf.DebconfError as err:
self.log(str(err))
self.rp_filesystem = TYPE_VFAT_LBA
#Behavior of the swap partition
try:
self.swap = self.db.get(SWAP_QUESTION)
if self.swap != "dynamic":
self.swap = misc.create_bool(self.swap)
except debconf.DebconfError as err:
self.log(str(err))
self.swap = 'dynamic'
#Proprietary driver installation preventions
try:
proprietary = self.db.get(DRIVER_INSTALL_QUESTION)
except debconf.DebconfError as err:
self.log(str(err))
proprietary = ''
#If we detect that we are booted into uEFI mode, then we only want
#to do a GPT install.
if os.path.isdir('/proc/efi') or os.path.isdir('/sys/firmware/efi'):
self.efi = True
#Amount of memory in the system
self.mem = 0
if os.path.exists('/sys/firmware/memmap'):
for root, dirs, files in os.walk('/sys/firmware/memmap', topdown=False):
if os.path.exists(os.path.join(root, 'type')):
with open(os.path.join(root, 'type')) as rfd:
type = rfd.readline().strip('\n')
if type != "System RAM":
continue
with open(os.path.join(root, 'start')) as rfd:
start = int(rfd.readline().strip('\n'),0)
with open(os.path.join(root, 'end')) as rfd:
end = int(rfd.readline().strip('\n'),0)
self.mem += (end - start + 1)
self.mem = float(self.mem/1024)
if self.mem == 0:
with open('/proc/meminfo','r') as rfd:
for line in rfd.readlines():
if line.startswith('MemTotal'):
self.mem = float(line.split()[1].strip())
break
self.mem = round(self.mem/1048575) #in GB
#Fill in UI data
twiddle = {"mount": mount,
"version": version,
SWAP_QUESTION: self.swap,
DRIVER_INSTALL_QUESTION: proprietary,
RP_FILESYSTEM_QUESTION: self.rp_filesystem,
"mem": self.mem,
"efi": self.efi}
# The order invoking set_advanced() is important. (LP: #1324394)
for twaddle in reversed(sorted(twiddle)):
self.ui.set_advanced(twaddle, twiddle[twaddle])
self.ui.set_type(rec_type, self.stage)
#Make sure some locale was set so we can guarantee automatic mode
try:
language = self.db.get('debian-installer/locale')
except debconf.DebconfError:
language = ''
if not language:
language = 'en_US.UTF-8'
self.preseed('debian-installer/locale', language)
self.ui.controller.translate(language)
# If there is a Kylin overlay, set language to zh_CN.UTF-8
client_type = os.path.join('/cdrom', '.oem', 'client_type')
if os.path.isfile(client_type):
with open (client_type, "r") as myfile:
content=myfile.read().replace('\n', '')
if content == "kylin":
language = 'zh_CN.UTF-8'
self.preseed('debian-installer/locale', language)
self.ui.controller.translate(language)
#Clarify which device we're operating on initially in the UI
try:
self.fixup_recovery_devices()
self.log("rec_type %s, stage %d, device %s" % (rec_type, self.stage, self.device))
if (rec_type == 'factory' and self.stage == 2) or rec_type == 'hdd':
self.fixup_factory_devices(rec_part)
except Exception as err:
self.handle_exception(err)
self.cancel_handler()
return (['/usr/share/ubiquity/dell-bootstrap'], [RECOVERY_TYPE_QUESTION])
def ok_handler(self):
"""Copy answers from debconf questions"""
#basic questions
rec_type = self.ui.get_type()
self.log("recovery type set to '%s'" % rec_type)
self.preseed(RECOVERY_TYPE_QUESTION, rec_type)
(device, size) = self.ui.get_selected_device()
if device:
self.device = device
if size:
self.device_size = size
self.log("selected device %s %d" % (device, size))
#advanced questions
for question in [SWAP_QUESTION,
DRIVER_INSTALL_QUESTION,
RP_FILESYSTEM_QUESTION]:
answer = self.ui.get_advanced(question)
if answer:
self.log("advanced option %s set to %s" % (question, answer))
self.preseed_config += question + "=" + answer + " "
if question == RP_FILESYSTEM_QUESTION:
self.rp_filesystem = answer
if type(answer) is bool:
self.preseed_bool(question, answer)
else:
self.preseed(question, answer)
return Plugin.ok_handler(self)
def report_progress(self, info, percent):
"""Reports to the frontend an update about th progress"""
self.frontend.debconf_progress_info(info)
self.frontend.debconf_progress_set(percent)
def cleanup(self):
"""Do all the real processing for this plugin.
* This has to be done here because ok_handler won't run in a fully
automated load, and we need to run all steps in all scenarios
* Run is the wrong time too because it runs before the user can
answer potential questions
"""
rec_type = self.db.get('dell-recovery/recovery_type')
try:
# User recovery - need to copy RP
if rec_type == "automatic" or \
(rec_type == "factory" and self.stage == 1):
if not (rec_type == "factory" and self.stage == 1):
self.ui.show_dialog("info")
self.sleep_network()
self.delete_swap()
#init progress bar and size thread
self.frontend.debconf_progress_start(0, 100, "")
size_thread = ProgressBySize("Copying Files",
"/mnt",
"0")
size_thread.progress = self.report_progress
#init builder
self.rp_builder = RPbuilder(self.device,
self.device_size,
self.rp_filesystem,
self.mem,
self.efi,
self.preseed_config,
size_thread)
self.rp_builder.exit = self.exit_ui_loops
self.rp_builder.status = self.report_progress
self.rp_builder.start()
self.enter_ui_loop()
self.rp_builder.join()
if self.rp_builder.exception:
self.handle_exception(self.rp_builder.exception)
reboot_machine(None)
# User recovery - resizing drives
elif rec_type == "interactive":
self.ui.show_dialog("forward")
self.unset_drive_preseeds()
# Factory install, and booting from RP
else:
if 'dell-recovery/recovery_type=hdd' in open('/proc/cmdline', 'r').read().split():
self.ui.toggle_progress()
self.sleep_network()
self.delete_swap()
self.clean_recipe()
self.remove_extra_partitions()
self.explode_sdr()
except Exception as err:
#For interactive types of installs show an error then reboot
#Otherwise, just reboot the system
if rec_type == "automatic" or rec_type == "interactive" or \
('UBIQUITY_DEBUG' in os.environ and 'UBIQUITY_ONLY' in os.environ):
self.handle_exception(err)
self.cancel_handler()
#translate languages
self.ui.controller.translate(just_me=False, not_me=True, reget=True)
Plugin.cleanup(self)
def cancel_handler(self):
"""Called when we don't want to perform recovery'"""
if os.path.exists(os.path.join('/cdrom', '.disk', 'info.recovery')) and \
os.path.exists(os.path.join('/cdrom', '.disk', 'info')) and \
misc.execute_root('mount', '-o', 'remount,rw', '/cdrom'):
with misc.raised_privileges():
os.remove(os.path.join('/cdrom', '.disk', 'info'))
misc.execute_root('mount', '-o', 'remount,ro', '/cdrom')
misc.execute_root('reboot')
def handle_exception(self, err):
"""Handle all exceptions thrown by any part of the application"""
self.log(str(err))
self.ui.show_dialog("exception", err)
############################
# RP Builder Worker Thread #
############################
class RPbuilder(Thread):
"""The recovery partition builder worker thread"""
def __init__(self, device, size, rp_type, mem, efi, preseed_config, sizing_thread):
self.device = device
self.device_size = size
self.rp_type = rp_type
self.mem = mem
self.efi = efi
self.preseed_config = preseed_config
self.exception = None
self.file_size_thread = sizing_thread
self.xml_obj = BTOxml()
Thread.__init__(self)
def build_rp(self, cushion=600):
"""Copies content to the recovery partition using a parted wrapper.
This might be better implemented in python-parted or parted_server/partman,
but those would require extra dependencies, and are generally more complex
than necessary for what needs to be accomplished here."""
black_pattern = re.compile('casper-rw|casper-uuid')
#Check if we are booted from same device as target
mounted_device = find_boot_device()
if self.device in mounted_device:
raise RuntimeError("Attempting to install to the same device as booted from.\n\
You will need to clear the contents of the recovery partition\n\
manually to proceed.")
#Adjust recovery partition type to something parted will recognize
if self.rp_type == TYPE_NTFS or \
self.rp_type == TYPE_NTFS_RE:
self.rp_type = 'ntfs'
elif self.rp_type == TYPE_VFAT or \
self.rp_type == TYPE_VFAT_LBA:
self.rp_type = 'fat32'
else:
raise RuntimeError("Unsupported recovery partition filesystem: %s" % self.rp_type)
#Calculate RP size
rp_size = magic.black_tree("size", black_pattern, magic.CDROM_MOUNT)
#in mbytes
rp_size_mb = (rp_size / 1000000) + cushion
# Build new partition table
command = ('parted', '-s', self.device, 'mklabel', 'gpt')
result = misc.execute_root(*command)
if result is False:
raise RuntimeError("Error creating new partition table on %s" % (self.device))
self.status("Creating Partitions", 1)
grub_size = 250
commands = [('parted', '-a', 'optimal', '-s', self.device, 'mkpart', 'primary', 'fat16', '0', str(grub_size)),
('parted', '-s', self.device, 'name', '1', "'EFI System Partition'"),
('parted', '-s', self.device, 'set', '1', 'boot', 'on')]
if '/dev/nvme' in self.device or '/dev/mmcblk' in self.device:
commands.append(('mkfs.msdos', self.device + 'p' + EFI_ESP_PARTITION))
rp_part = 'p' + EFI_RP_PARTITION
esp_part = 'p' + EFI_ESP_PARTITION
else:
commands.append(('mkfs.msdos', self.device + EFI_ESP_PARTITION))
rp_part = EFI_RP_PARTITION
esp_part = EFI_ESP_PARTITION
for command in commands:
#wait for settle
if command[0] == 'mkfs.msdos':
while not os.path.exists(command[-1]):
time.sleep(1)
result = misc.execute_root(*command)
if result is False:
if self.efi:
raise RuntimeError("Error formatting disk.")
#Build RP
command = ('parted', '-a', 'optimal', '-s', self.device, 'mkpart', self.rp_type, self.rp_type, str(grub_size), str(rp_size_mb + grub_size))
result = misc.execute_root(*command)
if result is False:
raise RuntimeError("Error creating new %s mb recovery partition on %s" % (rp_size_mb, self.device))
#Build RP filesystem
self.status("Formatting Partitions", 2)
if self.rp_type == 'fat32':
command = ('mkfs.msdos', '-n', 'OS', self.device + rp_part)
elif self.rp_type == 'ntfs':
command = ('mkfs.ntfs', '-f', '-L', 'RECOVERY', self.device + rp_part)
while not os.path.exists(command[-1]):
time.sleep(1)
result = misc.execute_root(*command)
if result is False:
raise RuntimeError("Error creating %s filesystem on %s%s" % (self.rp_type, self.device, rp_part))
#Mount RP
mount = misc.execute_root('mount', self.device + rp_part, '/mnt')
if mount is False:
raise RuntimeError("Error mounting %s%s" % (self.device, rp_part))
#Update status and start the file size thread
self.file_size_thread.reset_write(rp_size)
self.file_size_thread.set_scale_factor(85)
self.file_size_thread.set_starting_value(2)
self.file_size_thread.start()
#Copy RP Files
with misc.raised_privileges():
if os.path.exists(magic.ISO_MOUNT):
magic.black_tree("copy", re.compile(".*\.iso$"), magic.ISO_MOUNT, '/mnt')
magic.black_tree("copy", black_pattern, magic.CDROM_MOUNT, '/mnt')
self.file_size_thread.join()
#find uuid of drive
with misc.raised_privileges():
blkid = magic.fetch_output(['blkid', self.device + rp_part, "-p", "-o", "udev"]).split('\n')
for item in blkid:
if item.startswith('ID_FS_UUID'):
uuid = item.split('=')[1]
break
#read in any old seed
seed = os.path.join('/mnt', 'preseed', 'dell-recovery.seed')
keys = magic.parse_seed(seed)
#process the new options
for item in self.preseed_config.split():
if '=' in item:
key, value = item.split('=')
keys[key] = value
#write out a dell-recovery.seed configuration file
with misc.raised_privileges():
if not os.path.isdir(os.path.join('/mnt', 'preseed')):
os.makedirs(os.path.join('/mnt', 'preseed'))
magic.write_seed(seed, keys)
#Check for a grub.cfg - replace as necessary
files = {'recovery_partition.cfg': 'grub.cfg',
}
for item in files:
full_path = os.path.join('/mnt', 'factory', files[item])
if os.path.exists(full_path):
with misc.raised_privileges():
shutil.move(full_path, full_path + '.old')
with misc.raised_privileges():
magic.process_conf_file('/usr/share/dell/grub/' + item, \
full_path, uuid, EFI_RP_PARTITION)
#Install grub
self.status("Installing GRUB", 88)
##If we don't have grub binaries, build them
grub_files = ['/cdrom/efi/boot/bootx64.efi',
'/cdrom/efi/boot/grubx64.efi']
##Mount ESP
mount = misc.execute_root('mount', self.device + esp_part, '/mnt/efi')
if mount is False:
raise RuntimeError("Error mounting %s%s" % (self.device, esp_part))
##find old entries and prep directory
direct_path = '/mnt/efi' + '/efi/ubuntu'
with misc.raised_privileges():
os.makedirs(direct_path)
#copy boot loader files
for item in grub_files:
if not os.path.exists(item):
raise RuntimeError("Error, %s doesn't exist." % item)
shutil.copy(item, direct_path)
#find old entries
bootmgr_output = magic.fetch_output(['efibootmgr', '-v']).split('\n')
#delete old entries
for line in bootmgr_output:
bootnum = ''
if line.startswith('Boot') and 'ubuntu' in line.lower():
bootnum = line.split('Boot')[1].replace('*', '').split()[0]
if bootnum:
bootmgr = misc.execute_root('efibootmgr', '-v', '-b', bootnum, '-B')
if bootmgr is False:
raise RuntimeError("Error removing old EFI boot manager entries")
target = 'shimx64.efi'
with misc.raised_privileges():
os.rename(os.path.join(direct_path, 'bootx64.efi'),
os.path.join(direct_path, target))
add = misc.execute_root('efibootmgr', '-v', '-c', '-d', self.device, '-p', EFI_ESP_PARTITION, '-l', '\\EFI\\ubuntu\\%s' % target, '-L', 'ubuntu')
if add is False:
raise RuntimeError("Error adding efi entry to %s%s" % (self.device, esp_part))
##clean up ESP mount
misc.execute_root('umount', '/mnt/efi')
#Make changes that would normally be done in factory stage1
##rename efi directory so we don't offer it to customer boot in NVRAM menu
if os.path.exists('/mnt/efi'):
with misc.raised_privileges():
shutil.move('/mnt/efi', '/mnt/efi.factory')
##set install_in_progress flag
with misc.raised_privileges():
if not os.path.exists('/mnt/factory/grub.cfg'):
build = misc.execute_root('/usr/share/dell/grub/build-factory.sh')
if build is False:
raise RuntimeError("Error building grub cfg.")
with misc.raised_privileges():
magic.white_tree("copy", re.compile('.'), '/var/lib/dell-recovery', '/mnt/factory')
magic.fetch_output(['grub-editenv', '/mnt/factory/grubenv', 'set', 'install_in_progress=1'])
#update bto.xml
path = os.path.join(magic.CDROM_MOUNT, 'bto.xml')
if os.path.exists(path):
self.xml_obj.load_bto_xml(path)
bto_version = self.xml_obj.fetch_node_contents('iso')
bto_date = self.xml_obj.fetch_node_contents('date')
with misc.raised_privileges():
dr_version = magic.check_version('dell-recovery')
ubi_version = magic.check_version('ubiquity')
self.xml_obj.replace_node_contents('bootstrap', dr_version)
self.xml_obj.replace_node_contents('ubiquity' , ubi_version)
if os.path.exists('/var/log/syslog'):
with open('/var/log/syslog', 'rb') as rfd:
self.xml_obj.replace_node_contents('syslog', rfd.read())
if os.path.exists('/var/log/installer/debug'):
with open('/var/log/installer/debug', 'rb') as rfd:
self.xml_obj.replace_node_contents('debug', rfd.read())
if not bto_version:
self.xml_obj.replace_node_contents('iso', '[native]')
if not bto_date:
with open(os.path.join(magic.CDROM_MOUNT, '.disk', 'info')) as rfd:
line = rfd.readline().strip()
date = line.split()[len(line.split())-1]
self.xml_obj.replace_node_contents('date', date)
self.xml_obj.write_xml('/mnt/bto.xml')
misc.execute_root('umount', '/mnt')
for count in range(100,0,-10):
self.status("Restarting in %d seconds." % int(count/10), count)
time.sleep(1)
def exit(self):
"""Function to request the builder thread to close"""
pass
def status(self, info, percent):
"""Stub function for passing data back up"""
pass
def run(self):
"""Start the RP builder thread"""
try:
self.build_rp()
except Exception as err:
self.exception = err
self.exit()
####################
# Helper Functions #
####################
def find_boot_device():
"""Finds the device we're booted from'"""
mounted_device = ''
with open('/proc/mounts', 'r') as mounts:
for line in mounts.readlines():
if magic.ISO_MOUNT in line:
mounted_device = line.split()[0]
break
if magic.CDROM_MOUNT in line:
found = line.split()[0]
if not 'loop' in found:
mounted_device = line.split()[0]
break
return mounted_device
def reboot_machine(objpath):
"""Reboots the machine"""
reboot_cmd = '/sbin/reboot'
reboot = misc.execute_root(reboot_cmd)
if reboot is False:
raise RuntimeError("Reboot failed from %s" % str(objpath))
def find_item_iterator(combobox, value, column = 0):
"""Searches a combobox for a value and returns the iterator that matches"""
model = combobox.get_model()
iterator = model.get_iter_first()
while iterator is not None:
if value == model.get_value(iterator, column):
break
iterator = model.iter_next(iterator)
return iterator
def find_n_set_iterator(combobox, value, column = 0):
"""Searches a combobox for a value, and sets the iterator to that value if
it's found"""
iterator = find_item_iterator(combobox, value, column)
if iterator is not None:
combobox.set_active_iter(iterator)
###########################################
# Commands Processed During Install class #
###########################################
class Install(InstallPlugin):
"""The install time dell-bootstrap ubiquity plugin"""
def __init__(self, frontend, db=None, ui=None):
self.progress = None
self.target = None
InstallPlugin.__init__(self, frontend, db, ui)
def remove_ricoh_mmc(self):
'''Removes the ricoh_mmc kernel module which is known to cause problems
with MDIAGS'''
lsmod = magic.fetch_output('lsmod').split('\n')
for line in lsmod:
if line.startswith('ricoh_mmc'):
misc.execute('rmmod', line.split()[0])
def remove_unwanted_drivers(self):
'''Removes drivers that were preseeded to not used for postinstall'''
drivers = ''
try:
drivers = self.progress.get(DRIVER_INSTALL_QUESTION).split(',')
except debconf.DebconfError:
pass
if len(drivers) > 0:
for driver in drivers:
if driver:
with open (os.path.join(self.target, 'usr/share/jockey/modaliases/', driver), 'w') as wfd:
wfd.write('reset %s\n' % driver)
def wake_network(self):
"""Wakes the network back up"""
bus = dbus.SystemBus()
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
try:
backend_iface = dbus.Interface(bus.get_object(magic.DBUS_BUS_NAME, '/RecoveryMedia'), magic.DBUS_INTERFACE_NAME)
backend_iface.force_network(True)
backend_iface.request_exit()
except Exception:
pass
def install(self, target, progress, *args, **kwargs):
'''This is highly dependent upon being called AFTER configure_apt
in install. If that is ever converted into a plugin, we'll
have some major problems!'''
genuine = magic.check_vendor()
if not genuine:
raise RuntimeError("This recovery media requires Dell Hardware.")
self.target = target
self.progress = progress
rec_part = magic.find_partition()
from ubiquity import install_misc
to_install = []
to_remove = []
#if we are loop mounted, make sure the chroot knows it too
if os.path.isdir(magic.ISO_MOUNT):
os.makedirs(os.path.join(self.target, magic.ISO_MOUNT.lstrip('/')))
misc.execute_root('mount', '--bind', magic.ISO_MOUNT, os.path.join(self.target, magic.ISO_MOUNT.lstrip('/')))
#Fixup pool to only accept stuff on /cdrom or /isodevice
# - This is reverted during SUCCESS_SCRIPT
# - Might be in livefs already, but we always copy in in case there was an udpate
pool_cmd = '/usr/share/dell/scripts/pool.sh'
shutil.copy(pool_cmd, os.path.join(self.target, 'tmp', os.path.basename(pool_cmd)))
install_misc.chrex(self.target, os.path.join('/tmp', os.path.basename(pool_cmd)))
#Stuff that is installed on all configs without fish scripts
to_install += magic.mark_unconditional_debs()
#install dell-recovery only if there is an RP
if rec_part:
to_install.append('dell-recovery')
to_install.append('dell-eula')
#block os-prober in grub-installer
os.rename('/usr/bin/os-prober', '/usr/bin/os-prober.real')
#don't allow OS prober to probe other drives in single OS install
with open(os.path.join(self.target, 'etc/default/grub'), 'r') as rfd:
default_grub = rfd.readlines()
with open(os.path.join(self.target, 'etc/default/grub'), 'w') as wfd:
found = False
for line in default_grub:
if line.startswith("GRUB_DISABLE_OS_PROBER="):
line = "GRUB_DISABLE_OS_PROBER=true\n"
found = True
wfd.write(line)
if not found:
wfd.write("GRUB_DISABLE_OS_PROBER=true\n")
#for tos
try:
destination = progress.get('dell-recovery/destination')
except debconf.DebconfError:
destination = ''
fname = os.path.join(self.target, 'etc', 'default', 'dell-eula')
if destination and not os.path.exists(fname):
with open(fname, 'w') as wfd:
wfd.write('WARRANTY=%s\n' % destination)
to_install += magic.mark_upgrades()
self.remove_unwanted_drivers()
self.remove_ricoh_mmc()
self.wake_network()
install_misc.record_installed(to_install)
install_misc.record_removed(to_remove)
return InstallPlugin.install(self, target, progress, *args, **kwargs)
dell-recovery/ubiquity/stepDellBootstrap.ui 0000664 0000000 0000000 00000116375 12670505071 016426 0 ustar
False5Advanced OptionsFalseTruecenterdialogFalseTrueFalse2TrueFalseendgtk-closeFalseTrueTrueTrueFalseTrueFalseFalse1FalseTrueend0TrueFalse0<b>Runtime Details</b>TrueFalseFalse1TrueFalse0versionTrueTrue2TrueFalse0mountTrueTrue3TrueFalse0memoryTrueTrue4TrueFalse0<b>Advanced Options</b>TrueFalseFalse5TrueFalse721010TrueFalseproprietary_liststore01212GTK_FILLTrueFalseDisable Proprietary Drivers12TrueFalserp_filesystem_liststore11245GTK_FILLTrueFalseRecovery Partition Filesystem45TrueFalseSwap Partition Behavior56TrueFalseswap_liststore01256GTK_FILLTrueTrue6button1False5center-alwaysdialogTrueFalseerrorokException EncounteredTrueFalse2TrueFalseendFalseTrueend0TrueFalse00gnome-session-rebootTrueFalse00gnome-session-reboot
fglrx_updates
nvidia_current_updates
fglrx_updates,nvidia_current_updates
0c
vfat
07
ntfs
TrueFalse650TrueFalseTrueFalseGDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASKTrueFalseGDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK | GDK_STRUCTURE_MASK128dell-dvdTrueTrue0TrueFalseTrueFalse00This Dell Recovery Media can be used to restore the original factory software.TrueTrueFalseFalse0TrueFalse00<b>It is recommended you back up all important data before running this tool.</b>TrueTrueFalseFalse1TrueTrue111FalseFalse0600TrueFalse20FalseTrueFalse48object-rotate-rightFalseFalse0TrueFalse5Restore Linux OS partitions.FalseTrueTrueFalseFalseTrueautomated_recoveryFalseFalse0TrueFalse25600TrueFalse00This will rebuild all OS and swap partitions to factory defaults.TrueTrueFalseFalse1TrueFalse25600TrueFalse00<b>WARNING: </b>All personal files and changes will be lost.TrueTrueFalseFalse2TrueTrue1TrueFalse0TrueFalseTrueFalse48applications-utilitiesFalseFalse0TrueFalse5Restore entire hard drive.FalseTrueTrueFalseFalseTrueTrueFalseFalse0TrueFalse0025TrueFalseFalsehard_drive_liststore2FalseFalse1TrueFalse25600TrueFalse00This will rebuild all recovery, and OS partitions.TrueTrueFalseFalse2TrueFalse25600TrueFalse00<b>WARNING: </b>All personal files and changes will be lost.TrueTrueFalseFalse3TrueTrue1TrueFalse1TrueFalseTrueFalse48distributor-logoFalseFalse0TrueFalse5Restore only Linux OS partition.FalseTrueTrueFalseFalse0.49000000953674316Trueautomated_recoveryFalseFalse0TrueFalse25600TrueFalse00This option allows you to resize any existing partitions.TrueTrueFalseFalse1TrueTrue1TrueFalse2Internal Radio ButtonFalseTrueFalseFalseTrueautomated_recoveryTrueFalse3FalseTrueFalse48emblem-importantFalseTrue0TrueFalse0.05000000074505806<b>ERROR:</b> This recovery media only functions on Dell or Alienware systems.TrueTrueTrueTrue1TrueFalse4False5TrueFalse00<b>Building Recovery Partition</b>TrueTrueFalseFalse0TrueFalse00<b>NOTE:</b> Please be patient, this may take a while.TrueTrueFalseFalse1TrueFalse00Your system will restart automatically several times.TrueTrueTrueTrue2TrueFalseTrueTrue3TrueFalse5TrueTrue1
dynamic
true
false
dell-recovery/ubiquity/dell-bootstrap 0000775 0000000 0000000 00000000142 12670505071 015256 0 ustar #!/bin/sh -e
. /usr/share/debconf/confmodule
db_input high dell-recovery/recovery_type || true
dell-recovery/ubiquity/dell-recovery.py 0000664 0000000 0000000 00000030671 12703005517 015532 0 ustar #!/usr/bin/python3
# -*- coding: utf-8 -*-
#
# «dell-recovery» - OEM Config plugin for Dell-Recovery Media
#
# Copyright (C) 2010-2014, Dell Inc.
#
# Author:
# - Mario Limonciello
#
# This 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 application; if not, write to the Free Software Foundation, Inc., 51
# Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
##################################################################################
from ubiquity.plugin import PluginUI, InstallPlugin, Plugin
import pwd
import subprocess
import os
import Dell.recovery_common as magic
import dbus
import syslog
import gi
NAME = 'dell-recovery'
AFTER = 'usersetup'
BEFORE = None
WEIGHT = 12
ROTATIONAL_CHAR = ['\\', '|', '/', '-']
#Gtk widgets
class PageGtk(PluginUI):
"""GTK frontend for the dell-recovery oem-config plugin"""
plugin_title = 'ubiquity/text/recovery_heading_label'
def __init__(self, controller, *args, **kwargs):
self.controller = controller
rpart = magic.find_partition()
dvd, usb = magic.find_burners()
oem = 'UBIQUITY_OEM_USER_CONFIG' in os.environ
self.genuine = magic.check_vendor()
if oem and (dvd or usb) and (rpart or not self.genuine):
try:
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
builder = Gtk.Builder()
builder.add_from_file('/usr/share/ubiquity/gtk/stepRecoveryMedia.ui')
builder.connect_signals(self)
self.controller.add_builder(builder)
self.plugin_widgets = builder.get_object('stepRecoveryMedia')
self.usb_media = builder.get_object('save_to_usb')
self.dvd_media = builder.get_object('save_to_dvd')
self.none_media = builder.get_object('save_to_none')
self.grub_menu_98 = builder.get_object('98_grub_menu')
self.grub_menu_99 = builder.get_object('99_grub_menu')
if not dvd:
builder.get_object('dvd_box').hide()
if not usb:
builder.get_object('usb_box').hide()
if not self.genuine:
builder.get_object('usb_box').hide()
builder.get_object('dvd_box').hide()
builder.get_object('none_box').hide()
builder.get_object('genuine_box').show()
except Exception as err:
syslog.syslog('Could not create Dell Recovery page: %s', err)
self.plugin_widgets = None
else:
if not oem:
pass
elif not rpart:
syslog.syslog('%s: partition problems with rp[%s]'
% (NAME, rpart))
self.plugin_widgets = None
PluginUI.__init__(self, controller, *args, **kwargs)
def plugin_get_current_page(self):
"""Called when ubiquity tries to realize this page."""
if not self.genuine:
self.controller.allow_go_forward(False)
return self.plugin_widgets
def get_grub_line(self, grub_line):
if grub_line == '98_grub_menu':
obj = self.grub_menu_98
else:
obj = self.grub_menu_99
return obj.get_text()
def get_type(self):
"""Returns the type of recovery to do from GUI"""
if self.usb_media.get_active():
return "usb"
elif self.dvd_media.get_active():
return "dvd"
else:
return "none"
def set_type(self, value):
"""Sets the type of recovery to do in GUI"""
if value == "usb":
self.usb_media.set_active(True)
elif value == "dvd":
self.dvd_media.set_active(True)
else:
self.none_media.set_active(True)
class Page(Plugin):
"""Debconf driven page for the dell-recovery oem-config plugin"""
def prepare(self, unfiltered=False):
"""Prepares the debconf plugin"""
destination = self.db.get('dell-recovery/destination')
self.ui.set_type(destination)
return Plugin.prepare(self, unfiltered=unfiltered)
def ok_handler(self):
"""Handler ran when OK is pressed"""
destination = self.ui.get_type()
self.preseed('dell-recovery/destination', destination)
for item in ['98', '99']:
self.preseed('ubiquity/text/%s_grub_menu' % item, self.ui.get_grub_line('%s_grub_menu' % item))
Plugin.ok_handler(self)
class Install(InstallPlugin):
"""The dell-recovery media creator install time ubiquity plugin"""
def __init__(self, frontend, db=None, ui=None):
self.index = 0
self.progress = None
InstallPlugin.__init__(self, frontend, db, ui)
def log(self, error):
"""Outputs a debugging string to /var/log/installer/debug"""
self.debug("%s: %s" % (NAME, error))
def _update_progress_gui(self, progress_text, progress_percent):
"""Function called by the backend to update the progress in frontend"""
self.progress.substitute('dell-recovery/build_progress', 'MESSAGE', \
progress_text)
if float(progress_percent) < 0:
if self.index >= len(ROTATIONAL_CHAR):
self.index = 0
progress_percent = ROTATIONAL_CHAR[self.index]
self.index += 1
else:
progress_percent += "%"
self.progress.substitute('dell-recovery/build_progress', 'PERCENT', \
progress_percent)
self.progress.info('dell-recovery/build_progress')
def Set_RootPartitionLabel(self):
'''find the / mount partition then label it with UBUNTU for further use'''
mount_output = magic.fetch_output(['mount']).split('\n')
for line in mount_output:
item = line.split()
if '/' in item:
try:
from ubiquity import misc
misc.execute_root('e2label',item[0],'UBUNTU')
except Exception:
pass
def install(self, target, progress, *args, **kwargs):
"""Perform actual install time activities for oem-config"""
if not 'UBIQUITY_OEM_USER_CONFIG' in os.environ:
return
#find the '/' mount partition and then label it as UBUNTU
self.Set_RootPartitionLabel()
env = os.environ
lang = progress.get('debian-installer/locale')
env['LANG'] = lang
#can also expect that this was mounted at /cdrom during OOBE
rpart = magic.find_factory_partition_stats()
if rpart and os.path.exists('/cdrom/.disk/info.recovery'):
rec_text = progress.get('ubiquity/text/99_grub_menu')
magic.process_conf_file(original = '/usr/share/dell/grub/99_dell_recovery', \
new = '/etc/grub.d/99_dell_recovery', \
uuid = str(rpart["uuid"]), \
number = str(rpart["number"]), \
recovery_text = rec_text)
os.chmod('/etc/grub.d/99_dell_recovery', 0o755)
subprocess.call(['update-grub'],env=env)
dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
self.progress = progress
user = progress.get('passwd/username')
uid = pwd.getpwnam(user).pw_uid
gid = pwd.getpwnam(user).pw_gid
rec_type = progress.get('dell-recovery/destination')
if rec_type != "none":
dvd, usb = magic.find_burners()
rpart = magic.find_partition()
self.index = 0
#build all the user's home directories a little earlier than normal
subprocess.call(['su', user, '-c', 'xdg-user-dirs-update'])
directory = magic.fetch_output(['su', user, '-c', '/usr/bin/xdg-user-dir DOWNLOAD']).strip()
fname = os.path.join(directory, 'factory_image.iso')
try:
bus = dbus.SystemBus()
dbus_iface = dbus.Interface(bus.get_object(magic.DBUS_BUS_NAME,
'/RecoveryMedia'),
magic.DBUS_INTERFACE_NAME)
except Exception as err:
self.log('install function exception while creating dbus backend: %s' % str(err))
return
progress.info('dell-recovery/build_start')
#Determine internal version number of image
(version, date) = dbus_iface.query_bto_version(rpart)
version = magic.increment_bto_version(version)
self.log("Generating recovery media from %s : %s" % (version, date))
#Build image
try:
magic.dbus_sync_call_signal_wrapper(dbus_iface,
'create_ubuntu',
{'report_progress':self._update_progress_gui},
rpart,
version,
fname)
os.chown(fname.encode('utf-8'), uid, gid)
except dbus.DBusException as err:
self.log('install function exception while calling backend: %s' % str(err))
return
#Close backend
try:
dbus_iface.request_exit()
except dbus.DBusException as err:
if hasattr(err, '_dbus_error_name') and err._dbus_error_name == \
'org.freedesktop.DBus.Error.ServiceUnknown':
pass
else:
self.log("Received %s when closing recovery-media-backend" \
% str(err))
return
if rec_type:
if rec_type == "dvd":
cmd = ['dbus-launch'] + dvd + [fname.encode('utf-8')]
else:
cmd = ['dbus-launch'] + usb + [fname.encode('utf-8')]
if 'DBUS_SESSION_BUS_ADDRESS' in os.environ:
os.environ.pop('DBUS_SESSION_BUS_ADDRESS')
progress.info('dell-recovery/burning')
subprocess.call(cmd)
else:
#Mark burning tool to launch on 7th day
directory = '/home/%s/.config/autostart' % user
if not os.path.exists(directory):
os.makedirs(directory)
os.chown('/home/%s/.config' % user, uid, gid)
os.chown(directory, uid, gid)
fname = os.path.join(directory, 'dell-recovery.desktop')
with open('/usr/share/applications/dell-recovery-media.desktop', encoding='utf-8') as rfd:
with open(fname, 'w', encoding='utf-8') as wfd:
for line in rfd.readlines():
if line.startswith('Exec='):
line = 'Exec=/home/%s/.config/dell-recovery/reminder\n' % user
wfd.write(line)
os.chown(fname, uid, gid)
directory = '/home/%s/.config/dell-recovery' % user
if not os.path.exists(directory):
os.makedirs(directory)
os.chown(directory, uid, gid)
fname = os.path.join(directory, 'reminder')
date = magic.fetch_output(['date', '-d', '+7 days', '+%y%m%d'])
with open(fname, 'w', encoding='utf-8') as wfd:
wfd.write('#!/bin/sh\n')
wfd.write('LAUNCH=%s\n' % date)
wfd.write('TODAY=$(date +"%y%m%d")\n')
wfd.write('if [ $TODAY -ge $LAUNCH ]; then\n')
wfd.write(' dell-recovery\n')
wfd.write('fi\n')
os.chown(fname, uid, gid)
os.chmod(fname, 0o744)
return InstallPlugin.install(self, target, progress, *args, **kwargs)
dell-recovery/gtk/ 0000775 0000000 0000000 00000000000 12704013123 011300 5 ustar dell-recovery/gtk/dell-package.svg 0000664 0000000 0000000 00000023526 12670505071 014354 0 ustar
dell-recovery/gtk/dell-dvd.svg 0000664 0000000 0000000 00000007166 12670505071 013540 0 ustar