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
|
||||
/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
|
||||
)
|
||||
|
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)
|
||||
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 &
|
||||
}
|
||||
|
@ -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 = {}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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:
|
||||
|
@ -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))
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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):
|
||||
|
@ -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;
|
||||
|
@ -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:
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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),
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -3,4 +3,4 @@
|
||||
|
||||
## 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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
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
|
||||
|
||||
|
239
vchan/init.c
239
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 <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
|
||||
|
53
vchan/io.c
53
vchan/io.c
@ -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);
|
||||
}
|
||||
|
@ -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
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