OLD | NEW |
| (Empty) |
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. | |
5 | |
6 """Helper script to perform actions as a super-user on ChromeOS. | |
7 | |
8 Needs to be run with superuser privileges, typically using the | |
9 suid_python binary. | |
10 | |
11 Usage: | |
12 sudo python suid_actions.py --action=CleanFlimflamDirs | |
13 """ | |
14 | |
15 import optparse | |
16 import os | |
17 import shutil | |
18 import subprocess | |
19 import sys | |
20 import time | |
21 | |
22 sys.path.append('/usr/local') # to import autotest libs. | |
23 from autotest.cros import constants | |
24 from autotest.cros import cryptohome | |
25 | |
26 TEMP_BACKCHANNEL_FILE = '/tmp/pyauto_network_backchannel_file' | |
27 | |
28 | |
29 class SuidAction(object): | |
30 """Helper to perform some super-user actions on ChromeOS.""" | |
31 | |
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.') | |
39 | |
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 | |
48 | |
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'] | |
57 | |
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) | |
83 | |
84 def RemoveAllCryptohomeVaults(self): | |
85 """Remove any existing cryptohome vaults.""" | |
86 cryptohome.remove_all_vaults() | |
87 | |
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]) | |
95 | |
96 def _Renameif(self, old_iface, new_iface, mac_address): | |
97 """Renames the interface with mac_address from old_iface to new_iface. | |
98 | |
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. | |
103 """ | |
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']) | |
109 | |
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) | |
117 | |
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. | |
121 """ | |
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 | |
130 | |
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)) | |
137 | |
138 self._Renameif(eth_iface, 'eth_test', mac_address) | |
139 | |
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 | |
144 | |
145 with open(TEMP_BACKCHANNEL_FILE, 'r') as fp: | |
146 eth_iface, mac_address = fp.read().split(',') | |
147 | |
148 self._Renameif('eth_test', eth_iface, mac_address) | |
149 os.remove(TEMP_BACKCHANNEL_FILE) | |
150 | |
151 | |
152 if __name__ == '__main__': | |
153 sys.exit(SuidAction().Run()) | |
OLD | NEW |