def __init__(self): # Load preseed file if os.path.exists(DEFAULT_PRESEED_FILE): os.system("debconf-set-selections "+DEFAULT_PRESEED_FILE) db = DebconfCommunicator("ubiquity") self.BREADCRUMB_STEPS_PRESEED = { "stepLanguage": db.exist("ubiquity/stepLanguage")=="true" and db.get("ubiquity/stepLanguage")=="true", "stepLocation": db.exist("ubiquity/stepLocation")=="true" and db.get("ubiquity/stepLocation")=="true", "stepKeyboardConf": db.exist("ubiquity/stepKeyboardConf")=="true" and db.get("ubiquity/stepKeyboardConf")=="true", "stepUserInfo": db.exist("ubiquity/stepUserInfo")=="true" and db.get("ubiquity/stepUserInfo")=="true", "stepPartDisk": db.exist("ubiquity/stepPartDisk")=="true" and db.get("ubiquity/stepPartDisk")=="true", "stepPartAuto": db.exist("ubiquity/stepPartAuto")=="true" and db.get("ubiquity/stepPartAuto")=="true", "stepPartAdvanced": db.exist("ubiquity/stepPartAdvanced")=="true" and db.get("ubiquity/stepPartAdvanced")=="true", "stepPartMountpoints": db.exist("ubiquity/stepMountpoints")=="true" and db.get("ubiquity/stepMountpoints")=="true", "stepReady": db.exist("ubiquity/stepReady")=="true" and db.get("ubiquity/stepReady")=="true" } db.shutdown() self.BREADCRUMB_STEPS = { "stepLanguage": 1, "stepLocation": 2, "stepKeyboardConf": 3, "stepUserInfo": 4, "stepPartDisk": 5, "stepPartAuto": 5, "stepPartAdvanced": 5, "stepPartMountpoints": 5, "stepReady": 6 }
def __init__(self): # Load preseed file if os.path.exists(DEFAULT_PRESEED_FILE): os.system("debconf-set-selections " + DEFAULT_PRESEED_FILE) db = DebconfCommunicator("ubiquity") self.BREADCRUMB_STEPS_PRESEED = { "stepLanguage": db.exist("ubiquity/stepLanguage") == "true" and db.get("ubiquity/stepLanguage") == "true", "stepLocation": db.exist("ubiquity/stepLocation") == "true" and db.get("ubiquity/stepLocation") == "true", "stepKeyboardConf": db.exist("ubiquity/stepKeyboardConf") == "true" and db.get("ubiquity/stepKeyboardConf") == "true", "stepUserInfo": db.exist("ubiquity/stepUserInfo") == "true" and db.get("ubiquity/stepUserInfo") == "true", "stepPartDisk": db.exist("ubiquity/stepPartDisk") == "true" and db.get("ubiquity/stepPartDisk") == "true", "stepPartAuto": db.exist("ubiquity/stepPartAuto") == "true" and db.get("ubiquity/stepPartAuto") == "true", "stepPartAdvanced": db.exist("ubiquity/stepPartAdvanced") == "true" and db.get("ubiquity/stepPartAdvanced") == "true", "stepPartMountpoints": db.exist("ubiquity/stepMountpoints") == "true" and db.get("ubiquity/stepMountpoints") == "true", "stepReady": db.exist("ubiquity/stepReady") == "true" and db.get("ubiquity/stepReady") == "true" } db.shutdown() self.BREADCRUMB_STEPS = { "stepLanguage": 1, "stepLocation": 2, "stepKeyboardConf": 3, "stepUserInfo": 4, "stepPartDisk": 5, "stepPartAuto": 5, "stepPartAdvanced": 5, "stepPartMountpoints": 5, "stepReady": 6 }
class FilteredCommand(object): def __init__(self, frontend, db=None): self.frontend = frontend # db does not normally need to be specified. self.db = db self.done = False self.current_question = None self.succeeded = False @classmethod def debug(self, fmt, *args): if 'OEM_CONFIG_DEBUG' in os.environ: message = fmt % args print >>sys.stderr, '%s: %s' % (PACKAGE, message) sys.stderr.flush() def start(self, auto_process=False): self.status = None self.db = DebconfCommunicator(PACKAGE, cloexec=True) prep = self.prepare() self.command = prep[0] question_patterns = prep[1] if len(prep) > 2: env = prep[2] else: env = {} self.ui_loop_level = 0 self.debug("Starting up '%s' for %s.%s", self.command, self.__class__.__module__, self.__class__.__name__) self.debug("Watching for question patterns %s", ', '.join(question_patterns)) widgets = {} for pattern in question_patterns: widgets[pattern] = self self.dbfilter = DebconfFilter(self.db, widgets) # TODO: Set as unseen all questions that we're going to ask. self.dbfilter.start(self.command, blocking=False, extra_env=env) # Clearly, this isn't enough for full non-blocking operation. # However, debconf itself is generally quick, and the confmodule # will generally be listening for a reply when we try to send one; # the slow bit is waiting for the confmodule to decide to send a # command. Therefore, this is the only file descriptor we bother to # watch, which greatly simplifies our life. self.frontend.watch_debconf_fd( self.dbfilter.subout_fd, self.process_input) def process_line(self): return self.dbfilter.process_line() def wait(self): ret = self.dbfilter.wait() if ret != 0: # TODO: error message if ret != 10 self.debug("%s exited with code %d", self.command, ret) self.cleanup() self.db.shutdown() return ret def cleanup(self): pass def run_command(self, auto_process=False): self.start(auto_process=auto_process) if auto_process: self.enter_ui_loop() else: while self.process_line(): pass self.status = self.wait() return self.status def run_unfiltered(self): """This may only be called under the control of a debconf frontend.""" self.status = None prep = self.prepare(unfiltered=True) self.command = prep[0] if len(prep) > 2: env = prep[2] else: env = {} self.debug("Starting up '%s' unfiltered for %s.%s", self.command, self.__class__.__module__, self.__class__.__name__) def subprocess_setup(): os.environ['HOME'] = '/root' os.environ['LC_COLLATE'] = 'C' for key, value in env.iteritems(): os.environ[key] = value # Python installs a SIGPIPE handler by default. This is bad for # non-Python subprocesses, which need SIGPIPE set to the default # action. signal.signal(signal.SIGPIPE, signal.SIG_DFL) ret = subprocess.call(self.command, preexec_fn=subprocess_setup) if ret != 0: # TODO: error message if ret != 10 self.debug("%s exited with code %d", self.command, ret) self.cleanup() return ret def process_input(self, source, condition): if source != self.dbfilter.subout_fd: return True call_again = True if condition & DEBCONF_IO_IN: if not self.process_line(): call_again = False if (condition & DEBCONF_IO_ERR) or (condition & DEBCONF_IO_HUP): call_again = False if not call_again: # TODO cjwatson 2006-02-08: We hope this happens quickly! It # would be better to do this out-of-band somehow. self.status = self.wait() self.exit_ui_loops() self.frontend.debconffilter_done(self) return call_again # Split a string on commas, stripping surrounding whitespace, and # honouring backslash-quoting. def split_choices(self, text): textlen = len(text) index = 0 items = [] item = '' while index < textlen: if text[index] == '\\' and index + 1 < textlen: if text[index + 1] == ',' or text[index + 1] == ' ': item += text[index + 1] index += 1 elif text[index] == ',': items.append(item.strip()) item = '' else: item += text[index] index += 1 if item != '': items.append(item.strip()) return items def choices_untranslated(self, question): choices = unicode(self.db.metaget(question, 'choices-c'), 'utf-8') return self.split_choices(choices) def choices(self, question): choices = unicode(self.db.metaget(question, 'choices'), 'utf-8') return self.split_choices(choices) def choices_display_map(self, question): """Returns a mapping from displayed (translated) choices to database (untranslated) choices. It can be used both ways, since both choices and the untranslated choices are sequences without duplication. """ _map = {} choices = self.choices(question) choices_c = self.choices_untranslated(question) for i in range(len(choices)): _map[choices[i]] = choices_c[i] return _map def description(self, question): return unicode(self.db.metaget(question, 'description'), 'utf-8') def extended_description(self, question): return unicode(self.db.metaget(question, 'extended_description'), 'utf-8') def translate_title(self, question): # TODO cjwatson 2006-07-06: broken, needs to be done in frontend widget = self.glade.get_widget('dialog') widget.set_title(self.description(question)) def translate_labels(self, questions): # TODO cjwatson 2006-07-06: broken, needs to be done in frontend for label, question in questions.items(): widget = self.glade.get_widget(label) widget.set_label(self.description(question)) def translate_to_c(self, question, value): choices = self.choices(question) choices_c = self.choices_untranslated(question) for i in range(len(choices)): if choices[i] == value: return choices_c[i] raise ValueError, value def value_index(self, question): value = self.db.get(question) choices_c = self.choices_untranslated(question) for i in range(len(choices_c)): if choices_c[i] == value: return i raise ValueError, value def escape(self, text): escaped = text.replace('\\', '\\\\').replace('\n', '\\n') return re.sub(r'(\s)', r'\\\1', escaped) def preseed(self, name, value, seen=True, escape=False): if escape: value = self.escape(value) value = value.encode("UTF-8", "ignore") if escape: self.db.capb('escape') try: self.db.set(name, value) except debconf.DebconfError: self.db.register('debian-installer/dummy', name) self.db.set(name, value) self.db.subst(name, 'ID', name) if escape: self.db.capb('') if seen: self.db.fset(name, 'seen', 'true') def preseed_bool(self, name, value, seen=True): if value: self.preseed(name, 'true', seen) else: self.preseed(name, 'false', seen) def preseed_as_c(self, name, value, seen=True): self.preseed(name, self.translate_to_c(name, value), seen) # Cause the frontend to enter a recursive main loop. Will block until # something causes the frontend to exit that loop (probably by calling # exit_ui_loops). def enter_ui_loop(self): self.ui_loop_level += 1 self.frontend.run_main_loop() # Exit any recursive main loops we caused the frontend to enter. def exit_ui_loops(self): while self.ui_loop_level > 0: self.ui_loop_level -= 1 self.frontend.quit_main_loop() # User selected OK, Forward, or similar. Subclasses should override this # to send user-entered information back to debconf (perhaps using # preseed()) and return control to the filtered command. After this # point, self.done is set so no further user interaction should take # place unless an error resets it. def ok_handler(self): self.succeeded = True self.done = True self.exit_ui_loops() # User selected Cancel, Back, or similar. Subclasses should override # this to send user-entered information back to debconf (perhaps using # preseed()) and return control to the filtered command. After this # point, self.done is set so no further user interaction should take # place unless an error resets it. def cancel_handler(self): self.succeeded = False self.done = True self.exit_ui_loops() def error(self, priority, question): self.succeeded = False self.done = False return True def run(self, priority, question): self.current_question = question if not self.done: self.succeeded = False self.enter_ui_loop() return self.succeeded
class FilteredCommand(object): def __init__(self, frontend, db=None): self.frontend = frontend # db does not normally need to be specified. self.db = db self.done = False self.current_question = None self.succeeded = False @classmethod def debug(self, fmt, *args): if ('UBIQUITY_DEBUG_CORE' in os.environ and os.environ['UBIQUITY_DEBUG_CORE'] == '1'): message = fmt % args syslog.syslog(syslog.LOG_DEBUG, '%s: %s' % (PACKAGE, message)) def start(self, auto_process=False): self.status = None self.db = DebconfCommunicator(PACKAGE, cloexec=True) prep = self.prepare() self.command = ['log-output', '-t', 'ubiquity', '--pass-stdout'] if isinstance(prep[0], types.StringTypes): self.command.append(prep[0]) else: self.command.extend(prep[0]) question_patterns = prep[1] if len(prep) > 2: env = prep[2] else: env = {} self.ui_loop_level = 0 self.debug("Starting up '%s' for %s.%s", self.command, self.__class__.__module__, self.__class__.__name__) self.debug("Watching for question patterns %s", ', '.join(question_patterns)) widgets = {} for pattern in question_patterns: widgets[pattern] = self self.dbfilter = DebconfFilter(self.db, widgets) # TODO: Set as unseen all questions that we're going to ask. if auto_process: self.dbfilter.start(self.command, blocking=False, extra_env=env) # Clearly, this isn't enough for full non-blocking operation. # However, debconf itself is generally quick, and the confmodule # will generally be listening for a reply when we try to send # one; the slow bit is waiting for the confmodule to decide to # send a command. Therefore, this is the only file descriptor we # bother to watch, which greatly simplifies our life. self.frontend.watch_debconf_fd( self.dbfilter.subout_fd, self.process_input) else: self.dbfilter.start(self.command, blocking=True, extra_env=env) def process_line(self): return self.dbfilter.process_line() def wait(self): ret = self.dbfilter.wait() if ret != 0: # TODO: error message if ret != 10 self.debug("%s exited with code %d", self.command, ret) self.cleanup() self.db.shutdown() return ret def cleanup(self): pass def run_command(self, auto_process=False): # TODO cjwatson 2006-02-25: Hack to allow _apply functions to be run # from within the debconffiltered Config class. if self.frontend is None: prep = self.prepare() self.command = ['log-output', '-t', 'ubiquity', '--pass-stdout'] if isinstance(prep[0], types.StringTypes): self.command.append(prep[0]) else: self.command.extend(prep[0]) self.debug("Starting up '%s' for %s.%s", self.command, self.__class__.__module__, self.__class__.__name__) if len(prep) > 2: env = prep[2] else: env = {} def subprocess_setup(): for key, value in env.iteritems(): os.environ[key] = value os.environ['LC_COLLATE'] = 'C' # Python installs a SIGPIPE handler by default. This is bad # for non-Python subprocesses, which need SIGPIPE set to the # default action or else they won't notice if the # debconffilter dies. signal.signal(signal.SIGPIPE, signal.SIG_DFL) ret = subprocess.call(self.command, preexec_fn=subprocess_setup) if ret != 0: self.debug("%s exited with code %d", self.command, ret) return ret self.start(auto_process=auto_process) if auto_process: self.enter_ui_loop() else: while self.process_line(): pass self.status = self.wait() return self.status def process_input(self, source, condition): if source != self.dbfilter.subout_fd: return True call_again = True if condition & DEBCONF_IO_IN: if not self.process_line(): call_again = False if (condition & DEBCONF_IO_ERR) or (condition & DEBCONF_IO_HUP): call_again = False if not call_again: # TODO cjwatson 2006-02-08: We hope this happens quickly! It # would be better to do this out-of-band somehow. self.status = self.wait() self.exit_ui_loops() self.frontend.debconffilter_done(self) return call_again # Split a string on commas, stripping surrounding whitespace, and # honouring backslash-quoting. def split_choices(self, text): textlen = len(text) index = 0 items = [] item = '' while index < textlen: if text[index] == '\\' and index + 1 < textlen: if text[index + 1] == ',' or text[index + 1] == ' ': item += text[index + 1] index += 1 elif text[index] == ',': items.append(item.strip()) item = '' else: item += text[index] index += 1 if item != '': items.append(item.strip()) return items def choices_untranslated(self, question): choices = unicode(self.db.metaget(question, 'choices-c'), 'utf-8', 'replace') return self.split_choices(choices) def choices(self, question): choices = unicode(self.db.metaget(question, 'choices'), 'utf-8', 'replace') return self.split_choices(choices) def choices_display_map(self, question): """Returns a mapping from displayed (translated) choices to database (untranslated) choices. It can be used both ways, since both choices and the untranslated choices are sequences without duplication. """ _map = {} choices = self.choices(question) choices_c = self.choices_untranslated(question) for i in range(len(choices)): _map[choices[i]] = choices_c[i] return _map def description(self, question): return unicode(self.db.metaget(question, 'description'), 'utf-8', 'replace') def extended_description(self, question): self.db.capb('escape') data = unicode(self.db.metaget(question, 'extended_description'), 'utf-8', 'replace') self.db.capb('') return data def translate_to_c(self, question, value): choices = self.choices(question) choices_c = self.choices_untranslated(question) for i in range(len(choices)): if choices[i] == value: return choices_c[i] raise ValueError, value def value_index(self, question): value = self.db.get(question) choices_c = self.choices_untranslated(question) for i in range(len(choices_c)): if choices_c[i] == value: return i raise ValueError, value def escape(self, text): escaped = text.replace('\\', '\\\\').replace('\n', '\\n') return re.sub(r'(\s)', r'\\\1', escaped) def preseed(self, name, value, seen=True, escape=False): if escape: value = self.escape(value) value = value.encode("UTF-8", "ignore") if escape: self.db.capb('escape') try: self.db.set(name, value) except debconf.DebconfError: self.db.register('debian-installer/dummy', name) self.db.set(name, value) self.db.subst(name, 'ID', name) if escape: self.db.capb('') if seen: self.db.fset(name, 'seen', 'true') def preseed_as_c(self, name, value, seen=True): self.preseed(name, self.translate_to_c(name, value), seen) # Cause the frontend to enter a recursive main loop. Will block until # something causes the frontend to exit that loop (probably by calling # exit_ui_loops). def enter_ui_loop(self): self.ui_loop_level += 1 self.frontend.run_main_loop() # Exit any recursive main loops we caused the frontend to enter. def exit_ui_loops(self): while self.ui_loop_level > 0: self.ui_loop_level -= 1 self.frontend.quit_main_loop() # User selected OK, Forward, or similar. Subclasses should override this # to send user-entered information back to debconf (perhaps using # preseed()) and return control to the filtered command. After this # point, self.done is set so no further user interaction should take # place unless an error resets it. def ok_handler(self): self.succeeded = True self.done = True self.exit_ui_loops() # User selected Cancel, Back, or similar. Subclasses should override # this to send user-entered information back to debconf (perhaps using # preseed()) and return control to the filtered command. After this # point, self.done is set so no further user interaction should take # place unless an error resets it. def cancel_handler(self): self.succeeded = False self.done = True self.exit_ui_loops() def error(self, priority, question): self.succeeded = False self.done = False return True # The confmodule asked a question; process it. Subclasses only need to # override this if they want to do something special like updating their # UI depending on what questions were asked. def run(self, priority, question): self.current_question = question if not self.done: self.succeeded = False self.enter_ui_loop() return self.succeeded # Default progress bar handling: just pass it through to the frontend. def progress_start(self, progress_min, progress_max, progress_title): ret = self.frontend.debconf_progress_start( progress_min, progress_max, self.description(progress_title)) self.frontend.refresh() return ret def progress_set(self, progress_title, progress_val): ret = self.frontend.debconf_progress_set(progress_val) self.frontend.refresh() return ret def progress_step(self, progress_title, progress_inc): ret = self.frontend.debconf_progress_step(progress_inc) self.frontend.refresh() return ret def progress_info(self, progress_title, progress_info): try: ret = self.frontend.debconf_progress_info( self.description(progress_info)) self.frontend.refresh() return ret except debconf.DebconfError: # ignore unknown info templates return True def progress_stop(self, progress_title): ret = self.frontend.debconf_progress_stop() self.frontend.refresh() return ret def progress_region(self, progress_title, progress_region_start, progress_region_end): self.frontend.debconf_progress_region(progress_region_start, progress_region_end)