Merge branch 'hvm-for-master'
Conflicts: dom0/qvm-core/qubes.py dom0/qvm-tools/qvm-sync-clock version_dom0 vm-systemd/qubes-sysinit.sh
This commit is contained in:
commit
3e90174910
53
dom0/aux-tools/block_cleaner_daemon.py
Executable file
53
dom0/aux-tools/block_cleaner_daemon.py
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#!/usr/bin/python
|
||||||
|
|
||||||
|
import xen.lowlevel.xs
|
||||||
|
import time
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
xs = xen.lowlevel.xs.xs()
|
||||||
|
|
||||||
|
domain_list = []
|
||||||
|
|
||||||
|
def setup_watches():
|
||||||
|
global domain_list
|
||||||
|
|
||||||
|
new_domain_list = xs.ls('', '/local/domain')
|
||||||
|
for dom in new_domain_list:
|
||||||
|
if dom not in domain_list:
|
||||||
|
print "Adding: %s" % dom
|
||||||
|
xs.watch('/local/domain/%s/backend/vbd' % dom, int(dom))
|
||||||
|
for dom in domain_list:
|
||||||
|
if dom not in new_domain_list:
|
||||||
|
print "Removing: %s" % dom
|
||||||
|
xs.unwatch('/local/domain/%s/backend/vbd' % dom, int(dom))
|
||||||
|
domain_list = new_domain_list
|
||||||
|
|
||||||
|
def handle_vbd_state(path):
|
||||||
|
state = xs.read('', path)
|
||||||
|
if state == '6':
|
||||||
|
# Closed state; wait a moment to not interrupt reconnect
|
||||||
|
time.sleep(0.500)
|
||||||
|
state = xs.read('', path)
|
||||||
|
if state == '6':
|
||||||
|
# If still closed, detach device
|
||||||
|
path_components = path.split('/')
|
||||||
|
# /local/domain/<BACK XID>/backend/vbd/<FRONT XID>/<DEV>/...
|
||||||
|
vm_xid = path_components[6]
|
||||||
|
vm_dev = path_components[7]
|
||||||
|
if vm_xid in domain_list:
|
||||||
|
subprocess.call(['xl', 'block-detach', vm_xid, vm_dev])
|
||||||
|
|
||||||
|
def main():
|
||||||
|
|
||||||
|
xs.watch('@introduceDomain', 'reload')
|
||||||
|
xs.watch('@releaseDomain', 'reload')
|
||||||
|
setup_watches()
|
||||||
|
while True:
|
||||||
|
(path, token) = xs.read_watch()
|
||||||
|
if token == 'reload':
|
||||||
|
setup_watches()
|
||||||
|
else:
|
||||||
|
if path.endswith('/state'):
|
||||||
|
handle_vbd_state(path)
|
||||||
|
|
||||||
|
main()
|
@ -37,8 +37,10 @@ start()
|
|||||||
MEMINFO_DELAY_USEC=100000
|
MEMINFO_DELAY_USEC=100000
|
||||||
/usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC
|
/usr/lib/qubes/meminfo-writer $MEM_CHANGE_THRESHOLD_KB $MEMINFO_DELAY_USEC
|
||||||
|
|
||||||
|
/usr/lib/qubes/block_cleaner_daemon.py > /var/log/qubes/block_cleaner.log 2>&1 &
|
||||||
|
|
||||||
# Hide mounted devices from qubes-block list (at first udev run, only / is mounted)
|
# Hide mounted devices from qubes-block list (at first udev run, only / is mounted)
|
||||||
for dev in `xenstore-list /local/domain/0/qubes-block-devices`; do
|
for dev in `xenstore-list /local/domain/0/qubes-block-devices 2> /dev/null`; do
|
||||||
( eval `udevadm info -q property -n $dev|sed -e 's/\([^=]*\)=\(.*\)/export \1="\2"/'`;
|
( eval `udevadm info -q property -n $dev|sed -e 's/\([^=]*\)=\(.*\)/export \1="\2"/'`;
|
||||||
/usr/lib/qubes/block_add_change > /dev/null
|
/usr/lib/qubes/block_add_change > /dev/null
|
||||||
)
|
)
|
||||||
|
10
dom0/misc/qubes-start.desktop
Normal file
10
dom0/misc/qubes-start.desktop
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Desktop Entry]
|
||||||
|
Version=1.0
|
||||||
|
Type=Application
|
||||||
|
Exec=qvm-start --quiet --tray %VMNAME%
|
||||||
|
Icon=%VMDIR%/icon.png
|
||||||
|
Terminal=false
|
||||||
|
Name=%VMNAME%: Start
|
||||||
|
GenericName=%VMNAME%: Start
|
||||||
|
StartupNotify=false
|
||||||
|
Categories=System;
|
39
dom0/misc/vm-template-hvm.conf
Normal file
39
dom0/misc/vm-template-hvm.conf
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#
|
||||||
|
# This is a Xen VM config file for Qubes VM
|
||||||
|
# DO NOT EDIT - autogenerated by qubes tools
|
||||||
|
#
|
||||||
|
|
||||||
|
name = "{name}"
|
||||||
|
|
||||||
|
builder='hvm'
|
||||||
|
memory={mem}
|
||||||
|
viridian=1
|
||||||
|
kernel='hvmloader'
|
||||||
|
stdvga=1
|
||||||
|
#acpi=1
|
||||||
|
#apic=1
|
||||||
|
boot='dca'
|
||||||
|
device_model='stubdom-dm'
|
||||||
|
#pae=1
|
||||||
|
usbdevice='tablet'
|
||||||
|
sdl=0
|
||||||
|
vnc=0
|
||||||
|
localtime = {localtime}
|
||||||
|
rtc_timeoffset = {timeoffset}
|
||||||
|
disk = [ {rootdev}
|
||||||
|
{privatedev}
|
||||||
|
{otherdevs}
|
||||||
|
]
|
||||||
|
vif = [ {netdev} ]
|
||||||
|
pci = [ {pcidev} ]
|
||||||
|
vcpus = {vcpus}
|
||||||
|
|
||||||
|
#tsc_mode = 2
|
||||||
|
#xen_extended_power_mgmt=0
|
||||||
|
|
||||||
|
on_poweroff = 'destroy'
|
||||||
|
on_reboot = 'destroy'
|
||||||
|
on_crash = 'destroy'
|
||||||
|
|
||||||
|
# Use of DNS2 as DHCP server IP makes DNS2 not accessible, but DNS1 still should work
|
||||||
|
device_model_args = [ '-net', 'lwip,client_ip={ip},server_ip={dns2},dns={dns1},gw={gateway},netmask={netmask}' ]
|
@ -8,7 +8,7 @@ sync_qubes_vms_wallclock()
|
|||||||
DATE=$(date)
|
DATE=$(date)
|
||||||
echo
|
echo
|
||||||
echo "Syncing VMs clock to: $DATE"
|
echo "Syncing VMs clock to: $DATE"
|
||||||
qvm-run --all --exclude=`qubes-prefs --get clockvm` -u root "date -s \"$DATE\""
|
qvm-run --all -u root "date -s \"$DATE\""
|
||||||
# Then try to sync from the network
|
# Then try to sync from the network
|
||||||
/usr/bin/qvm-sync-clock &
|
/usr/bin/qvm-sync-clock &
|
||||||
}
|
}
|
||||||
|
@ -52,12 +52,11 @@ void wait_for_child(int statusfd)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void do_unpack(int);
|
extern int do_unpack(void);
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
char *incoming_dir;
|
char *incoming_dir;
|
||||||
int pipefds[2];
|
|
||||||
int uid;
|
int uid;
|
||||||
char *var;
|
char *var;
|
||||||
long long files_limit = DEFAULT_MAX_UPDATES_FILES;
|
long long files_limit = DEFAULT_MAX_UPDATES_FILES;
|
||||||
@ -73,32 +72,15 @@ int main(int argc, char ** argv)
|
|||||||
if ((var=getenv("UPDATES_MAX_FILES")))
|
if ((var=getenv("UPDATES_MAX_FILES")))
|
||||||
files_limit = atoll(var);
|
files_limit = atoll(var);
|
||||||
|
|
||||||
pipe(pipefds);
|
|
||||||
|
|
||||||
uid = prepare_creds_return_uid(argv[1]);
|
uid = prepare_creds_return_uid(argv[1]);
|
||||||
|
|
||||||
incoming_dir = argv[2];
|
incoming_dir = argv[2];
|
||||||
mkdir(incoming_dir, 0700);
|
mkdir(incoming_dir, 0700);
|
||||||
if (chdir(incoming_dir))
|
if (chdir(incoming_dir))
|
||||||
gui_fatal("Error chdir to %s", incoming_dir);
|
gui_fatal("Error chdir to %s", incoming_dir);
|
||||||
switch (fork()) {
|
if (chroot(incoming_dir)) //impossible
|
||||||
case -1:
|
gui_fatal("Error chroot to %s", incoming_dir);
|
||||||
perror("fork");
|
|
||||||
exit(1);
|
|
||||||
case 0:
|
|
||||||
if (chroot(incoming_dir)) //impossible
|
|
||||||
gui_fatal("Error chroot to %s", incoming_dir);
|
|
||||||
setuid(uid);
|
|
||||||
close(pipefds[0]);
|
|
||||||
set_size_limit(bytes_limit, files_limit);
|
|
||||||
do_unpack(pipefds[1]);
|
|
||||||
exit(0);
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
|
|
||||||
setuid(uid);
|
setuid(uid);
|
||||||
close(pipefds[1]);
|
set_size_limit(bytes_limit, files_limit);
|
||||||
wait_for_child(pipefds[0]);
|
return do_unpack();
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import stat
|
||||||
import os
|
import os
|
||||||
import os.path
|
import os.path
|
||||||
import subprocess
|
import subprocess
|
||||||
@ -76,7 +77,14 @@ default_memory = 400
|
|||||||
default_kernelopts = ""
|
default_kernelopts = ""
|
||||||
default_kernelopts_pcidevs = "iommu=soft swiotlb=2048"
|
default_kernelopts_pcidevs = "iommu=soft swiotlb=2048"
|
||||||
|
|
||||||
|
default_hvm_disk_size = 20*1024*1024*1024
|
||||||
|
default_hvm_private_img_size = 2*1024*1024*1024
|
||||||
|
default_hvm_memory = 512
|
||||||
|
|
||||||
config_template_pv = '/usr/share/qubes/vm-template.conf'
|
config_template_pv = '/usr/share/qubes/vm-template.conf'
|
||||||
|
config_template_hvm = '/usr/share/qubes/vm-template-hvm.conf'
|
||||||
|
|
||||||
|
start_appmenu_template = '/usr/share/qubes/qubes-start.desktop'
|
||||||
|
|
||||||
qubes_whitelisted_appmenus = 'whitelisted-appmenus.list'
|
qubes_whitelisted_appmenus = 'whitelisted-appmenus.list'
|
||||||
|
|
||||||
@ -252,6 +260,7 @@ class QubesVm(object):
|
|||||||
"services": { "default": {}, "eval": "eval(str(value))" },
|
"services": { "default": {}, "eval": "eval(str(value))" },
|
||||||
"debug": { "default": False },
|
"debug": { "default": False },
|
||||||
"default_user": { "default": "user" },
|
"default_user": { "default": "user" },
|
||||||
|
"qrexec_timeout": { "default": 60, "eval": "int(value)" },
|
||||||
##### Internal attributes - will be overriden in __init__ regardless of args
|
##### Internal attributes - will be overriden in __init__ regardless of args
|
||||||
"appmenus_templates_dir": { "eval": \
|
"appmenus_templates_dir": { "eval": \
|
||||||
'self.dir_path + "/" + default_appmenus_templates_subdir if self.updateable else ' + \
|
'self.dir_path + "/" + default_appmenus_templates_subdir if self.updateable else ' + \
|
||||||
@ -263,6 +272,7 @@ class QubesVm(object):
|
|||||||
"kernels_dir": { 'eval': 'qubes_kernels_base_dir + "/" + self.kernel if self.kernel is not None else ' + \
|
"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
|
# for backward compatibility (or another rare case): kernel=None -> kernel in VM dir
|
||||||
'self.dir_path + "/" + default_kernels_subdir' },
|
'self.dir_path + "/" + default_kernels_subdir' },
|
||||||
|
"_start_guid_first": { 'eval': 'False' },
|
||||||
}
|
}
|
||||||
|
|
||||||
### Mark attrs for XML inclusion
|
### Mark attrs for XML inclusion
|
||||||
@ -271,7 +281,7 @@ class QubesVm(object):
|
|||||||
'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\
|
'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\
|
||||||
'kernelopts', 'services', 'installed_by_rpm',\
|
'kernelopts', 'services', 'installed_by_rpm',\
|
||||||
'uses_default_netvm', 'include_in_backups', 'debug',\
|
'uses_default_netvm', 'include_in_backups', 'debug',\
|
||||||
'default_user' ]:
|
'default_user', 'qrexec_timeout' ]:
|
||||||
attrs[prop]['save'] = 'str(self.%s)' % prop
|
attrs[prop]['save'] = 'str(self.%s)' % prop
|
||||||
# Simple paths
|
# Simple paths
|
||||||
for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']:
|
for prop in ['conf_file', 'root_img', 'volatile_img', 'private_img']:
|
||||||
@ -896,18 +906,24 @@ class QubesVm(object):
|
|||||||
# If dynamic memory management disabled, set maxmem=mem
|
# If dynamic memory management disabled, set maxmem=mem
|
||||||
args['maxmem'] = args['mem']
|
args['maxmem'] = args['mem']
|
||||||
args['vcpus'] = str(self.vcpus)
|
args['vcpus'] = str(self.vcpus)
|
||||||
args['ip'] = self.ip
|
|
||||||
args['mac'] = self.mac
|
|
||||||
args['gateway'] = self.gateway
|
|
||||||
args['dns1'] = self.gateway
|
|
||||||
args['dns2'] = self.secondary_dns
|
|
||||||
args['netmask'] = self.netmask
|
|
||||||
if self.netvm is not None:
|
if self.netvm is not None:
|
||||||
|
args['ip'] = self.ip
|
||||||
|
args['mac'] = self.mac
|
||||||
|
args['gateway'] = self.netvm.gateway
|
||||||
|
args['dns1'] = self.netvm.gateway
|
||||||
|
args['dns2'] = self.secondary_dns
|
||||||
|
args['netmask'] = self.netmask
|
||||||
args['netdev'] = "'mac={mac},script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip, mac=self.mac)
|
args['netdev'] = "'mac={mac},script=/etc/xen/scripts/vif-route-qubes,ip={ip}".format(ip=self.ip, mac=self.mac)
|
||||||
if self.netvm.qid != 0:
|
if self.netvm.qid != 0:
|
||||||
args['netdev'] += ",backend={0}".format(self.netvm.name)
|
args['netdev'] += ",backend={0}".format(self.netvm.name)
|
||||||
args['netdev'] += "'"
|
args['netdev'] += "'"
|
||||||
else:
|
else:
|
||||||
|
args['ip'] = ''
|
||||||
|
args['mac'] = ''
|
||||||
|
args['gateway'] = ''
|
||||||
|
args['dns1'] = ''
|
||||||
|
args['dns2'] = ''
|
||||||
|
args['netmask'] = ''
|
||||||
args['netdev'] = ''
|
args['netdev'] = ''
|
||||||
args['rootdev'] = self.get_rootdev(source_template=source_template)
|
args['rootdev'] = self.get_rootdev(source_template=source_template)
|
||||||
args['privatedev'] = "'script:file:{dir}/private.img,xvdb,w',".format(dir=self.dir_path)
|
args['privatedev'] = "'script:file:{dir}/private.img,xvdb,w',".format(dir=self.dir_path)
|
||||||
@ -1346,6 +1362,7 @@ class QubesVm(object):
|
|||||||
|
|
||||||
if passio_popen:
|
if passio_popen:
|
||||||
popen_kwargs={'stdout': subprocess.PIPE}
|
popen_kwargs={'stdout': subprocess.PIPE}
|
||||||
|
popen_kwargs['stdin'] = subprocess.PIPE
|
||||||
if passio_stderr:
|
if passio_stderr:
|
||||||
popen_kwargs['stderr'] = subprocess.PIPE
|
popen_kwargs['stderr'] = subprocess.PIPE
|
||||||
else:
|
else:
|
||||||
@ -1422,6 +1439,17 @@ class QubesVm(object):
|
|||||||
if notify_function is not None:
|
if notify_function is not None:
|
||||||
notify_function("error", "ERROR: Cannot start the Qubes Clipboard Notifier!")
|
notify_function("error", "ERROR: Cannot start the Qubes Clipboard Notifier!")
|
||||||
|
|
||||||
|
def start_qrexec_daemon(self, verbose = False):
|
||||||
|
if verbose:
|
||||||
|
print >> sys.stderr, "--> Starting the qrexec daemon..."
|
||||||
|
xid = self.get_xid()
|
||||||
|
qrexec_env = os.environ
|
||||||
|
qrexec_env['QREXEC_STARTUP_TIMEOUT'] = str(self.qrexec_timeout)
|
||||||
|
retcode = subprocess.call ([qrexec_daemon_path, str(xid), self.default_user], env=qrexec_env)
|
||||||
|
if (retcode != 0) :
|
||||||
|
self.force_shutdown()
|
||||||
|
raise OSError ("ERROR: Cannot execute qrexec_daemon!")
|
||||||
|
|
||||||
def start(self, debug_console = False, verbose = False, preparing_dvm = False, start_guid = True):
|
def start(self, debug_console = False, verbose = False, preparing_dvm = False, start_guid = True):
|
||||||
if dry_run:
|
if dry_run:
|
||||||
return
|
return
|
||||||
@ -1501,15 +1529,13 @@ class QubesVm(object):
|
|||||||
# the successful unpause is some indicator of it
|
# the successful unpause is some indicator of it
|
||||||
qmemman_client.close()
|
qmemman_client.close()
|
||||||
|
|
||||||
if not preparing_dvm:
|
if self._start_guid_first and start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'):
|
||||||
if verbose:
|
self.start_guid(verbose=verbose)
|
||||||
print >> sys.stderr, "--> Starting the qrexec daemon..."
|
|
||||||
retcode = subprocess.call ([qrexec_daemon_path, str(xid)])
|
|
||||||
if (retcode != 0) :
|
|
||||||
self.force_shutdown()
|
|
||||||
raise OSError ("ERROR: Cannot execute qrexec_daemon!")
|
|
||||||
|
|
||||||
if start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'):
|
if not preparing_dvm:
|
||||||
|
self.start_qrexec_daemon(verbose=verbose)
|
||||||
|
|
||||||
|
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)
|
self.start_guid(verbose=verbose)
|
||||||
|
|
||||||
if preparing_dvm:
|
if preparing_dvm:
|
||||||
@ -2223,6 +2249,253 @@ class QubesAppVm(QubesVm):
|
|||||||
self.remove_appmenus()
|
self.remove_appmenus()
|
||||||
super(QubesAppVm, self).remove_from_disk()
|
super(QubesAppVm, self).remove_from_disk()
|
||||||
|
|
||||||
|
class QubesHVm(QubesVm):
|
||||||
|
"""
|
||||||
|
A class that represents an HVM. A child of QubesVm.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# FIXME: logically should inherit after QubesAppVm, but none of its methods
|
||||||
|
# are useful for HVM
|
||||||
|
|
||||||
|
def _get_attrs_config(self):
|
||||||
|
attrs = super(QubesHVm, self)._get_attrs_config()
|
||||||
|
attrs.pop('kernel')
|
||||||
|
attrs.pop('kernels_dir')
|
||||||
|
attrs.pop('kernelopts')
|
||||||
|
attrs.pop('uses_default_kernel')
|
||||||
|
attrs.pop('uses_default_kernelopts')
|
||||||
|
attrs['volatile_img']['eval'] = 'None'
|
||||||
|
attrs['config_file_template']['eval'] = 'config_template_hvm'
|
||||||
|
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
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
|
||||||
|
if "dir_path" not in kwargs or kwargs["dir_path"] is None:
|
||||||
|
kwargs["dir_path"] = qubes_appvms_dir + "/" + kwargs["name"]
|
||||||
|
|
||||||
|
# only updateable HVM supported
|
||||||
|
kwargs["updateable"] = True
|
||||||
|
kwargs["template_vm"] = None
|
||||||
|
if "memory" not in kwargs or kwargs["memory"] is None:
|
||||||
|
kwargs["memory"] = default_hvm_memory
|
||||||
|
|
||||||
|
super(QubesHVm, self).__init__(**kwargs)
|
||||||
|
# HVM doesn't support dynamic memory management
|
||||||
|
self.maxmem = self.memory
|
||||||
|
|
||||||
|
@property
|
||||||
|
def type(self):
|
||||||
|
return "HVM"
|
||||||
|
|
||||||
|
def is_appvm(self):
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get_clone_attrs(self):
|
||||||
|
attrs = super(QubesHVm, self).get_clone_attrs()
|
||||||
|
attrs.remove('kernel')
|
||||||
|
attrs.remove('uses_default_kernel')
|
||||||
|
attrs.remove('kernelopts')
|
||||||
|
attrs.remove('uses_default_kernelopts')
|
||||||
|
attrs += [ 'timezone' ]
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create_on_disk(self, verbose, source_template = None):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print >> sys.stderr, "--> Creating directory: {0}".format(self.dir_path)
|
||||||
|
os.mkdir (self.dir_path)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print >> sys.stderr, "--> Creating icon symlink: {0} -> {1}".format(self.icon_path, self.label.icon_path)
|
||||||
|
os.symlink (self.label.icon_path, self.icon_path)
|
||||||
|
|
||||||
|
if verbose:
|
||||||
|
print >> sys.stderr, "--> Creating appmenus directory: {0}".format(self.appmenus_templates_dir)
|
||||||
|
os.mkdir (self.appmenus_templates_dir)
|
||||||
|
shutil.copy (start_appmenu_template, self.appmenus_templates_dir)
|
||||||
|
|
||||||
|
if not self.internal:
|
||||||
|
self.create_appmenus (verbose, source_template=source_template)
|
||||||
|
|
||||||
|
self.create_config_file()
|
||||||
|
|
||||||
|
# create empty disk
|
||||||
|
f_root = open(self.root_img, "w")
|
||||||
|
f_root.truncate(default_hvm_disk_size)
|
||||||
|
f_root.close()
|
||||||
|
|
||||||
|
# create empty private.img
|
||||||
|
f_private = open(self.private_img, "w")
|
||||||
|
f_private.truncate(default_hvm_private_img_size)
|
||||||
|
f_root.close()
|
||||||
|
|
||||||
|
def remove_from_disk(self):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.remove_appmenus()
|
||||||
|
super(QubesHVm, self).remove_from_disk()
|
||||||
|
|
||||||
|
def get_disk_utilization_private_img(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def get_private_img_sz(self):
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def resize_private_img(self, size):
|
||||||
|
raise NotImplementedError("HVM has no private.img")
|
||||||
|
|
||||||
|
def get_config_params(self, source_template=None):
|
||||||
|
|
||||||
|
params = super(QubesHVm, self).get_config_params(source_template=source_template)
|
||||||
|
|
||||||
|
params['volatiledev'] = ''
|
||||||
|
if self.drive:
|
||||||
|
type_mode = ":cdrom,r"
|
||||||
|
drive_path = self.drive
|
||||||
|
# leave empty to use standard syntax in case of dom0
|
||||||
|
backend_domain = ""
|
||||||
|
if drive_path.startswith("hd:"):
|
||||||
|
type_mode = ",w"
|
||||||
|
drive_path = drive_path[3:]
|
||||||
|
elif drive_path.startswith("cdrom:"):
|
||||||
|
type_mode = ":cdrom,r"
|
||||||
|
drive_path = drive_path[6:]
|
||||||
|
backend_split = re.match(r"^([a-zA-Z0-9-]*):(.*)", drive_path)
|
||||||
|
if backend_split:
|
||||||
|
backend_domain = "," + backend_split.group(1)
|
||||||
|
drive_path = backend_split.group(2)
|
||||||
|
|
||||||
|
# FIXME: os.stat will work only when backend in dom0...
|
||||||
|
stat_res = None
|
||||||
|
if backend_domain == "":
|
||||||
|
stat_res = os.stat(drive_path)
|
||||||
|
if stat_res and stat.S_ISBLK(stat_res.st_mode):
|
||||||
|
params['otherdevs'] = "'phy:%s,xvdc%s%s'," % (drive_path, type_mode, backend_domain)
|
||||||
|
else:
|
||||||
|
params['otherdevs'] = "'script:file:%s,xvdc%s%s'," % (drive_path, type_mode, backend_domain)
|
||||||
|
else:
|
||||||
|
params['otherdevs'] = ''
|
||||||
|
|
||||||
|
# Disable currently unused private.img - to be enabled when TemplateHVm done
|
||||||
|
params['privatedev'] = ''
|
||||||
|
|
||||||
|
if self.timezone.lower() == 'localtime':
|
||||||
|
params['localtime'] = '1'
|
||||||
|
params['timeoffset'] = '0'
|
||||||
|
elif self.timezone.isdigit():
|
||||||
|
params['localtime'] = '0'
|
||||||
|
params['timeoffset'] = self.timezone
|
||||||
|
else:
|
||||||
|
print >>sys.stderr, "WARNING: invalid 'timezone' value: %s" % self.timezone
|
||||||
|
params['localtime'] = '0'
|
||||||
|
params['timeoffset'] = '0'
|
||||||
|
return params
|
||||||
|
|
||||||
|
def verify_files(self):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
if not os.path.exists (self.dir_path):
|
||||||
|
raise QubesException (
|
||||||
|
"VM directory doesn't exist: {0}".\
|
||||||
|
format(self.dir_path))
|
||||||
|
|
||||||
|
if self.is_updateable() and not os.path.exists (self.root_img):
|
||||||
|
raise QubesException (
|
||||||
|
"VM root image file doesn't exist: {0}".\
|
||||||
|
format(self.root_img))
|
||||||
|
|
||||||
|
if not os.path.exists (self.private_img):
|
||||||
|
print >>sys.stderr, "WARNING: Creating empty VM private image file: {0}".\
|
||||||
|
format(self.private_img)
|
||||||
|
f_private = open(self.private_img, "w")
|
||||||
|
f_private.truncate(default_hvm_private_img_size)
|
||||||
|
f_private.close()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def reset_volatile_storage(self, **kwargs):
|
||||||
|
pass
|
||||||
|
|
||||||
|
@property
|
||||||
|
def vif(self):
|
||||||
|
if self.xid < 0:
|
||||||
|
return None
|
||||||
|
if self.netvm is None:
|
||||||
|
return None
|
||||||
|
return "vif{0}.+".format(self.stubdom_xid)
|
||||||
|
|
||||||
|
def run(self, command, **kwargs):
|
||||||
|
if self.qrexec_installed:
|
||||||
|
if 'gui' in kwargs and kwargs['gui']==False:
|
||||||
|
command = "nogui:" + command
|
||||||
|
return 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):
|
||||||
|
if self.xid < 0:
|
||||||
|
return -1
|
||||||
|
|
||||||
|
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:
|
||||||
|
print >> sys.stderr, "--> Starting Qubes GUId..."
|
||||||
|
|
||||||
|
retcode = subprocess.call ([qubes_guid_path, "-d", str(self.stubdom_xid), "-c", self.label.color, "-i", self.label.icon, "-l", str(self.label.index)])
|
||||||
|
if (retcode != 0) :
|
||||||
|
raise QubesException("Cannot start qubes_guid!")
|
||||||
|
|
||||||
|
def start_qrexec_daemon(self, **kwargs):
|
||||||
|
if self.qrexec_installed:
|
||||||
|
super(QubesHVm, self).start_qrexec_daemon(**kwargs)
|
||||||
|
|
||||||
|
if kwargs.get('verbose'):
|
||||||
|
print >> sys.stderr, "--> Waiting for user '%s' login..." % self.default_user
|
||||||
|
|
||||||
|
p = self.run('SYSTEM:QUBESRPC qubes.WaitForSession', passio_popen=True, gui=False, wait=True)
|
||||||
|
p.communicate(input=self.default_user)
|
||||||
|
|
||||||
|
retcode = subprocess.call([qubes_clipd_path])
|
||||||
|
if retcode != 0:
|
||||||
|
print >> sys.stderr, "ERROR: Cannot start qclipd!"
|
||||||
|
|
||||||
|
def pause(self):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
xc.domain_pause(self.stubdom_xid)
|
||||||
|
super(QubesHVm, self).pause()
|
||||||
|
|
||||||
|
def unpause(self):
|
||||||
|
if dry_run:
|
||||||
|
return
|
||||||
|
|
||||||
|
xc.domain_unpause(self.stubdom_xid)
|
||||||
|
super(QubesHVm, self).unpause()
|
||||||
|
|
||||||
|
def is_guid_running(self):
|
||||||
|
xid = self.stubdom_xid
|
||||||
|
if xid < 0:
|
||||||
|
return False
|
||||||
|
if not os.path.exists('/var/run/qubes/guid_running.%d' % xid):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
class QubesVmCollection(dict):
|
class QubesVmCollection(dict):
|
||||||
"""
|
"""
|
||||||
@ -2279,6 +2552,18 @@ class QubesVmCollection(dict):
|
|||||||
self[vm.qid]=vm
|
self[vm.qid]=vm
|
||||||
return vm
|
return vm
|
||||||
|
|
||||||
|
def add_new_hvm(self, name, label = None):
|
||||||
|
|
||||||
|
qid = self.get_new_unused_qid()
|
||||||
|
vm = QubesHVm (qid=qid, name=name,
|
||||||
|
netvm = self.get_default_netvm(),
|
||||||
|
label=label)
|
||||||
|
|
||||||
|
if not self.verify_new_vm (vm):
|
||||||
|
assert False, "Wrong VM description!"
|
||||||
|
self[vm.qid]=vm
|
||||||
|
return vm
|
||||||
|
|
||||||
def add_new_disposablevm(self, name, template, dispid,
|
def add_new_disposablevm(self, name, template, dispid,
|
||||||
label = None):
|
label = None):
|
||||||
|
|
||||||
@ -2574,7 +2859,8 @@ class QubesVmCollection(dict):
|
|||||||
"installed_by_rpm", "internal",
|
"installed_by_rpm", "internal",
|
||||||
"uses_default_netvm", "label", "memory", "vcpus", "pcidevs",
|
"uses_default_netvm", "label", "memory", "vcpus", "pcidevs",
|
||||||
"maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts",
|
"maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts",
|
||||||
"mac", "services", "include_in_backups", "debug", "default_user" )
|
"mac", "services", "include_in_backups", "debug",
|
||||||
|
"default_user", "qrexec_timeout", "qrexec_installed", "drive" )
|
||||||
|
|
||||||
for attribute in common_attr_list:
|
for attribute in common_attr_list:
|
||||||
kwargs[attribute] = element.get(attribute)
|
kwargs[attribute] = element.get(attribute)
|
||||||
@ -2628,13 +2914,20 @@ class QubesVmCollection(dict):
|
|||||||
if "uses_default_kernelopts" in kwargs:
|
if "uses_default_kernelopts" in kwargs:
|
||||||
kwargs["uses_default_kernelopts"] = False if kwargs["uses_default_kernelopts"] == "False" else True
|
kwargs["uses_default_kernelopts"] = False if kwargs["uses_default_kernelopts"] == "False" else True
|
||||||
|
|
||||||
if "kernelopts" not in kwargs or kwargs["kernelopts"] == "None":
|
if "kernelopts" in kwargs and kwargs["kernelopts"] == "None":
|
||||||
kwargs.pop("kernelopts")
|
kwargs.pop("kernelopts")
|
||||||
|
if "kernelopts" not in kwargs:
|
||||||
kwargs["uses_default_kernelopts"] = True
|
kwargs["uses_default_kernelopts"] = True
|
||||||
|
|
||||||
if "debug" in kwargs:
|
if "debug" in kwargs:
|
||||||
kwargs["debug"] = True if kwargs["debug"] == "True" else False
|
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
|
||||||
|
|
||||||
return kwargs
|
return kwargs
|
||||||
|
|
||||||
def set_netvm_dependency(self, element):
|
def set_netvm_dependency(self, element):
|
||||||
@ -2807,6 +3100,20 @@ class QubesVmCollection(dict):
|
|||||||
os.path.basename(sys.argv[0]), err))
|
os.path.basename(sys.argv[0]), err))
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
# And HVMs
|
||||||
|
for element in tree.findall("QubesHVm"):
|
||||||
|
try:
|
||||||
|
kwargs = self.parse_xml_element(element)
|
||||||
|
vm = QubesHVm(**kwargs)
|
||||||
|
|
||||||
|
self[vm.qid] = vm
|
||||||
|
|
||||||
|
self.set_netvm_dependency(element)
|
||||||
|
except (ValueError, LookupError) as err:
|
||||||
|
print("{0}: import error (QubesHVm): {1}".format(
|
||||||
|
os.path.basename(sys.argv[0]), err))
|
||||||
|
return False
|
||||||
|
|
||||||
# Really finally, read in the DisposableVMs
|
# Really finally, read in the DisposableVMs
|
||||||
for element in tree.findall("QubesDisposableVm"):
|
for element in tree.findall("QubesDisposableVm"):
|
||||||
try:
|
try:
|
||||||
|
@ -896,8 +896,8 @@ def backup_restore_print_summary(restore_info, print_callback = print_stdout):
|
|||||||
+ ('}' if vm.is_netvm() else '')"},
|
+ ('}' if vm.is_netvm() else '')"},
|
||||||
|
|
||||||
"type": {"func": "'Tpl' if vm.is_template() else \
|
"type": {"func": "'Tpl' if vm.is_template() else \
|
||||||
('Proxy' if vm.is_proxyvm() else \
|
'HVM' if vm.type == 'HVM' else \
|
||||||
(' Net' if vm.is_netvm() else 'App'))"},
|
vm.type.replace('VM','')"},
|
||||||
|
|
||||||
"updbl" : {"func": "'Yes' if vm.updateable else ''"},
|
"updbl" : {"func": "'Yes' if vm.updateable else ''"},
|
||||||
|
|
||||||
@ -1131,10 +1131,14 @@ def backup_restore_do(backup_dir, restore_info, host_collection = None, print_ca
|
|||||||
new_vm = None
|
new_vm = None
|
||||||
try:
|
try:
|
||||||
restore_vm_dir (backup_dir, vm.dir_path, qubes_appvms_dir);
|
restore_vm_dir (backup_dir, vm.dir_path, qubes_appvms_dir);
|
||||||
new_vm = host_collection.add_new_appvm(vm.name, template,
|
if vm.type == "HVM":
|
||||||
conf_file=vm.conf_file,
|
new_vm = host_collection.add_new_hvm(vm.name,
|
||||||
dir_path=vm.dir_path,
|
label=vm.label)
|
||||||
label=vm.label)
|
else:
|
||||||
|
new_vm = host_collection.add_new_appvm(vm.name, template,
|
||||||
|
conf_file=vm.conf_file,
|
||||||
|
dir_path=vm.dir_path,
|
||||||
|
label=vm.label)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
error_callback("ERROR: {0}".format(err))
|
error_callback("ERROR: {0}".format(err))
|
||||||
error_callback("*** Skipping VM: {0}".format(vm.name))
|
error_callback("*** Skipping VM: {0}".format(vm.name))
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
#
|
#
|
||||||
|
|
||||||
from qubes.qubes import QubesVmCollection
|
from qubes.qubes import QubesVmCollection
|
||||||
from qubes.qubes import QubesAppVm, QubesTemplateVm
|
from qubes.qubes import QubesAppVm, QubesTemplateVm, QubesHVm
|
||||||
from qubes.qubes import QubesException
|
from qubes.qubes import QubesException
|
||||||
from optparse import OptionParser;
|
from optparse import OptionParser;
|
||||||
import sys
|
import sys
|
||||||
@ -72,6 +72,8 @@ def main():
|
|||||||
dst_vm = qvm_collection.add_new_appvm(name=dstname, template=src_vm.template,
|
dst_vm = qvm_collection.add_new_appvm(name=dstname, template=src_vm.template,
|
||||||
label=src_vm.label,
|
label=src_vm.label,
|
||||||
dir_path=options.dir_path)
|
dir_path=options.dir_path)
|
||||||
|
elif isinstance(src_vm, QubesHVm):
|
||||||
|
dst_vm = qvm_collection.add_new_hvm(name=dstname, label=src_vm.label)
|
||||||
else:
|
else:
|
||||||
print >> sys.stderr, "ERROR: Clone not supported for this type of VM"
|
print >> sys.stderr, "ERROR: Clone not supported for this type of VM"
|
||||||
exit(1)
|
exit(1)
|
||||||
|
@ -38,10 +38,14 @@ def main():
|
|||||||
help="Specify the label to use for the new VM (e.g. red, yellow, green, ...)")
|
help="Specify the label to use for the new VM (e.g. red, yellow, green, ...)")
|
||||||
parser.add_option ("-p", "--proxy", action="store_true", dest="proxyvm", default=False,
|
parser.add_option ("-p", "--proxy", action="store_true", dest="proxyvm", default=False,
|
||||||
help="Create ProxyVM")
|
help="Create ProxyVM")
|
||||||
|
parser.add_option ("-H", "--hvm", action="store_true", dest="hvm", default=False,
|
||||||
|
help="Create HVM (implies --standalone)")
|
||||||
parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False,
|
parser.add_option ("-n", "--net", action="store_true", dest="netvm", default=False,
|
||||||
help="Create NetVM")
|
help="Create NetVM")
|
||||||
parser.add_option ("-s", "--standalone", action="store_true", dest="standalone", default=False,
|
parser.add_option ("-s", "--standalone", action="store_true", dest="standalone", default=False,
|
||||||
help="Create standalone VM - independent of template ")
|
help="Create standalone VM - independent of template ")
|
||||||
|
parser.add_option ("-r", "--root", dest="root", default=None,
|
||||||
|
help="Use provided root.img instead of default/empty one (file will be MOVED)")
|
||||||
parser.add_option ("-m", "--mem", dest="mem", default=None,
|
parser.add_option ("-m", "--mem", dest="mem", default=None,
|
||||||
help="Initial memory size (in MB)")
|
help="Initial memory size (in MB)")
|
||||||
parser.add_option ("-c", "--vcpus", dest="vcpus", default=None,
|
parser.add_option ("-c", "--vcpus", dest="vcpus", default=None,
|
||||||
@ -83,6 +87,18 @@ def main():
|
|||||||
exit (1)
|
exit (1)
|
||||||
label = QubesVmLabels[options.label]
|
label = QubesVmLabels[options.label]
|
||||||
|
|
||||||
|
if options.hvm:
|
||||||
|
# Only standalone HVMs are supported for now
|
||||||
|
options.standalone = True
|
||||||
|
|
||||||
|
if not options.standalone and options.root is not None:
|
||||||
|
print >> sys.stderr, "root.img can be specified only for standalone VMs"
|
||||||
|
exit (1)
|
||||||
|
|
||||||
|
if options.root is not None and not os.path.exists(options.root):
|
||||||
|
print >> sys.stderr, "File specified as root.img does not exists"
|
||||||
|
exit (1)
|
||||||
|
|
||||||
qvm_collection = QubesVmCollection()
|
qvm_collection = QubesVmCollection()
|
||||||
qvm_collection.lock_db_for_writing()
|
qvm_collection.lock_db_for_writing()
|
||||||
qvm_collection.load()
|
qvm_collection.load()
|
||||||
@ -91,6 +107,7 @@ def main():
|
|||||||
print >> sys.stderr, "A VM with the name '{0}' already exists in the system.".format(vmname)
|
print >> sys.stderr, "A VM with the name '{0}' already exists in the system.".format(vmname)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
template = None
|
||||||
if options.template is not None:
|
if options.template is not None:
|
||||||
template = qvm_collection.get_vm_by_name(options.template)
|
template = qvm_collection.get_vm_by_name(options.template)
|
||||||
if template is None:
|
if template is None:
|
||||||
@ -102,7 +119,7 @@ def main():
|
|||||||
if (options.verbose):
|
if (options.verbose):
|
||||||
print "--> Using TemplateVM: {0}".format(template.name)
|
print "--> Using TemplateVM: {0}".format(template.name)
|
||||||
|
|
||||||
else:
|
elif not options.hvm:
|
||||||
if qvm_collection.get_default_template() is None:
|
if qvm_collection.get_default_template() is None:
|
||||||
print >> sys.stderr, "No default TempleteVM defined!"
|
print >> sys.stderr, "No default TempleteVM defined!"
|
||||||
exit (1)
|
exit (1)
|
||||||
@ -122,6 +139,8 @@ def main():
|
|||||||
vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label)
|
vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label)
|
||||||
elif options.proxyvm:
|
elif options.proxyvm:
|
||||||
vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label)
|
vm = qvm_collection.add_new_proxyvm(vmname, new_vm_template, label = label)
|
||||||
|
elif options.hvm:
|
||||||
|
vm = qvm_collection.add_new_hvm(vmname, label = label)
|
||||||
else:
|
else:
|
||||||
vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label)
|
vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label)
|
||||||
except QubesException as err:
|
except QubesException as err:
|
||||||
@ -139,6 +158,9 @@ def main():
|
|||||||
|
|
||||||
try:
|
try:
|
||||||
vm.create_on_disk(verbose=options.verbose, source_template=template)
|
vm.create_on_disk(verbose=options.verbose, source_template=template)
|
||||||
|
if options.root:
|
||||||
|
os.unlink(vm.root_img)
|
||||||
|
os.rename(options.root, vm.root_img)
|
||||||
|
|
||||||
except (IOError, OSError) as err:
|
except (IOError, OSError) as err:
|
||||||
print >> sys.stderr, "ERROR: {0}".format(err)
|
print >> sys.stderr, "ERROR: {0}".format(err)
|
||||||
|
@ -53,21 +53,23 @@ def do_list(vm):
|
|||||||
print fmt.format ("root COW img", vm.rootcow_img)
|
print fmt.format ("root COW img", vm.rootcow_img)
|
||||||
if vm.template is not None:
|
if vm.template is not None:
|
||||||
print fmt.format ("root img", vm.template.root_img)
|
print fmt.format ("root img", vm.template.root_img)
|
||||||
if hasattr(vm, 'volatile_img'):
|
if hasattr(vm, 'volatile_img') and vm.volatile_img is not None:
|
||||||
print fmt.format ("root volatile img", vm.volatile_img)
|
print fmt.format ("root volatile img", vm.volatile_img)
|
||||||
|
|
||||||
if hasattr(vm, 'private_img'):
|
if hasattr(vm, 'private_img') and vm.private_img is not None:
|
||||||
print fmt.format ("private img", vm.private_img)
|
print fmt.format ("private img", vm.private_img)
|
||||||
print fmt.format ("vcpus", str(vm.vcpus))
|
print fmt.format ("vcpus", str(vm.vcpus))
|
||||||
print fmt.format ("memory", vm.memory)
|
print fmt.format ("memory", vm.memory)
|
||||||
if hasattr(vm, 'maxmem'):
|
if hasattr(vm, 'maxmem'):
|
||||||
print fmt.format ("maxmem", vm.maxmem)
|
print fmt.format ("maxmem", vm.maxmem)
|
||||||
print fmt.format ("MAC", "%s%s" % (vm.mac, " (auto)" if vm._mac is None else ""))
|
print fmt.format ("MAC", "%s%s" % (vm.mac, " (auto)" if vm._mac is None else ""))
|
||||||
|
|
||||||
if hasattr(vm, 'kernel'):
|
if hasattr(vm, 'kernel'):
|
||||||
if vm.uses_default_kernel:
|
if vm.uses_default_kernel:
|
||||||
print fmt.format ("kernel", "%s (default)" % vm.kernel)
|
print fmt.format ("kernel", "%s (default)" % vm.kernel)
|
||||||
else:
|
else:
|
||||||
print fmt.format ("kernel", vm.kernel)
|
print fmt.format ("kernel", vm.kernel)
|
||||||
|
|
||||||
if hasattr(vm, 'kernelopts'):
|
if hasattr(vm, 'kernelopts'):
|
||||||
if vm.uses_default_kernelopts:
|
if vm.uses_default_kernelopts:
|
||||||
print fmt.format ("kernelopts", "%s (default)" % vm.kernelopts)
|
print fmt.format ("kernelopts", "%s (default)" % vm.kernelopts)
|
||||||
@ -79,6 +81,18 @@ def do_list(vm):
|
|||||||
if hasattr(vm, 'default_user'):
|
if hasattr(vm, 'default_user'):
|
||||||
print fmt.format("default user", str(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))
|
||||||
|
|
||||||
|
if hasattr(vm, 'qrexec_timeout'):
|
||||||
|
print fmt.format("qrexec timeout", str(vm.qrexec_timeout))
|
||||||
|
|
||||||
|
if hasattr(vm, 'drive'):
|
||||||
|
print fmt.format("drive", str(vm.drive))
|
||||||
|
|
||||||
|
if hasattr(vm, 'timezone'):
|
||||||
|
print fmt.format("timezone", str(vm.timezone))
|
||||||
|
|
||||||
def set_label(vms, vm, args):
|
def set_label(vms, vm, args):
|
||||||
if len (args) != 1:
|
if len (args) != 1:
|
||||||
print >> sys.stderr, "Missing label name argument!"
|
print >> sys.stderr, "Missing label name argument!"
|
||||||
@ -274,6 +288,16 @@ def set_name(vms, vm, args):
|
|||||||
vm.set_name(args[0])
|
vm.set_name(args[0])
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def set_drive(vms, vm, args):
|
||||||
|
if len (args) != 1:
|
||||||
|
print >> sys.stderr, "Missing new drive content (file/device)!"
|
||||||
|
return False
|
||||||
|
|
||||||
|
if args[0] == '' or args[0].lower() == 'none':
|
||||||
|
vm.drive = None
|
||||||
|
else:
|
||||||
|
vm.drive = args[0]
|
||||||
|
return True
|
||||||
|
|
||||||
def set_debug(vms, vm, args):
|
def set_debug(vms, vm, args):
|
||||||
if len (args) != 1:
|
if len (args) != 1:
|
||||||
@ -304,6 +328,34 @@ def set_include_in_backups(vms, vm, args):
|
|||||||
vm.include_in_backups = bool(eval(args[0].capitalize()))
|
vm.include_in_backups = bool(eval(args[0].capitalize()))
|
||||||
return True
|
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_qrexec_timeout(vms, vm, args):
|
||||||
|
if len (args) != 1:
|
||||||
|
print >> sys.stderr, "Missing timeout value (seconds)!"
|
||||||
|
return False
|
||||||
|
|
||||||
|
vm.qrexec_timeout = int(args[0])
|
||||||
|
return True
|
||||||
|
|
||||||
|
def set_timezone(vms, vm, args):
|
||||||
|
if len (args) != 1:
|
||||||
|
print >> sys.stderr, "Missing value ('localtime' or timeoffset in seconds)!"
|
||||||
|
return False
|
||||||
|
|
||||||
|
if not args[0].isdigit() and args[0].lower() == 'localtime':
|
||||||
|
print >> sys.stderr, "Invalid timezone value!"
|
||||||
|
return False
|
||||||
|
|
||||||
|
vm.timezone = args[0]
|
||||||
|
return True
|
||||||
|
|
||||||
properties = {
|
properties = {
|
||||||
"include_in_backups": set_include_in_backups,
|
"include_in_backups": set_include_in_backups,
|
||||||
"pcidevs": set_pcidevs,
|
"pcidevs": set_pcidevs,
|
||||||
@ -316,9 +368,13 @@ properties = {
|
|||||||
"vcpus" : set_vcpus,
|
"vcpus" : set_vcpus,
|
||||||
"kernelopts": set_kernelopts,
|
"kernelopts": set_kernelopts,
|
||||||
"name": set_name,
|
"name": set_name,
|
||||||
|
"drive": set_drive,
|
||||||
"mac": set_mac,
|
"mac": set_mac,
|
||||||
"debug": set_debug,
|
"debug": set_debug,
|
||||||
"default_user": set_default_user,
|
"default_user": set_default_user,
|
||||||
|
"qrexec_installed": set_qrexec_installed,
|
||||||
|
"qrexec_timeout": set_qrexec_timeout,
|
||||||
|
"timezone": set_timezone,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -78,6 +78,23 @@ def main():
|
|||||||
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
|
||||||
exit(1)
|
exit(1)
|
||||||
|
|
||||||
|
if bool(options.drive_hd) + bool(options.drive_cdrom) + bool(options.drive) > 1:
|
||||||
|
print >> sys.stderr, "Only one of --drive, --cdrom, --hddisk can be specified"
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
if options.drive_hd:
|
||||||
|
options.drive = 'hd:' + options.drive_hd
|
||||||
|
|
||||||
|
if options.drive_cdrom:
|
||||||
|
options.drive = 'cdrom:' + options.drive_cdrom
|
||||||
|
|
||||||
|
if options.drive:
|
||||||
|
if hasattr(vm, 'drive'):
|
||||||
|
vm.drive = options.drive
|
||||||
|
else:
|
||||||
|
print >> sys.stderr, "This VM does not support attaching drives"
|
||||||
|
exit (1)
|
||||||
|
|
||||||
if options.custom_config:
|
if options.custom_config:
|
||||||
vm.conf_file = options.custom_config
|
vm.conf_file = options.custom_config
|
||||||
|
|
||||||
|
@ -64,7 +64,7 @@ def main():
|
|||||||
|
|
||||||
# Ignore retcode, try even if nm-online failed - user can setup network manually
|
# Ignore retcode, try even if nm-online failed - user can setup network manually
|
||||||
# on-online has timeout 30sec by default
|
# on-online has timeout 30sec by default
|
||||||
net_vm.run('user:nm-online -x', verbose=verbose, wait=True, ignore_stderr=True)
|
net_vm.run('DEFAULT:nm-online -x', verbose=verbose, wait=True, ignore_stderr=True)
|
||||||
|
|
||||||
# Sync clock
|
# Sync clock
|
||||||
if clock_vm.run('root:QUBESRPC qubes.SyncNtpClock dom0', verbose=verbose, wait=True, ignore_stderr=True) != 0:
|
if clock_vm.run('root:QUBESRPC qubes.SyncNtpClock dom0', verbose=verbose, wait=True, ignore_stderr=True) != 0:
|
||||||
@ -72,7 +72,7 @@ def main():
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
# Use the date format based on RFC2822 to avoid localisation issues
|
# Use the date format based on RFC2822 to avoid localisation issues
|
||||||
p = clock_vm.run('user:date -u -R', verbose=verbose, passio_popen=True, ignore_stderr=True)
|
p = clock_vm.run('DEFAULT:date -u -R', verbose=verbose, passio_popen=True, ignore_stderr=True)
|
||||||
date_out = p.stdout.read(100)
|
date_out = p.stdout.read(100)
|
||||||
date_out = date_out.strip()
|
date_out = date_out.strip()
|
||||||
if not re.match(r'^[A-Za-z]+[,] [0-9][0-9] [A-Za-z]+ [0-9][0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [+]0000$', date_out):
|
if not re.match(r'^[A-Za-z]+[,] [0-9][0-9] [A-Za-z]+ [0-9][0-9][0-9][0-9] [0-9][0-9]:[0-9][0-9]:[0-9][0-9] [+]0000$', date_out):
|
||||||
|
@ -228,8 +228,8 @@ void send_exit_code(int client_id, int status)
|
|||||||
s_hdr.len = sizeof status;
|
s_hdr.len = sizeof status;
|
||||||
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
|
||||||
write_all_vchan_ext(&status, sizeof(status));
|
write_all_vchan_ext(&status, sizeof(status));
|
||||||
fprintf(stderr, "send exit code for client_id %d pid %d\n",
|
fprintf(stderr, "send exit code %d for client_id %d pid %d\n",
|
||||||
client_id, client_info[client_id].pid);
|
status, client_id, client_info[client_id].pid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -239,8 +239,9 @@ void remove_process(int client_id, int status)
|
|||||||
int i;
|
int i;
|
||||||
if (!client_info[client_id].pid)
|
if (!client_info[client_id].pid)
|
||||||
return;
|
return;
|
||||||
fork_and_flush_stdin(client_info[client_id].stdin_fd,
|
if (client_info[client_id].stdin_fd >= 0)
|
||||||
&client_info[client_id].buffer);
|
fork_and_flush_stdin(client_info[client_id].stdin_fd,
|
||||||
|
&client_info[client_id].buffer);
|
||||||
#if 0
|
#if 0
|
||||||
// let's let it die by itself, possibly after it has received buffered stdin
|
// let's let it die by itself, possibly after it has received buffered stdin
|
||||||
kill(client_info[client_id].pid, SIGKILL);
|
kill(client_info[client_id].pid, SIGKILL);
|
||||||
@ -283,7 +284,7 @@ void handle_input(int client_id, int len)
|
|||||||
char buf[len];
|
char buf[len];
|
||||||
|
|
||||||
read_all_vchan_ext(buf, len);
|
read_all_vchan_ext(buf, len);
|
||||||
if (!client_info[client_id].pid)
|
if (!client_info[client_id].pid || client_info[client_id].stdin_fd == -1)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (len == 0) {
|
if (len == 0) {
|
||||||
@ -306,7 +307,10 @@ void handle_input(int client_id, int len)
|
|||||||
client_info[client_id].is_blocked = 1;
|
client_info[client_id].is_blocked = 1;
|
||||||
break;
|
break;
|
||||||
case WRITE_STDIN_ERROR:
|
case WRITE_STDIN_ERROR:
|
||||||
remove_process(client_id, 128);
|
// do not remove process, as it still can write data to stdout
|
||||||
|
close(client_info[client_id].stdin_fd);
|
||||||
|
client_info[client_id].stdin_fd = -1;
|
||||||
|
client_info[client_id].is_blocked = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unknown write_stdin?\n");
|
fprintf(stderr, "unknown write_stdin?\n");
|
||||||
@ -494,7 +498,11 @@ void flush_client_data_agent(int client_id)
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case WRITE_STDIN_ERROR:
|
case WRITE_STDIN_ERROR:
|
||||||
remove_process(client_id, 128);
|
// do not remove process, as it still can write data to stdout
|
||||||
|
info->is_blocked = 0;
|
||||||
|
close(info->stdin_fd);
|
||||||
|
info->stdin_fd = -1;
|
||||||
|
info->is_close_after_flush_needed = 0;
|
||||||
break;
|
break;
|
||||||
case WRITE_STDIN_BUFFERED:
|
case WRITE_STDIN_BUFFERED:
|
||||||
break;
|
break;
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <ioall.h>
|
#include <ioall.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "qrexec.h"
|
#include "qrexec.h"
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "glue.h"
|
#include "glue.h"
|
||||||
@ -106,12 +107,33 @@ void handle_input(int s)
|
|||||||
do_exit(1);
|
do_exit(1);
|
||||||
}
|
}
|
||||||
if (ret == 0) {
|
if (ret == 0) {
|
||||||
|
close(local_stdout_fd);
|
||||||
local_stdout_fd = -1;
|
local_stdout_fd = -1;
|
||||||
shutdown(s, SHUT_WR);
|
shutdown(s, SHUT_WR);
|
||||||
|
if (local_stdin_fd == -1) {
|
||||||
|
// if pipe in opposite direction already closed, no need to stay alive
|
||||||
|
do_exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (!write_all(s, buf, ret)) {
|
if (!write_all(s, buf, ret)) {
|
||||||
perror("write daemon");
|
if (errno == EPIPE) {
|
||||||
do_exit(1);
|
// daemon disconnected its end of socket, so no future data will be
|
||||||
|
// send there; there is no sense to read from child stdout
|
||||||
|
//
|
||||||
|
// since AF_UNIX socket is buffered it doesn't mean all data was
|
||||||
|
// received from the agent
|
||||||
|
close(local_stdout_fd);
|
||||||
|
local_stdout_fd = -1;
|
||||||
|
if (local_stdin_fd == -1) {
|
||||||
|
// since child does no longer accept data on its stdin, doesn't
|
||||||
|
// make sense to process the data from the daemon
|
||||||
|
//
|
||||||
|
// we don't know real exit VM process code (exiting here, before
|
||||||
|
// MSG_SERVER_TO_CLIENT_EXIT_CODE message)
|
||||||
|
do_exit(1);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
perror("write daemon");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,11 +158,20 @@ void handle_daemon_data(int s)
|
|||||||
|
|
||||||
switch (hdr.type) {
|
switch (hdr.type) {
|
||||||
case MSG_SERVER_TO_CLIENT_STDOUT:
|
case MSG_SERVER_TO_CLIENT_STDOUT:
|
||||||
if (hdr.len == 0)
|
if (local_stdin_fd == -1)
|
||||||
|
break;
|
||||||
|
if (hdr.len == 0) {
|
||||||
close(local_stdin_fd);
|
close(local_stdin_fd);
|
||||||
else if (!write_all(local_stdin_fd, buf, hdr.len)) {
|
local_stdin_fd = -1;
|
||||||
perror("write local stdout");
|
} else if (!write_all(local_stdin_fd, buf, hdr.len)) {
|
||||||
do_exit(1);
|
if (errno == EPIPE) {
|
||||||
|
// remote side have closed its stdin, handle data in oposite
|
||||||
|
// direction (if any) before exit
|
||||||
|
local_stdin_fd = -1;
|
||||||
|
} else {
|
||||||
|
perror("write local stdout");
|
||||||
|
do_exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case MSG_SERVER_TO_CLIENT_STDERR:
|
case MSG_SERVER_TO_CLIENT_STDERR:
|
||||||
|
@ -38,8 +38,10 @@ enum client_flags {
|
|||||||
CLIENT_INVALID = 0, // table slot not used
|
CLIENT_INVALID = 0, // table slot not used
|
||||||
CLIENT_CMDLINE = 1, // waiting for cmdline from client
|
CLIENT_CMDLINE = 1, // waiting for cmdline from client
|
||||||
CLIENT_DATA = 2, // waiting for data from client
|
CLIENT_DATA = 2, // waiting for data from client
|
||||||
CLIENT_DONT_READ = 4, // don't read from the client, the other side pipe is full, or EOF
|
CLIENT_DONT_READ = 4, // don't read from the client, the other side pipe is full, or EOF (additionally marked with CLIENT_EOF)
|
||||||
CLIENT_OUTQ_FULL = 8 // don't write to client, its stdin pipe is full
|
CLIENT_OUTQ_FULL = 8, // don't write to client, its stdin pipe is full
|
||||||
|
CLIENT_EOF = 16, // got EOF
|
||||||
|
CLIENT_EXITED = 32 // only send remaining data from client and remove from list
|
||||||
};
|
};
|
||||||
|
|
||||||
struct _client {
|
struct _client {
|
||||||
@ -57,6 +59,9 @@ struct _client clients[MAX_CLIENTS]; // data on all qrexec_client connections
|
|||||||
|
|
||||||
int max_client_fd = -1; // current max fd of all clients; so that we need not to scan all the "clients" table
|
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
|
int qrexec_daemon_unix_socket_fd; // /var/run/qubes/qrexec.xid descriptor
|
||||||
|
char *default_user = "user";
|
||||||
|
char default_user_keyword[] = "DEFAULT:";
|
||||||
|
#define default_user_keyword_len_without_colon (sizeof(default_user_keyword)-2)
|
||||||
|
|
||||||
void sigusr1_handler(int x)
|
void sigusr1_handler(int x)
|
||||||
{
|
{
|
||||||
@ -82,7 +87,31 @@ int create_qrexec_socket(int domid, char *domname)
|
|||||||
return get_server_socket(socket_address);
|
return get_server_socket(socket_address);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define MAX_STARTUP_TIME 60
|
#define MAX_STARTUP_TIME_DEFAULT 60
|
||||||
|
|
||||||
|
/* ask on qrexec connect timeout */
|
||||||
|
int ask_on_connect_timeout(int xid, int timeout)
|
||||||
|
{
|
||||||
|
char text[1024];
|
||||||
|
int ret;
|
||||||
|
snprintf(text, sizeof(text),
|
||||||
|
"kdialog --title 'Qrexec daemon' --warningyesno "
|
||||||
|
"'Timeout while trying connecting to qrexec agent (Xen domain ID: %d). Do you want to wait next %d seconds?'",
|
||||||
|
xid, timeout);
|
||||||
|
ret = system(text);
|
||||||
|
ret = WEXITSTATUS(ret);
|
||||||
|
// fprintf(stderr, "ret=%d\n", ret);
|
||||||
|
switch (ret) {
|
||||||
|
case 1: /* NO */
|
||||||
|
return 0;
|
||||||
|
case 0: /*YES */
|
||||||
|
return 1;
|
||||||
|
default:
|
||||||
|
// this can be the case at system startup (netvm), when Xorg isn't running yet
|
||||||
|
// so just don't give possibility to extend the timeout
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* do the preparatory tasks, needed before entering the main event loop */
|
/* do the preparatory tasks, needed before entering the main event loop */
|
||||||
void init(int xid)
|
void init(int xid)
|
||||||
@ -90,13 +119,23 @@ void init(int xid)
|
|||||||
char qrexec_error_log_name[256];
|
char qrexec_error_log_name[256];
|
||||||
int logfd;
|
int logfd;
|
||||||
int i;
|
int i;
|
||||||
|
pid_t pid;
|
||||||
|
int startup_timeout = MAX_STARTUP_TIME_DEFAULT;
|
||||||
|
char *startup_timeout_str = NULL;
|
||||||
|
|
||||||
if (xid <= 0) {
|
if (xid <= 0) {
|
||||||
fprintf(stderr, "domain id=0?\n");
|
fprintf(stderr, "domain id=0?\n");
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
startup_timeout_str = getenv("QREXEC_STARTUP_TIMEOUT");
|
||||||
|
if (startup_timeout_str) {
|
||||||
|
startup_timeout = atoi(startup_timeout_str);
|
||||||
|
if (startup_timeout == 0)
|
||||||
|
// invalid number
|
||||||
|
startup_timeout = MAX_STARTUP_TIME_DEFAULT;
|
||||||
|
}
|
||||||
signal(SIGUSR1, sigusr1_handler);
|
signal(SIGUSR1, sigusr1_handler);
|
||||||
switch (fork()) {
|
switch (pid=fork()) {
|
||||||
case -1:
|
case -1:
|
||||||
perror("fork");
|
perror("fork");
|
||||||
exit(1);
|
exit(1);
|
||||||
@ -104,11 +143,16 @@ void init(int xid)
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "Waiting for VM's qrexec agent.");
|
fprintf(stderr, "Waiting for VM's qrexec agent.");
|
||||||
for (i=0;i<MAX_STARTUP_TIME;i++) {
|
for (i=0;i<startup_timeout;i++) {
|
||||||
sleep(1);
|
sleep(1);
|
||||||
fprintf(stderr, ".");
|
fprintf(stderr, ".");
|
||||||
|
if (i==startup_timeout-1) {
|
||||||
|
if (ask_on_connect_timeout(xid, startup_timeout))
|
||||||
|
i=0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
fprintf(stderr, "Cannot connect to qrexec agent for %d seconds, giving up\n", MAX_STARTUP_TIME);
|
fprintf(stderr, "Cannot connect to qrexec agent for %d seconds, giving up\n", startup_timeout);
|
||||||
|
kill(pid, SIGTERM);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
close(0);
|
close(0);
|
||||||
@ -170,7 +214,7 @@ void terminate_client_and_flush_data(int fd)
|
|||||||
int i;
|
int i;
|
||||||
struct server_header s_hdr;
|
struct server_header s_hdr;
|
||||||
|
|
||||||
if (fork_and_flush_stdin(fd, &clients[fd].buffer))
|
if (!(clients[fd].state & CLIENT_EXITED) && fork_and_flush_stdin(fd, &clients[fd].buffer))
|
||||||
children_count++;
|
children_count++;
|
||||||
close(fd);
|
close(fd);
|
||||||
clients[fd].state = CLIENT_INVALID;
|
clients[fd].state = CLIENT_INVALID;
|
||||||
@ -186,17 +230,28 @@ void terminate_client_and_flush_data(int fd)
|
|||||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||||
}
|
}
|
||||||
|
|
||||||
void get_cmdline_body_from_client_and_pass_to_agent(int fd, struct server_header
|
int get_cmdline_body_from_client_and_pass_to_agent(int fd, struct server_header
|
||||||
*s_hdr)
|
*s_hdr)
|
||||||
{
|
{
|
||||||
int len = s_hdr->len;
|
int len = s_hdr->len;
|
||||||
char buf[len];
|
char buf[len];
|
||||||
|
int use_default_user = 0;
|
||||||
if (!read_all(fd, buf, len)) {
|
if (!read_all(fd, buf, len)) {
|
||||||
terminate_client_and_flush_data(fd);
|
terminate_client_and_flush_data(fd);
|
||||||
return;
|
return 0;
|
||||||
|
}
|
||||||
|
if (!strncmp(buf, default_user_keyword, default_user_keyword_len_without_colon+1)) {
|
||||||
|
use_default_user = 1;
|
||||||
|
s_hdr->len -= default_user_keyword_len_without_colon; // -1 because of colon
|
||||||
|
s_hdr->len += strlen(default_user);
|
||||||
}
|
}
|
||||||
write_all_vchan_ext(s_hdr, sizeof(*s_hdr));
|
write_all_vchan_ext(s_hdr, sizeof(*s_hdr));
|
||||||
write_all_vchan_ext(buf, len);
|
if (use_default_user) {
|
||||||
|
write_all_vchan_ext(default_user, strlen(default_user));
|
||||||
|
write_all_vchan_ext(buf+default_user_keyword_len_without_colon, len-default_user_keyword_len_without_colon);
|
||||||
|
} else
|
||||||
|
write_all_vchan_ext(buf, len);
|
||||||
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_cmdline_message_from_client(int fd)
|
void handle_cmdline_message_from_client(int fd)
|
||||||
@ -224,7 +279,10 @@ void handle_cmdline_message_from_client(int fd)
|
|||||||
|
|
||||||
s_hdr.client_id = fd;
|
s_hdr.client_id = fd;
|
||||||
s_hdr.len = hdr.len;
|
s_hdr.len = hdr.len;
|
||||||
get_cmdline_body_from_client_and_pass_to_agent(fd, &s_hdr);
|
if (!get_cmdline_body_from_client_and_pass_to_agent(fd, &s_hdr))
|
||||||
|
// client disconnected while sending cmdline, above call already
|
||||||
|
// cleaned up client info
|
||||||
|
return;
|
||||||
clients[fd].state = CLIENT_DATA;
|
clients[fd].state = CLIENT_DATA;
|
||||||
set_nonblock(fd); // so that we can detect full queue without blocking
|
set_nonblock(fd); // so that we can detect full queue without blocking
|
||||||
if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC)
|
if (hdr.type == MSG_CLIENT_TO_SERVER_JUST_EXEC)
|
||||||
@ -262,7 +320,10 @@ void handle_message_from_client(int fd)
|
|||||||
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
write_all_vchan_ext(&s_hdr, sizeof(s_hdr));
|
||||||
write_all_vchan_ext(buf, ret);
|
write_all_vchan_ext(buf, ret);
|
||||||
if (ret == 0) // EOF - so don't select() on this client
|
if (ret == 0) // EOF - so don't select() on this client
|
||||||
clients[fd].state |= CLIENT_DONT_READ;
|
clients[fd].state |= CLIENT_DONT_READ | CLIENT_EOF;
|
||||||
|
if (clients[fd].state & CLIENT_EXITED)
|
||||||
|
//client already exited and all data sent - cleanup now
|
||||||
|
terminate_client_and_flush_data(fd);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -278,7 +339,14 @@ void write_buffered_data_to_client(int client_id)
|
|||||||
clients[client_id].state &= ~CLIENT_OUTQ_FULL;
|
clients[client_id].state &= ~CLIENT_OUTQ_FULL;
|
||||||
break;
|
break;
|
||||||
case WRITE_STDIN_ERROR:
|
case WRITE_STDIN_ERROR:
|
||||||
terminate_client_and_flush_data(client_id);
|
// do not write to this fd anymore
|
||||||
|
clients[client_id].state |= CLIENT_EXITED;
|
||||||
|
if (clients[client_id].state & CLIENT_EOF)
|
||||||
|
terminate_client_and_flush_data(client_id);
|
||||||
|
else
|
||||||
|
// client will be removed when read returns 0 (EOF)
|
||||||
|
// clear CLIENT_OUTQ_FULL flag to no select on this fd anymore
|
||||||
|
clients[client_id].state &= ~CLIENT_OUTQ_FULL;
|
||||||
break;
|
break;
|
||||||
case WRITE_STDIN_BUFFERED: // no room for all data, don't clear CLIENT_OUTQ_FULL flag
|
case WRITE_STDIN_BUFFERED: // no room for all data, don't clear CLIENT_OUTQ_FULL flag
|
||||||
break;
|
break;
|
||||||
@ -301,6 +369,9 @@ void get_packet_data_from_agent_and_pass_to_client(int client_id, struct client_
|
|||||||
/* make both the header and data be consecutive in the buffer */
|
/* make both the header and data be consecutive in the buffer */
|
||||||
*(struct client_header *) buf = *hdr;
|
*(struct client_header *) buf = *hdr;
|
||||||
read_all_vchan_ext(buf + sizeof(*hdr), len);
|
read_all_vchan_ext(buf + sizeof(*hdr), len);
|
||||||
|
if (clients[client_id].state & CLIENT_EXITED)
|
||||||
|
// ignore data for no longer running client
|
||||||
|
return;
|
||||||
|
|
||||||
switch (write_stdin
|
switch (write_stdin
|
||||||
(client_id, client_id, buf, len + sizeof(*hdr),
|
(client_id, client_id, buf, len + sizeof(*hdr),
|
||||||
@ -311,7 +382,11 @@ void get_packet_data_from_agent_and_pass_to_client(int client_id, struct client_
|
|||||||
clients[client_id].state |= CLIENT_OUTQ_FULL;
|
clients[client_id].state |= CLIENT_OUTQ_FULL;
|
||||||
break;
|
break;
|
||||||
case WRITE_STDIN_ERROR:
|
case WRITE_STDIN_ERROR:
|
||||||
terminate_client_and_flush_data(client_id);
|
// do not write to this fd anymore
|
||||||
|
clients[client_id].state |= CLIENT_EXITED;
|
||||||
|
// if already got EOF, remove client
|
||||||
|
if (clients[client_id].state & CLIENT_EOF)
|
||||||
|
terminate_client_and_flush_data(client_id);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fprintf(stderr, "unknown write_stdin?\n");
|
fprintf(stderr, "unknown write_stdin?\n");
|
||||||
@ -557,10 +632,12 @@ int main(int argc, char **argv)
|
|||||||
int max;
|
int max;
|
||||||
sigset_t chld_set;
|
sigset_t chld_set;
|
||||||
|
|
||||||
if (argc != 2) {
|
if (argc != 2 && argc != 3) {
|
||||||
fprintf(stderr, "usage: %s domainid\n", argv[0]);
|
fprintf(stderr, "usage: %s domainid [default user]\n", argv[0]);
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
if (argc == 3)
|
||||||
|
default_user = argv[2];
|
||||||
init(atoi(argv[1]));
|
init(atoi(argv[1]));
|
||||||
sigemptyset(&chld_set);
|
sigemptyset(&chld_set);
|
||||||
sigaddset(&chld_set, SIGCHLD);
|
sigaddset(&chld_set, SIGCHLD);
|
||||||
|
@ -5,6 +5,7 @@ import os.path
|
|||||||
import subprocess
|
import subprocess
|
||||||
import xen.lowlevel.xl
|
import xen.lowlevel.xl
|
||||||
import qubes.guihelpers
|
import qubes.guihelpers
|
||||||
|
from optparse import OptionParser
|
||||||
import fcntl
|
import fcntl
|
||||||
|
|
||||||
POLICY_FILE_DIR="/etc/qubes_rpc/policy"
|
POLICY_FILE_DIR="/etc/qubes_rpc/policy"
|
||||||
@ -126,10 +127,18 @@ def policy_editor(domain, target, exec_index):
|
|||||||
subprocess.call(["/usr/bin/zenity", "--info", "--text", text])
|
subprocess.call(["/usr/bin/zenity", "--info", "--text", text])
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
domain=sys.argv[1]
|
usage = "usage: %prog [options] <src-domain> <target-domain> <service> <process-ident>"
|
||||||
target=sys.argv[2]
|
parser = OptionParser (usage)
|
||||||
exec_index=sys.argv[3]
|
parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False,
|
||||||
process_ident=sys.argv[4]
|
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=args[0]
|
||||||
|
target=args[1]
|
||||||
|
exec_index=args[2]
|
||||||
|
process_ident=args[3]
|
||||||
|
|
||||||
policy_list=read_policy_file(exec_index)
|
policy_list=read_policy_file(exec_index)
|
||||||
if policy_list==None:
|
if policy_list==None:
|
||||||
@ -140,6 +149,9 @@ def main():
|
|||||||
|
|
||||||
policy_dict=find_policy(policy_list, domain, target)
|
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":
|
if policy_dict["action"] == "ask":
|
||||||
user_choice = confirm_execution(domain, target, exec_index)
|
user_choice = confirm_execution(domain, target, exec_index)
|
||||||
if user_choice == UserChoice.ALWAYS_ALLOW:
|
if user_choice == UserChoice.ALWAYS_ALLOW:
|
||||||
@ -150,13 +162,19 @@ def main():
|
|||||||
else:
|
else:
|
||||||
policy_dict["action"] = "deny"
|
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["action"] == "allow":
|
||||||
if policy_dict.has_key("action.target"):
|
if policy_dict.has_key("action.target"):
|
||||||
target=policy_dict["action.target"]
|
target=policy_dict["action.target"]
|
||||||
if policy_dict.has_key("action.user"):
|
if policy_dict.has_key("action.user"):
|
||||||
user=policy_dict["action.user"]
|
user=policy_dict["action.user"]
|
||||||
else:
|
else:
|
||||||
user="user"
|
user="DEFAULT"
|
||||||
do_execute(domain, target, user, exec_index, process_ident)
|
do_execute(domain, target, user, exec_index, process_ident)
|
||||||
|
|
||||||
print >> sys.stderr, "Rpc denied:", domain, target, exec_index
|
print >> sys.stderr, "Rpc denied:", domain, target, exec_index
|
||||||
|
@ -109,18 +109,6 @@ int write_stdin(int fd, int client_id, char *data, int len,
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_nonblock(int fd)
|
|
||||||
{
|
|
||||||
int fl = fcntl(fd, F_GETFL, 0);
|
|
||||||
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
void set_block(int fd)
|
|
||||||
{
|
|
||||||
int fl = fcntl(fd, F_GETFL, 0);
|
|
||||||
fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Data feed process has exited, so we need to clear all control structures for
|
Data feed process has exited, so we need to clear all control structures for
|
||||||
the client. However, if we have buffered data for the client (which is rare btw),
|
the client. However, if we have buffered data for the client (which is rare btw),
|
||||||
|
@ -32,6 +32,17 @@ void perror_wrapper(char * msg)
|
|||||||
errno=prev;
|
errno=prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void set_nonblock(int fd)
|
||||||
|
{
|
||||||
|
int fl = fcntl(fd, F_GETFL, 0);
|
||||||
|
fcntl(fd, F_SETFL, fl | O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_block(int fd)
|
||||||
|
{
|
||||||
|
int fl = fcntl(fd, F_GETFL, 0);
|
||||||
|
fcntl(fd, F_SETFL, fl & ~O_NONBLOCK);
|
||||||
|
}
|
||||||
|
|
||||||
int write_all(int fd, void *buf, int size)
|
int write_all(int fd, void *buf, int size)
|
||||||
{
|
{
|
||||||
@ -42,7 +53,6 @@ int write_all(int fd, void *buf, int size)
|
|||||||
if (ret == -1 && errno == EINTR)
|
if (ret == -1 && errno == EINTR)
|
||||||
continue;
|
continue;
|
||||||
if (ret <= 0) {
|
if (ret <= 0) {
|
||||||
perror_wrapper("write");
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
written += ret;
|
written += ret;
|
||||||
@ -65,9 +75,14 @@ int read_all(int fd, void *buf, int size)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
perror_wrapper("read");
|
if (errno != EAGAIN)
|
||||||
|
perror_wrapper("read");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (got_read == 0) {
|
||||||
|
// force blocking operation on further reads
|
||||||
|
set_block(fd);
|
||||||
|
}
|
||||||
got_read += ret;
|
got_read += ret;
|
||||||
}
|
}
|
||||||
// fprintf(stderr, "read %d bytes\n", size);
|
// fprintf(stderr, "read %d bytes\n", size);
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
int write_all(int fd, void *buf, int size);
|
int write_all(int fd, void *buf, int size);
|
||||||
int read_all(int fd, void *buf, int size);
|
int read_all(int fd, void *buf, int size);
|
||||||
int copy_fd_all(int fdout, int fdin);
|
int copy_fd_all(int fdout, int fdin);
|
||||||
|
void set_nonblock(int fd);
|
||||||
|
void set_block(int fd);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <ioall.h>
|
#include <ioall.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#include <errno.h>
|
||||||
#include <gui-fatal.h>
|
#include <gui-fatal.h>
|
||||||
#include "filecopy.h"
|
#include "filecopy.h"
|
||||||
#include "crc32.h"
|
#include "crc32.h"
|
||||||
@ -49,6 +50,37 @@ void do_notify_progress(long long total, int flag)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void wait_for_result()
|
||||||
|
{
|
||||||
|
struct result_header hdr;
|
||||||
|
|
||||||
|
if (!read_all(0, &hdr, sizeof(hdr))) {
|
||||||
|
if (errno == EAGAIN) {
|
||||||
|
// no result sent and stdin still open
|
||||||
|
return;
|
||||||
|
} else {
|
||||||
|
// other read error or EOF
|
||||||
|
exit(1); // hopefully remote has produced error message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hdr.error_code != 0) {
|
||||||
|
switch (hdr.error_code) {
|
||||||
|
case EEXIST:
|
||||||
|
gui_fatal("File copy: not overwriting existing file. Clean incoming dir, and retry copy");
|
||||||
|
break;
|
||||||
|
case EINVAL:
|
||||||
|
gui_fatal("File copy: Corrupted data from packer");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
gui_fatal("File copy: %s",
|
||||||
|
strerror(hdr.error_code));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (hdr.crc32 != crc32_sum) {
|
||||||
|
gui_fatal("File transfer failed: checksum mismatch");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void notify_progress(int size, int flag)
|
void notify_progress(int size, int flag)
|
||||||
{
|
{
|
||||||
static long long total = 0;
|
static long long total = 0;
|
||||||
@ -56,6 +88,11 @@ void notify_progress(int size, int flag)
|
|||||||
total += size;
|
total += size;
|
||||||
if (total > prev_total + PROGRESS_NOTIFY_DELTA
|
if (total > prev_total + PROGRESS_NOTIFY_DELTA
|
||||||
|| (flag != PROGRESS_FLAG_NORMAL)) {
|
|| (flag != PROGRESS_FLAG_NORMAL)) {
|
||||||
|
// check for possible error from qfile-unpacker; if error occured,
|
||||||
|
// exit() will be called, so don't bother with current state
|
||||||
|
// (notify_progress can be called as callback from copy_file())
|
||||||
|
if (flag == PROGRESS_FLAG_NORMAL)
|
||||||
|
wait_for_result();
|
||||||
do_notify_progress(total, flag);
|
do_notify_progress(total, flag);
|
||||||
prev_total = total;
|
prev_total = total;
|
||||||
}
|
}
|
||||||
@ -64,8 +101,11 @@ void notify_progress(int size, int flag)
|
|||||||
void write_headers(struct file_header *hdr, char *filename)
|
void write_headers(struct file_header *hdr, char *filename)
|
||||||
{
|
{
|
||||||
if (!write_all_with_crc(1, hdr, sizeof(*hdr))
|
if (!write_all_with_crc(1, hdr, sizeof(*hdr))
|
||||||
|| !write_all_with_crc(1, filename, hdr->namelen))
|
|| !write_all_with_crc(1, filename, hdr->namelen)) {
|
||||||
|
set_block(0);
|
||||||
|
wait_for_result();
|
||||||
exit(1);
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int single_file_processor(char *filename, struct stat *st)
|
int single_file_processor(char *filename, struct stat *st)
|
||||||
@ -89,13 +129,15 @@ int single_file_processor(char *filename, struct stat *st)
|
|||||||
hdr.filelen = st->st_size;
|
hdr.filelen = st->st_size;
|
||||||
write_headers(&hdr, filename);
|
write_headers(&hdr, filename);
|
||||||
ret = copy_file(1, fd, hdr.filelen, &crc32_sum);
|
ret = copy_file(1, fd, hdr.filelen, &crc32_sum);
|
||||||
// if COPY_FILE_WRITE_ERROR, hopefully remote will produce a message
|
|
||||||
if (ret != COPY_FILE_OK) {
|
if (ret != COPY_FILE_OK) {
|
||||||
if (ret != COPY_FILE_WRITE_ERROR)
|
if (ret != COPY_FILE_WRITE_ERROR)
|
||||||
gui_fatal("Copying file %s: %s", filename,
|
gui_fatal("Copying file %s: %s", filename,
|
||||||
copy_file_status_to_str(ret));
|
copy_file_status_to_str(ret));
|
||||||
else
|
else {
|
||||||
|
set_block(0);
|
||||||
|
wait_for_result();
|
||||||
exit(1);
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
}
|
}
|
||||||
@ -109,9 +151,14 @@ int single_file_processor(char *filename, struct stat *st)
|
|||||||
gui_fatal("readlink %s", filename);
|
gui_fatal("readlink %s", filename);
|
||||||
hdr.filelen = st->st_size + 1;
|
hdr.filelen = st->st_size + 1;
|
||||||
write_headers(&hdr, filename);
|
write_headers(&hdr, filename);
|
||||||
if (!write_all_with_crc(1, name, st->st_size + 1))
|
if (!write_all_with_crc(1, name, st->st_size + 1)) {
|
||||||
|
set_block(0);
|
||||||
|
wait_for_result();
|
||||||
exit(1);
|
exit(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
// check for possible error from qfile-unpacker
|
||||||
|
wait_for_result();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +194,6 @@ int do_fs_walk(char *file)
|
|||||||
|
|
||||||
void notify_end_and_wait_for_result()
|
void notify_end_and_wait_for_result()
|
||||||
{
|
{
|
||||||
struct result_header hdr;
|
|
||||||
struct file_header end_hdr;
|
struct file_header end_hdr;
|
||||||
|
|
||||||
/* nofity end of transfer */
|
/* nofity end of transfer */
|
||||||
@ -156,17 +202,8 @@ void notify_end_and_wait_for_result()
|
|||||||
end_hdr.filelen = 0;
|
end_hdr.filelen = 0;
|
||||||
write_all_with_crc(1, &end_hdr, sizeof(end_hdr));
|
write_all_with_crc(1, &end_hdr, sizeof(end_hdr));
|
||||||
|
|
||||||
/* wait for result */
|
set_block(0);
|
||||||
if (!read_all(0, &hdr, sizeof(hdr))) {
|
wait_for_result();
|
||||||
exit(1); // hopefully remote has produced error message
|
|
||||||
}
|
|
||||||
if (hdr.error_code != 0) {
|
|
||||||
gui_fatal("Error writing files: %s",
|
|
||||||
strerror(hdr.error_code));
|
|
||||||
}
|
|
||||||
if (hdr.crc32 != crc32_sum) {
|
|
||||||
gui_fatal("File transfer failed: checksum mismatch");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
char *get_abs_path(char *cwd, char *pathname)
|
char *get_abs_path(char *cwd, char *pathname)
|
||||||
@ -186,6 +223,8 @@ int main(int argc, char **argv)
|
|||||||
char *sep;
|
char *sep;
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
// this will allow checking for possible feedback packet in the middle of transfer
|
||||||
|
set_nonblock(0);
|
||||||
notify_progress(0, PROGRESS_FLAG_INIT);
|
notify_progress(0, PROGRESS_FLAG_INIT);
|
||||||
crc32_sum = 0;
|
crc32_sum = 0;
|
||||||
cwd = getcwd(NULL, 0);
|
cwd = getcwd(NULL, 0);
|
||||||
|
@ -29,32 +29,14 @@ int prepare_creds_return_uid(char *username)
|
|||||||
return pwd->pw_uid;
|
return pwd->pw_uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
void wait_for_child(int statusfd)
|
extern int do_unpack(void);
|
||||||
{
|
|
||||||
int status;
|
|
||||||
if (read(statusfd, &status, sizeof status)!=sizeof status)
|
|
||||||
gui_fatal("File copy error: Internal error reading status from unpacker");
|
|
||||||
errno = status;
|
|
||||||
switch (status) {
|
|
||||||
case LEGAL_EOF: break;
|
|
||||||
case 0: gui_fatal("File copy: Connection terminated unexpectedly"); break;
|
|
||||||
case EINVAL: gui_fatal("File copy: Corrupted data from packer"); break;
|
|
||||||
case EEXIST: gui_fatal("File copy: not overwriting existing file. Clean ~/incoming, and retry copy"); break;
|
|
||||||
default: gui_fatal("File copy");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
extern void do_unpack(int);
|
|
||||||
|
|
||||||
int main(int argc, char ** argv)
|
int main(int argc, char ** argv)
|
||||||
{
|
{
|
||||||
char *incoming_dir;
|
char *incoming_dir;
|
||||||
int pipefds[2];
|
|
||||||
int uid;
|
int uid;
|
||||||
char *remote_domain;
|
char *remote_domain;
|
||||||
|
|
||||||
pipe(pipefds);
|
|
||||||
|
|
||||||
uid = prepare_creds_return_uid("user");
|
uid = prepare_creds_return_uid("user");
|
||||||
|
|
||||||
remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
|
remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
|
||||||
@ -67,23 +49,8 @@ int main(int argc, char ** argv)
|
|||||||
mkdir(incoming_dir, 0700);
|
mkdir(incoming_dir, 0700);
|
||||||
if (chdir(incoming_dir))
|
if (chdir(incoming_dir))
|
||||||
gui_fatal("Error chdir to %s", incoming_dir);
|
gui_fatal("Error chdir to %s", incoming_dir);
|
||||||
switch (fork()) {
|
if (chroot(incoming_dir)) //impossible
|
||||||
case -1:
|
gui_fatal("Error chroot to %s", incoming_dir);
|
||||||
perror("fork");
|
|
||||||
exit(1);
|
|
||||||
case 0:
|
|
||||||
if (chroot(incoming_dir)) //impossible
|
|
||||||
gui_fatal("Error chroot to %s", incoming_dir);
|
|
||||||
setuid(uid);
|
|
||||||
close(pipefds[0]);
|
|
||||||
do_unpack(pipefds[1]);
|
|
||||||
exit(0);
|
|
||||||
default:;
|
|
||||||
}
|
|
||||||
|
|
||||||
setuid(uid);
|
setuid(uid);
|
||||||
close(pipefds[1]);
|
return do_unpack();
|
||||||
wait_for_child(pipefds[0]);
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
@ -3,4 +3,4 @@
|
|||||||
|
|
||||||
## Please use a single # to start your custom comments
|
## Please use a single # to start your custom comments
|
||||||
|
|
||||||
$anyvm $anyvm ask,user=root
|
$anyvm $anyvm ask
|
||||||
|
@ -35,14 +35,24 @@ int read_all_with_crc(int fd, void *buf, int size) {
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
int global_status_fd;
|
void send_status_and_crc(int code) {
|
||||||
void do_exit(int code)
|
struct result_header hdr;
|
||||||
{
|
int saved_errno;
|
||||||
int codebuf = code;
|
|
||||||
write(global_status_fd, &codebuf, sizeof codebuf);
|
saved_errno = errno;
|
||||||
exit(0);
|
hdr.error_code = code;
|
||||||
|
hdr.crc32 = crc32_sum;
|
||||||
|
if (!write_all(1, &hdr, sizeof(hdr)))
|
||||||
|
perror("write status");
|
||||||
|
errno = saved_errno;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void do_exit(int code)
|
||||||
|
{
|
||||||
|
close(0);
|
||||||
|
send_status_and_crc(code);
|
||||||
|
exit(code);
|
||||||
|
}
|
||||||
|
|
||||||
void fix_times_and_perms(struct file_header *untrusted_hdr,
|
void fix_times_and_perms(struct file_header *untrusted_hdr,
|
||||||
char *untrusted_name)
|
char *untrusted_name)
|
||||||
@ -130,20 +140,8 @@ void process_one_file(struct file_header *untrusted_hdr)
|
|||||||
do_exit(EINVAL);
|
do_exit(EINVAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void send_status_and_crc() {
|
int do_unpack()
|
||||||
struct result_header hdr;
|
|
||||||
int saved_errno;
|
|
||||||
|
|
||||||
saved_errno = errno;
|
|
||||||
hdr.error_code = errno;
|
|
||||||
hdr.crc32 = crc32_sum;
|
|
||||||
write_all(1, &hdr, sizeof(hdr));
|
|
||||||
errno = saved_errno;
|
|
||||||
}
|
|
||||||
|
|
||||||
void do_unpack(int fd)
|
|
||||||
{
|
{
|
||||||
global_status_fd = fd;
|
|
||||||
struct file_header untrusted_hdr;
|
struct file_header untrusted_hdr;
|
||||||
/* initialize checksum */
|
/* initialize checksum */
|
||||||
crc32_sum = 0;
|
crc32_sum = 0;
|
||||||
@ -158,9 +156,6 @@ void do_unpack(int fd)
|
|||||||
if (files_limit && total_files > files_limit)
|
if (files_limit && total_files > files_limit)
|
||||||
do_exit(EDQUOT);
|
do_exit(EDQUOT);
|
||||||
}
|
}
|
||||||
send_status_and_crc();
|
send_status_and_crc(errno);
|
||||||
if (errno)
|
return errno;
|
||||||
do_exit(errno);
|
|
||||||
else
|
|
||||||
do_exit(LEGAL_EOF);
|
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,7 @@ BuildRequires: xen-devel
|
|||||||
Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0
|
Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0
|
||||||
Conflicts: qubes-gui-dom0 < 1.1.13
|
Conflicts: qubes-gui-dom0 < 1.1.13
|
||||||
Requires: xen >= 4.1.0-2
|
Requires: xen >= 4.1.0-2
|
||||||
|
Requires: xen-hvm
|
||||||
Requires: createrepo
|
Requires: createrepo
|
||||||
Requires: gnome-packagekit
|
Requires: gnome-packagekit
|
||||||
Requires: cronie
|
Requires: cronie
|
||||||
@ -115,6 +116,7 @@ cp qubes_rpc/qubes-receive-updates $RPM_BUILD_ROOT/usr/lib/qubes/
|
|||||||
cp ../misc/block_add_change $RPM_BUILD_ROOT/usr/lib/qubes/
|
cp ../misc/block_add_change $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||||
cp ../misc/block_remove $RPM_BUILD_ROOT/usr/lib/qubes/
|
cp ../misc/block_remove $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||||
cp ../misc/block_cleanup $RPM_BUILD_ROOT/usr/lib/qubes/
|
cp ../misc/block_cleanup $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||||
|
cp aux-tools/block_cleaner_daemon.py $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||||
cp aux-tools/fix_dir_perms.sh $RPM_BUILD_ROOT/usr/lib/qubes/
|
cp aux-tools/fix_dir_perms.sh $RPM_BUILD_ROOT/usr/lib/qubes/
|
||||||
|
|
||||||
mkdir -p $RPM_BUILD_ROOT/etc/qubes_rpc/policy
|
mkdir -p $RPM_BUILD_ROOT/etc/qubes_rpc/policy
|
||||||
@ -162,7 +164,9 @@ cp misc/qubes-servicevm.directory.template $RPM_BUILD_ROOT/usr/share/qubes/
|
|||||||
cp misc/qubes-dispvm.directory $RPM_BUILD_ROOT/usr/share/qubes/
|
cp misc/qubes-dispvm.directory $RPM_BUILD_ROOT/usr/share/qubes/
|
||||||
cp misc/qubes-dispvm-firefox.desktop $RPM_BUILD_ROOT/usr/share/qubes/
|
cp misc/qubes-dispvm-firefox.desktop $RPM_BUILD_ROOT/usr/share/qubes/
|
||||||
cp misc/qubes-appmenu-select.desktop $RPM_BUILD_ROOT/usr/share/qubes/
|
cp misc/qubes-appmenu-select.desktop $RPM_BUILD_ROOT/usr/share/qubes/
|
||||||
|
cp misc/qubes-start.desktop $RPM_BUILD_ROOT/usr/share/qubes/
|
||||||
cp misc/vm-template.conf $RPM_BUILD_ROOT/usr/share/qubes/
|
cp misc/vm-template.conf $RPM_BUILD_ROOT/usr/share/qubes/
|
||||||
|
cp misc/vm-template-hvm.conf $RPM_BUILD_ROOT/usr/share/qubes/
|
||||||
|
|
||||||
mkdir -p $RPM_BUILD_ROOT/usr/bin
|
mkdir -p $RPM_BUILD_ROOT/usr/bin
|
||||||
cp ../network/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes
|
cp ../network/qubes_setup_dnat_to_ns $RPM_BUILD_ROOT/usr/lib/qubes
|
||||||
@ -361,6 +365,7 @@ fi
|
|||||||
/usr/lib/qubes/block_add_change
|
/usr/lib/qubes/block_add_change
|
||||||
/usr/lib/qubes/block_remove
|
/usr/lib/qubes/block_remove
|
||||||
/usr/lib/qubes/block_cleanup
|
/usr/lib/qubes/block_cleanup
|
||||||
|
/usr/lib/qubes/block_cleaner_daemon.py*
|
||||||
/usr/lib/qubes/fix_dir_perms.sh
|
/usr/lib/qubes/fix_dir_perms.sh
|
||||||
%attr(4750,root,qubes) /usr/lib/qubes/qfile-dom0-unpacker
|
%attr(4750,root,qubes) /usr/lib/qubes/qfile-dom0-unpacker
|
||||||
%attr(770,root,qubes) %dir /var/lib/qubes
|
%attr(770,root,qubes) %dir /var/lib/qubes
|
||||||
@ -378,7 +383,9 @@ fi
|
|||||||
/usr/share/qubes/qubes-dispvm.directory
|
/usr/share/qubes/qubes-dispvm.directory
|
||||||
/usr/share/qubes/qubes-dispvm-firefox.desktop
|
/usr/share/qubes/qubes-dispvm-firefox.desktop
|
||||||
/usr/share/qubes/qubes-appmenu-select.desktop
|
/usr/share/qubes/qubes-appmenu-select.desktop
|
||||||
|
/usr/share/qubes/qubes-start.desktop
|
||||||
/usr/share/qubes/vm-template.conf
|
/usr/share/qubes/vm-template.conf
|
||||||
|
/usr/share/qubes/vm-template-hvm.conf
|
||||||
/usr/lib/qubes/qubes_setup_dnat_to_ns
|
/usr/lib/qubes/qubes_setup_dnat_to_ns
|
||||||
/usr/lib/qubes/qubes_fix_nm_conf.sh
|
/usr/lib/qubes/qubes_fix_nm_conf.sh
|
||||||
/etc/dhclient.d/qubes_setup_dnat_to_ns.sh
|
/etc/dhclient.d/qubes_setup_dnat_to_ns.sh
|
||||||
|
@ -405,7 +405,7 @@ rm -rf $RPM_BUILD_ROOT
|
|||||||
/usr/lib/qubes/meminfo-writer
|
/usr/lib/qubes/meminfo-writer
|
||||||
/usr/lib/qubes/network-manager-prepare-conf-dir
|
/usr/lib/qubes/network-manager-prepare-conf-dir
|
||||||
/usr/lib/qubes/qfile-agent
|
/usr/lib/qubes/qfile-agent
|
||||||
/usr/lib/qubes/qfile-unpacker
|
%attr(4755,root,root) /usr/lib/qubes/qfile-unpacker
|
||||||
/usr/lib/qubes/qopen-in-vm
|
/usr/lib/qubes/qopen-in-vm
|
||||||
/usr/lib/qubes/qrexec_agent
|
/usr/lib/qubes/qrexec_agent
|
||||||
/usr/lib/qubes/qrexec_client_vm
|
/usr/lib/qubes/qrexec_client_vm
|
||||||
|
36
vchan/Makefile.stubdom
Normal file
36
vchan/Makefile.stubdom
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#
|
||||||
|
# The Qubes OS Project, http://www.qubes-os.org
|
||||||
|
#
|
||||||
|
# Copyright (C) 2012 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.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
# Assume it is placed as xen-root/tools/vchan
|
||||||
|
|
||||||
|
XEN_ROOT = ../..
|
||||||
|
include $(XEN_ROOT)/tools/Rules.mk
|
||||||
|
|
||||||
|
CFLAGS+=-Wall -I$(XEN_ROOT)/tools/libxc -DCONFIG_STUBDOM
|
||||||
|
all: libvchan.a
|
||||||
|
|
||||||
|
libvchan.a: init.o io.o
|
||||||
|
$(AR) rc $@ $^
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o *so *~ client server node node-select
|
||||||
|
|
||||||
|
|
229
vchan/init.c
229
vchan/init.c
@ -19,41 +19,103 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef WINNT
|
||||||
|
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/unistd.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <sys/mman.h>
|
#include <sys/mman.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/ioctl.h>
|
#include <sys/ioctl.h>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <xs.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
#ifndef CONFIG_STUBDOM
|
||||||
|
#include "../u2mfn/u2mfnlib.h"
|
||||||
|
#else
|
||||||
|
#include <mm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <xs.h>
|
||||||
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include "libvchan.h"
|
#include "libvchan.h"
|
||||||
#include "../u2mfn/u2mfnlib.h"
|
|
||||||
|
|
||||||
|
static int fill_ctrl(struct libvchan *ctrl, struct vchan_interface *ring, int ring_ref)
|
||||||
|
{
|
||||||
|
if (!ctrl || !ring)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
ctrl->ring = ring;
|
||||||
|
ctrl->ring_ref = ring_ref;
|
||||||
|
|
||||||
|
ring->cons_in = ring->prod_in = ring->cons_out = ring->prod_out =
|
||||||
|
0;
|
||||||
|
ring->server_closed = ring->client_closed = 0;
|
||||||
|
ring->debug = 0xaabbccdd;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef QREXEC_RING_V2
|
||||||
|
static int ring_init(struct libvchan *ctrl)
|
||||||
|
{
|
||||||
|
struct gntmem_handle* h;
|
||||||
|
grant_ref_t grants[1];
|
||||||
|
int result;
|
||||||
|
struct vchan_interface *ring;
|
||||||
|
|
||||||
|
h = gntmem_open();
|
||||||
|
if (h == INVALID_HANDLE_VALUE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
gntmem_set_local_quota(h, 1);
|
||||||
|
gntmem_set_global_quota(h, 1);
|
||||||
|
|
||||||
|
memset(grants, 0, sizeof(grants));
|
||||||
|
ring = gntmem_grant_pages_to_domain(h, 0, 1, grants);
|
||||||
|
if (!ring) {
|
||||||
|
gntmem_close(h);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return fill_ctrl(ctrl, ring, grants[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
static int ring_init(struct libvchan *ctrl)
|
static int ring_init(struct libvchan *ctrl)
|
||||||
{
|
{
|
||||||
int u2mfn = open("/proc/u2mfn", O_RDONLY);
|
|
||||||
int mfn;
|
int mfn;
|
||||||
struct vchan_interface *ring;
|
struct vchan_interface *ring;
|
||||||
|
#ifdef CONFIG_STUBDOM
|
||||||
|
ring = (struct vchan_interface *) memalign(XC_PAGE_SIZE, sizeof(*ring));
|
||||||
|
|
||||||
|
if (!ring)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
|
||||||
|
mfn = virtual_to_mfn(ring);
|
||||||
|
#else
|
||||||
ring = (struct vchan_interface *) u2mfn_alloc_kpage ();
|
ring = (struct vchan_interface *) u2mfn_alloc_kpage ();
|
||||||
|
|
||||||
if (ring == MAP_FAILED)
|
if (ring == MAP_FAILED)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
ctrl->ring = ring;
|
|
||||||
if (u2mfn_get_last_mfn (&mfn) < 0)
|
if (u2mfn_get_last_mfn (&mfn) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
ctrl->ring_ref = mfn;
|
return fill_ctrl(ctrl, ring, mfn);
|
||||||
close(u2mfn);
|
|
||||||
ring->cons_in = ring->prod_in = ring->cons_out = ring->prod_out =
|
|
||||||
0;
|
|
||||||
ring->server_closed = ring->client_closed = 0;
|
|
||||||
ring->debug = 0xaabbccdd;
|
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
creates event channel;
|
creates event channel;
|
||||||
creates "ring-ref" and "event-channel" xenstore entries;
|
creates "ring-ref" and "event-channel" xenstore entries;
|
||||||
@ -66,12 +128,16 @@ static int server_interface_init(struct libvchan *ctrl, int devno)
|
|||||||
char buf[64];
|
char buf[64];
|
||||||
char ref[16];
|
char ref[16];
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
#ifdef XENCTRL_HAS_XC_INTERFACE
|
||||||
xc_evtchn *evfd;
|
xc_evtchn *evfd;
|
||||||
#else
|
#else
|
||||||
int evfd;
|
EVTCHN evfd;
|
||||||
#endif
|
#endif
|
||||||
evtchn_port_or_error_t port;
|
evtchn_port_or_error_t port;
|
||||||
|
#ifdef WINNT
|
||||||
xs = xs_domain_open();
|
xs = xs_domain_open();
|
||||||
|
#else
|
||||||
|
xs = xs_daemon_open();
|
||||||
|
#endif
|
||||||
if (!xs) {
|
if (!xs) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
@ -90,6 +156,14 @@ static int server_interface_init(struct libvchan *ctrl, int devno)
|
|||||||
if (port < 0)
|
if (port < 0)
|
||||||
goto fail2;
|
goto fail2;
|
||||||
ctrl->evport = port;
|
ctrl->evport = port;
|
||||||
|
ctrl->devno = devno;
|
||||||
|
|
||||||
|
#ifdef QREXEC_RING_V2
|
||||||
|
snprintf(buf, sizeof buf, "device/vchan/%d/version", devno);
|
||||||
|
if (!xs_write(xs, 0, buf, "2", strlen("2")))
|
||||||
|
goto fail2;
|
||||||
|
#endif
|
||||||
|
|
||||||
snprintf(ref, sizeof ref, "%d", ctrl->ring_ref);
|
snprintf(ref, sizeof ref, "%d", ctrl->ring_ref);
|
||||||
snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", devno);
|
snprintf(buf, sizeof buf, "device/vchan/%d/ring-ref", devno);
|
||||||
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
|
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
|
||||||
@ -98,21 +172,21 @@ static int server_interface_init(struct libvchan *ctrl, int devno)
|
|||||||
snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", devno);
|
snprintf(buf, sizeof buf, "device/vchan/%d/event-channel", devno);
|
||||||
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
|
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
|
||||||
goto fail2;
|
goto fail2;
|
||||||
// wait for the peer to arrive
|
// do not block in stubdom and windows - libvchan_server_handle_connected will be
|
||||||
|
// called on first input
|
||||||
|
#ifndef ASYNC_INIT
|
||||||
|
// wait for the peer to arrive
|
||||||
if (xc_evtchn_pending(evfd) == -1)
|
if (xc_evtchn_pending(evfd) == -1)
|
||||||
goto fail2;
|
goto fail2;
|
||||||
xc_evtchn_unmask(ctrl->evfd, ctrl->evport);
|
xc_evtchn_unmask(ctrl->evfd, ctrl->evport);
|
||||||
snprintf(buf, sizeof buf, "device/vchan/%d", devno);
|
snprintf(buf, sizeof buf, "device/vchan/%d", devno);
|
||||||
xs_rm(xs, 0, buf);
|
xs_rm(xs, 0, buf);
|
||||||
|
#endif
|
||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail2:
|
fail2:
|
||||||
if (ret)
|
if (ret)
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
|
||||||
xc_evtchn_close(evfd);
|
xc_evtchn_close(evfd);
|
||||||
#else
|
|
||||||
close(evfd);
|
|
||||||
#endif
|
|
||||||
fail:
|
fail:
|
||||||
xs_daemon_close(xs);
|
xs_daemon_close(xs);
|
||||||
return ret;
|
return ret;
|
||||||
@ -130,7 +204,7 @@ static int server_interface_init(struct libvchan *ctrl, int devno)
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
Run in AppVM (any domain).
|
Run in AppVM (any domain).
|
||||||
Sleeps until the connection is established.
|
Sleeps until the connection is established. (unless in stubdom)
|
||||||
\param devno something like a well-known port.
|
\param devno something like a well-known port.
|
||||||
\returns NULL on failure, handle on success
|
\returns NULL on failure, handle on success
|
||||||
*/
|
*/
|
||||||
@ -156,6 +230,44 @@ struct libvchan *libvchan_server_init(int devno)
|
|||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
int libvchan_server_handle_connected(struct libvchan *ctrl)
|
||||||
|
{
|
||||||
|
struct xs_handle *xs;
|
||||||
|
char buf[64];
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
#ifdef WINNT
|
||||||
|
xs = xs_domain_open();
|
||||||
|
#else
|
||||||
|
xs = xs_daemon_open();
|
||||||
|
#endif
|
||||||
|
if (!xs) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef WINNT
|
||||||
|
// clear the pending flag
|
||||||
|
xc_evtchn_pending(ctrl->evfd);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
snprintf(buf, sizeof buf, "device/vchan/%d", ctrl->devno);
|
||||||
|
xs_rm(xs, 0, buf);
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
fail2:
|
||||||
|
if (ret)
|
||||||
|
xc_evtchn_close(ctrl->evfd);
|
||||||
|
#endif
|
||||||
|
xs_daemon_close(xs);
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef WINNT
|
||||||
|
|
||||||
/**
|
/**
|
||||||
retrieves ring-ref and event-channel numbers from xenstore (if
|
retrieves ring-ref and event-channel numbers from xenstore (if
|
||||||
they don't exist, return error, because nobody seems to listen);
|
they don't exist, return error, because nobody seems to listen);
|
||||||
@ -167,14 +279,17 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
|
|||||||
unsigned int len;
|
unsigned int len;
|
||||||
struct xs_handle *xs;
|
struct xs_handle *xs;
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
#ifdef XENCTRL_HAS_XC_INTERFACE
|
||||||
xc_interface *xcfd;
|
xc_interface *xcfd;
|
||||||
|
xc_gnttab *xcg;
|
||||||
#else
|
#else
|
||||||
int xcfd;
|
int xcfd;
|
||||||
|
int xcg;
|
||||||
#endif
|
#endif
|
||||||
char buf[64];
|
char buf[64];
|
||||||
char *ref;
|
char *ref;
|
||||||
|
int version;
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
#ifdef XENCTRL_HAS_XC_INTERFACE
|
||||||
xc_evtchn *evfd;
|
xc_evtchn *evfd;
|
||||||
#else
|
#else
|
||||||
int evfd;
|
int evfd;
|
||||||
#endif
|
#endif
|
||||||
@ -183,6 +298,18 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
|
|||||||
if (!xs) {
|
if (!xs) {
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
version = 1;
|
||||||
|
snprintf(buf, sizeof buf,
|
||||||
|
"/local/domain/%d/device/vchan/%d/version", domain,
|
||||||
|
devno);
|
||||||
|
ref = xs_read(xs, 0, buf, &len);
|
||||||
|
if (ref) {
|
||||||
|
version = atoi(ref);
|
||||||
|
free(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
snprintf(buf, sizeof buf,
|
snprintf(buf, sizeof buf,
|
||||||
"/local/domain/%d/device/vchan/%d/ring-ref", domain,
|
"/local/domain/%d/device/vchan/%d/ring-ref", domain,
|
||||||
devno);
|
devno);
|
||||||
@ -190,9 +317,9 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
|
|||||||
if (!ref)
|
if (!ref)
|
||||||
goto fail;
|
goto fail;
|
||||||
ctrl->ring_ref = atoi(ref);
|
ctrl->ring_ref = atoi(ref);
|
||||||
|
free(ref);
|
||||||
if (!ctrl->ring_ref)
|
if (!ctrl->ring_ref)
|
||||||
goto fail;
|
goto fail;
|
||||||
free(ref);
|
|
||||||
snprintf(buf, sizeof buf,
|
snprintf(buf, sizeof buf,
|
||||||
"/local/domain/%d/device/vchan/%d/event-channel", domain,
|
"/local/domain/%d/device/vchan/%d/event-channel", domain,
|
||||||
devno);
|
devno);
|
||||||
@ -200,26 +327,39 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
|
|||||||
if (!ref)
|
if (!ref)
|
||||||
goto fail;
|
goto fail;
|
||||||
remote_port = atoi(ref);
|
remote_port = atoi(ref);
|
||||||
|
free(ref);
|
||||||
if (!remote_port)
|
if (!remote_port)
|
||||||
goto fail;
|
goto fail;
|
||||||
free(ref);
|
|
||||||
|
switch (version) {
|
||||||
|
case 1:
|
||||||
|
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
#ifdef XENCTRL_HAS_XC_INTERFACE
|
||||||
xcfd = xc_interface_open(NULL, NULL, 0);
|
xcfd = xc_interface_open(NULL, NULL, 0);
|
||||||
if (!xcfd)
|
if (!xcfd)
|
||||||
goto fail;
|
goto fail;
|
||||||
#else
|
#else
|
||||||
xcfd = xc_interface_open();
|
xcfd = xc_interface_open();
|
||||||
if (xcfd < 0)
|
if (xcfd < 0)
|
||||||
|
goto fail;
|
||||||
|
#endif
|
||||||
|
ctrl->ring = (struct vchan_interface *)
|
||||||
|
xc_map_foreign_range(xcfd, domain, 4096,
|
||||||
|
PROT_READ | PROT_WRITE, ctrl->ring_ref);
|
||||||
|
xc_interface_close(xcfd);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
xcg = xc_gnttab_open(NULL, 0);
|
||||||
|
if (xcg < 0)
|
||||||
|
goto fail;
|
||||||
|
ctrl->ring = (struct vchan_interface *)
|
||||||
|
xc_gnttab_map_grant_ref(xcg, domain, ctrl->ring_ref, PROT_READ | PROT_WRITE);
|
||||||
|
xc_gnttab_close(xcg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
goto fail;
|
goto fail;
|
||||||
#endif
|
}
|
||||||
ctrl->ring = (struct vchan_interface *)
|
|
||||||
xc_map_foreign_range(xcfd, domain, 4096,
|
|
||||||
PROT_READ | PROT_WRITE, ctrl->ring_ref);
|
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
|
||||||
xc_interface_close(xcfd);
|
|
||||||
#else
|
|
||||||
close(xcfd);
|
|
||||||
#endif
|
|
||||||
if (ctrl->ring == 0 || ctrl->ring == MAP_FAILED)
|
if (ctrl->ring == 0 || ctrl->ring == MAP_FAILED)
|
||||||
goto fail;
|
goto fail;
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
#ifdef XENCTRL_HAS_XC_INTERFACE
|
||||||
@ -235,11 +375,7 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
|
|||||||
ctrl->evport =
|
ctrl->evport =
|
||||||
xc_evtchn_bind_interdomain(evfd, domain, remote_port);
|
xc_evtchn_bind_interdomain(evfd, domain, remote_port);
|
||||||
if (ctrl->evport < 0 || xc_evtchn_notify(evfd, ctrl->evport))
|
if (ctrl->evport < 0 || xc_evtchn_notify(evfd, ctrl->evport))
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
|
||||||
xc_evtchn_close(evfd);
|
xc_evtchn_close(evfd);
|
||||||
#else
|
|
||||||
close(evfd);
|
|
||||||
#endif
|
|
||||||
else
|
else
|
||||||
ret = 0;
|
ret = 0;
|
||||||
fail:
|
fail:
|
||||||
@ -264,3 +400,14 @@ struct libvchan *libvchan_client_init(int domain, int devno)
|
|||||||
ctrl->is_server = 0;
|
ctrl->is_server = 0;
|
||||||
return ctrl;
|
return ctrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
// Windows domains can not be dom0
|
||||||
|
|
||||||
|
struct libvchan *libvchan_client_init(int domain, int devno)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
53
vchan/io.c
53
vchan/io.c
@ -20,8 +20,15 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "libvchan.h"
|
#include "libvchan.h"
|
||||||
|
|
||||||
|
#ifndef WINNT
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/select.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|
||||||
/**
|
/**
|
||||||
\return How much data is immediately available for reading
|
\return How much data is immediately available for reading
|
||||||
*/
|
*/
|
||||||
@ -64,10 +71,21 @@ int libvchan_is_eof(struct libvchan *ctrl)
|
|||||||
/**
|
/**
|
||||||
\return -1 return value means peer has closed
|
\return -1 return value means peer has closed
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifdef WINNT
|
||||||
int libvchan_wait(struct libvchan *ctrl)
|
int libvchan_wait(struct libvchan *ctrl)
|
||||||
{
|
{
|
||||||
int ret;
|
int ret;
|
||||||
ret = xc_evtchn_pending(ctrl->evfd);
|
|
||||||
|
ret = xc_evtchn_pending_with_flush(ctrl->evfd);
|
||||||
|
// I don't know how to avoid evtchn ring buffer overflow without
|
||||||
|
// introducing any race condition (in qrexec-agent code). Because of that,
|
||||||
|
// handle overflow with ring reset - because we just received some events
|
||||||
|
// (overflow means ring full, so some events was recorded...) the reset
|
||||||
|
// isn't critical here - always after libvchan_wait we check if there is
|
||||||
|
// something to read from the vchan
|
||||||
|
if (ret == -1 && GetLastError() == ERROR_IO_DEVICE)
|
||||||
|
ret = xc_evtchn_reset(ctrl->evfd);
|
||||||
if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport))
|
if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport))
|
||||||
return -1;
|
return -1;
|
||||||
if (ret!=-1 && libvchan_is_eof(ctrl))
|
if (ret!=-1 && libvchan_is_eof(ctrl))
|
||||||
@ -75,6 +93,37 @@ int libvchan_wait(struct libvchan *ctrl)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
int libvchan_wait(struct libvchan *ctrl)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
#ifndef CONFIG_STUBDOM
|
||||||
|
ret = xc_evtchn_pending(ctrl->evfd);
|
||||||
|
#else
|
||||||
|
int vchan_fd = libvchan_fd_for_select(ctrl);
|
||||||
|
fd_set rfds;
|
||||||
|
|
||||||
|
libvchan_prepare_to_select(ctrl);
|
||||||
|
while ((ret = xc_evtchn_pending(ctrl->evfd)) < 0) {
|
||||||
|
FD_ZERO(&rfds);
|
||||||
|
FD_SET(0, &rfds);
|
||||||
|
FD_SET(vchan_fd, &rfds);
|
||||||
|
ret = select(vchan_fd + 1, &rfds, NULL, NULL, NULL);
|
||||||
|
if (ret < 0 && errno != EINTR) {
|
||||||
|
perror("select");
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (ret!=-1 && xc_evtchn_unmask(ctrl->evfd, ctrl->evport))
|
||||||
|
return -1;
|
||||||
|
if (ret!=-1 && libvchan_is_eof(ctrl))
|
||||||
|
return -1;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/**
|
/**
|
||||||
may sleep (only if no buffer space available);
|
may sleep (only if no buffer space available);
|
||||||
may write less data than requested;
|
may write less data than requested;
|
||||||
@ -147,7 +196,7 @@ int libvchan_close(struct libvchan *ctrl)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// The fd to use for select() set
|
/// The fd to use for select() set
|
||||||
int libvchan_fd_for_select(struct libvchan *ctrl)
|
EVTCHN libvchan_fd_for_select(struct libvchan *ctrl)
|
||||||
{
|
{
|
||||||
return xc_evtchn_fd(ctrl->evfd);
|
return xc_evtchn_fd(ctrl->evfd);
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,29 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#ifndef _LIBVCHAN_H
|
||||||
|
#define _LIBVCHAN_H
|
||||||
|
|
||||||
|
#ifdef WINNT
|
||||||
|
#include <windows.h>
|
||||||
|
#include <xen_gntmem.h>
|
||||||
|
#include <xs.h>
|
||||||
|
typedef HANDLE EVTCHN;
|
||||||
|
#define snprintf _snprintf
|
||||||
|
#else /* WINNT */
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
typedef int EVTCHN;
|
||||||
|
#endif /* WINNT */
|
||||||
|
|
||||||
|
/* config vchan features */
|
||||||
|
#ifdef WINNT
|
||||||
|
#define QREXEC_RING_V2
|
||||||
|
#define ASYNC_INIT
|
||||||
|
#endif /* WINNT */
|
||||||
|
#ifdef CONFIG_STUBDOM
|
||||||
|
#define ASYNC_INIT
|
||||||
|
#endif /* CONFIG_STUBDOM */
|
||||||
|
|
||||||
#include <xenctrl.h>
|
#include <xenctrl.h>
|
||||||
typedef uint32_t VCHAN_RING_IDX;
|
typedef uint32_t VCHAN_RING_IDX;
|
||||||
|
|
||||||
@ -39,11 +61,12 @@ struct libvchan {
|
|||||||
uint32_t ring_ref;
|
uint32_t ring_ref;
|
||||||
/// descriptor to event channel interface
|
/// descriptor to event channel interface
|
||||||
#ifdef XENCTRL_HAS_XC_INTERFACE
|
#ifdef XENCTRL_HAS_XC_INTERFACE
|
||||||
xc_evtchn *evfd;
|
xc_evtchn *evfd;
|
||||||
#else
|
#else
|
||||||
int evfd;
|
EVTCHN evfd;
|
||||||
#endif
|
#endif
|
||||||
int evport;
|
int evport;
|
||||||
|
int devno;
|
||||||
VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod;
|
VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod;
|
||||||
char *rd_ring, *wr_ring;
|
char *rd_ring, *wr_ring;
|
||||||
int rd_ring_size, wr_ring_size;
|
int rd_ring_size, wr_ring_size;
|
||||||
@ -54,12 +77,15 @@ struct libvchan *libvchan_server_init(int devno);
|
|||||||
|
|
||||||
struct libvchan *libvchan_client_init(int domain, int devno);
|
struct libvchan *libvchan_client_init(int domain, int devno);
|
||||||
|
|
||||||
|
int libvchan_server_handle_connected(struct libvchan *ctrl);
|
||||||
int libvchan_write(struct libvchan *ctrl, char *data, int size);
|
int libvchan_write(struct libvchan *ctrl, char *data, int size);
|
||||||
int libvchan_read(struct libvchan *ctrl, char *data, int size);
|
int libvchan_read(struct libvchan *ctrl, char *data, int size);
|
||||||
int libvchan_wait(struct libvchan *ctrl);
|
int libvchan_wait(struct libvchan *ctrl);
|
||||||
int libvchan_close(struct libvchan *ctrl);
|
int libvchan_close(struct libvchan *ctrl);
|
||||||
void libvchan_prepare_to_select(struct libvchan *ctrl);
|
void libvchan_prepare_to_select(struct libvchan *ctrl);
|
||||||
int libvchan_fd_for_select(struct libvchan *ctrl);
|
EVTCHN libvchan_fd_for_select(struct libvchan *ctrl);
|
||||||
int libvchan_is_eof(struct libvchan *ctrl);
|
int libvchan_is_eof(struct libvchan *ctrl);
|
||||||
int libvchan_data_ready(struct libvchan *ctrl);
|
int libvchan_data_ready(struct libvchan *ctrl);
|
||||||
int libvchan_buffer_space(struct libvchan *ctrl);
|
int libvchan_buffer_space(struct libvchan *ctrl);
|
||||||
|
|
||||||
|
#endif /* _LIBVCHAN_H */
|
||||||
|
22
vchan/sources
Normal file
22
vchan/sources
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
TARGETNAME=libvchan
|
||||||
|
TARGETTYPE=LIBRARY
|
||||||
|
TARGETPATH=..\win\libs
|
||||||
|
|
||||||
|
#BUILD_PASS0_CONSUMES=gntmem evtchn xenstore
|
||||||
|
#BUILD_PASS0_PRODUCES=vchan
|
||||||
|
|
||||||
|
USE_MSVCRT=1
|
||||||
|
|
||||||
|
DLLDEF=libvchan.def
|
||||||
|
|
||||||
|
INCLUDES=$(INCLUDES); \
|
||||||
|
..\win\include;\
|
||||||
|
|
||||||
|
SOURCES=io.c init.c
|
||||||
|
|
||||||
|
TARGETLIBS=\
|
||||||
|
$(SDK_LIB_PATH)\setupapi.lib \
|
||||||
|
$(SDK_LIB_PATH)\kernel32.lib
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1 +1 @@
|
|||||||
1.7.46
|
2.0.36
|
||||||
|
Loading…
Reference in New Issue
Block a user