Advertisement
Guest User

Untitled

a guest
Jun 1st, 2017
98
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 17.46 KB | None | 0 0
  1. import os
  2. import subprocess
  3. from subprocess import Popen
  4. import time
  5. import shutil
  6. import gettext
  7. import stat
  8. import commands
  9. from configobj import ConfigObj
  10.  
  11. gettext.install("distro-installer", "/usr/share/locale")
  12.            
  13. class SystemUser:
  14.     ''' Represents the main user '''
  15.    
  16.     def __init__(self, username=None, realname=None, password=None, sudo=False):
  17.         ''' create new SystemUser '''
  18.         self.username = username
  19.         self.realname = realname
  20.         self.password = password
  21.         self.sudo = sudo
  22.  
  23. class HostMachine:
  24.     ''' Used to probe information about the host '''
  25.    
  26.     def is_laptop(self):
  27.         ''' Returns True/False as to whether the host is a laptop '''
  28.         ret = False
  29.         try:
  30.             p = Popen("laptop-detect", shell=True)
  31.             p.wait() # we want the return code
  32.             retcode = p.returncode
  33.             if(retcode == 0):
  34.                 # its a laptop
  35.                 ret = True
  36.         except:
  37.             pass # doesn't matter, laptop-detect doesnt exist on the host
  38.         return ret
  39.        
  40.     def get_model(self):
  41.         ''' return the model of the pooter '''
  42.         ret = None
  43.         try:
  44.             model = commands.getoutput("dmidecode --string system-product-name")
  45.             ret = model.rstrip("\r\n").lstrip()
  46.         except:
  47.             pass # doesn't matter.
  48.         return ret
  49.        
  50.     def get_manufacturer(self):
  51.         ''' return the system manufacturer '''
  52.         ret = None
  53.         try:
  54.             manu = commands.getoutput("dmidecode --string system-manufacturer")
  55.             ret = manu.rstrip("\r\n ").lstrip()
  56.         except:
  57.             pass # doesn't matter
  58.         return ret
  59.            
  60. class InstallerEngine:
  61.     ''' This is central to the distro installer '''
  62.    
  63.     # some noice definitions..
  64.     PROGRESS_START        = "start"
  65.     PROGRESS_UPDATE     = "update"
  66.     PROGRESS_COMPLETE    = "complete"
  67.     PROGRESS_ERROR        = "error"
  68.    
  69.     def __init__(self):
  70.         # set up stuffhs
  71.         self.conf_file = '/etc/distro-installer/install.conf'
  72.         configuration = ConfigObj(self.conf_file)
  73.         distro = configuration['distro']
  74.         install = configuration['install']
  75.         self.distro_name = distro['DISTRO_NAME']
  76.         self.distro_version = distro['DISTRO_VERSION']
  77.  
  78.         self.user = None
  79.         self.live_user = install['LIVE_USER_NAME']
  80.         self.set_install_media(media=install['LIVE_MEDIA_SOURCE'], type=install['LIVE_MEDIA_TYPE'])
  81.        
  82.         self.grub_device = None
  83.        
  84.     def set_main_user(self, user):
  85.         ''' Set the main user to be used by the installer '''
  86.         if(user is not None):
  87.             self.user = user
  88.        
  89.     def get_main_user(self):
  90.         ''' Return the main user '''
  91.         return self.user
  92.        
  93.     def set_root_password(self, password):
  94.         ''' Set the root (0) user password '''
  95.         self.root_password = password
  96.        
  97.     def format_device(self, device, filesystem):
  98.         ''' Format the given device to the specified filesystem '''
  99.         cmd = "mkfs -t %s %s" % (filesystem, device)
  100.         p = Popen(cmd, shell=True)
  101.         p.wait() # this blocks
  102.         return p.returncode
  103.        
  104.     def set_install_media(self, media=None, type=None):
  105.         ''' Sets the location of our install source '''
  106.         self.media = media
  107.         self.media_type = type
  108.  
  109.     def set_hostname(self, hostname):
  110.         ''' Set the hostname on the target machine '''
  111.         self.hostname = hostname
  112.  
  113.     def set_install_bootloader(self, device=None):
  114.         ''' The device to install grub to '''
  115.         self.grub_device = device
  116.        
  117.     def add_to_blacklist(self, blacklistee):
  118.         ''' This will add a directory or file to the blacklist, so that '''
  119.         ''' it is not copied onto the new filesystem '''
  120.         try:
  121.             self.blacklist.index(blacklistee)
  122.             self.blacklist.append(blacklistee)
  123.         except:
  124.             # We haven't got this item yet
  125.             pass
  126.  
  127.     def set_progress_hook(self, progresshook):
  128.         ''' Set a callback to be called on progress updates '''
  129.         ''' i.e. def my_callback(progress_type, message, current_progress, total) '''
  130.         ''' Where progress_type is any off PROGRESS_START, PROGRESS_UPDATE, PROGRESS_COMPLETE, PROGRESS_ERROR '''
  131.         self.update_progress = progresshook
  132.  
  133.     def get_distro_name(self):
  134.         return self.distro_name
  135.        
  136.     def get_distro_version(self):
  137.         return self.distro_version
  138.        
  139.     def get_locale(self):
  140.         ''' Return the locale we're setting '''
  141.         return self.locale
  142.    
  143.     def set_locale(self, newlocale):
  144.         ''' Set the locale '''
  145.         self.locale = newlocale
  146.        
  147.     def install(self):
  148.         ''' Install this baby to disk '''
  149.         # mount the media location.
  150.         try:
  151.             if(not os.path.exists("/target")):
  152.                 os.mkdir("/target")
  153.             if(not os.path.exists("/source")):
  154.                 os.mkdir("/source")
  155.             # find the squashfs..
  156.             root = self.media
  157.             root_type = self.media_type
  158.             if(not os.path.exists(root)):
  159.                 print _("Base filesystem does not exist! Bailing")
  160.                 sys.exit(1) # change to report
  161.             root_device = None
  162.             # format partitions as appropriate
  163.             for item in self.fstab.get_entries():
  164.                 if(item.mountpoint == "/"):
  165.                     root_device = item
  166.                     item.format = True
  167.                 if(item.format):
  168.                     # well now, we gets to nuke stuff.
  169.                     # report it. should grab the total count of filesystems to be formatted ..
  170.                     self.update_progress(total=4, current=1, message=_("Formatting %s to %s..." % (item.device, item.filesystem)))
  171.                     self.format_device(item.device, item.filesystem)
  172.             # mount filesystem
  173.             self.update_progress(total=4, current=2, message=_("Mounting %s on %s") % (root, "/source/"))
  174.             self.do_mount(root, "/source/", root_type, options="loop")
  175.             self.update_progress(total=4, current=3, message=_("Mounting %s on %s") % (root_device.device, "/target/"))
  176.             self.do_mount(root_device.device, "/target", root_device.filesystem, None)
  177.             # walk root filesystem. we're too lazy though :P
  178.             SOURCE = "/source/"
  179.             DEST = "/target/"
  180.             directory_times = []
  181.             our_total = 0
  182.             our_current = -1
  183.             os.chdir(SOURCE)
  184.             # index the files
  185.             for top,dirs,files in os.walk(SOURCE, topdown=False):
  186.                 our_total += len(dirs) + len(files)
  187.                 self.update_progress(pulse=True, message=_("Indexing files to be copied.."))
  188.             our_total += 1 # safenessness
  189.             for top,dirs,files in os.walk(SOURCE):
  190.                 # Sanity check. Python is a bit schitzo
  191.                 dirpath = top
  192.                 if(dirpath.startswith(SOURCE)):
  193.                     dirpath = dirpath[len(SOURCE):]
  194.                 for name in dirs + files:
  195.                     # following is hacked/copied from Ubiquity
  196.                     rpath = os.path.join(dirpath, name)
  197.                     sourcepath = os.path.join(SOURCE, rpath)
  198.                     targetpath = os.path.join(DEST, rpath)
  199.                     st = os.lstat(sourcepath)
  200.                     mode = stat.S_IMODE(st.st_mode)
  201.  
  202.                     # now show the world what we're doing
  203.                     our_current += 1
  204.                     self.update_progress(total=our_total, current=our_current, message=_("Copying %s" % rpath))
  205.  
  206.                     if stat.S_ISLNK(st.st_mode):
  207.                         if os.path.lexists(targetpath):
  208.                             os.unlink(targetpath)
  209.                         linkto = os.readlink(sourcepath)
  210.                         os.symlink(linkto, targetpath)
  211.                     elif stat.S_ISDIR(st.st_mode):
  212.                         if not os.path.isdir(targetpath):
  213.                             os.mkdir(targetpath, mode)
  214.                     elif stat.S_ISCHR(st.st_mode):
  215.                         os.mknod(targetpath, stat.S_IFCHR | mode, st.st_rdev)
  216.                     elif stat.S_ISBLK(st.st_mode):
  217.                         os.mknod(targetpath, stat.S_IFBLK | mode, st.st_rdev)
  218.                     elif stat.S_ISFIFO(st.st_mode):
  219.                         os.mknod(targetpath, stat.S_IFIFO | mode)
  220.                     elif stat.S_ISSOCK(st.st_mode):
  221.                         os.mknod(targetpath, stat.S_IFSOCK | mode)
  222.                     elif stat.S_ISREG(st.st_mode):
  223.                         # we don't do blacklisting yet..
  224.                         try:
  225.                             os.unlink(targetpath)
  226.                         except:
  227.                             pass
  228.                         self.copy_file(sourcepath, targetpath)
  229.                     os.lchown(targetpath, st.st_uid, st.st_gid)
  230.                     if not stat.S_ISLNK(st.st_mode):
  231.                         os.chmod(targetpath, mode)
  232.                     if stat.S_ISDIR(st.st_mode):
  233.                         directory_times.append((targetpath, st.st_atime, st.st_mtime))
  234.                     # os.utime() sets timestamp of target, not link
  235.                     elif not stat.S_ISLNK(st.st_mode):
  236.                         os.utime(targetpath, (st.st_atime, st.st_mtime))
  237.             # Apply timestamps to all directories now that the items within them
  238.             # have been copied.
  239.             for dirtime in directory_times:
  240.                 (directory, atime, mtime) = dirtime
  241.                 try:
  242.                     self.update_progress(pulse=True, message=_("Restoring meta-information on %s" % directory))
  243.                     os.utime(directory, (atime, mtime))
  244.                 except OSError:
  245.                     pass
  246.             # Steps:
  247.             our_total = 8
  248.             our_current = 0
  249.             # chroot
  250.             self.update_progress(total=our_total, current=our_current, message=_("Entering new system.."))
  251.             os.system("mkfifo /target/tmp/INSTALL_PIPE")
  252.             os.system("mount --bind /dev/ /target/dev/")
  253.             os.system("mount --bind /dev/shm /target/dev/shm")
  254.             os.system("mount --bind /dev/pts /target/dev/pts")
  255.             os.system("mount --bind /sys/ /target/sys/")
  256.             os.system("mount --bind /proc/ /target/proc/")
  257.             child_pid = os.fork()
  258.             if(child_pid == 0):
  259.                 # we be the child.
  260.                 os.chroot("/target/")
  261.                 # remove live user
  262.                 live_user = self.live_user
  263.                 our_current += 1
  264.                 self.sub_update_progress(total=our_total, current=our_current, message=_("Removing live configuration (user)"))
  265.                 os.system("deluser %s" % live_user)
  266.                 # can happen
  267.                 if(os.path.exists("/home/%s" % live_user)):
  268.                     os.system("rm -rf /home/%s" % live_user)
  269.                 # remove live-initramfs (or w/e)
  270.                 our_current += 1
  271.                 self.sub_update_progress(total=our_total, current=our_current, message=_("Removing live configuration (packages)"))
  272.                 os.system("apt-get remove --purge --yes --force-yes live-initramfs distro-installer")
  273.                 # add new user
  274.                 our_current += 1
  275.                 self.sub_update_progress(total=our_total, current=our_current, message=_("Adding new user to system"))
  276.                 user = self.get_main_user()
  277.                 if(user.sudo):
  278.                     os.system("useradd -s %s -c \"%s\" -G sudo -m %s" % ("/bin/bash", user.realname, user.username))
  279.                 else:
  280.                     os.system("useradd -s %s -c \"%s\" -m %s" % ("/bin/bash", user.realname, user.username))
  281.                 newusers = open("/tmp/newusers.conf", "w")
  282.                 newusers.write("%s:%s\n" % (user.username, user.password))
  283.                 # add root's password
  284.                 our_current += 1
  285.                 self.sub_update_progress(total=our_total, current=our_current, message=_("Setting root password"))
  286.                 root_password = self.root_password
  287.                 newusers.write("root:%s\n" % root_password)
  288.                 newusers.close()
  289.                 os.system("cat /tmp/newusers.conf | chpasswd")
  290.                 os.system("rm -rf /tmp/newusers.conf")
  291.                 # write the /etc/fstab
  292.                 our_current += 1
  293.                 self.sub_update_progress(total=our_total, current=our_current, message=_("Writing filesystem mount information"))
  294.                 # make sure fstab has default /proc and /sys entries
  295.                 if(not os.path.exists("/etc/fstab")):
  296.                     os.system("echo \"#### Static Filesystem Table File\" > /etc/fstab")
  297.                 fstabber = open("/etc/fstab", "a")
  298.                 fstabber.write("proc\t/proc\tproc\tnodev,noexec,nosuid\t0\t0\n")
  299.                 for item in self.fstab.get_entries():
  300.                     if(item.options is None):
  301.                         item.options = "rw,errors=remount-ro"
  302.                     if(item.filesystem == "swap"):
  303.                         # special case..
  304.                         fstabber.write("%s\tnone\tswap\tsw\t0\t0" % item.device)
  305.                     else:
  306.                         fstabber.write("%s\t%s\t%s\t%s\t%s\t%s\n" % (item.device, item.mountpoint, item.filesystem, item.options, "0", "0"))
  307.                 fstabber.close()
  308.         # write host+hostname infos
  309.         our_current += 1
  310.         self.sub_update_progress(total=our_total, current=our_current, message=_("Setting hostname"))
  311.         hostnamefh = open("/etc/hostname", "w")
  312.         hostnamefh.write("%s\n" % self.hostname)
  313.         hostnamefh.close()
  314.         hostsfh = open("/etc/hosts", "w")
  315.         hostsfh.write("127.0.0.1\tlocalhost\n")
  316.         hostsfh.write("127.0.1.1\t%s\n" % self.hostname)
  317.         hostsfh.write("# The following lines are desirable for IPv6 capable hosts\n")
  318.         hostsfh.write("::1     localhost ip6-localhost ip6-loopback\n")
  319.         hostsfh.write("fe00::0 ip6-localnet\n")
  320.         hostsfh.write("ff00::0 ip6-mcastprefix\n")
  321.         hostsfh.write("ff02::1 ip6-allnodes\n")
  322.         hostsfh.write("ff02::2 ip6-allrouters\n")
  323.         hostsfh.write("ff02::3 ip6-allhosts\n")
  324.         hostsfh.close()
  325.  
  326.         # gdm overwrite (specific to Debian/live-initramfs)
  327.         gdmconffh = open("/etc/gdm3/daemon.conf", "w")
  328.         gdmconffh.write("# GDM configuration storage\n")
  329.         gdmconffh.write("\n[daemon]\n")
  330.         gdmconffh.write("\n[security]\n")
  331.         gdmconffh.write("\n[xdmcp]\n")
  332.         gdmconffh.write("\n[greeter]\n")
  333.         gdmconffh.write("\n[chooser]\n")
  334.         gdmconffh.write("\n[debug]\n")
  335.         gdmconffh.close()
  336.  
  337.                 # write MBR (grub)
  338.         our_current += 1
  339.         if(self.grub_device is not None):
  340.                 self.sub_update_progress(total=our_total, current=our_current, message=_("Installing bootloader"))
  341.                 os.system("grub-install %s" % self.grub_device) # safety..
  342.                 os.system("update-grub")
  343.                 # ^ needs to be done in a chroot ._.
  344.                 # notify that we be finished now.
  345.         our_current += 1
  346.         self.sub_update_progress(done=True, total=our_total, current=our_current, message=_("Done."))
  347.             else:
  348.                 thepipe = open("/target/tmp/INSTALL_PIPE", "r")
  349.                 while(True):
  350.                     if(not os.path.exists("/target/tmp/INSTALL_PIPE")):
  351.                         break # file may disappear
  352.                     line = thepipe.readline()
  353.                     line = line.rstrip("\r\n")
  354.                     if (line.replace(" ", "") == ""):
  355.                         continue # skip blank lines
  356.                     if( line == "DONE" ):
  357.                         break
  358.                     self.update_progress(pulse=True, message=line)
  359.                 # now nuke the pipe
  360.                 if(os.path.exists("/target/tmp/INSTALL_PIPE")):
  361.                     os.unlink("/target/tmp/INSTALL_PIPE")
  362.                    
  363.                 # now unmount it
  364.             os.system("umount --force /target/dev/shm")
  365.             os.system("umount --force /target/dev/pts")
  366.             os.system("umount --force /target/dev/")
  367.             os.system("umount --force /target/sys/")
  368.             os.system("umount --force /target/proc/")
  369.             self.do_unmount("/target")
  370.             self.do_unmount("/source")
  371.         except Exception,detail:
  372.             print detail
  373.  
  374.     def sub_update_progress(self, total=None,current=None,fail=False,done=False,message=None):
  375.         ''' Only called from the chroot '''
  376.         if(fail or done):
  377.             os.system("echo \"DONE\" >> /tmp/INSTALL_PIPE")
  378.         else:
  379.             os.system("echo \"%s\" >> /tmp/INSTALL_PIPE" % message)
  380.            
  381.     def do_mount(self, device, dest, type, options=None):
  382.         ''' Mount a filesystem '''
  383.         p = None
  384.         if(options is not None):
  385.             p = Popen("mount -o %s -t %s %s %s" % (options, type, device, dest),shell=True)
  386.         else:
  387.             p = Popen("mount -t %s %s %s" % (type, device, dest),shell=True)
  388.         p.wait()
  389.         return p.returncode
  390.        
  391.     def do_unmount(self, mountpoint):
  392.         ''' Unmount a filesystem '''
  393.         p = Popen("umount %s" % mountpoint, shell=True)
  394.         p.wait()
  395.         return p.returncode
  396.        
  397.     def copy_file(self, source, dest):
  398.         # TODO: Add md5 checks. BADLY needed..
  399.         BUF_SIZE = 16 * 1048
  400.         input = open(source, "rb")
  401.         dst = open(dest, "wb")
  402.         while(True):
  403.             read = input.read(BUF_SIZE)
  404.             if not read:
  405.                 break
  406.             dst.write(read)
  407.         input.close()
  408.         dst.close()
  409.                
  410. class fstab(object):
  411.     ''' This represents the filesystem table (/etc/fstab) '''
  412.     def __init__(self):
  413.         self.mapping = dict()
  414.        
  415.     def add_mount(self, device=None, mountpoint=None, filesystem=None, options=None,format=False):
  416.         if(not self.mapping.has_key(device)):
  417.             self.mapping[device] = fstab_entry(device, mountpoint, filesystem, options)
  418.             self.mapping[device].format = format
  419.    
  420.     def remove_mount(self, device):
  421.         if(self.mapping.has_key(device)):
  422.             del self.mapping[device]
  423.  
  424.     def get_entries(self):
  425.         ''' Return our list '''
  426.         return self.mapping.values()
  427.  
  428.     def has_device(self, device):
  429.         return self.mapping.has_key(device)
  430.        
  431.     def has_mount(self, mountpoint):
  432.         for item in self.get_entries():
  433.             if(item.mountpoint == mountpoint):
  434.                 return True
  435.         return False
  436.        
  437. class fstab_entry(object):
  438.     ''' Represents an entry in fstab '''
  439.    
  440.     def __init__(self, device, mountpoint, filesystem, options):
  441.         ''' Creates a new fstab entry '''
  442.         self.device = device
  443.         self.mountpoint = mountpoint
  444.         self.filesystem = filesystem
  445.         self.options = options
  446.         self.format = False
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement