• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python3
2# -*- coding: utf-8 -*-
3#
4# Copyright 2021 The Chromium OS Authors. All rights reserved.
5# Use of this source code is governed by a BSD-style license that can be
6# found in the LICENSE file.
7
8"""Script to make / directory on chromebook writable.
9
10This script updates a remote chromebook to make the / directory writable."
11"""
12
13from __future__ import print_function
14
15__author__ = 'cmtice@google.com (Caroline Tice)'
16
17import argparse
18import os
19import sys
20import time
21
22from cros_utils import command_executer
23from cros_utils import locks
24from cros_utils import logger
25from cros_utils import machines
26from cros_utils import misc
27
28lock_file = '/tmp/image_chromeos_lock/image_chromeos_lock'
29
30
31def Usage(parser, message):
32  print('ERROR: %s' % message)
33  parser.print_help()
34  sys.exit(0)
35
36
37def RebootChromebook(chromeos_root, remote, cmd_executer):
38  cmd = 'sudo reboot'
39  cmd_executer.CrosRunCommand(cmd, chromeos_root=chromeos_root, machine=remote)
40  time.sleep(10)
41  success = False
42  for _ in range(1, 10):
43    if machines.MachineIsPingable(remote):
44      success = True
45      break
46    time.sleep(1)
47  return success
48
49
50def ParseOutput(output):
51  # See comment in FindPartitionNum.
52  lines = output.split('\n')
53  num_str = '-1'
54  for line in lines:
55    l = line.strip()
56    words = l.split()
57    if (len(words) > 2 and words[0] == 'sudo' and
58        words[1] == '/usr/share/vboot/bin/make_dev_ssd.sh' and
59        words[-2] == '--partitions'):
60      num_str = words[-1]
61      break
62  num = int(num_str)
63
64  return num
65
66
67def FindPartitionNum(chromeos_root, remote, logs, cmd_executer):
68  partition_cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh '
69                   '--remove_rootfs_verification')
70  _, output, _ = cmd_executer.CrosRunCommandWOutput(
71      partition_cmd,
72      chromeos_root=chromeos_root,
73      machine=remote,
74      terminated_timeout=10)
75
76  # The command above, with no --partitions flag, should return output
77  # in the following form:
78
79  # make_dev_ssd.sh: INFO: checking system firmware...
80  #
81  #  ERROR: YOU ARE TRYING TO MODIFY THE LIVE SYSTEM IMAGE /dev/mmcblk0.
82  #
83  #  The system may become unusable after that change, especially when you have
84  #  some auto updates in progress. To make it safer, we suggest you to only
85  #  change the partition you have booted with. To do that, re-execute this
86  #  command as:
87  #
88  #  sudo /usr/share/vboot/bin/make_dev_ssd.sh  --partitions 4
89  #
90  #  If you are sure to modify other partition, please invoke the command again
91  #  and explicitly assign only one target partition for each time
92  # (--partitions N )
93  #
94  # make_dev_ssd.sh: ERROR: IMAGE /dev/mmcblk0 IS NOT MODIFIED.
95
96  # We pass this output to the ParseOutput function where it finds the 'sudo'
97  # line with the partition number and returns the partition number.
98
99  num = ParseOutput(output)
100
101  if num == -1:
102    logs.LogOutput('Failed to find partition number in "%s"' % output)
103  return num
104
105
106def TryRemoveRootfsFromPartition(chromeos_root, remote, cmd_executer,
107                                 partition_num):
108  partition_cmd = ('/usr/share/vboot/bin/make_dev_ssd.sh '
109                   '--remove_rootfs_verification --partition %d' %
110                   partition_num)
111  ret = cmd_executer.CrosRunCommand(
112      partition_cmd,
113      chromeos_root=chromeos_root,
114      machine=remote,
115      terminated_timeout=10)
116  return ret
117
118
119def TryRemountPartitionAsRW(chromeos_root, remote, cmd_executer):
120  command = 'sudo mount -o remount,rw /'
121  ret = cmd_executer.CrosRunCommand(
122      command,
123      chromeos_root=chromeos_root,
124      machine=remote,
125      terminated_timeout=10)
126  return ret
127
128
129def Main(argv):
130  parser = argparse.ArgumentParser()
131  parser.add_argument(
132      '-c',
133      '--chromeos_root',
134      dest='chromeos_root',
135      help='Target directory for ChromeOS installation.')
136  parser.add_argument('-r', '--remote', dest='remote', help='Target device.')
137  parser.add_argument(
138      '-n',
139      '--no_lock',
140      dest='no_lock',
141      default=False,
142      action='store_true',
143      help='Do not attempt to lock remote before imaging.  '
144      'This option should only be used in cases where the '
145      'exclusive lock has already been acquired (e.g. in '
146      'a script that calls this one).')
147
148  options = parser.parse_args(argv[1:])
149
150  # Common initializations
151  log_level = 'average'
152  cmd_executer = command_executer.GetCommandExecuter(log_level=log_level)
153  l = logger.GetLogger()
154
155  if options.chromeos_root is None:
156    Usage(parser, '--chromeos_root must be set')
157
158  if options.remote is None:
159    Usage(parser, '--remote must be set')
160
161  options.chromeos_root = os.path.expanduser(options.chromeos_root)
162
163  try:
164    should_unlock = False
165    if not options.no_lock:
166      try:
167        _ = locks.AcquireLock(
168            list(options.remote.split()), options.chromeos_root)
169        should_unlock = True
170      except Exception as e:
171        raise RuntimeError('Error acquiring machine: %s' % str(e))
172
173    # Workaround for crosbug.com/35684.
174    os.chmod(misc.GetChromeOSKeyFile(options.chromeos_root), 0o600)
175
176    if log_level == 'average':
177      cmd_executer.SetLogLevel('verbose')
178
179    if not machines.MachineIsPingable(options.remote):
180      raise RuntimeError('Machine %s does not appear to be up.' %
181                         options.remote)
182
183    ret = TryRemountPartitionAsRW(options.chromeos_root, options.remote,
184                                  cmd_executer)
185
186    if ret != 0:
187      l.LogOutput('Initial mount command failed. Looking for root partition'
188                  ' number.')
189      part_num = FindPartitionNum(options.chromeos_root, options.remote, l,
190                                  cmd_executer)
191      if part_num != -1:
192        l.LogOutput('Attempting to remove rootfs verification on partition %d' %
193                    part_num)
194        ret = TryRemoveRootfsFromPartition(options.chromeos_root,
195                                           options.remote, cmd_executer,
196                                           part_num)
197        if ret == 0:
198          l.LogOutput('Succeeded in removing roofs verification from'
199                      ' partition %d. Rebooting...' % part_num)
200          if not RebootChromebook(options.chromeos_root, options.remote,
201                                  cmd_executer):
202            raise RuntimeError('Chromebook failed to reboot.')
203          l.LogOutput('Reboot succeeded. Attempting to remount partition.')
204          ret = TryRemountPartitionAsRW(options.chromeos_root, options.remote,
205                                        cmd_executer)
206          if ret == 0:
207            l.LogOutput('Re-mounted / as writable.')
208          else:
209            l.LogOutput('Re-mount failed. / is not writable.')
210        else:
211          l.LogOutput('Failed to remove rootfs verification from partition'
212                      ' %d.' % part_num)
213    else:
214      l.LogOutput('Re-mounted / as writable.')
215
216    l.LogOutput('Exiting.')
217
218  finally:
219    if should_unlock:
220      locks.ReleaseLock(list(options.remote.split()), options.chromeos_root)
221
222  return ret
223
224
225if __name__ == '__main__':
226  retval = Main(sys.argv)
227  sys.exit(retval)
228