Add search for DeviceID because UnsupportedModels is absent from macOS 11.* dist files

This commit is contained in:
Graham R Pugh 2020-08-06 14:04:45 +02:00
parent 278f2fa889
commit f8cdedd509

View File

@ -105,6 +105,17 @@ def get_hw_model():
return hw_model return hw_model
def get_bridge_id():
"""Gets the local system DeviceID for T2 Macs"""
remotectl_cmd = ["/usr/libexec/remotectl", "get-property", "localbridge", "HWModel"]
try:
remotectl_output = subprocess.check_output(remotectl_cmd)
bridge_id = remotectl_output.decode("utf8").split(" ")[-1].split("\n")[0]
except subprocess.CalledProcessError as err:
raise ReplicationError(err)
return bridge_id
def get_current_build_info(): def get_current_build_info():
"""Gets the local system build""" """Gets the local system build"""
build_info = [] build_info = []
@ -412,7 +423,7 @@ def get_server_metadata(catalog, product_key, workdir, ignore_cache=False):
print("Could not replicate %s: %s" % (url, err), file=sys.stderr) print("Could not replicate %s: %s" % (url, err), file=sys.stderr)
return None return None
except KeyError: except KeyError:
# print('Malformed catalog.', file=sys.stderr) # print("No metadata for %s.\n" % product_key, file=sys.stderr)
return None return None
@ -468,20 +479,36 @@ def get_board_ids(filename):
with open(filename) as search: with open(filename) as search:
for line in search: for line in search:
line = line.rstrip() # remove '\n' at end of line line = line.rstrip() # remove '\n' at end of line
# dist files for macOS 10.* list boardIDs whereas dist files for
# macOS 11.* list supportedBoardIDs
if "boardIds" in line: if "boardIds" in line:
supported_board_ids = line.split(" ")[-1][:-1] supported_board_ids = line.lstrip("var boardIDs = ")
elif "supportedBoardIDs" in line:
supported_board_ids = line.lstrip("var supportedBoardIDs = ")
return supported_board_ids return supported_board_ids
def get_device_ids(filename):
"""Parses a softwareupdate dist file, returning a list of supported
Device IDs. These are used for identifying T2 chips in the dist files of macOS 11.* - not checked in older builds"""
supported_device_ids = ""
with open(filename) as search:
for line in search:
line = line.rstrip() # remove '\n' at end of line
if "supportedDeviceIDs" in line:
supported_device_ids = line.lstrip("var supportedDeviceIDs = ")
return supported_device_ids
def get_unsupported_models(filename): def get_unsupported_models(filename):
"""Parses a softwareupdate dist file, returning a list of non-supported """Parses a softwareupdate dist file, returning a list of non-supported
ModelIdentifiers""" ModelIdentifiers. This is not used in macOS 11.*"""
unsupported_models = "" unsupported_models = ""
with open(filename) as search: with open(filename) as search:
for line in search: for line in search:
line = line.rstrip() # remove '\n' at end of line line = line.rstrip() # remove '\n' at end of line
if "nonSupportedModels" in line: if "nonSupportedModels" in line:
unsupported_models = line.split(" ")[-1][:-1] unsupported_models = line.lstrip("var nonSupportedModels = ")
return unsupported_models return unsupported_models
@ -527,18 +554,19 @@ def find_mac_os_installers(catalog):
return mac_os_installer_products return mac_os_installer_products
def os_installer_product_info(catalog, workdir, warnings, ignore_cache=False): def os_installer_product_info(catalog, workdir, ignore_cache=False):
"""Returns a dict of info about products that look like macOS installers""" """Returns a dict of info about products that look like macOS installers"""
product_info = {} product_info = {}
installer_products = find_mac_os_installers(catalog) installer_products = find_mac_os_installers(catalog)
for product_key in installer_products: for product_key in installer_products:
product_info[product_key] = {} product_info[product_key] = {}
# get the localized title (e.g. "macOS Catalina Beta") and version
# from ServerMetadataURL for macOS 10.*
# For macOS 11.* we get these directly from the dist file
filename = get_server_metadata(catalog, product_key, workdir) filename = get_server_metadata(catalog, product_key, workdir)
if filename: if filename:
product_info[product_key] = parse_server_metadata(filename) product_info[product_key] = parse_server_metadata(filename)
else: else:
if warnings:
print("WARNING: No server metadata for %s\n" % product_key)
product_info[product_key]["title"] = None product_info[product_key]["title"] = None
product_info[product_key]["version"] = None product_info[product_key]["version"] = None
@ -559,6 +587,8 @@ def os_installer_product_info(catalog, workdir, warnings, ignore_cache=False):
product_info[product_key]["UnsupportedModels"] = unsupported_models product_info[product_key]["UnsupportedModels"] = unsupported_models
board_ids = get_board_ids(dist_path) board_ids = get_board_ids(dist_path)
product_info[product_key]["BoardIDs"] = board_ids product_info[product_key]["BoardIDs"] = board_ids
device_ids = get_device_ids(dist_path)
product_info[product_key]["DeviceIDs"] = device_ids
product_info[product_key].update(dist_info) product_info[product_key].update(dist_info)
if not product_info[product_key]["title"]: if not product_info[product_key]["title"]:
product_info[product_key]["title"] = dist_info.get("title_from_dist") product_info[product_key]["title"] = dist_info.get("title_from_dist")
@ -745,6 +775,7 @@ def main():
# show this Mac's info # show this Mac's info
hw_model = get_hw_model() hw_model = get_hw_model()
board_id = get_board_id() board_id = get_board_id()
bridge_id = get_bridge_id()
build_info = get_current_build_info() build_info = get_current_build_info()
is_vm = is_a_vm() is_vm = is_a_vm()
@ -752,6 +783,7 @@ def main():
if is_vm == True: if is_vm == True:
print("Identified as a Virtual Machine") print("Identified as a Virtual Machine")
print("%-17s: %s" % ("Model Identifier", hw_model)) print("%-17s: %s" % ("Model Identifier", hw_model))
print("%-17s: %s" % ("Bridge ID", bridge_id))
print("%-17s: %s" % ("Board ID", board_id)) print("%-17s: %s" % ("Board ID", board_id))
print("%-17s: %s" % ("OS Version", build_info[0])) print("%-17s: %s" % ("OS Version", build_info[0]))
print("%-17s: %s\n" % ("Build ID", build_info[1])) print("%-17s: %s\n" % ("Build ID", build_info[1]))
@ -790,7 +822,7 @@ def main():
su_catalog_url, args.workdir, ignore_cache=args.ignore_cache su_catalog_url, args.workdir, ignore_cache=args.ignore_cache
) )
product_info = os_installer_product_info( product_info = os_installer_product_info(
catalog, args.workdir, args.warnings, ignore_cache=args.ignore_cache catalog, args.workdir, ignore_cache=args.ignore_cache
) )
if not product_info: if not product_info:
print("No macOS installer products found in the sucatalog.", file=sys.stderr) print("No macOS installer products found in the sucatalog.", file=sys.stderr)
@ -810,30 +842,25 @@ def main():
"%2s %-15s %-10s %-8s %-11s %-30s %s" "%2s %-15s %-10s %-8s %-11s %-30s %s"
% ("#", "ProductID", "Version", "Build", "Post Date", "Title", validity_header) % ("#", "ProductID", "Version", "Build", "Post Date", "Title", validity_header)
) )
# this is where we do checks for validity based on model type and version
for index, product_id in enumerate(product_info): for index, product_id in enumerate(product_info):
not_valid = "" not_valid = ""
if ( if is_vm == False:
not product_info[product_id]["UnsupportedModels"] # first look for a BoardID (not present in modern hardware)
and not product_info[product_id]["BoardIDs"] if board_id and product_info[product_id]["BoardIDs"]:
): if board_id not in product_info[product_id]["BoardIDs"]:
not_valid = "No unsupported Model or supported Board ID data"
else:
if not product_info[product_id]["UnsupportedModels"]:
not_valid = "No unsupported model data "
else:
if (
hw_model in product_info[product_id]["UnsupportedModels"]
and is_vm == False
):
not_valid = "Unsupported Model Identifier"
if not product_info[product_id]["BoardIDs"]:
not_valid += "No supported Board ID data "
else:
if (
board_id not in product_info[product_id]["BoardIDs"]
and is_vm == False
):
not_valid = "Unsupported Board ID" not_valid = "Unsupported Board ID"
# if there's no Board ID there has to be a BridgeID:
elif bridge_id and product_info[product_id]["DeviceIDs"]:
if bridge_id not in product_info[product_id]["DeviceIDs"]:
not_valid = "Unsupported Bridge ID"
# finally we fall back on ModelIdentifiers for T1 and older
elif hw_model and product_info[product_id]["UnsupportedModels"]:
if hw_model in product_info[product_id]["UnsupportedModels"]:
not_valid = "Unsupported Model Identifier"
# if we don't have any of those matches, then we can't do a comparison
else:
not_valid = "No supported model data"
if ( if (
get_latest_version(build_info[0], product_info[product_id]["version"]) get_latest_version(build_info[0], product_info[product_id]["version"])
!= product_info[product_id]["version"] != product_info[product_id]["version"]
@ -841,6 +868,7 @@ def main():
not_valid = "Unsupported macOS version" not_valid = "Unsupported macOS version"
else: else:
valid_build_found = True valid_build_found = True
validity_info = "" validity_info = ""
if args.warnings: if args.warnings:
validity_info = not_valid validity_info = not_valid
@ -863,7 +891,9 @@ def main():
# skip if build is not suitable for current device # skip if build is not suitable for current device
# and a validation parameter was chosen # and a validation parameter was chosen
if not_valid and ( if not_valid and (
args.validate or (args.auto or args.version or args.os) and not args.beta args.validate
or (args.auto or args.version or args.os)
# and not args.beta # not needed now we have DeviceID check
): ):
continue continue
@ -1131,7 +1161,7 @@ def main():
"Adding seeding program %s extended attribute to app" "Adding seeding program %s extended attribute to app"
% seeding_program % seeding_program
) )
xattr.setxattr(installer_app, "SeedProgram", seeding_program) xattr.setxattr(installer_app, "SeedProgram", seeding_program.encode())
print("Product downloaded and installed to %s" % sparse_diskimage_path) print("Product downloaded and installed to %s" % sparse_diskimage_path)
if args.raw: if args.raw:
unmountdmg(mountpoint) unmountdmg(mountpoint)