186 |
- |
1 |
"""
|
|
|
2 |
Copyright (C) 2008-2019 VMware, Inc. All rights reserved. VMware Confidential
|
|
|
3 |
|
|
|
4 |
VMware Player App component installer.
|
|
|
5 |
"""
|
|
|
6 |
from random import randint
|
|
|
7 |
import os
|
|
|
8 |
import sys
|
|
|
9 |
import threading
|
|
|
10 |
import uuid
|
|
|
11 |
from vmis import ui
|
|
|
12 |
|
|
|
13 |
GCONF_DEFAULTS = 'xml:readwrite:/etc/gconf/gconf.xml.defaults'
|
|
|
14 |
DEST = LIBDIR/'vmware'
|
|
|
15 |
CONFIG = DEST/'setup/vmware-config'
|
|
|
16 |
CUPSLIBDIR = LIBDIR/'cups'
|
|
|
17 |
|
|
|
18 |
# These get set in PostInstall
|
|
|
19 |
SETTINGS = \
|
|
|
20 |
{'product.buildNumber': None,
|
|
|
21 |
'player.product.version': None,
|
|
|
22 |
'vix.config.version': None}
|
|
|
23 |
|
|
|
24 |
vmwareSentinel = '# Automatically generated by the VMware Installer - DO NOT REMOVE\n'
|
|
|
25 |
|
|
|
26 |
# Player and Workstation both depend on some configuration living
|
|
|
27 |
# in /etc/vmware
|
|
|
28 |
ETCDIR = Destination('/etc/vmware')
|
|
|
29 |
|
|
|
30 |
class PlayerApp(Installer):
|
|
|
31 |
def PreTransactionInstall(self, old, new, upgrade):
|
|
|
32 |
pass
|
|
|
33 |
|
|
|
34 |
def InitializeQuestions(self, old, new, upgrade):
|
|
|
35 |
key = 'softwareUpdateEnabled'
|
|
|
36 |
text = 'Would you like to check for product updates on startup?'
|
|
|
37 |
value = self.GetAnswer(key)
|
|
|
38 |
if value:
|
|
|
39 |
qlevel = 'CUSTOM'
|
|
|
40 |
default = value
|
|
|
41 |
existing = True
|
|
|
42 |
else:
|
|
|
43 |
qlevel = 'REGULAR'
|
|
|
44 |
default = 'yes'
|
|
|
45 |
existing = False
|
|
|
46 |
|
|
|
47 |
self.AddQuestion('YesNo',
|
|
|
48 |
key=key,
|
|
|
49 |
text=text,
|
|
|
50 |
required=False,
|
|
|
51 |
default=default,
|
|
|
52 |
level=qlevel,
|
|
|
53 |
deferrable=True,
|
|
|
54 |
existing=existing)
|
|
|
55 |
|
|
|
56 |
key = 'dataCollectionEnabled'
|
|
|
57 |
text = 'VMware\'s Customer Experience Improvement Program ("CEIP") provides ' \
|
|
|
58 |
'VMware with information that enables VMware to improve its products and ' \
|
|
|
59 |
'services, to fix problems, and to advise you on how best to deploy and ' \
|
|
|
60 |
'use our products. As part of the CEIP, VMware collects technical ' \
|
|
|
61 |
'information about your organization\'s use of VMware products and ' \
|
|
|
62 |
'services on a regular basis in association with your organization\'s ' \
|
|
|
63 |
'VMware license key(s). This information does not personally identify ' \
|
|
|
64 |
'any individual.\nAdditional information regarding the data collected ' \
|
|
|
65 |
'through CEIP and the purposes for which it is used by VMware is set ' \
|
|
|
66 |
'forth in the Trust & Assurance Center at ' \
|
|
|
67 |
'http://www.vmware.com/trustvmware/ceip.html.\n\n' \
|
|
|
68 |
'Join the VMware Customer Experience Improvement Program ("CEIP")?\n' \
|
|
|
69 |
'If you prefer not to participate in VMware\'s CEIP for this product, ' \
|
|
|
70 |
'you should select "No" below.\nYou may join or leave VMware\'s CEIP ' \
|
|
|
71 |
'for this product at any time.'
|
|
|
72 |
|
|
|
73 |
self.AddQuestion('YesNo',
|
|
|
74 |
key=key,
|
|
|
75 |
text=text,
|
|
|
76 |
required=False,
|
|
|
77 |
default='yes',
|
|
|
78 |
level='REGULAR',
|
|
|
79 |
deferrable=True)
|
|
|
80 |
|
|
|
81 |
def InitializeInstall(self, old, new, upgrade):
|
|
|
82 |
global CUPSLIBDIR
|
|
|
83 |
# Initialize CUPSLIBDIR
|
|
|
84 |
# XXX: LIBDIR should be properly calculated, to make this cleaner
|
|
|
85 |
if (PREFIX/'lib64/cups').exists():
|
|
|
86 |
CUPSLIBDIR = PREFIX/'lib64/cups'
|
|
|
87 |
|
|
|
88 |
self.AddTarget('File', 'bin/*', BINDIR)
|
|
|
89 |
self.AddTarget('File', 'sbin/*', SBINDIR)
|
|
|
90 |
|
|
|
91 |
for d in [ 'desktop-directories', 'icons', 'mime']:
|
|
|
92 |
self.AddTarget('File', 'share/%s/*' % d, DATADIR/d)
|
|
|
93 |
|
|
|
94 |
if self.GetConfig('installShortcuts', component='vmware-installer') != 'no':
|
|
|
95 |
self.AddTarget('File', 'share/applications/*', DATADIR/'applications')
|
|
|
96 |
self.AddTarget('File', 'share/appdata/*', DATADIR/'appdata')
|
|
|
97 |
|
|
|
98 |
self.AddTarget('File', 'lib/*', DEST)
|
|
|
99 |
self.AddTarget('File', 'doc/*', DOCDIR/'vmware-player')
|
|
|
100 |
self.AddTarget('File', 'etc/xdg/*', SYSCONFDIR/'xdg')
|
|
|
101 |
self.AddTarget('File', 'etc/cups/*', SYSCONFDIR/'cups')
|
|
|
102 |
self.AddTarget('File', 'etc/init.d/*', SYSCONFDIR/'init.d')
|
|
|
103 |
self.AddTarget('File', 'var/*', '/var')
|
|
|
104 |
|
|
|
105 |
# Symlink all binaries to appLoader.
|
|
|
106 |
for i in [
|
|
|
107 |
'vmplayer',
|
|
|
108 |
'vmware-enter-serial',
|
|
|
109 |
'vmware-setup-helper',
|
|
|
110 |
'licenseTool',
|
|
|
111 |
'vmware-mount',
|
|
|
112 |
'vmware-fuseUI',
|
|
|
113 |
'vmware-app-control',
|
|
|
114 |
'vmware-zenity',
|
|
|
115 |
]:
|
|
|
116 |
self.AddTarget('Link', DEST/'bin/appLoader', DEST/'bin'/i)
|
|
|
117 |
|
|
|
118 |
self.SetPermission(DEST/'bin/*', BINARY)
|
|
|
119 |
|
|
|
120 |
# .thnumod is an executable that just happens to live in /etc/thnuclnt,
|
|
|
121 |
# and might as well be in /usr/bin or similar instead. For SELinux systems,
|
|
|
122 |
# files under /etc may be given an etc_t file context, which is off-limits
|
|
|
123 |
# to the reduced privilege lp processes.
|
|
|
124 |
#
|
|
|
125 |
# To get around this (since we can't guarantee semanage is available to
|
|
|
126 |
# properly and permanently relabel .thnumod), we'll just use a symlink to
|
|
|
127 |
# $libdir/bin/.thnumod and leave it at that. ($libdir/bin/.thnumod will have
|
|
|
128 |
# the lib_t context, which is fine for CUPS filters.)
|
|
|
129 |
self.AddTarget('File', 'extras/.thnumod', SYSCONFDIR/'thnuclnt/.thnumod')
|
|
|
130 |
self.AddTarget('File', 'extras/thnucups', CUPSLIBDIR/'filter/thnucups')
|
|
|
131 |
self.SetPermission(SYSCONFDIR/'thnuclnt/.thnumod', BINARY)
|
|
|
132 |
self.SetPermission(CUPSLIBDIR/'filter/thnucups', BINARY)
|
|
|
133 |
|
|
|
134 |
# Ubuntu 10.04 requires additional .desktop files in /usr/local/share/applications
|
|
|
135 |
# If GNOME or Ubuntu is going this way, rather than just add these links for
|
|
|
136 |
# Ubuntu 10.04, always add them for future-proofing.
|
|
|
137 |
|
|
|
138 |
# Some linux distributions use yet another standard for DE metadata, requiring
|
|
|
139 |
# .appdata.xml files in order for WS to be usable via the DE app launcher.
|
|
|
140 |
# Like the .desktop files, just install them along with the rest for futureproofing.
|
|
|
141 |
if self.GetConfig('installShortcuts', component='vmware-installer') != 'no':
|
|
|
142 |
self.AddTarget('Link', DATADIR/'applications/vmware-player.desktop',
|
|
|
143 |
PREFIX/'local/share/applications/vmware-player.desktop')
|
|
|
144 |
self.AddTarget('Link', DATADIR/'appdata/vmware-player.appdata.xml',
|
|
|
145 |
PREFIX/'local/share/appdata/vmware-player.appdata.xml')
|
|
|
146 |
|
|
|
147 |
self.AddTarget('Link', DEST/'bin/vmware-mount', BINDIR/'vmware-mount')
|
|
|
148 |
self.AddTarget('Link', DEST/'bin/vmware-netcfg', BINDIR/'vmware-netcfg')
|
|
|
149 |
self.AddTarget('Link', DEST/'bin/vmware-fuseUI', BINDIR/'vmware-fuseUI')
|
|
|
150 |
self.AddTarget('Link', DEST/'bin/appLoader', BINDIR/'vmrest')
|
|
|
151 |
|
|
|
152 |
themeIndex = DATADIR/'icons/hicolor/index.theme'
|
|
|
153 |
|
|
|
154 |
# @todo: This should probably be treated as a SYSTEM file type
|
|
|
155 |
# but it's not yet implemented.
|
|
|
156 |
not themeIndex.exists() and self.AddTarget('File', 'files/index.theme', themeIndex)
|
|
|
157 |
|
|
|
158 |
def _scriptRunnable(self, script):
|
|
|
159 |
""" Returns True if the script exists and is in a runnable state """
|
|
|
160 |
return script.isexe() and script.isfile() and self.RunCommand(script, 'validate').retCode == 100
|
|
|
161 |
|
|
|
162 |
def _vmwareMountRunnable(self, vmwareMount):
|
|
|
163 |
# vmware-mount may exist but if libfuse isn't installed we have
|
|
|
164 |
# to check explicitly, otherwise it'll appear that disks are
|
|
|
165 |
# mounted when in fact the program just can't be linked or
|
|
|
166 |
# executed.
|
|
|
167 |
#
|
|
|
168 |
# We must check that -L works, otherwise there may be some
|
|
|
169 |
# initialization error that will be improperly interpreted as
|
|
|
170 |
# disks being mounted.
|
|
|
171 |
return vmwareMount.exists() and self.RunCommand(vmwareMount, noLogging=True).retCode != 127 and \
|
|
|
172 |
self.RunCommand(vmwareMount, '-L', noLogging=True).retCode == 0
|
|
|
173 |
|
|
|
174 |
def InitializeUninstall(self, old, new, upgrade):
|
|
|
175 |
vmwareMount = BINDIR/'vmware-mount'
|
|
|
176 |
|
|
|
177 |
if self._vmwareMountRunnable(vmwareMount):
|
|
|
178 |
# Unmount all virtual disks.
|
|
|
179 |
if self.RunCommand(vmwareMount, '-X', noLogging=True).retCode == 0:
|
|
|
180 |
log.Info('All virtual disks were unmounted successfully')
|
|
|
181 |
return
|
|
|
182 |
else:
|
|
|
183 |
log.Error('Some virtual disks were unable to be unmounted')
|
|
|
184 |
raise InstallError('Some virtual disks could not be unmounted. Make '
|
|
|
185 |
'sure that all files opened on all virtual disks are '
|
|
|
186 |
'closed and then run this installer again.')
|
|
|
187 |
else:
|
|
|
188 |
log.Info('vmware-mount did not exist or was unable to be run')
|
|
|
189 |
|
|
|
190 |
def _killVMwareProcesses(self, upgrade):
|
|
|
191 |
self.RunCommand('killall',
|
|
|
192 |
'vmplayer', 'vmware', 'vmware-tray', 'vmware-unity-helper',
|
|
|
193 |
'vmware-enter-serial', 'vmrest',
|
|
|
194 |
'vmnet-natd', 'vmnet-dhcpd',
|
|
|
195 |
'vmware-netcfg', 'vmnet-netifup', 'vmnet-bridge',
|
|
|
196 |
ignoreErrors=True, noLogging=True);
|
|
|
197 |
# Don't kill fuseUI so we can keep the virtual disk mounted for upgrade.
|
|
|
198 |
if not upgrade:
|
|
|
199 |
self.RunCommand('killall', 'vmware-fuseUI',
|
|
|
200 |
ignoreErrors=True, noLogging=True);
|
|
|
201 |
|
|
|
202 |
|
|
|
203 |
def PreInstall(self, old, new, upgrade):
|
|
|
204 |
# Make sure we kill all processes that should not be running!
|
|
|
205 |
self._killVMwareProcesses(upgrade)
|
|
|
206 |
|
|
|
207 |
def PreUninstall(self, old, new, upgrade):
|
|
|
208 |
self._deconfigureVMStreamingHandlers()
|
|
|
209 |
|
|
|
210 |
# Deconfigure Services
|
|
|
211 |
inits = self.LoadInclude('initscript')
|
|
|
212 |
|
|
|
213 |
# This must be the last thing done since the actions above may
|
|
|
214 |
# depend on it.
|
|
|
215 |
for key in list(SETTINGS.keys()):
|
|
|
216 |
self.RunCommand(CONFIG, '-d', key)
|
|
|
217 |
|
|
|
218 |
# Make sure we kill all processes that should not be running!
|
|
|
219 |
self._killVMwareProcesses(upgrade)
|
|
|
220 |
|
|
|
221 |
def PostUninstall(self, old, new, upgrade):
|
|
|
222 |
VMNETS = 255
|
|
|
223 |
|
|
|
224 |
keepConfig = self.GetConfig('keepConfigOnUninstall', component='vmware-installer')
|
|
|
225 |
if not upgrade and keepConfig != 'yes':
|
|
|
226 |
(ETCDIR/'networking').remove(ignore_errors=True)
|
|
|
227 |
|
|
|
228 |
# Clean up bits and pieces that networking has left behind.
|
|
|
229 |
for f in ETCDIR.walkfiles('networking.bak*'):
|
|
|
230 |
f.remove(ignore_errors=True)
|
|
|
231 |
|
|
|
232 |
for i in range(0, VMNETS + 1):
|
|
|
233 |
vnet = ETCDIR/('vmnet%d' % i)
|
|
|
234 |
vnet.exists() and vnet.rmtree(ignore_errors=True)
|
|
|
235 |
|
|
|
236 |
# Remove link to deprecated uninstall mechanism
|
|
|
237 |
self.RemoveUninstallLinks()
|
|
|
238 |
|
|
|
239 |
def GetConfigValue(self, key):
|
|
|
240 |
ret = self.RunCommand(CONFIG, '-g', key)
|
|
|
241 |
if ret and ret.stdout:
|
|
|
242 |
return ret.stdout.strip()
|
|
|
243 |
return None
|
|
|
244 |
|
|
|
245 |
def PostInstall(self, old, new, upgrade):
|
|
|
246 |
# Used by VIX to locate correct provider.
|
|
|
247 |
SETTINGS['player.product.version'] = new
|
|
|
248 |
SETTINGS['vix.config.version'] = 1
|
|
|
249 |
SETTINGS['product.buildNumber'] = self.GetManifestValue('buildNumber', '0')
|
|
|
250 |
|
|
|
251 |
if self.GetConfigValue('telemetryUUID') == None and self.GetConfigValue('hostUUID') == None:
|
|
|
252 |
# https://docs.python.org/2/library/uuid.html
|
|
|
253 |
SETTINGS['telemetryUUID'] = uuid.uuid1()
|
|
|
254 |
elif self.GetConfigValue('hostUUID') != None:
|
|
|
255 |
SETTINGS['telemetryUUID'] = self.GetConfigValue('hostUUID')
|
|
|
256 |
self.RunCommand(CONFIG, '-d', 'hostUUID')
|
|
|
257 |
else:
|
|
|
258 |
pass
|
|
|
259 |
|
|
|
260 |
# only set the value in the config file when it has changed or wasn't there before:
|
|
|
261 |
if self.GetAnswer('softwareUpdateEnabled.deferred') == 'yes':
|
|
|
262 |
SETTINGS['installerDefaults.autoSoftwareUpdateEnabled'] = 'none'
|
|
|
263 |
SETTINGS['installerDefaults.autoSoftwareUpdateEnabled.epoch'] = '%s' % self.randomNumber()
|
|
|
264 |
self.DelConfig('softwareUpdateEnabled')
|
|
|
265 |
self.DelConfig('softwareUpdateEnabled.deferred')
|
|
|
266 |
else:
|
|
|
267 |
softwareUpdateEnabled = self.GetConfigValue('installerDefaults.autoSoftwareUpdateEnabled')
|
|
|
268 |
if softwareUpdateEnabled == None or softwareUpdateEnabled != self.GetAnswer('softwareUpdateEnabled'):
|
|
|
269 |
SETTINGS['installerDefaults.autoSoftwareUpdateEnabled'] = self.GetAnswer('softwareUpdateEnabled')
|
|
|
270 |
SETTINGS['installerDefaults.autoSoftwareUpdateEnabled.epoch'] = '%s' % self.randomNumber()
|
|
|
271 |
|
|
|
272 |
# only set the value in the config file when it has changed or wasn't there before:
|
|
|
273 |
dataCollectionEnabled = self.GetConfigValue('installerDefaults.dataCollectionEnabled')
|
|
|
274 |
if self.GetAnswer('dataCollectionEnabled.deferred') == 'yes':
|
|
|
275 |
if dataCollectionEnabled == None:
|
|
|
276 |
SETTINGS['installerDefaults.dataCollectionEnabled'] = self.GetAnswer('dataCollectionEnabled')
|
|
|
277 |
SETTINGS['installerDefaults.dataCollectionEnabled.epoch'] = '%s' % self.randomNumber()
|
|
|
278 |
|
|
|
279 |
SETTINGS['installerDefaults.dataCollectionEnabled.initialized'] = 'none'
|
|
|
280 |
self.DelConfig('dataCollectionEnabled.deferred')
|
|
|
281 |
else:
|
|
|
282 |
if dataCollectionEnabled == None or dataCollectionEnabled != self.GetAnswer('dataCollectionEnabled'):
|
|
|
283 |
SETTINGS['installerDefaults.dataCollectionEnabled'] = self.GetAnswer('dataCollectionEnabled')
|
|
|
284 |
SETTINGS['installerDefaults.dataCollectionEnabled.epoch'] = '%s' % self.randomNumber()
|
|
|
285 |
|
|
|
286 |
SETTINGS['installerDefaults.simplifiedUI'] = self.GetAnswer('simplifiedUI')
|
|
|
287 |
|
|
|
288 |
SETTINGS['installerDefaults.supportURL'] = self.GetAnswer('supportURL')
|
|
|
289 |
SETTINGS['componentDownload.server'] = self.GetAnswer('softwareUpdateURL')
|
|
|
290 |
|
|
|
291 |
# Component Download should always be set to yes.
|
|
|
292 |
# XXX: If this ever has a chance to change in the future, set the .epoch to generate
|
|
|
293 |
# a random number like the above two settings.
|
|
|
294 |
SETTINGS['installerDefaults.componentDownloadEnabled'] = 'yes'
|
|
|
295 |
|
|
|
296 |
SETTINGS['installerDefaults.transferVersion'] = 1
|
|
|
297 |
|
|
|
298 |
for key, val in list(SETTINGS.items()):
|
|
|
299 |
if val != "" and val != None:
|
|
|
300 |
self.RunCommand(CONFIG, '-s', key, val)
|
|
|
301 |
# when the setting is empty or unset remove it from the config file:
|
|
|
302 |
else:
|
|
|
303 |
self.RunCommand(CONFIG, '-d', key)
|
|
|
304 |
|
|
|
305 |
configPath = path(SYSCONFDIR/'vmware/config')
|
|
|
306 |
configPath.chmod(0o644)
|
|
|
307 |
|
|
|
308 |
launcher = DATADIR/'applications/vmware-player.desktop'
|
|
|
309 |
binary = BINDIR/'vmplayer'
|
|
|
310 |
self.RunCommand('sed', '-e', 's,@@BINARY@@,%s,g' % binary, '-i', launcher)
|
|
|
311 |
|
|
|
312 |
self._configureVMStreamingHandlers()
|
|
|
313 |
|
|
|
314 |
updateModule = self.LoadInclude('update')
|
|
|
315 |
updateModule.UpdateIconCache(self, DATADIR)
|
|
|
316 |
updateModule.UpdateMIME(self, DATADIR)
|
|
|
317 |
|
|
|
318 |
# According to ThinPrint's setup.sh, SELinux systems may have issue with
|
|
|
319 |
# thnucups context. For now, ignore failures since they're harmless.
|
|
|
320 |
restcon = path(self._which('restorecon'))
|
|
|
321 |
if not restcon:
|
|
|
322 |
restcon = path('/sbin/restorecon')
|
|
|
323 |
if restcon.exists():
|
|
|
324 |
self.RunCommand('restorecon', CUPSLIBDIR/'filter/thnucups', ignoreErrors=True)
|
|
|
325 |
|
|
|
326 |
# restart cups
|
|
|
327 |
script = INITSCRIPTDIR/'cups'
|
|
|
328 |
if INITSCRIPTDIR and script.exists():
|
|
|
329 |
self.RunCommand(script, 'restart', ignoreErrors=True)
|
|
|
330 |
|
|
|
331 |
# Set up our service scripts.
|
|
|
332 |
inits = self.LoadInclude('initscript')
|
|
|
333 |
|
|
|
334 |
# We killed all running vmware processes before installing. Restart all our
|
|
|
335 |
# init scripts
|
|
|
336 |
for scriptName in ['vmware']:
|
|
|
337 |
script = INITSCRIPTDIR/scriptName
|
|
|
338 |
if INITSCRIPTDIR and script.exists():
|
|
|
339 |
self.RunCommand(script, 'stop', ignoreErrors=True)
|
|
|
340 |
self.RunCommand(script, 'start', ignoreErrors=True)
|
|
|
341 |
|
|
|
342 |
# Add link to deprecated uninstall mechanism to catch downgrades
|
|
|
343 |
self.AddUninstallLinks()
|
|
|
344 |
|
|
|
345 |
def _AddLineToFile(self, fil, text, addToEnd=True):
|
|
|
346 |
"""
|
|
|
347 |
Add a line/consecutive lines to a file, surrounded by the VMware Sentinel.
|
|
|
348 |
### This method only adds a single line to a file ###
|
|
|
349 |
### You cannot make more than one modification per file ###
|
|
|
350 |
|
|
|
351 |
@param fil: The file name. Either a string or path object
|
|
|
352 |
@param text: The text to add. This function appends a \n
|
|
|
353 |
@param addToEnd: Add to the end of the file? If false, to the beginning.
|
|
|
354 |
|
|
|
355 |
@return: True on successful modification, False if the file did not exist.
|
|
|
356 |
"""
|
|
|
357 |
fil = path(fil) # Make sure it's a path object
|
|
|
358 |
if fil.exists():
|
|
|
359 |
txt = fil.text()
|
|
|
360 |
# Modify the text
|
|
|
361 |
if addToEnd:
|
|
|
362 |
txt = ''.join([txt, vmwareSentinel, text, '\n', vmwareSentinel])
|
|
|
363 |
else:
|
|
|
364 |
txt = ''.join([vmwareSentinel, text, '\n', vmwareSentinel, txt])
|
|
|
365 |
# Write it back to the file
|
|
|
366 |
fil.write_text(txt)
|
|
|
367 |
return True
|
|
|
368 |
else:
|
|
|
369 |
log.Info('Attempted to modify file %s, does not exist.' % fil)
|
|
|
370 |
return False
|
|
|
371 |
|
|
|
372 |
def _RemoveLineFromFile(self, fil):
|
|
|
373 |
"""
|
|
|
374 |
Remove a line bracketed by the VMware Sentinel
|
|
|
375 |
|
|
|
376 |
@param fil: The file name. Either a string or path object
|
|
|
377 |
|
|
|
378 |
@return: True on successful modification, False if the file did not exist.
|
|
|
379 |
"""
|
|
|
380 |
fil = path(fil) # Make sure it's a path object
|
|
|
381 |
if fil.exists():
|
|
|
382 |
txt = fil.text()
|
|
|
383 |
m = re.sub(vmwareSentinel + '.*\n' + vmwareSentinel,
|
|
|
384 |
'', txt, re.DOTALL)
|
|
|
385 |
fil.write_text(m)
|
|
|
386 |
return True
|
|
|
387 |
else:
|
|
|
388 |
log.Info('Attempted to modify file %s, does not exist.' % fil)
|
|
|
389 |
return False
|
|
|
390 |
|
|
|
391 |
def _configurePrelink(self, enable):
|
|
|
392 |
"""
|
|
|
393 |
Configures prelinking by adding appLoader exclusion.
|
|
|
394 |
|
|
|
395 |
@param enable: True if to add exclusion, False if to remove it.
|
|
|
396 |
"""
|
|
|
397 |
prelink = path('/etc/prelink.conf')
|
|
|
398 |
|
|
|
399 |
if prelink.exists():
|
|
|
400 |
# XXX: It would probably be good to refactor this into some
|
|
|
401 |
# sort of helper function that can add and remove lines from
|
|
|
402 |
# files.
|
|
|
403 |
skipPrelink = [ '# appLoader will segfault if prelinked.',
|
|
|
404 |
'-b %s' % (DEST/'bin/appLoader') ]
|
|
|
405 |
|
|
|
406 |
lines = prelink.lines(encoding='utf-8', retain=False)
|
|
|
407 |
|
|
|
408 |
# Strip whitespace from lines so that trailing whitespace
|
|
|
409 |
# won't impact matching lines.
|
|
|
410 |
lines = [line.strip() for line in lines]
|
|
|
411 |
|
|
|
412 |
if enable:
|
|
|
413 |
if skipPrelink[-1] not in lines:
|
|
|
414 |
lines += skipPrelink
|
|
|
415 |
log.Info('Added appLoader prelink exclusion to %s.', prelink)
|
|
|
416 |
else:
|
|
|
417 |
log.Warn('appLoader skip prelinking already present.')
|
|
|
418 |
else:
|
|
|
419 |
found = False
|
|
|
420 |
|
|
|
421 |
for line in skipPrelink:
|
|
|
422 |
if line in lines:
|
|
|
423 |
found = True
|
|
|
424 |
lines.remove(line)
|
|
|
425 |
|
|
|
426 |
if found:
|
|
|
427 |
log.Info('Removed appLoader prelink exclusion from %s.', prelink)
|
|
|
428 |
|
|
|
429 |
# XXX: This can technically fail. Actually, there are a
|
|
|
430 |
# whole host of things that can fail in this function. On
|
|
|
431 |
# one hand we would like to catch errors and correct them
|
|
|
432 |
# while being fairly resilient at installation time.
|
|
|
433 |
#
|
|
|
434 |
# One option might be to have a @failok decoration for things
|
|
|
435 |
# that can fail loudly for internal builds but do not cause
|
|
|
436 |
# installations to fail in release builds.
|
|
|
437 |
prelink.write_lines(lines, encoding='utf-8')
|
|
|
438 |
else:
|
|
|
439 |
log.Info('Prelink not present, skipping configuration.')
|
|
|
440 |
|
|
|
441 |
def _isGConfUsable(self):
|
|
|
442 |
""" Return True if GConf settings can be configured, otherwise False """
|
|
|
443 |
return self.RunCommand('gconftool-2', '--help', ignoreErrors=True, noLogging=True).retCode == 0
|
|
|
444 |
|
|
|
445 |
def _configureVMStreamingHandlers(self):
|
|
|
446 |
""" Configures handlers for vm:// and vms:// used for VM streaming """
|
|
|
447 |
def configureGConf():
|
|
|
448 |
# If Player isn't being installed as a product then
|
|
|
449 |
# Workstation must be.
|
|
|
450 |
if self.isProduct:
|
|
|
451 |
target = BINDIR/'vmplayer'
|
|
|
452 |
else:
|
|
|
453 |
target = BINDIR/'vmware'
|
|
|
454 |
|
|
|
455 |
target = self._escape(target)
|
|
|
456 |
|
|
|
457 |
settings = (
|
|
|
458 |
('string', '/desktop/gnome/url-handlers/%s/command', '%s "%%s"' % target),
|
|
|
459 |
('bool', '/desktop/gnome/url-handlers/%s/enabled', 'true'),
|
|
|
460 |
('bool', '/desktop/gnome/url-handlers/%s/needs_terminal', 'false'),
|
|
|
461 |
)
|
|
|
462 |
|
|
|
463 |
for handler in ('vm', 'vms'):
|
|
|
464 |
for gconfType, key, value in settings:
|
|
|
465 |
key = key % handler
|
|
|
466 |
self.RunCommand('gconftool-2', '--direct', '--config-source', GCONF_DEFAULTS,
|
|
|
467 |
'--type', gconfType, '--set', key, value)
|
|
|
468 |
|
|
|
469 |
# Instruct all gconfd daemons to reload.
|
|
|
470 |
self.RunCommand('killall', '-HUP', 'gconfd-2')
|
|
|
471 |
|
|
|
472 |
self._isGConfUsable() and configureGConf()
|
|
|
473 |
|
|
|
474 |
def _deconfigureVMStreamingHandlers(self):
|
|
|
475 |
""" Deconfigures the handlers for vm:// and vms:// used for VM streaming"""
|
|
|
476 |
def deconfigureGConf():
|
|
|
477 |
for handler in ('vm', 'vms'):
|
|
|
478 |
self.RunCommand('gconftool-2', '--direct', '--config-source', GCONF_DEFAULTS,
|
|
|
479 |
'--recursive-unset', '/desktop/gnome/url-handlers/%s' % handler)
|
|
|
480 |
|
|
|
481 |
# Instruct all gconfd daemons to reload.
|
|
|
482 |
self.RunCommand('killall', '-HUP', 'gconfd-2')
|
|
|
483 |
|
|
|
484 |
self._isGConfUsable() and deconfigureGConf()
|
|
|
485 |
|
|
|
486 |
def _escape(self, string):
|
|
|
487 |
""" Escapes a string for use in a shell context """
|
|
|
488 |
# XXX: Borrowed from util/shell.py: Escape. Break that into a component-side
|
|
|
489 |
# include file and remove this method once that's done.
|
|
|
490 |
return "'%s'" % string.replace("'", '"\'"')
|
|
|
491 |
|
|
|
492 |
# XXX: Remove this code duplication
|
|
|
493 |
# XXX: Duplicated with vmware-vix.py, but until the
|
|
|
494 |
# infrastructure exists to include these two functions properly,
|
|
|
495 |
# it must be duplicated.
|
|
|
496 |
def AddUninstallLinks(self, suffix=None):
|
|
|
497 |
extension = ''
|
|
|
498 |
if suffix:
|
|
|
499 |
extension = '-%s' % suffix
|
|
|
500 |
|
|
|
501 |
uninstaller = 'vmware-uninstall%s' % extension
|
|
|
502 |
|
|
|
503 |
links = [BINDIR/uninstaller,
|
|
|
504 |
SYSCONFDIR/('vmware%s/installer.sh' % extension)]
|
|
|
505 |
|
|
|
506 |
# Remove the old file/links
|
|
|
507 |
for link in links:
|
|
|
508 |
try:
|
|
|
509 |
link.remove()
|
|
|
510 |
except OSError:
|
|
|
511 |
# We don't care if it already doesn't exist.
|
|
|
512 |
pass
|
|
|
513 |
|
|
|
514 |
# Create installer hooks. symlink expects a string and can't convert
|
|
|
515 |
# a ComponentDestination object. Convert them manually.
|
|
|
516 |
try:
|
|
|
517 |
BINDIR.makedirs()
|
|
|
518 |
except OSError:
|
|
|
519 |
# It's okay if it already exists.
|
|
|
520 |
pass
|
|
|
521 |
|
|
|
522 |
bin = LIBDIR/'vmware-installer/3.0.0'
|
|
|
523 |
bin.perm = BINARY
|
|
|
524 |
path(bin/'vmware-uninstall-downgrade').symlink(str(link))
|
|
|
525 |
|
|
|
526 |
locationsFile = SYSCONFDIR/('vmware%s/locations' % extension)
|
|
|
527 |
locationsFile.write_text('# Empty locations file to catch downgrade\n'
|
|
|
528 |
'# to WS 6.0\n')
|
|
|
529 |
|
|
|
530 |
def RemoveUninstallLinks(self, suffix=None):
|
|
|
531 |
extension = ''
|
|
|
532 |
if suffix:
|
|
|
533 |
extension = '-%s' % suffix
|
|
|
534 |
|
|
|
535 |
uninstaller = 'vmware-uninstall%s' % extension
|
|
|
536 |
|
|
|
537 |
links = [BINDIR/uninstaller,
|
|
|
538 |
SYSCONFDIR/('vmware%s/installer.sh' % extension)]
|
|
|
539 |
|
|
|
540 |
for link in links:
|
|
|
541 |
try:
|
|
|
542 |
link.remove()
|
|
|
543 |
except OSError:
|
|
|
544 |
# We don't care if it already doesn't exist.
|
|
|
545 |
pass
|
|
|
546 |
|
|
|
547 |
locationsFile = SYSCONFDIR/('vmware%s/locations' % extension)
|
|
|
548 |
if locationsFile.exists():
|
|
|
549 |
locationsFile.remove()
|
|
|
550 |
|
|
|
551 |
def SystemType(self):
|
|
|
552 |
"""
|
|
|
553 |
Returns a tuple of results for the system found.
|
|
|
554 |
(sysName = 'Ubuntu', 'RHEL', 'SLE', 'Fedora', or None
|
|
|
555 |
sysVersion = The system version or None
|
|
|
556 |
sysExtra) = Desktop or Server (RHEL, SLE) or None
|
|
|
557 |
"""
|
|
|
558 |
# We must scan through these in order. SuSE systems for example
|
|
|
559 |
# have a /etc/lsb-release file AND a /etc/SuSE-release file. The
|
|
|
560 |
# latter contains the right information.
|
|
|
561 |
possibles = ('/etc/lsb-release', '/etc/redhat-release',
|
|
|
562 |
'/etc/SuSE-release', '/etc/fedora-release')
|
|
|
563 |
|
|
|
564 |
txt = ''
|
|
|
565 |
for p in possibles:
|
|
|
566 |
fil = path(p)
|
|
|
567 |
if fil.exists():
|
|
|
568 |
txt = fil.text()
|
|
|
569 |
|
|
|
570 |
if txt == '':
|
|
|
571 |
log.Warn('No release file found...')
|
|
|
572 |
return (None, None, None)
|
|
|
573 |
|
|
|
574 |
sysName = ''
|
|
|
575 |
sysVersion = ''
|
|
|
576 |
sysExtra = ''
|
|
|
577 |
|
|
|
578 |
# All sorts of things can go wrong with this... Let's not die if it does.
|
|
|
579 |
try:
|
|
|
580 |
if re.findall('DISTRIB_ID=Ubuntu', txt):
|
|
|
581 |
sysName = 'Ubuntu'
|
|
|
582 |
mt = re.findall('DISTRIB_RELEASE=\d+\.\d+', txt)
|
|
|
583 |
sysVersion = re.sub('DISTRIB_RELEASE=', '', mt[0])
|
|
|
584 |
|
|
|
585 |
elif re.findall('Red Hat Enterprise Linux', txt):
|
|
|
586 |
sysName = 'RHEL'
|
|
|
587 |
mt = re.findall('elease \d+\.\d+', txt)
|
|
|
588 |
sysVersion = re.sub('elease ', '', mt[0])
|
|
|
589 |
mt = re.findall('Enterprise Linux \w+', txt)
|
|
|
590 |
sysExtra = re.sub('Enterprise Linux ', '', mt[0])
|
|
|
591 |
|
|
|
592 |
elif re.findall('SUSE Linux Enterprise', txt):
|
|
|
593 |
sysName = 'SLE'
|
|
|
594 |
mt = re.findall('VERSION = \d+', txt)
|
|
|
595 |
sysVersion = re.sub('VERSION = ', '', mt[0])
|
|
|
596 |
mt = re.findall('Enterprise \w+ ', txt)
|
|
|
597 |
sysExtra = re.sub('Enterprise ', '', mt[0])
|
|
|
598 |
|
|
|
599 |
elif re.findall('Fedora release', txt):
|
|
|
600 |
sysName = 'Fedora'
|
|
|
601 |
mt = re.findall('Fedora release \d+', txt)
|
|
|
602 |
sysVersion = re.sub('Fedora release ', '', mt[0])
|
|
|
603 |
|
|
|
604 |
except Exception:
|
|
|
605 |
log.Warn('Could not determine system type... Exception caught.')
|
|
|
606 |
log.Warn('Found text reads:')
|
|
|
607 |
log.Warn(txt)
|
|
|
608 |
pass
|
|
|
609 |
|
|
|
610 |
return (sysName, sysVersion, sysExtra)
|
|
|
611 |
|
|
|
612 |
def _which(self, program):
|
|
|
613 |
"""
|
|
|
614 |
Gets the PATH environment variable and checks for program
|
|
|
615 |
in order.
|
|
|
616 |
|
|
|
617 |
@param program: Executable to search for
|
|
|
618 |
@returns: Full path if found and executable, None otherwise
|
|
|
619 |
"""
|
|
|
620 |
systemPath = ENV['PATH']
|
|
|
621 |
paths = systemPath.split(':')
|
|
|
622 |
for p in paths:
|
|
|
623 |
fullPath = path(p)/program
|
|
|
624 |
if fullPath.isexe():
|
|
|
625 |
return str(fullPath) # Return a string, not a path object
|
|
|
626 |
|
|
|
627 |
return None
|
|
|
628 |
|
|
|
629 |
def randomNumber(self):
|
|
|
630 |
return randint(1000000000, 9999999999)
|