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:
Marek Marczykowski 2012-10-17 21:41:03 +02:00
commit 3e90174910
33 changed files with 1160 additions and 239 deletions

View 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()

View File

@ -37,8 +37,10 @@ start()
MEMINFO_DELAY_USEC=100000
/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)
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"/'`;
/usr/lib/qubes/block_add_change > /dev/null
)

View 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;

View 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}' ]

View File

@ -8,7 +8,7 @@ sync_qubes_vms_wallclock()
DATE=$(date)
echo
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
/usr/bin/qvm-sync-clock &
}

View File

@ -53,7 +53,7 @@ class SystemState:
self.domdict[id].memory_maximum = self.xs.read('', '/local/domain/%s/memory/static-max' % str(id))
if not self.domdict[id].memory_maximum:
self.domdict[id].memory_maximum = self.ALL_PHYS_MEM
# the previous line used to be
# the previous line used to be
# self.domdict[id].memory_maximum = domain['maxmem_kb']*1024
# but domain['maxmem_kb'] changes in self.mem_set as well, and this results in
# the memory never increasing
@ -109,7 +109,7 @@ class SystemState:
for i in self.domdict.keys():
dom = self.domdict[i]
if dom.memory_actual is not None and dom.memory_actual + 200*1024 < dom.last_target:
print "Preventing balloon up to", dom.last_target
print "Preventing balloon up to", dom.last_target
self.mem_set(i, dom.memory_actual)
#perform memory ballooning, across all domains, to add "memsize" to Xen free memory
@ -144,7 +144,7 @@ class SystemState:
prev_memory_actual[dom] = self.domdict[dom].memory_actual
time.sleep(self.BALOON_DELAY)
niter = niter + 1
def refresh_meminfo(self, domid, untrusted_meminfo_key):
qmemman_algo.refresh_meminfo_for_domain(self.domdict[domid], untrusted_meminfo_key)
self.do_balance()
@ -174,7 +174,7 @@ class SystemState:
if self.domdict[i].meminfo is not None:
print 'dom' , i, 'act/pref', self.domdict[i].memory_actual, qmemman_algo.prefmem(self.domdict[i])
print 'xenfree=', xenfree, 'balance req:', memset_reqs
def do_balance(self):
if os.path.isfile('/var/run/qubes/do-not-membalance'):
return
@ -184,7 +184,7 @@ class SystemState:
memset_reqs = qmemman_algo.balance(xenfree - self.XEN_FREE_MEM_LEFT, self.domdict)
if not self.is_balance_req_significant(memset_reqs, xenfree):
return
self.print_stats(xenfree, memset_reqs)
prev_memactual = {}

View File

@ -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)
{
char *incoming_dir;
int pipefds[2];
int uid;
char *var;
long long files_limit = DEFAULT_MAX_UPDATES_FILES;
@ -73,32 +72,15 @@ int main(int argc, char ** argv)
if ((var=getenv("UPDATES_MAX_FILES")))
files_limit = atoll(var);
pipe(pipefds);
uid = prepare_creds_return_uid(argv[1]);
incoming_dir = argv[2];
mkdir(incoming_dir, 0700);
if (chdir(incoming_dir))
gui_fatal("Error chdir to %s", incoming_dir);
switch (fork()) {
case -1:
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:;
}
if (chroot(incoming_dir)) //impossible
gui_fatal("Error chroot to %s", incoming_dir);
setuid(uid);
close(pipefds[1]);
wait_for_child(pipefds[0]);
return 0;
set_size_limit(bytes_limit, files_limit);
return do_unpack();
}

View File

@ -21,6 +21,7 @@
#
import sys
import stat
import os
import os.path
import subprocess
@ -76,7 +77,14 @@ default_memory = 400
default_kernelopts = ""
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_hvm = '/usr/share/qubes/vm-template-hvm.conf'
start_appmenu_template = '/usr/share/qubes/qubes-start.desktop'
qubes_whitelisted_appmenus = 'whitelisted-appmenus.list'
@ -252,6 +260,7 @@ class QubesVm(object):
"services": { "default": {}, "eval": "eval(str(value))" },
"debug": { "default": False },
"default_user": { "default": "user" },
"qrexec_timeout": { "default": 60, "eval": "int(value)" },
##### Internal attributes - will be overriden in __init__ regardless of args
"appmenus_templates_dir": { "eval": \
'self.dir_path + "/" + default_appmenus_templates_subdir if self.updateable else ' + \
@ -263,6 +272,7 @@ class QubesVm(object):
"kernels_dir": { 'eval': 'qubes_kernels_base_dir + "/" + self.kernel if self.kernel is not None else ' + \
# for backward compatibility (or another rare case): kernel=None -> kernel in VM dir
'self.dir_path + "/" + default_kernels_subdir' },
"_start_guid_first": { 'eval': 'False' },
}
### Mark attrs for XML inclusion
@ -271,7 +281,7 @@ class QubesVm(object):
'uses_default_kernel', 'kernel', 'uses_default_kernelopts',\
'kernelopts', 'services', 'installed_by_rpm',\
'uses_default_netvm', 'include_in_backups', 'debug',\
'default_user' ]:
'default_user', 'qrexec_timeout' ]:
attrs[prop]['save'] = 'str(self.%s)' % prop
# Simple paths
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
args['maxmem'] = args['mem']
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:
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)
if self.netvm.qid != 0:
args['netdev'] += ",backend={0}".format(self.netvm.name)
args['netdev'] += "'"
else:
args['ip'] = ''
args['mac'] = ''
args['gateway'] = ''
args['dns1'] = ''
args['dns2'] = ''
args['netmask'] = ''
args['netdev'] = ''
args['rootdev'] = self.get_rootdev(source_template=source_template)
args['privatedev'] = "'script:file:{dir}/private.img,xvdb,w',".format(dir=self.dir_path)
@ -1346,6 +1362,7 @@ class QubesVm(object):
if passio_popen:
popen_kwargs={'stdout': subprocess.PIPE}
popen_kwargs['stdin'] = subprocess.PIPE
if passio_stderr:
popen_kwargs['stderr'] = subprocess.PIPE
else:
@ -1422,6 +1439,17 @@ class QubesVm(object):
if notify_function is not None:
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):
if dry_run:
return
@ -1501,15 +1529,13 @@ class QubesVm(object):
# the successful unpause is some indicator of it
qmemman_client.close()
if not preparing_dvm:
if 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 self._start_guid_first and start_guid and not preparing_dvm and os.path.exists('/var/run/shm.id'):
self.start_guid(verbose=verbose)
if 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)
if preparing_dvm:
@ -2223,6 +2249,253 @@ class QubesAppVm(QubesVm):
self.remove_appmenus()
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):
"""
@ -2279,6 +2552,18 @@ class QubesVmCollection(dict):
self[vm.qid]=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,
label = None):
@ -2574,7 +2859,8 @@ class QubesVmCollection(dict):
"installed_by_rpm", "internal",
"uses_default_netvm", "label", "memory", "vcpus", "pcidevs",
"maxmem", "kernel", "uses_default_kernel", "kernelopts", "uses_default_kernelopts",
"mac", "services", "include_in_backups", "debug", "default_user" )
"mac", "services", "include_in_backups", "debug",
"default_user", "qrexec_timeout", "qrexec_installed", "drive" )
for attribute in common_attr_list:
kwargs[attribute] = element.get(attribute)
@ -2628,13 +2914,20 @@ class QubesVmCollection(dict):
if "uses_default_kernelopts" in kwargs:
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")
if "kernelopts" not in kwargs:
kwargs["uses_default_kernelopts"] = True
if "debug" in kwargs:
kwargs["debug"] = True if kwargs["debug"] == "True" else False
if "qrexec_installed" in kwargs:
kwargs["qrexec_installed"] = True if kwargs["qrexec_installed"] == "True" else False
if "drive" in kwargs and kwargs["drive"] == "None":
kwargs["drive"] = None
return kwargs
def set_netvm_dependency(self, element):
@ -2807,6 +3100,20 @@ class QubesVmCollection(dict):
os.path.basename(sys.argv[0]), err))
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
for element in tree.findall("QubesDisposableVm"):
try:

View File

@ -896,8 +896,8 @@ def backup_restore_print_summary(restore_info, print_callback = print_stdout):
+ ('}' if vm.is_netvm() else '')"},
"type": {"func": "'Tpl' if vm.is_template() else \
('Proxy' if vm.is_proxyvm() else \
(' Net' if vm.is_netvm() else 'App'))"},
'HVM' if vm.type == 'HVM' else \
vm.type.replace('VM','')"},
"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
try:
restore_vm_dir (backup_dir, vm.dir_path, qubes_appvms_dir);
new_vm = host_collection.add_new_appvm(vm.name, template,
conf_file=vm.conf_file,
dir_path=vm.dir_path,
label=vm.label)
if vm.type == "HVM":
new_vm = host_collection.add_new_hvm(vm.name,
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:
error_callback("ERROR: {0}".format(err))
error_callback("*** Skipping VM: {0}".format(vm.name))

View File

@ -21,7 +21,7 @@
#
from qubes.qubes import QubesVmCollection
from qubes.qubes import QubesAppVm, QubesTemplateVm
from qubes.qubes import QubesAppVm, QubesTemplateVm, QubesHVm
from qubes.qubes import QubesException
from optparse import OptionParser;
import sys
@ -72,6 +72,8 @@ def main():
dst_vm = qvm_collection.add_new_appvm(name=dstname, template=src_vm.template,
label=src_vm.label,
dir_path=options.dir_path)
elif isinstance(src_vm, QubesHVm):
dst_vm = qvm_collection.add_new_hvm(name=dstname, label=src_vm.label)
else:
print >> sys.stderr, "ERROR: Clone not supported for this type of VM"
exit(1)

View File

@ -38,10 +38,14 @@ def main():
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,
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,
help="Create NetVM")
parser.add_option ("-s", "--standalone", action="store_true", dest="standalone", default=False,
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,
help="Initial memory size (in MB)")
parser.add_option ("-c", "--vcpus", dest="vcpus", default=None,
@ -83,6 +87,18 @@ def main():
exit (1)
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.lock_db_for_writing()
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)
exit(1)
template = None
if options.template is not None:
template = qvm_collection.get_vm_by_name(options.template)
if template is None:
@ -102,7 +119,7 @@ def main():
if (options.verbose):
print "--> Using TemplateVM: {0}".format(template.name)
else:
elif not options.hvm:
if qvm_collection.get_default_template() is None:
print >> sys.stderr, "No default TempleteVM defined!"
exit (1)
@ -122,6 +139,8 @@ def main():
vm = qvm_collection.add_new_netvm(vmname, new_vm_template, label = label)
elif options.proxyvm:
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:
vm = qvm_collection.add_new_appvm(vmname, new_vm_template, label = label)
except QubesException as err:
@ -139,6 +158,9 @@ def main():
try:
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:
print >> sys.stderr, "ERROR: {0}".format(err)

View File

@ -53,21 +53,23 @@ def do_list(vm):
print fmt.format ("root COW img", vm.rootcow_img)
if vm.template is not None:
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)
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 ("vcpus", str(vm.vcpus))
print fmt.format ("memory", vm.memory)
if hasattr(vm, 'maxmem'):
print fmt.format ("maxmem", vm.maxmem)
print fmt.format ("MAC", "%s%s" % (vm.mac, " (auto)" if vm._mac is None else ""))
if hasattr(vm, 'kernel'):
if vm.uses_default_kernel:
print fmt.format ("kernel", "%s (default)" % vm.kernel)
else:
print fmt.format ("kernel", vm.kernel)
if hasattr(vm, 'kernelopts'):
if vm.uses_default_kernelopts:
print fmt.format ("kernelopts", "%s (default)" % vm.kernelopts)
@ -79,6 +81,18 @@ def do_list(vm):
if hasattr(vm, 'default_user'):
print fmt.format("default user", str(vm.default_user))
if hasattr(vm, 'qrexec_installed'):
print fmt.format("qrexec_installed", str(vm.qrexec_installed))
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):
if len (args) != 1:
print >> sys.stderr, "Missing label name argument!"
@ -274,6 +288,16 @@ def set_name(vms, vm, args):
vm.set_name(args[0])
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):
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()))
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 = {
"include_in_backups": set_include_in_backups,
"pcidevs": set_pcidevs,
@ -316,9 +368,13 @@ properties = {
"vcpus" : set_vcpus,
"kernelopts": set_kernelopts,
"name": set_name,
"drive": set_drive,
"mac": set_mac,
"debug": set_debug,
"default_user": set_default_user,
"qrexec_installed": set_qrexec_installed,
"qrexec_timeout": set_qrexec_timeout,
"timezone": set_timezone,
}

View File

@ -78,6 +78,23 @@ def main():
print >> sys.stderr, "A VM with the name '{0}' does not exist in the system.".format(vmname)
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:
vm.conf_file = options.custom_config

View File

@ -64,7 +64,7 @@ def main():
# Ignore retcode, try even if nm-online failed - user can setup network manually
# 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
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)
# 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 = 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):

View File

@ -228,8 +228,8 @@ void send_exit_code(int client_id, int status)
s_hdr.len = sizeof status;
write_all_vchan_ext(&s_hdr, sizeof s_hdr);
write_all_vchan_ext(&status, sizeof(status));
fprintf(stderr, "send exit code for client_id %d pid %d\n",
client_id, client_info[client_id].pid);
fprintf(stderr, "send exit code %d for client_id %d pid %d\n",
status, client_id, client_info[client_id].pid);
}
@ -239,8 +239,9 @@ void remove_process(int client_id, int status)
int i;
if (!client_info[client_id].pid)
return;
fork_and_flush_stdin(client_info[client_id].stdin_fd,
&client_info[client_id].buffer);
if (client_info[client_id].stdin_fd >= 0)
fork_and_flush_stdin(client_info[client_id].stdin_fd,
&client_info[client_id].buffer);
#if 0
// let's let it die by itself, possibly after it has received buffered stdin
kill(client_info[client_id].pid, SIGKILL);
@ -283,7 +284,7 @@ void handle_input(int client_id, int len)
char 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;
if (len == 0) {
@ -306,7 +307,10 @@ void handle_input(int client_id, int len)
client_info[client_id].is_blocked = 1;
break;
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;
default:
fprintf(stderr, "unknown write_stdin?\n");
@ -494,7 +498,11 @@ void flush_client_data_agent(int client_id)
}
break;
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;
case WRITE_STDIN_BUFFERED:
break;

View File

@ -27,6 +27,7 @@
#include <unistd.h>
#include <ioall.h>
#include <sys/wait.h>
#include <errno.h>
#include "qrexec.h"
#include "buffer.h"
#include "glue.h"
@ -106,12 +107,33 @@ void handle_input(int s)
do_exit(1);
}
if (ret == 0) {
close(local_stdout_fd);
local_stdout_fd = -1;
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)) {
perror("write daemon");
do_exit(1);
if (errno == EPIPE) {
// 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) {
case MSG_SERVER_TO_CLIENT_STDOUT:
if (hdr.len == 0)
if (local_stdin_fd == -1)
break;
if (hdr.len == 0) {
close(local_stdin_fd);
else if (!write_all(local_stdin_fd, buf, hdr.len)) {
perror("write local stdout");
do_exit(1);
local_stdin_fd = -1;
} else if (!write_all(local_stdin_fd, buf, hdr.len)) {
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;
case MSG_SERVER_TO_CLIENT_STDERR:

View File

@ -38,8 +38,10 @@ enum client_flags {
CLIENT_INVALID = 0, // table slot not used
CLIENT_CMDLINE = 1, // waiting for cmdline 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_OUTQ_FULL = 8 // don't write to client, its stdin pipe is full
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_EOF = 16, // got EOF
CLIENT_EXITED = 32 // only send remaining data from client and remove from list
};
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 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)
{
@ -82,7 +87,31 @@ int create_qrexec_socket(int domid, char *domname)
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 */
void init(int xid)
@ -90,13 +119,23 @@ void init(int xid)
char qrexec_error_log_name[256];
int logfd;
int i;
pid_t pid;
int startup_timeout = MAX_STARTUP_TIME_DEFAULT;
char *startup_timeout_str = NULL;
if (xid <= 0) {
fprintf(stderr, "domain id=0?\n");
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);
switch (fork()) {
switch (pid=fork()) {
case -1:
perror("fork");
exit(1);
@ -104,11 +143,16 @@ void init(int xid)
break;
default:
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);
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);
}
close(0);
@ -170,7 +214,7 @@ void terminate_client_and_flush_data(int fd)
int i;
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++;
close(fd);
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));
}
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)
{
int len = s_hdr->len;
char buf[len];
int use_default_user = 0;
if (!read_all(fd, buf, len)) {
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(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)
@ -224,7 +279,10 @@ void handle_cmdline_message_from_client(int fd)
s_hdr.client_id = fd;
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;
set_nonblock(fd); // so that we can detect full queue without blocking
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(buf, ret);
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;
break;
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;
case WRITE_STDIN_BUFFERED: // no room for all data, don't clear CLIENT_OUTQ_FULL flag
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 */
*(struct client_header *) buf = *hdr;
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
(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;
break;
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;
default:
fprintf(stderr, "unknown write_stdin?\n");
@ -557,10 +632,12 @@ int main(int argc, char **argv)
int max;
sigset_t chld_set;
if (argc != 2) {
fprintf(stderr, "usage: %s domainid\n", argv[0]);
if (argc != 2 && argc != 3) {
fprintf(stderr, "usage: %s domainid [default user]\n", argv[0]);
exit(1);
}
if (argc == 3)
default_user = argv[2];
init(atoi(argv[1]));
sigemptyset(&chld_set);
sigaddset(&chld_set, SIGCHLD);

View File

@ -5,6 +5,7 @@ import os.path
import subprocess
import xen.lowlevel.xl
import qubes.guihelpers
from optparse import OptionParser
import fcntl
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])
def main():
domain=sys.argv[1]
target=sys.argv[2]
exec_index=sys.argv[3]
process_ident=sys.argv[4]
usage = "usage: %prog [options] <src-domain> <target-domain> <service> <process-ident>"
parser = OptionParser (usage)
parser.add_option ("--assume-yes-for-ask", action="store_true", dest="assume_yes_for_ask", default=False,
help="Allow run of service without confirmation if policy say 'ask'")
parser.add_option ("--just-evaluate", action="store_true", dest="just_evaluate", default=False,
help="Do not run the service, only evaluate policy; retcode=0 means 'allow'")
(options, args) = parser.parse_args ()
domain=args[0]
target=args[1]
exec_index=args[2]
process_ident=args[3]
policy_list=read_policy_file(exec_index)
if policy_list==None:
@ -139,6 +148,9 @@ def main():
policy_list=list()
policy_dict=find_policy(policy_list, domain, target)
if policy_dict["action"] == "ask" and options.assume_yes_for_ask:
policy_dict["action"] = "allow"
if policy_dict["action"] == "ask":
user_choice = confirm_execution(domain, target, exec_index)
@ -149,14 +161,20 @@ def main():
policy_dict["action"] = "allow"
else:
policy_dict["action"] = "deny"
if options.just_evaluate:
if policy_dict["action"] == "allow":
exit(0)
else:
exit(1)
if policy_dict["action"] == "allow":
if policy_dict.has_key("action.target"):
target=policy_dict["action.target"]
if policy_dict.has_key("action.user"):
user=policy_dict["action.user"]
else:
user="user"
user="DEFAULT"
do_execute(domain, target, user, exec_index, process_ident)
print >> sys.stderr, "Rpc denied:", domain, target, exec_index

View File

@ -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
the client. However, if we have buffered data for the client (which is rare btw),

View File

@ -32,6 +32,17 @@ void perror_wrapper(char * msg)
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)
{
@ -42,7 +53,6 @@ int write_all(int fd, void *buf, int size)
if (ret == -1 && errno == EINTR)
continue;
if (ret <= 0) {
perror_wrapper("write");
return 0;
}
written += ret;
@ -65,9 +75,14 @@ int read_all(int fd, void *buf, int size)
return 0;
}
if (ret < 0) {
perror_wrapper("read");
if (errno != EAGAIN)
perror_wrapper("read");
return 0;
}
if (got_read == 0) {
// force blocking operation on further reads
set_block(fd);
}
got_read += ret;
}
// fprintf(stderr, "read %d bytes\n", size);

View File

@ -1,3 +1,5 @@
int write_all(int fd, void *buf, int size);
int read_all(int fd, void *buf, int size);
int copy_fd_all(int fdout, int fdin);
void set_nonblock(int fd);
void set_block(int fd);

View File

@ -9,6 +9,7 @@
#include <stdlib.h>
#include <ioall.h>
#include <unistd.h>
#include <errno.h>
#include <gui-fatal.h>
#include "filecopy.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)
{
static long long total = 0;
@ -56,6 +88,11 @@ void notify_progress(int size, int flag)
total += size;
if (total > prev_total + PROGRESS_NOTIFY_DELTA
|| (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);
prev_total = total;
}
@ -64,8 +101,11 @@ void notify_progress(int size, int flag)
void write_headers(struct file_header *hdr, char *filename)
{
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);
}
}
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;
write_headers(&hdr, filename);
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_WRITE_ERROR)
gui_fatal("Copying file %s: %s", filename,
copy_file_status_to_str(ret));
else
else {
set_block(0);
wait_for_result();
exit(1);
}
}
close(fd);
}
@ -109,9 +151,14 @@ int single_file_processor(char *filename, struct stat *st)
gui_fatal("readlink %s", filename);
hdr.filelen = st->st_size + 1;
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);
}
}
// check for possible error from qfile-unpacker
wait_for_result();
return 0;
}
@ -147,7 +194,6 @@ int do_fs_walk(char *file)
void notify_end_and_wait_for_result()
{
struct result_header hdr;
struct file_header end_hdr;
/* nofity end of transfer */
@ -156,17 +202,8 @@ void notify_end_and_wait_for_result()
end_hdr.filelen = 0;
write_all_with_crc(1, &end_hdr, sizeof(end_hdr));
/* wait for result */
if (!read_all(0, &hdr, sizeof(hdr))) {
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");
}
set_block(0);
wait_for_result();
}
char *get_abs_path(char *cwd, char *pathname)
@ -186,6 +223,8 @@ int main(int argc, char **argv)
char *sep;
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);
crc32_sum = 0;
cwd = getcwd(NULL, 0);

View File

@ -29,32 +29,14 @@ int prepare_creds_return_uid(char *username)
return pwd->pw_uid;
}
void wait_for_child(int statusfd)
{
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);
extern int do_unpack(void);
int main(int argc, char ** argv)
{
char *incoming_dir;
int pipefds[2];
int uid;
char *remote_domain;
pipe(pipefds);
uid = prepare_creds_return_uid("user");
remote_domain = getenv("QREXEC_REMOTE_DOMAIN");
@ -67,23 +49,8 @@ int main(int argc, char ** argv)
mkdir(incoming_dir, 0700);
if (chdir(incoming_dir))
gui_fatal("Error chdir to %s", incoming_dir);
switch (fork()) {
case -1:
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:;
}
if (chroot(incoming_dir)) //impossible
gui_fatal("Error chroot to %s", incoming_dir);
setuid(uid);
close(pipefds[1]);
wait_for_child(pipefds[0]);
return 0;
return do_unpack();
}

View File

@ -3,4 +3,4 @@
## Please use a single # to start your custom comments
$anyvm $anyvm ask,user=root
$anyvm $anyvm ask

View File

@ -35,14 +35,24 @@ int read_all_with_crc(int fd, void *buf, int size) {
return ret;
}
int global_status_fd;
void do_exit(int code)
{
int codebuf = code;
write(global_status_fd, &codebuf, sizeof codebuf);
exit(0);
void send_status_and_crc(int code) {
struct result_header hdr;
int saved_errno;
saved_errno = errno;
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,
char *untrusted_name)
@ -130,20 +140,8 @@ void process_one_file(struct file_header *untrusted_hdr)
do_exit(EINVAL);
}
void send_status_and_crc() {
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)
int do_unpack()
{
global_status_fd = fd;
struct file_header untrusted_hdr;
/* initialize checksum */
crc32_sum = 0;
@ -158,9 +156,6 @@ void do_unpack(int fd)
if (files_limit && total_files > files_limit)
do_exit(EDQUOT);
}
send_status_and_crc();
if (errno)
do_exit(errno);
else
do_exit(LEGAL_EOF);
send_status_and_crc(errno);
return errno;
}

View File

@ -40,6 +40,7 @@ BuildRequires: xen-devel
Requires: python, xen-runtime, pciutils, python-inotify, python-daemon, kernel-qubes-dom0
Conflicts: qubes-gui-dom0 < 1.1.13
Requires: xen >= 4.1.0-2
Requires: xen-hvm
Requires: createrepo
Requires: gnome-packagekit
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_remove $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/
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-firefox.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-hvm.conf $RPM_BUILD_ROOT/usr/share/qubes/
mkdir -p $RPM_BUILD_ROOT/usr/bin
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_remove
/usr/lib/qubes/block_cleanup
/usr/lib/qubes/block_cleaner_daemon.py*
/usr/lib/qubes/fix_dir_perms.sh
%attr(4750,root,qubes) /usr/lib/qubes/qfile-dom0-unpacker
%attr(770,root,qubes) %dir /var/lib/qubes
@ -378,7 +383,9 @@ fi
/usr/share/qubes/qubes-dispvm.directory
/usr/share/qubes/qubes-dispvm-firefox.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-hvm.conf
/usr/lib/qubes/qubes_setup_dnat_to_ns
/usr/lib/qubes/qubes_fix_nm_conf.sh
/etc/dhclient.d/qubes_setup_dnat_to_ns.sh

View File

@ -405,7 +405,7 @@ rm -rf $RPM_BUILD_ROOT
/usr/lib/qubes/meminfo-writer
/usr/lib/qubes/network-manager-prepare-conf-dir
/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/qrexec_agent
/usr/lib/qubes/qrexec_client_vm

36
vchan/Makefile.stubdom Normal file
View 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

View File

@ -19,41 +19,103 @@
*
*/
#ifndef WINNT
#include <sys/types.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <malloc.h>
#include <xs.h>
#include <string.h>
#include <xenctrl.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 "libvchan.h"
#include "../u2mfn/u2mfnlib.h"
static int ring_init(struct libvchan *ctrl)
static int fill_ctrl(struct libvchan *ctrl, struct vchan_interface *ring, int ring_ref)
{
int u2mfn = open("/proc/u2mfn", O_RDONLY);
int mfn;
struct vchan_interface *ring;
ring = (struct vchan_interface *) u2mfn_alloc_kpage ();
if (ring == MAP_FAILED)
if (!ctrl || !ring)
return -1;
ctrl->ring = ring;
if (u2mfn_get_last_mfn (&mfn) < 0)
return -1;
ctrl->ring_ref = ring_ref;
ctrl->ring_ref = 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;
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)
{
int mfn;
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 ();
if (ring == MAP_FAILED)
return -1;
if (u2mfn_get_last_mfn (&mfn) < 0)
return -1;
#endif
return fill_ctrl(ctrl, ring, mfn);
}
#endif
/**
creates event channel;
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 ref[16];
#ifdef XENCTRL_HAS_XC_INTERFACE
xc_evtchn *evfd;
xc_evtchn *evfd;
#else
int evfd;
EVTCHN evfd;
#endif
evtchn_port_or_error_t port;
#ifdef WINNT
xs = xs_domain_open();
#else
xs = xs_daemon_open();
#endif
if (!xs) {
return ret;
}
@ -90,6 +156,14 @@ static int server_interface_init(struct libvchan *ctrl, int devno)
if (port < 0)
goto fail2;
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(buf, sizeof buf, "device/vchan/%d/ring-ref", devno);
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);
if (!xs_write(xs, 0, buf, ref, strlen(ref)))
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)
goto fail2;
xc_evtchn_unmask(ctrl->evfd, ctrl->evport);
snprintf(buf, sizeof buf, "device/vchan/%d", devno);
xs_rm(xs, 0, buf);
#endif
ret = 0;
fail2:
if (ret)
#ifdef XENCTRL_HAS_XC_INTERFACE
xc_evtchn_close(evfd);
#else
close(evfd);
#endif
fail:
xs_daemon_close(xs);
return ret;
@ -129,8 +203,8 @@ static int server_interface_init(struct libvchan *ctrl, int devno)
ctrl->rd_ring_size = sizeof(ctrl->ring->buf_##dir2)
/**
Run in AppVM (any domain).
Sleeps until the connection is established.
Run in AppVM (any domain).
Sleeps until the connection is established. (unless in stubdom)
\param devno something like a well-known port.
\returns NULL on failure, handle on success
*/
@ -156,6 +230,44 @@ struct libvchan *libvchan_server_init(int devno)
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
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;
struct xs_handle *xs;
#ifdef XENCTRL_HAS_XC_INTERFACE
xc_interface *xcfd;
xc_interface *xcfd;
xc_gnttab *xcg;
#else
int xcfd;
int xcg;
#endif
char buf[64];
char *ref;
int version;
#ifdef XENCTRL_HAS_XC_INTERFACE
xc_evtchn *evfd;
xc_evtchn *evfd;
#else
int evfd;
#endif
@ -183,6 +298,18 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
if (!xs) {
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,
"/local/domain/%d/device/vchan/%d/ring-ref", domain,
devno);
@ -190,9 +317,9 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
if (!ref)
goto fail;
ctrl->ring_ref = atoi(ref);
free(ref);
if (!ctrl->ring_ref)
goto fail;
free(ref);
snprintf(buf, sizeof buf,
"/local/domain/%d/device/vchan/%d/event-channel", domain,
devno);
@ -200,26 +327,39 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
if (!ref)
goto fail;
remote_port = atoi(ref);
free(ref);
if (!remote_port)
goto fail;
free(ref);
switch (version) {
case 1:
#ifdef XENCTRL_HAS_XC_INTERFACE
xcfd = xc_interface_open(NULL, NULL, 0);
if (!xcfd)
goto fail;
xcfd = xc_interface_open(NULL, NULL, 0);
if (!xcfd)
goto fail;
#else
xcfd = xc_interface_open();
if (xcfd < 0)
xcfd = xc_interface_open();
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;
#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)
goto fail;
#ifdef XENCTRL_HAS_XC_INTERFACE
@ -235,11 +375,7 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
ctrl->evport =
xc_evtchn_bind_interdomain(evfd, domain, remote_port);
if (ctrl->evport < 0 || xc_evtchn_notify(evfd, ctrl->evport))
#ifdef XENCTRL_HAS_XC_INTERFACE
xc_evtchn_close(evfd);
#else
close(evfd);
#endif
else
ret = 0;
fail:
@ -250,7 +386,7 @@ static int client_interface_init(struct libvchan *ctrl, int domain, int devno)
/**
Run on the client side of connection (currently, must be dom0).
\returns NULL on failure (e.g. noone listening), handle on success
*/
*/
struct libvchan *libvchan_client_init(int domain, int devno)
{
struct libvchan *ctrl =
@ -264,3 +400,14 @@ struct libvchan *libvchan_client_init(int domain, int devno)
ctrl->is_server = 0;
return ctrl;
}
#else
// Windows domains can not be dom0
struct libvchan *libvchan_client_init(int domain, int devno)
{
return NULL;
}
#endif

View File

@ -20,8 +20,15 @@
*/
#include "libvchan.h"
#ifndef WINNT
#include <xenctrl.h>
#include <errno.h>
#include <sys/select.h>
#endif
#include <string.h>
/**
\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
*/
#ifdef WINNT
int libvchan_wait(struct libvchan *ctrl)
{
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))
return -1;
if (ret!=-1 && libvchan_is_eof(ctrl))
@ -75,6 +93,37 @@ int libvchan_wait(struct libvchan *ctrl)
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 write less data than requested;
@ -147,7 +196,7 @@ int libvchan_close(struct libvchan *ctrl)
}
/// 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);
}

View File

@ -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>
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>
typedef uint32_t VCHAN_RING_IDX;
@ -39,11 +61,12 @@ struct libvchan {
uint32_t ring_ref;
/// descriptor to event channel interface
#ifdef XENCTRL_HAS_XC_INTERFACE
xc_evtchn *evfd;
xc_evtchn *evfd;
#else
int evfd;
EVTCHN evfd;
#endif
int evport;
int devno;
VCHAN_RING_IDX *wr_cons, *wr_prod, *rd_cons, *rd_prod;
char *rd_ring, *wr_ring;
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);
int libvchan_server_handle_connected(struct libvchan *ctrl);
int libvchan_write(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_close(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_data_ready(struct libvchan *ctrl);
int libvchan_buffer_space(struct libvchan *ctrl);
#endif /* _LIBVCHAN_H */

22
vchan/sources Normal file
View 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

View File

@ -1 +1 @@
1.7.46
2.0.36