diff options
Diffstat (limited to 'build/scripts-2.7/dogtail-run-headless-next')
-rwxr-xr-x | build/scripts-2.7/dogtail-run-headless-next | 319 |
1 files changed, 319 insertions, 0 deletions
diff --git a/build/scripts-2.7/dogtail-run-headless-next b/build/scripts-2.7/dogtail-run-headless-next new file mode 100755 index 00000000000..ae481cf1c44 --- /dev/null +++ b/build/scripts-2.7/dogtail-run-headless-next @@ -0,0 +1,319 @@ +#!/usr/bin/python +descr = """ +Unlike the original headless script this will make use of an Display Manager +(DM - currently gdm) to handle starting the X server and user session. It's motivated +by changes related to systemd - that disallows running a gnome session from an +environment spawned by 'su'. The original headless will not work in these cases +anymore on systemd systems + +Instead this script uses the AutoLogin feature of the DM, so that when it starts DM's +service the session will login for particular user at once. It then uses the +environment properties from the new session and runs the target script inthere. + +Will work with distros where 'service gdm/kdm start/stop' takes an effect, and quite +likely only on systemd systems that use systemd-logind service. + +Even if you are still able to use dogtail-run-headless in your usecase, you might +consider switching to this script - as making use of DM is likely to be more reliable +and less buggy compared to headless itself taking care of everything. +""" + +drop_overview = '''from dogtail.utils import absoluteMotion, keyPress +absoluteMotion(100,100) +keyPress('esc')''' + +import argparse +import sys +import os +import glob +import subprocess +import time +import ConfigParser +import shutil +import re +from dogtail.sessions import Script + +preserve_envs = ['PYTHONPATH', 'TEST'] + + +def getSessionEnvironment(sessionBinary): + + def isSessionProcess(fileName): + try: + if os.path.realpath(fileName + 'exe') != ('/usr/bin/plasma-desktop' + if sessionBinary.split('/')[-1] == 'startkde' + else sessionBinary): + return False + except OSError: + return False + pid = fileName.split('/')[2] + if pid == 'self' or pid == str(os.getpid()): + return False + return True + + def getEnvDict(fileName): + try: + envString = open(fileName, 'r').read() + except IOError: + return {} + envItems = envString.split('\x00') + envDict = {} + for item in envItems: + if not '=' in item: + continue + k, v = item.split('=', 1) + envDict[k] = v + return envDict + + def copyVars(envDict): + '''Copy a couple of old variables we want to preserve''' + for env in preserve_envs: + if os.environ.has_key(env): + envDict[env] = os.environ[env] + return envDict + + envDict = False + for path in glob.glob('/proc/*/'): + if not isSessionProcess(path): + continue + envFile = path + 'environ' + envDict = getEnvDict(envFile) + if not envDict: + raise RuntimeError("Can't find our environment!") + return copyVars(envDict) + + +def execCodeWithEnv(code, env=None): + with open("/tmp/execcode.dogtail", "w") as text_file: + text_file.write(code) + subprocess.Popen('python /tmp/execcode.dogtail'.split(), + env=(os.environ if env is None else env)).wait() + + +class DisplayManagerSession(object): + + gdm_config = '/etc/gdm/custom.conf' + kdm_config = '/etc/kde/kdm/kdmrc' + gdm_options = {'section': 'daemon', 'enable': + 'AutomaticLoginEnable', 'user': 'AutomaticLogin'} + kdm_options = {'section': 'X-:0-Core', 'enable': + 'AutoLoginEnable', 'user': 'AutoLoginUser'} + scriptDelay = 20 + user = 'test' + + def isProcessRunning(self, process): + '''Gives true if process can be greped out of full ps dump ''' + s = subprocess.Popen(["ps", "axw"], stdout=subprocess.PIPE) + for x in s.stdout: + if re.search(process, x): + return True + return False + + def waitForProcess(self, process, invert=False): + '''Waits until a process appears''' + while self.isProcessRunning(process) is invert: + time.sleep(1) + + def __init__(self, dm='gdm', session='gnome', session_binary='gnome-shell', user=None): + self.session_binary = session_binary + self.session = session + self.accountfile = '/var/lib/AccountsService/users/%s' % self.user + if user is not None: + self.user = user + if dm == 'gdm': + self.tmp_file = '/tmp/%s' % os.path.basename(self.gdm_config) + self.options = self.gdm_options + self.config = self.gdm_config + elif dm == 'kdm': + self.tmp_file = '/tmp/%s' % os.path.basename(self.kdm_config) + self.options = self.kdm_options + self.config = self.kdm_config + self.dm = dm + + def setup(self, restore=False): + shutil.copy(self.config, self.tmp_file) + config = ConfigParser.SafeConfigParser() + config.optionxform = str + config.read(self.tmp_file) + if not restore: + config.set(self.options['section'], self.options['enable'], 'true') + config.set( + self.options['section'], self.options['user'], self.user) + else: + config.remove_option( + self.options['section'], self.options['enable']) + config.remove_option(self.options['section'], self.options['user']) + output = open(self.tmp_file, 'w') + config.write(output) + output.flush() + subprocess.Popen('sudo mv -f %s %s' % + (self.tmp_file, self.config), shell=True).wait() + if not restore: + if 'kwin' in self.session_binary: + try: + os.makedirs(os.getenv('HOME') + '/.kde/env/') + except: + pass + subprocess.Popen( + 'echo "export QT_ACCESSIBILITY=1" > ~/.kde/env/qt-at-spi.sh', shell=True).wait() + if self.dm == 'gdm': + need_restart = False + tempfile = '/tmp/%s_headless' % self.user + subprocess.Popen('cp -f %s %s' % (self.accountfile, tempfile), shell=True).wait() + account_config = ConfigParser.SafeConfigParser() + account_config.optionxform = str + account_config.read(tempfile) + try: + saved_session = account_config.get('User', 'XSession') + if self.session is None: + if 'kde' in saved_session and 'gnome-shell' in self.session_binary: + self.session_binary = '/usr/bin/kwin' + elif 'gnome' in saved_session and 'kwin' in self.session_binary: + self.session_binary = '/usr/bin/gnome-shell' + elif saved_session != self.session: + account_config.set('User', 'XSession', self.session) + need_restart = True + except ConfigParser.NoSectionError: + account_config.add_section('User') + account_config.set('User', 'XSession', self.session if self.session else '') + account_config.set('User', 'SystemAccount', 'false') + need_restart = True + if need_restart: + output = open(tempfile, 'w') + account_config.write(output) + output.flush() + subprocess.Popen('sudo mv -f %s %s' % (tempfile, self.accountfile), shell=True).wait() + time.sleep(1) + os.system('sudo service accounts-daemon restart') + time.sleep(6) # prevent a possible race condition + os.system('sudo service systemd-logind restart') + time.sleep(6) # these are fast, but we still need to make sure no races happen + else: + subprocess.Popen('sudo rm -f %s' % tempfile, shell=True).wait() + elif self.dm == 'kdm': + if self.session is not None: + subprocess.Popen( + 'echo "[Desktop]\nSession=%s" > /home/%s/.dmrc' % (self.session, self.user), shell=True).wait() + + def start(self, restart=False): + if restart: + subprocess.Popen(('sudo service %s stop' % (self.dm)).split()) + time.sleep(0.5) + subprocess.Popen(('sudo service %s start' % (self.dm)).split()) + self.waitForProcess(self.session_binary.split('/')[-1]) + # some extra time for an environment (shell/kwin) to load all resources + # etc. + if self.dm == 'kdm': + time.sleep(10) # KDE keeps loading screen on untill all is loaded + else: + time.sleep(4) # GNOME shows stuff as it appears + + def setA11y(self, enable): + subprocess.Popen('/usr/bin/gsettings set org.gnome.desktop.interface toolkit-accessibility %s' + % ('true' if enable else 'false'), shell=True, env=os.environ) +# if enable: # mouse is at 0x0 at the start - which brings in the overview +# execCodeWithEnv(drop_overview, env = os.environ) +# time.sleep(2) # time for the overview to go away + + def stop(self): + subprocess.Popen(('sudo service %s stop' % (self.dm)).split()).wait() + self.waitForProcess('/usr/bin/%s' % self.dm, invert=True) + time.sleep(3) # extra safe time + # did i.e. gnome-shell get stuck running? + if self.isProcessRunning(self.session_binary.split('/')[-1]): + print( + 'dogtail-run-headless-next: WARNING: %s still running, proceeding with kill -9' % + self.session_binary.split('/')[-1]) + subprocess.Popen( + ('sudo pkill --signal 9 %s' % (self.session_binary.split('/')[-1])).split()).wait() + time.sleep(1) + + +def parse(): + parser = argparse.ArgumentParser( + prog='$ dogtail-run-headless-next', description=descr, formatter_class=argparse.RawDescriptionHelpFormatter) + parser.add_argument('script', help="""Command to execute the script""") + parser.add_argument('--session', required=False, + help="""What session to use. Not specifying results in system default session on first login + or test user's last session with follow-up logins. + Otherwise you can set any xsession desktop file name here (i.e. 'gnome-classic', 'gnome', 'lxde' etc.). + 'kde' defaults to 'kde-plasma for legacy reasons.'""") + parser.add_argument('--session-binary', required=False, + help="""Specify an in-session ever-running binary (full path) to get the environment from. Only needed for non-gnome/kde sessions.""") + parser.add_argument('--dm', required=False, + help="""What display manager to use for spawning session. Supported are 'gdm' (default) and 'kdm'.""") + parser.add_argument('--restart', action='store_true', + help="""Restart previously running display manager session before script execution.""") + parser.add_argument('--dont-start', action='store_true', + help="""Use already running session (doesn't have to be under Display Manager)""") + parser.add_argument('--dont-kill', action='store_true', + help="""Do not kill session when script exits.""") + parser.add_argument('--disable-a11y', action='store_true', + help="""Disable accessibility technologies on script(not session) exit.""") + return parser.parse_args() + + +def main(): + args = parse() + scriptCmdList = args.script.split() + + if args.session is None or 'gnome' in args.session: + if args.session_binary is None: + args.session_binary = '/usr/bin/gnome-shell' + elif 'kde' in args.session: + if args.session_binary is None: + args.session_binary = '/usr/bin/kwin' + if args.session == 'kde': + args.session = 'kde-plasma' + else: + if args.session_binary is None: + print('dogtail-run-headless-next: Need to specify a --session-binary ever-present in the session to get env from.') + sys.exit(-1) + if args.dm == 'gdm' or args.dm is None: + dm_name = 'gdm' + elif args.dm == 'kdm': + dm_name = 'kdm' + else: + print('dogtail-run-headless-next: I do not recognize the display manager!') + sys.exit(-1) + + print('dogtail-run-headless-next: Using display manager: %s' % dm_name) + + dm = DisplayManagerSession(dm_name, args.session, args.session_binary) + + if args.dont_start is not True: + dm.setup() + dm.start(restart=args.restart) + + print('dogtail-run-headless-next: Using %s to bind to the session' % dm.session_binary) + + try: + os.environ = getSessionEnvironment(dm.session_binary) + except: + print( + 'dogtail-run-headless-next: Could not get the environment from %s process' % + dm.session_binary) + dm.stop() + dm.setup(restore=True) + sys.exit(1) + + if dm_name == 'gdm': + dm.setA11y(True) + + script = Script(scriptCmdList) + scriptPid = script.start() + print('dogtail-run-headless-next: Started the script with PID %d' % scriptPid) + exitCode = script.wait() + print('dogtail-run-headless-next: The script has finnished with return code %d' % exitCode) + + if args.disable_a11y is True: + dm.setA11y(False) + + if args.dont_kill is False: + dm.stop() + dm.setup(restore=True) + + sys.exit(exitCode) + +if __name__ == "__main__": + main() |