Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 22 Aug 2007 22:32:08 GMT
From:      Ivan Voras <ivoras@FreeBSD.org>
To:        Perforce Change Reviews <perforce@FreeBSD.org>
Subject:   PERFORCE change 125568 for review
Message-ID:  <200708222232.l7MMW8TN090877@repoman.freebsd.org>

next in thread | raw e-mail | index | archive | help
http://perforce.freebsd.org/chv.cgi?CH=125568

Change 125568 by ivoras@ivoras_finstall on 2007/08/22 22:31:40

	Started work on post-install system configuration

Affected files ...

.. //depot/projects/soc2007/ivoras_finstall/installer/finstall.py#13 edit
.. //depot/projects/soc2007/ivoras_finstall/installer/glade/createuser.glade#1 add
.. //depot/projects/soc2007/ivoras_finstall/installer/glade/hostroot.glade#1 add
.. //depot/projects/soc2007/ivoras_finstall/installer/text/createuser.txt#1 add
.. //depot/projects/soc2007/ivoras_finstall/installer/text/hostroot.txt#1 add
.. //depot/projects/soc2007/ivoras_finstall/pybackend/freebsd.py#8 edit
.. //depot/projects/soc2007/ivoras_finstall/pybackend/systoolengine.py#11 edit

Differences ...

==== //depot/projects/soc2007/ivoras_finstall/installer/finstall.py#13 (text+ko) ====

@@ -23,7 +23,7 @@
 # finstall Installer
 
 import os,sys,time
-import logging
+import logging, base64, random
 from types import MethodType
 from xmlrpclib import ServerProxy
 import gobject, gtk, gtk.gdk, gtk.glade
@@ -45,7 +45,13 @@
 		{ "tile" : "nparts" },
 		{ "tile" : "ndefaultfs" },
 		{ "tile" : "nverify" },
-		{ "tile" : "ninstall", "glade" : "installprogress.glade" }
+		{ "tile" : "ninstall", "glade" : "installprogress.glade" },
+		{ "tile" : "hostroot" },
+		{ "tile" : "createuser" },
+		{ "tile" : "netconf" },
+		{ "tile" : "x11conf" },
+		{ "tile" : "soundconf" },
+		{ "tile" : "nfinish" }
 	]
 
 
@@ -93,7 +99,7 @@
 		if "on_load" in self.tile_handlers:
 			try:
 				if not self.tile_handlers["on_load"]():
-					logging.error("On_Load refused by %s, but it's not implemented" % tile_name)
+					logging.error("On_Load refused by %s, but it's ignored (not implemented yet)" % tile_name)
 			except:
 				logging.exception("Error executing on_load handler for %s" % tile_name)
 
@@ -317,8 +323,6 @@
 			/var
 			/home
 		and a symlink from /usr/ports to /usr/local/ports.
-		We'll not use the traditional BSD layout of bsdlabels (a=root, b=swap), except
-		for the unfortunately special "c" label.
 		"""
 
 		if self["radio_ufs"].get_active():
@@ -359,7 +363,7 @@
 
 		# Calculate the partitioning scheme
 		var_size = min(2048, int(self.trackdata["drive_size"] * 0.1))
-		usr_size = min(15*1024, int(self.trackdata["drive_size"] * 0.3))
+		usr_size = min(10*1024, int(self.trackdata["drive_size"] * 0.5))
 
 		if fs in ("UFS+SU", "UFS+GJ", "Ext2"):
 			# "Normal" file systems, unix-like
@@ -527,6 +531,7 @@
 		self.next_progress_advert()
 		self.trackdata["part_job"] = self.server.StartPartitionJob(self.trackdata["new_parts"])
 		gobject.timeout_add(500, self.part_progress)
+		return True
 
 	
 	def add_install_list(self, msg):
@@ -559,7 +564,7 @@
 			self._show_message(result[-1000:], "Error during backend function (part_job)")
 			return False
 		self["progressbar"].set_fraction(float(pcnt) / 100)
-		if pcnt == 100:
+		if pcnt == 100 and self.server.QueryIsJobFinished(self.trackdata["part_job"]):
 			result = self.server.QueryJobResult(self.trackdata["part_job"])
 			self.server.DismantleJob(self.trackdata["part_job"])
 			del self.trackdata["part_job"]
@@ -567,7 +572,7 @@
 			self.trackdata["install_job"] = self.server.StartInstallJob(self.trackdata["new_parts"])
 			self["progressbar"].set_fraction(0)
 			self.add_install_list("Installing FreeBSD base system...")
-			gobject.timeout_add(1000, self.install_progress)
+			gobject.timeout_add(2000, self.install_progress)
 			self.next_progress_advert()
 			return False
 		return True
@@ -582,13 +587,119 @@
 			self._show_message(result[-1000:], "Error during backend function (install_job)")
 			return False
 		self["progressbar"].set_fraction(float(pcnt) / 100)
-		if pcnt > 0 and pcnt % 15 == 0:
+		if pcnt > 0 and pcnt % 25 == 0:
+			self.next_progress_advert()
+		if pcnt == 100 and self.server.QueryIsJobFinished(self.trackdata["install_job"]):
+			self.server.DismantleJob(self.trackdata["install_job"])
+			del self.trackdata["install_job"]
+			# Start a new job - install packages
+			self.trackdata["pkginstall_job"] = self.server.StartPkgInstallJob(self.trackdata["new_parts"])
+			self["progressbar"].set_fraction(0)
+			self.add_install_list("Installing packages...")
+			gobject.timeout_add(2000, self.pkginstall_progress)
+			self.next_progress_advert()
+			return False
+		return True
+
+
+	def pkginstall_progress(self):
+		try:
+			pcnt = self.server.QueryJobProgress(self.trackdata["pkginstall_job"])
+		except Exception, e:
+			code, result = self.server.QueryJobError(self.trackdata["pkginstall_job"])
+			print code, result
+			self._show_message(result[-1000:], "Error during backend function (install_job)")
+			return False
+		self["progressbar"].set_fraction(float(pcnt) / 100)
+		if pcnt > 0 and pcnt % 25 == 0:
 			self.next_progress_advert()
-		if pcnt == 100:
+		if pcnt == 100 and self.server.QueryIsJobFinished(self.trackdata["pkginstall_job"]):
+			self.server.DismantleJob(self.trackdata["pkginstall_job"])
+			del self.trackdata["pkginstall_job"]
+			logging.info("PkgInstall finished")
+			self._load_tile("hostusername")
+			return False
+		return True
+
+
+	def hostroot_on_load(self):
+		self._load_label(self["label2"], "hostroot.txt")
+		return True
+
+
+	def on_button_generate_clicked(self, evt):
+		seq = ""
+		for x in xrange(8):
+			seq += chr(random.randint(0, 255))
+		str = base64.b64encode(seq)
+		password = str[0:8]
+		self._show_message("Your generated password is:\n\n"+password+"\n\nPLEASE REMEMBER IT. DO NOT WRITE DOWN YOUR PASSWORDS.", "Generated random password")
+		self["entry_password"].set_text(password)
+		self["entry_confirm_password"].set_text(password)
+
+
+	def hostroot_on_next(self):
+		if self["entry_password"].get_text() != self["entry_confirm_password"].get_text():
+			self._show_message("Passwords do not match! Please enter the same password\nin the Password and Confirm password fields", "Password mismatch")
+			return False
+		code, result = self.server.SetHostName(self["entry_hostname"].get_text())
+		if code != 0:
+			self._show_message(result, "Error")
+			return False
+		code, result = self.server.SetUserPassword("root", self["entry_password"].get_text())
+		return True
+
+
+	def createuser_on_load(self):
+		self._load_label(self["label2"], "createuser.txt")
+		return True
+
+
+	def createuser_on_next(self):
+		if self["entry_password"].get_text() != self["entry_confirm_password"].get_text():
+			self._show_message("Passwords do not match! Please enter the same password\nin the Password and Confirm password fields", "Password mismatch")
+			return False
+		username = self["entry_username"].get_text()
+		if username.find(" ") == -1:
+			self._show_message("Invalid username. No whitespace, please.", "Error")
+			return False
+		code, result = self.server.AddUser(username, self["entry_user"].get_text(), self["entry_password"].get_text())
+		if code != 0:
+			self._show_message(result, "Error")
 			return False
 		return True
 
 
+	def netconf_on_load(self):
+		pass
+
+
+	def netconf_on_next(self):
+		pass
+
+
+	def x11conf_on_load(self):
+		pass
+
+
+	def x11conf_on_next(self):
+		pass
+
+
+	def soundconf_on_load(self):
+		pass
+
+
+	def soundconf_on_next(self):
+		pass
+
+
+	def nfinish_on_load(self):
+		pass
+
+
+	def nfinish_on_next(self):
+		pass
 
 
 my_dir = os.path.split(sys.argv[0])[0]

==== //depot/projects/soc2007/ivoras_finstall/pybackend/freebsd.py#8 (text+ko) ====

@@ -26,6 +26,8 @@
 import re
 import xmldict
 
+live_root = "/"
+
 cmd_sysctl = "/sbin/sysctl"
 cmd_geom = "/sbin/geom"
 cmd_mount = "/sbin/mount"
@@ -39,6 +41,7 @@
 cmd_kldload = "/sbin/kldload"
 cmd_mke2fs = "/usr/local/sbin/mke2fs"
 cmd_boot0cfg = "/usr/sbin/boot0cfg"
+cmd_pw = "/usr/sbin/pw"
 
 file_dmesg = "/var/run/dmesg.boot"
 
@@ -62,15 +65,18 @@
 
 
 def get_cmd_output(name):
+	"""Executes a command and returns its output."""
 	return os.popen(name).read().strip()
 
 
 def get_dmesg():
+	"""Return the contents of the dmesg file."""
 	global file_dmesg
 	return [x.strip() for x in file(file_dmesg, "r").readlines()]
 
 
 def get_geom_xml():
+	"""Returns XMLDict object containing the GEOM XML tree."""
 	return xmldict.buildxmldict(get_sysctl("kern.geom.confxml", True))
 
 
@@ -102,6 +108,10 @@
 
 
 def guess_fs_last_mount(dev, fs_type="UFS"):
+	"""
+	Guess where the file system (device) was last mounted on in the
+	file system tree.
+	"""
 	global cmd_file
 	if not fs_type in ("UFS2", "UFS"): # For now, we only know how to handle UFS
 		return None
@@ -129,6 +139,7 @@
 
 
 def geom_sector_size(dev):
+	"""Find out sector size of the device."""
 	xml = get_geom_xml()
 	for cls in xml["mesh"]["class"]:
 		if "geom" in cls:
@@ -144,9 +155,13 @@
 	"""
 	Convenience function that executes the command specified by
 	the cmd argument and returns its exit status and its output from both stdout
-	and stderr. Optionally, a simple string can be send to the process'
-	stdin.
+	and stderr. Optionally, a simple string can be send to the process' stdin.
+	If the cmd starts with "/" (as it should), it will be prepended with
+	global variable live_root, which should point to a FreeBSD live tree.
 	"""
+	global live_root
+	if cmd.startswith("/") and live_root != "/":
+		cmd = "%s/%d" % (live_root, cmd)
 	logging.info("Executing %s" %cmd)
 	p = popen2.Popen4(cmd)
 	if input != None:
@@ -163,6 +178,9 @@
 
 
 def create_fdisk_partition(dev, index, offset_sectors, size_sectors, flags=[]):
+	"""
+	Create a fdisk partition on the device.
+	"""
 	global cmd_fdisk, cmd_boot0cfg
 	temp_fname, f = make_temp_script()
 	# Create the script
@@ -199,6 +217,7 @@
 
 
 def create_bsdlabel_partition(dev, index, offset_sectors, size_sectors, flags=[], fs_type="4.2BSD"):
+	"""Create a bsdlabel partition on the device."""
 	global cmd_bsdlabel, bsdlabel_map
 	output = ""
 	if "init" in flags:
@@ -238,7 +257,7 @@
 	f.write("\n")
 	f.close()
 	code, output = exec_cmd("%s -R %s %s" % (cmd_bsdlabel, dev, script_fname))
-	#os.unlink(script_fname)
+	os.unlink(script_fname)
 	if code != 0:
 		return (code, output, None)
 	# Verify the result & fetch the last_offset
@@ -286,6 +305,7 @@
 
 
 def mount(dev, mount, fs_type, mkdir_mount=True):
+	"""Mounts a file system / device on a directory."""
 	global cmd_mount
 	if not dev.startswith("/dev/"):
 		dev = "/dev/"+dev
@@ -303,9 +323,56 @@
 
 
 def umount(mount):
+	"""Umounts a mount point"""
 	global cmd_umount
 	return exec_cmd("%s %s" % (cmd_umount, mount))
 
+
+def fstab_line(dev, mount, fs_type):
+	"""Generates a line for the fstab file"""
+	if fs_type == "ZFS":
+		return None
+	if fs_type == "swap":
+		mount = "none"
+		opt = "sw"
+		dump = fpass = 0
+	else:
+		opt = "rw"
+		if mount == "/":
+			dump = fpass = 1
+		else:
+			dump = fpass = 2
+		if fs_type in ("UFS", "UFS+SU"):
+			fs_type = "ufs"
+		elif fs_type == "UFS+GJ":
+			fs_type = "ufs"
+			dev = dev + ".journal"
+			opt += ",async"
+		elif fs_type == "Ext2":
+			fs_type = "ext2fs"
+
+	return "%s\t\%s\t%s\t%s\t%s\t%s" % (dev, mount, fs_type, opt, dump, fpass)
+
+
+def chpasswd(user_name, password, etc_dir="/etc"):
+	"""Changes the user's password. The user must exist."""
+	global cmd_pw
+	return exec_cmd("%s -V %s usermod %s -h 0" % (cmd_pw, etc_dir, user_name), password)
+
+
+def useradd(user_name, user, password, shell, groups=[], etc_dir="/etc"):
+	"""Adds a user and sets his default shell and member groups"""
+	global cmd_pw
+	guess_root = os.path.split(etc_dir)[0]
+	if guess_root == "/":
+		guess_root = ""
+	guess_home = "%s/home" % guess_root
+	if len(groups) != 0:
+		return exec_cmd("%s -V %s useradd %s -m -s %s -G %s -c \"%s\" -b %s" % (cmd_pw, etc_dir, user_name, shell, ",".join(groups), user, guess_home))
+	else:
+		return exec_cmd("%s -V %s useradd %s -m -s %s -c \"%s\" -b %s" % (cmd_pw, etc_dir, user_name, shell, user, guess_home))
+
+
 if __name__ == "__main__":
 	xml = get_geom_xml()
 	for cls in xml["mesh"]["class"]:

==== //depot/projects/soc2007/ivoras_finstall/pybackend/systoolengine.py#11 (text+ko) ====

@@ -22,7 +22,7 @@
 # SysToolD Engine: implements SysToolD XML-RPC methods
 
 import os, sys, shutil
-import re
+import re, time
 import logging, warnings
 from threading import Thread, Lock
 from StringIO import StringIO
@@ -33,8 +33,10 @@
 
 
 def logexception(func):
-	"""Method call decorator that routes exceptions to the logger
-	before passing them on"""
+	"""
+	Method call decorator that routes exceptions to the logger
+	before passing them on.
+	"""
 	def call(*args, **kwds):
 		try:
 			return func(*args, **kwds)
@@ -45,8 +47,10 @@
 
 
 def tolist(e):
-	"""Coalesce argument to list (if the argument is not list,
-	wrap it in a list of one element)."""
+	"""
+	Coalesce argument to list (if the argument is not list,
+	wrap it in a list of one element).
+	"""
 	if type(e) == type([]):
 		return e
 	else:
@@ -54,11 +58,17 @@
 
 
 class SysToolJobException(Exception):
+	"""Error in a SysToolJob"""
 	pass
 
 
 class SysToolJob(Thread):
-	"""A generic asynchronous SysTool job"""
+	"""A generic asynchronous SysTool job, with common methods"""
+
+	INSTALL_ROOT = "/dist"
+	BASE_MAPTREE_FILE = "/install/base.maptree"
+	USRLOCAL_MAPTREE_FILE = "/install/usrlocal.maptree"
+	VARDBPKG_MAPTREE_FILE = "/install/vardbpkg.maptree"
 
 	def __init__(self):
 		Thread.__init__(self)
@@ -70,7 +80,93 @@
 	def _calc_percent(self, i, total):
 		return int((float(i+1) / total) * 100)
 
+	def MountPartitions(self, part_spec, install_root):
+		"""
+		Mount partitions from part_spec. Returnes a (code, result) tuple.
+		"""
+		for part in part_spec:
+			if part["fs"] == None or part["fs"] == "swap":
+				continue
+			if part["mount"] == "/":
+				mount = install_root
+			else:
+				mount = "%s/%s" % (install_root, part["mount"])
+			code, result = freebsd.mount(part["name"], mount, part["fs"])
+			if code != 0:
+				return (code, result)
+		return (0, None)
 
+	def UmountPartitions(self, part_spec, install_root, force=False):
+		"""
+		Unmount partitions from part_spec. If force parameter is True, errors
+		from umount are ignored, otherwise, a (code,result) pair is returned.
+		"""
+		for part in reversed(part_spec):
+			if part["fs"] == None or part["fs"] == "swap":
+				continue
+			if part["mount"] == "/":
+				mount = install_root
+			else:
+				mount = "%s/%s" % (install_root, part["mount"])
+			code, result = freebsd.umount(mount)
+			if code != 0 and not force:
+				return (code, result)
+		return (0, None)
+
+	def InstallFromMapTree(self, maptree_filename, install_root, from_root):
+		"""
+		The method will process the records in maptree and copy the files found
+		there from from_root to install_root. Since this operation normally takes
+		a long time, the method updates self.percent_complete.
+		"""
+		if from_root == "/":
+			from_root = ""
+		maptree_size = os.path.getsize(maptree_filename)
+		f = file(maptree_filename, "r")
+		for line in f:
+			self.percent_complete = self._calc_percent(f.tell(), maptree_size)
+			rec = line.strip().split("|")
+			rec_file = rec[-1]
+			dest_file = "%s/%s" % (install_root, rec_file)
+			src_file = "%s/%s" % (from_root, rec_file)
+			if rec[0] == "F":
+				if not os.path.exists(src_file):
+					logging.info("File not found: "+src_file)
+					continue
+				logging.info("copy %s to %s" % (src_file, dest_file))
+				shutil.copyfile(src_file, dest_file)
+				shutil.copystat(src_file, dest_file)
+			elif rec[0] == "D":
+				if not os.path.exists(dest_file):
+					os.mkdir(dest_file)
+			elif rec[0] == "H":
+				if os.path.exists(dest_file):
+					os.unlink(dest_file)
+				try:
+					os.link("%s/%s" % (install_root, rec[1]), dest_file)
+				except:
+					logging.error("Cannot hardlink '%s/%s' to '%s'" % (from_root, rec[1], dest_file))
+					try:
+						shutil.copyfile("/%s" % rec[1], dest_file)
+					except:
+						logging.error("Cannot replace hardlink from '%s/%s' to '%s' with file copy" % (from_root, rec[1], dest_file))
+			elif rec[0] == "L":
+				# symlinks are a special case - they don't need to exist and they can be
+				# embarrasingly relative
+				if os.path.exists(dest_file):
+					if os.path.islink(dest_file):
+						os.unlink(dest_file)
+						os.symlink("%s" % rec[1], dest_file)
+				else:
+					os.symlink("%s" % rec[1], dest_file)
+			else:
+				self.error = 1
+				self.result = "Unknown record type: "+line
+				break
+		f.close()
+
+
+
 class PartitionJob(SysToolJob):
 	"""
 	A partitioning SysTool job. This one accept a list of partitions
@@ -163,6 +259,15 @@
 					break
 		if self.result == None:
 			self.result = buf.getvalue()
+		if self.error == None:
+			# mount partitions now
+			install_root = self.INSTALL_ROOT
+			if not os.path.exists(install_root):
+				os.mkdir(install_root)
+			code, result = self.MountPartitions(self.part_spec, install_root)
+			if code != 0:
+				self.error = code
+				self.result = result
 		self.finished = True
 		del self.part_spec
 
@@ -170,104 +275,113 @@
 class InstallJob(SysToolJob):
 	"""
 	The install job. The job progress is:
-		- Mount partitions to a temporary tree
-		- Parse mtree record and use it as a list of files to install
+		- Mount partitions from part_spec to the temporary tree
+		- Parse maptree record and use it as a list of files to install
+		- Generate /etc/fstab from the part_spec
+	"""
+	def __init__(self, part_spec):
+		SysToolJob.__init__(self)
+		self.part_spec = part_spec
+
+	def run(self):
+		install_root = self.INSTALL_ROOT
+		maptree_filename = self.BASE_MAPTREE_FILE
+		if not os.path.exists(maptree_filename):
+			self.error = 1
+			self.result = "No "+maptree_filename
+			self.finished = True
+			return
+		try:
+			self.InstallFromMapTree(maptree_filename, install_root, "/")
+		except Exception, e:
+			self.error = 1
+			self.result = str(e)
+		if self.error == None:
+			# create fstab
+			f = file("%s/etc/fstab" % install_root, "w+")
+			f.write("# Generated by finstall on %s\n" % time.strftime("%c"))
+			f.write("# Device\tMountpoint\tFStype\tOptions\tDump\tPass\n")
+			for part in self.part_spec:
+				if part["mount"] != None:
+					line = freebsd.fstab_line(part["name"], part["mount"], part["fs"])
+					if line != None:
+						f.write("%s\n" % line)
+			f.close()
+
+		self.finished = True
+
+
+class PkgInstallJob(SysToolJob):
+	"""
+	This job simply copies /usr/local and /var/pkg trees to the install 
+	destination tree. Thus, it's not suitable for all types of packages (those
+	with post-install scripts that do non-trivial stuff) but will work ok
+	for most.
+	The main benefit of this is that it's way faster than going through
+	pkg_create -b + pkg_install sequence.
 	"""
 	def __init__(self, part_spec):
 		SysToolJob.__init__(self)
 		self.part_spec = part_spec
 
 	def run(self):
-		install_root = "/dist"
-		if not os.path.exists(install_root):
-			os.mkdir(install_root)
-		buf = StringIO()
-		# mount partitions
-		for part in self.part_spec:
-			if part["fs"] == None or part["fs"] == "swap":
-				continue
-			if part["mount"] == "/":
-				mount = install_root
-			else:
-				mount = "%s/%s" % (install_root, part["mount"])
-			code, result = freebsd.mount(part["name"], mount, part["fs"])
-			buf.write(result)
-			if code != 0:
-				self.error = code
-				self.result = buf.getvalue()
-				self.finished = True
-				return
-		maptree_filename = "/maptree.txt"
+		install_root = self.INSTALL_ROOT
+		maptree_filename = self.VARDBPKG_MAPTREE_FILE
+		if not os.path.exists(maptree_filename):
+			self.error = 1
+			self.result = "No "+maptree_filename
+			self.finished = True
+			return
+		try:
+			self.InstallFromMapTree(maptree_filename, "%s/var/db/pkg" % install_root, "/var/db/pkg")
+		except Exception, e:
+			self.error = 1
+			self.result = str(e)
+			self.finished = True
+			return
+		maptree_filename = self.USRLOCAL_MAPTREE_FILE
 		if not os.path.exists(maptree_filename):
 			self.error = 1
 			self.result = "No "+maptree_filename
 			self.finished = True
 			return
-		maptree_size = os.path.getsize(maptree_filename)
-		f = file(maptree_filename, "r")
-		for line in f:
-			self.percent_complete = self._calc_percent(f.tell(), maptree_size)
-			rec = line.strip().split("|")
-			rec_file = rec[-1]
-			dest_file = "%s/%s" % (install_root, rec_file)
-			src_file = "/%s" % rec_file
-			if rec[0] == "F":
-				if not os.path.exists(src_file):
-					logging.info("File not found: "+src_file)
-					continue
-				shutil.copyfile(src_file, dest_file)
-				shutil.copystat(src_file, dest_file)
-				logging.info("copy %s to %s" % (src_file, dest_file))
-			elif rec[0] == "D":
-				if not os.path.exists(dest_file):
-					os.mkdir(dest_file)
-			elif rec[0] == "H":
-				if os.path.exists(dest_file):
-					os.unlink(dest_file)
-				try:
-					os.link("%s/%s" % (install_root, rec[1]), dest_file)
-				except:
-					logging.error("Cannot hardlink '/%s' to '%s'" % (rec[1], dest_file))
-					try:
-						shutil.copyfile("/%s" % rec[1], dest_file)
-					except:
-						logging.error("Cannot replace hardlink from '/%s' to '%s' with file copy" % (rec[1], dest_file))
-			elif rec[0] == "L":
-				if os.path.exists(dest_file):
-					if os.path.islink(dest_file):
-						os.unlink(dest_file)
-						os.symlink("/%s" % rec[1], dest_file)
-				else:
-					os.symlink("/%s" % rec[1], dest_file)
-			else:
-				self.error = 1
-				self.result = "Unknown record type: "+line
-				break
-		for part in reversed(self.part_spec):
-			if part["fs"] == None or part["fs"] == "swap":
-				continue
-			if part["mount"] == "/":
-				mount = install_root
-			else:
-				mount = "%s/%s" % (install_root, part["mount"])
-			code, result = freebsd.umount(mount)
-			if code != 0:
-				self.error = 1
-				if self.result != None:
-					self.result += "\n"+result
-				else:
-					self.result = result
+		try:
+			self.InstallFromMapTree(maptree_filename, "%s/usr/local" % install_root, "/usr/local")
+		except Exception, e:
+			self.error = 1
+			self.result = str(e)
+			self.finished = True
+			return
+		self.finished = True
+
+
+class PostInstallJob(SysToolJob):
+	"""
+	This job finishes the installation - unmounts the temporary tree, etc.
+	"""
+	def __init__(self, part_spec, force):
+		SysToolJob.__init__(self)
+		self.part_spec = part_spec
+		self.force = force
+
+	def run(self):
+		code, result = self.UmountPartitions(self.part_spec, self.INSTALL_ROOT, force)
+		if code != 0:
+			self.error = code
+			self.result = result
 		self.finished = True
 
-				
+
 class SysToolEngine:
 
 	def __init__(self):
-		self.root_dest = ""	# Config file / "new" root, sans final slash
+		self.root_dest = ""	# "new" root, sans final slash
 		self.root_live = ""	# Live file system root (for binaries!)
 		self.job_list = []
 		self.job_list_lock = Lock()
 		warnings.simplefilter('ignore', RuntimeWarning)
+		self.SetDestRoot("/dist")
+		self.SetLiveRoot("/")
 
 
 	def GetId(self):
@@ -281,35 +395,44 @@
 
 
 	def SetDestRoot(self, root_dir):
-		"""Sets internal root directory for installation and config tasks
-		(so Get/SetConf can be used with alternative root)"""
+		"""
+		Sets internal root directory for installation and config tasks
+		(so Get/SetConf can be used with alternative root)
+		"""
 		self.root_conf = root_dir
+		SysToolJob.INSTALL_ROOT = root_dir
 		return True
 
 
 	def SetLiveRoot(self, root_dir):
 		"""Notifies SysTool Engine where to expect utility binaries."""
 		self.root_live = root_dir
+		freebsd.live_root = root_dir
 		return True
 
 
-	def GetConf(self, name):
+	def GetConf(self, name, default=None):
 		"""Returns configuration variable from /etc/rc.conf"""
 		cf = ConfFile("%s/etc/rc.conf" % self.root_dest)
-		val = cf.GetConf(name, None)
-		return val
+		return cf.GetConf(name, default)
 
 
 	def SetConf(self, name, value, description):
 		"""Sets configuration variable to /etc/rc.conf"""
 		cf = ConfFile("%s/etc/rc.conf" % self.root_dest)
-		cf.SetConf(name, value, description)
-		return True
+		return cf.SetConf(name, value, description)
+
+
+	@logexception
+	def GetShells(self):
+		"""Returns a list of acceptable shells"""
+		return [x.strip() for x in file("%s/etc/shells" % self.root_dest, "r") if len(x) > 1 and x[0] == "/"]
 
 
 	@logexception
 	def GetDrives(self):
-		"""Returns information on drives the kernel knows about.
+		"""
+		Returns information on drives the kernel knows about.
 		Examines "kern.drives" sysctl. This is NOT the list of
 		valid GEOM leaves which can be mounted, but a list of
 		found hardware.
@@ -323,7 +446,8 @@
 					"name": "ACME MegaDrive",
 					"mediasize": 300000  # (MB)
 				}
-		}"""
+		}
+		"""
 		drive_list = freebsd.get_sysctl("kern.disks", True).split(" ")
 		dmesg = freebsd.get_dmesg()
 		drive_dict = {}
@@ -360,7 +484,8 @@
 
 	@logexception
 	def GetDrivePartitions(self, drive):
-		"""Returns a list of leaf partitions created on the drive. 
+		"""
+		Returns a list of leaf partitions created on the drive. 
 		ARGUMENTS:
 		drive : The drive to inspect
 		RETURN VALUE:
@@ -377,7 +502,8 @@
 				"fs_type" : "UFS2",
 				"fs_last_mount", "/usr"
 				}
-		}"""
+		}
+		"""
 		parts = {}
 		used_parts = []
 		geomxml = freebsd.get_geom_xml()
@@ -438,8 +564,10 @@
 
 
 	def GetMountPoints(self):
-		"""Returns a list of dictionaries containing information
-		about currently mounted file systems"""
+		"""
+		Returns a list of dictionaries containing information
+		about currently mounted file systems
+		"""
 		raw_mounts = freebsd.get_cmd_output("%s -p" % freebsd.cmd_mount).split("\n")
 		mounts = []
 		for m in raw_mounts:
@@ -461,6 +589,7 @@
 		physmem = int(freebsd.get_sysctl("hw.realmem"))
 		return int(physmem / (1024*1024))
 
+ # Jobs #######################################################################
 
 	@logexception
 	def StartPartitionJob(self, part_spec):
@@ -492,6 +621,20 @@
 
 
 	@logexception
+	def StartPkgInstallJob(self, part_spec):
+		"""
+		Starts the install job. Returns an integer job_id.
+		"""
+		self.job_list_lock.acquire()
+		job = PkgInstallJob(part_spec)
+		self.job_list.append(job)
+		job_id = len(self.job_list)
+		job.start()
+		self.job_list_lock.release()
+		return job_id
+
+
+	@logexception
 	def QueryJobProgress(self, job_id):
 		"""
 		Queries the progress of a job, returns percent complete or None
@@ -509,6 +652,18 @@
 
 
 	@logexception
+	def QueryIsJobFinished(self, job_id):
+		"""
+		Queries whether the job has finished. Testing for job progress percent == 100 
+		is not enough.
+		"""
+		self.job_list_lock.acquire()
+		job = self.job_list[job_id-1]
+		self.job_list_lock.release()
+		return job.finished
+
+
+	@logexception
 	def QueryJobResult(self, job_id):
 		"""
 		Queries the result of a job, if the job is finished. Returns
@@ -545,4 +700,32 @@
 		self.job_list_lock.release()
 		return True
 
+# System configuration ########################################################
+
+	@logexception
+	def SetHostName(self, host_name):
+		if self.SetConf("hostname", host_name, "Host name"):
+			return (0, None)
+		else:
+			return (1, "Cannot set host name")
+
+
+	@logexception
+	def GetHostName(self):
+		host_name = self.GetConf("hostname")
+		if host_name == None:
+			return "amnesiac"
+		else:
+			return host_name
+
+
+	@logexception
+	def SetUserPassword(self, user_name, password):
+		return freebsd.chpasswd(user_name, password, "%s/etc" % self.root_dest)
+
+	
+	@logexception
+	def AddUser(self, user_name, user, password, shell, groups):
+		return freebsd.useradd(user_name, user, password, shell, groups, "%s/etc" % self.root_dest)
+
 



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?200708222232.l7MMW8TN090877>