#!/usr/bin/python3 # -*- coding: utf-8 -*- # vim: set et ts=4: # Filename: grml-x # Purpose: wrapper for startx on grml [providing new xconfiguration tool] # Authors: grml-team (grml.org), (c) Christian Hofstaedtler # Bug-Reports: see http://grml.org/bugs/ # License: This file is licensed under the GPL v2. ############################################################################### import os import subprocess import sys import tempfile import time import traceback from optparse import OptionParser class Section(object): def __init__(self, name, identifier, data): self.name = name self.identifer = identifier self.data = data self.subsect = "" def __str__(self): s = 'Section "%s"\n\tIdentifier "%s"\n' % (self.name, self.identifer) for k in self.data: v = self.data[k] if isinstance(v, list): v = '" "'.join(v) elif not isinstance(v, str): # int, others v = str(v) elif "-" in v: # sync range pass else: v = '"%s"' % v s += "\t%s %s\n" % (k, v) s += self.subsect s += "EndSection\n" return s def get_monitor_section(options, force): if not options.hsync and not options.vsync and not force: return None d = {} d["HorizSync"] = options.hsync or "28.0 - 96.0" d["VertRefresh"] = options.vsync or "50.0 - 60.0" return Section("Monitor", "Monitor0", d) def get_device_section(options): if not options.module: return None d = {} d["Driver"] = options.module d["VendorName"] = "All" d["BoardName"] = "All" return Section("Device", "Card0", d) def build_bootparams(): lines = [] def walk_bootparams_path(p): try: if not os.path.exists(p): return for root, dirs, files in os.walk(p): for name in files: f = open(os.path.join(root, name)) lines.extend(f.readlines()) f.close() except Exception: print("W: Error while getting bootparams from %s" % p) f = open("/proc/cmdline") lines.append(f.readline()) f.close() walk_bootparams_path("/lib/live/mount/medium/bootparams") walk_bootparams_path("/run/live/medium/bootparams") params = {} for p in " ".join(lines).split(" "): if "=" in p: (k, v) = p.split("=", 1) params[k] = v else: params[p] = True return params def detect_qemu(): f = open("/proc/cpuinfo") x = "".join(f.readlines()) f.close() if "QEMU" in x: return True return False def get_program_output(args): p = subprocess.Popen( args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, close_fds=True ) return p.communicate()[0] def run_program(args): subprocess.Popen(args, close_fds=True).wait() def which(program): def is_exe(fpath): return os.path.exists(fpath) and os.access(fpath, os.X_OK) fpath, fname = os.path.split(program) if fpath: if is_exe(program): return program else: for path in os.environ["PATH"].split(os.pathsep): exe_file = os.path.join(path, program) if is_exe(exe_file): return exe_file return None XORG_CONF_HEADER = "# Automatically generated by grml-x.\n" def check_old_xorg_conf(filename, overwrite): # True: no problem, we can create/overwrite the config file # False: pre-existing config file, and we are not to overwrite it if overwrite: return True if not os.path.exists(filename): return True try: f = open(filename, "r") lines = f.readlines() f.close() return XORG_CONF_HEADER not in lines except IOError: return False parser = OptionParser(usage="usage: %prog [options] [window-manager]") parser.add_option( "--nostart", action="store_false", dest="start_server", default=True, help="Don't start X server", ) parser.add_option( "--display", action="store", type="string", dest="display", help="Start X server on display DISPLAY", ) parser.add_option( "--hsync", action="store", type="string", dest="hsync", help="Force writing a HorizSync range", ) parser.add_option( "--vsync", action="store", type="string", dest="vsync", help="Force writing a VertRefresh range", ) parser.add_option( "--mode", action="store", type="string", dest="mode", help="Force a specific resolution", ) parser.add_option( "--module", action="store", type="string", dest="module", help="Force driver MODULE instead of Xorg autodetection", ) parser.add_option( "-o", action="store", type="string", dest="xorg_conf", default="/etc/X11/xorg.conf", help="Specify alternate xorg.conf file [default: %default]", ) parser.add_option( "-f", "--force", action="store_true", dest="overwrite", default=False, help="Overwrite xorg.conf if it exists [default: %default]", ) def main(): (options, args) = parser.parse_args() bootparams = build_bootparams() if os.getuid() == 0 and options.start_server: print("W: running as root is unsupported and may not work.") time.sleep(1) if not check_old_xorg_conf(options.xorg_conf, options.overwrite): print("E: Not overwriting existing %r without --force." % options.xorg_conf) print("I: If you previously ran grml-x, use startx /usr/bin/x-window-manager") return 1 if "xmode" in bootparams and not options.mode: options.mode = bootparams["xmode"] if "xmodule" in bootparams and not options.module: options.module = bootparams["xmodule"] force_monitor = False # cirrus driver for QEMU doesn't do 1024x768 without HorizSync set if detect_qemu(): force_monitor = True monitor = get_monitor_section(options, force_monitor) device = get_device_section(options) # build Screen section ourselves d = {} if monitor: d["Monitor"] = monitor.identifer if device: d["Device"] = device.identifer screen = Section("Screen", "Screen0", d) if options.mode: d["DefaultColorDepth"] = 16 for depth in [8, 15, 16, 24, 32]: screen.subsect += ( 'SubSection "Display"\n\tDepth %d\n\tModes "%s"\t\nEndSubSection\n' % (depth, options.mode) ) xinitrc = "~/.xinitrc" if "XINITRC" in os.environ: xinitrc = os.environ["XINITRC"] xinitrc = os.path.expanduser(xinitrc) window_manager = "x-window-manager" if len(args) == 1: window_manager = args[0] window_manager_path = which(window_manager) if not window_manager_path: print("E: Cannot find window manager %r, aborting." % window_manager) return 2 wm_exec = "exec %s\n" % window_manager_path if not os.path.exists(xinitrc): f = open(xinitrc, "w") f.write("#!/bin/sh\n") f.write(wm_exec) f.close() else: f = open(xinitrc, "r") lines = f.readlines() f.close() f = open(xinitrc, "w") for line in lines: if line.strip().startswith("exec "): line = wm_exec f.write(line) os.fchmod(f.fileno(), 0o750) f.close() # write new config if monitor or device or len(screen.data) > 0 or screen.subsect != "": try: f = tempfile.NamedTemporaryFile(mode="w+", delete=False) f.write(XORG_CONF_HEADER) f.write( "# DO NOT MODIFY, YOUR CHANGES WILL BE LOST - OR REMOVE ALL HEADER LINES\n" ) f.write("# See man xorg.conf or /etc/X11/xorg.conf.example for more\n") if monitor: f.write(str(monitor)) if device: f.write(str(device)) f.write(str(screen)) f.flush() os.fchmod(f.fileno(), 0o644) run_program(["sudo", "mv", "-f", f.name, options.xorg_conf]) finally: f.close() if options.start_server: startx = ["startx", xinitrc, "--"] if options.display: startx.append(":" + options.display) print("Starting X: %r" % startx) run_program(startx) return 0 if __name__ == "__main__": rc = 1 try: rc = main() except Exception: print("E: Exception: ", end=" ") traceback.print_exc() sys.exit(1)