19
19
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
30
from debconffilter import DebconfFilter
31
from debconfcommunicator import DebconfCommunicator
33
menudir = '/usr/lib/oem-config/menu'
35
menu_line_re = re.compile(r'(.*?): (.*)')
38
def __init__(self, includes=None, excludes=None):
39
if 'OEM_CONFIG_DEBUG' in os.environ:
40
self.debug_enabled = True
42
self.debug_enabled = False
45
for menu in [f for f in os.listdir(menudir) if f.endswith('.mnu')]:
46
name = '.'.join(menu.split('.')[:-1])
48
# Always include the exit item. Otherwise, check includes and
51
if includes is not None and not name in includes:
53
if excludes is not None and name in excludes:
57
menufile = open(os.path.join(menudir, menu))
59
match = menu_line_re.match(line)
61
menudata[match.group(1).lower()] = match.group(2)
63
# Load any templates that come with this item.
64
templates = os.path.join(menudir, '%s.templates' % name)
65
if os.path.exists(templates):
66
if self.load_template(templates) != 0:
69
if 'extra-templates' in menudata:
70
extras = menudata['extra-templates']
71
for extra in extras.split(' '):
72
if not extra.startswith('/'):
73
extra = os.path.join(menudir, extra)
74
if self.load_template(extra) != 0:
77
# If there is a test script, check that it succeeds.
78
testscript = os.path.join(menudir, '%s.tst' % name)
79
if os.access(testscript, os.X_OK):
80
if os.spawnl(os.P_WAIT, testscript, testscript) != 0:
83
self.menus[name] = menudata
85
# If there is an Asks: field, match it against the list of
86
# question names in the debconf database.
87
if 'asks' in self.menus[name]:
88
asks_re = self.menus[name]['asks']
91
# It isn't possible to use debconf-copydb after the debconf
92
# frontend has started up, so we have to use
93
# DebconfCommunicator to talk to a separate
94
# debconf-communicate process rather than starting a proper
97
# The best fix for this mess is to make debconf-copydb treat
98
# its source database as read-only. Unfortunately, layering
99
# issues inside debconf make this difficult for the time
102
# TODO: os.popen() doesn't take a list, so we have to
103
# quote metacharacters by hand. Once we're entirely
104
# comfortable with relying on Python 2.4, we can use
105
# subprocess.call() instead.
106
asks_re = re.sub(r'\W', r'\\\g<0>', asks_re)
107
for line in os.popen(
108
'debconf-copydb configdb pipe' +
109
' --config=Name:pipe --config=Driver:Pipe' +
110
' --config=InFd:none --pattern=%s' % asks_re):
111
line = line.rstrip('\n')
112
if line.startswith('Name: '):
113
asks.append(line[6:])
114
self.menus[name]['asks-questions'] = asks
116
db = DebconfCommunicator('oem-config')
118
for name in self.menus:
119
self.menus[name]['description'] = \
120
db.metaget('oem-config/menu/%s' % name, 'description')
123
for glade in [f for f in os.listdir(menudir) if f.endswith('.glade')]:
124
name = '.'.join(glade.split('.')[:-1])
125
self.glades[name] = os.path.join(menudir, glade)
128
for step in [f for f in os.listdir(menudir) if f.endswith('.py')]:
129
name = '.'.join(step.split('.')[:-1])
130
mod = getattr(__import__('menu.%s' % name), name)
131
if hasattr(mod, 'stepname'):
132
stepmethod = getattr(mod, mod.stepname)
133
self.steps[name] = stepmethod(self.glades[name])
136
for name in self.menus:
137
if name in self.steps:
138
self.widgets[self.menus[name]['asks']] = self.steps[name]
142
# Get the list of supported locales.
143
self.supported_locales = {}
144
for line in open('/usr/share/i18n/SUPPORTED'):
145
(locale, charset) = line.split(None, 1)
146
self.supported_locales[locale] = charset
148
def debug(self, message):
149
if self.debug_enabled:
150
print >>sys.stderr, message
152
def load_template(self, template):
153
return os.spawnlp(os.P_WAIT, 'debconf-loadtemplate',
154
'debconf-loadtemplate', 'oem-config', template)
156
# Get a list of the menu items, sorted by their Order: fields.
157
def get_menu_items(self):
159
return cmp(int(self.menus[x]['order']),
160
int(self.menus[y]['order']))
162
items = self.menus.keys()
163
items.sort(menu_sort)
25
def __init__(self, frontend_name=None):
26
if frontend_name is None:
27
frontend_names = ['gtk-ui']
29
frontend_names = [frontend_name]
30
mod = __import__('frontend', globals(), locals(), frontend_names)
31
for f in frontend_names:
36
raise AttributeError, ('No frontend available; tried %s' %
37
', '.join(frontend_names))
38
self.frontend = ui.Frontend()
167
# Get initial language.
168
db = DebconfCommunicator('oem-config')
169
language = db.get('debian-installer/locale')
170
if language not in self.supported_locales:
171
language = db.get('debian-installer/fallbacklocale')
173
self.debug("oem-config: LANG=%s" % language)
174
os.environ['LANG'] = language
175
os.environ['LANGUAGE'] = language
177
# LANGUAGE just confuses matters, so unset it.
178
if 'LANGUAGE' in os.environ:
179
del os.environ['LANGUAGE']
180
language_changed = False
183
items = self.get_menu_items()
185
while index >= 0 and index < len(items):
187
self.debug("oem-config: Running menu item %s" % item)
190
if language != os.environ['LANG']:
191
self.debug("oem-config: LANG=%s" % language)
192
os.environ['LANG'] = language
193
os.environ['LANGUAGE'] = language
194
language_changed = True
196
# LANGUAGE just confuses matters, so unset it.
197
if 'LANGUAGE' in os.environ:
198
del os.environ['LANGUAGE']
200
db = DebconfCommunicator('oem-config')
201
debconffilter = DebconfFilter(db, self.widgets)
204
# The language has just been changed, so we must be about to
205
# re-run localechooser. Stop localechooser from thinking
206
# that the change of language (which will be an incomplete
207
# locale) indicates preseeding.
208
db.set('debian-installer/locale', '')
209
language_changed = False
211
# Hack to allow a menu item to repeat on backup as long as the
212
# value of any one of a named set of questions has changed. This
213
# allows the locale question to back up when the language
214
# changes and start a new debconf frontend, while still backing
215
# up normally if the user cancels.
216
if 'repeat-if-changed' in self.menus[item]:
218
for name in self.menus[item]['repeat-if-changed'].split():
219
oldrepeat[name] = db.get(name)
221
# Set as unseen all questions that we're going to ask.
222
if 'asks-questions' in self.menus[item]:
223
for name in self.menus[item]['asks-questions']:
224
db.fset(name, 'seen', 'false')
226
if item in self.steps:
227
self.steps[item].prepare(db)
229
# Run the menu item through a debconf filter, which may display
230
# custom widgets as required.
231
itempath = os.path.join(menudir, item)
232
ret = debconffilter.run(itempath)
234
language = db.get('debian-installer/locale')
235
if language not in self.supported_locales:
236
language = db.get('debian-installer/fallbacklocale')
238
if (ret / 256) == 10:
239
if 'repeat-if-changed' in self.menus[item]:
240
for name in self.menus[item]['repeat-if-changed'].split():
241
if oldrepeat[name] != db.get(name):
250
# TODO: We should pop up a more visible error message here,
251
# but it's too late to add UI for that. For now, we continue
252
# because oem-config really wants to finish so that you get
253
# a non-system user created.
254
print >>sys.stderr, "Menu item %s exited %d" % (item, ret)
258
# Did this menu item finish the configuration process?
259
if ('exit-menu' in self.menus[item] and
260
self.menus[item]['exit-menu'] == 'true'):
41
return self.frontend.run()
270
43
if __name__ == '__main__':
271
44
parser = optparse.OptionParser()
272
parser.add_option('-i', '--include', action='append', metavar='ITEM',
273
help="Display this menu item.")
274
parser.add_option('-e', '--exclude', action='append', metavar='ITEM',
275
help="Don't display this menu item.")
45
parser.add_option('-f', '--frontend', metavar='FRONTEND',
46
help="Use the given frontend (gtk-ui).")
47
parser.set_defaults(frontend=None)
276
48
(options, args) = parser.parse_args()
278
wizard = Wizard(includes=options.include, excludes=options.exclude)
50
wizard = Wizard(frontend_name=options.frontend)
279
51
sys.exit(wizard.run())