1 |
# |
2 |
# text.py - text mode frontend to anaconda |
3 |
# |
4 |
# Erik Troan <ewt@redhat.com> |
5 |
# Matt Wilson <msw@redhat.com> |
6 |
# |
7 |
# Copyright 1999-2004 Red Hat, Inc. |
8 |
# |
9 |
# This software may be freely redistributed under the terms of the GNU |
10 |
# library public license. |
11 |
# |
12 |
# You should have received a copy of the GNU Library Public License |
13 |
# along with this program; if not, write to the Free Software |
14 |
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
15 |
# |
16 |
|
17 |
from snack import * |
18 |
import sys |
19 |
import os |
20 |
import isys |
21 |
import iutil |
22 |
import time |
23 |
import signal |
24 |
import parted |
25 |
import string |
26 |
import kudzu |
27 |
from language import expandLangs |
28 |
from flags import flags |
29 |
from constants_text import * |
30 |
from constants import * |
31 |
|
32 |
from rhpl.log import log |
33 |
from rhpl.translate import _, cat, N_ |
34 |
|
35 |
stepToClasses = { |
36 |
"language" : ("language_text", "LanguageWindow"), |
37 |
"keyboard" : ("keyboard_text", "KeyboardWindow"), |
38 |
"mouse" : ("mouse_text", ("MouseWindow", "MouseDeviceWindow")), |
39 |
"welcome" : ("welcome_text", "WelcomeWindow"), |
40 |
"installtype" : ("installpath_text", "InstallPathWindow"), |
41 |
"autopartition" : ("partition_text", "AutoPartitionWindow"), |
42 |
"custom-upgrade" : ("upgrade_text", "UpgradeExamineWindow"), |
43 |
"addswap" : ("upgrade_text", "UpgradeSwapWindow"), |
44 |
"upgrademigratefs" : ("upgrade_text", "UpgradeMigrateFSWindow"), |
45 |
"fdisk" : ("fdisk_text", "fdiskPartitionWindow"), |
46 |
"partitionmethod" : ("partmethod_text", ("PartitionMethod")), |
47 |
"partition": ("partition_text", ("PartitionWindow")), |
48 |
"zfcpconfig": ("zfcp_text", ("ZFCPWindow")), |
49 |
"findinstall" : ("upgrade_text", ("UpgradeExamineWindow")), |
50 |
# replace with below if you want customize screen |
51 |
# "findinstall" : ("upgrade_text", ("UpgradeExamineWindow", |
52 |
# "CustomizeUpgradeWindow")), |
53 |
"addswap" : ("upgrade_text", "UpgradeSwapWindow"), |
54 |
"upgbootloader": ("upgrade_bootloader_text", "UpgradeBootloaderWindow"), |
55 |
"bootloader" : ("bootloader_text", ("BootloaderChoiceWindow", |
56 |
"BootloaderAppendWindow", |
57 |
"BootloaderPasswordWindow")), |
58 |
"bootloaderadvanced" : ("bootloader_text", ("BootloaderImagesWindow", |
59 |
"BootloaderLocationWindow")), |
60 |
"network" : ("network_text", ("NetworkDeviceWindow", "NetworkGlobalWindow", |
61 |
"HostnameWindow")), |
62 |
"firewall" : ("firewall_text", ("FirewallWindow", |
63 |
"SELinuxWindow")), |
64 |
"languagesupport" : ("language_text", ("LanguageSupportWindow", |
65 |
"LanguageDefaultWindow")), |
66 |
"timezone" : ("timezone_text", "TimezoneWindow"), |
67 |
"accounts" : ("userauth_text", "RootPasswordWindow"), |
68 |
"authentication" : ("userauth_text", ("AuthConfigWindow")), |
69 |
"desktopchoice": ("desktop_choice_text", "DesktopChoiceWindow"), |
70 |
"package-selection" : ("packages_text", "PackageGroupWindow"), |
71 |
"indivpackage" : ("packages_text", ("IndividualPackageWindow")), |
72 |
"dependencies" : ("packages_text", "PackageDepWindow"), |
73 |
"videocard" : ("xconfig_text", "XConfigWindowCard"), |
74 |
"monitor" : ("xconfig_text", "MonitorWindow"), |
75 |
"xcustom" : ("xconfig_text", "XCustomWindow"), |
76 |
"confirminstall" : ("confirm_text", "BeginInstallWindow"), |
77 |
"confirmupgrade" : ("confirm_text", "BeginUpgradeWindow"), |
78 |
"install" : ("progress_text", "setupForInstall"), |
79 |
"bootdisk" : ("bootdisk_text", ("BootDiskWindow")), |
80 |
"complete" : ("complete_text", "FinishedWindow"), |
81 |
} |
82 |
|
83 |
if iutil.getArch() == 'sparc': |
84 |
stepToClasses["bootloader"] = ("silo_text", ("SiloAppendWindow", |
85 |
"SiloWindow" |
86 |
"SiloImagesWindow")) |
87 |
if iutil.getArch() == 's390': |
88 |
stepToClasses["bootloader"] = ("zipl_text", ( "ZiplWindow")) |
89 |
|
90 |
class InstallWindow: |
91 |
def __call__ (self, screen): |
92 |
raise RuntimeError, "Unimplemented screen" |
93 |
|
94 |
class WaitWindow: |
95 |
def pop(self): |
96 |
self.screen.popWindow() |
97 |
self.screen.refresh() |
98 |
|
99 |
def __init__(self, screen, title, text): |
100 |
self.screen = screen |
101 |
width = 40 |
102 |
if (len(text) < width): width = len(text) |
103 |
|
104 |
t = TextboxReflowed(width, text) |
105 |
|
106 |
g = GridForm(self.screen, title, 1, 1) |
107 |
g.add(t, 0, 0) |
108 |
g.draw() |
109 |
self.screen.refresh() |
110 |
|
111 |
class OkCancelWindow: |
112 |
def getrc(self): |
113 |
return self.rc |
114 |
|
115 |
def __init__(self, screen, title, text): |
116 |
rc = ButtonChoiceWindow(screen, title, text, |
117 |
buttons=[TEXT_OK_BUTTON, _("Cancel")]) |
118 |
if rc == string.lower(_("Cancel")): |
119 |
self.rc = 1 |
120 |
else: |
121 |
self.rc = 0 |
122 |
|
123 |
class ProgressWindow: |
124 |
def pop(self): |
125 |
self.screen.popWindow() |
126 |
self.screen.refresh() |
127 |
del self.scale |
128 |
self.scale = None |
129 |
|
130 |
def set(self, amount): |
131 |
self.scale.set(amount) |
132 |
self.screen.refresh() |
133 |
|
134 |
def __init__(self, screen, title, text, total): |
135 |
self.screen = screen |
136 |
width = 55 |
137 |
if (len(text) > width): width = len(text) |
138 |
|
139 |
t = TextboxReflowed(width, text) |
140 |
|
141 |
g = GridForm(self.screen, title, 1, 2) |
142 |
g.add(t, 0, 0, (0, 0, 0, 1), anchorLeft=1) |
143 |
|
144 |
self.scale = Scale(width, total) |
145 |
g.add(self.scale, 0, 1) |
146 |
|
147 |
g.draw() |
148 |
self.screen.refresh() |
149 |
|
150 |
class InstallInterface: |
151 |
def helpWindow(self, screen, key): |
152 |
lang = self.instLanguage.getCurrent() |
153 |
lang = self.instLanguage.getLangNick(lang) |
154 |
self.langSearchPath = expandLangs(lang) + ['C'] |
155 |
|
156 |
if key == "helponhelp": |
157 |
if self.showingHelpOnHelp: |
158 |
return None |
159 |
else: |
160 |
self.showingHelpOnHelp = 1 |
161 |
try: |
162 |
f = None |
163 |
|
164 |
if self.configFileData.has_key("helptag"): |
165 |
helpTag = "-%s" % (self.configFileData["helptag"],) |
166 |
else: |
167 |
helpTag = "" |
168 |
arch = "-%s" % (iutil.getArch(),) |
169 |
tags = [ "%s%s" % (helpTag, arch), "%s" % (helpTag,), |
170 |
"%s" % (arch,), "" ] |
171 |
|
172 |
# XXX |
173 |
# |
174 |
# HelpWindow can't get to the langauge |
175 |
|
176 |
found = 0 |
177 |
for path in ("./text-", "/mnt/source/RHupdates/", |
178 |
"/usr/share/anaconda/"): |
179 |
if found: |
180 |
break |
181 |
for lang in self.langSearchPath: |
182 |
for tag in tags: |
183 |
fn = "%shelp/%s/s1-help-screens-%s%s.txt" \ |
184 |
% (path, lang, key, tag) |
185 |
|
186 |
try: |
187 |
f = open(fn) |
188 |
except IOError, msg: |
189 |
continue |
190 |
found = 1 |
191 |
break |
192 |
|
193 |
if not f: |
194 |
ButtonChoiceWindow(screen, _("Help not available"), |
195 |
_("No help is available for this " |
196 |
"step of the install."), |
197 |
buttons=[TEXT_OK_BUTTON]) |
198 |
return None |
199 |
|
200 |
lines = f.readlines() |
201 |
for l in lines: |
202 |
l = l.replace("@RHL@", productName) |
203 |
l = l.replace("@RHLVER@", productVersion) |
204 |
while not string.strip(l[0]): |
205 |
l = l[1:] |
206 |
title = string.strip(l[0]) |
207 |
l = l[1:] |
208 |
while not string.strip(l[0]): |
209 |
l = l[1:] |
210 |
f.close() |
211 |
|
212 |
height = 10 |
213 |
scroll = 1 |
214 |
if len(l) < height: |
215 |
height = len(l) |
216 |
scroll = 0 |
217 |
|
218 |
width = len(title) + 6 |
219 |
stream = "" |
220 |
for line in l: |
221 |
line = string.strip(line) |
222 |
stream = stream + line + "\n" |
223 |
if len(line) > width: |
224 |
width = len(line) |
225 |
|
226 |
bb = ButtonBar(screen, [TEXT_OK_BUTTON]) |
227 |
t = Textbox(width, height, stream, scroll=scroll) |
228 |
|
229 |
g = GridFormHelp(screen, title, "helponhelp", 1, 2) |
230 |
g.add(t, 0, 0, padding=(0, 0, 0, 1)) |
231 |
g.add(bb, 0, 1, growx=1) |
232 |
|
233 |
g.runOnce() |
234 |
self.showingHelpOnHelp = 0 |
235 |
except: |
236 |
import traceback |
237 |
(type, value, tb) = sys.exc_info() |
238 |
from string import joinfields |
239 |
list = traceback.format_exception(type, value, tb) |
240 |
text = joinfields(list, "") |
241 |
rc = self.exceptionWindow(_("Exception Occurred"), text) |
242 |
if rc: |
243 |
import pdb |
244 |
pdb.post_mortem(tb) |
245 |
os._exit(1) |
246 |
|
247 |
def progressWindow(self, title, text, total): |
248 |
return ProgressWindow(self.screen, title, text, total) |
249 |
|
250 |
def messageWindow(self, title, text, type="ok", default = None, |
251 |
custom_icon=None, custom_buttons=[]): |
252 |
if type == "ok": |
253 |
ButtonChoiceWindow(self.screen, title, text, |
254 |
buttons=[TEXT_OK_BUTTON]) |
255 |
elif type == "yesno": |
256 |
if default and default == "no": |
257 |
btnlist = [TEXT_NO_BUTTON, TEXT_YES_BUTTON] |
258 |
else: |
259 |
btnlist = [TEXT_YES_BUTTON, TEXT_NO_BUTTON] |
260 |
rc = ButtonChoiceWindow(self.screen, title, text, |
261 |
buttons=btnlist) |
262 |
if rc == "yes": |
263 |
return 1 |
264 |
else: |
265 |
return 0 |
266 |
elif type == "custom": |
267 |
tmpbut = [] |
268 |
for but in custom_buttons: |
269 |
tmpbut.append(string.replace(but,"_","")) |
270 |
|
271 |
rc = ButtonChoiceWindow(self.screen, title, text, width=60, |
272 |
buttons=tmpbut) |
273 |
|
274 |
idx = 0 |
275 |
for b in tmpbut: |
276 |
if string.lower(b) == rc: |
277 |
return idx != 0 |
278 |
idx = idx + 1 |
279 |
return 0 |
280 |
else: |
281 |
return OkCancelWindow(self.screen, title, text) |
282 |
|
283 |
def kickstartErrorWindow(self, text): |
284 |
s = _("The following error was found while parsing your " |
285 |
"kickstart configuration:\n\n%s") %(text,) |
286 |
self.messageWindow(_("Error Parsing Kickstart Config"), |
287 |
s, |
288 |
type = "custom", |
289 |
custom_buttons = [("_Reboot")], |
290 |
custom_icon="error") |
291 |
|
292 |
|
293 |
|
294 |
def dumpWindow(self): |
295 |
rc = ButtonChoiceWindow(self.screen, _("Save Crash Dump"), |
296 |
_("Please insert a floppy now. All contents of the disk " |
297 |
"will be erased, so please choose your diskette carefully."), |
298 |
[TEXT_OK_BUTTON, _("Cancel")]) |
299 |
|
300 |
if rc == string.lower(_("Cancel")): |
301 |
return 1 |
302 |
|
303 |
return 0 |
304 |
|
305 |
def exceptionWindow(self, title, text): |
306 |
try: |
307 |
floppyDevices = 0 |
308 |
for dev in kudzu.probe(kudzu.CLASS_FLOPPY, kudzu.BUS_UNSPEC, |
309 |
kudzu.PROBE_ALL): |
310 |
if not dev.detached: |
311 |
floppyDevices = floppyDevices + 1 |
312 |
except: |
313 |
floppyDevices = 0 |
314 |
if floppyDevices > 0 or DEBUG: |
315 |
ugh = "%s\n\n" % (exceptionText,) |
316 |
buttons=[TEXT_OK_BUTTON, _("Save"), _("Debug")] |
317 |
else: |
318 |
ugh = "%s\n\n" % (exceptionTextNoFloppy,) |
319 |
buttons=[TEXT_OK_BUTTON, _("Debug")] |
320 |
|
321 |
rc = ButtonChoiceWindow(self.screen, title, ugh + text, buttons) |
322 |
if rc == string.lower(_("Debug")): |
323 |
return 1 |
324 |
elif rc == string.lower(_("Save")): |
325 |
return 2 |
326 |
return None |
327 |
|
328 |
def partedExceptionWindow(self, exc): |
329 |
# if our only option is to cancel, let us handle the exception |
330 |
# in our code and avoid popping up the exception window here. |
331 |
if exc.options == parted.EXCEPTION_CANCEL: |
332 |
return parted.EXCEPTION_UNHANDLED |
333 |
log("parted exception: %s: %s" %(exc.type_string,exc.message)) |
334 |
buttons = [] |
335 |
buttonToAction = {} |
336 |
flags = ((parted.EXCEPTION_FIX, N_("Fix")), |
337 |
(parted.EXCEPTION_YES, N_("Yes")), |
338 |
(parted.EXCEPTION_NO, N_("No")), |
339 |
(parted.EXCEPTION_OK, N_("OK")), |
340 |
(parted.EXCEPTION_RETRY, N_("Retry")), |
341 |
(parted.EXCEPTION_IGNORE, N_("Ignore")), |
342 |
(parted.EXCEPTION_CANCEL, N_("Cancel"))) |
343 |
for flag, errorstring in flags: |
344 |
if exc.options & flag: |
345 |
buttons.append(_(errorstring)) |
346 |
buttonToAction[string.lower(_(errorstring))] = flag |
347 |
|
348 |
rc = None |
349 |
while not buttonToAction.has_key(rc): |
350 |
rc = ButtonChoiceWindow(self.screen, exc.type_string, exc.message, |
351 |
buttons=buttons) |
352 |
|
353 |
return buttonToAction[rc] |
354 |
|
355 |
|
356 |
def waitWindow(self, title, text): |
357 |
return WaitWindow(self.screen, title, text) |
358 |
|
359 |
def drawFrame(self): |
360 |
self.welcomeText = _("%s Released via the GPL") % (productName,) |
361 |
self.screen.drawRootText (0, 0, self.welcomeText) |
362 |
self.screen.drawRootText (len(_(self.welcomeText)), 0, |
363 |
(self.screen.width - |
364 |
len(_(self.welcomeText))) * " ") |
365 |
|
366 |
if (os.access("/usr/share/anaconda/help/C/s1-help-screens-lang.txt", os.R_OK)): |
367 |
self.screen.pushHelpLine(_(" <F1> for help | <Tab> between elements | <Space> selects | <F12> next screen")) |
368 |
else: |
369 |
self.screen.pushHelpLine(_(" <Tab>/<Alt-Tab> between elements | <Space> selects | <F12> next screen")) |
370 |
|
371 |
def setScreen(self, screen): |
372 |
self.screen = screen |
373 |
|
374 |
def shutdown(self): |
375 |
self.screen.finish() |
376 |
self.screen = None |
377 |
|
378 |
def __init__(self): |
379 |
signal.signal(signal.SIGINT, signal.SIG_IGN) |
380 |
signal.signal(signal.SIGTSTP, signal.SIG_IGN) |
381 |
self.screen = None |
382 |
self.showingHelpOnHelp = 0 |
383 |
|
384 |
def __del__(self): |
385 |
if self.screen: |
386 |
self.screen.finish() |
387 |
|
388 |
def run(self, id, dispatch, configFileData): |
389 |
# set up for CJK text mode if needed |
390 |
oldlang = None |
391 |
if (flags.setupFilesystems and |
392 |
(id.instLanguage.getFontFile(id.instLanguage.getCurrent()) == "bterm") |
393 |
and not flags.serial and not flags.virtpconsole |
394 |
and not isys.isPsudoTTY(0) and not isys.isVioConsole()): |
395 |
log("starting bterm") |
396 |
rc = 1 |
397 |
try: |
398 |
rc = isys.startBterm() |
399 |
time.sleep(1) |
400 |
except Exception, e: |
401 |
log("got an exception starting bterm: %s" %(e,)) |
402 |
|
403 |
if rc == 1: |
404 |
log("unable to start bterm, falling back to english") |
405 |
oldlang = id.instLanguage.getCurrent() |
406 |
log("old language was %s" %(oldlang,)) |
407 |
id.instLanguage.setRuntimeLanguage("English") |
408 |
id.instLanguage.setRuntimeDefaults(oldlang) |
409 |
|
410 |
if id.instLanguage.getFontFile(id.instLanguage.getCurrent()) == "none": |
411 |
oldlang = id.instLanguage.getCurrent() |
412 |
id.instLanguage.setRuntimeLanguage("English") |
413 |
id.instLanguage.setRuntimeDefaults(oldlang) |
414 |
|
415 |
self.screen = SnackScreen() |
416 |
self.configFileData = configFileData |
417 |
self.screen.helpCallback(self.helpWindow) |
418 |
|
419 |
# uncomment this line to make the installer quit on <Ctrl+Z> |
420 |
# handy for quick debugging. |
421 |
# self.screen.suspendCallback(killSelf, self.screen) |
422 |
# uncomment this line to drop into the python debugger on <Ctrl+Z> |
423 |
# --VERY handy-- |
424 |
if DEBUG or flags.test: |
425 |
self.screen.suspendCallback(debugSelf, self.screen) |
426 |
|
427 |
if flags.serial or flags.virtpconsole or isys.isPsudoTTY(0) or isys.isVioConsole(): |
428 |
self.screen.suspendCallback(spawnShell, self.screen) |
429 |
|
430 |
# clear out the old root text by writing spaces in the blank |
431 |
# area on the right side of the screen |
432 |
#self.screen.drawRootText (len(_(self.welcomeText)), 0, |
433 |
#(self.screen.width - len(_(self.welcomeText))) * " ") |
434 |
#self.screen.drawRootText (0 - len(_(step[0])), 0, _(step[0])) |
435 |
langname = id.instLanguage.getCurrent() |
436 |
lang = id.instLanguage.getLangNick(langname) |
437 |
|
438 |
self.langSearchPath = expandLangs(lang) + ['C'] |
439 |
self.instLanguage = id.instLanguage |
440 |
|
441 |
# draw the frame after setting up the fallback |
442 |
self.drawFrame() |
443 |
|
444 |
# draw the frame after setting up the fallback |
445 |
self.drawFrame() |
446 |
|
447 |
if oldlang is not None: |
448 |
ButtonChoiceWindow(self.screen, "Language Unavailable", |
449 |
"%s display is unavailable in text mode. " |
450 |
"The installation will continue in " |
451 |
"English." % (oldlang,), |
452 |
buttons=[TEXT_OK_BUTTON]) |
453 |
|
454 |
|
455 |
id.fsset.registerMessageWindow(self.messageWindow) |
456 |
id.fsset.registerProgressWindow(self.progressWindow) |
457 |
id.fsset.registerWaitWindow(self.waitWindow) |
458 |
parted.exception_set_handler(self.partedExceptionWindow) |
459 |
|
460 |
lastrc = INSTALL_OK |
461 |
(step, args) = dispatch.currentStep() |
462 |
while step: |
463 |
(file, classNames) = stepToClasses[step] |
464 |
|
465 |
if type(classNames) != type(()): |
466 |
classNames = (classNames,) |
467 |
|
468 |
if lastrc == INSTALL_OK: |
469 |
step = 0 |
470 |
else: |
471 |
step = len(classNames) - 1 |
472 |
|
473 |
while step >= 0 and step < len(classNames): |
474 |
# reget the args. they could change (especially direction) |
475 |
(foo, args) = dispatch.currentStep() |
476 |
|
477 |
nextWindow = None |
478 |
s = "from %s import %s; nextWindow = %s" % \ |
479 |
(file, classNames[step], classNames[step]) |
480 |
exec s |
481 |
|
482 |
win = nextWindow() |
483 |
|
484 |
#log("TUI running step %s (class %s, file %s)" % |
485 |
#(step, file, classNames)) |
486 |
|
487 |
rc = apply(win, (self.screen, ) + args) |
488 |
|
489 |
if rc == INSTALL_NOOP: |
490 |
rc = lastrc |
491 |
|
492 |
if rc == INSTALL_BACK: |
493 |
step = step - 1 |
494 |
dispatch.dir = DISPATCH_BACK |
495 |
elif rc == INSTALL_OK: |
496 |
step = step + 1 |
497 |
dispatch.dir = DISPATCH_FORWARD |
498 |
|
499 |
lastrc = rc |
500 |
|
501 |
if step == -1: |
502 |
if not dispatch.canGoBack(): |
503 |
ButtonChoiceWindow(self.screen, _("Cancelled"), |
504 |
_("I can't go to the previous step " |
505 |
"from here. You will have to try " |
506 |
"again."), |
507 |
buttons=[_("OK")]) |
508 |
else: |
509 |
dispatch.gotoPrev() |
510 |
else: |
511 |
dispatch.gotoNext() |
512 |
|
513 |
(step, args) = dispatch.currentStep() |
514 |
|
515 |
self.screen.finish() |
516 |
|
517 |
def killSelf(screen): |
518 |
screen.finish() |
519 |
os._exit(0) |
520 |
|
521 |
def debugSelf(screen): |
522 |
screen.suspend() |
523 |
import pdb |
524 |
try: |
525 |
pdb.set_trace() |
526 |
except: |
527 |
sys.exit(-1) |
528 |
screen.resume() |
529 |
|
530 |
def spawnShell(screen): |
531 |
screen.suspend() |
532 |
print "\n\nType <exit> to return to the install program.\n" |
533 |
if os.path.exists("/bin/sh"): |
534 |
iutil.execWithRedirect("/bin/sh", ["-/bin/sh"]) |
535 |
else: |
536 |
print "Unable to find /bin/sh to execute! Not starting shell" |
537 |
time.sleep(5) |
538 |
screen.resume() |