aboutsummaryrefslogtreecommitdiffstats
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/dogtail-detect-session66
-rw-r--r--scripts/dogtail-logout28
-rw-r--r--scripts/dogtail-run-headless79
-rw-r--r--scripts/dogtail-run-headless-next319
4 files changed, 492 insertions, 0 deletions
diff --git a/scripts/dogtail-detect-session b/scripts/dogtail-detect-session
new file mode 100644
index 00000000000..8160201361e
--- /dev/null
+++ b/scripts/dogtail-detect-session
@@ -0,0 +1,66 @@
+#!/usr/bin/env python
+"""
+dogtail-detect session
+
+This script checks for some main pieces of a running GNOME session,
+specifically gnome-panel and metacity.
+
+It checks to see that the gnome-panel node has at least some child nodes.
+For example, the main gnome-panel node by default has two direct descendents:
+the top panel, and the bottom panel.
+Metacity's existence is also checked. However, metacity currently never has
+any child nodes.
+
+If a proper session is running, the scripts exits with a status of 0.
+If no session is found, a non-zero exit code is returned.
+
+Author: Zack Cerza <zcerza@redhat.com>
+"""
+
+__author__ = "Zack Cerza <zcerza@redhat.com>"
+
+from dogtail.procedural import *
+import sys
+
+
+def GNOME():
+ """
+ "Is an accessibility-enabled GNOME session running?"
+ """
+ running = False
+ try:
+ assert focus.desktop
+ assert focus.desktop.children
+
+ focus.application('gnome-panel')
+ assert focus.application.children
+
+ focus.application('metacity')
+ print focus.application.node
+ assert focus.application.node
+ running = True
+ print "GNOME Session detected."
+ except AttributeError or AssertionError or FocusError:
+ print "ERROR: No session found."
+ return running
+
+
+def KDE():
+ """
+ "Is an accessibility-enabled KDE session running?"
+ """
+ running = False
+ return running
+
+
+def JustSomeApps():
+ """
+ "Is at least one accessibility-enabled application running?"
+ """
+ assert focus.desktop
+ assert focus.desktop.children
+
+if GNOME() or KDE() or JustSomeApps():
+ sys.exit()
+else:
+ sys.exit(1)
diff --git a/scripts/dogtail-logout b/scripts/dogtail-logout
new file mode 100644
index 00000000000..c76a4519e08
--- /dev/null
+++ b/scripts/dogtail-logout
@@ -0,0 +1,28 @@
+#!/usr/bin/python
+# Logs out the full gnome session. Be sure to have your documents saved, as running
+# may cause loosing the changes, or it may halt the logout process.
+
+from dogtail.tree import *
+from dogtail.rawinput import pressKey
+from time import sleep
+import getpass
+
+# A gnome-shell object
+shell = root.application('gnome-shell')
+# Click onto a super menu label that we find under the g-s top panel object.
+# We need these indexes as g-s a11y support is a wee bit messy.
+shell[0][1][2].child(getpass.getuser(), roleName='label').click()
+# We can child this all the way down from the app as there's no other Log
+# Out... label
+shell.child('Log Out...', roleName='label').click()
+# This takes care of the 60 second dialog.
+# Sometimes a dialog warning about unsaved work in gedit etc. pops out, but that has the same
+# push button in which case this will take care of that dialog. If another dialog pops-out
+# in the affected application however, that might put the logout process on hold again. Unfortunatelly
+# we cannot do anything about that with dotail at that point as a11y registry got disabled already
+# by the logout process.
+shell[0][1].child(roleName='dialog', recursive=False).child(
+ 'Log Out', roleName='push button').click()
+
+# Give the session some time to end before we kill it.
+sleep(10)
diff --git a/scripts/dogtail-run-headless b/scripts/dogtail-run-headless
new file mode 100644
index 00000000000..edab53ddeed
--- /dev/null
+++ b/scripts/dogtail-run-headless
@@ -0,0 +1,79 @@
+#!/usr/bin/python
+"""
+dogtail-run-headless
+
+This script runs a session within an X server, allowing dogtail scripts to be
+run on systems with no graphic cards, among other things.
+
+Scripts are run in the current directory. After they are finished, dogtail can
+optionally log out of the session, which will also termninate the X server.
+"""
+
+import optparse
+from dogtail import sessions
+import sys
+import os.path
+
+
+def findXServers(path="/usr/bin"):
+ l = [os.path.join(path, f) for f in os.listdir(path) if f[0] == 'X']
+ s = set(os.path.realpath(p) for p in l)
+ return list(s)
+
+
+def parse():
+ yesno = ('y', 'n')
+ sessions = ("GNOME", "KDE")
+ usage = "usage: %prog: [options] {script [args]}"
+ parser = optparse.OptionParser(usage=usage)
+
+ parser.add_option("-s", "--session", type="choice",
+ dest="session",
+ choices=sessions,
+ help="which session to use")
+ parser.add_option("-x", "--x-server", type="choice",
+ dest="xserver",
+ choices=findXServers(), help="which X server to use")
+ parser.add_option("-l", "--logout", type="choice",
+ dest="logout",
+ choices=yesno,
+ help="attempt to log out of the session gracefully after" +
+ "script completion")
+ parser.add_option("-t", "--terminate", type="choice",
+ dest="terminate",
+ choices=yesno,
+ help="after script completion, and after any attempt to log" +
+ "out, terminate the session")
+
+ parser.set_defaults(session=sessions[0], logout='y', terminate='y')
+ options, args = parser.parse_args()
+ if not args:
+ parser.print_usage()
+ sys.exit(1)
+ return options, args
+
+
+def main():
+ options, args = parse()
+ if 'XDG_RUNTIME_DIR' in os.environ:
+ del os.environ['XDG_RUNTIME_DIR']
+ if options.session == "GNOME":
+ session = sessions.Session(sessionBinary='/usr/bin/gnome-session',
+ scriptCmdList=args, scriptDelay=10)
+ if options.session == "KDE":
+ session = sessions.Session(sessionBinary='/usr/bin/startkde',
+ scriptCmdList=args, scriptDelay=25)
+ if options.xserver:
+ session.xserver.server = options.xserver
+ pid = session.start()
+ scriptExitCode = session.script.exitCode
+ if options.logout == 'y':
+ session.attemptLogout()
+ if options.terminate == 'y':
+ session.stop()
+ else:
+ session.wait()
+ sys.exit(scriptExitCode)
+
+if __name__ == "__main__":
+ main()
diff --git a/scripts/dogtail-run-headless-next b/scripts/dogtail-run-headless-next
new file mode 100644
index 00000000000..ae481cf1c44
--- /dev/null
+++ b/scripts/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()