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
12 sudo python suid_actions.py --action=CleanFlimflamDirs
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."""
33 parser
= optparse
.OptionParser()
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.')
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
50 def CleanFlimflamDirs(self
):
51 """Clean the contents of all connection manager (shill/flimflam) profiles.
53 flimflam_dirs
= ['/home/chronos/user/flimflam',
54 '/home/chronos/user/shill',
55 '/var/cache/flimflam',
58 # The stop/start flimflam command should stop/start shill respectivly if
60 os
.system('stop flimflam')
62 for flimflam_dir
in flimflam_dirs
:
63 if not os
.path
.exists(flimflam_dir
):
65 for item
in os
.listdir(flimflam_dir
):
66 path
= os
.path
.join(flimflam_dir
, item
)
67 if os
.path
.isdir(path
):
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.
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.')
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.
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.
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():
131 # Write backup file to be used by TeardownBackchannel to restore the
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
):
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())