Allow overlapping sync and async startup requests
[chromium-blink-merge.git] / chrome / test / pyautolib / chromeos / suid_actions.py
blobb0ba529bfd2d99e1f1baf9a03eaae98fb041c59b
1 #!/usr/bin/env python
2 # Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Helper script to perform actions as a super-user on ChromeOS.
8 Needs to be run with superuser privileges, typically using the
9 suid_python binary.
11 Usage:
12 sudo python suid_actions.py --action=CleanFlimflamDirs
13 """
15 import optparse
16 import os
17 import shutil
18 import subprocess
19 import sys
20 import time
22 sys.path.append('/usr/local') # to import autotest libs.
23 from autotest.cros import constants
24 from autotest.cros import cryptohome
26 TEMP_BACKCHANNEL_FILE = '/tmp/pyauto_network_backchannel_file'
29 class SuidAction(object):
30 """Helper to perform some super-user actions on ChromeOS."""
32 def _ParseArgs(self):
33 parser = optparse.OptionParser()
34 parser.add_option(
35 '-a', '--action', help='Action to perform.')
36 self._options = parser.parse_args()[0]
37 if not self._options.action:
38 raise RuntimeError('No action specified.')
40 def Run(self):
41 self._ParseArgs()
42 assert os.geteuid() == 0, 'Needs superuser privileges.'
43 handler = getattr(self, self._options.action)
44 assert handler and callable(handler), \
45 'No handler for %s' % self._options.action
46 handler()
47 return 0
49 ## Actions ##
50 def CleanFlimflamDirs(self):
51 """Clean the contents of all connection manager (shill/flimflam) profiles.
52 """
53 flimflam_dirs = ['/home/chronos/user/flimflam',
54 '/home/chronos/user/shill',
55 '/var/cache/flimflam',
56 '/var/cache/shill']
58 # The stop/start flimflam command should stop/start shill respectivly if
59 # enabled.
60 os.system('stop flimflam')
61 try:
62 for flimflam_dir in flimflam_dirs:
63 if not os.path.exists(flimflam_dir):
64 continue
65 for item in os.listdir(flimflam_dir):
66 path = os.path.join(flimflam_dir, item)
67 if os.path.isdir(path):
68 shutil.rmtree(path)
69 else:
70 os.remove(path)
71 finally:
72 os.system('start flimflam')
73 # TODO(stanleyw): crosbug.com/29421 This method should wait until
74 # flimflam/shill is fully initialized and accessible via DBus again.
75 # Otherwise, there is a race conditions and subsequent accesses to
76 # flimflam/shill may fail. Until this is fixed, waiting for the
77 # resolv.conf file to be created is better than nothing.
78 begin = time.time()
79 while not os.path.exists(constants.RESOLV_CONF_FILE):
80 if time.time() - begin > 10:
81 raise RuntimeError('Timeout while waiting for flimflam/shill start.')
82 time.sleep(.25)
84 def RemoveAllCryptohomeVaults(self):
85 """Remove any existing cryptohome vaults."""
86 cryptohome.remove_all_vaults()
88 def _GetEthInterfaces(self):
89 """Returns a list of the eth* interfaces detected by the device."""
90 # Assumes ethernet interfaces all have "eth" in the name.
91 import pyudev
92 return sorted([iface.sys_name for iface in
93 pyudev.Context().list_devices(subsystem='net')
94 if 'eth' in iface.sys_name])
96 def _Renameif(self, old_iface, new_iface, mac_address):
97 """Renames the interface with mac_address from old_iface to new_iface.
99 Args:
100 old_iface: The name of the interface you want to change.
101 new_iface: The name of the interface you want to change to.
102 mac_address: The mac address of the interface being changed.
104 subprocess.call(['stop', 'flimflam'])
105 subprocess.call(['ifconfig', old_iface, 'down'])
106 subprocess.call(['nameif', new_iface, mac_address])
107 subprocess.call(['ifconfig', new_iface, 'up'])
108 subprocess.call(['start', 'flimflam'])
110 # Check and make sure interfaces have been renamed
111 eth_ifaces = self._GetEthInterfaces()
112 if new_iface not in eth_ifaces:
113 raise RuntimeError('Interface %s was not renamed to %s' %
114 (old_iface, new_iface))
115 elif old_iface in eth_ifaces:
116 raise RuntimeError('Old iface %s is still present' % old_iface)
118 def SetupBackchannel(self):
119 """Renames the connected ethernet interface to eth_test for offline mode
120 testing. Does nothing if no connected interface is found.
122 # Return the interface with ethernet connected or returns if none found.
123 for iface in self._GetEthInterfaces():
124 with open('/sys/class/net/%s/operstate' % iface, 'r') as fp:
125 if 'up' in fp.read():
126 eth_iface = iface
127 break
128 else:
129 return
131 # Write backup file to be used by TeardownBackchannel to restore the
132 # interface names.
133 with open(TEMP_BACKCHANNEL_FILE, 'w') as fpw:
134 with open('/sys/class/net/%s/address' % eth_iface) as fp:
135 mac_address = fp.read().strip()
136 fpw.write('%s, %s' % (eth_iface, mac_address))
138 self._Renameif(eth_iface, 'eth_test', mac_address)
140 def TeardownBackchannel(self):
141 """Restores the eth interface names if SetupBackchannel was called."""
142 if not os.path.isfile(TEMP_BACKCHANNEL_FILE):
143 return
145 with open(TEMP_BACKCHANNEL_FILE, 'r') as fp:
146 eth_iface, mac_address = fp.read().split(',')
148 self._Renameif('eth_test', eth_iface, mac_address)
149 os.remove(TEMP_BACKCHANNEL_FILE)
152 if __name__ == '__main__':
153 sys.exit(SuidAction().Run())