Begin cleanup; string formats, verbosity

This is the start of a major cleanup of the code in install-gsos.py
(which really is turning into install-a2boot.py).  First, the code is
developing three levels of output: Silent, normal, and verbose.  There
might eventually be also a fourth debug level, but that's not happening
yet.

Second, variable names just haphazardly chosen as I've been working on
this are being sanitized, made consistent, and otherwise improved.

Third (and this has only just begun), the code is being refactored a
bit for what belongs where.  That will continue.
This commit is contained in:
T. Joseph Carter 2015-11-14 03:50:29 -08:00
parent 2c9a1ed072
commit 1c95cadacb

View File

@ -7,82 +7,91 @@ import hashlib
import shutil
import xml.etree.cElementTree as ET
gsosDir = '/media/A2SHARED/FILES'
imagesDir = gsosDir + '/GSOS.INSTALLER/IMAGES'
imageToolsDir = gsosDir + '/GSOS.INSTALLER/IMAGE.TOOLS'
netInstallDir = gsosDir + '/GSOS.INSTALLER/NET.INSTALL'
gsosDir = "/media/A2SHARED/FILES"
imagesDir = gsosDir + "/GSOS.INSTALLER/IMAGES"
imageToolsDir = gsosDir + "/GSOS.INSTALLER/IMAGE.TOOLS"
netInstallDir = gsosDir + "/GSOS.INSTALLER/NET.INSTALL"
p8Dir = '/media/A2SHARED/A2FILES'
diskToolsP8Dir = p8Dir + '/DISK.TOOLS.P8'
p8Dir = "/media/A2SHARED/A2FILES"
diskToolsP8Dir = p8Dir + "/DISK.TOOLS.P8"
commDir = '/media/A2SHARED/A2FILES/COMM'
spectrumDir = commDir + '/SPECTRUM'
protermDir = commDir + '/PROTERM'
zlinkDir = commDir + '/Z.LINK'
adtproDir = commDir + '/ADTPRO'
commDir = "/media/A2SHARED/A2FILES/COMM"
spectrumDir = commDir + "/SPECTRUM"
protermDir = commDir + "/PROTERM"
zlinkDir = commDir + "/Z.LINK"
adtproDir = commDir + "/ADTPRO"
quiet = False
verbose = False
disk7_sources = [
{
'type' : 'sea.bin',
'url' : 'http://download.info.apple.com/Apple_Support_Area/Apple_Software_Updates/English-North_American/Apple_II/Apple_IIGS_System_6.0.1/Disk_7_of_7-Apple_II_Setup.sea.bin',
'file' : 'Disk_7_of_7-Apple_II_Setup.sea.bin'
"src" : "Apple",
"type" : "sea.bin",
"url" : "http://download.info.apple.com/Apple_Support_Area/Apple_Software_Updates/English-North_American/Apple_II/Apple_IIGS_System_6.0.1/Disk_7_of_7-Apple_II_Setup.sea.bin",
"file" : "Disk_7_of_7-Apple_II_Setup.sea.bin"
},
{
'type' : 'sea.bin',
'url' : 'http://archive.org/download/download.info.apple.com.2012.11/download.info.apple.com.2012.11.zip/download.info.apple.com%2FApple_Support_Area%2FApple_Software_Updates%2FEnglish-North_American%2FApple_II%2FApple_IIGS_System_6.0.1%2FDisk_7_of_7-Apple_II_Setup.sea.bin',
'file' : 'Disk_7_of_7-Apple_II_Setup.sea.bin'
"src" : "archive.org",
"type" : "sea.bin",
"url" : "http://archive.org/download/download.info.apple.com.2012.11/download.info.apple.com.2012.11.zip/download.info.apple.com%2FApple_Support_Area%2FApple_Software_Updates%2FEnglish-North_American%2FApple_II%2FApple_IIGS_System_6.0.1%2FDisk_7_of_7-Apple_II_Setup.sea.bin",
"file" : "Disk_7_of_7-Apple_II_Setup.sea.bin"
}
]
a2boot_files = [
{
'unix' : 'Apple ::e Boot Blocks',
'hfsutils' : 'Apple_--e_Boot_Blocks.bin',
'netatalk' : 'Apple :2f:2fe Boot Blocks',
'digest' : 'cada362ac2eca3ffa506e9b4e76650ba031e0035',
'patches' : {
'6.0.1' : (
"unix" : "Apple ::e Boot Blocks",
"hfsutils" : "Apple_--e_Boot_Blocks.bin",
"netatalk" : "Apple :2f:2fe Boot Blocks",
"digest" : "cada362ac2eca3ffa506e9b4e76650ba031e0035",
"patches" : {
"6.0.1" : (
[
'Cleartext password login bug',
(0x4d43, '\xA8\xA2\x01\xBD\x80\x38\x99\xA0\x38\xC8\xE8\xE0\x09\x90\xF4')
"Cleartext password login bug",
(0x4d43, "\xA8\xA2\x01\xBD\x80\x38\x99\xA0\x38\xC8\xE8\xE0\x09\x90\xF4")
],
'6b7fc12fd118e1cb9e39c7a2b8cc870c844a3bac'
"6b7fc12fd118e1cb9e39c7a2b8cc870c844a3bac"
)
}
},
{
'unix' : 'Basic.System',
'hfsutils' : 'Basic.System.bin',
'digest' : '4d53424f1451cd2e874cf792dbdc8cc6735dcd36'
"unix" : "Basic.System",
"hfsutils" : "Basic.System.bin",
"netatalk" : "Basic.System",
"digest" : "4d53424f1451cd2e874cf792dbdc8cc6735dcd36"
},
{
'unix' : 'ProDOS16 Boot Blocks',
'hfsutils' : 'ProDOS16_Boot_Blocks.bin',
'digest' : 'fab829e82e6662ed6aab119ad18e16ded7d43cda',
"unix" : "ProDOS16 Boot Blocks",
"hfsutils" : "ProDOS16_Boot_Blocks.bin",
"netatalk" : "ProDOS16 Boot Blocks",
"digest" : "fab829e82e6662ed6aab119ad18e16ded7d43cda",
},
{
'unix' : 'ProDOS16 Image',
'hfsutils' : 'ProDOS16_Image.bin',
'digest' : 'db4608067b9e7877f45eb557971c4d8c45b46be5',
'patches' : {
'6.0.1' : (
"unix" : "ProDOS16 Image",
"hfsutils" : "ProDOS16_Image.bin",
"netatalk" : "ProDOS16 Image",
"digest" : "db4608067b9e7877f45eb557971c4d8c45b46be5",
"patches" : {
"6.0.1" : (
[
'Cleartext password login bug',
(0x5837, '\xA8\xA2\x01\xBD\x80\x38\x99\xA0\x38\xC8\xE8\xE0\x09\x90\xF4'),
"Cleartext password login bug",
(0x5837, "\xA8\xA2\x01\xBD\x80\x38\x99\xA0\x38\xC8\xE8\xE0\x09\x90\xF4"),
'Enable pressing "8" during GS/OS netboot to load ProDOS 8',
(0x0100, '\x92'),
(0x0360, '\x20\x7d\x14'),
(0x067d, '\xad\x00\xc0\x29\xff\x00\xc9\xb8\x00\xd0\x06\xa9\x02\x00\x8d\x53\x14\xa9\x10\x0f\x60')
"Enable pressing \"8\" during GS/OS netboot to load ProDOS 8",
(0x0100, "\x92"),
(0x0360, "\x20\x7d\x14"),
(0x067d, "\xad\x00\xc0\x29\xff\x00\xc9\xb8\x00\xd0\x06\xa9\x02\x00\x8d\x53\x14\xa9\x10\x0f\x60")
],
'5c35d5533901b292ab7c2f5a3c76cb3113f66085'
"5c35d5533901b292ab7c2f5a3c76cb3113f66085"
)
}
},
{
'unix' : 'p8',
'hfsutils' : 'p8.bin',
'digest' : '36c288a5272cf01e0a64eed16786258959118e0e'
"unix" : "p8",
"hfsutils" : "p8.bin",
"netatalk" : "p8",
"digest" : "36c288a5272cf01e0a64eed16786258959118e0e"
}
]
@ -98,13 +107,13 @@ else:
# Differing from the shell script in that we explicitly strip the / here
if 'A2SERVER_SCRIPT_URL' in os.environ:
scriptURL = os.environ['A2SERVER_SCRIPT_URL']
if "A2SERVER_SCRIPT_URL" in os.environ:
scriptURL = os.environ["A2SERVER_SCRIPT_URL"]
# Strip trailing slash
if scriptURL.endswith('/'):
if scriptURL.endswith("/"):
scriptURL = scriptURL[:-1]
else:
scriptURL = 'http://appleii.ivanx.com/a2server'
scriptURL = "http://appleii.ivanx.com/a2server"
def sha1file (filename, blocksize=65536):
f = open(filename, "rb")
@ -114,28 +123,52 @@ def sha1file (filename, blocksize=65536):
digest.update(buf)
buf = f.read(blocksize)
f.close()
if verbose:
print("SHA-1: %s %s" % (digest.hexdigest(), filename))
return digest.hexdigest()
def download_url(url, filename):
def download_url(url, filename, output_dir = None):
try:
if output_dir != None:
dest = os.path.join(output_dir, filename)
else:
dest = filename
if verbose:
print(""" URL : %s
Dest : %s""" % (url, dest))
html = urlrequest.urlopen(url)
data = html.read()
f = open(filename, 'wb')
f = open(dest, "wb")
f.write(data)
f.close
if verbose:
print("File downloaded.\n")
return True
except:
if verbose:
print("Download failed.")
return False
# Apple's GS/OS 6.0.1 images are stored in MacBinary-wrapped
# self-extracting disk image files. The Unarchiver's unar is able
# to unwrap the MacBinary wrapper for us, but we have to extract the
# disk image oursselves. Fortunately, it's uncompressed.
def extract_800k_sea_bin(wrapper_name, image_name, extract_dir):
# First we need to get rid of the MacBinary wrapper
# FIXME: Can we learn to read MacBinary? I bet we can!
if not os.path.isfile(wrapper_name):
raise IOError('Archive file "' + wrapper_name + '" does not exist')
def extract_800k_sea_bin(archive_name, image_name, archive_dir):
if not quiet:
print("Extracting %s from %s..." % (image_name, archive_name))
if archive_dir != None:
archive_path = os.path.join(archive_dir, archive_name)
image_path = os.path.join(archive_dir, image_name)
else:
archive_path = archive_name
impage_path = image_name
if not os.path.isfile(archive_path):
raise IOError("Archive file \"" + archive_path + "\" does not exist")
# Extract the original filename from the file
# MacBinary II header is 128 bytes. The first byte is NUL, followed by a
@ -145,183 +178,205 @@ def extract_800k_sea_bin(wrapper_name, image_name, extract_dir):
# Source: http://files.stairways.com/other/macbinaryii-standard-info.txt
# FIXME: We should eventually implement a full MacBinary reader.
f = open(wrapper_name, "rb")
f = open(archive_path, "rb")
sea_name = f.read(65)
f.close()
if PY3:
sea_name = sea_name[2:2 + sea_name[1]].decode('mac_roman')
sea_name = sea_name[2:2 + sea_name[1]].decode("mac_roman")
else:
sea_name = sea_name[2:2 + ord(sea_name[1])]
cmdline = ['unar', '-q', '-o', extract_dir, '-k', 'skip', wrapper_name]
if verbose:
print("Running unar on \"%s\" to extract %s..." % (archive_name, sea_name))
cmdline = ["unar", "-q", "-o", archive_dir, "-k", "skip", archive_path]
ret = subprocess.call(cmdline)
if ret != 0:
raise IOError('unar returned with status %i' % (ret))
raise IOError("unar returned with status %i" % (ret))
# Do we have the right file?
sea_name = os.path.join(extract_dir, sea_name)
if not os.path.isfile(sea_name):
raise IOError('Expected image archive "' + sea_name + '" does not exist')
sea_path = os.path.join(archive_dir, sea_name)
if not os.path.isfile(sea_path):
raise IOError("Expected image archive \"" + sea_name + "\" does not exist")
# Cowardly refuse to overwrite image_name
if os.path.exists(image_name):
raise IOError('"' + image_name + '" already exists')
if verbose:
print("Extracting disk image from %s..." % (sea_name))
# Cowardly refuse to overwrite image_path
if os.path.exists(image_path):
raise IOError("\"" + image_path + "\" already exists")
# The image starts 84 bytes in, and is exactly 819200 bytes long
with open(sea_name, 'rb') as src, open(image_name, 'wb') as dst:
with open(sea_path, "rb") as src, open(image_path, "wb") as dst:
src.seek(84)
dst.write(src.read(819200))
if dst.tell() != 819200:
raise IOError(wrapper_name + ' did not contain an 800k floppy image')
raise IOError(archive_name + " did not contain an 800k floppy image")
# Now just clean up the archive files and we're done
os.unlink(sea_name)
os.unlink(sea_path)
os.unlink(archive_path)
if verbose:
print("%s extracted." % (image_name))
def plist_keyvalue(plist_dict, key):
if plist_dict.tag != 'dict':
raise ValueError('not a plist dict')
if plist_dict.tag != "dict":
raise ValueError("not a plist dict")
found = False
for elem in plist_dict:
if found:
return elem
if elem.tag == 'key' and elem.text == key:
if elem.tag == "key" and elem.text == key:
found = True
return None
def find_mountpoint(xmlstr):
plistroot = ET.fromstring(xmlstr)
if plistroot.tag != 'plist':
raise ValueError('xmlstr is not an XML-format plist')
if plistroot[0].tag == 'dict':
sys_entities = plist_keyvalue(plistroot[0], 'system-entities')
if sys_entities.tag != 'array':
raise ValueError('expected dict to contain an array')
if plistroot.tag != "plist":
raise ValueError("xmlstr is not an XML-format plist")
if plistroot[0].tag == "dict":
sys_entities = plist_keyvalue(plistroot[0], "system-entities")
if sys_entities.tag != "array":
raise ValueError("expected dict to contain an array")
for child in sys_entities:
if child.tag == 'dict':
mountpoint = plist_keyvalue(child, 'mount-point')
if child.tag == "dict":
mountpoint = plist_keyvalue(child, "mount-point")
return mountpoint.text
else:
raise ValueError('system-entities should be an array of dict objects')
raise ValueError("system-entities should be an array of dict objects")
else:
raise ValueError('Root element is not a dict')
raise ValueError("Root element is not a dict")
def install_bootblocks(installdir, installtype):
if installtype not in ['unix', 'netatalk']:
raise ValueError('Only basic UNIX and netatalk formats are supported for now')
def install_bootblocks(dest_dir, dest_fmt, gsos_version):
if dest_fmt not in ["unix", "netatalk"]:
raise ValueError("Only basic UNIX and netatalk formats are supported for now")
if not quiet:
print("Installing Apple // boot blocks (GS/OS version %s)..." % (gsos_version))
devnull = open(os.devnull, "wb")
if not os.path.isdir(installdir):
os.makedirs(installdir, mode=0o0755)
if not os.path.isdir(dest_dir):
os.makedirs(dest_dir, mode=0o0755)
bootblock_tmp = tempfile.mkdtemp(prefix = "tmp-a2sv-bootblocks.")
work_dir = tempfile.mkdtemp(prefix = "tmp-a2sv-bootblocks.")
a2setup_name = "A2SETUP.img"
if verbose:
print(" dest_fmt : %s\n dest_dir : %s\n work_dir : %s\n"
% (dest_fmt, dest_dir, work_dir))
platform = os.uname()[0]
if platform not in ['Linux', 'Darwin']:
if platform not in ["Linux", "Darwin"]:
platform = "hfsutils"
elif platform == 'Linux':
elif platform == "Linux":
use_sudo = False
if os.geteuid() != 0:
reply = stdin_input("""
You must have either root access or the hfsutils package to
access the disk image containing Apple // boot blocks. Do
you want to mount the image using the sudo command? [y] """)
if reply.startswith('y') or reply.startswith('Y') or reply == '':
if reply.startswith("y") or reply.startswith("Y") or reply == "":
use_sudo = True
print("""
Okay, if asked for a password, type your user password. It
will not be echoed when you type.""")
else:
platform = 'hfsutils'
platform = "hfsutils"
unpacked_a2boot = False
for bootfile in a2boot_files:
if installtype == 'unix':
dst = bootfile['unix']
elif installtype == 'netatalk':
if 'netatalk' in bootfile:
dst = bootfile['netatalk']
if dest_fmt == "unix":
dst_name = bootfile["unix"]
elif dest_fmt == "netatalk":
if "netatalk" in bootfile:
dst_name = bootfile["netatalk"]
else:
dst = bootfile['unix']
dst = os.path.join(installdir, dst)
dst_name = bootfile["unix"]
dst_path = os.path.join(dest_dir, dst_name)
if not os.path.isfile(dst):
if not os.path.isfile(dst_path):
# We need to fetch it
if not unpacked_a2boot:
a2setup_img = os.path.join(bootblock_tmp, 'A2SETUP.img')
a2setup_path = os.path.join(work_dir, a2setup_name)
disk7_downloaded = False
for disk7_source in disk7_sources:
disk7_file = os.path.join(bootblock_tmp, disk7_source['file'])
if download_url(disk7_source['url'], disk7_file):
if not quiet:
print("Downloading %s from %s..."
% (disk7_source["file"], disk7_source["src"]))
if download_url(disk7_source["url"], disk7_source["file"], work_dir):
disk7_downloaded = True
else:
continue
# If file is wrapped as .sea.bin (always true for now)
if disk7_source['type'] == 'sea.bin':
extract_800k_sea_bin(disk7_file, a2setup_img, bootblock_tmp)
os.unlink(disk7_file)
if disk7_source["type"] == "sea.bin":
extract_800k_sea_bin(disk7_source["file"], a2setup_name, work_dir)
else:
# Implement non .sea.bin version
# Placeholder for 6.0.4+ files packed some other way
pass
break
if not disk7_downloaded:
raise IOError('Could not download disk7')
raise IOError("Could not download disk7")
if platform == 'Linux':
mountpoint = os.path.join(bootblock_tmp, 'a2boot')
if platform == "Linux":
mountpoint = os.path.join(work_dir, "a2boot")
os.mkdir(mountpoint)
mount_cmd = ['mount', '-t', 'hfs', '-o', 'ro,loop', a2setup_img, mountpoint]
mount_cmd = ["mount", "-t", "hfs", "-o", "ro,loop", a2setup_path, mountpoint]
if use_sudo:
mount_cmd = ['sudo'] + mount_cmd
mount_cmd = ["sudo"] + mount_cmd
subprocess.call(mount_cmd)
srcdir = os.path.join(mountpoint, 'System Folder')
elif platform == 'Darwin':
xmlstr = subprocess.check_output(['hdiutil', 'attach', '-plist',
a2setup_img])
src_dir = os.path.join(mountpoint, "System Folder")
elif platform == "Darwin":
xmlstr = subprocess.check_output(["hdiutil", "attach", "-plist",
a2setup_path])
mountpoint = find_mountpoint(xmlstr)
srcdir = os.path.join(mountpoint, 'System Folder')
elif platform == 'hfsutils':
srcdir = os.path.join(bootblock_tmp, 'a2boot')
os.mkdir(srcdir)
subprocess.call(['hmount', a2setup_img], stdout=devnull)
subprocess.call(['hcopy', 'Apple II Setup:System Folder:*',
srcdir], stdout=devnull)
subprocess.call(['humount', 'Apple II Setup'], stdout=devnull)
src_dir = os.path.join(mountpoint, "System Folder")
elif platform == "hfsutils":
src_dir = os.path.join(work_dir, "a2boot")
os.mkdir(src_dir)
subprocess.call(["hmount", a2setup_path], stdout=devnull)
subprocess.call(["hcopy", "Apple II Setup:System Folder:*",
src_dir], stdout=devnull)
subprocess.call(["humount", "Apple II Setup"], stdout=devnull)
unpacked_a2boot = True
# Copy the file
if platform == 'Linux' or platform == 'Darwin':
src = os.path.join(srcdir, bootfile['unix'])
elif platform == 'hfsutils':
src = os.path.join(srcdir, bootfile['hfsutils'])
shutil.copyfile(src, dst)
if platform == "Linux" or platform == "Darwin":
src_path = os.path.join(src_dir, bootfile["unix"])
elif platform == "hfsutils":
src_path = os.path.join(src_dir, bootfile["hfsutils"])
shutil.copyfile(src_path, dst_path)
else:
if verbose:
print("\"%s\" already exists." % (dst_name))
# Clean up the mounted/unpacked image
if unpacked_a2boot:
if platform == 'Linux':
umount_cmd = ['umount', mountpoint]
if platform == "Linux":
umount_cmd = ["umount", mountpoint]
if use_sudo:
umount_cmd = ['sudo'] + umount_cmd
umount_cmd = ["sudo"] + umount_cmd
subprocess.call(umount_cmd)
os.rmdir(mountpoint)
elif platform == 'Darwin':
subprocess.call(['hdiutil', 'eject', mountpoint], stdout=devnull)
elif platform == 'hfsutils':
elif platform == "Darwin":
subprocess.call(["hdiutil", "eject", mountpoint], stdout=devnull)
elif platform == "hfsutils":
for bootfile in a2boot_files:
name = os.path.join(srcdir, bootfile['hfsutils'])
name = os.path.join(src_dir, bootfile["hfsutils"])
if os.path.isfile(name):
os.unlink(name)
os.rmdir(srcdir)
os.unlink(a2setup_img)
os.rmdir(src_dir)
os.unlink(a2setup_path)
devnull.close()
os.rmdir(bootblock_tmp)
os.rmdir(work_dir)
def do_install():
netboot_tmp = tempfile.mkdtemp(suffix = '.a2server-netboot')
print('You\'ll want to go and delete this directory:')
netboot_tmp = tempfile.mkdtemp(suffix = ".a2server-netboot")
print("You'll want to go and delete this directory:")
print(netboot_tmp)
os.chdir(netboot_tmp)
@ -362,8 +417,8 @@ def do_install():
if __name__ == '__main__':
# bail out on automated netboot setup unless -b is also specified
# FIXME: This logic belongs in a2server-setup, not here
autoAnswerYes = os.path.isfile('/tmp/a2server-autoAnswerYes')
if autoAnswerYes and not os.path.isfile('/tmp/a2server-setupNetBoot'):
autoAnswerYes = os.path.isfile("/tmp/a2server-autoAnswerYes")
if autoAnswerYes and not os.path.isfile("/tmp/a2server-setupNetBoot"):
sys.exit(0)
# We need root to do this. If we don't have it, just rerun the command with
@ -379,14 +434,14 @@ if __name__ == '__main__':
"""
if os.geteuid() != 0:
args = sys.argv
args.insert(0, 'sudo')
# At the very least, we should print the command line we're running here
print ('Rerunning with sudo...')
args.insert(0, "sudo")
# At the very least, we should print the command line we"re running here
print ("Rerunning with sudo...")
ret = subprocess.call(args)
sys.exit(ret)
"""
install_bootblocks(os.path.join(os.getcwd(), 'a2boot'), 'netatalk')
install_bootblocks(os.path.join(os.getcwd(), "a2boot"), "netatalk", "6.0.1")
#reply = stdin_input("""\nDo you want to set up A2SERVER to be able to boot Apple II\ncomputers over the network? [y] """)
#if reply.startswith('y') or reply.startswith('Y') or reply == '':
#if reply.startswith("y") or reply.startswith("Y") or reply == "":
# do_install()