Add /etc/qubes/applications override, use it for gnome-terminal
Used by qubes.StartApp so that we can override distribution-provided .desktop files. The mechanism is introduced to run gnome-terminal with --wait option, so that it's compatible with DispVMs. Fixes QubesOS/qubes-issues#2581.
This commit is contained in:
parent
943f37b481
commit
3a6e77aa43
1
Makefile
1
Makefile
@ -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
|
||||
|
6
applications-dropins/Makefile
Normal file
6
applications-dropins/Makefile
Normal 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)/
|
20
applications-dropins/README.txt
Normal file
20
applications-dropins/README.txt
Normal 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`.
|
2
applications-dropins/org.gnome.Terminal.desktop
Normal file
2
applications-dropins/org.gnome.Terminal.desktop
Normal file
@ -0,0 +1,2 @@
|
||||
[Desktop Entry]
|
||||
Exec=qubes-run-gnome-terminal
|
@ -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
|
||||
|
1
debian/qubes-core-agent.install
vendored
1
debian/qubes-core-agent.install
vendored
@ -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
|
||||
|
@ -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
67
qubesagent/test_xdg.py
Normal 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())
|
@ -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
|
||||
|
@ -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
|
||||
@ -675,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
|
||||
|
Loading…
Reference in New Issue
Block a user