Merge remote-tracking branch 'origin/pr/210'

* origin/pr/210:
  Add /etc/qubes/applications override, use it for gnome-terminal
  Add qubes-run-gnome-terminal utility that uses --wait
This commit is contained in:
Marek Marczykowski-Górecki 2020-01-28 21:44:23 +01:00
commit 1e12b87086
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724
12 changed files with 190 additions and 26 deletions

View File

@ -186,6 +186,7 @@ install-doc:
install-common: install-doc
$(MAKE) -C autostart-dropins install
$(MAKE) -C applications-dropins install
install -m 0644 -D misc/fstab $(DESTDIR)/etc/fstab
# force /usr/bin before /bin to have /usr/bin/python instead of /bin/python
@ -233,6 +234,7 @@ install-common: install-doc
install -m 0755 misc/qvm-features-request $(DESTDIR)$(BINDIR)/qvm-features-request
install -m 0755 misc/qubes-run-terminal $(DESTDIR)/$(BINDIR)
install -D -m 0644 misc/qubes-run-terminal.desktop $(DESTDIR)/$(APPLICATIONSDIR)/qubes-run-terminal.desktop
install -m 0755 misc/qubes-run-gnome-terminal $(DESTDIR)/$(BINDIR)
install -D -m 0644 misc/dconf-db-local-dpi $(DESTDIR)/etc/dconf/db/local.d/dpi

View File

@ -0,0 +1,6 @@
DROPINS_DIR = /etc/qubes/applications
install:
for f in *.desktop; do install -m 0644 -D $$f $(DESTDIR)$(DROPINS_DIR)/$$f.d/30_qubes.conf; done
install -m 0644 README.txt $(DESTDIR)$(DROPINS_DIR)/

View File

@ -0,0 +1,20 @@
This directory (/etc/qubes/applications) is used to override parts of files in
/usr/share/applications and other applications directories.
For each desktop file there, you can create directory named after the file plus
".d", then place files there. All such files will be read (in lexicographical
order) and lines specified there will override respective entries in the
original file.
This can be used for example to override behaviour of a specific application in
particular VM type.
For example, you can extend `/usr/share/applications/firefox.desktop` by
creating `/etc/qubes/applications/firefox.desktop.d/50_user.conf` with:
```
[Desktop Entry]
Exec=firefox --private-window http://example.com %u
```
This would mean that `Exec` key would be read as your command line, regardless
of original entry in `/usr/share/applications/firefox.desktop`.

View File

@ -0,0 +1,2 @@
[Desktop Entry]
Exec=qubes-run-gnome-terminal

View File

@ -33,7 +33,7 @@ noextract=()
md5sums=(SKIP)
build() {
for source in autostart-dropins qubes-rpc qrexec misc Makefile vm-init.d vm-systemd network init version doc setup.py qubesagent post-install.d; do
for source in autostart-dropins applications-dropins qubes-rpc qrexec misc Makefile vm-init.d vm-systemd network init version doc setup.py qubesagent post-install.d; do
# shellcheck disable=SC2154
(ln -s "$srcdir/../$source" "$srcdir/$source")
done

View File

@ -36,6 +36,7 @@ etc/qubes-rpc/qubes.WaitForSession
etc/qubes-rpc/qubes.GetDate
etc/qubes-suspend-module-blacklist
etc/qubes/autostart/*
etc/qubes/applications/*
etc/qubes/post-install.d/README
etc/qubes/post-install.d/*.sh
etc/qubes/rpc-config/qubes.OpenInVM
@ -97,6 +98,7 @@ usr/bin/qubes-desktop-run
usr/bin/qubes-open
usr/bin/qubes-session-autostart
usr/bin/qubes-run-terminal
usr/bin/qubes-run-gnome-terminal
usr/bin/qubes-vmexec
usr/bin/qvm-copy
usr/bin/qvm-copy-to-vm

12
misc/qubes-run-gnome-terminal Executable file
View File

@ -0,0 +1,12 @@
#!/bin/sh
# Wrapper around gnome-terminal that runs it with --wait option
# (if supoported).
# Check if our gnome-terminal version supports --wait
# (we can't just run it and check exit code, because if it works, it will
# return the exit code of the child process)
if gnome-terminal --help-terminal-options | grep --silent -- --wait; then
exec gnome-terminal --wait "$@"
else
exec gnome-terminal "$@"
fi

View File

@ -12,14 +12,7 @@ if is_command x-terminal-emulator; then
fi
if is_command gnome-terminal; then
# Check if our gnome-terminal version supports --wait
# (we can't just run it and check exit code, because if it works, it will
# return the exit code of the child process)
if gnome-terminal --help-terminal-options | grep --silent -- --wait; then
exec gnome-terminal --wait
else
exec gnome-terminal
fi
exec qubes-run-gnome-terminal
fi
for terminal in xfce4-terminal konsole urxvt rxvt termit terminator Eterm aterm roxterm termite lxterminal mate-terminal terminology st xterm; do

View File

@ -24,23 +24,14 @@
import sys
from xdg.DesktopEntry import DesktopEntry
from qubesagent.xdg import launch
from qubesagent.xdg import launch, find_dropins, \
load_desktop_entry_with_dropins
import xdg.BaseDirectory
import os
QUBES_XDG_CONFIG_DROPINS = '/etc/qubes/autostart'
def open_desktop_entry_and_dropins(filename):
desktop_entry = DesktopEntry(filename)
dropins_dir = os.path.join(QUBES_XDG_CONFIG_DROPINS,
os.path.basename(filename) + '.d')
if os.path.isdir(dropins_dir):
for dropin in sorted(os.listdir(dropins_dir)):
dropin_content = DesktopEntry(os.path.join(dropins_dir, dropin))
desktop_entry.content.update(dropin_content.content)
return desktop_entry
def entry_should_be_started(entry, environments):
"""
@ -80,7 +71,10 @@ def process_autostart(environments):
entry_path = os.path.join(path, entry_name)
# files in $HOME have higher priority than dropins
if not path.startswith(xdg.BaseDirectory.xdg_config_home):
entry = open_desktop_entry_and_dropins(entry_path)
dropins = find_dropins(
entry_path, QUBES_XDG_CONFIG_DROPINS)
entry = load_desktop_entry_with_dropins(
entry_path, dropins)
else:
entry = DesktopEntry(entry_path)
if entry_should_be_started(entry, environments):

67
qubesagent/test_xdg.py Normal file
View File

@ -0,0 +1,67 @@
from unittest import TestCase
import tempfile
import shutil
import os
from qubesagent.xdg import find_dropins, load_desktop_entry_with_dropins, \
ini_to_string
class TestXdg(TestCase):
def setUp(self):
self.tempdir = tempfile.mkdtemp()
self.addCleanup(shutil.rmtree, self.tempdir)
def test_00_load_desktop_entry(self):
filename = os.path.join(self.tempdir, 'firefox.desktop')
dropins_dir = os.path.join(self.tempdir, 'dropins')
dropin_filename = os.path.join(
self.tempdir, 'dropins', 'firefox.desktop.d', '030_qubes.conf')
with open(filename, 'w') as f:
f.write('''\
[Desktop Entry]
Name=Firefox
Exec=firefox %u
''')
os.makedirs(os.path.dirname(dropin_filename))
with open(dropin_filename, 'w') as f:
f.write('''\
[Desktop Entry]
Exec=my-firefox %u
[Other Group]
X-Key=yes
''')
dropins = find_dropins(filename, dropins_dir)
self.assertListEqual(
dropins,
[dropin_filename])
desktop_entry = load_desktop_entry_with_dropins(filename, dropins)
self.assertEqual(desktop_entry.content['Desktop Entry']['Name'],
'Firefox')
self.assertEqual(desktop_entry.content['Desktop Entry']['Exec'],
'my-firefox %u')
self.assertEqual(desktop_entry.content['Other Group']['X-Key'],
'yes')
def test_01_init_to_string(self):
filename = os.path.join(self.tempdir, 'firefox.desktop')
content = '''\
[Desktop Entry]
Name=Firefox
Exec=firefox %u
[Other Group]
X-Key=yes
'''
with open(filename, 'w') as f:
f.write(content)
desktop_entry = load_desktop_entry_with_dropins(filename, [])
output = ini_to_string(desktop_entry)
self.assertEqual(output.rstrip(), content.rstrip())

View File

@ -4,17 +4,78 @@ from gi.repository import Gio # pylint: disable=import-error
from gi.repository import GLib # pylint: disable=import-error
import sys
import os
import io
from xdg.DesktopEntry import DesktopEntry
DROPINS_DIR = '/etc/qubes/applications'
def find_dropins(filename, dropins_dir):
result = []
app_dropins_dir = os.path.join(
dropins_dir,
os.path.basename(filename) + '.d')
if os.path.isdir(app_dropins_dir):
for dropin in sorted(os.listdir(app_dropins_dir)):
result.append(
os.path.join(app_dropins_dir, dropin))
return result
def load_desktop_entry_with_dropins(filename, dropins):
desktop_entry = DesktopEntry(filename)
for dropin in dropins:
dropin_entry = DesktopEntry(dropin)
for group_name, group in dropin_entry.content.items():
desktop_entry.content.setdefault(group_name, {}).update(group)
return desktop_entry
def make_launcher(filename, dropins_dir=DROPINS_DIR):
dropins = find_dropins(filename, dropins_dir)
if not dropins:
return Gio.DesktopAppInfo.new_from_filename(filename)
desktop_entry = load_desktop_entry_with_dropins(filename, dropins)
data = GLib.Bytes(ini_to_string(desktop_entry).encode('utf-8'))
keyfile = GLib.KeyFile()
keyfile.load_from_bytes(data, 0)
return Gio.DesktopAppInfo.new_from_keyfile(keyfile)
def ini_to_string(ini):
# See IniFile.write() in xdg package.
output = io.StringIO()
if ini.defaultGroup:
output.write("[%s]\n" % ini.defaultGroup)
for (key, value) in ini.content[ini.defaultGroup].items():
output.write("%s=%s\n" % (key, value))
output.write("\n")
for (name, group) in ini.content.items():
if name != ini.defaultGroup:
output.write("[%s]\n" % name)
for (key, value) in group.items():
output.write("%s=%s\n" % (key, value))
output.write("\n")
return output.getvalue()
def pid_callback(launcher, pid, pid_list):
pid_list.append(pid)
def dbus_name_change(loop, name, old_owner, new_owner):
if not new_owner:
loop.quit()
def launch(desktop, *files, **kwargs):
def launch(filename, *files, **kwargs):
wait = kwargs.pop('wait', True)
launcher = Gio.DesktopAppInfo.new_from_filename(desktop)
launcher = make_launcher(filename)
try:
import dbus
from dbus.mainloop.glib import DBusGMainLoop

View File

@ -577,10 +577,13 @@ rm -f %{name}-%{version}
%config(noreplace) /etc/qubes/rpc-config/qubes.InstallUpdatesGUI
%config(noreplace) /etc/qubes/rpc-config/qubes.VMShell+WaitForSession
%config(noreplace) /etc/qubes/rpc-config/qubes.VMExecGUI
%dir /etc/qubes/autostart
%config(noreplace) /etc/default/grub.qubes
%dir /etc/qubes/autostart
/etc/qubes/autostart/README.txt
%config /etc/qubes/autostart/*.desktop.d/30_qubes.conf
%dir /etc/qubes/applications
/etc/qubes/applications/README.txt
%config /etc/qubes/applications/*.desktop.d/30_qubes.conf
%dir /etc/qubes/suspend-pre.d
/etc/qubes/suspend-pre.d/README
%dir /etc/qubes/suspend-post.d
@ -615,6 +618,7 @@ rm -f %{name}-%{version}
/usr/bin/xenstore-watch-qubes
/usr/bin/qubes-desktop-run
/usr/bin/qubes-run-terminal
/usr/bin/qubes-run-gnome-terminal
/usr/bin/qubes-open
/usr/bin/qubes-session-autostart
/usr/bin/qvm-console
@ -674,6 +678,7 @@ rm -f %{name}-%{version}
%{python3_sitelib}/qubesagent/vmexec.py*
%{python3_sitelib}/qubesagent/test_vmexec.py*
%{python3_sitelib}/qubesagent/xdg.py*
%{python3_sitelib}/qubesagent/test_xdg.py*
/usr/share/qubes/mime-override/globs
/usr/share/qubes/qubes-master-key.asc