From b6c19fc2ef5c62edd06b5605889cefbd3acd4552 Mon Sep 17 00:00:00 2001 From: Jason Mehring Date: Sun, 2 Aug 2015 17:30:26 -0400 Subject: [PATCH] qubes-desktop-file-install: Manages xdg desktop entry files qubes-desktop-file-install is called by qubes-triggers-desktop-file-install. It's arguments are based on the Gnome desktop-install-file utility to allow it to be replaced by same. Currently the Gnome utility can not be used since it automatically validates the .desktop entry files with no option to skip validation and will fail on some third party .desktop files that are not formed properly. A single trigger script is shared between Fedora, Debian. This script is used by the package managers triggers and will copy original .desktop files from `/etc/xdg/autostart` to `/usr/share/qubes/xdg/autostart` and modify the OnlyShownIn / NotShownIn, etc. The original .desktop files are left untouched and left in place. Qubes modifies the XDG_CONFIG_DIRS to first include the `/usr/share/qubes/xdg` directory (XDG_CONFIG_DIRS=/usr/share/qubes/xdg:/etc/xdg). If a package gets removed, it's desktop entry is also removed from the /usr/share/qubes/xdg directory. 'qubes-desktop-file-install' options: --dir DIR Install desktop files to the DIR directory (default: ) --force Force overwrite of existing desktop files (default: False) --remove-show-in Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file (default: False) --remove-key KEY Remove the KEY key from the desktop files, if present --set-key (KEY VALUE) Set the KEY key to VALUE --remove-only-show-in ENVIRONMENT Remove ENVIRONMENT from the list of desktop environment where the desktop files should be displayed --add-only-show-in ENVIRONMENT Add ENVIRONMENT to the list of desktop environment where the desktop files should be displayed --remove-not-show-in ENVIRONMENT Remove ENVIRONMENT from the list of desktop environment where the desktop files should not be displayed --add-not-show-in ENVIRONMENT Add ENVIRONMENT to the list of desktop environment where the desktop files should not be displayed --- Makefile | 3 + debian/qubes-core-agent.postinst | 231 ++++------- debian/qubes-core-agent.triggers | 45 +-- misc/qubes-desktop-file-install | 367 ++++++++++++++++++ misc/qubes-trigger-desktop-file-install | 97 +++++ .../qubes-trigger-desktop-file-install.action | 1 + rpm_spec/core-vm.spec | 57 +-- vm-systemd/misc-post.sh | 12 +- 8 files changed, 571 insertions(+), 242 deletions(-) create mode 100755 misc/qubes-desktop-file-install create mode 100755 misc/qubes-trigger-desktop-file-install create mode 100644 misc/qubes-trigger-desktop-file-install.action diff --git a/Makefile b/Makefile index 5a2fb5a..b1e7335 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,7 @@ install-rh: install-systemd install-systemd-dropins install-sysvinit touch $(DESTDIR)/etc/yum.conf.d/qubes-proxy.conf install -D -m 0644 misc/qubes-trigger-sync-appmenus.action $(DESTDIR)/etc/yum/post-actions/qubes-trigger-sync-appmenus.action + install -D -m 0644 misc/qubes-trigger-desktop-file-install.action $(DESTDIR)/etc/yum/post-actions/qubes-trigger-desktop-file-install.action install -D -m 0644 misc/serial.conf $(DESTDIR)/usr/share/qubes/serial.conf install -D misc/qubes-serial-login $(DESTDIR)/$(SBINDIR)/qubes-serial-login @@ -166,6 +167,8 @@ install-common: install network/qubes-netwatcher $(DESTDIR)/$(SBINDIR)/ install -d $(DESTDIR)/usr/bin + install -m 0755 misc/qubes-desktop-file-install $(DESTDIR)/usr/bin/qubes-desktop-file-install + install -m 0755 misc/qubes-trigger-desktop-file-install $(DESTDIR)$(LIBDIR)/qubes/qubes-trigger-desktop-file-install install qubes-rpc/{qvm-open-in-dvm,qvm-open-in-vm,qvm-copy-to-vm,qvm-move-to-vm,qvm-run,qvm-mru-entry} $(DESTDIR)/usr/bin install qubes-rpc/wrap-in-html-if-url.sh $(DESTDIR)$(LIBDIR)/qubes diff --git a/debian/qubes-core-agent.postinst b/debian/qubes-core-agent.postinst index 03ceb17..29f57fd 100755 --- a/debian/qubes-core-agent.postinst +++ b/debian/qubes-core-agent.postinst @@ -19,41 +19,10 @@ set -e # https://www.debian.org/doc/debian-policy/ch-maintainerscripts.html or # the debian-policy package -# Directory that modified desktop entry config files are stored in -XDG_CONFIG_QUBES="/usr/share/qubes/xdg" -remove_ShowIn() { - if [ -e "${1}" ]; then - sed -i '/^\(Not\|Only\)ShowIn/d' "${1}" - fi -} - -showIn() { - desktop_entry="${1}" - shown_in="${2}" - message="${shown_in:-"Shown in All;"}" - desktop_entry_qubes="${XDG_CONFIG_QUBES}/autostart/${desktop_entry##*/}" - - # Make sure Qubes autostart directory exists - mkdir -p "${XDG_CONFIG_QUBES}/autostart" - - # Desktop entry exists, so move to Qubes directory and modify it - if [ -e "${desktop_entry}" ]; then - echo "Desktop Entry Modification - ${message} ${desktop_entry##*/}..." - cp -pf "${desktop_entry}" "${desktop_entry_qubes}" - - remove_ShowIn "${desktop_entry_qubes}" - sed -i '/^X-GNOME-Autostart-enabled.*[fF0]/d' "${desktop_entry_qubes}" - - # Will only be '' if shown in all - if [ ! "${shown_in}x" == "x" ]; then - echo "${shown_in}" >> "${desktop_entry_qubes}" || true - fi - - # Desktop entry must have been removed, so also remove from Qubes directory - else - echo "Desktop Entry Modification - Remove: ${desktop_entry##*/}..." - rm -f "${desktop_entry_qubes}" +debug() { + if [ "${DEBDEBUG}" == "1" ]; then + echo -e ""$@"" fi } @@ -86,19 +55,76 @@ systemdPreload() { systemctl daemon-reload } -# Manually trigger all triggers to automaticatly configure -triggerTriggers() { - path="$(readlink -m ${0})" - triggers="${path/postinst/triggers}" +installSerialConf() { + debug "Installing over-ridden serial.conf init script..." + if [ -e /etc/init/serial.conf ]; then + cp /usr/share/qubes/serial.conf /etc/init/serial.conf + fi +} - awk '{sub(/[ \t]*#.*/,"")} NF' ${triggers} | while read line - do - /bin/bash -c "${0} triggered ${line##* }" || true - done +disableSELinux() { + debug "Disabling SELinux..." + if [ -e /etc/selinux/config ]; then + sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed + mv /etc/selinux/config.processed /etc/selinux/config + setenforce 0 2>/dev/null + fi } case "${1}" in configure) + # Initial installation of package only + # ($2 contains version number on update; nothing on initial installation) + if [ -z "${2}" ]; then + + debug "FIRST INSTALL..." + # Create NetworkManager configuration if we do not have it + if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then + echo '[main]' > /etc/NetworkManager/NetworkManager.conf + echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf + echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf + fi + /usr/lib/qubes/qubes-fix-nm-conf.sh + + # Location of files which contains list of protected files + PROTECTED_FILE_LIST='/etc/qubes/protected-files.d' + + # ensure that hostname resolves to 127.0.1.1 resp. ::1 and that /etc/hosts is + # in the form expected by qubes-sysinit.sh + if ! grep -rq "^/etc/hostname$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then + for ip in '127\.0\.1\.1' '::1'; do + if grep -q "^${ip}\(\s\|$\)" /etc/hosts; then + sed -i "/^${ip}\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true + sed -i "s/^${ip}\(\s\|$\).*$/\0 `hostname`/" /etc/hosts || true + else + echo "${ip//\\/} `hostname`" >> /etc/hosts || true + fi + done + fi + + # remove hostname from 127.0.0.1 line (in debian the hostname is by default + # resolved to 127.0.1.1) + if ! grep -rq "^/etc/hosts$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then + sed -i "/^127\.0\.0\.1\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true + fi + + chown user:user /home_volatile/user + + # Set default "runlevel" + rm -f /etc/systemd/system/default.target + ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + + # Systemd preload-all + systemdPreload + + # Maybe install overridden serial.conf init script + installSerialConf + + # Maybe disable SELinux + disableSELinux + fi + + debug "UPDATE..." # disable some Upstart services for init in plymouth-shutdown \ prefdm \ @@ -109,14 +135,6 @@ case "${1}" in done dpkg-divert --divert /etc/init/serial.conf.qubes-orig --package qubes-core-agent --rename --add /etc/init/serial.conf - # Create NetworkManager configuration if we do not have it - if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then - echo '[main]' > /etc/NetworkManager/NetworkManager.conf - echo 'plugins = keyfile' >> /etc/NetworkManager/NetworkManager.conf - echo '[keyfile]' >> /etc/NetworkManager/NetworkManager.conf - fi - /usr/lib/qubes/qubes-fix-nm-conf.sh - # make sure locale is really generated current_locale=`grep 'LANG\|LC_ALL' /etc/default/locale|head -n 1|cut -f 2 -d =` if [ -n "$current_locale" ] && ! locale -a | grep -q "$current_locale"; then @@ -131,43 +149,16 @@ case "${1}" in rm -f /lib/firmware/updates fi - # Location of files which contains list of protected files - PROTECTED_FILE_LIST='/etc/qubes/protected-files.d' - - # ensure that hostname resolves to 127.0.1.1 resp. ::1 and that /etc/hosts is - # in the form expected by qubes-sysinit.sh - if ! grep -rq "^/etc/hostname$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then - for ip in '127\.0\.1\.1' '::1'; do - if grep -q "^${ip}\(\s\|$\)" /etc/hosts; then - sed -i "/^${ip}\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true - sed -i "s/^${ip}\(\s\|$\).*$/\0 `hostname`/" /etc/hosts || true - else - echo "${ip//\\/} `hostname`" >> /etc/hosts || true - fi - done - fi - - # remove hostname from 127.0.0.1 line (in debian the hostname is by default - # resolved to 127.0.1.1) - if ! grep -rq "^/etc/hosts$" "${PROTECTED_FILE_LIST}" 2>/dev/null; then - sed -i "/^127\.0\.0\.1\s/,+0s/\(\s`hostname`\)\+\(\s\|$\)/\2/g" /etc/hosts || true - fi - - chown user:user /home_volatile/user - if ! dpkg-statoverride --list /var/lib/qubes/dom0-updates >/dev/null 2>&1; then dpkg-statoverride --update --add user user 775 /var/lib/qubes/dom0-updates fi - # Set default "runlevel" - rm -f /etc/systemd/system/default.target - ln -s /lib/systemd/system/multi-user.target /etc/systemd/system/default.target + # Update Qubes App Menus" + /usr/lib/qubes/qubes-trigger-sync-appmenus.sh || true - ## Systemd preload-all - systemdPreload + ## Update all xdg autostart desktop entries + /usr/lib/qubes/qubes-trigger-desktop-file-install clean || true - ## Process all triggers which will set defaults to wanted values - triggerTriggers ;; abort-upgrade|abort-remove|abort-deconfigure) @@ -178,85 +169,27 @@ case "${1}" in for trigger in ${2}; do case "${trigger}" in - # Update Qubes App Menus /usr/share/applications) - echo "Updating Qubes App Menus..." + debug "Updating Qubes App Menus..." /usr/lib/qubes/qubes-trigger-sync-appmenus.sh || true + + debug "Updating XDG Config..." + /usr/lib/qubes/qubes-trigger-desktop-file-install || true + ;; - ## Systemd preload-all - #systemdPreload + /etc/xdg) + debug "Updating XDG Config..." + /usr/lib/qubes/qubes-trigger-desktop-file-install || true ;; # Install overridden serial.conf init script /etc/init/serial.conf) - echo "Installing over-ridden serial.conf init script..." - if [ -e /etc/init/serial.conf ]; then - cp /usr/share/qubes/serial.conf /etc/init/serial.conf - fi + installSerialConf ;; # Disable SELinux" /etc/selinux/config) - echo "Disabling SELinux..." - if [ -e /etc/selinux/config ]; then - sed -e s/^SELINUX=.*$/SELINUX=disabled/ /etc/selinux/config.processed - mv /etc/selinux/config.processed /etc/selinux/config - setenforce 0 2>/dev/null - fi - ;; - - # Desktop Entry Modification - Remove existing rules - /etc/xdg/autostart/gpk-update-icon.desktop | \ - /etc/xdg/autostart/nm-applet.desktop | \ - /etc/xdg/autostart/abrt-applet.desktop | \ - /etc/xdg/autostart/notify-osd.desktop) - showIn "${trigger}" - ;; - - # Desktop Entry Modification - Not shown in Qubes - /etc/xdg/autostart/pulseaudio.desktop | \ - /etc/xdg/autostart/deja-dup-monitor.desktop | \ - /etc/xdg/autostart/imsettings-start.desktop | \ - /etc/xdg/autostart/krb5-auth-dialog.desktop | \ - /etc/xdg/autostart/pulseaudio.desktop | \ - /etc/xdg/autostart/restorecond.desktop | \ - /etc/xdg/autostart/sealertauto.desktop | \ - /etc/xdg/autostart/gnome-power-manager.desktop | \ - /etc/xdg/autostart/gnome-sound-applet.desktop | \ - /etc/xdg/autostart/gnome-screensaver.desktop | \ - /etc/xdg/autostart/orca-autostart.desktop) - showIn "${trigger}" 'NotShowIn=QUBES;' - ;; - - # Desktop Entry Modification - Not shown in in DisposableVM - /etc/xdg/autostart/gcm-apply.desktop) - showIn "${trigger}" 'NotShowIn=DisposableVM;' - ;; - - # Desktop Entry Modification - Only shown in AppVM - /etc/xdg/autostart/gnome-keyring-gpg.desktop | \ - /etc/xdg/autostart/gnome-keyring-pkcs11.desktop | \ - /etc/xdg/autostart/gnome-keyring-secrets.desktop | \ - /etc/xdg/autostart/gnome-keyring-ssh.desktop | \ - /etc/xdg/autostart/gnome-settings-daemon.desktop | \ - /etc/xdg/autostart/user-dirs-update-gtk.desktop | \ - /etc/xdg/autostart/gsettings-data-convert.desktop) - showIn "${trigger}" 'OnlyShowIn=GNOME;AppVM;' - ;; - - # Desktop Entry Modification - Only shown in Gnome & UpdateableVM - /etc/xdg/autostart/gpk-update-icon.desktop) - showIn "${trigger}" 'OnlyShowIn=GNOME;UpdateableVM;' - ;; - - # Desktop Entry Modification - Only shown in Gnome & Qubes - /etc/xdg/autostart/nm-applet.desktop) - showIn "${trigger}" 'OnlyShowIn=GNOME;QUBES;' - ;; - - *) - echo "postinst called with unknown trigger \`${2}'" >&2 - exit 1 + disableSELinux ;; esac done diff --git a/debian/qubes-core-agent.triggers b/debian/qubes-core-agent.triggers index 6b3b875..817775b 100644 --- a/debian/qubes-core-agent.triggers +++ b/debian/qubes-core-agent.triggers @@ -1,41 +1,4 @@ -interest-await /usr/share/applications -interest-await /etc/init/serial.conf -interest-await /etc/selinux/config - -# Desktop Entry Modification - Not shown in Qubes -interest-await /etc/xdg/autostart/pulseaudio.desktop -interest-await /etc/xdg/autostart/deja-dup-monitor.desktop -interest-await /etc/xdg/autostart/imsettings-start.desktop -interest-await /etc/xdg/autostart/krb5-auth-dialog.desktop -interest-await /etc/xdg/autostart/restorecond.desktop -interest-await /etc/xdg/autostart/sealertauto.desktop -interest-await /etc/xdg/autostart/gnome-power-manager.desktop -interest-await /etc/xdg/autostart/gnome-sound-applet.desktop -interest-await /etc/xdg/autostart/gnome-screensaver.desktop -interest-await /etc/xdg/autostart/orca-autostart.desktop - -# Desktop Entry Modification - Not shown in in DisposableVM -interest-await /etc/xdg/autostart/gcm-apply.desktop - -# Desktop Entry Modification - Only shown in AppVM -interest-await /etc/xdg/autostart/gnome-keyring-gpg.desktop -interest-await /etc/xdg/autostart/gnome-keyring-pkcs11.desktop -interest-await /etc/xdg/autostart/gnome-keyring-secrets.desktop -interest-await /etc/xdg/autostart/gnome-keyring-ssh.desktop -interest-await /etc/xdg/autostart/gnome-settings-daemon.desktop -interest-await /etc/xdg/autostart/user-dirs-update-gtk.desktop -interest-await /etc/xdg/autostart/gsettings-data-convert.desktop - -# Desktop Entry Modification - Remove existing rules -#interest-await /etc/xdg/autostart/gpk-update-icon.desktop -#interest-await /etc/xdg/autostart/nm-applet.desktop -interest-await /etc/xdg/autostart/abrt-applet.desktop - -# Desktop Entry Modification - Only shown in Gnome & UpdateableVM -interest-await /etc/xdg/autostart/gpk-update-icon.desktop - -# Desktop Entry Modification - Only shown in Gnome & Qubes -interest-await /etc/xdg/autostart/nm-applet.desktop - -# Desktop Entry Modification - Show in all -interest-await /etc/xdg/autostart/notify-osd.desktop +interest-noawait /usr/share/applications +interest-noawait /etc/xdg +interest-noawait /etc/init/serial.conf +interest-noawait /etc/selinux/config diff --git a/misc/qubes-desktop-file-install b/misc/qubes-desktop-file-install new file mode 100755 index 0000000..1510a7c --- /dev/null +++ b/misc/qubes-desktop-file-install @@ -0,0 +1,367 @@ +#! /usr/bin/env python +# -*- coding: utf-8 -*- +# vim: set ft=python ts=4 sw=4 sts=4 et : + +# Copyright (C) 2015 Jason Mehring +# License: GPL-2+ +# Authors: Jason Mehring +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +'''Installation and edition of desktop files. + +Description: + The desktop-file-install program is a tool to install, and optionally edit, + desktop files. They are mostly useful for developers and packagers. + + Various options are available to edit the desktop files. The edit options can + be specified more than once and will be processed in the same order as the + options passed to the program. + + The original .desktop files are left untouched and left in place. + + Qubes modifies the XDG_CONFIG_DIRS to first include the `/usr/share/qubes/xdg` + directory (XDG_CONFIG_DIRS=/usr/share/qubes/xdg:/etc/xdg). + +Usage: + qubes-desktop-file-install [--dir DIR] [--force] + [--remove-show-in] + [--remove-key KEY] + [--remove-only-show-in ENVIRONMENT] + [--add-only-show-in ENVIRONMENT] + [--remove-not-show-in ENVIRONMENT] + [--add-not-show-in ENVIRONMENT] + [(--set-key KEY VALUE)] + FILE + qubes-desktop-file-install -h | --help + qubes-desktop-file-install --version + +Examples: + qubes-desktop-file-install --dir /usr/share/qubes/xdg/autostart --add-only-show-in X-QUBES /etc/xdg/autostart/pulseaudio.desktop + +Arguments: + FILE Path to desktop entry file + +Help Options: + -h, --help show this help message and exit + +Installation options for desktop file: + --dir DIR Install desktop files to the DIR directory (default: ) + --force Force overwrite of existing desktop files (default: False) + +Edition options for desktop file: + --remove-show-in Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file (default: False) + --remove-key KEY Remove the KEY key from the desktop files, if present + --set-key (KEY VALUE) Set the KEY key to VALUE + --remove-only-show-in ENVIRONMENT Remove ENVIRONMENT from the list of desktop environment where the desktop files should be displayed + --add-only-show-in ENVIRONMENT Add ENVIRONMENT to the list of desktop environment where the desktop files should be displayed + --remove-not-show-in ENVIRONMENT Remove ENVIRONMENT from the list of desktop environment where the desktop files should not be displayed + --add-not-show-in ENVIRONMENT Add ENVIRONMENT to the list of desktop environment where the desktop files should not be displayed +''' + +import argparse +import codecs +import io +import locale +import os +import sys + +try: + import ConfigParser as configparser +except ImportError: + import configparser + +from collections import OrderedDict + +# Third party libs +import xdg.DesktopEntry + +__all__ = [] +__version__ = '1.0.0' + +# This is almost always a good thing to do at the beginning of your programs. +locale.setlocale(locale.LC_ALL, '') + +# Default Qubes directory that modified desktop entry config files are stored in +QUBES_XDG_CONFIG_DIR = '/usr/share/qubes/xdg' + + +class DesktopEntry(xdg.DesktopEntry.DesktopEntry): + '''Class to parse and validate Desktop Entries (OVERRIDE). + + xdg.DesktopEntry.DesktopEntry does not maintain order of content + ''' + + defaultGroup = 'Desktop Entry' + + def __init__(self, filename=None): + """Create a new DesktopEntry + + If filename exists, it will be parsed as a desktop entry file. If not, + or if filename is None, a blank DesktopEntry is created. + """ + self.content = OrderedDict() + if filename and os.path.exists(filename): + self.parse(filename) + elif filename: + self.new(filename) + + def parse(self, filename): + '''Parse a desktop entry file.''' + headers = [u'Desktop Entry', u'KDE Desktop Entry'] + cfgparser = configparser.RawConfigParser() + cfgparser.optionxform = unicode + + try: + cfgparser.readfp(codecs.open(filename, 'r', 'utf8')) + except configparser.MissingSectionHeaderError: + sys.exit('{0} missing header!'.format(filename, headers[0])) + + self.filename = filename + self.tainted = False + + for header in headers: + if cfgparser.has_section(header): + self.content[header] = OrderedDict(cfgparser.items(header)) + if not self.defaultGroup: + self.defaultGroup = header + + if not self.defaultGroup: + sys.exit('{0} missing header!'.format(filename, headers[0])) + + # Write support broken in Wheezy; override here + def write(self, filename=None, trusted=False): + if not filename and not self.filename: + raise ParsingError("File not found", "") + + if filename: + self.filename = filename + else: + filename = self.filename + + if os.path.dirname(filename) and not os.path.isdir(os.path.dirname(filename)): + os.makedirs(os.path.dirname(filename)) + + with io.open(filename, 'w', encoding='utf-8') as fp: + + # An executable bit signifies that the desktop file is + # trusted, but then the file can be executed. Add hashbang to + # make sure that the file is opened by something that + # understands desktop files. + if trusted: + fp.write(u("#!/usr/bin/env xdg-open\n")) + + if self.defaultGroup: + fp.write(unicode("[%s]\n") % self.defaultGroup) + for (key, value) in self.content[self.defaultGroup].items(): + fp.write(unicode("%s=%s\n") % (key, value)) + fp.write(unicode("\n")) + for (name, group) in self.content.items(): + if name != self.defaultGroup: + fp.write(unicode("[%s]\n") % name) + for (key, value) in group.items(): + fp.write(unicode("%s=%s\n") % (key, value)) + fp.write(unicode("\n")) + + # Add executable bits to the file to show that it's trusted. + if trusted: + oldmode = os.stat(filename).st_mode + mode = oldmode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH + os.chmod(filename, mode) + + self.tainted = False + + +def delete(path): + '''Delete a file. + ''' + if os.path.exists(path): + try: + os.unlink(os.path.abspath(path)) + except IOError as error: + sys.exit('Unable to delete file: {0}\n{1}'.format(path, error)) + + +def set_key(entry, key, value): + '''Set a key with value within an desktop-file entry object. + ''' + key = unicode(key) + if isinstance(value, list): + entry.set(key, u';'.join(value)) + else: + entry.set(key, unicode(value)) + + +def remove_key(entry, key): + '''Remove a key within an desktop-file entry object. + ''' + entry.removeKey(unicode(key)) + + +def add_value(entry, key, value): + '''Add a value to a desktop-file entry object. + ''' + values = entry.getList(unicode(value)) + for value in values: + entry_values = entry.get(key, list=True) + if value not in entry_values: + entry_values.append(value) + set_key(entry, key, entry_values) + + +def remove_value(entry, key, value): + '''Remove a value to a desktop-file entry object. + ''' + value = unicode(value) + entry_values = entry.get(key, list=True) + if value in entry_values: + entry_values.remove(value) + if entry_values: + set_key(entry, key, entry_values) + else: + remove_key(entry, key) + + +def install(**kwargs): + '''Install a copy of a desktop-file entry file to a new location and + optionally edit it. + ''' + paths = kwargs.get('path', []) + for path in paths: + if not path: + sys.exit('No path selected!') + + filename, extension = os.path.splitext(path) + if extension.lower() not in ['.desktop']: + sys.exit("Invalid desktop extenstion '{0}'! Was expecting '.desktop'.".format(extension)) + + new_path = os.path.join(kwargs['dir'], os.path.basename(path)) + + if os.path.exists(path) and os.path.isfile(path): + stat_info = os.stat(path) + + # Don't update if file has previously been updated unless force is True + if os.path.exists(new_path) and not kwargs['force']: + if os.stat(new_path).st_mtime == stat_info.st_mtime: + continue + else: + if os.path.exists(new_path) and os.path.isfile(new_path): + delete(new_path) + continue + + entry = DesktopEntry(path) + + if kwargs['remove_show_in']: + kwargs['remove_key'].append(u'OnlyShowIn') + kwargs['remove_key'].append(u'NotShowIn') + + if kwargs['remove_key']: + for value in kwargs['remove_key']: + remove_key(entry, value) + + if kwargs['remove_only_show_in']: + for value in kwargs['remove_only_show_in']: + remove_value(entry, u'OnlyShowIn', value) + + if kwargs['add_only_show_in']: + for value in kwargs['add_only_show_in']: + add_value(entry, u'OnlyShowIn', value) + + if kwargs['remove_not_show_in']: + for value in kwargs['remove_not_show_in']: + remove_value(entry, u'NotShowIn', value) + + if kwargs['add_not_show_in']: + for value in kwargs['add_not_show_in']: + add_value(entry, u'NotShowIn', value) + + if kwargs['set_key']: + for key, value in kwargs['set_key']: + set_key(entry, key, value) + + entry.write(new_path) + if stat_info: + os.utime(new_path, (stat_info.st_atime, stat_info.st_mtime)) + + + +def parse(args): + '''Argparse configuration. + ''' + parser = argparse.ArgumentParser() + + parser.add_argument('--version', action='version', version='%(prog)s (version {0})'.format(__version__)) + + parser.add_argument('--force', action='store_true', default=False, help='\ + Force overwrite of existing desktop files.') + + parser.add_argument('--dir', default=QUBES_XDG_CONFIG_DIR, help='\ + Install desktop files to the DIR directory.') + + parser.add_argument('--remove-show-in', action='store_true', default=False, help='\ + Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file') + + parser.add_argument('--remove-key', action='append', metavar='KEY', default=[], help='\ + Remove the KEY key from the desktop file') + + parser.add_argument('--remove-only-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\ + Remove ENVIRONMENT from the list of desktop environments where the\ + desktop files should be displayed (key OnlyShowIn). If ENVIRONMENT was\ + not present in the list, this operation is a no-op.') + + parser.add_argument('--add-only-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\ + Add ENVIRONMENT to the list of desktop environments where the desktop\ + files should be displayed (key OnlyShowIn). If ENVIRONMENT was already\ + present in the list, this operation is a no-op. A non-registered desktop\ + environment should be prefixed with X-. Note that an empty OnlyShowIn\ + key in a desktop file means that the desktop file will be displayed in\ + all environments.') + + parser.add_argument('--remove-not-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\ + Remove ENVIRONMENT from the list of desktop environments where the\ + desktop files should not be displayed (key NotShowIn). If\ + ENVIRONMENT was not present in the list, this operation is a no-op.') + + parser.add_argument('--add-not-show-in', action='append', metavar='ENVIRONMENT', default=[], help='\ + Add ENVIRONMENT to the list of desktop environments where the desktop\ + files should not be displayed (key NotShowIn). If ENVIRONMENT was\ + already present in the list, this operation is a no-op. A non-registered\ + desktop environment should be prefixed with X-. Note that an empty\ + NotShowIn key in a desktop file means that the desktop file will be\ + displayed in all environments.') + + parser.add_argument('--set-key', action='append', nargs=2, metavar=('KEY', 'VALUE'), default=[], help='\ + Set the KEY key to the VALUE passed.') + + parser.add_argument('path', action='store', nargs='+', metavar='FILE', default=None, + help='Path to desktop entry file') + + args = parser.parse_args(args) + + if not os.path.isabs(args.dir): + args.dir = os.path.join(QUBES_XDG_CONFIG_DIR, args.dir) + + return args + + +def main(argv): + '''Main function. + ''' + args = parse(argv[1:]) + install(**vars(args)) + + +if __name__ == '__main__': + main(sys.argv) + sys.exit(0) diff --git a/misc/qubes-trigger-desktop-file-install b/misc/qubes-trigger-desktop-file-install new file mode 100755 index 0000000..d08c674 --- /dev/null +++ b/misc/qubes-trigger-desktop-file-install @@ -0,0 +1,97 @@ +#!/bin/bash -e +# vim: set ts=4 sw=4 sts=4 et : + +# +# qubes-trigger-desktop-file-install +# +# This trigger script calls qubes-desktop-file-install to installation and edit +# desktop file overrides, leaving the original desktop file in-place and +# untouched. +# +# 'qubes-desktop-file-install' options: +# --dir DIR Install desktop files to the DIR directory (default: ) +# --force Force overwrite of existing desktop files (default: False) +# --remove-show-in Remove the "OnlyShowIn" and "NotShowIn" entries from the desktop file (default: False) +# --remove-key KEY Remove the KEY key from the desktop files, if present +# --set-key (KEY VALUE) Set the KEY key to VALUE +# --remove-only-show-in ENVIRONMENT Remove ENVIRONMENT from the list of desktop environment where the desktop files should be displayed +# --add-only-show-in ENVIRONMENT Add ENVIRONMENT to the list of desktop environment where the desktop files should be displayed +# --remove-not-show-in ENVIRONMENT Remove ENVIRONMENT from the list of desktop environment where the desktop files should not be displayed +# --add-not-show-in ENVIRONMENT Add ENVIRONMENT to the list of desktop environment where the desktop files should not be displayed + +QUBES_DESKTOP_FILE_INSTALL='/usr/bin/qubes-desktop-file-install' +QUBES_XDG_CONFIG_DIR=/usr/share/qubes/xdg/autostart +XDG_CONFIG_DIR=/etc/xdg/autostart + +INSTALL_CMD=""${QUBES_DESKTOP_FILE_INSTALL}" --force --dir "${QUBES_XDG_CONFIG_DIR}"" + +# Remove all current Qubes desktop entry files +if [ "${1}" == "clean" ]; then + rm -f "${QUBES_XDG_CONFIG_DIR}"/* +fi + +generatePath () { + echo "${XDG_CONFIG_DIR}/${1}.desktop" + +} + +generateFileList () { + for key in "${!FILES[@]}"; do + FILES[${key}]="$(generatePath ${FILES[key]})" + done +} + +install () { + local options="${@}" + + # Install an edited version of desktop file in $QUBES_XDG_CONFIG_DIR + generateFileList + $INSTALL_CMD "${@}" "${FILES[@]}" || true + +} + +# Desktop Entry Modification - NotShowIn=QUBES +FILES=( + 'pulseaudio' + 'deja-dup-monitor' + 'imsettings-start' + 'krb5-auth-dialog' + 'restorecond' + 'sealertauto' + 'gnome-power-manager' + 'gnome-sound-applet' + 'gnome-screensaver' + 'orca-autostart' +); install --remove-show-in --add-not-show-in X-QUBES + +# Desktop Entry Modification - NotShowIn=DisposableVM +FILES=('gcm-apply') +install --remove-show-in --add-not-show-in X-DisposableVM + +# Desktop Entry Modification - OnlyShowIn=GNOME;AppVM; +FILES=( + 'gnome-keyring-gpg' + 'gnome-keyring-pkcs11' + 'gnome-keyring-secrets' + 'gnome-keyring-ssh' + 'gnome-settings-daemon' + 'user-dirs-update-gtk' + 'gsettings-data-convert' +); install --remove-show-in --add-only-show-in 'GNOME;X-AppVM' + +# Desktop Entry Modification - OnlyShowIn=GNOME;UpdateableVM +FILES=('gpk-update-icon') +install --remove-show-in --add-only-show-in 'GNOME;X-UpdateableVM' + +# Desktop Entry Modification - OnlyShowIn=GNOME;QUBES +FILES=('nm-applet') + install --remove-show-in --add-only-show-in 'GNOME;X-QUBES' + +# Desktop Entry Modification - Remove existing rules +FILES=( + 'abrt-applet' +); install --remove-show-in + +# Desktop Entry Modification - Misc +# notify-osd +$INSTALL_CMD --remove-show-in --remove-key X-GNOME-Autostart-enabled "$(generatePath notify-osd)" diff --git a/misc/qubes-trigger-desktop-file-install.action b/misc/qubes-trigger-desktop-file-install.action new file mode 100644 index 0000000..c76ce07 --- /dev/null +++ b/misc/qubes-trigger-desktop-file-install.action @@ -0,0 +1 @@ +*:any:/usr/lib/qubes/qubes-trigger-desktop-file-install diff --git a/rpm_spec/core-vm.spec b/rpm_spec/core-vm.spec index fa4e2e0..a61137a 100644 --- a/rpm_spec/core-vm.spec +++ b/rpm_spec/core-vm.spec @@ -51,6 +51,8 @@ Requires: qubes-utils Requires: initscripts # for qubes-desktop-run Requires: pygobject3-base +# for qubes-desktop-file-install +Requires: pyxdg %if %{fedora} >= 20 # gpk-update-viewer required by qubes-manager Requires: gnome-packagekit-updater @@ -127,8 +129,7 @@ if [ -e /etc/init/serial.conf ]; then fi %triggerin -- pulseaudio-module-x11 -sed -i '/^\(Not\|Only\)ShowIn/d' /etc/xdg/autostart/pulseaudio.desktop -echo 'NotShowIn=QUBES;' >> /etc/xdg/autostart/pulseaudio.desktop +/usr/bin/qubes-desktop-file-install --force --dir /usr/share/qubes/xdg/autostart --remove-show-in --add-not-show-in X-QUBES /etc/xdg/autostart/pulseaudio.desktop %triggerin -- iptables if ! grep -q IPTABLES_DATA /etc/sysconfig/iptables-config; then @@ -158,50 +159,9 @@ for F in plymouth-shutdown prefdm splash-manager start-ttys tty ; do fi done -remove_ShowIn () { - if [ -e /etc/xdg/autostart/$1.desktop ]; then - sed -i '/^\(Not\|Only\)ShowIn/d' /etc/xdg/autostart/$1.desktop - fi -} - -# reenable if disabled by some earlier version of package -remove_ShowIn abrt-applet.desktop imsettings-start.desktop - -# don't want it at all -for F in deja-dup-monitor krb5-auth-dialog pulseaudio restorecond sealertauto gnome-power-manager gnome-sound-applet gnome-screensaver orca-autostart; do - if [ -e /etc/xdg/autostart/$F.desktop ]; then - remove_ShowIn $F - echo 'NotShowIn=QUBES;' >> /etc/xdg/autostart/$F.desktop - fi -done - -# don't want it in DisposableVM -for F in gcm-apply ; do - if [ -e /etc/xdg/autostart/$F.desktop ]; then - remove_ShowIn $F - echo 'NotShowIn=DisposableVM;' >> /etc/xdg/autostart/$F.desktop - fi -done - -# want it in AppVM only -for F in gnome-keyring-gpg gnome-keyring-pkcs11 gnome-keyring-secrets gnome-keyring-ssh gnome-settings-daemon user-dirs-update-gtk gsettings-data-convert ; do - if [ -e /etc/xdg/autostart/$F.desktop ]; then - remove_ShowIn $F - echo 'OnlyShowIn=GNOME;AppVM;' >> /etc/xdg/autostart/$F.desktop - fi -done - -# remove existing rule to add own later -for F in gpk-update-icon nm-applet ; do - remove_ShowIn $F -done - -echo 'OnlyShowIn=GNOME;UpdateableVM;' >> /etc/xdg/autostart/gpk-update-icon.desktop || : -%if %{fedora} >= 20 -echo 'OnlyShowIn=GNOME;QUBES;' >> /etc/xdg/autostart/nm-applet.desktop || : -%else -echo 'OnlyShowIn=GNOME;NetVM;' >> /etc/xdg/autostart/nm-applet.desktop || : -%endif +# Update all autostart xdg desktop configuration files (modified copies are +# placed in /usr/share/qubes/xdg/autostart) +/usr/lib/qubes/qubes-trigger-desktop-file-install clean # Create NetworkManager configuration if we do not have it if ! [ -e /etc/NetworkManager/NetworkManager.conf ]; then @@ -352,6 +312,8 @@ if [ $1 -eq 0 ] ; then if [ -L /lib/firmware/updates ]; then rm /lib/firmware/updates fi + + rm -rf /usr/share/qubes/xdg fi %posttrans @@ -409,6 +371,7 @@ rm -f %{name}-%{version} %config(noreplace) /etc/yum.repos.d/qubes-r3.repo /etc/yum/pluginconf.d/yum-qubes-hooks.conf /etc/yum/post-actions/qubes-trigger-sync-appmenus.action +/etc/yum/post-actions/qubes-trigger-desktop-file-install.action /usr/lib/systemd/system/user@.service.d/90-session-stop-timeout.conf /usr/sbin/qubes-serial-login /usr/bin/qvm-copy-to-vm @@ -421,6 +384,7 @@ rm -f %{name}-%{version} /usr/bin/qubes-desktop-run /usr/bin/qrexec-fork-server /usr/bin/qrexec-client-vm +/usr/bin/qubes-desktop-file-install %dir /usr/lib/qubes /usr/lib/qubes/vusb-ctl.py* /usr/lib/qubes/dispvm-prerun.sh @@ -450,6 +414,7 @@ rm -f %{name}-%{version} /usr/lib/qubes/wrap-in-html-if-url.sh /usr/lib/qubes/iptables-updates-proxy /usr/lib/qubes/close-window +/usr/lib/qubes/qubes-trigger-desktop-file-install /usr/lib/yum-plugins/yum-qubes-hooks.py* /usr/sbin/qubes-firewall /usr/sbin/qubes-netwatcher diff --git a/vm-systemd/misc-post.sh b/vm-systemd/misc-post.sh index d3717d3..a68fc90 100755 --- a/vm-systemd/misc-post.sh +++ b/vm-systemd/misc-post.sh @@ -27,24 +27,24 @@ INTERFACE=eth0 /usr/lib/qubes/setup-ip # Start services which haven't own proper systemd unit: # Start AppVM specific services +INSTALL_CMD='/usr/bin/qubes-desktop-file-install --force --dir /usr/share/qubes/xdg/autostart' + if [ ! -f /etc/systemd/system/cups.service ]; then if [ -f /var/run/qubes-service/cups ]; then /usr/sbin/service cups start # Allow also notification icon - sed -i -e '/^NotShowIn=.*QUBES/s/;QUBES//' /etc/xdg/autostart/print-applet.desktop + $INSTALL_CMD --remove-not-show-in X-QUBES /etc/xdg/autostart/print-applet.desktop else # Disable notification icon - sed -i -e '/QUBES/!s/^NotShowIn=\(.*\)/NotShowIn=QUBES;\1/' /etc/xdg/autostart/print-applet.desktop + $INSTALL_CMD --add-not-show-in X-QUBES /etc/xdg/autostart/print-applet.desktop fi fi if [ -f /var/run/qubes-service/network-manager ]; then # Allow also notification icon - sed -i -e '/QUBES/!s/^OnlyShowIn=.*/\0QUBES;/' /etc/xdg/autostart/nm-applet.desktop - sed -i -e '/^NotShowIn=.*/s/QUBES;//' /etc/xdg/autostart/nm-applet.desktop + $INSTALL_CMD --remove-not-show-in X-QUBES --add-only-show-in X-QUBES /etc/xdg/autostart/nm-applet.desktop else # Disable notification icon - sed -i -e '/^OnlyShowIn=.*/s/QUBES;//' /etc/xdg/autostart/nm-applet.desktop - sed -i -e '/QUBES/!s/^NotShowIn=.*/\0QUBES;/' /etc/xdg/autostart/nm-applet.desktop + $INSTALL_CMD --remove-only-show-in X-QUBES --add-not-show-in X-QUBES /etc/xdg/autostart/nm-applet.desktop fi exit 0