209 |
- |
1 |
"""
|
|
|
2 |
Copyright 2008-2020 VMware, Inc. All rights reserved. -- VMware Confidential
|
|
|
3 |
|
|
|
4 |
VMware Workstation component installer.
|
|
|
5 |
"""
|
|
|
6 |
DEST = LIBDIR/'vmware'
|
|
|
7 |
conf = DEST/'setup/vmware-config'
|
|
|
8 |
LICENSETOOL=BINDIR/'vmware-license-enter.sh'
|
|
|
9 |
|
|
|
10 |
PRODUCT = 'VMware Workstation'
|
|
|
11 |
LICENSEVERSION = '16.0'
|
|
|
12 |
|
|
|
13 |
LIMITSFILE = Destination('/etc/security/limits.conf')
|
|
|
14 |
NOFILE_MINIMUM = 4096
|
|
|
15 |
PAMLOGINFILE = Destination('/etc/pam.d/login')
|
|
|
16 |
|
|
|
17 |
vmwareSentinel = '# Automatically generated by the VMware Installer - DO NOT REMOVE\n'
|
|
|
18 |
pamLoginLine = 'session required pam_limits.so\n'
|
|
|
19 |
|
|
|
20 |
class Workstation(Installer):
|
|
|
21 |
def PreTransactionInstall(self, old, new, upgrade):
|
|
|
22 |
# Include update module
|
|
|
23 |
globals()['update'] = self.LoadInclude('update')
|
|
|
24 |
|
|
|
25 |
def PreTransactionUninstall(self, old, new, upgrade):
|
|
|
26 |
# Include update module
|
|
|
27 |
globals()['update'] = self.LoadInclude('update')
|
|
|
28 |
|
|
|
29 |
def InitializeQuestions(self, old, new, upgrade):
|
|
|
30 |
|
|
|
31 |
self.AddQuestion('TextEntry',
|
|
|
32 |
key='serialNumber',
|
|
|
33 |
text='',
|
|
|
34 |
header='Enter license key.',
|
|
|
35 |
footer='(optional) You can enter this information later.',
|
|
|
36 |
default='',
|
|
|
37 |
required=True,
|
|
|
38 |
level='REGULAR',
|
|
|
39 |
deferrable=True)
|
|
|
40 |
|
|
|
41 |
# NOFILE hardlimit
|
|
|
42 |
nofileHL = self.GetAnswer('nofileHardLimit')
|
|
|
43 |
if nofileHL:
|
|
|
44 |
qlevel = 'CUSTOM'
|
|
|
45 |
else:
|
|
|
46 |
qlevel = 'REGULAR'
|
|
|
47 |
try:
|
|
|
48 |
self.hardLimit = self.RunCommand('/bin/sh', '-c', 'ulimit -H -n').stdout
|
|
|
49 |
self.hardLimit = self.hardLimit.strip()
|
|
|
50 |
self.hardLimit = int(self.hardLimit)
|
|
|
51 |
if self.hardLimit < NOFILE_MINIMUM:
|
|
|
52 |
log.Debug('Hard limit is %d, adding question.', self.hardLimit)
|
|
|
53 |
self.AddQuestion('NumericEntry',
|
|
|
54 |
key='nofileHardLimit',
|
|
|
55 |
text='Insufficient file descriptors can cause virtual machines to '
|
|
|
56 |
'crash when using snapshots. The installer has detected that '
|
|
|
57 |
'your hard limit for open files is %d, which is lower than VMware '
|
|
|
58 |
'Workstation may require. Please enter a new limit.' % self.hardLimit,
|
|
|
59 |
min=1024,
|
|
|
60 |
max=65536,
|
|
|
61 |
required=False, default=NOFILE_MINIMUM, level=qlevel)
|
|
|
62 |
except ValueError:
|
|
|
63 |
# Log this, but move on. Not a fatal error.
|
|
|
64 |
log.Error('Hard limit returned non-integer value: %s', self.hardLimit)
|
|
|
65 |
|
|
|
66 |
def InitializeInstall(self, old, new, upgrade):
|
|
|
67 |
self.AddTarget('File', 'bin/*', BINDIR)
|
|
|
68 |
self.AddTarget('File', 'man/*', MANDIR)
|
|
|
69 |
|
|
|
70 |
self.AddTarget('File', 'share/icons/*', DATADIR/'icons')
|
|
|
71 |
|
|
|
72 |
if self.GetConfig('installShortcuts', component='vmware-installer') != 'no':
|
|
|
73 |
self.AddTarget('File', 'share/applications/*', DATADIR/'applications')
|
|
|
74 |
self.AddTarget('File', 'share/appdata/*', DATADIR/'appdata')
|
|
|
75 |
|
|
|
76 |
self.AddTarget('File', 'lib/*', DEST)
|
|
|
77 |
self.AddTarget('File', 'doc/*', DOCDIR/'vmware-workstation')
|
|
|
78 |
self.AddTarget('File', 'etc/*', SYSCONFDIR)
|
|
|
79 |
|
|
|
80 |
# Symlink all binaries to appLoader.
|
|
|
81 |
for i in ('vmware', 'vmware-tray'):
|
|
|
82 |
self.AddTarget('Link', DEST/'bin/appLoader', DEST/'bin'/i)
|
|
|
83 |
|
|
|
84 |
self.SetPermission(DEST/'bin/*', BINARY)
|
|
|
85 |
|
|
|
86 |
# Ubuntu 10.04 requires additional .desktop files in /usr/local/share/applications
|
|
|
87 |
# If GNOME or Ubuntu is going this way, rather than just add these links for
|
|
|
88 |
# Ubuntu 10.04, always add them for future-proofing.
|
|
|
89 |
|
|
|
90 |
# Some linux distributions use yet another standard for DE metadata, requiring
|
|
|
91 |
# .appdata.xml files in order for WS to be usable via the DE app launcher.
|
|
|
92 |
# Like the .desktop files, just install them along with the rest for futureproofing.
|
|
|
93 |
if self.GetConfig('installShortcuts', component='vmware-installer') != 'no':
|
|
|
94 |
self.AddTarget('Link', DATADIR/'applications/vmware-workstation.desktop',
|
|
|
95 |
PREFIX/'local/share/applications/vmware-workstation.desktop')
|
|
|
96 |
self.AddTarget('Link', DATADIR/'appdata/vmware-workstation.appdata.xml',
|
|
|
97 |
PREFIX/'local/share/appdata/vmware-workstation.appdata.xml')
|
|
|
98 |
|
|
|
99 |
|
|
|
100 |
def PreUninstall(self, old, new, upgrade):
|
|
|
101 |
self.RunCommand(conf, '-d', 'product.version')
|
|
|
102 |
self.RunCommand(conf, '-d', 'workstation.product.version')
|
|
|
103 |
self.RunCommand(conf, '-d', 'vix.config.version')
|
|
|
104 |
|
|
|
105 |
# Stop our init script for uninstallation
|
|
|
106 |
script = INITSCRIPTDIR/'vmware'
|
|
|
107 |
if INITSCRIPTDIR and script.exists():
|
|
|
108 |
self.RunCommand(script, 'stop', ignoreErrors=True)
|
|
|
109 |
|
|
|
110 |
|
|
|
111 |
def PostInstall(self, old, new, upgrade):
|
|
|
112 |
# Used by VIX to locate correct provider.
|
|
|
113 |
self.RunCommand(conf, '-s', 'product.version', self.GetManifestValue('version'))
|
|
|
114 |
self.RunCommand(conf, '-s', 'workstation.product.version', self.GetManifestValue('version'))
|
|
|
115 |
self.RunCommand(conf, '-s', 'product.name', PRODUCT)
|
|
|
116 |
self.RunCommand(conf, '-s', 'vix.config.version', 1)
|
|
|
117 |
|
|
|
118 |
if self.GetAnswer('eula.deferred', component='vmware-workstation') == 'yes':
|
|
|
119 |
self.RunCommand(conf, '-s', 'acceptEULA', 'none')
|
|
|
120 |
self.DelConfig('eula.deferred')
|
|
|
121 |
else:
|
|
|
122 |
self.RunCommand(conf, '-s', 'acceptEULA', 'yes')
|
|
|
123 |
|
|
|
124 |
if self.GetConfig('installShortcuts', component='vmware-installer') != 'no':
|
|
|
125 |
launcher = DATADIR/'applications/vmware-workstation.desktop'
|
|
|
126 |
binary = BINDIR/'vmware'
|
|
|
127 |
self.RunCommand('sed', '-e', 's,@@BINARY@@,%s,g' % binary, '-i', launcher)
|
|
|
128 |
|
|
|
129 |
update.UpdateIconCache(self, DATADIR)
|
|
|
130 |
update.UpdateMIME(self, DATADIR)
|
|
|
131 |
|
|
|
132 |
# Update hard limit for the number of open files.
|
|
|
133 |
self._ModifyVMwareLimitsConf(LIMITSFILE)
|
|
|
134 |
|
|
|
135 |
# We killed all running vmware processes before installing,
|
|
|
136 |
# so be sure to restart them.
|
|
|
137 |
script = INITSCRIPTDIR/'vmware'
|
|
|
138 |
if INITSCRIPTDIR and script.exists():
|
|
|
139 |
self.RunCommand(script, 'stop', ignoreErrors=True)
|
|
|
140 |
self.RunCommand(script, 'start')
|
|
|
141 |
|
|
|
142 |
# serial entered by user:
|
|
|
143 |
serialNumber = self.GetAnswer('serialNumber')
|
|
|
144 |
if serialNumber:
|
|
|
145 |
self.RunCommand(LICENSETOOL, serialNumber, PRODUCT, LICENSEVERSION)
|
|
|
146 |
|
|
|
147 |
def PostUninstall(self, old, new, upgrade):
|
|
|
148 |
# Reset hard limit for the number of open files on the system.
|
|
|
149 |
self._ClearVMwareLimitsConf(LIMITSFILE, restoreEntry=True)
|
|
|
150 |
|
|
|
151 |
# Empty out the Winger cache
|
|
|
152 |
try:
|
|
|
153 |
path('/var/lib/vmware/compcache').rmtree()
|
|
|
154 |
except OSError:
|
|
|
155 |
pass # Okay if the directory was already removed by the user
|
|
|
156 |
|
|
|
157 |
# This seems a little counterintuitive, but we killed all running
|
|
|
158 |
# vmware processes before uninstalling Workstation. At this point
|
|
|
159 |
# Player is still installed though, so we want to be
|
|
|
160 |
# sure to restart the services for Player.
|
|
|
161 |
script = INITSCRIPTDIR/'vmware'
|
|
|
162 |
if INITSCRIPTDIR and script.exists():
|
|
|
163 |
self.RunCommand(script, 'stop', ignoreErrors=True)
|
|
|
164 |
self.RunCommand(script, 'start')
|
|
|
165 |
|
|
|
166 |
def _ClearVMwareLimitsConf(self, limitsFile, restoreEntry=False):
|
|
|
167 |
# Check if our section already exists at the beginning
|
|
|
168 |
# of the file. If it does clear it.
|
|
|
169 |
log.Debug('nofile: Clearing limits file')
|
|
|
170 |
text = limitsFile.text()
|
|
|
171 |
newtext = re.sub(vmwareSentinel + '.*\n' + vmwareSentinel,
|
|
|
172 |
'', text, re.DOTALL)
|
|
|
173 |
limitsFile.write_text(newtext)
|
|
|
174 |
|
|
|
175 |
# If we're uninstalling and restoring the old file, add the
|
|
|
176 |
# old limit back since we wiped it on install.
|
|
|
177 |
if restoreEntry:
|
|
|
178 |
oldLimit = self.GetConfig('oldNofileHardLimit')
|
|
|
179 |
if oldLimit:
|
|
|
180 |
log.Debug('nofile: Restoring old nofile hard limit.')
|
|
|
181 |
self._WriteLimitsConfEntry(limitsFile, '*\t\thard\tnofile\t\t%s\n' % oldLimit)
|
|
|
182 |
# And remove the entry from our config file.
|
|
|
183 |
self.DelConfig('oldNofileHardLimit')
|
|
|
184 |
|
|
|
185 |
self._ClearPamD(PAMLOGINFILE)
|
|
|
186 |
|
|
|
187 |
def _RemoveMarkedLineFromFile(self, file_, entry):
|
|
|
188 |
""" Remove a line wrapped in vmwareSentinel from a given file """
|
|
|
189 |
text = file_.text()
|
|
|
190 |
searchText = vmwareSentinel + entry + vmwareSentinel
|
|
|
191 |
matches = re.findall(searchText, text, re.DOTALL)
|
|
|
192 |
if not matches:
|
|
|
193 |
return False
|
|
|
194 |
newtext = re.sub(searchText, '', text, re.DOTALL)
|
|
|
195 |
file_.write_text(newtext)
|
|
|
196 |
return True
|
|
|
197 |
|
|
|
198 |
def _WriteLimitsConfEntry(self, limitsFile, entry):
|
|
|
199 |
# This function assumes that the file has already been cleared and prepped for
|
|
|
200 |
# us to write an entry.
|
|
|
201 |
text = limitsFile.text()
|
|
|
202 |
|
|
|
203 |
# See if a limits line already exists for nofile
|
|
|
204 |
# Remove comments.
|
|
|
205 |
justText = re.sub('#.*\n', '', text)
|
|
|
206 |
|
|
|
207 |
matches = re.findall('^\*.+hard.+nofile.+\d+', justText, re.MULTILINE)
|
|
|
208 |
# If there is a match, we need to remove this line.
|
|
|
209 |
if matches:
|
|
|
210 |
# Store the old value. We'll need to replace it later.
|
|
|
211 |
self.SetConfig('oldNofileHardLimit', self.hardLimit)
|
|
|
212 |
log.Debug('Removing existing hard nofile line.')
|
|
|
213 |
text = re.sub('\*.+hard.+nofile.+\d+.*\n', '', text, re.MULTILINE)
|
|
|
214 |
# Some systems have an '# End of file' marker. Remove it and
|
|
|
215 |
# set a flag to replace it if it's found.
|
|
|
216 |
setFileEnd = False
|
|
|
217 |
newText = re.sub('# End of file.*', '', text)
|
|
|
218 |
if newText != text:
|
|
|
219 |
setFileEnd = True
|
|
|
220 |
text = newText
|
|
|
221 |
# Now add in our line.
|
|
|
222 |
text = text + entry
|
|
|
223 |
if setFileEnd:
|
|
|
224 |
text = text + '# End of file.'
|
|
|
225 |
limitsFile.write_text(text)
|
|
|
226 |
|
|
|
227 |
def _ClearPamD(self, pamFile):
|
|
|
228 |
if pamFile.exists():
|
|
|
229 |
self._RemoveMarkedLineFromFile(pamFile, pamLoginLine)
|
|
|
230 |
|
|
|
231 |
def _WritePamD(self, pamFile):
|
|
|
232 |
if pamFile.exists():
|
|
|
233 |
text = pamFile.text()
|
|
|
234 |
|
|
|
235 |
# Search for the entry we want:
|
|
|
236 |
matches = re.findall('session\s+required\s+pam_limits.so', text)
|
|
|
237 |
|
|
|
238 |
# If no matches were found, we need to add the entry.
|
|
|
239 |
if not matches:
|
|
|
240 |
newStr = text + vmwareSentinel + \
|
|
|
241 |
pamLoginLine + \
|
|
|
242 |
vmwareSentinel
|
|
|
243 |
pamFile.write_text(newStr)
|
|
|
244 |
|
|
|
245 |
def _ModifyVMwareLimitsConf(self, limitsFile):
|
|
|
246 |
# Modify the limits.conf file to include the lines:
|
|
|
247 |
# * hard nofile ####
|
|
|
248 |
# The validator ensures that the answer was an integer, so no need
|
|
|
249 |
# to check.
|
|
|
250 |
nofileHL = self.GetAnswer('nofileHardLimit')
|
|
|
251 |
if nofileHL and (self.hardLimit != int(nofileHL)):
|
|
|
252 |
limitsFile = Destination('/etc/security/limits.conf')
|
|
|
253 |
if limitsFile.exists():
|
|
|
254 |
self._ClearVMwareLimitsConf(limitsFile, restoreEntry=False)
|
|
|
255 |
log.Debug('Modifying /etc/security/limits.conf hard limit from '
|
|
|
256 |
'%d to %d.', self.hardLimit, nofileHL)
|
|
|
257 |
self._WriteLimitsConfEntry(limitsFile, vmwareSentinel + \
|
|
|
258 |
'*\t\thard\tnofile\t\t%s\n' % nofileHL + \
|
|
|
259 |
vmwareSentinel)
|
|
|
260 |
self._WritePamD(PAMLOGINFILE)
|