Merge branch 'core3-devel'
@ -4,4 +4,4 @@ language: generic
|
||||
install: git clone https://github.com/QubesOS/qubes-builder ~/qubes-builder
|
||||
script: ~/qubes-builder/scripts/travis-build
|
||||
env:
|
||||
- DIST_DOM0=fc23 USE_QUBES_REPO_VERSION=3.2 USE_QUBES_REPO_TESTING=1
|
||||
- DIST_DOM0=fc25 USE_QUBES_REPO_VERSION=4.0 USE_QUBES_REPO_TESTING=1
|
||||
|
@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Name=Command Prompt
|
||||
Comment=Use the command line
|
||||
Categories=GNOME;GTK;Utility;TerminalEmulator;System;
|
||||
Exec=cmd /c start cmd
|
@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Name=Explorer
|
||||
Comment=Browse files
|
||||
Categories=Utility;Core;
|
||||
Exec=explorer
|
@ -1,8 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Terminal=false
|
||||
Name=Internet Explorer
|
||||
Comment=Browse the Web
|
||||
Categories=Network;WebBrowser;
|
||||
Exec=C:\\Program Files\\Internet Explorer\\iexplore.exe
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=qubes-vm-settings %VMNAME% applications
|
||||
Icon=qubes-appmenu-select
|
||||
Terminal=false
|
||||
Name=%VMNAME%: Add more shortcuts...
|
||||
GenericName=%VMNAME%: Add more shortcuts...
|
||||
StartupNotify=false
|
||||
Categories=System;X-Qubes-VM;
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=sh -c 'echo firefox | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
|
||||
Icon=dispvm-red
|
||||
Terminal=false
|
||||
Name=DispVM: Firefox web browser
|
||||
GenericName=DispVM: Web browser
|
||||
StartupNotify=false
|
||||
Categories=Network;X-Qubes-VM;
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=sh -c 'echo xterm | /usr/lib/qubes/qfile-daemon-dvm qubes.VMShell dom0 DEFAULT red'
|
||||
Icon=dispvm-red
|
||||
Terminal=false
|
||||
Name=DispVM: xterm
|
||||
GenericName=DispVM: Terminal
|
||||
StartupNotify=false
|
||||
Categories=Network;X-Qubes-VM;
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=DisposableVM
|
||||
Icon=dispvm-red
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=ServiceVM: %VMNAME%
|
||||
Icon=%XDGICON%
|
@ -1,10 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Exec=qvm-start --quiet --tray %VMNAME%
|
||||
Icon=%XDGICON%
|
||||
Terminal=false
|
||||
Name=%VMNAME%: Start
|
||||
GenericName=%VMNAME%: Start
|
||||
StartupNotify=false
|
||||
Categories=System;X-Qubes-VM;
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=Template: %VMNAME%
|
||||
Icon=%XDGICON%
|
@ -1,5 +0,0 @@
|
||||
[Desktop Entry]
|
||||
Encoding=UTF-8
|
||||
Type=Directory
|
||||
Name=Domain: %VMNAME%
|
||||
Icon=%XDGICON%
|
@ -1,20 +0,0 @@
|
||||
#!/bin/sh
|
||||
SRC=$1
|
||||
DSTDIR=$2
|
||||
VMNAME=$3
|
||||
VMDIR=$4
|
||||
XDGICON=$5
|
||||
|
||||
DST=$DSTDIR/$VMNAME-$(basename $SRC)
|
||||
|
||||
if ! [ -r "$SRC" ]; then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sed \
|
||||
-e "s/%VMNAME%/$VMNAME/" \
|
||||
-e "s %VMDIR% $VMDIR " \
|
||||
-e "s/%XDGICON%/$XDGICON/" \
|
||||
<$SRC >$DST
|
||||
|
||||
|
@ -1,14 +0,0 @@
|
||||
#!/bin/sh
|
||||
SRC=$1
|
||||
DST=$2
|
||||
VMNAME=$3
|
||||
VMDIR=$4
|
||||
XDGICON=$5
|
||||
|
||||
sed \
|
||||
-e "s/%VMNAME%/$VMNAME/" \
|
||||
-e "s %VMDIR% $VMDIR " \
|
||||
-e "s/%XDGICON%/$XDGICON/" \
|
||||
<$SRC >$DST
|
||||
|
||||
|
@ -1,64 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
SRCDIR=$1
|
||||
VMNAME=$2
|
||||
VMTYPE=$3
|
||||
if [ -z "$VMTYPE" ]; then
|
||||
VMTYPE=appvms
|
||||
fi
|
||||
XDGICON=$4
|
||||
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
|
||||
APPSDIR=$VMDIR/apps
|
||||
|
||||
if [ $# -lt 2 ]; then
|
||||
echo "usage: $0 <apps_templates_dir> <vmname> [appvms|vm-templates|servicevms]"
|
||||
exit
|
||||
fi
|
||||
mkdir -p $APPSDIR
|
||||
|
||||
if [ "$SRCDIR" != "none" ]; then
|
||||
echo "--> Converting Appmenu Templates..."
|
||||
if [ -r "$VMDIR/whitelisted-appmenus.list" ]; then
|
||||
cat $VMDIR/whitelisted-appmenus.list | xargs -I{} /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh $SRCDIR/{} $APPSDIR $VMNAME $VMDIR $XDGICON
|
||||
else
|
||||
find $SRCDIR -name "*.desktop" $CHECK_WHITELISTED -exec /usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh {} $APPSDIR $VMNAME $VMDIR $XDGICON \;
|
||||
fi
|
||||
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh /usr/share/qubes-appmenus/qubes-appmenu-select.desktop $APPSDIR $VMNAME $VMDIR $XDGICON
|
||||
|
||||
if [ "$VMTYPE" = "vm-templates" ]; then
|
||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-templatevm.directory.template
|
||||
elif [ "$VMTYPE" = "servicevms" ]; then
|
||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-servicevm.directory.template
|
||||
else
|
||||
DIR_TEMPLATE=/usr/share/qubes-appmenus/qubes-vm.directory.template
|
||||
fi
|
||||
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh $DIR_TEMPLATE $APPSDIR/$VMNAME-vm.directory $VMNAME $VMDIR $XDGICON
|
||||
fi
|
||||
|
||||
echo "--> Adding Apps to the Menu..."
|
||||
LC_COLLATE=C xdg-desktop-menu install --noupdate $APPSDIR/*.directory $APPSDIR/*.desktop
|
||||
|
||||
if [ -n "$KDE_SESSION_UID" -a -z "$SKIP_CACHE_REBUILD" ]; then
|
||||
xdg-desktop-menu forceupdate
|
||||
kbuildsycoca$KDE_SESSION_VERSION
|
||||
fi
|
@ -1,394 +0,0 @@
|
||||
#!/usr/bin/python2
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2010 Joanna Rutkowska <joanna@invisiblethingslab.com>
|
||||
# Copyright (C) 2013 Marek Marczykowski <marmarek@invisiblethingslab.com>
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
|
||||
# USA.
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import shutil
|
||||
import dbus
|
||||
|
||||
from qubes.qubes import QubesVm, QubesHVm
|
||||
from qubes.qubes import vm_files, system_path
|
||||
|
||||
import qubes.imgconverter
|
||||
|
||||
vm_files['appmenus_templates_subdir'] = 'apps.templates'
|
||||
vm_files['appmenus_template_icons_subdir'] = 'apps.tempicons'
|
||||
vm_files['appmenus_subdir'] = 'apps'
|
||||
vm_files['appmenus_icons_subdir'] = 'apps.icons'
|
||||
vm_files['appmenus_template_templates_subdir'] = 'apps-template.templates'
|
||||
vm_files['appmenus_whitelist'] = 'whitelisted-appmenus.list'
|
||||
|
||||
system_path['appmenu_start_hvm_template'] = \
|
||||
'/usr/share/qubes-appmenus/qubes-start.desktop'
|
||||
system_path['appmenu_create_cmd'] = \
|
||||
'/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh'
|
||||
system_path['appmenu_remove_cmd'] = \
|
||||
'/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh'
|
||||
|
||||
|
||||
def QubesVm_get_appmenus_templates_dir(self):
|
||||
if self.updateable:
|
||||
return self.absolute_path(vm_files["appmenus_templates_subdir"], None)
|
||||
elif self.template is not None:
|
||||
return self.template.appmenus_templates_dir
|
||||
else:
|
||||
return None
|
||||
|
||||
QubesVm.appmenus_templates_dir = property(QubesVm_get_appmenus_templates_dir)
|
||||
|
||||
|
||||
def QubesVm_get_appmenus_template_icons_dir(self):
|
||||
if self.updateable:
|
||||
return self.absolute_path(vm_files["appmenus_template_icons_subdir"],
|
||||
None)
|
||||
elif self.template:
|
||||
return self.template.appmenus_template_icons_dir
|
||||
else:
|
||||
return None
|
||||
|
||||
QubesVm.appmenus_template_icons_dir = \
|
||||
property(QubesVm_get_appmenus_template_icons_dir)
|
||||
|
||||
|
||||
def QubesVm_get_appmenus_dir(self):
|
||||
return self.absolute_path(vm_files["appmenus_subdir"], None)
|
||||
|
||||
QubesVm.appmenus_dir = property(QubesVm_get_appmenus_dir)
|
||||
|
||||
|
||||
def QubesVm_get_appmenus_icons_dir(self):
|
||||
return self.absolute_path(vm_files["appmenus_icons_subdir"], None)
|
||||
|
||||
|
||||
QubesVm.appmenus_icons_dir = property(QubesVm_get_appmenus_icons_dir)
|
||||
|
||||
|
||||
def get_whitelist_names(vm):
|
||||
return (whitelist for whitelist in (
|
||||
vm_files["appmenus_whitelist"],
|
||||
'vm-' + vm_files["appmenus_whitelist"],
|
||||
'netvm-' + vm_files["appmenus_whitelist"])
|
||||
if os.path.exists(os.path.join(vm.dir_path, whitelist)))
|
||||
|
||||
|
||||
def QubesVm_appmenus_create(self, verbose=False, source_template=None):
|
||||
if source_template is None:
|
||||
source_template = self.template
|
||||
|
||||
if self.internal:
|
||||
return
|
||||
if self.is_disposablevm():
|
||||
return
|
||||
|
||||
if self.is_netvm():
|
||||
vmtype = 'servicevms'
|
||||
elif self.is_template():
|
||||
vmtype = 'vm-templates'
|
||||
else:
|
||||
vmtype = 'appvms'
|
||||
|
||||
try:
|
||||
msgoutput = None if verbose else open(os.devnull, 'w')
|
||||
if source_template is not None:
|
||||
subprocess.check_call([system_path["appmenu_create_cmd"],
|
||||
source_template.appmenus_templates_dir,
|
||||
self.name, vmtype, self.label.icon],
|
||||
stdout=msgoutput, stderr=msgoutput)
|
||||
elif self.appmenus_templates_dir is not None:
|
||||
subprocess.check_call([system_path["appmenu_create_cmd"],
|
||||
self.appmenus_templates_dir, self.name,
|
||||
vmtype, self.label.icon],
|
||||
stdout=msgoutput, stderr=msgoutput)
|
||||
else:
|
||||
# Only add apps to menu
|
||||
subprocess.check_call([system_path["appmenu_create_cmd"],
|
||||
"none", self.name, vmtype,
|
||||
self.label.icon],
|
||||
stdout=msgoutput, stderr=msgoutput)
|
||||
except subprocess.CalledProcessError:
|
||||
print >> sys.stderr, "Ooops, there was a problem creating appmenus " \
|
||||
"for {0} VM!".format(self.name)
|
||||
|
||||
|
||||
def QubesVm_appmenus_remove(self):
|
||||
if self.is_netvm():
|
||||
vmtype = 'servicevms'
|
||||
elif self.is_template():
|
||||
vmtype = 'vm-templates'
|
||||
else:
|
||||
vmtype = 'appvms'
|
||||
subprocess.check_call([system_path["appmenu_remove_cmd"], self.name,
|
||||
vmtype], stderr=open(os.devnull, 'w'))
|
||||
|
||||
|
||||
def QubesVm_appmenus_cleanup(self):
|
||||
srcdir = self.appmenus_templates_dir
|
||||
if srcdir is None:
|
||||
return
|
||||
if not os.path.exists(srcdir):
|
||||
return
|
||||
if not os.path.exists(self.appmenus_dir):
|
||||
return
|
||||
|
||||
for appmenu in os.listdir(self.appmenus_dir):
|
||||
if not os.path.exists(os.path.join(srcdir, appmenu)):
|
||||
os.unlink(os.path.join(self.appmenus_dir, appmenu))
|
||||
|
||||
|
||||
def QubesVm_appmenus_replace_entry(self, old_name, new_name):
|
||||
for whitelist in get_whitelist_names(self):
|
||||
whitelist_path = os.path.join(self.dir_path, whitelist)
|
||||
with open(whitelist_path) as f:
|
||||
old_lines = f.readlines()
|
||||
new_lines = [
|
||||
(new_name + '\n' if l == old_name + '\n' else l)
|
||||
for l in old_lines]
|
||||
if new_lines != old_lines:
|
||||
with open(whitelist_path, 'w') as f:
|
||||
f.write(''.join(new_lines))
|
||||
|
||||
|
||||
def QubesVm_appicons_create(self, srcdir=None, force=False):
|
||||
if srcdir is None:
|
||||
srcdir = self.appmenus_template_icons_dir
|
||||
if srcdir is None:
|
||||
return
|
||||
if not os.path.exists(srcdir):
|
||||
return
|
||||
|
||||
if self.internal:
|
||||
return
|
||||
if self.is_disposablevm():
|
||||
return
|
||||
|
||||
whitelist = os.path.join(self.dir_path, vm_files['appmenus_whitelist'])
|
||||
if os.path.exists(whitelist):
|
||||
whitelist = [line.strip() for line in open(whitelist)]
|
||||
else:
|
||||
whitelist = None
|
||||
|
||||
if not os.path.exists(self.appmenus_icons_dir):
|
||||
os.mkdir(self.appmenus_icons_dir)
|
||||
elif not os.path.isdir(self.appmenus_icons_dir):
|
||||
os.unlink(self.appmenus_icons_dir)
|
||||
os.mkdir(self.appmenus_icons_dir)
|
||||
|
||||
for icon in os.listdir(srcdir):
|
||||
desktop = os.path.splitext(icon)[0] + '.desktop'
|
||||
if whitelist and desktop not in whitelist:
|
||||
continue
|
||||
|
||||
src_icon = os.path.join(srcdir, icon)
|
||||
dst_icon = os.path.join(self.appmenus_icons_dir, icon)
|
||||
if not os.path.exists(dst_icon) or force or \
|
||||
os.path.getmtime(src_icon) > os.path.getmtime(dst_icon):
|
||||
qubes.imgconverter.tint(src_icon, dst_icon, self.label.color)
|
||||
|
||||
|
||||
def QubesVm_appicons_remove(self):
|
||||
if not os.path.exists(self.appmenus_icons_dir):
|
||||
return
|
||||
for icon in os.listdir(self.appmenus_icons_dir):
|
||||
os.unlink(os.path.join(self.appmenus_icons_dir, icon))
|
||||
|
||||
|
||||
def QubesVm_appicons_cleanup(self):
|
||||
srcdir = self.appmenus_template_icons_dir
|
||||
if srcdir is None:
|
||||
return
|
||||
if not os.path.exists(srcdir):
|
||||
return
|
||||
if not os.path.exists(self.appmenus_icons_dir):
|
||||
return
|
||||
|
||||
for icon in os.listdir(self.appmenus_icons_dir):
|
||||
if not os.path.exists(os.path.join(srcdir, icon)):
|
||||
os.unlink(os.path.join(self.appmenus_icons_dir, icon))
|
||||
|
||||
|
||||
def QubesVm_pre_rename(self, new_name):
|
||||
self.appmenus_remove()
|
||||
|
||||
|
||||
def QubesVm_post_rename(self, old_name):
|
||||
self.appmenus_create()
|
||||
|
||||
|
||||
def QubesVm_create_on_disk(self, verbose, source_template):
|
||||
if isinstance(self, QubesHVm) and source_template is None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating appmenus directory: {0}".format(
|
||||
self.appmenus_templates_dir)
|
||||
os.mkdir(self.appmenus_templates_dir)
|
||||
shutil.copy(system_path["appmenu_start_hvm_template"],
|
||||
self.appmenus_templates_dir)
|
||||
|
||||
source_whitelist_filename = 'vm-' + vm_files["appmenus_whitelist"]
|
||||
if self.is_netvm():
|
||||
source_whitelist_filename = 'netvm-' + vm_files["appmenus_whitelist"]
|
||||
if source_template and os.path.exists(
|
||||
os.path.join(source_template.dir_path, source_whitelist_filename)):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Creating default whitelisted apps list: {0}". \
|
||||
format(self.dir_path + '/' + vm_files["whitelisted_appmenus"])
|
||||
shutil.copy(
|
||||
os.path.join(source_template.dir_path, source_whitelist_filename),
|
||||
os.path.join(self.dir_path, vm_files["whitelisted_appmenus"]))
|
||||
|
||||
if source_template and self.updateable:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \
|
||||
format(source_template.appmenus_templates_dir,
|
||||
self.appmenus_templates_dir)
|
||||
if os.path.isdir(source_template.appmenus_templates_dir):
|
||||
shutil.copytree(source_template.appmenus_templates_dir,
|
||||
self.appmenus_templates_dir)
|
||||
else:
|
||||
os.mkdir(self.appmenus_templates_dir)
|
||||
if os.path.isdir(source_template.appmenus_template_icons_dir):
|
||||
shutil.copytree(source_template.appmenus_template_icons_dir,
|
||||
self.appmenus_template_icons_dir)
|
||||
else:
|
||||
os.mkdir(self.appmenus_template_icons_dir)
|
||||
|
||||
# Create appmenus
|
||||
self.appicons_create()
|
||||
self.appmenus_create(verbose=verbose)
|
||||
|
||||
|
||||
def QubesVm_clone_disk_files(self, src_vm, verbose):
|
||||
if src_vm.updateable and src_vm.appmenus_templates_dir is not None and \
|
||||
self.appmenus_templates_dir is not None:
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \
|
||||
format(src_vm.appmenus_templates_dir,
|
||||
self.appmenus_templates_dir)
|
||||
shutil.copytree(src_vm.appmenus_templates_dir,
|
||||
self.appmenus_templates_dir)
|
||||
|
||||
if src_vm.updateable and src_vm.appmenus_template_icons_dir is not None \
|
||||
and self.appmenus_template_icons_dir is not None and \
|
||||
os.path.isdir(src_vm.appmenus_template_icons_dir):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying the template's appmenus " \
|
||||
"template icons dir:\n{0} ==>\n{1}". \
|
||||
format(src_vm.appmenus_template_icons_dir,
|
||||
self.appmenus_template_icons_dir)
|
||||
shutil.copytree(src_vm.appmenus_template_icons_dir,
|
||||
self.appmenus_template_icons_dir)
|
||||
|
||||
for whitelist in get_whitelist_names(src_vm):
|
||||
if verbose:
|
||||
print >> sys.stderr, "--> Copying whitelisted apps list: {0}". \
|
||||
format(whitelist)
|
||||
shutil.copy(os.path.join(src_vm.dir_path, whitelist),
|
||||
os.path.join(self.dir_path, whitelist))
|
||||
|
||||
# Create appmenus
|
||||
self.appicons_create()
|
||||
self.appmenus_create(verbose=verbose)
|
||||
|
||||
|
||||
def QubesVm_remove_from_disk(self):
|
||||
self.appmenus_remove()
|
||||
|
||||
|
||||
def QubesVm_label_setter(self, _):
|
||||
self.appicons_create(force=True)
|
||||
|
||||
# Apparently desktop environments heavily caches the icons,
|
||||
# see #751 for details
|
||||
if "plasma" in os.environ.get("DESKTOP_SESSION", ""):
|
||||
try:
|
||||
os.unlink(os.path.expandvars(
|
||||
"$HOME/.kde/cache-$HOSTNAME/icon-cache.kcache"))
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
notify_object = dbus.SessionBus().get_object(
|
||||
"org.freedesktop.Notifications",
|
||||
"/org/freedesktop/Notifications")
|
||||
notify_object.Notify(
|
||||
"Qubes", 0, self.label.icon, "Qubes",
|
||||
"You will need to log off and log in again for the VM icons "
|
||||
"to update in the KDE launcher menu",
|
||||
[], [], 10000,
|
||||
dbus_interface="org.freedesktop.Notifications")
|
||||
except:
|
||||
pass
|
||||
elif "xfce" in os.environ.get("DESKTOP_SESSION", ""):
|
||||
self.appmenus_remove()
|
||||
self.appmenus_create()
|
||||
|
||||
|
||||
def QubesVm_appmenus_recreate(self):
|
||||
"""
|
||||
Force recreation of all appmenus and icons. For example when VM label
|
||||
color was changed
|
||||
"""
|
||||
self.appmenus_remove()
|
||||
self.appmenus_cleanup()
|
||||
self.appicons_remove()
|
||||
self.appicons_create()
|
||||
self.appmenus_create()
|
||||
|
||||
|
||||
def QubesVm_appmenus_update(self):
|
||||
"""
|
||||
Similar to appmenus_recreate, but do not touch unchanged files
|
||||
"""
|
||||
self.appmenus_remove()
|
||||
self.appmenus_cleanup()
|
||||
self.appicons_create()
|
||||
self.appicons_cleanup()
|
||||
self.appmenus_create()
|
||||
|
||||
|
||||
def QubesVm_set_attr(self, name, newvalue, oldvalue):
|
||||
if name == 'internal':
|
||||
if newvalue and not oldvalue:
|
||||
self.appmenus_remove()
|
||||
elif not newvalue and oldvalue:
|
||||
self.appmenus_create()
|
||||
|
||||
|
||||
# new methods
|
||||
QubesVm.appmenus_create = QubesVm_appmenus_create
|
||||
QubesVm.appmenus_remove = QubesVm_appmenus_remove
|
||||
QubesVm.appmenus_cleanup = QubesVm_appmenus_cleanup
|
||||
QubesVm.appmenus_recreate = QubesVm_appmenus_recreate
|
||||
QubesVm.appmenus_update = QubesVm_appmenus_update
|
||||
QubesVm.appmenus_replace_entry = QubesVm_appmenus_replace_entry
|
||||
QubesVm.appicons_create = QubesVm_appicons_create
|
||||
QubesVm.appicons_cleanup = QubesVm_appicons_cleanup
|
||||
QubesVm.appicons_remove = QubesVm_appicons_remove
|
||||
|
||||
# hooks for existing methods
|
||||
QubesVm.hooks_pre_rename.append(QubesVm_pre_rename)
|
||||
QubesVm.hooks_post_rename.append(QubesVm_post_rename)
|
||||
QubesVm.hooks_create_on_disk.append(QubesVm_create_on_disk)
|
||||
QubesVm.hooks_clone_disk_files.append(QubesVm_clone_disk_files)
|
||||
QubesVm.hooks_remove_from_disk.append(QubesVm_remove_from_disk)
|
||||
QubesVm.hooks_label_setter.append(QubesVm_label_setter)
|
||||
QubesVm.hooks_set_attr.append(QubesVm_set_attr)
|
@ -1,392 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# Copyright (C) 2011 Marek Marczykowski <marmarek@mimuw.edu.pl>
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import optparse
|
||||
|
||||
import subprocess
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import pipes
|
||||
|
||||
from optparse import OptionParser
|
||||
from qubes.qubes import QubesVmCollection, QubesException, system_path
|
||||
from qubes.qubes import QubesHVm
|
||||
from qubes.qubes import vm_files
|
||||
import qubes.imgconverter
|
||||
from qubes.qubes import vmm
|
||||
|
||||
# fields required to be present (and verified) in retrieved desktop file
|
||||
required_fields = ["Name", "Exec"]
|
||||
|
||||
# limits
|
||||
appmenus_line_size = 1024
|
||||
appmenus_line_count = 100000
|
||||
|
||||
# regexps for sanitization of retrieved values
|
||||
std_re = re.compile(r"^[/a-zA-Z0-9.,:&()_ -]*$")
|
||||
fields_regexp = {
|
||||
"Name": std_re,
|
||||
"GenericName": std_re,
|
||||
"Comment": std_re,
|
||||
"Categories": re.compile(r"^[a-zA-Z0-9/.;:'() -]*$"),
|
||||
"Exec": re.compile(r"^[a-zA-Z0-9()_%&>/{}\"'\\:.= -]*$"),
|
||||
"Icon": re.compile(r"^[a-zA-Z0-9/_.-]*$"),
|
||||
}
|
||||
|
||||
CATEGORIES_WHITELIST = {
|
||||
# Main Categories
|
||||
# http://standards.freedesktop.org/menu-spec/1.1/apa.html 20140507
|
||||
'AudioVideo', 'Audio', 'Video', 'Development', 'Education', 'Game',
|
||||
'Graphics', 'Network', 'Office', 'Science', 'Settings', 'System',
|
||||
'Utility',
|
||||
|
||||
# Additional Categories
|
||||
# http://standards.freedesktop.org/menu-spec/1.1/apas02.html
|
||||
'Building', 'Debugger', 'IDE', 'GUIDesigner', 'Profiling',
|
||||
'RevisionControl', 'Translation', 'Calendar', 'ContactManagement',
|
||||
'Database', 'Dictionary', 'Chart', 'Email', 'Finance', 'FlowChart', 'PDA',
|
||||
'ProjectManagement', 'Presentation', 'Spreadsheet', 'WordProcessor',
|
||||
'2DGraphics', 'VectorGraphics', 'RasterGraphics', '3DGraphics', 'Scanning',
|
||||
'OCR', 'Photography', 'Publishing', 'Viewer', 'TextTools',
|
||||
'DesktopSettings', 'HardwareSettings', 'Printing', 'PackageManager',
|
||||
'Dialup', 'InstantMessaging', 'Chat', 'IRCClient', 'Feed', 'FileTransfer',
|
||||
'HamRadio', 'News', 'P2P', 'RemoteAccess', 'Telephony', 'TelephonyTools',
|
||||
'VideoConference', 'WebBrowser', 'WebDevelopment', 'Midi', 'Mixer',
|
||||
'Sequencer', 'Tuner', 'TV', 'AudioVideoEditing', 'Player', 'Recorder',
|
||||
'DiscBurning', 'ActionGame', 'AdventureGame', 'ArcadeGame', 'BoardGame',
|
||||
'BlocksGame', 'CardGame', 'KidsGame', 'LogicGame', 'RolePlaying',
|
||||
'Shooter', 'Simulation', 'SportsGame', 'StrategyGame', 'Art',
|
||||
'Construction', 'Music', 'Languages', 'ArtificialIntelligence',
|
||||
'Astronomy', 'Biology', 'Chemistry', 'ComputerScience',
|
||||
'DataVisualization', 'Economy', 'Electricity', 'Geography', 'Geology',
|
||||
'Geoscience', 'History', 'Humanities', 'ImageProcessing', 'Literature',
|
||||
'Maps', 'Math', 'NumericalAnalysis', 'MedicalSoftware', 'Physics',
|
||||
'Robotics', 'Spirituality', 'Sports', 'ParallelComputing', 'Amusement',
|
||||
'Archiving', 'Compression', 'Electronics', 'Emulator', 'Engineering',
|
||||
'FileTools', 'FileManager', 'TerminalEmulator', 'Filesystem', 'Monitor',
|
||||
'Security', 'Accessibility', 'Calculator', 'Clock', 'TextEditor',
|
||||
'Documentation', 'Adult', 'Core', 'KDE', 'GNOME', 'XFCE', 'GTK', 'Qt',
|
||||
'Motif', 'Java', 'ConsoleOnly',
|
||||
|
||||
# Reserved Categories (not whitelisted)
|
||||
# http://standards.freedesktop.org/menu-spec/1.1/apas03.html
|
||||
# 'Screensaver', 'TrayIcon', 'Applet', 'Shell',
|
||||
}
|
||||
|
||||
|
||||
def sanitise_categories(untrusted_value):
|
||||
untrusted_categories = (c.strip() for c in untrusted_value.split(';') if c)
|
||||
categories = (c for c in untrusted_categories if c in CATEGORIES_WHITELIST)
|
||||
|
||||
return ';'.join(categories) + ';'
|
||||
|
||||
|
||||
def fallback_hvm_appmenulist():
|
||||
p = subprocess.Popen(["grep", "-rH", "=", "/usr/share/qubes-appmenus/hvm"],
|
||||
stdout=subprocess.PIPE)
|
||||
(stdout, stderr) = p.communicate()
|
||||
return stdout.splitlines()
|
||||
|
||||
|
||||
def get_appmenus(vm):
|
||||
global appmenus_line_count
|
||||
global appmenus_line_size
|
||||
untrusted_appmenulist = []
|
||||
if vm is None:
|
||||
while appmenus_line_count > 0:
|
||||
untrusted_line = sys.stdin.readline(appmenus_line_size)
|
||||
if untrusted_line == "":
|
||||
break
|
||||
untrusted_appmenulist.append(untrusted_line.strip())
|
||||
appmenus_line_count -= 1
|
||||
if appmenus_line_count == 0:
|
||||
raise QubesException("Line count limit exceeded")
|
||||
else:
|
||||
p = vm.run('QUBESRPC qubes.GetAppmenus dom0', passio_popen=True,
|
||||
gui=False)
|
||||
while appmenus_line_count > 0:
|
||||
untrusted_line = p.stdout.readline(appmenus_line_size)
|
||||
if untrusted_line == "":
|
||||
break
|
||||
untrusted_appmenulist.append(untrusted_line.strip())
|
||||
appmenus_line_count -= 1
|
||||
p.wait()
|
||||
if p.returncode != 0:
|
||||
if isinstance(vm, QubesHVm):
|
||||
untrusted_appmenulist = fallback_hvm_appmenulist()
|
||||
else:
|
||||
raise QubesException("Error getting application list")
|
||||
if appmenus_line_count == 0:
|
||||
raise QubesException("Line count limit exceeded")
|
||||
|
||||
appmenus = {}
|
||||
line_rx = re.compile(
|
||||
r"([a-zA-Z0-9.()_-]+.desktop):([a-zA-Z0-9-]+(?:\[[a-zA-Z@_]+\])?)=(.*)")
|
||||
ignore_rx = re.compile(r".*([a-zA-Z0-9._-]+.desktop):(#.*|\s+)$")
|
||||
for untrusted_line in untrusted_appmenulist:
|
||||
# Ignore blank lines and comments
|
||||
if len(untrusted_line) == 0 or ignore_rx.match(untrusted_line):
|
||||
continue
|
||||
# use search instead of match to skip file path
|
||||
untrusted_m = line_rx.search(untrusted_line)
|
||||
if untrusted_m:
|
||||
filename = untrusted_m.group(1)
|
||||
assert '/' not in filename
|
||||
assert '\0' not in filename
|
||||
|
||||
untrusted_key = untrusted_m.group(2)
|
||||
assert '\0' not in untrusted_key
|
||||
assert '\x1b' not in untrusted_key
|
||||
assert '=' not in untrusted_key
|
||||
|
||||
untrusted_value = untrusted_m.group(3)
|
||||
# TODO add key-dependent asserts
|
||||
|
||||
# Look only at predefined keys
|
||||
if untrusted_key in fields_regexp:
|
||||
if fields_regexp[untrusted_key].match(untrusted_value):
|
||||
# now values are sanitized
|
||||
key = untrusted_key
|
||||
if key == 'Categories':
|
||||
value = sanitise_categories(untrusted_value)
|
||||
else:
|
||||
value = untrusted_value
|
||||
|
||||
if filename not in appmenus:
|
||||
appmenus[filename] = {}
|
||||
|
||||
appmenus[filename][key] = value
|
||||
else:
|
||||
print >> sys.stderr, \
|
||||
"Warning: ignoring key %r of %s" % \
|
||||
(untrusted_key, filename)
|
||||
# else: ignore this key
|
||||
|
||||
return appmenus
|
||||
|
||||
|
||||
def create_template(path, values):
|
||||
# check if all required fields are present
|
||||
for key in required_fields:
|
||||
if key not in values:
|
||||
print >> sys.stderr, "Warning: not creating/updating '%s' " \
|
||||
"because of missing '%s' key" % (
|
||||
path, key)
|
||||
return
|
||||
|
||||
desktop_entry = ""
|
||||
desktop_entry += "[Desktop Entry]\n"
|
||||
desktop_entry += "Version=1.0\n"
|
||||
desktop_entry += "Type=Application\n"
|
||||
desktop_entry += "Terminal=false\n"
|
||||
desktop_entry += "X-Qubes-VmName=%VMNAME%\n"
|
||||
|
||||
if 'Icon' in values:
|
||||
icon_file = os.path.splitext(os.path.split(path)[1])[0] + '.png'
|
||||
desktop_entry += "Icon={0}\n".format(os.path.join(
|
||||
'%VMDIR%', vm_files['appmenus_icons_subdir'], icon_file))
|
||||
else:
|
||||
desktop_entry += "Icon=%XDGICON%\n"
|
||||
|
||||
for key in ["Name", "GenericName"]:
|
||||
if key in values:
|
||||
desktop_entry += "{0}=%VMNAME%: {1}\n".format(key, values[key])
|
||||
|
||||
# force category X-Qubes-VM
|
||||
values["Categories"] = values.get("Categories", "") + "X-Qubes-VM;"
|
||||
|
||||
for key in ["Comment", "Categories"]:
|
||||
if key in values:
|
||||
desktop_entry += "{0}={1}\n".format(key, values[key])
|
||||
|
||||
desktop_entry += "Exec=qvm-run -q --tray -a %VMNAME% -- {0}\n".format(
|
||||
pipes.quote(values['Exec']))
|
||||
if not os.path.exists(path) or desktop_entry != open(path, "r").read():
|
||||
desktop_file = open(path, "w")
|
||||
desktop_file.write(desktop_entry)
|
||||
desktop_file.close()
|
||||
|
||||
|
||||
def main():
|
||||
env_vmname = os.environ.get("QREXEC_REMOTE_DOMAIN")
|
||||
usage = "usage: %prog [options] <vm-name>\n" \
|
||||
"Update desktop file templates for given StandaloneVM or TemplateVM"
|
||||
|
||||
parser = OptionParser(usage)
|
||||
parser.add_option("-v", "--verbose", action="store_true", dest="verbose",
|
||||
default=False)
|
||||
parser.add_option("--force-root", action="store_true", dest="force_root",
|
||||
default=False,
|
||||
help="Force to run, even with root privileges")
|
||||
parser.add_option("--force-rpc", action="store_true", dest="force_rpc",
|
||||
default=False,
|
||||
help="Force to start a new RPC call, "
|
||||
"even if called from existing one")
|
||||
# Do not use any RPC call, expects data on stdin (in qubes.GetAppmenus
|
||||
# format)
|
||||
parser.add_option("--offline-mode", dest="offline_mode",
|
||||
action="store_true", default=False,
|
||||
help=optparse.SUPPRESS_HELP)
|
||||
|
||||
(options, args) = parser.parse_args()
|
||||
if (len(args) != 1) and env_vmname is None:
|
||||
parser.error("You must specify at least the VM name!")
|
||||
|
||||
if env_vmname:
|
||||
vmname = env_vmname
|
||||
else:
|
||||
vmname = args[0]
|
||||
|
||||
if os.geteuid() == 0:
|
||||
if not options.force_root:
|
||||
print >> sys.stderr, "*** Running this tool as root is strongly " \
|
||||
"discouraged, this will lead you into " \
|
||||
"permissions problems."
|
||||
print >> sys.stderr, "Retry as unprivileged user."
|
||||
print >> sys.stderr, "... or use --force-root to continue anyway."
|
||||
exit(1)
|
||||
|
||||
if options.offline_mode:
|
||||
vmm.offline_mode = True
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vmname)
|
||||
|
||||
if vm is None:
|
||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' " \
|
||||
"does not exist in the system.".format(
|
||||
vmname)
|
||||
exit(1)
|
||||
|
||||
if vm.template is not None:
|
||||
print >> sys.stderr, "ERROR: To sync appmenus for template based VM, " \
|
||||
"do it on template instead"
|
||||
exit(1)
|
||||
|
||||
if not options.offline_mode and not vm.is_running():
|
||||
print >> sys.stderr, "ERROR: Appmenus can be retrieved only from " \
|
||||
"running VM - start it first"
|
||||
exit(1)
|
||||
|
||||
if not options.offline_mode and env_vmname is None or options.force_rpc:
|
||||
new_appmenus = get_appmenus(vm)
|
||||
else:
|
||||
options.verbose = False
|
||||
new_appmenus = get_appmenus(None)
|
||||
|
||||
if len(new_appmenus) == 0:
|
||||
print >> sys.stderr, "ERROR: No appmenus received, terminating"
|
||||
exit(1)
|
||||
|
||||
os.umask(002)
|
||||
|
||||
if not os.path.exists(vm.appmenus_templates_dir):
|
||||
os.mkdir(vm.appmenus_templates_dir)
|
||||
|
||||
if not os.path.exists(vm.appmenus_template_icons_dir):
|
||||
os.mkdir(vm.appmenus_template_icons_dir)
|
||||
|
||||
# Create new/update existing templates
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "--> Got {0} appmenus, storing to disk".format(
|
||||
str(len(new_appmenus)))
|
||||
for appmenu_file in new_appmenus.keys():
|
||||
if options.verbose:
|
||||
if os.path.exists(
|
||||
os.path.join(vm.appmenus_templates_dir, appmenu_file)):
|
||||
print >> sys.stderr, "---> Updating {0}".format(appmenu_file)
|
||||
else:
|
||||
print >> sys.stderr, "---> Creating {0}".format(appmenu_file)
|
||||
|
||||
# TODO: icons support in offline mode
|
||||
if options.offline_mode:
|
||||
new_appmenus[appmenu_file].pop('Icon', None)
|
||||
if 'Icon' in new_appmenus[appmenu_file]:
|
||||
# the following line is used for time comparison
|
||||
icondest = os.path.join(vm.appmenus_template_icons_dir,
|
||||
os.path.splitext(appmenu_file)[0] + '.png')
|
||||
|
||||
try:
|
||||
icon = qubes.imgconverter.Image. \
|
||||
get_xdg_icon_from_vm(vm,
|
||||
new_appmenus[
|
||||
appmenu_file][
|
||||
'Icon'])
|
||||
if os.path.exists(icondest):
|
||||
old_icon = qubes.imgconverter.Image.load_from_file(icondest)
|
||||
else:
|
||||
old_icon = None
|
||||
if old_icon is None or icon != old_icon:
|
||||
icon.save(icondest)
|
||||
except Exception, e:
|
||||
print >> sys.stderr, '----> Failed to get icon for {0}: {1!s}'.\
|
||||
format(appmenu_file, e)
|
||||
|
||||
if os.path.exists(icondest):
|
||||
print >> sys.stderr, '-----> Found old icon, ' \
|
||||
'using it instead'
|
||||
else:
|
||||
del new_appmenus[appmenu_file]['Icon']
|
||||
|
||||
create_template(os.path.join(vm.appmenus_templates_dir, appmenu_file),
|
||||
new_appmenus[appmenu_file])
|
||||
|
||||
# Delete appmenus of removed applications
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "--> Cleaning old files"
|
||||
for appmenu_file in os.listdir(vm.appmenus_templates_dir):
|
||||
if not appmenu_file.endswith('.desktop'):
|
||||
continue
|
||||
|
||||
if appmenu_file not in new_appmenus:
|
||||
if options.verbose:
|
||||
print >> sys.stderr, "---> Removing {0}".format(appmenu_file)
|
||||
os.unlink(os.path.join(vm.appmenus_templates_dir, appmenu_file))
|
||||
|
||||
if isinstance(vm, QubesHVm):
|
||||
if not os.path.exists(os.path.join(vm.appmenus_templates_dir,
|
||||
os.path.basename(system_path[
|
||||
'appmenu_start_hvm_template']))):
|
||||
shutil.copy(system_path['appmenu_start_hvm_template'],
|
||||
vm.appmenus_templates_dir)
|
||||
|
||||
vm.appmenus_update()
|
||||
if hasattr(vm, 'appvms'):
|
||||
os.putenv('SKIP_CACHE_REBUILD', '1')
|
||||
for child_vm in vm.appvms.values():
|
||||
try:
|
||||
child_vm.appmenus_update()
|
||||
except Exception, e:
|
||||
print >> sys.stderr, "---> Failed to recreate appmenus for " \
|
||||
"'{0}': {1}".format(child_vm.name, str(e))
|
||||
subprocess.call(['xdg-desktop-menu', 'forceupdate'])
|
||||
if 'KDE_SESSION_UID' in os.environ:
|
||||
subprocess.call(['kbuildsycoca' + os.environ.get('KDE_SESSION_VERSION', '4')])
|
||||
os.unsetenv('SKIP_CACHE_REBUILD')
|
||||
|
||||
|
||||
main()
|
@ -1 +0,0 @@
|
||||
/usr/libexec/qubes-appmenus/qubes-receive-appmenus
|
@ -1,56 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
# 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import sys
|
||||
from qubes.qubes import QubesVmCollection
|
||||
|
||||
def main():
|
||||
if len(sys.argv) != 4:
|
||||
print >> sys.stderr, \
|
||||
'Usage: qvm-appmenu-replace VM_NAME OLD_NAME.desktop NEW_NAME.desktop'
|
||||
sys.exit(1)
|
||||
vm_name = sys.argv[1]
|
||||
old_name = sys.argv[2]
|
||||
new_name = sys.argv[3]
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
vm = qvm_collection.get_vm_by_name(vm_name)
|
||||
|
||||
if vm is None:
|
||||
print >> sys.stderr, "ERROR: A VM with the name '{0}' " \
|
||||
"does not exist in the system.".format(
|
||||
vm_name)
|
||||
exit(1)
|
||||
|
||||
if vm.template is not None:
|
||||
print >> sys.stderr, "ERROR: To replace appmenu for template based VM, " \
|
||||
"do it on template instead"
|
||||
exit(1)
|
||||
|
||||
vm.appmenus_replace_entry(old_name, new_name)
|
||||
if hasattr(vm, 'appvms'):
|
||||
for child_vm in vm.appvms.values():
|
||||
child_vm.appmenus_replace_entry(old_name, new_name)
|
||||
|
||||
main()
|
@ -1,2 +0,0 @@
|
||||
#!/bin/sh
|
||||
exec /usr/libexec/qubes-appmenus/qubes-receive-appmenus $@
|
@ -1,23 +0,0 @@
|
||||
#!/bin/sh
|
||||
VMNAME=$1
|
||||
VMTYPE=$2
|
||||
if [ -z "$VMTYPE" ]; then
|
||||
VMTYPE=appvms
|
||||
fi
|
||||
VMDIR=/var/lib/qubes/$VMTYPE/$VMNAME
|
||||
APPSDIR=$VMDIR/apps
|
||||
|
||||
if [ $# -lt 1 ]; then
|
||||
echo "usage: $0 <vmname> [appvms|vm-templates|servicevms]"
|
||||
exit
|
||||
fi
|
||||
|
||||
if ls $APPSDIR/*.directory $APPSDIR/*.desktop > /dev/null 2>&1; then
|
||||
LC_COLLATE=C xdg-desktop-menu uninstall $APPSDIR/*.directory $APPSDIR/*.desktop
|
||||
rm -f $APPSDIR/*.desktop $APPSDIR/*.directory
|
||||
rm -f $HOME/.config/menus/applications-merged/user-$VMNAME-vm.menu
|
||||
fi
|
||||
|
||||
if [ -n "$KDE_SESSION_UID" -a -z "$SKIP_CACHE_REBUILD" ]; then
|
||||
kbuildsycoca$KDE_SESSION_VERSION
|
||||
fi
|
@ -1,6 +1,6 @@
|
||||
#!/bin/bash
|
||||
|
||||
UPDATEVM=`qubes-prefs --get updatevm`
|
||||
UPDATEVM=`qubes-prefs --force-root updatevm`
|
||||
UPDATES_STAT_FILE=/var/lib/qubes/updates/dom0-updates-available
|
||||
|
||||
if [ -z "$UPDATEVM" ]; then
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python2
|
||||
#!/usr/bin/python3
|
||||
#
|
||||
# The Qubes OS Project, http://www.qubes-os.org
|
||||
#
|
||||
@ -18,16 +18,14 @@
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
import os
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
import subprocess
|
||||
import shutil
|
||||
import glob
|
||||
import grp
|
||||
from qubes.qubes import QubesVmCollection
|
||||
import qubes
|
||||
|
||||
updates_dir = "/var/lib/qubes/updates"
|
||||
updates_rpm_dir = updates_dir + "/rpm"
|
||||
@ -48,20 +46,23 @@ package_regex = re.compile(r"^[A-Za-z0-9._+-]{1,128}.rpm$")
|
||||
# .....rpm: RSA sha1 ((MD5) PGP) md5 NOT OK (MISSING KEYS: (MD5) PGP#246110c1)
|
||||
gpg_ok_regex = re.compile(r": [a-z0-9() ]* (pgp|gpg) [a-z0-9 ]*OK$")
|
||||
|
||||
|
||||
def dom0updates_fatal(pkg, msg):
|
||||
global updates_error_file_handle
|
||||
print >> sys.stderr, msg
|
||||
print(msg, file=sys.stderr)
|
||||
if updates_error_file_handle is None:
|
||||
updates_error_file_handle = open(updates_error_file, "a")
|
||||
updates_error_file_handle.write(msg + "\n")
|
||||
os.remove(pkg)
|
||||
|
||||
|
||||
def handle_dom0updates(updatevm):
|
||||
global updates_error_file_handle
|
||||
|
||||
source=os.getenv("QREXEC_REMOTE_DOMAIN")
|
||||
source = os.getenv("QREXEC_REMOTE_DOMAIN")
|
||||
if source != updatevm.name:
|
||||
print >> sys.stderr, 'Domain ' + str(source) + ' not allowed to send dom0 updates'
|
||||
print('Domain ' + str(source) + ' not allowed to send dom0 updates',
|
||||
file=sys.stderr)
|
||||
exit(1)
|
||||
# Clean old packages
|
||||
if os.path.exists(updates_rpm_dir):
|
||||
@ -72,15 +73,17 @@ def handle_dom0updates(updatevm):
|
||||
os.remove(updates_error_file)
|
||||
os.environ['LC_ALL'] = 'C'
|
||||
qubes_gid = grp.getgrnam('qubes').gr_gid
|
||||
old_umask = os.umask(002)
|
||||
old_umask = os.umask(0o002)
|
||||
os.mkdir(updates_rpm_dir)
|
||||
os.chown(updates_rpm_dir, -1, qubes_gid)
|
||||
os.chmod(updates_rpm_dir, 0775)
|
||||
subprocess.check_call(["/usr/libexec/qubes/qfile-dom0-unpacker", str(os.getuid()), updates_rpm_dir])
|
||||
os.chmod(updates_rpm_dir, 0o0775)
|
||||
subprocess.check_call(["/usr/libexec/qubes/qfile-dom0-unpacker",
|
||||
str(os.getuid()), updates_rpm_dir])
|
||||
# Verify received files
|
||||
for untrusted_f in os.listdir(updates_rpm_dir):
|
||||
if not package_regex.match(untrusted_f):
|
||||
dom0updates_fatal(updates_rpm_dir + '/' + untrusted_f, 'Domain ' + source + ' sent unexpected file: ' + untrusted_f)
|
||||
dom0updates_fatal(updates_rpm_dir + '/' + untrusted_f,
|
||||
'Domain ' + source + ' sent unexpected file: ' + untrusted_f)
|
||||
else:
|
||||
f = untrusted_f
|
||||
assert '/' not in f
|
||||
@ -89,14 +92,17 @@ def handle_dom0updates(updatevm):
|
||||
|
||||
full_path = updates_rpm_dir + "/" + f
|
||||
if os.path.islink(full_path) or not os.path.isfile(full_path):
|
||||
dom0updates_fatal(full_path, 'Domain ' + source + ' sent not regular file')
|
||||
p = subprocess.Popen (["/bin/rpm", "-K", full_path],
|
||||
dom0updates_fatal(
|
||||
full_path, 'Domain ' + source + ' sent not regular file')
|
||||
p = subprocess.Popen(["/bin/rpm", "-K", full_path],
|
||||
stdout=subprocess.PIPE)
|
||||
output = p.communicate()[0]
|
||||
output = p.communicate()[0].decode('ascii')
|
||||
if p.returncode != 0:
|
||||
dom0updates_fatal(full_path, 'Error while verifing %s signature: %s' % (f, output))
|
||||
dom0updates_fatal(full_path,
|
||||
'Error while verifing %s signature: %s' % (f, output))
|
||||
if not gpg_ok_regex.search(output.strip()):
|
||||
dom0updates_fatal(full_path, 'Domain ' + source + ' sent not signed rpm: ' + f)
|
||||
dom0updates_fatal(full_path,
|
||||
'Domain ' + source + ' sent not signed rpm: ' + f)
|
||||
if updates_error_file_handle is not None:
|
||||
updates_error_file_handle.close()
|
||||
# After updates received - create repo metadata
|
||||
@ -106,27 +112,28 @@ def handle_dom0updates(updatevm):
|
||||
createrepo_cmd += ["-q", updates_dir]
|
||||
subprocess.check_call(createrepo_cmd)
|
||||
os.chown(updates_repodata_dir, -1, qubes_gid)
|
||||
os.chmod(updates_repodata_dir, 0775)
|
||||
os.chmod(updates_repodata_dir, 0o0775)
|
||||
# Clean old cache
|
||||
subprocess.call(["sudo", "/usr/bin/yum", "-q", "clean", "all"], stdout=sys.stderr)
|
||||
# This will fail because of "smart" detection of no-network, but it will invalidate the cache
|
||||
subprocess.call(["sudo", "/usr/bin/yum", "-q", "clean", "all"],
|
||||
stdout=sys.stderr)
|
||||
# This will fail because of "smart" detection of no-network,
|
||||
# but it will invalidate the cache
|
||||
try:
|
||||
null = open('/dev/null','w')
|
||||
null = open('/dev/null', 'w')
|
||||
subprocess.call(["/usr/bin/pkcon", "refresh"], stdout=null)
|
||||
null.close()
|
||||
except:
|
||||
except subprocess.CalledProcessError:
|
||||
pass
|
||||
os.umask(old_umask)
|
||||
exit(0)
|
||||
|
||||
|
||||
def main():
|
||||
app = qubes.Qubes()
|
||||
|
||||
qvm_collection = QubesVmCollection()
|
||||
qvm_collection.lock_db_for_reading()
|
||||
qvm_collection.load()
|
||||
qvm_collection.unlock_db()
|
||||
|
||||
updatevm = qvm_collection.get_updatevm_vm()
|
||||
updatevm = app.updatevm
|
||||
if updatevm is None:
|
||||
exit(1)
|
||||
handle_dom0updates(updatevm)
|
||||
|
||||
main()
|
||||
|
BIN
icons/black.png
Before Width: | Height: | Size: 169 KiB |
BIN
icons/blue.png
Before Width: | Height: | Size: 181 KiB |
@ -1,10 +0,0 @@
|
||||
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
||||
This copyright and license notice covers the images in this directory.
|
||||
************************************************************************
|
||||
|
||||
TITLE: Crystal Project Icons
|
||||
AUTHOR: Everaldo Coelho
|
||||
SITE: http://www.everaldo.com
|
||||
CONTACT: everaldo@everaldo.com
|
||||
|
||||
Copyright (c) 2006-2007 Everaldo Coelho.
|
@ -1 +0,0 @@
|
||||
dom0-update-avail icon from gnome-packagekit project distributed under GPLv2
|
@ -1 +0,0 @@
|
||||
Color padlock images downloaded from www.openclipart.org
|
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 11 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 2.5 KiB |
BIN
icons/gray.png
Before Width: | Height: | Size: 192 KiB |
BIN
icons/green.png
Before Width: | Height: | Size: 187 KiB |
BIN
icons/netvm.png
Before Width: | Height: | Size: 15 KiB |
BIN
icons/orange.png
Before Width: | Height: | Size: 188 KiB |
BIN
icons/purple.png
Before Width: | Height: | Size: 188 KiB |
BIN
icons/qubes.png
Before Width: | Height: | Size: 20 KiB |
BIN
icons/red.png
Before Width: | Height: | Size: 177 KiB |
Before Width: | Height: | Size: 20 KiB |
BIN
icons/yellow.png
Before Width: | Height: | Size: 185 KiB |
@ -545,12 +545,13 @@ static void select_loop(libvchan_t *vchan)
|
||||
static void usage(char *name)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"usage: %s [-w timeout] [-t] [-T] -d domain_name ["
|
||||
"usage: %s [-w timeout] [-W] [-t] [-T] -d domain_name ["
|
||||
"-l local_prog|"
|
||||
"-c request_id,src_domain_name,src_domain_id|"
|
||||
"-e] remote_cmdline\n"
|
||||
"-e means exit after sending cmd,\n"
|
||||
"-t enables replacing problematic bytes with '_' in command output, -T is the same for stderr\n"
|
||||
"-W waits for connection end even in case of VM-VM (-c)\n"
|
||||
"-c: connect to existing process (response to trigger service call)\n"
|
||||
"-w timeout: override default connection timeout of 5s (set 0 for no timeout)\n",
|
||||
name);
|
||||
@ -649,6 +650,7 @@ int main(int argc, char **argv)
|
||||
int msg_type;
|
||||
int s;
|
||||
int just_exec = 0;
|
||||
int wait_connection_end = 0;
|
||||
int connect_existing = 0;
|
||||
char *local_cmdline = NULL;
|
||||
char *remote_cmdline = NULL;
|
||||
@ -657,7 +659,7 @@ int main(int argc, char **argv)
|
||||
int src_domain_id = 0; /* if not -c given, the process is run in dom0 */
|
||||
int connection_timeout = 5;
|
||||
struct service_params svc_params;
|
||||
while ((opt = getopt(argc, argv, "d:l:ec:tTw:")) != -1) {
|
||||
while ((opt = getopt(argc, argv, "d:l:ec:tTw:W")) != -1) {
|
||||
switch (opt) {
|
||||
case 'd':
|
||||
domname = strdup(optarg);
|
||||
@ -682,6 +684,9 @@ int main(int argc, char **argv)
|
||||
case 'w':
|
||||
connection_timeout = atoi(optarg);
|
||||
break;
|
||||
case 'W':
|
||||
wait_connection_end = 1;
|
||||
break;
|
||||
default:
|
||||
usage(argv[0]);
|
||||
}
|
||||
@ -757,6 +762,11 @@ int main(int argc, char **argv)
|
||||
strlen(remote_cmdline) + 1,
|
||||
&data_domain,
|
||||
&data_port);
|
||||
if (wait_connection_end && connect_existing)
|
||||
/* save socket fd, 's' will be reused for the other qrexec-daemon
|
||||
* connection */
|
||||
wait_connection_end = s;
|
||||
else
|
||||
close(s);
|
||||
setenv("QREXEC_REMOTE_DOMAIN", domname, 1);
|
||||
prepare_local_fds(local_cmdline);
|
||||
@ -764,6 +774,13 @@ int main(int argc, char **argv)
|
||||
s = connect_unix_socket(src_domain_name);
|
||||
send_service_connect(s, request_id, data_domain, data_port);
|
||||
close(s);
|
||||
if (wait_connection_end) {
|
||||
/* wait for EOF */
|
||||
fd_set read_fd;
|
||||
FD_ZERO(&read_fd);
|
||||
FD_SET(wait_connection_end, &read_fd);
|
||||
select(wait_connection_end+1, &read_fd, NULL, NULL, 0);
|
||||
}
|
||||
} else {
|
||||
data_vchan = libvchan_server_init(data_domain, data_port,
|
||||
VCHAN_BUFFER_SIZE, VCHAN_BUFFER_SIZE);
|
||||
|
@ -71,6 +71,12 @@ int policy_pending_max = -1;
|
||||
* either VCHAN_PORT_* or remote domain id for used port */
|
||||
int used_vchan_ports[MAX_CLIENTS];
|
||||
|
||||
/* notify client (close its connection) when connection initiated by it was
|
||||
* terminated - used by qrexec-policy to cleanup (disposable) VM; indexed with
|
||||
* vchan port number relative to VCHAN_BASE_DATA_PORT; stores fd of given
|
||||
* client or -1 if none requested */
|
||||
int vchan_port_notify_client[MAX_CLIENTS];
|
||||
|
||||
int max_client_fd = -1; // current max fd of all clients; so that we need not to scan all the "clients" table
|
||||
int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor
|
||||
const char *default_user = "user";
|
||||
@ -312,6 +318,7 @@ void init(int xid)
|
||||
clients[i].state = CLIENT_INVALID;
|
||||
policy_pending[i].pid = 0;
|
||||
used_vchan_ports[i] = VCHAN_PORT_UNUSED;
|
||||
vchan_port_notify_client[i] = VCHAN_PORT_UNUSED;
|
||||
}
|
||||
|
||||
/* When running as root, make the socket accessible; perms on /var/run/qubes still apply */
|
||||
@ -358,14 +365,6 @@ static int allocate_vchan_port(int new_state)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void release_vchan_port(int port, int expected_remote_id)
|
||||
{
|
||||
/* release only if was reserved for connection to given domain */
|
||||
if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
|
||||
used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_new_client()
|
||||
{
|
||||
int fd = do_accept(qrexec_daemon_unix_socket_fd);
|
||||
@ -387,8 +386,25 @@ static void handle_new_client()
|
||||
|
||||
static void terminate_client(int fd)
|
||||
{
|
||||
int port;
|
||||
clients[fd].state = CLIENT_INVALID;
|
||||
close(fd);
|
||||
/* if client requested vchan connection end notify, cancel it */
|
||||
for (port = 0; port < MAX_CLIENTS; port++) {
|
||||
if (vchan_port_notify_client[port] == fd)
|
||||
vchan_port_notify_client[port] = VCHAN_PORT_UNUSED;
|
||||
}
|
||||
}
|
||||
|
||||
static void release_vchan_port(int port, int expected_remote_id)
|
||||
{
|
||||
/* release only if was reserved for connection to given domain */
|
||||
if (used_vchan_ports[port-VCHAN_BASE_DATA_PORT] == expected_remote_id) {
|
||||
used_vchan_ports[port-VCHAN_BASE_DATA_PORT] = VCHAN_PORT_UNUSED;
|
||||
/* notify client if requested - it will clear notification request */
|
||||
if (vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT] != VCHAN_PORT_UNUSED)
|
||||
terminate_client(vchan_port_notify_client[port-VCHAN_BASE_DATA_PORT]);
|
||||
}
|
||||
}
|
||||
|
||||
static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
|
||||
@ -433,6 +449,8 @@ static int handle_cmdline_body_from_client(int fd, struct msg_header *hdr)
|
||||
terminate_client(fd);
|
||||
return 0;
|
||||
}
|
||||
/* notify the client when this connection got terminated */
|
||||
vchan_port_notify_client[params.connect_port-VCHAN_BASE_DATA_PORT] = fd;
|
||||
client_params.connect_port = params.connect_port;
|
||||
client_params.connect_domain = remote_domain_id;
|
||||
hdr->len = sizeof(client_params);
|
||||
@ -694,7 +712,7 @@ static void handle_execute_service(void)
|
||||
signal(SIGPIPE, SIG_DFL);
|
||||
snprintf(remote_domain_id_str, sizeof(remote_domain_id_str), "%d",
|
||||
remote_domain_id);
|
||||
execl("/usr/lib/qubes/qrexec-policy", "qrexec-policy", "--",
|
||||
execl("/usr/bin/qrexec-policy", "qrexec-policy", "--",
|
||||
remote_domain_id_str, remote_domain_name, params.target_domain,
|
||||
params.service_name, params.request_id.ident, NULL);
|
||||
perror("execl");
|
||||
|
@ -1,257 +0,0 @@
|
||||
#!/usr/bin/python
|
||||
import sys
|
||||
import os
|
||||
import os.path
|
||||
import subprocess
|
||||
from qubes.qubes import vmm
|
||||
from qubes.qubes import QubesVmCollection
|
||||
import qubes.guihelpers
|
||||
import libvirt
|
||||
from optparse import OptionParser
|
||||
import fcntl
|
||||
|
||||
POLICY_FILE_DIR="/etc/qubes-rpc/policy"
|
||||
# XXX: Backward compatibility, to be removed soon
|
||||
DEPRECATED_POLICY_FILE_DIR="/etc/qubes_rpc/policy"
|
||||
QREXEC_CLIENT="/usr/lib/qubes/qrexec-client"
|
||||
QUBES_RPC_MULTIPLEXER_PATH="/usr/lib/qubes/qubes-rpc-multiplexer"
|
||||
|
||||
class UserChoice:
|
||||
ALLOW=0
|
||||
DENY=1
|
||||
ALWAYS_ALLOW=2
|
||||
|
||||
def line_to_dict(line):
|
||||
tokens=line.split()
|
||||
if len(tokens) < 3:
|
||||
return None
|
||||
|
||||
if tokens[0][0] == '#':
|
||||
return None
|
||||
|
||||
dict={}
|
||||
dict['source']=tokens[0]
|
||||
dict['dest']=tokens[1]
|
||||
|
||||
dict['full-action']=tokens[2]
|
||||
action_list=tokens[2].split(',')
|
||||
dict['action']=action_list.pop(0)
|
||||
|
||||
for iter in action_list:
|
||||
paramval=iter.split("=")
|
||||
dict["action."+paramval[0]]=paramval[1]
|
||||
|
||||
# Warn if we're ignoring extra data after a space, such as:
|
||||
# vm1 vm2 allow, user=foo
|
||||
if len(tokens) > 3:
|
||||
print >>sys.stderr, "Trailing data ignored in %s" % line
|
||||
|
||||
return dict
|
||||
|
||||
|
||||
def read_policy_file(service_name):
|
||||
policy_file = os.path.join(POLICY_FILE_DIR, service_name)
|
||||
if not os.path.isfile(policy_file):
|
||||
# fallback to policy without specific argument set (if any)
|
||||
policy_file = os.path.join(POLICY_FILE_DIR, service_name.split("+")[0])
|
||||
if not os.path.isfile(policy_file):
|
||||
policy_file = os.path.join(DEPRECATED_POLICY_FILE_DIR, service_name)
|
||||
if not os.path.isfile(policy_file):
|
||||
return None
|
||||
print >>sys.stderr, "RPC service '%s' uses deprecated policy location, please move to %s" % (service_name, POLICY_FILE_DIR)
|
||||
policy_list=list()
|
||||
f = open(policy_file)
|
||||
fcntl.flock(f, fcntl.LOCK_SH)
|
||||
for iter in f.readlines():
|
||||
dict = line_to_dict(iter)
|
||||
if dict is not None:
|
||||
policy_list.append(dict)
|
||||
f.close()
|
||||
return policy_list
|
||||
|
||||
def is_match(item, config_term):
|
||||
return (item != "dom0" and config_term == "$anyvm") or item == config_term
|
||||
|
||||
def get_default_policy():
|
||||
dict={}
|
||||
dict["action"]="deny"
|
||||
return dict
|
||||
|
||||
|
||||
def find_policy(policy, domain, target):
|
||||
for iter in policy:
|
||||
if not is_match(domain, iter["source"]):
|
||||
continue
|
||||
if not is_match(target, iter["dest"]):
|
||||
continue
|
||||
return iter
|
||||
return get_default_policy()
|
||||
|
||||
def validate_target(target):
|
||||
# special targets
|
||||
if target in ['$dispvm']:
|
||||
return True
|
||||
|
||||
qc = QubesVmCollection()
|
||||
qc.lock_db_for_reading()
|
||||
qc.load()
|
||||
qc.unlock_db()
|
||||
|
||||
return qc.get_vm_by_name(target)
|
||||
|
||||
def spawn_target_if_necessary(vm):
|
||||
if vm.is_running():
|
||||
return
|
||||
# use qvm-run instead of vm.start() to make sure that nothing is written
|
||||
# to stdout and nothing is read from stdin
|
||||
null = open("/dev/null", "r+")
|
||||
subprocess.call(["qvm-run", "-a", "--tray", "-q", vm.name, "true"],
|
||||
stdin=null, stdout=null)
|
||||
null.close()
|
||||
|
||||
def do_execute(domain, target, user, service_name, process_ident, vm=None):
|
||||
if target == "$dispvm":
|
||||
cmd = "/usr/lib/qubes/qfile-daemon-dvm " + service_name + " " + domain + " " +user
|
||||
os.execl(QREXEC_CLIENT, "qrexec-client",
|
||||
"-d", "dom0", "-c", process_ident, cmd)
|
||||
else:
|
||||
if isinstance(vm, qubes.qubes.QubesVm):
|
||||
spawn_target_if_necessary(vm)
|
||||
if target == "dom0":
|
||||
cmd = QUBES_RPC_MULTIPLEXER_PATH + " " + service_name + " " + domain
|
||||
else:
|
||||
cmd = user + ":QUBESRPC "+ service_name + " " + domain
|
||||
# stderr should be logged in source/target VM
|
||||
null = open(os.devnull, 'w')
|
||||
os.dup2(null.fileno(), 2)
|
||||
os.execl(QREXEC_CLIENT, "qrexec-client",
|
||||
"-d", target, "-c", process_ident, cmd)
|
||||
|
||||
def confirm_execution(domain, target, service_name):
|
||||
text = "Do you allow domain \"" +domain + "\" to execute " + service_name
|
||||
text+= " operation on the domain \"" + target +"\"?<br>"
|
||||
text+= " \"Yes to All\" option will automatically allow this operation in the future."
|
||||
return qubes.guihelpers.ask(text, yestoall=True)
|
||||
|
||||
def add_always_allow(domain, target, service_name, options):
|
||||
policy_file=POLICY_FILE_DIR+"/"+service_name
|
||||
if not os.path.isfile(policy_file):
|
||||
return None
|
||||
f = open(policy_file, 'r+')
|
||||
fcntl.flock(f, fcntl.LOCK_EX)
|
||||
lines = []
|
||||
for l in f.readlines():
|
||||
lines.append(l)
|
||||
lines.insert(0, "%s\t%s\tallow%s\n" % (domain, target, options))
|
||||
f.seek(0)
|
||||
f.write("".join(lines))
|
||||
f.close()
|
||||
|
||||
def info_dialog(msg_type, text):
|
||||
if msg_type not in ['info', 'warning', 'error', 'entry']:
|
||||
raise ValueError("Invalid msg_type value")
|
||||
if msg_type in ['info', 'warning', 'error']:
|
||||
try:
|
||||
subprocess.call(["/usr/bin/zenity", "--{}".format(msg_type), "--text",
|
||||
text])
|
||||
except OSError:
|
||||
kdialog_msg_type = {
|
||||
'info': 'msgbox',
|
||||
'warning': 'sorry',
|
||||
'error': 'error'
|
||||
}[msg_type]
|
||||
subprocess.call(["/usr/bin/kdialog", "--{}".format(kdialog_msg_type), text])
|
||||
else:
|
||||
response = subprocess.check_output(["/usr/bin/zenity", "--{}".format(msg_type), "--text",
|
||||
text])
|
||||
return response
|
||||
|
||||
|
||||
def policy_editor(domain, target, service_name):
|
||||
text = "No policy definition found for " + service_name + " action. \n"
|
||||
text+= "Type YES if you want to create a default policy file"
|
||||
response = info_dialog('entry', text)
|
||||
if response.strip() == 'YES':
|
||||
create_policy(service_name)
|
||||
|
||||
|
||||
def create_policy(service_name):
|
||||
policyFile = "/etc/qubes-rpc/policy/"+service_name
|
||||
policy = open(policyFile, "w")
|
||||
policy.write("## Note that policy parsing stops at the first match,\n")
|
||||
policy.write("## so adding anything below \"$anyvm $anyvm action\" line will have no effect\n")
|
||||
policy.write("\n")
|
||||
policy.write("## Please use a single # to start your custom comments\n")
|
||||
policy.write("\n")
|
||||
policy.write("$anyvm $anyvm ask\n")
|
||||
policy.close()
|
||||
|
||||
def main():
|
||||
usage = "usage: %prog [options] <src-domain-id> <src-domain> <target-domain> <service> <process-ident>"
|
||||
parser = OptionParser (usage)
|
||||
parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False,
|
||||
help="Allow run of service without confirmation if policy say 'ask'")
|
||||
parser.add_option ("--just-evaluate", action="store_true", dest="just_evaluate", default=False,
|
||||
help="Do not run the service, only evaluate policy; retcode=0 means 'allow'")
|
||||
|
||||
(options, args) = parser.parse_args ()
|
||||
domain_id=args[0]
|
||||
domain=args[1]
|
||||
target=args[2]
|
||||
service_name=args[3]
|
||||
process_ident=args[4]
|
||||
|
||||
# Add source domain information, required by qrexec-client for establishing
|
||||
# connection
|
||||
process_ident+=","+domain+","+domain_id
|
||||
|
||||
vm = validate_target(target)
|
||||
if vm is None:
|
||||
print >> sys.stderr, "Rpc failed (unknown domain):", domain, target, service_name
|
||||
text = "Domain '%s' doesn't exist (service %s called by domain %s)." % (
|
||||
target, service_name, domain)
|
||||
info_dialog("error", text)
|
||||
exit(1)
|
||||
|
||||
policy_list=read_policy_file(service_name)
|
||||
if policy_list==None:
|
||||
policy_editor(domain, target, service_name)
|
||||
policy_list=read_policy_file(service_name)
|
||||
if policy_list==None:
|
||||
policy_list=list()
|
||||
|
||||
policy_dict=find_policy(policy_list, domain, target)
|
||||
|
||||
if policy_dict["action"] == "ask" and options.assume_yes_for_ask:
|
||||
policy_dict["action"] = "allow"
|
||||
|
||||
if policy_dict["action"] == "ask":
|
||||
user_choice = confirm_execution(domain, target, service_name)
|
||||
if user_choice == UserChoice.ALWAYS_ALLOW:
|
||||
add_always_allow(domain, target, service_name, policy_dict["full-action"].lstrip('ask'))
|
||||
policy_dict["action"] = "allow"
|
||||
elif user_choice == UserChoice.ALLOW:
|
||||
policy_dict["action"] = "allow"
|
||||
else:
|
||||
policy_dict["action"] = "deny"
|
||||
|
||||
if options.just_evaluate:
|
||||
if policy_dict["action"] == "allow":
|
||||
exit(0)
|
||||
else:
|
||||
exit(1)
|
||||
|
||||
if policy_dict["action"] == "allow":
|
||||
if policy_dict.has_key("action.target"):
|
||||
target=policy_dict["action.target"]
|
||||
if policy_dict.has_key("action.user"):
|
||||
user=policy_dict["action.user"]
|
||||
else:
|
||||
user="DEFAULT"
|
||||
print >> sys.stderr, "Rpc allowed:", domain, target, service_name
|
||||
do_execute(domain, target, user, service_name, process_ident, vm=vm)
|
||||
|
||||
print >> sys.stderr, "Rpc denied:", domain, target, service_name
|
||||
exit(1)
|
||||
|
||||
main()
|
@ -47,6 +47,7 @@ BuildRequires: qubes-utils-devel >= 3.1.3
|
||||
BuildRequires: qubes-libvchan-devel
|
||||
Requires: qubes-core-dom0
|
||||
Requires: qubes-utils >= 3.1.3
|
||||
Requires: python3-PyQt4
|
||||
Requires: %{name}-kernel-install
|
||||
Requires: xdotool
|
||||
|
||||
@ -76,8 +77,6 @@ ln -sf . %{name}-%{version}
|
||||
%setup -T -D
|
||||
|
||||
%build
|
||||
python -m compileall appmenus-scripts
|
||||
python -O -m compileall appmenus-scripts
|
||||
(cd dom0-updates; make)
|
||||
(cd qrexec; make)
|
||||
(cd file-copy-vm; make)
|
||||
@ -85,25 +84,9 @@ python -O -m compileall appmenus-scripts
|
||||
|
||||
%install
|
||||
|
||||
### Appmenus
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules
|
||||
cp appmenus-scripts/qubes-core-appmenus.py $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.py
|
||||
cp appmenus-scripts/qubes-core-appmenus.pyc $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.pyc
|
||||
cp appmenus-scripts/qubes-core-appmenus.pyo $RPM_BUILD_ROOT%{python_sitearch}/qubes/modules/10appmenus.pyo
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus
|
||||
cp appmenus-scripts/*.sh $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus/
|
||||
cp appmenus-scripts/qubes-receive-appmenus $RPM_BUILD_ROOT/usr/libexec/qubes-appmenus/
|
||||
|
||||
install -D appmenus-scripts/qvm-sync-appmenus $RPM_BUILD_ROOT/usr/bin/qvm-sync-appmenus
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/etc/qubes-rpc/policy
|
||||
cp appmenus-scripts/qubes.SyncAppMenus $RPM_BUILD_ROOT/etc/qubes-rpc/
|
||||
cp appmenus-scripts/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes-rpc/policy/qubes.SyncAppMenus
|
||||
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/qubes-appmenus/
|
||||
cp -r appmenus-files/* $RPM_BUILD_ROOT/usr/share/qubes-appmenus/
|
||||
## Appmenus
|
||||
install -d $RPM_BUILD_ROOT/etc/qubes-rpc/policy
|
||||
cp qubesappmenus/qubes.SyncAppMenus.policy $RPM_BUILD_ROOT/etc/qubes-rpc/policy/qubes.SyncAppMenus
|
||||
|
||||
### Dom0 updates
|
||||
install -D dom0-updates/qubes-dom0-updates.cron $RPM_BUILD_ROOT/etc/cron.daily/qubes-dom0-updates.cron
|
||||
@ -118,12 +101,12 @@ install -m 0664 -D dom0-updates/qubes.ReceiveUpdates.policy $RPM_BUILD_ROOT/etc/
|
||||
install -d $RPM_BUILD_ROOT/var/lib/qubes/updates
|
||||
|
||||
# Qrexec
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp qrexec/qrexec-daemon $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
cp qrexec/qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/bin $RPM_BUILD_ROOT/usr/sbin
|
||||
install qrexec/qrexec-daemon $RPM_BUILD_ROOT/usr/sbin/
|
||||
install qrexec/qrexec-client $RPM_BUILD_ROOT/usr/bin/
|
||||
# XXX: Backward compatibility
|
||||
ln -s qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/qrexec_client
|
||||
cp qrexec/qrexec-policy $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
ln -s ../../bin/qrexec-client $RPM_BUILD_ROOT/usr/lib/qubes/qrexec-client
|
||||
ln -s ../../sbin/qrexec-daemon $RPM_BUILD_ROOT/usr/lib/qubes/qrexec-daemon
|
||||
cp qrexec/qubes-rpc-multiplexer $RPM_BUILD_ROOT/usr/lib/qubes
|
||||
|
||||
### pm-utils
|
||||
@ -167,12 +150,6 @@ install -m 755 file-copy-vm/qfile-dom0-agent $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||
install -m 755 file-copy-vm/qvm-copy-to-vm $RPM_BUILD_ROOT/usr/bin/
|
||||
ln -s qvm-copy-to-vm $RPM_BUILD_ROOT/usr/bin/qvm-move-to-vm
|
||||
|
||||
### Icons
|
||||
mkdir -p $RPM_BUILD_ROOT/usr/share/qubes/icons
|
||||
for icon in icons/*.png; do
|
||||
convert -resize 48 $icon $RPM_BUILD_ROOT/usr/share/qubes/$icon
|
||||
done
|
||||
|
||||
### Documentation
|
||||
(cd doc; make DESTDIR=$RPM_BUILD_ROOT install)
|
||||
|
||||
@ -183,13 +160,6 @@ fi
|
||||
|
||||
%post
|
||||
|
||||
for i in /usr/share/qubes/icons/*.png ; do
|
||||
xdg-icon-resource install --noupdate --novendor --size 48 $i
|
||||
done
|
||||
xdg-icon-resource forceupdate
|
||||
|
||||
xdg-desktop-menu install /usr/share/qubes-appmenus/qubes-dispvm.directory /usr/share/qubes-appmenus/qubes-dispvm-*.desktop
|
||||
|
||||
/usr/lib/qubes/patch-dnf-yum-config
|
||||
|
||||
systemctl enable qubes-suspend.service >/dev/null 2>&1
|
||||
@ -198,12 +168,6 @@ systemctl enable qubes-suspend.service >/dev/null 2>&1
|
||||
if [ "$1" = 0 ] ; then
|
||||
# no more packages left
|
||||
|
||||
for i in /usr/share/qubes/icons/*.png ; do
|
||||
xdg-icon-resource uninstall --novendor --size 48 $i
|
||||
done
|
||||
|
||||
xdg-desktop-menu uninstall /usr/share/qubes-appmenus/qubes-dispvm.directory /usr/share/qubes-appmenus/qubes-dispvm-*.desktop
|
||||
|
||||
systemctl disable qubes-suspend.service > /dev/null 2>&1
|
||||
fi
|
||||
|
||||
@ -219,29 +183,7 @@ rm -f /lib/udev/rules.d/69-xorg-vmmouse.rules
|
||||
chmod -x /etc/grub.d/10_linux
|
||||
|
||||
%files
|
||||
%attr(2775,root,qubes) %dir /etc/qubes-rpc
|
||||
%attr(2775,root,qubes) %dir /etc/qubes-rpc/policy
|
||||
/etc/qubes-rpc/policy/qubes.SyncAppMenus
|
||||
/etc/qubes-rpc/qubes.SyncAppMenus
|
||||
%{python_sitearch}/qubes/modules/10appmenus.py
|
||||
%{python_sitearch}/qubes/modules/10appmenus.pyc
|
||||
%{python_sitearch}/qubes/modules/10appmenus.pyo
|
||||
/usr/libexec/qubes-appmenus/convert-apptemplate2vm.sh
|
||||
/usr/libexec/qubes-appmenus/convert-dirtemplate2vm.sh
|
||||
/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh
|
||||
/usr/libexec/qubes-appmenus/qubes-receive-appmenus
|
||||
/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh
|
||||
/usr/share/qubes-appmenus/qubes-appmenu-select.desktop
|
||||
/usr/share/qubes-appmenus/qubes-dispvm-firefox.desktop
|
||||
/usr/share/qubes-appmenus/qubes-dispvm-xterm.desktop
|
||||
/usr/share/qubes-appmenus/qubes-dispvm.directory
|
||||
/usr/share/qubes-appmenus/qubes-servicevm.directory.template
|
||||
/usr/share/qubes-appmenus/qubes-start.desktop
|
||||
/usr/share/qubes-appmenus/qubes-templatevm.directory.template
|
||||
/usr/share/qubes-appmenus/qubes-vm.directory.template
|
||||
/usr/share/qubes-appmenus/hvm
|
||||
/usr/share/qubes/icons/*.png
|
||||
/usr/bin/qvm-sync-appmenus
|
||||
# Dom0 updates
|
||||
/etc/cron.daily/qubes-dom0-updates.cron
|
||||
/etc/yum.real.repos.d/qubes-cached.repo
|
||||
@ -259,11 +201,12 @@ chmod -x /etc/grub.d/10_linux
|
||||
%dir %{_dracutmoddir}/90extra-modules
|
||||
%{_dracutmoddir}/90extra-modules/*
|
||||
# Qrexec
|
||||
%attr(4750,root,qubes) /usr/lib/qubes/qrexec-daemon
|
||||
/usr/lib/qubes/qrexec-client
|
||||
/usr/lib/qubes/qrexec_client
|
||||
/usr/sbin/qrexec-daemon
|
||||
/usr/bin/qrexec-client
|
||||
/usr/lib/qubes/qubes-rpc-multiplexer
|
||||
/usr/lib/qubes/qrexec-policy
|
||||
# compat symlinks
|
||||
/usr/lib/qubes/qrexec-client
|
||||
/usr/lib/qubes/qrexec-daemon
|
||||
# file copy
|
||||
/usr/bin/qvm-copy-to-vm
|
||||
/usr/bin/qvm-move-to-vm
|
||||
|
@ -28,6 +28,7 @@ enable xenstored.service
|
||||
enable xenstored.socket
|
||||
enable xenstored_ro.socket
|
||||
enable xenconsoled.service
|
||||
enable xen-init-dom0.service
|
||||
enable libvirtd.service
|
||||
enable virlockd.socket
|
||||
|
||||
@ -45,6 +46,7 @@ enable qubes-qmemman.service
|
||||
enable qubes-suspend.service
|
||||
enable qubes-setupdvm.service
|
||||
enable qubes-block-cleaner.service
|
||||
enable qubesd.service
|
||||
enable anti-evil-maid-unseal.service
|
||||
enable anti-evil-maid-check-mount-devs.service
|
||||
enable anti-evil-maid-seal.service
|
||||
|