appmenus: initial conversion to core3 API

This commits change code itself for core3 API, but doesn't plug it
anywhere yet.

QubesOS/qubes-issues#1897
This commit is contained in:
Marek Marczykowski-Górecki 2016-04-07 13:16:23 +02:00
parent b91eace873
commit d446f849d8
No known key found for this signature in database
GPG Key ID: 063938BA42CFA724

View File

@ -27,172 +27,140 @@ import os.path
import shutil import shutil
import dbus import dbus
from qubes.qubes import QubesVm, QubesHVm import qubes.ext
from qubes.qubes import vm_files, system_path
import qubesimgconverter import qubesimgconverter
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'] = \ class AppmenusSubdirs:
templates_subdir = 'apps.templates'
template_icons_subdir = 'apps.tempicons'
subdir = 'apps'
icons_subdir = 'apps.icons'
template_templates_subdir = 'apps-template.templates'
whitelist = 'whitelisted-appmenus.list'
class AppmenusPaths:
appmenu_start_hvm_template = \
'/usr/share/qubes-appmenus/qubes-start.desktop' '/usr/share/qubes-appmenus/qubes-start.desktop'
system_path['appmenu_create_cmd'] = \ appmenu_create_cmd = \
'/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh' '/usr/libexec/qubes-appmenus/create-apps-for-appvm.sh'
system_path['appmenu_remove_cmd'] = \ appmenu_remove_cmd = \
'/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh' '/usr/libexec/qubes-appmenus/remove-appvm-appmenus.sh'
def QubesVm_get_appmenus_templates_dir(self): class AppmenusExtension(qubes.ext.Extension):
if self.updateable: def __init__(self, *args):
return self.absolute_path(vm_files["appmenus_templates_subdir"], None) super(AppmenusExtension, self).__init__(*args)
elif self.template is not None: import qubes.vm.qubesvm
return self.template.appmenus_templates_dir
def templates_dir(self, vm):
"""
:type vm: qubes.vm.qubesvm.QubesVM
"""
assert isinstance(vm, qubes.vm.qubesvm.QubesVM)
if vm.updateable:
return os.path.join(vm.dir_path,
AppmenusSubdirs.templates_subdir)
elif hasattr(vm, 'template'):
return self.templates_dir(vm.template)
else: else:
return None return None
QubesVm.appmenus_templates_dir = property(QubesVm_get_appmenus_templates_dir) def template_icons_dir(self, vm):
if vm.updateable:
return os.path.join(vm.dir_path,
def QubesVm_get_appmenus_template_icons_dir(self): AppmenusSubdirs.template_icons_subdir)
if self.updateable: elif hasattr(vm, 'template'):
return self.absolute_path(vm_files["appmenus_template_icons_subdir"], return self.template_icons_dir(vm.template)
None)
elif self.template:
return self.template.appmenus_template_icons_dir
else: else:
return None return None
QubesVm.appmenus_template_icons_dir = \ def appmenus_dir(self, vm):
property(QubesVm_get_appmenus_template_icons_dir) return os.path.join(vm.dir_path, AppmenusSubdirs.subdir)
def icons_dir(self, vm):
return os.path.join(vm.dir_path, AppmenusSubdirs.icons_subdir)
def QubesVm_get_appmenus_dir(self): def appmenus_create(self, vm, source_template=None):
return self.absolute_path(vm_files["appmenus_subdir"], None) if source_template is None and hasattr(vm, 'template'):
source_template = vm.template
QubesVm.appmenus_dir = property(QubesVm_get_appmenus_dir) if vm.internal:
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 return
if self.is_disposablevm(): if vm.is_disposablevm():
return return
if self.is_netvm(): vmsubdir = vm.dir_path.split(os.path.sep)[-2]
vmtype = 'servicevms'
elif self.is_template():
vmtype = 'vm-templates'
else:
vmtype = 'appvms'
try: try:
msgoutput = None if verbose else open(os.devnull, 'w') #TODO msgoutput = None if verbose else open(os.devnull, 'w')
msgoutput = None
if source_template is not None: if source_template is not None:
subprocess.check_call([system_path["appmenu_create_cmd"], subprocess.check_call([AppmenusPaths.appmenu_create_cmd,
source_template.appmenus_templates_dir, self.templates_dir(source_template),
self.name, vmtype, self.label.icon], vm.name, vmsubdir, vm.label.icon],
stdout=msgoutput, stderr=msgoutput) stdout=msgoutput, stderr=msgoutput)
elif self.appmenus_templates_dir is not None: elif self.templates_dir(vm) is not None:
subprocess.check_call([system_path["appmenu_create_cmd"], subprocess.check_call([AppmenusPaths.appmenu_create_cmd,
self.appmenus_templates_dir, self.name, self.templates_dir(vm), vm.name,
vmtype, self.label.icon], vmsubdir, vm.label.icon],
stdout=msgoutput, stderr=msgoutput) stdout=msgoutput, stderr=msgoutput)
else: else:
# Only add apps to menu # Only add apps to menu
subprocess.check_call([system_path["appmenu_create_cmd"], subprocess.check_call([AppmenusPaths.appmenu_create_cmd,
"none", self.name, vmtype, "none", vm.name, vmsubdir,
self.label.icon], vm.label.icon],
stdout=msgoutput, stderr=msgoutput) stdout=msgoutput, stderr=msgoutput)
except subprocess.CalledProcessError: except subprocess.CalledProcessError:
print >> sys.stderr, "Ooops, there was a problem creating appmenus " \ vm.log.warning("Ooops, there was a problem creating appmenus "
"for {0} VM!".format(self.name) "for {0} VM!")
def QubesVm_appmenus_remove(self): def appmenus_remove(self, vm):
if self.is_netvm(): vmsubdir = vm.dir_path.split(os.path.sep)[-2]
vmtype = 'servicevms' subprocess.check_call([AppmenusPaths.appmenu_remove_cmd, vm.name,
elif self.is_template(): vmsubdir], stderr=open(os.devnull, 'w'))
vmtype = 'vm-templates'
else:
vmtype = 'appvms'
subprocess.check_call([system_path["appmenu_remove_cmd"], self.name,
vmtype], stderr=open(os.devnull, 'w'))
def appmenus_cleanup(self, vm):
def QubesVm_appmenus_cleanup(self): srcdir = self.templates_dir(vm)
srcdir = self.appmenus_templates_dir
if srcdir is None: if srcdir is None:
return return
if not os.path.exists(srcdir): if not os.path.exists(srcdir):
return return
if not os.path.exists(self.appmenus_dir): if not os.path.exists(self.appmenus_dir(vm)):
return return
for appmenu in os.listdir(self.appmenus_dir): for appmenu in os.listdir(self.appmenus_dir(vm)):
if not os.path.exists(os.path.join(srcdir, appmenu)): if not os.path.exists(os.path.join(srcdir, appmenu)):
os.unlink(os.path.join(self.appmenus_dir, appmenu)) os.unlink(os.path.join(self.appmenus_dir(vm), appmenu))
def appicons_create(self, vm, srcdir=None, force=False):
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: if srcdir is None:
srcdir = self.appmenus_template_icons_dir srcdir = self.template_icons_dir(vm)
if srcdir is None: if srcdir is None:
return return
if not os.path.exists(srcdir): if not os.path.exists(srcdir):
return return
if self.internal: if vm.internal:
return return
if self.is_disposablevm(): if vm.is_disposablevm():
return return
whitelist = os.path.join(self.dir_path, vm_files['appmenus_whitelist']) whitelist = os.path.join(vm.dir_path, AppmenusSubdirs.whitelist)
if os.path.exists(whitelist): if os.path.exists(whitelist):
whitelist = [line.strip() for line in open(whitelist)] whitelist = [line.strip() for line in open(whitelist)]
else: else:
whitelist = None whitelist = None
if not os.path.exists(self.appmenus_icons_dir): if not os.path.exists(self.icons_dir(vm)):
os.mkdir(self.appmenus_icons_dir) os.mkdir(self.icons_dir(vm))
elif not os.path.isdir(self.appmenus_icons_dir): elif not os.path.isdir(self.icons_dir(vm)):
os.unlink(self.appmenus_icons_dir) os.unlink(self.icons_dir(vm))
os.mkdir(self.appmenus_icons_dir) os.mkdir(self.icons_dir(vm))
for icon in os.listdir(srcdir): for icon in os.listdir(srcdir):
desktop = os.path.splitext(icon)[0] + '.desktop' desktop = os.path.splitext(icon)[0] + '.desktop'
@ -200,122 +168,123 @@ def QubesVm_appicons_create(self, srcdir=None, force=False):
continue continue
src_icon = os.path.join(srcdir, icon) src_icon = os.path.join(srcdir, icon)
dst_icon = os.path.join(self.appmenus_icons_dir, icon) dst_icon = os.path.join(self.icons_dir(vm), icon)
if not os.path.exists(dst_icon) or force or \ if not os.path.exists(dst_icon) or force or \
os.path.getmtime(src_icon) > os.path.getmtime(dst_icon): os.path.getmtime(src_icon) > os.path.getmtime(dst_icon):
qubesimgconverter.tint(src_icon, dst_icon, self.label.color) qubesimgconverter.tint(src_icon, dst_icon, vm.label.color)
def appicons_remove(self, vm):
def QubesVm_appicons_remove(self): if not os.path.exists(self.icons_dir(vm)):
if not os.path.exists(self.appmenus_icons_dir):
return return
for icon in os.listdir(self.appmenus_icons_dir): for icon in os.listdir(self.icons_dir(vm)):
os.unlink(os.path.join(self.appmenus_icons_dir, icon)) os.unlink(os.path.join(self.icons_dir(vm), icon))
def QubesVm_appicons_cleanup(self): def appicons_cleanup(self, vm):
srcdir = self.appmenus_template_icons_dir srcdir = self.template_icons_dir(vm)
if srcdir is None: if srcdir is None:
return return
if not os.path.exists(srcdir): if not os.path.exists(srcdir):
return return
if not os.path.exists(self.appmenus_icons_dir): if not os.path.exists(self.icons_dir(vm)):
return return
for icon in os.listdir(self.appmenus_icons_dir): for icon in os.listdir(self.icons_dir(vm)):
if not os.path.exists(os.path.join(srcdir, icon)): if not os.path.exists(os.path.join(srcdir, icon)):
os.unlink(os.path.join(self.appmenus_icons_dir, icon)) os.unlink(os.path.join(self.icons_dir(vm), icon))
@qubes.ext.handler('property-pre-set:name')
def pre_rename(self, vm, event, prop, *args):
self.appmenus_remove(vm)
def QubesVm_pre_rename(self, new_name): @qubes.ext.handler('property-set:name')
self.appmenus_remove() def post_rename(self, vm, event, prop, *args):
self.appmenus_create(vm)
@qubes.ext.handler('domain-create-on-disk')
def create_on_disk(self, vm, event, source_template=None):
if vm.hvm and source_template is None:
vm.log.info("Creating appmenus directory: {0}".format(
self.templates_dir(vm)))
os.mkdir(self.templates_dir(vm))
shutil.copy(AppmenusPaths.appmenu_start_hvm_template,
self.templates_dir(vm))
def QubesVm_post_rename(self, old_name): source_whitelist_filename = 'vm-' + AppmenusSubdirs.whitelist
self.appmenus_create() if vm.is_netvm():
source_whitelist_filename = 'netvm-' + AppmenusSubdirs.whitelist
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( if source_template and os.path.exists(
os.path.join(source_template.dir_path, source_whitelist_filename)): os.path.join(source_template.dir_path, source_whitelist_filename)):
if verbose: vm.log.info("Creating default whitelisted apps list: {0}".
print >> sys.stderr, "--> Creating default whitelisted apps list: {0}". \ format(vm.dir_path + '/' + AppmenusSubdirs.whitelist))
format(self.dir_path + '/' + vm_files["whitelisted_appmenus"])
shutil.copy( shutil.copy(
os.path.join(source_template.dir_path, source_whitelist_filename), os.path.join(source_template.dir_path, source_whitelist_filename),
os.path.join(self.dir_path, vm_files["whitelisted_appmenus"])) os.path.join(vm.dir_path, AppmenusSubdirs.whitelist))
if source_template and self.updateable: if source_template and vm.updateable:
if verbose: vm.log.info("--> Copying the template's appmenus templates "
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \ "dir:\n{0} ==>\n{1}".
format(source_template.appmenus_templates_dir, format(self.templates_dir(source_template),
self.appmenus_templates_dir) self.templates_dir(vm)))
if os.path.isdir(source_template.appmenus_templates_dir): if os.path.isdir(self.templates_dir(source_template)):
shutil.copytree(source_template.appmenus_templates_dir, shutil.copytree(self.templates_dir(source_template),
self.appmenus_templates_dir) self.templates_dir(vm))
else: else:
os.mkdir(self.appmenus_templates_dir) os.mkdir(self.templates_dir(vm))
if os.path.isdir(source_template.appmenus_template_icons_dir): if os.path.isdir(self.template_icons_dir(source_template)):
shutil.copytree(source_template.appmenus_template_icons_dir, shutil.copytree(self.template_icons_dir(source_template),
self.appmenus_template_icons_dir) self.template_icons_dir(vm))
else: else:
os.mkdir(self.appmenus_template_icons_dir) os.mkdir(self.template_icons_dir(vm))
# Create appmenus # Create appmenus
self.appicons_create() self.appicons_create(vm)
self.appmenus_create(verbose=verbose) self.appmenus_create(vm)
@qubes.ext.handler('domain-clone-files')
def clone_disk_files(self, vm, event, src_vm):
if src_vm.updateable and src_vm.templates_dir(vm) is not None and \
self.templates_dir(vm) is not None:
vm.log.info("Copying the template's appmenus templates "
"dir:\n{0} ==>\n{1}".
format(self.templates_dir(src_vm),
self.templates_dir(vm)))
shutil.copytree(self.templates_dir(src_vm),
self.templates_dir(vm))
def QubesVm_clone_disk_files(self, src_vm, verbose): if src_vm.updateable and src_vm.template_icons_dir(vm) is not None \
if src_vm.updateable and src_vm.appmenus_templates_dir is not None and \ and self.template_icons_dir(vm) is not None and \
self.appmenus_templates_dir is not None: os.path.isdir(self.template_icons_dir(src_vm)):
if verbose: vm.log.info("Copying the template's appmenus "
print >> sys.stderr, "--> Copying the template's appmenus templates dir:\n{0} ==>\n{1}". \ "template icons dir:\n{0} ==>\n{1}".
format(src_vm.appmenus_templates_dir, format(self.template_icons_dir(src_vm),
self.appmenus_templates_dir) self.template_icons_dir(vm)))
shutil.copytree(src_vm.appmenus_templates_dir, shutil.copytree(self.template_icons_dir(src_vm),
self.appmenus_templates_dir) self.template_icons_dir(vm))
if src_vm.updateable and src_vm.appmenus_template_icons_dir is not None \ for whitelist in (
and self.appmenus_template_icons_dir is not None and \ AppmenusSubdirs.whitelist,
os.path.isdir(src_vm.appmenus_template_icons_dir): 'vm-' + AppmenusSubdirs.whitelist,
if verbose: 'netvm-' + AppmenusSubdirs.whitelist):
print >> sys.stderr, "--> Copying the template's appmenus " \ if os.path.exists(os.path.join(src_vm.dir_path, whitelist)):
"template icons dir:\n{0} ==>\n{1}". \ vm.log.info("Copying whitelisted apps list: {0}".
format(src_vm.appmenus_template_icons_dir, format(whitelist))
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), shutil.copy(os.path.join(src_vm.dir_path, whitelist),
os.path.join(self.dir_path, whitelist)) os.path.join(vm.dir_path, whitelist))
# Create appmenus # Create appmenus
self.appicons_create() self.appicons_create(vm)
self.appmenus_create(verbose=verbose) self.appmenus_create(vm)
def QubesVm_remove_from_disk(self): @qubes.ext.handler('domain-remove-from-disk')
self.appmenus_remove() def remove_from_disk(self, vm, event):
self.appmenus_remove(vm)
def QubesVm_label_setter(self, _): @qubes.ext.handler('property-set:label')
self.appicons_create(force=True) def label_setter(self, vm, event, *args):
self.appicons_create(vm, force=True)
# Apparently desktop environments heavily caches the icons, # Apparently desktop environments heavily caches the icons,
# see #751 for details # see #751 for details
@ -330,7 +299,7 @@ def QubesVm_label_setter(self, _):
"org.freedesktop.Notifications", "org.freedesktop.Notifications",
"/org/freedesktop/Notifications") "/org/freedesktop/Notifications")
notify_object.Notify( notify_object.Notify(
"Qubes", 0, self.label.icon, "Qubes", "Qubes", 0, vm.label.icon, "Qubes",
"You will need to log off and log in again for the VM icons " "You will need to log off and log in again for the VM icons "
"to update in the KDE launcher menu", "to update in the KDE launcher menu",
[], [], 10000, [], [], 10000,
@ -338,57 +307,51 @@ def QubesVm_label_setter(self, _):
except: except:
pass pass
elif "xfce" in os.environ.get("DESKTOP_SESSION", ""): elif "xfce" in os.environ.get("DESKTOP_SESSION", ""):
self.appmenus_remove() self.appmenus_remove(vm)
self.appmenus_create() self.appmenus_create(vm)
def appmenus_recreate(self, vm):
def QubesVm_appmenus_recreate(self):
""" """
Force recreation of all appmenus and icons. For example when VM label Force recreation of all appmenus and icons. For example when VM label
color was changed color was changed
""" """
self.appmenus_remove() self.appmenus_remove(vm)
self.appmenus_cleanup() self.appmenus_cleanup(vm)
self.appicons_remove() self.appicons_remove(vm)
self.appicons_create() self.appicons_create(vm)
self.appmenus_create() self.appmenus_create(vm)
def appmenus_update(self, vm):
def QubesVm_appmenus_update(self):
""" """
Similar to appmenus_recreate, but do not touch unchanged files Similar to appmenus_recreate, but do not touch unchanged files
""" """
self.appmenus_remove() self.appmenus_remove(vm)
self.appmenus_cleanup() self.appmenus_cleanup(vm)
self.appicons_create() self.appicons_create(vm)
self.appicons_cleanup() self.appicons_cleanup(vm)
self.appmenus_create() self.appmenus_create(vm)
@qubes.ext.handler('property-set:internal')
def QubesVm_set_attr(self, name, newvalue, oldvalue): def set_attr(self, vm, event, prop, newvalue, *args):
if name == 'internal': if len(args):
oldvalue = args[0]
else:
oldvalue = vm.__class__.internal._default
if newvalue and not oldvalue: if newvalue and not oldvalue:
self.appmenus_remove() self.appmenus_remove(vm)
elif not newvalue and oldvalue: elif not newvalue and oldvalue:
self.appmenus_create() self.appmenus_create(vm)
@qubes.ext.handler('backup-get-files')
# new methods def files_for_backup(self, vm, event):
QubesVm.appmenus_create = QubesVm_appmenus_create if vm.internal:
QubesVm.appmenus_remove = QubesVm_appmenus_remove return
QubesVm.appmenus_cleanup = QubesVm_appmenus_cleanup if vm.updateable:
QubesVm.appmenus_recreate = QubesVm_appmenus_recreate yield self.templates_dir(vm)
QubesVm.appmenus_update = QubesVm_appmenus_update yield self.template_icons_dir(vm)
QubesVm.appmenus_replace_entry = QubesVm_appmenus_replace_entry yield os.path.join(vm.dir_path, AppmenusSubdirs.whitelist)
QubesVm.appicons_create = QubesVm_appicons_create if vm.is_template():
QubesVm.appicons_cleanup = QubesVm_appicons_cleanup for whitelist in (
QubesVm.appicons_remove = QubesVm_appicons_remove 'vm-' + AppmenusSubdirs.whitelist,
'netvm-' + AppmenusSubdirs.whitelist):
# hooks for existing methods yield os.path.join(vm.dir_path, whitelist)
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)