aboutsummaryrefslogtreecommitdiffstats
path: root/build/scripts-2.7/dogtail-run-headless-next
diff options
context:
space:
mode:
Diffstat (limited to 'build/scripts-2.7/dogtail-run-headless-next')
-rwxr-xr-xbuild/scripts-2.7/dogtail-run-headless-next319
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()