| OLD | NEW |
| (Empty) |
| 1 # Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """Chromoting helper to install/uninstall host and replace pref pane.""" | |
| 6 | |
| 7 import abc | |
| 8 import os | |
| 9 import shutil | |
| 10 import sys | |
| 11 import subprocess | |
| 12 | |
| 13 | |
| 14 class ChromotingHelper(object): | |
| 15 """Chromoting helper base class.""" | |
| 16 __metaclass__ = abc.ABCMeta | |
| 17 | |
| 18 @abc.abstractmethod | |
| 19 def InstallHost(self, bin_dir): | |
| 20 """Installs the chromoting host""" | |
| 21 return | |
| 22 | |
| 23 @abc.abstractmethod | |
| 24 def UninstallHost(self, bin_dir): | |
| 25 """Uninstalls the chromoting host""" | |
| 26 return | |
| 27 | |
| 28 | |
| 29 class ChromotingHelperMac(ChromotingHelper): | |
| 30 """Chromoting Helper class for Mac. | |
| 31 | |
| 32 Installs/uninstalls host and replace the pref pane for testing purpose. | |
| 33 """ | |
| 34 | |
| 35 def InstallHost(self, bin_dir): | |
| 36 """Installs host on Mac.""" | |
| 37 assert os.geteuid() == 0, 'Need superuser privileges' | |
| 38 | |
| 39 # Run most of the steps here with login user | |
| 40 login_uid = os.getuid() | |
| 41 os.seteuid(login_uid) | |
| 42 | |
| 43 # Change the working dir to the dir that has the host zip file | |
| 44 current_dir = os.getcwd() | |
| 45 pyautolib_dir = os.path.dirname(os.path.abspath(__file__)) | |
| 46 os.chdir(bin_dir) | |
| 47 host_dir = 'remoting-me2me-host-mac' | |
| 48 output_dir = os.path.join(host_dir, 'output') | |
| 49 | |
| 50 # Remove remoting-me2me-host-mac dir just in case | |
| 51 shutil.rmtree(host_dir, True) | |
| 52 | |
| 53 # Unzip the host archive and prepare the files/dirs | |
| 54 subprocess.call('unzip remoting-me2me-host-mac.zip', shell=True) | |
| 55 subprocess.call('mkdir ' + output_dir, shell=True) | |
| 56 | |
| 57 # Prepare security identity for code signing purpose | |
| 58 os.seteuid(0) | |
| 59 key_chain = '/Library/Keychains/ChromotingTest' | |
| 60 password = '1111' | |
| 61 key = os.path.join(pyautolib_dir, 'chromoting_key.p12') | |
| 62 cert = os.path.join(pyautolib_dir, 'chromoting_cert.p12') | |
| 63 subprocess.call(['security', 'delete-keychain', key_chain]) | |
| 64 subprocess.call(['security', 'create-keychain', '-p', | |
| 65 password, key_chain]) | |
| 66 subprocess.call(['security', 'import', key, | |
| 67 '-k', key_chain, '-P', password, '-A']) | |
| 68 subprocess.call(['security', 'import', cert, | |
| 69 '-k', key_chain, '-P', password]) | |
| 70 os.seteuid(login_uid) | |
| 71 | |
| 72 # Sign the host | |
| 73 do_signing = os.path.join(host_dir, 'do_signing.sh') | |
| 74 subprocess.call(do_signing + ' ' + output_dir + ' ' + host_dir + ' ' + | |
| 75 key_chain + ' "Chromoting Test"', shell=True) | |
| 76 | |
| 77 # Remove security identify | |
| 78 os.seteuid(0) | |
| 79 subprocess.call(['security', 'delete-keychain', key_chain]) | |
| 80 os.seteuid(login_uid) | |
| 81 | |
| 82 # Figure out the dmg name | |
| 83 version = "" | |
| 84 for output_file in os.listdir(output_dir): | |
| 85 if output_file.endswith('.dmg'): | |
| 86 version = os.path.basename(output_file)[len('ChromotingHost-'):-4] | |
| 87 | |
| 88 # Mount before installation | |
| 89 dmg = os.path.join(output_dir, 'ChromotingHost-' + version + '.dmg') | |
| 90 subprocess.call('hdiutil' + ' mount ' + dmg, shell=True) | |
| 91 | |
| 92 # Install host | |
| 93 os.seteuid(0) | |
| 94 mpkg = os.path.join('/Volumes', 'Chromoting Host ' + version, | |
| 95 'Chromoting Host.pkg') | |
| 96 subprocess.call(['/usr/sbin/installer', '-pkg', | |
| 97 mpkg, '-target', '/']) | |
| 98 os.seteuid(login_uid) | |
| 99 | |
| 100 # Unmount after installation | |
| 101 mounted = os.path.join('/Volumes', 'Chromoting Host ' + version) | |
| 102 subprocess.call('hdiutil detach "' + mounted + '"', shell=True) | |
| 103 | |
| 104 # Clean up remoting-me2me-host-mac dir | |
| 105 shutil.rmtree(host_dir, True) | |
| 106 | |
| 107 # Resume the original working dir | |
| 108 os.chdir(current_dir) | |
| 109 | |
| 110 def UninstallHost(self, bin_dir): | |
| 111 """Uninstalls host on Mac.""" | |
| 112 assert os.geteuid() == 0, 'Need superuser privileges' | |
| 113 uninstall_app = os.path.join('/', 'Applications', | |
| 114 'Chromoting Host Uninstaller.app', | |
| 115 'Contents', 'MacOS', | |
| 116 'remoting_host_uninstaller') | |
| 117 subprocess.call([uninstall_app, '--no-ui']) | |
| 118 | |
| 119 def ReplacePrefPaneMac(self, operation): | |
| 120 """Constructs mock pref pane to replace the actual pref pane on Mac.""" | |
| 121 assert os.geteuid() == 0, 'Need superuser privileges' | |
| 122 | |
| 123 pref_pane_dir = os.path.join('/Library', 'PreferencePanes') | |
| 124 | |
| 125 mock_pref_pane = os.path.join(pref_pane_dir, 'mock_pref_pane') | |
| 126 pref_pane = os.path.join(pref_pane_dir, | |
| 127 'ChromeRemoteDesktop.prefPane') | |
| 128 mock_pref_pane_python = os.path.join( | |
| 129 os.path.dirname(os.path.abspath(__file__)), | |
| 130 'mock_pref_pane.py') | |
| 131 | |
| 132 # When the symlink from real pref pane to mock pref pane exists, | |
| 133 # mock pref pane will be modified to be a dir when the host is installed. | |
| 134 # After the host is installed and mock pref pane is modified to be a file, | |
| 135 # it will be a file until next host installation. | |
| 136 if os.path.isdir(mock_pref_pane): | |
| 137 shutil.rmtree(mock_pref_pane, True) | |
| 138 elif os.path.isfile(mock_pref_pane): | |
| 139 os.remove(mock_pref_pane) | |
| 140 | |
| 141 mock_pref_pane_file = open(mock_pref_pane, 'w') | |
| 142 mock_pref_pane_file.write('#!/bin/bash\n') | |
| 143 mock_pref_pane_file.write('\n') | |
| 144 mock_pref_pane_file.write('suid-python' + | |
| 145 ' ' + mock_pref_pane_python + ' ' + operation) | |
| 146 mock_pref_pane_file.close() | |
| 147 | |
| 148 subprocess.call(['chmod', 'a+x', mock_pref_pane]) | |
| 149 | |
| 150 # The real pref pane is a dir if the host is installed on a clean machine. | |
| 151 # Once the test is run on the machine, real pref pane will be replaced to | |
| 152 # a symlink. | |
| 153 if os.path.isdir(pref_pane): | |
| 154 shutil.rmtree(pref_pane, True) | |
| 155 elif os.path.isfile(pref_pane): | |
| 156 os.remove(pref_pane) | |
| 157 | |
| 158 subprocess.call(['ln', '-s', mock_pref_pane, pref_pane]) | |
| 159 | |
| 160 | |
| 161 class ChromotingHelperWindows(ChromotingHelper): | |
| 162 """Chromoting Helper class for Windows for installing/uninstalling host.""" | |
| 163 | |
| 164 def InstallHost(self, bin_dir): | |
| 165 """Installs host on Windows.""" | |
| 166 host_msi = os.path.join(bin_dir, 'chromoting.msi') | |
| 167 subprocess.Popen(['msiexec', '/i', host_msi, '/passive']).wait() | |
| 168 | |
| 169 def UninstallHost(self, bin_dir): | |
| 170 """Uninstalls host on Windows.""" | |
| 171 host_msi = os.path.join(bin_dir, 'chromoting.msi') | |
| 172 subprocess.Popen(['msiexec', '/x', host_msi, '/passive']).wait() | |
| 173 | |
| 174 | |
| 175 def Main(): | |
| 176 """Main function to dispatch operations.""" | |
| 177 assert sys.platform.startswith('win') or \ | |
| 178 sys.platform.startswith('darwin'), \ | |
| 179 'Only support Windows and Mac' | |
| 180 | |
| 181 if sys.platform.startswith('win'): | |
| 182 helper = ChromotingHelperWindows() | |
| 183 elif sys.platform.startswith('darwin'): | |
| 184 helper = ChromotingHelperMac() | |
| 185 | |
| 186 if sys.argv[1] == 'install': | |
| 187 helper.InstallHost(sys.argv[2]) | |
| 188 elif sys.argv[1] == 'uninstall': | |
| 189 helper.UninstallHost(sys.argv[2]) | |
| 190 elif sys.argv[1] in ['enable', 'disable', 'changepin']: | |
| 191 assert sys.platform.startswith('darwin'), \ | |
| 192 'Replacing pref pane is Mac specific' | |
| 193 helper.ReplacePrefPaneMac(sys.argv[1]) | |
| 194 else: | |
| 195 print >>sys.stderr, 'Invalid syntax' | |
| 196 return 1 | |
| 197 | |
| 198 | |
| 199 if __name__ == '__main__': | |
| 200 Main() | |
| OLD | NEW |