From 6a929190d2073f32e86a2088708ea5ee7f9ee9b2 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 13 Jun 2012 17:01:57 +0200 Subject: [PATCH 1/6] dom0/core: fix order of loading VM attributes Parsing kernelopts depends on uses_default_kernelopts, so set 'order' appropriate. --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 09d311aa..c6945b28 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -253,7 +253,7 @@ class QubesVm(object): "kernel": { "default": None, 'order': 30 }, "uses_default_kernel": { "default": True, 'order': 30 }, "uses_default_kernelopts": { "default": True, 'order': 30 }, - "kernelopts": { "default": "", 'order': 30, "eval": \ + "kernelopts": { "default": "", 'order': 31, "eval": \ 'value if not self.uses_default_kernelopts else default_kernelopts_pcidevs if len(self.pcidevs) > 0 else default_kernelopts' }, "mac": { "attr": "_mac", "default": None }, "include_in_backups": { "default": True }, From 46db1e83f0d34aadd1f0d638020e809b01387e99 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 13 Jun 2012 17:08:30 +0200 Subject: [PATCH 2/6] dom0/core: introduce is_guid_running() and use it in is_fully_usable() To improve code reuse, especially to remove direct checking for "/var/run/qubes/guid_running.{0}" in many places. --- dom0/qvm-core/qubes.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index c6945b28..83153eb5 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -650,12 +650,18 @@ class QubesVm(object): return "NA" - def is_fully_usable(self): + def is_guid_running(self): xid = self.get_xid() if xid < 0: return False if not os.path.exists('/var/run/qubes/guid_running.%d' % xid): return False + return True + + def is_fully_usable(self): + # Running gui-daemon implies also VM running + if not self.is_guid_running(): + return False # currently qrexec daemon doesn't cleanup socket in /var/run/qubes, so # it can be left from some other VM return True @@ -1312,7 +1318,7 @@ class QubesVm(object): raise QubesException("Not enough memory to start '{0}' VM! Close one or more running VMs and try again.".format(self.name)) xid = self.get_xid() - if os.getenv("DISPLAY") is not None and not os.path.isfile("/var/run/qubes/guid_running.{0}".format(xid)): + if os.getenv("DISPLAY") is not None and not self.is_guid_running(): self.start_guid(verbose = verbose, notify_function = notify_function) args = [qrexec_client_path, "-d", str(xid), command] From 784f0f560794f76d22d898c47295ca53652b662a Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 13 Jun 2012 17:09:06 +0200 Subject: [PATCH 3/6] dom0/core/hvm: rename is_fully_usable() to is_guid_running() This is what this function does. --- dom0/qvm-core/qubes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 83153eb5..1cad8818 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -2431,7 +2431,7 @@ class QubesHVm(QubesVm): xc.domain_unpause(self.stubdom_xid) super(QubesHVm, self).unpause() - def is_fully_usable(self): + def is_guid_running(self): xid = self.stubdom_xid if xid < 0: return False From 5504142187b7bd51c5c1ec318745558761546b30 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 13 Jun 2012 17:11:21 +0200 Subject: [PATCH 4/6] dom0/core: fix race in QubesHVm.stubdom_xid device-model-domid is written to xenstore after domaid id itself, so do not assume the former is present when we know VM XID. --- dom0/qvm-core/qubes.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 1cad8818..2cccf5dd 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -2404,7 +2404,11 @@ class QubesHVm(QubesVm): if self.xid < 0: return -1 - return int(xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid)) + stubdom_xid_str = xs.read('', '/local/domain/%d/image/device-model-domid' % self.xid) + if stubdom_xid_str is not None: + return int(stubdom_xid_str) + else: + return -1 def start_guid(self, verbose = True, notify_function = None): if verbose: From e6a75c732e5f2cae013fb5e0087e617f8b18fc06 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 13 Jun 2012 17:14:25 +0200 Subject: [PATCH 5/6] dom0/core: support for qrexec for Windows HVM Because not every HVM will have qrexec agent installed, introduce VM property 'qrexec_installed'. If it is set, spawn qrexec_daemon at VM startup and allow use of qvm-run. --- dom0/qvm-core/qubes.py | 21 +++++++++++++++++---- dom0/qvm-tools/qvm-prefs | 12 ++++++++++++ 2 files changed, 29 insertions(+), 4 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 2cccf5dd..6447a486 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -268,6 +268,7 @@ class QubesVm(object): "kernels_dir": { 'eval': 'qubes_kernels_base_dir + "/" + self.kernel if self.kernel is not None else ' + \ # for backward compatibility (or another rare case): kernel=None -> kernel in VM dir 'self.dir_path + "/" + default_kernels_subdir' }, + "_start_guid_first": { 'eval': 'False' }, } ### Mark attrs for XML inclusion @@ -1479,10 +1480,13 @@ class QubesVm(object): # the successful unpause is some indicator of it qmemman_client.close() + if self._start_guid_first and start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'): + self.start_guid(verbose=verbose) + if not preparing_dvm: self.start_qrexec_daemon(verbose=verbose) - if start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'): + if not self._start_guid_first and start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'): self.start_guid(verbose=verbose) if preparing_dvm: @@ -2227,6 +2231,8 @@ class QubesHVm(QubesVm): attrs['drive'] = { 'save': 'str(self.drive)' } attrs['maxmem'].pop('save') attrs['timezone'] = { 'default': 'localtime', 'save': 'str(self.timezone)' } + attrs['qrexec_installed'] = { 'default': False, 'save': 'str(self.qrexec_installed)' } + attrs['_start_guid_first']['eval'] = 'True' return attrs @@ -2397,7 +2403,10 @@ class QubesHVm(QubesVm): return "vif{0}.+".format(self.stubdom_xid) def run(self, command, **kwargs): - raise NotImplementedError("Needs qrexec agent - TODO") + if self.qrexec_installed: + super(QubesHVm, self).run(command, **kwargs) + else: + raise QubesException("Needs qrexec agent installed in VM to use this function. See also qvm-prefs.") @property def stubdom_xid(self): @@ -2419,7 +2428,8 @@ class QubesHVm(QubesVm): raise QubesException("Cannot start qubes_guid!") def start_qrexec_daemon(self, **kwargs): - pass + if self.qrexec_installed: + super(QubesHVm, self).start_qrexec_daemon(**kwargs) def pause(self): if dry_run: @@ -2805,7 +2815,7 @@ class QubesVmCollection(dict): "installed_by_rpm", "internal", "uses_default_netvm", "label", "memory", "vcpus", "pcidevs", "maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts", - "mac", "services", "include_in_backups", "debug", "drive" ) + "mac", "services", "include_in_backups", "debug", "qrexec_installed", "drive" ) for attribute in common_attr_list: kwargs[attribute] = element.get(attribute) @@ -2867,6 +2877,9 @@ class QubesVmCollection(dict): if "debug" in kwargs: kwargs["debug"] = True if kwargs["debug"] == "True" else False + if "qrexec_installed" in kwargs: + kwargs["qrexec_installed"] = True if kwargs["qrexec_installed"] == "True" else False + if "drive" in kwargs and kwargs["drive"] == "None": kwargs["drive"] = None diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index a1e60002..84e21ee0 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -78,6 +78,9 @@ def do_list(vm): if hasattr(vm, 'debug'): print fmt.format("debug", "on" if vm.debug else "off") + if hasattr(vm, 'qrexec_installed'): + print fmt.format("qrexec_installed", str(vm.qrexec_installed)) + if hasattr(vm, 'drive'): print fmt.format("drive", str(vm.drive)) @@ -311,6 +314,14 @@ def set_debug(vms, vm, args): vm.debug = bool(eval(args[0].capitalize())) return True +def set_qrexec_installed(vms, vm, args): + if len (args) != 1: + print >> sys.stderr, "Missing value (True/False)!" + return False + + vm.qrexec_installed = bool(eval(args[0].capitalize())) + return True + def set_timezone(vms, vm, args): if len (args) != 1: print >> sys.stderr, "Missing value ('localtime' or timeoffset in seconds)!" @@ -338,6 +349,7 @@ properties = { "drive": set_drive, "mac": set_mac, "debug": set_debug, + "qrexec_installed": set_qrexec_installed, "timezone": set_timezone, } From 6bd988bf8104f330ecef7704de90fd21d5375335 Mon Sep 17 00:00:00 2001 From: Marek Marczykowski Date: Wed, 13 Jun 2012 17:16:12 +0200 Subject: [PATCH 6/6] dom0/core: allow to change default user for qvm-run (#577) This doesn't make all dom0 code VM-username independent, still 'user' is hardcoded in many places. This only change behavior of qvm-run, especially for use in HVM. --- dom0/qvm-core/qubes.py | 6 ++++-- dom0/qvm-tools/qvm-prefs | 12 ++++++++++++ dom0/qvm-tools/qvm-run | 12 ++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/dom0/qvm-core/qubes.py b/dom0/qvm-core/qubes.py index 6447a486..08c2b727 100755 --- a/dom0/qvm-core/qubes.py +++ b/dom0/qvm-core/qubes.py @@ -259,6 +259,7 @@ class QubesVm(object): "include_in_backups": { "default": True }, "services": { "default": {}, "eval": "eval(str(value))" }, "debug": { "default": False }, + "default_user": { "default": "user" }, ##### Internal attributes - will be overriden in __init__ regardless of args "appmenus_templates_dir": { "eval": \ 'self.dir_path + "/" + default_appmenus_templates_subdir if self.updateable else ' + \ @@ -276,7 +277,8 @@ class QubesVm(object): for prop in ['qid', 'name', 'dir_path', 'memory', 'maxmem', 'pcidevs', 'vcpus', 'internal',\ 'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\ 'kernelopts', 'services', 'installed_by_rpm',\ - 'uses_default_netvm', 'include_in_backups', 'debug' ]: + 'uses_default_netvm', 'include_in_backups', 'debug',\ + 'default_user' ]: attrs[prop]['save'] = 'str(self.%s)' % prop # Simple paths for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']: @@ -2815,7 +2817,7 @@ class QubesVmCollection(dict): "installed_by_rpm", "internal", "uses_default_netvm", "label", "memory", "vcpus", "pcidevs", "maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts", - "mac", "services", "include_in_backups", "debug", "qrexec_installed", "drive" ) + "mac", "services", "include_in_backups", "debug", "default_user", "qrexec_installed", "drive" ) for attribute in common_attr_list: kwargs[attribute] = element.get(attribute) diff --git a/dom0/qvm-tools/qvm-prefs b/dom0/qvm-tools/qvm-prefs index 84e21ee0..1f5d6e23 100755 --- a/dom0/qvm-tools/qvm-prefs +++ b/dom0/qvm-tools/qvm-prefs @@ -78,6 +78,9 @@ def do_list(vm): if hasattr(vm, 'debug'): print fmt.format("debug", "on" if vm.debug else "off") + if hasattr(vm, 'default_user'): + print fmt.format("default user", str(vm.default_user)) + if hasattr(vm, 'qrexec_installed'): print fmt.format("qrexec_installed", str(vm.qrexec_installed)) @@ -314,6 +317,14 @@ def set_debug(vms, vm, args): vm.debug = bool(eval(args[0].capitalize())) return True +def set_default_user(vms, vm, args): + if len (args) != 1: + print >> sys.stderr, "Missing user name!" + return False + + vm.default_user = args[0] + return True + def set_qrexec_installed(vms, vm, args): if len (args) != 1: print >> sys.stderr, "Missing value (True/False)!" @@ -349,6 +360,7 @@ properties = { "drive": set_drive, "mac": set_mac, "debug": set_debug, + "default_user": set_default_user, "qrexec_installed": set_qrexec_installed, "timezone": set_timezone, } diff --git a/dom0/qvm-tools/qvm-run b/dom0/qvm-tools/qvm-run index 84f7e099..89d53756 100755 --- a/dom0/qvm-tools/qvm-run +++ b/dom0/qvm-tools/qvm-run @@ -98,7 +98,7 @@ def main(): parser.add_option ("-q", "--quiet", action="store_false", dest="verbose", default=True) parser.add_option ("-a", "--auto", action="store_true", dest="auto", default=False, help="Auto start the VM if not running") - parser.add_option ("-u", "--user", action="store", dest="user", default="user", + parser.add_option ("-u", "--user", action="store", dest="user", default=None, help="Run command in a VM as a specified user") parser.add_option ("--tray", action="store_true", dest="tray", default=False, help="Use tray notifications instead of stdout" ) @@ -188,12 +188,12 @@ def main(): exit(1) vms_list.append(vm) - if takes_cmd_argument: - cmd = "{user}:{cmd}".format(user=options.user, cmd=cmdstr) - else: - cmd = None - for vm in vms_list: + if takes_cmd_argument: + cmd = "{user}:{cmd}".format(user=options.user if options.user else vm.default_user, cmd=cmdstr) + else: + cmd = None + vm_run_cmd(vm, cmd, options)