| #!/usr/bin/env python3 |
| # -*- coding: utf-8 -*- |
| # |
| # Copyright 2021 The ChromiumOS Authors |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| """Script to make / directory on chromebook writable. |
| |
| This script updates a remote chromebook to make the / directory writable." |
| """ |
| |
| |
| __author__ = "[email protected] (Caroline Tice)" |
| |
| import argparse |
| import os |
| import sys |
| import time |
| |
| from cros_utils import command_executer |
| from cros_utils import locks |
| from cros_utils import logger |
| from cros_utils import machines |
| from cros_utils import misc |
| |
| |
| lock_file = "/tmp/image_chromeos_lock/image_chromeos_lock" |
| |
| |
| def Usage(parser, message): |
| print("ERROR: %s" % message) |
| parser.print_help() |
| sys.exit(0) |
| |
| |
| def RebootChromebook(chromeos_root, remote, cmd_executer): |
| cmd = "sudo reboot" |
| cmd_executer.CrosRunCommand( |
| cmd, chromeos_root=chromeos_root, machine=remote |
| ) |
| time.sleep(10) |
| success = False |
| for _ in range(1, 10): |
| if machines.MachineIsPingable(remote): |
| success = True |
| break |
| time.sleep(1) |
| return success |
| |
| |
| def ParseOutput(output): |
| # See comment in FindPartitionNum. |
| lines = output.split("\n") |
| num_str = "-1" |
| for line in lines: |
| l = line.strip() |
| words = l.split() |
| if ( |
| len(words) > 2 |
| and words[0] == "sudo" |
| and words[1] == "/usr/share/vboot/bin/make_dev_ssd.sh" |
| and words[-2] == "--partitions" |
| ): |
| num_str = words[-1] |
| break |
| num = int(num_str) |
| |
| return num |
| |
| |
| def FindPartitionNum(chromeos_root, remote, logs, cmd_executer): |
| partition_cmd = ( |
| "/usr/share/vboot/bin/make_dev_ssd.sh " "--remove_rootfs_verification" |
| ) |
| _, output, _ = cmd_executer.CrosRunCommandWOutput( |
| partition_cmd, |
| chromeos_root=chromeos_root, |
| machine=remote, |
| terminated_timeout=10, |
| ) |
| |
| # The command above, with no --partitions flag, should return output |
| # in the following form: |
| |
| # make_dev_ssd.sh: INFO: checking system firmware... |
| # |
| # ERROR: YOU ARE TRYING TO MODIFY THE LIVE SYSTEM IMAGE /dev/mmcblk0. |
| # |
| # The system may become unusable after that change, especially when you have |
| # some auto updates in progress. To make it safer, we suggest you to only |
| # change the partition you have booted with. To do that, re-execute this |
| # command as: |
| # |
| # sudo /usr/share/vboot/bin/make_dev_ssd.sh --partitions 4 |
| # |
| # If you are sure to modify other partition, please invoke the command again |
| # and explicitly assign only one target partition for each time |
| # (--partitions N ) |
| # |
| # make_dev_ssd.sh: ERROR: IMAGE /dev/mmcblk0 IS NOT MODIFIED. |
| |
| # We pass this output to the ParseOutput function where it finds the 'sudo' |
| # line with the partition number and returns the partition number. |
| |
| num = ParseOutput(output) |
| |
| if num == -1: |
| logs.LogOutput('Failed to find partition number in "%s"' % output) |
| return num |
| |
| |
| def TryRemoveRootfsFromPartition( |
| chromeos_root, remote, cmd_executer, partition_num |
| ): |
| partition_cmd = ( |
| "/usr/share/vboot/bin/make_dev_ssd.sh " |
| "--remove_rootfs_verification --partition %d" % partition_num |
| ) |
| ret = cmd_executer.CrosRunCommand( |
| partition_cmd, |
| chromeos_root=chromeos_root, |
| machine=remote, |
| terminated_timeout=10, |
| ) |
| return ret |
| |
| |
| def TryRemountPartitionAsRW(chromeos_root, remote, cmd_executer): |
| command = "sudo mount -o remount,rw /" |
| ret = cmd_executer.CrosRunCommand( |
| command, |
| chromeos_root=chromeos_root, |
| machine=remote, |
| terminated_timeout=10, |
| ) |
| return ret |
| |
| |
| def Main(argv): |
| parser = argparse.ArgumentParser() |
| parser.add_argument( |
| "-c", |
| "--chromeos_root", |
| dest="chromeos_root", |
| help="Target directory for ChromeOS installation.", |
| ) |
| parser.add_argument("-r", "--remote", dest="remote", help="Target device.") |
| parser.add_argument( |
| "-n", |
| "--no_lock", |
| dest="no_lock", |
| default=False, |
| action="store_true", |
| help="Do not attempt to lock remote before imaging. " |
| "This option should only be used in cases where the " |
| "exclusive lock has already been acquired (e.g. in " |
| "a script that calls this one).", |
| ) |
| |
| options = parser.parse_args(argv[1:]) |
| |
| # Common initializations |
| log_level = "average" |
| cmd_executer = command_executer.GetCommandExecuter(log_level=log_level) |
| l = logger.GetLogger() |
| |
| if options.chromeos_root is None: |
| Usage(parser, "--chromeos_root must be set") |
| |
| if options.remote is None: |
| Usage(parser, "--remote must be set") |
| |
| options.chromeos_root = os.path.expanduser(options.chromeos_root) |
| |
| try: |
| should_unlock = False |
| if not options.no_lock: |
| try: |
| _ = locks.AcquireLock( |
| list(options.remote.split()), options.chromeos_root |
| ) |
| should_unlock = True |
| except Exception as e: |
| raise RuntimeError("Error acquiring machine: %s" % str(e)) |
| |
| # Workaround for crosbug.com/35684. |
| os.chmod(misc.GetChromeOSKeyFile(options.chromeos_root), 0o600) |
| |
| if log_level == "average": |
| cmd_executer.SetLogLevel("verbose") |
| |
| if not machines.MachineIsPingable(options.remote): |
| raise RuntimeError( |
| "Machine %s does not appear to be up." % options.remote |
| ) |
| |
| ret = TryRemountPartitionAsRW( |
| options.chromeos_root, options.remote, cmd_executer |
| ) |
| |
| if ret != 0: |
| l.LogOutput( |
| "Initial mount command failed. Looking for root partition" |
| " number." |
| ) |
| part_num = FindPartitionNum( |
| options.chromeos_root, options.remote, l, cmd_executer |
| ) |
| if part_num != -1: |
| l.LogOutput( |
| "Attempting to remove rootfs verification on partition %d" |
| % part_num |
| ) |
| ret = TryRemoveRootfsFromPartition( |
| options.chromeos_root, |
| options.remote, |
| cmd_executer, |
| part_num, |
| ) |
| if ret == 0: |
| l.LogOutput( |
| "Succeeded in removing roofs verification from" |
| " partition %d. Rebooting..." % part_num |
| ) |
| if not RebootChromebook( |
| options.chromeos_root, options.remote, cmd_executer |
| ): |
| raise RuntimeError("Chromebook failed to reboot.") |
| l.LogOutput( |
| "Reboot succeeded. Attempting to remount partition." |
| ) |
| ret = TryRemountPartitionAsRW( |
| options.chromeos_root, options.remote, cmd_executer |
| ) |
| if ret == 0: |
| l.LogOutput("Re-mounted / as writable.") |
| else: |
| l.LogOutput("Re-mount failed. / is not writable.") |
| else: |
| l.LogOutput( |
| "Failed to remove rootfs verification from partition" |
| " %d." % part_num |
| ) |
| else: |
| l.LogOutput("Re-mounted / as writable.") |
| |
| l.LogOutput("Exiting.") |
| |
| finally: |
| if should_unlock: |
| locks.ReleaseLock( |
| list(options.remote.split()), options.chromeos_root |
| ) |
| |
| return ret |
| |
| |
| if __name__ == "__main__": |
| retval = Main(sys.argv) |
| sys.exit(retval) |