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/control b/debian/control index 31a397b..5d49ca4 100644 --- a/debian/control +++ b/debian/control @@ -9,8 +9,53 @@ Vcs-Git: git://git.qubes-os.org/marmarek/core-agent-linux.git Package: qubes-core-agent Architecture: any -Depends: qubes-utils (>= 3.0.1), libvchan-xen, xenstore-utils, iptables-persistent, xserver-xorg-video-dummy, xen-utils-common, ethtool, python2.7, python-gi, init-system-helpers, xdg-user-dirs, iptables, net-tools, initscripts, imagemagick, fakeroot, systemd, locales, sudo, dmsetup, psmisc, ncurses-term, xserver-xorg-core, x11-xserver-utils, xinit, ${shlibs:Depends}, ${misc:Depends} -Recommends: tinyproxy, gnome-themes-standard, xsettingsd, gnome-packagekit, chrony, ntpdate, network-manager (>= 0.8.1-1), network-manager-gnome, haveged, libnotify-bin, notify-osd, gnome-terminal, python-nautilus, yum, yum-utils, cups, system-config-printer +Depends: + dmsetup, + ethtool, + fakeroot, + imagemagick, + init-system-helpers, + initscripts, + iptables, + iptables-persistent, + libvchan-xen, + locales, + ncurses-term, + net-tools, + psmisc, + python2.7, + python-gi, + qubes-utils (>= 3.0.1), + sudo, + systemd, + x11-xserver-utils, + xdg-user-dirs, + xen-utils-common, + xenstore-utils, + xinit, + xserver-xorg-core, + xserver-xorg-video-dummy, + ${shlibs:Depends}, + ${misc:Depends} +Recommends: + chrony, + cups, + gnome-packagekit, + gnome-terminal, + gnome-themes-standard, + haveged, + libnotify-bin, + network-manager (>= 0.8.1-1), + network-manager-gnome, + notify-osd, + ntpdate, + python-nautilus, + python-xdg, + system-config-printer, + tinyproxy, + xsettingsd, + yum, + yum-utils Conflicts: qubes-core-agent-linux, firewalld, qubes-core-vm-sysvinit Description: Qubes core agent This package includes various daemons necessary for qubes domU support, 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