1 #!/usr/bin/python2.6 |
1 #!/usr/bin/python2.6 |
|
2 |
|
3 # Copyright (c) 2014, 2015, Oracle and/or its affiliates. All rights reserved. |
|
4 # |
|
5 # Licensed under the Apache License, Version 2.0 (the "License"); you may |
|
6 # not use this file except in compliance with the License. You may obtain |
|
7 # a copy of the License at |
|
8 # |
|
9 # http://www.apache.org/licenses/LICENSE-2.0 |
|
10 # |
|
11 # Unless required by applicable law or agreed to in writing, software |
|
12 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
|
13 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
|
14 # License for the specific language governing permissions and limitations |
|
15 # under the License. |
2 |
16 |
3 import errno |
17 import errno |
4 import os |
18 import os |
5 import pwd |
19 import pwd |
6 import smf_include |
20 import smf_include |
7 import subprocess |
21 import subprocess |
8 import sys |
22 import sys |
|
23 import time |
9 |
24 |
10 from subprocess import CalledProcessError, check_call, Popen |
25 from subprocess import CalledProcessError, check_call, Popen |
11 from tempfile import mkstemp |
26 from tempfile import mkstemp |
12 |
27 |
|
28 GTF = "/usr/bin/gtf" |
13 SVCCFG = "/usr/sbin/svccfg" |
29 SVCCFG = "/usr/sbin/svccfg" |
14 SVCPROP = "/usr/bin/svcprop" |
30 SVCPROP = "/usr/bin/svcprop" |
15 VNCSERVER = "/usr/bin/vncserver" |
31 VNCSERVER = "/usr/bin/vncserver" |
|
32 XRANDR = "/usr/bin/xrandr" |
16 XSTARTUPHDR = "# WARNING: THIS FILE GENERATED BY SMF.\n" + \ |
33 XSTARTUPHDR = "# WARNING: THIS FILE GENERATED BY SMF.\n" + \ |
17 "# DO NOT EDIT THIS FILE. EDITS WILL BE LOST.\n" |
34 "# DO NOT EDIT THIS FILE. EDITS WILL BE LOST.\n" |
18 XTERM = "/usr/bin/xterm" |
35 XTERM = "/usr/bin/xterm" |
19 # Monospsce font, point size 15, white foreground on black background" |
36 # Borderless, Monospsce font, point size 14, white foreground on black |
20 XTERMOPTS = ' -fa Monospace -fs 15 -fg white -bg black' |
37 # background are reasonable defaults. |
|
38 XTERMOPTS = ' -b 0 -fa Monospace -fs 14 -fg white -bg black -title ' + \ |
|
39 '"Zone Console: $ZONENAME"' |
|
40 XWININFO = "/usr/bin/xwininfo" |
21 # Enclose command in comments to prevent xterm consuming zlogin opts |
41 # Enclose command in comments to prevent xterm consuming zlogin opts |
22 ZLOGINOPTS = ' -e "/usr/bin/pfexec /usr/sbin/zlogin -C -E $ZONENAME"\n' |
42 ZLOGINOPTS = ' -e "/usr/bin/pfexec /usr/sbin/zlogin -C -E $ZONENAME"\n' |
23 XSTARTUP = XSTARTUPHDR + XTERM + XTERMOPTS + ZLOGINOPTS |
43 XSTARTUP = XSTARTUPHDR + XTERM + XTERMOPTS + ZLOGINOPTS |
24 |
44 |
25 |
45 |
36 zonename = fmri.rsplit(':', 1)[1] |
56 zonename = fmri.rsplit(':', 1)[1] |
37 os.putenv("ZONENAME", zonename) |
57 os.putenv("ZONENAME", zonename) |
38 desktop_name = zonename + ' console' |
58 desktop_name = zonename + ' console' |
39 # NOTE: 'geometry' below is that which matches the size of standard |
59 # NOTE: 'geometry' below is that which matches the size of standard |
40 # 80 character undecorated xterm window using font style specified in |
60 # 80 character undecorated xterm window using font style specified in |
41 # XTERMOPTS. Update this geometry whenever XTERMOPTS are changed. |
61 # XTERMOPTS. The geometry doesn't matter too much because the display |
42 # Avoids exposing X root window within noVNC canvas widget. |
62 # will be resized using xrandr once the xterm geometry is established. |
43 cmd = [VNCSERVER, "-name", desktop_name, "-SecurityTypes=None", |
63 cmd = [VNCSERVER, "-name", desktop_name, "-SecurityTypes=None", |
44 "-geometry", "964x580", "-localhost", "-autokill"] |
64 "-geometry", "964x580", "-localhost", "-autokill"] |
45 vnc = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
65 vnc = Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, |
46 env=None) |
66 env=None) |
47 out, err = vnc.communicate() |
67 out, err = vnc.communicate() |
133 os.close(stemp[0]) |
155 os.close(stemp[0]) |
134 os.chmod(stemp[1], 0700) |
156 os.chmod(stemp[1], 0700) |
135 os.rename(stemp[1], xstartup_path) |
157 os.rename(stemp[1], xstartup_path) |
136 |
158 |
137 |
159 |
|
160 def resize_xserver(display, zonename): |
|
161 """ Try to determine xterm window geometry and resize the Xvnc display |
|
162 to match using XRANDR. Treat failure as non-fatal since an |
|
163 incorrectly sized console is arguably better than none. |
|
164 """ |
|
165 class UnmappedWindowError(Exception): |
|
166 pass |
|
167 |
|
168 def _get_window_geometry(display, windowname): |
|
169 """ Find the xterm xwindow by name/title and extract its geometry |
|
170 Returns: tuple of window [width, height] |
|
171 Raises: UnmappedWindowError if window is not viewable/unmapped |
|
172 """ |
|
173 cmd = [XWININFO, '-d', display, '-name', windowname] |
|
174 xwininfo = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
|
175 stderr=subprocess.PIPE) |
|
176 out, err = xwininfo.communicate() |
|
177 retcode = xwininfo.wait() |
|
178 if retcode != 0: |
|
179 print "Error finding console xwindow info: " + err |
|
180 return |
|
181 |
|
182 width = None |
|
183 height = None |
|
184 mapped = False |
|
185 for line in out.splitlines(): |
|
186 line = line.strip() |
|
187 if line.startswith("Map State:"): |
|
188 if line.split()[-1] != "IsViewable": |
|
189 # Window is not mapped yet. |
|
190 raise UnmappedWindowError |
|
191 else: |
|
192 mapped = True |
|
193 if line.startswith("Width:"): |
|
194 width = int(line.split()[1]) |
|
195 elif line.startswith("Height:"): |
|
196 height = int(line.split()[1]) |
|
197 if width and height and mapped: |
|
198 return [width, height] |
|
199 else: |
|
200 # What, no width and height??? |
|
201 print "No window geometry info returned by " + XWINFINFO |
|
202 raise UnmappedWindowError |
|
203 |
|
204 retries = 5 |
|
205 width = 0 |
|
206 height = 0 |
|
207 for tries in range(retries): |
|
208 try: |
|
209 width, height = _get_window_geometry(display, |
|
210 'Zone Console: ' + zonename) |
|
211 print "Discovered xterm geometry: %d x %d" % (width, height) |
|
212 break |
|
213 except UnmappedWindowError: |
|
214 if tries < retries: |
|
215 print "Discovered xterm not mapped yet. Retrying in 0.5s" |
|
216 time.sleep(0.5) |
|
217 continue |
|
218 else: |
|
219 print "Discovered xterm window is taking too long to map" |
|
220 return |
|
221 else: |
|
222 print "Too many failed attempts to discover xterm window geometry" |
|
223 return |
|
224 |
|
225 # Generate a mode line for width and height, with a refresh of 60.0Hz |
|
226 cmd = [GTF, str(width), str(height), '60.0', '-x'] |
|
227 gtf = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
|
228 stderr=subprocess.PIPE) |
|
229 out, err = gtf.communicate() |
|
230 retcode = gtf.wait() |
|
231 if retcode != 0: |
|
232 print "Error creating new modeline for VNC display: " + err |
|
233 return |
|
234 |
|
235 for line in out.splitlines(): |
|
236 line = line.strip() |
|
237 if line.startswith('Modeline'): |
|
238 modeline = line.split('Modeline')[1] |
|
239 print "New optimal modeline for Xvnc server: " + modeline |
|
240 mode = modeline.split() |
|
241 break |
|
242 |
|
243 # Create a new mode for the Xvnc server using the modeline generated by gtf |
|
244 cmd = [XRANDR, '-d', display, '--newmode'] |
|
245 cmd.extend(mode) |
|
246 newmode = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
|
247 stderr=subprocess.PIPE) |
|
248 out, err = newmode.communicate() |
|
249 retcode = newmode.wait() |
|
250 if retcode != 0: |
|
251 print "Error creating new xrandr modeline for VNC display: " + err |
|
252 return |
|
253 |
|
254 # Add the new mode to the default display output |
|
255 modename = mode[0] |
|
256 cmd = [XRANDR, '-d', display, '--addmode', 'default', modename] |
|
257 addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
|
258 stderr=subprocess.PIPE) |
|
259 out, err = addmode.communicate() |
|
260 retcode = addmode.wait() |
|
261 if retcode != 0: |
|
262 print "Error adding new xrandr modeline for VNC display: " + err |
|
263 return |
|
264 |
|
265 # Activate the new mode on the default display output |
|
266 cmd = [XRANDR, '-d', display, '--output', 'default', '--mode', modename] |
|
267 addmode = subprocess.Popen(cmd, stdout=subprocess.PIPE, |
|
268 stderr=subprocess.PIPE) |
|
269 out, err = addmode.communicate() |
|
270 retcode = addmode.wait() |
|
271 if retcode != 0: |
|
272 print "Error setting new xrandr modeline for VNC display: " + err |
|
273 return |
|
274 |
138 if __name__ == "__main__": |
275 if __name__ == "__main__": |
139 os.putenv("LC_ALL", "C") |
276 os.putenv("LC_ALL", "C") |
140 smf_include.smf_main() |
277 smf_include.smf_main() |