also look for bootparams in /lib/live/mount/medium [Closes: issue1239]
[grml-x.git] / grml-x
1 #!/usr/bin/python
2 # -*- coding: utf-8 -*-
3 # vim: set et ts=4:
4
5 # Filename:      grml-x
6 # Purpose:       wrapper for startx on grml [providing new xconfiguration tool]
7 # Authors:       grml-team (grml.org), (c) Christian Hofstaedtler <ch@grml.org>
8 # Bug-Reports:   see http://grml.org/bugs/
9 # License:       This file is licensed under the GPL v2.
10 ###############################################################################
11
12 # Requires python 2.6 or, possibly, a newer version of python 2.X.
13
14 import fileinput, os, subprocess, sys, tempfile, time, traceback
15 from optparse import OptionParser
16
17 class Section(object):
18     def __init__(self, name, identifier, data):
19         self.name = name
20         self.identifer = identifier
21         self.data = data
22         self.subsect = ""
23     def __str__(self):
24         s = "Section \"%s\"\n\tIdentifier \"%s\"\n" % (self.name, self.identifer)
25         for k in self.data:
26             v = self.data[k]
27             if isinstance(v, list):
28                 v = '" "'.join(v)
29             elif not isinstance(v, basestring): # int, others
30                 v = str(v)
31             elif '-' in v: # sync range
32                 pass
33             else:
34                 v = '"%s"' % v
35             s += "\t%s %s\n" % (k, v)
36         s += self.subsect
37         s += 'EndSection\n'
38         return s
39
40 def get_monitor_section(options, force):
41     if not options.hsync and not options.vsync and not force:
42         return None
43     d = {}
44     d['HorizSync'] = options.hsync or '28.0 - 96.0'
45     d['VertRefresh'] = options.vsync or '50.0 - 60.0'
46     return Section('Monitor', 'Monitor0', d)
47
48 def get_device_section(options):
49     if not options.module:
50         return None
51     d = {}
52     d['Driver'] = options.module
53     d['VendorName'] = 'All'
54     d['BoardName'] = 'All'
55     return Section('Device', 'Card0', d)
56
57 def build_bootparams():
58     lines = []
59     def walk_bootparams_path(p):
60         try:
61             if not os.path.exists(p): return
62             for root, dirs, files in os.walk(p):
63                 for name in files:
64                     f = open(os.path.join(root, name))
65                     lines.extend(f.readlines())
66                     f.close()
67         except:
68             print 'W: Error while getting bootparams from %s' % p
69     f = open('/proc/cmdline')
70     lines.append(f.readline())
71     f.close()
72     walk_bootparams_path('/cdrom/bootparams')
73     walk_bootparams_path('/live/image/bootparams')
74     walk_bootparams_path('/lib/live/mount/medium/bootparams')
75     params = {}
76     for p in ' '.join(lines).split(' '):
77         if '=' in p:
78             (k,v) = p.split('=', 1)
79             params[k] = v
80         else:
81             params[p] = True
82     return params
83
84 def detect_qemu():
85     f = open('/proc/cpuinfo')
86     x = ''.join(f.readlines())
87     f.close()
88     if 'QEMU' in x: return True
89     return False
90
91 def get_program_output(args):
92     p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True)
93     return p.communicate()[0]
94
95 def run_program(args):
96     subprocess.Popen(args, close_fds=True).wait()
97
98 def which(program):
99     def is_exe(fpath):
100         return os.path.exists(fpath) and os.access(fpath, os.X_OK)
101
102     fpath, fname = os.path.split(program)
103     if fpath:
104         if is_exe(program):
105             return program
106     else:
107         for path in os.environ["PATH"].split(os.pathsep):
108             exe_file = os.path.join(path, program)
109             if is_exe(exe_file):
110                 return exe_file
111
112     return None
113
114 XORG_CONF_HEADER = "# Automatically generated by grml-x."
115 def check_old_xorg_conf(filename, overwrite):
116     # True: no problem, we can create/overwrite the config file
117     # False: pre-existing config file, and we are not to overwrite it
118     if overwrite: return True
119     if not os.path.exists(filename): return True
120     try:
121         f = file(filename, 'r')
122         lines = f.readlines()
123         f.close()
124         return (not XORG_CONF_HEADER in lines)
125     except IOError:
126         return False
127
128 parser = OptionParser(usage="usage: %prog [options] [window-manager]")
129 parser.add_option("--nostart", action="store_false", dest="start_server", default=True,
130                 help="Don't start X server")
131 parser.add_option("--display", action="store", type="string", dest="display",
132                 help="Start X server on display DISPLAY")
133 parser.add_option("--hsync", action="store", type="string", dest="hsync",
134                 help="Force writing a HorizSync range")
135 parser.add_option("--vsync", action="store", type="string", dest="vsync",
136                 help="Force writing a VertRefresh range")
137 parser.add_option("--mode", action="store", type="string", dest="mode",
138                 help="Force a specific resolution")
139 parser.add_option("--module", action="store", type="string", dest="module",
140                 help="Force driver MODULE instead of Xorg autodetection")
141 parser.add_option("-o", action="store", type="string", dest="xorg_conf", default="/etc/X11/xorg.conf",
142                 help="Specify alternate xorg.conf file [default: %default]")
143 parser.add_option("-f", "--force", action="store_true", dest="overwrite", default=False,
144                 help="Overwrite xorg.conf if it exists [default: %default]")
145
146 def main():
147     (options, args) = parser.parse_args()
148     bootparams = build_bootparams()
149
150     if os.getuid() == 0 and options.start_server:
151         print "W: running as root is unsupported and may not work."
152         time.sleep(1)
153
154     if not check_old_xorg_conf(options.xorg_conf, options.overwrite):
155         print "E: Not overwriting existing %r without --force." % options.xorg_conf
156         print "I: If you previously ran grml-x, use startx /usr/bin/x-window-manager"
157         return 1
158
159     if 'xmode' in bootparams and not options.mode: options.mode = bootparams['xmode']
160     if 'xmodule' in bootparams and not options.module: options.module = bootparams['xmodule']
161
162     force_monitor = False
163     # cirrus driver for QEMU doesn't do 1024x768 without HorizSync set
164     if detect_qemu(): force_monitor = True
165
166     monitor = get_monitor_section(options, force_monitor)
167     device = get_device_section(options)
168
169     # build Screen section ourselves
170     d = {}
171     if monitor: d['Monitor'] = monitor.identifer
172     if device: d['Device'] = device.identifer
173     screen = Section('Screen', 'Screen0', d)
174     if options.mode:
175         d['DefaultColorDepth'] = 16
176         for depth in [8, 15, 16, 24, 32]:
177             screen.subsect += "SubSection \"Display\"\n\tDepth %d\n\tModes \"%s\"\t\nEndSubSection\n" % (depth, options.mode)
178
179     xinitrc = '~/.xinitrc'
180     if 'XINITRC' in os.environ: xinitrc = os.environ['XINITRC']
181     xinitrc = os.path.expanduser(xinitrc)
182
183     window_manager = 'x-window-manager'
184     if len(args) == 1: window_manager = args[0]
185     window_manager_path = which(window_manager)
186     if not window_manager_path:
187         print "E: Cannot find window manager %r, aborting." % window_manager
188         return 2
189
190     wm_exec = "exec %s\n" % window_manager_path
191     if not os.path.exists(xinitrc):
192         f = open(xinitrc, 'w')
193         f.write("#!/bin/sh\n")
194         f.write(wm_exec)
195         f.close()
196     else:
197         f = open(xinitrc, 'r')
198         lines = f.readlines()
199         f.close()
200         f = open(xinitrc, 'w')
201         for line in lines:
202             if line.strip().startswith('exec '): line = wm_exec
203             f.write(line)
204         os.fchmod(f.fileno(), 0750)
205         f.close()
206
207     # write new config
208     if monitor or device or len(screen.data) > 0 or screen.subsect != '':
209         try:
210             f = tempfile.NamedTemporaryFile(delete=False)
211             f.write(XORG_CONF_HEADER + "\n")
212             f.write("# DO NOT MODIFY, YOUR CHANGES WILL BE LOST - OR REMOVE ALL HEADER LINES\n")
213             f.write("# See man xorg.conf or /etc/X11/xorg.conf.example for more\n")
214             if monitor: f.write(str(monitor))
215             if device: f.write(str(device))
216             f.write(str(screen))
217             f.flush()
218             os.fchmod(f.fileno(), 0644)
219             run_program(['sudo', 'mv', '-f', f.name, options.xorg_conf])
220         finally:
221             f.close()
222
223     if options.start_server:
224         startx = ['startx', xinitrc, '--']
225         if options.display: startx.append(':' + options.display)
226         print "Starting X: %r" % startx
227         run_program(startx)
228
229     return 0
230
231 if __name__ == '__main__':
232     rc = 1
233     try:
234         rc = main()
235     except Exception:
236         print "E: Exception: ",
237         traceback.print_exc()
238     sys.exit(1)
239