Subversion Repositories configs

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
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)