1#!/usr/bin/python2
2#
3# Copyright 2010 Google Inc. All Rights Reserved.
4"""Script to enter the ChromeOS chroot with mounted sources.
5
6This script enters the chroot with mounted sources.
7"""
8
9from __future__ import print_function
10
11__author__ = 'asharif@google.com (Ahmad Sharif)'
12
13import argparse
14import getpass
15import os
16import pwd
17import sys
18
19from cros_utils import command_executer
20from cros_utils import logger
21from cros_utils import misc
22
23
24class MountPoint(object):
25  """Mount point class"""
26
27  def __init__(self, external_dir, mount_dir, owner, options=None):
28    self.external_dir = os.path.realpath(external_dir)
29    self.mount_dir = os.path.realpath(mount_dir)
30    self.owner = owner
31    self.options = options
32
33  def CreateAndOwnDir(self, dir_name):
34    retv = 0
35    if not os.path.exists(dir_name):
36      command = 'mkdir -p ' + dir_name
37      command += ' || sudo mkdir -p ' + dir_name
38      retv = command_executer.GetCommandExecuter().RunCommand(command)
39    if retv != 0:
40      return retv
41    pw = pwd.getpwnam(self.owner)
42    if os.stat(dir_name).st_uid != pw.pw_uid:
43      command = 'sudo chown -f ' + self.owner + ' ' + dir_name
44      retv = command_executer.GetCommandExecuter().RunCommand(command)
45    return retv
46
47  def DoMount(self):
48    ce = command_executer.GetCommandExecuter()
49    mount_signature = '%s on %s' % (self.external_dir, self.mount_dir)
50    command = 'mount'
51    retv, out, _ = ce.RunCommandWOutput(command)
52    if mount_signature not in out:
53      retv = self.CreateAndOwnDir(self.mount_dir)
54      logger.GetLogger().LogFatalIf(retv, 'Cannot create mount_dir!')
55      retv = self.CreateAndOwnDir(self.external_dir)
56      logger.GetLogger().LogFatalIf(retv, 'Cannot create external_dir!')
57      retv = self.MountDir()
58      logger.GetLogger().LogFatalIf(retv, 'Cannot mount!')
59      return retv
60    else:
61      return 0
62
63  def UnMount(self):
64    ce = command_executer.GetCommandExecuter()
65    return ce.RunCommand('sudo umount %s' % self.mount_dir)
66
67  def MountDir(self):
68    command = 'sudo mount --bind ' + self.external_dir + ' ' + self.mount_dir
69    if self.options == 'ro':
70      command += ' && sudo mount --bind -oremount,ro ' + self.mount_dir
71    retv = command_executer.GetCommandExecuter().RunCommand(command)
72    return retv
73
74  def __str__(self):
75    ret = ''
76    ret += self.external_dir + '\n'
77    ret += self.mount_dir + '\n'
78    if self.owner:
79      ret += self.owner + '\n'
80    if self.options:
81      ret += self.options + '\n'
82    return ret
83
84
85def Main(argv, return_output=False):
86  """The main function."""
87
88  parser = argparse.ArgumentParser()
89  parser.add_argument('-c',
90                      '--chromeos_root',
91                      dest='chromeos_root',
92                      default='../..',
93                      help='ChromeOS root checkout directory.')
94  parser.add_argument('-t',
95                      '--toolchain_root',
96                      dest='toolchain_root',
97                      help='Toolchain root directory.')
98  parser.add_argument('-o',
99                      '--output',
100                      dest='output',
101                      help='Toolchain output directory')
102  parser.add_argument('--sudo',
103                      dest='sudo',
104                      action='store_true',
105                      default=False,
106                      help='Run the command with sudo.')
107  parser.add_argument('-r',
108                      '--third_party',
109                      dest='third_party',
110                      help='The third_party directory to mount.')
111  parser.add_argument('-m',
112                      '--other_mounts',
113                      dest='other_mounts',
114                      help='Other mount points in the form: '
115                      'dir:mounted_dir:options')
116  parser.add_argument('-s',
117                      '--mount-scripts-only',
118                      dest='mount_scripts_only',
119                      action='store_true',
120                      default=False,
121                      help='Mount only the scripts dir, and not the sources.')
122  parser.add_argument('passthrough_argv', nargs='*',
123                      help='Command to be executed inside the chroot.')
124
125  options = parser.parse_args(argv)
126
127  chromeos_root = options.chromeos_root
128
129  chromeos_root = os.path.expanduser(chromeos_root)
130  if options.toolchain_root:
131    options.toolchain_root = os.path.expanduser(options.toolchain_root)
132
133  chromeos_root = os.path.abspath(chromeos_root)
134
135  tc_dirs = []
136  if options.toolchain_root is None or options.mount_scripts_only:
137    m = 'toolchain_root not specified. Will not mount toolchain dirs.'
138    logger.GetLogger().LogWarning(m)
139  else:
140    tc_dirs = [options.toolchain_root + '/google_vendor_src_branch/gcc',
141               options.toolchain_root + '/google_vendor_src_branch/binutils']
142
143  for tc_dir in tc_dirs:
144    if not os.path.exists(tc_dir):
145      logger.GetLogger().LogError('toolchain path ' + tc_dir +
146                                  ' does not exist!')
147      parser.print_help()
148      sys.exit(1)
149
150  if not os.path.exists(chromeos_root):
151    logger.GetLogger().LogError('chromeos_root ' + options.chromeos_root +
152                                ' does not exist!')
153    parser.print_help()
154    sys.exit(1)
155
156  if not os.path.exists(chromeos_root + '/src/scripts/build_packages'):
157    logger.GetLogger(
158    ).LogError(options.chromeos_root + '/src/scripts/build_packages'
159               ' not found!')
160    parser.print_help()
161    sys.exit(1)
162
163  version_dir = os.path.realpath(os.path.expanduser(os.path.dirname(__file__)))
164
165  mounted_tc_root = '/usr/local/toolchain_root'
166  full_mounted_tc_root = chromeos_root + '/chroot/' + mounted_tc_root
167  full_mounted_tc_root = os.path.abspath(full_mounted_tc_root)
168
169  mount_points = []
170  for tc_dir in tc_dirs:
171    last_dir = misc.GetRoot(tc_dir)[1]
172    mount_point = MountPoint(tc_dir, full_mounted_tc_root + '/' + last_dir,
173                             getpass.getuser(), 'ro')
174    mount_points.append(mount_point)
175
176  # Add the third_party mount point if it exists
177  if options.third_party:
178    third_party_dir = options.third_party
179    logger.GetLogger().LogFatalIf(
180        not os.path.isdir(third_party_dir),
181        '--third_party option is not a valid dir.')
182  else:
183    third_party_dir = os.path.abspath('%s/../../../third_party' %
184                                      os.path.dirname(__file__))
185
186  if os.path.isdir(third_party_dir):
187    mount_point = MountPoint(third_party_dir, ('%s/%s' % (
188        full_mounted_tc_root, os.path.basename(third_party_dir))),
189                             getpass.getuser())
190    mount_points.append(mount_point)
191
192  output = options.output
193  if output is None and options.toolchain_root:
194    # Mount the output directory at /usr/local/toolchain_root/output
195    output = options.toolchain_root + '/output'
196
197  if output:
198    mount_points.append(MountPoint(output, full_mounted_tc_root + '/output',
199                                   getpass.getuser()))
200
201  # Mount the other mount points
202  mount_points += CreateMountPointsFromString(options.other_mounts,
203                                              chromeos_root + '/chroot/')
204
205  last_dir = misc.GetRoot(version_dir)[1]
206
207  # Mount the version dir (v14) at /usr/local/toolchain_root/v14
208  mount_point = MountPoint(version_dir, full_mounted_tc_root + '/' + last_dir,
209                           getpass.getuser())
210  mount_points.append(mount_point)
211
212  for mount_point in mount_points:
213    retv = mount_point.DoMount()
214    if retv != 0:
215      return retv
216
217  # Finally, create the symlink to build-gcc.
218  command = 'sudo chown ' + getpass.getuser() + ' ' + full_mounted_tc_root
219  retv = command_executer.GetCommandExecuter().RunCommand(command)
220
221  try:
222    CreateSymlink(last_dir + '/build-gcc', full_mounted_tc_root + '/build-gcc')
223    CreateSymlink(last_dir + '/build-binutils',
224                  full_mounted_tc_root + '/build-binutils')
225  except Exception as e:
226    logger.GetLogger().LogError(str(e))
227
228  # Now call cros_sdk --enter with the rest of the arguments.
229  command = 'cd %s/src/scripts && cros_sdk --enter' % chromeos_root
230
231  if len(options.passthrough_argv) > 1:
232    inner_command = ' '.join(options.passthrough_argv[1:])
233    inner_command = inner_command.strip()
234    if inner_command.startswith('-- '):
235      inner_command = inner_command[3:]
236    command_file = 'tc_enter_chroot.cmd'
237    command_file_path = chromeos_root + '/src/scripts/' + command_file
238    retv = command_executer.GetCommandExecuter().RunCommand('sudo rm -f ' +
239                                                            command_file_path)
240    if retv != 0:
241      return retv
242    f = open(command_file_path, 'w')
243    f.write(inner_command)
244    f.close()
245    logger.GetLogger().LogCmd(inner_command)
246    retv = command_executer.GetCommandExecuter().RunCommand('chmod +x ' +
247                                                            command_file_path)
248    if retv != 0:
249      return retv
250
251    if options.sudo:
252      command += ' sudo ./' + command_file
253    else:
254      command += ' ./' + command_file
255    retv = command_executer.GetCommandExecuter().RunCommandGeneric(
256        command, return_output)
257    return retv
258  else:
259    os.chdir('%s/src/scripts' % chromeos_root)
260    ce = command_executer.GetCommandExecuter()
261    _, out, _ = ce.RunCommandWOutput('which cros_sdk')
262    cros_sdk_binary = out.split()[0]
263    return os.execv(cros_sdk_binary, ['', '--enter'])
264
265
266def CreateMountPointsFromString(mount_strings, chroot_dir):
267  # String has options in the form dir:mount:options
268  mount_points = []
269  if not mount_strings:
270    return mount_points
271  mount_list = mount_strings.split()
272  for mount_string in mount_list:
273    mount_values = mount_string.split(':')
274    external_dir = mount_values[0]
275    mount_dir = mount_values[1]
276    if len(mount_values) > 2:
277      options = mount_values[2]
278    else:
279      options = None
280    mount_point = MountPoint(external_dir, chroot_dir + '/' + mount_dir,
281                             getpass.getuser(), options)
282    mount_points.append(mount_point)
283  return mount_points
284
285
286def CreateSymlink(target, link_name):
287  logger.GetLogger().LogFatalIf(
288      target.startswith('/'), "Can't create symlink to absolute path!")
289  real_from_file = misc.GetRoot(link_name)[0] + '/' + target
290  if os.path.realpath(real_from_file) != os.path.realpath(link_name):
291    if os.path.exists(link_name):
292      command = 'rm -rf ' + link_name
293      command_executer.GetCommandExecuter().RunCommand(command)
294    os.symlink(target, link_name)
295
296
297if __name__ == '__main__':
298  retval = Main(sys.argv)
299  sys.exit(retval)
300