mirror of
https://gitlab.com/libvirt/libvirt.git
synced 2024-11-09 23:10:08 +00:00
tools: Introduce SSH proxy
This allows users to SSH into a domain with a VSOCK device: ssh user@qemu/machineName So far, only QEMU domains are supported AND qemu:///system is looked for the first for 'machineName' followed by qemu:///session. I took an inspiration from Systemd's ssh proxy [1] [2]. To just work out of the box, it requires (yet unreleased) systemd to be running inside the guest to set up a socket activated SSHD on the VSOCK. Alternatively, users can set up the socket activation themselves, or just run a socat that'll forward vsock <-> TCP communication. 1: https://github.com/systemd/systemd/blob/main/src/ssh-generator/ssh-proxy.c 2: https://github.com/systemd/systemd/blob/main/src/ssh-generator/20-systemd-ssh-proxy.conf.in Resolves: https://gitlab.com/libvirt/libvirt/-/issues/579 Signed-off-by: Michal Privoznik <mprivozn@redhat.com> Reviewed-by: Ján Tomko <jtomko@redhat.com>
This commit is contained in:
parent
9e59ba56c8
commit
0287b5dfd2
@ -91,6 +91,7 @@
|
|||||||
# Other optional features
|
# Other optional features
|
||||||
%define with_numactl 0%{!?_without_numactl:1}
|
%define with_numactl 0%{!?_without_numactl:1}
|
||||||
%define with_userfaultfd_sysctl 0%{!?_without_userfaultfd_sysctl:1}
|
%define with_userfaultfd_sysctl 0%{!?_without_userfaultfd_sysctl:1}
|
||||||
|
%define with_ssh_proxy 0%{!?_without_ssh_proxy:1}
|
||||||
|
|
||||||
# A few optional bits off by default, we enable later
|
# A few optional bits off by default, we enable later
|
||||||
%define with_fuse 0
|
%define with_fuse 0
|
||||||
@ -903,6 +904,9 @@ Requires: libvirt-daemon-driver-nodedev = %{version}-%{release}
|
|||||||
Requires: libvirt-daemon-driver-nwfilter = %{version}-%{release}
|
Requires: libvirt-daemon-driver-nwfilter = %{version}-%{release}
|
||||||
Requires: libvirt-daemon-driver-secret = %{version}-%{release}
|
Requires: libvirt-daemon-driver-secret = %{version}-%{release}
|
||||||
Requires: libvirt-daemon-driver-storage = %{version}-%{release}
|
Requires: libvirt-daemon-driver-storage = %{version}-%{release}
|
||||||
|
%if %{with_ssh_proxy}
|
||||||
|
Requires: libvirt-ssh-proxy = %{version}-%{release}
|
||||||
|
%endif
|
||||||
Requires: qemu
|
Requires: qemu
|
||||||
|
|
||||||
%description daemon-qemu
|
%description daemon-qemu
|
||||||
@ -931,6 +935,9 @@ Requires: libvirt-daemon-driver-nodedev = %{version}-%{release}
|
|||||||
Requires: libvirt-daemon-driver-nwfilter = %{version}-%{release}
|
Requires: libvirt-daemon-driver-nwfilter = %{version}-%{release}
|
||||||
Requires: libvirt-daemon-driver-secret = %{version}-%{release}
|
Requires: libvirt-daemon-driver-secret = %{version}-%{release}
|
||||||
Requires: libvirt-daemon-driver-storage = %{version}-%{release}
|
Requires: libvirt-daemon-driver-storage = %{version}-%{release}
|
||||||
|
%if %{with_ssh_proxy}
|
||||||
|
Requires: libvirt-ssh-proxy = %{version}-%{release}
|
||||||
|
%endif
|
||||||
Requires: qemu-kvm
|
Requires: qemu-kvm
|
||||||
|
|
||||||
%description daemon-kvm
|
%description daemon-kvm
|
||||||
@ -1018,6 +1025,9 @@ Summary: Client side utilities of the libvirt library
|
|||||||
Requires: libvirt-libs = %{version}-%{release}
|
Requires: libvirt-libs = %{version}-%{release}
|
||||||
# Needed by virt-pki-validate script.
|
# Needed by virt-pki-validate script.
|
||||||
Requires: gnutls-utils
|
Requires: gnutls-utils
|
||||||
|
%if %{with_ssh_proxy}
|
||||||
|
Recommends: libvirt-ssh-proxy = %{version}-%{release}
|
||||||
|
%endif
|
||||||
|
|
||||||
# Ensure smooth upgrades
|
# Ensure smooth upgrades
|
||||||
Obsoletes: libvirt-bash-completion < 7.3.0
|
Obsoletes: libvirt-bash-completion < 7.3.0
|
||||||
@ -1100,6 +1110,15 @@ Requires: libvirt-daemon-driver-network = %{version}-%{release}
|
|||||||
Libvirt plugin for NSS for translating domain names into IP addresses.
|
Libvirt plugin for NSS for translating domain names into IP addresses.
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with_ssh_proxy}
|
||||||
|
%package ssh-proxy
|
||||||
|
Summary: Libvirt SSH proxy
|
||||||
|
Requires: libvirt-libs = %{version}-%{release}
|
||||||
|
|
||||||
|
%description ssh-proxy
|
||||||
|
Allows SSH into domains via VSOCK without need for network.
|
||||||
|
%endif
|
||||||
|
|
||||||
%if %{with_mingw32}
|
%if %{with_mingw32}
|
||||||
%package -n mingw32-libvirt
|
%package -n mingw32-libvirt
|
||||||
Summary: %{summary}
|
Summary: %{summary}
|
||||||
@ -1291,6 +1310,12 @@ exit 1
|
|||||||
%define arg_userfaultfd_sysctl -Duserfaultfd_sysctl=disabled
|
%define arg_userfaultfd_sysctl -Duserfaultfd_sysctl=disabled
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
|
%if %{with_ssh_proxy}
|
||||||
|
%define arg_ssh_proxy -Dssh_proxy=enabled
|
||||||
|
%else
|
||||||
|
%define arg_ssh_proxy -Dssh_proxy=disabled
|
||||||
|
%endif
|
||||||
|
|
||||||
%define when %(date +"%%F-%%T")
|
%define when %(date +"%%F-%%T")
|
||||||
%define where %(hostname)
|
%define where %(hostname)
|
||||||
%define who %{?packager}%{!?packager:Unknown}
|
%define who %{?packager}%{!?packager:Unknown}
|
||||||
@ -1372,6 +1397,7 @@ export SOURCE_DATE_EPOCH=$(stat --printf='%Y' %{_specdir}/libvirt.spec)
|
|||||||
-Dtls_priority=%{tls_priority} \
|
-Dtls_priority=%{tls_priority} \
|
||||||
-Dsysctl_config=enabled \
|
-Dsysctl_config=enabled \
|
||||||
%{?arg_userfaultfd_sysctl} \
|
%{?arg_userfaultfd_sysctl} \
|
||||||
|
%{?arg_ssh_proxy} \
|
||||||
%{?enable_werror} \
|
%{?enable_werror} \
|
||||||
-Dexpensive_tests=enabled \
|
-Dexpensive_tests=enabled \
|
||||||
-Dinit_script=systemd \
|
-Dinit_script=systemd \
|
||||||
@ -1456,6 +1482,7 @@ export SOURCE_DATE_EPOCH=$(stat --printf='%Y' %{_specdir}/libvirt.spec)
|
|||||||
-Dstorage_zfs=disabled \
|
-Dstorage_zfs=disabled \
|
||||||
-Dsysctl_config=disabled \
|
-Dsysctl_config=disabled \
|
||||||
-Duserfaultfd_sysctl=disabled \
|
-Duserfaultfd_sysctl=disabled \
|
||||||
|
-Dssh_proxy=disabled \
|
||||||
-Dtests=disabled \
|
-Dtests=disabled \
|
||||||
-Dudev=disabled \
|
-Dudev=disabled \
|
||||||
-Dwireshark_dissector=disabled \
|
-Dwireshark_dissector=disabled \
|
||||||
@ -2426,6 +2453,12 @@ exit 0
|
|||||||
%{_libdir}/libnss_libvirt.so.2
|
%{_libdir}/libnss_libvirt.so.2
|
||||||
%{_libdir}/libnss_libvirt_guest.so.2
|
%{_libdir}/libnss_libvirt_guest.so.2
|
||||||
|
|
||||||
|
%if %{with_ssh_proxy}
|
||||||
|
%files ssh-proxy
|
||||||
|
%config(noreplace) %{_sysconfdir}/ssh/ssh_config.d/30-libvirt-ssh-proxy.conf
|
||||||
|
%{_libexecdir}/libvirt-ssh-proxy
|
||||||
|
%endif
|
||||||
|
|
||||||
%if %{with_lxc}
|
%if %{with_lxc}
|
||||||
%files login-shell
|
%files login-shell
|
||||||
%attr(4750, root, virtlogin) %{_bindir}/virt-login-shell
|
%attr(4750, root, virtlogin) %{_bindir}/virt-login-shell
|
||||||
|
16
meson.build
16
meson.build
@ -113,6 +113,11 @@ endif
|
|||||||
confdir = sysconfdir / meson.project_name()
|
confdir = sysconfdir / meson.project_name()
|
||||||
pkgdatadir = datadir / meson.project_name()
|
pkgdatadir = datadir / meson.project_name()
|
||||||
|
|
||||||
|
sshconfdir = get_option('sshconfdir')
|
||||||
|
if sshconfdir == ''
|
||||||
|
sshconfdir = sysconfdir / 'ssh' / 'ssh_config.d'
|
||||||
|
endif
|
||||||
|
|
||||||
|
|
||||||
# generate configmake.h header
|
# generate configmake.h header
|
||||||
|
|
||||||
@ -690,12 +695,14 @@ if host_machine.system() == 'linux'
|
|||||||
symbols += [
|
symbols += [
|
||||||
# process management
|
# process management
|
||||||
[ 'sys/syscall.h', 'SYS_pidfd_open' ],
|
[ 'sys/syscall.h', 'SYS_pidfd_open' ],
|
||||||
|
# vsock
|
||||||
|
[ 'linux/vm_sockets.h', 'struct sockaddr_vm', '#include <sys/socket.h>' ],
|
||||||
]
|
]
|
||||||
endif
|
endif
|
||||||
|
|
||||||
foreach symbol : symbols
|
foreach symbol : symbols
|
||||||
if cc.has_header_symbol(symbol[0], symbol[1], args: '-D_GNU_SOURCE', prefix: symbol.get(2, ''))
|
if cc.has_header_symbol(symbol[0], symbol[1], args: '-D_GNU_SOURCE', prefix: symbol.get(2, ''))
|
||||||
conf.set('WITH_DECL_@0@'.format(symbol[1].to_upper()), 1)
|
conf.set('WITH_DECL_@0@'.format(symbol[1].underscorify().to_upper()), 1)
|
||||||
endif
|
endif
|
||||||
endforeach
|
endforeach
|
||||||
|
|
||||||
@ -2033,6 +2040,12 @@ if not get_option('pm_utils').disabled()
|
|||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if not get_option('ssh_proxy').disabled() and conf.has('WITH_DECL_STRUCT_SOCKADDR_VM')
|
||||||
|
conf.set('WITH_SSH_PROXY', 1)
|
||||||
|
elif get_option('ssh_proxy').enabled()
|
||||||
|
error('ssh proxy requires vm_sockets.h which wasn\'t found')
|
||||||
|
endif
|
||||||
|
|
||||||
if not get_option('sysctl_config').disabled() and host_machine.system() == 'linux'
|
if not get_option('sysctl_config').disabled() and host_machine.system() == 'linux'
|
||||||
conf.set('WITH_SYSCTL', 1)
|
conf.set('WITH_SYSCTL', 1)
|
||||||
elif get_option('sysctl_config').enabled()
|
elif get_option('sysctl_config').enabled()
|
||||||
@ -2344,6 +2357,7 @@ misc_summary = {
|
|||||||
'virt-login-shell': conf.has('WITH_LOGIN_SHELL'),
|
'virt-login-shell': conf.has('WITH_LOGIN_SHELL'),
|
||||||
'virt-host-validate': conf.has('WITH_HOST_VALIDATE'),
|
'virt-host-validate': conf.has('WITH_HOST_VALIDATE'),
|
||||||
'TLS priority': conf.get_unquoted('TLS_PRIORITY'),
|
'TLS priority': conf.get_unquoted('TLS_PRIORITY'),
|
||||||
|
'SSH proxy': conf.has('WITH_SSH_PROXY'),
|
||||||
'sysctl config': conf.has('WITH_SYSCTL'),
|
'sysctl config': conf.has('WITH_SYSCTL'),
|
||||||
'userfaultfd sysctl': conf.has('WITH_USERFAULTFD_SYSCTL'),
|
'userfaultfd sysctl': conf.has('WITH_USERFAULTFD_SYSCTL'),
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ option('sanlock', type: 'feature', value: 'auto', description: 'sanlock support'
|
|||||||
option('sasl', type: 'feature', value: 'auto', description: 'sasl support')
|
option('sasl', type: 'feature', value: 'auto', description: 'sasl support')
|
||||||
option('selinux', type: 'feature', value: 'auto', description: 'selinux support')
|
option('selinux', type: 'feature', value: 'auto', description: 'selinux support')
|
||||||
option('selinux_mount', type: 'string', value: '', description: 'set SELinux mount point')
|
option('selinux_mount', type: 'string', value: '', description: 'set SELinux mount point')
|
||||||
|
option('sshconfdir', type: 'string', value: '', description: 'directory for SSH client configuration')
|
||||||
# dep:pciaccess
|
# dep:pciaccess
|
||||||
option('udev', type: 'feature', value: 'auto', description: 'udev support')
|
option('udev', type: 'feature', value: 'auto', description: 'udev support')
|
||||||
# dep:driver_remote
|
# dep:driver_remote
|
||||||
@ -126,6 +127,7 @@ option('nbdkit', type: 'feature', value: 'auto', description: 'Build nbdkit stor
|
|||||||
# dep:nbdkit
|
# dep:nbdkit
|
||||||
option('nbdkit_config_default', type: 'feature', value: 'auto', description: 'Whether to use nbdkit storage backend for network disks by default (configurable)')
|
option('nbdkit_config_default', type: 'feature', value: 'auto', description: 'Whether to use nbdkit storage backend for network disks by default (configurable)')
|
||||||
option('pm_utils', type: 'feature', value: 'auto', description: 'use pm-utils for power management')
|
option('pm_utils', type: 'feature', value: 'auto', description: 'use pm-utils for power management')
|
||||||
|
option('ssh_proxy', type: 'feature', value: 'auto', description: 'Build ssh-proxy for ssh over vsock')
|
||||||
option('sysctl_config', type: 'feature', value: 'auto', description: 'Whether to install sysctl configs')
|
option('sysctl_config', type: 'feature', value: 'auto', description: 'Whether to install sysctl configs')
|
||||||
# dep:sysctl_config
|
# dep:sysctl_config
|
||||||
option('userfaultfd_sysctl', type: 'feature', value: 'auto', description: 'Whether to install sysctl config for enabling unprivileged userfaultfd')
|
option('userfaultfd_sysctl', type: 'feature', value: 'auto', description: 'Whether to install sysctl config for enabling unprivileged userfaultfd')
|
||||||
|
@ -359,6 +359,7 @@ src/vz/vz_utils.c
|
|||||||
src/vz/vz_utils.h
|
src/vz/vz_utils.h
|
||||||
tests/virpolkittest.c
|
tests/virpolkittest.c
|
||||||
tools/libvirt-guests.sh.in
|
tools/libvirt-guests.sh.in
|
||||||
|
tools/ssh-proxy/ssh-proxy.c
|
||||||
tools/virsh-backup.c
|
tools/virsh-backup.c
|
||||||
tools/virsh-checkpoint.c
|
tools/virsh-checkpoint.c
|
||||||
tools/virsh-completer-host.c
|
tools/virsh-completer-host.c
|
||||||
|
@ -343,3 +343,5 @@ endif
|
|||||||
if wireshark_dep.found()
|
if wireshark_dep.found()
|
||||||
subdir('wireshark')
|
subdir('wireshark')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
subdir('ssh-proxy')
|
||||||
|
6
tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in
Normal file
6
tools/ssh-proxy/30-libvirt-ssh-proxy.conf.in
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
# SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
|
||||||
|
Host qemu/* qemu:system/* qemu:session/*
|
||||||
|
ProxyCommand @libexecdir@/libvirt-ssh-proxy %h %p
|
||||||
|
ProxyUseFdpass yes
|
||||||
|
CheckHostIP no
|
25
tools/ssh-proxy/meson.build
Normal file
25
tools/ssh-proxy/meson.build
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
if conf.has('WITH_SSH_PROXY')
|
||||||
|
executable(
|
||||||
|
'libvirt-ssh-proxy',
|
||||||
|
[
|
||||||
|
'ssh-proxy.c'
|
||||||
|
],
|
||||||
|
dependencies: [
|
||||||
|
src_dep,
|
||||||
|
],
|
||||||
|
link_with: [
|
||||||
|
libvirt_lib,
|
||||||
|
],
|
||||||
|
install: true,
|
||||||
|
install_dir: libexecdir,
|
||||||
|
install_rpath: libvirt_rpath,
|
||||||
|
)
|
||||||
|
|
||||||
|
configure_file(
|
||||||
|
input : '30-libvirt-ssh-proxy.conf.in',
|
||||||
|
output: '@BASENAME@',
|
||||||
|
configuration: tools_conf,
|
||||||
|
install: true,
|
||||||
|
install_dir : sshconfdir,
|
||||||
|
)
|
||||||
|
endif
|
296
tools/ssh-proxy/ssh-proxy.c
Normal file
296
tools/ssh-proxy/ssh-proxy.c
Normal file
@ -0,0 +1,296 @@
|
|||||||
|
/*
|
||||||
|
* SPDX-License-Identifier: LGPL-2.1-or-later
|
||||||
|
*
|
||||||
|
* For given domain and port create a VSOCK socket and pass it onto STDOUT.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <config.h>
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <linux/vm_sockets.h>
|
||||||
|
|
||||||
|
#include "internal.h"
|
||||||
|
#include "virsocket.h"
|
||||||
|
#include "virstring.h"
|
||||||
|
#include "virfile.h"
|
||||||
|
#include "datatypes.h"
|
||||||
|
#include "virgettext.h"
|
||||||
|
#include "virxml.h"
|
||||||
|
|
||||||
|
#define VIR_FROM_THIS VIR_FROM_NONE
|
||||||
|
|
||||||
|
#define SYS_ERROR(...) \
|
||||||
|
do { \
|
||||||
|
int err = errno; \
|
||||||
|
fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, " : %s\n", g_strerror(err)); \
|
||||||
|
fprintf(stderr, "\n"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define ERROR(...) \
|
||||||
|
do { \
|
||||||
|
fprintf(stderr, "ERROR %s:%d : ", __FUNCTION__, __LINE__); \
|
||||||
|
fprintf(stderr, __VA_ARGS__); \
|
||||||
|
fprintf(stderr, "\n"); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
#define HOSTNAME_PREFIX "qemu"
|
||||||
|
#define QEMU_SYSTEM_URI "qemu:///system"
|
||||||
|
#define QEMU_SESSION_URI "qemu:///session"
|
||||||
|
|
||||||
|
static void
|
||||||
|
dummyErrorHandler(void *opaque G_GNUC_UNUSED,
|
||||||
|
virErrorPtr error G_GNUC_UNUSED)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
printUsage(const char *argv0)
|
||||||
|
{
|
||||||
|
const char *progname;
|
||||||
|
|
||||||
|
if (!(progname = strrchr(argv0, '/')))
|
||||||
|
progname = argv0;
|
||||||
|
else
|
||||||
|
progname++;
|
||||||
|
|
||||||
|
printf(_("\n"
|
||||||
|
"Usage:\n"
|
||||||
|
"%1$s hostname port\n"
|
||||||
|
"\n"
|
||||||
|
"Hostname should be in one of the following forms:\n"
|
||||||
|
"\n"
|
||||||
|
" qemu:system/$domname\t\tfor domains under qemu:///system\n"
|
||||||
|
" qemu:session/$domname\t\tfor domains under qemu:///session\n"
|
||||||
|
" qemu/$domname\t\t\ttries looking up $domname under system followed by session URI\n"),
|
||||||
|
progname);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
parseArgs(int argc,
|
||||||
|
char *argv[],
|
||||||
|
const char **uriRet,
|
||||||
|
const char **domname,
|
||||||
|
unsigned int *port)
|
||||||
|
{
|
||||||
|
const char *uri = NULL;
|
||||||
|
|
||||||
|
/* Accepted URIs are:
|
||||||
|
*
|
||||||
|
* qemu/virtualMachine
|
||||||
|
* qemu:system/virtualMachine
|
||||||
|
* qemu:session/virtualMachine
|
||||||
|
*
|
||||||
|
* The last two result in system or session connection URIs passed to
|
||||||
|
* virConnectOpen(), the first one tries to find the machine under system
|
||||||
|
* connection first, followed by session connection.
|
||||||
|
*/
|
||||||
|
if (argc != 3 ||
|
||||||
|
!(uri = STRSKIP(argv[1], HOSTNAME_PREFIX))) {
|
||||||
|
ERROR(_("Bad usage"));
|
||||||
|
printUsage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*uri == ':') {
|
||||||
|
const char *tmp = NULL;
|
||||||
|
|
||||||
|
uri++;
|
||||||
|
if ((tmp = STRSKIP(uri, "system"))) {
|
||||||
|
*uriRet = QEMU_SYSTEM_URI;
|
||||||
|
} else if ((tmp = STRSKIP(uri, "session"))) {
|
||||||
|
*uriRet = QEMU_SESSION_URI;
|
||||||
|
} else {
|
||||||
|
ERROR(_("Unknown connection URI: '%1$s'"), uri);
|
||||||
|
printUsage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
uri = tmp;
|
||||||
|
} else {
|
||||||
|
*uriRet = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(*domname = STRSKIP(uri, "/")) ||
|
||||||
|
**domname == '\0') {
|
||||||
|
ERROR(_("Bad usage"));
|
||||||
|
printUsage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (virStrToLong_ui(argv[2], NULL, 10, port) < 0) {
|
||||||
|
ERROR(_("Unable to parse port: %1$s"), argv[2]);
|
||||||
|
printUsage(argv[0]);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#define VSOCK_CID_XPATH "/domain/devices/vsock/cid"
|
||||||
|
|
||||||
|
static int
|
||||||
|
extractCID(virDomainPtr dom,
|
||||||
|
unsigned long long *cidRet)
|
||||||
|
{
|
||||||
|
g_autofree char *domxml = NULL;
|
||||||
|
g_autoptr(xmlDoc) doc = NULL;
|
||||||
|
g_autoptr(xmlXPathContext) ctxt = NULL;
|
||||||
|
g_autofree xmlNodePtr *nodes = NULL;
|
||||||
|
int nnodes = 0;
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
if (!(domxml = virDomainGetXMLDesc(dom, 0)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
doc = virXMLParseStringCtxtWithIndent(domxml, "domain", &ctxt);
|
||||||
|
if (!doc)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if ((nnodes = virXPathNodeSet(VSOCK_CID_XPATH, ctxt, &nodes)) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < nnodes; i++) {
|
||||||
|
unsigned long long cid;
|
||||||
|
|
||||||
|
if (virXMLPropULongLong(nodes[i], "address", 10, 0, &cid) > 0) {
|
||||||
|
*cidRet = cid;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef VSOCK_CID_XPATH
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
lookupDomainAndFetchCID(const char *uri,
|
||||||
|
const char *domname,
|
||||||
|
unsigned long long *cid)
|
||||||
|
{
|
||||||
|
g_autoptr(virConnect) conn = NULL;
|
||||||
|
g_autoptr(virDomain) dom = NULL;
|
||||||
|
|
||||||
|
if (!(conn = virConnectOpenReadOnly(uri)))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
dom = virDomainLookupByName(conn, domname);
|
||||||
|
if (!dom)
|
||||||
|
dom = virDomainLookupByUUIDString(conn, domname);
|
||||||
|
if (!dom) {
|
||||||
|
int id;
|
||||||
|
|
||||||
|
if (virStrToLong_i(domname, NULL, 10, &id) >= 0)
|
||||||
|
dom = virDomainLookupByID(conn, id);
|
||||||
|
}
|
||||||
|
if (!dom)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return extractCID(dom, cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
findDomain(const char *domname,
|
||||||
|
unsigned long long *cid)
|
||||||
|
{
|
||||||
|
const char *uris[] = {QEMU_SYSTEM_URI, QEMU_SESSION_URI};
|
||||||
|
const uid_t userid = geteuid();
|
||||||
|
size_t i;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS(uris); i++) {
|
||||||
|
if (userid == 0 &&
|
||||||
|
STREQ(uris[i], "qemu:///session")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (lookupDomainAndFetchCID(uris[i], domname, cid) >= 0)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int
|
||||||
|
processVsock(const char *uri,
|
||||||
|
const char *domname,
|
||||||
|
unsigned int port)
|
||||||
|
{
|
||||||
|
struct sockaddr_vm sa = {
|
||||||
|
.svm_family = AF_VSOCK,
|
||||||
|
.svm_port = port,
|
||||||
|
};
|
||||||
|
VIR_AUTOCLOSE fd = -1;
|
||||||
|
unsigned long long cid = -1;
|
||||||
|
|
||||||
|
if (uri) {
|
||||||
|
lookupDomainAndFetchCID(uri, domname, &cid);
|
||||||
|
} else {
|
||||||
|
findDomain(domname, &cid);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cid == -1) {
|
||||||
|
ERROR(_("No usable vsock found"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sa.svm_cid = cid;
|
||||||
|
|
||||||
|
fd = socket(AF_VSOCK, SOCK_STREAM | SOCK_CLOEXEC, 0);
|
||||||
|
if (fd < 0) {
|
||||||
|
SYS_ERROR(_("Failed to allocate AF_VSOCK socket"));
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connect(fd, (const struct sockaddr *)&sa, sizeof(sa)) < 0) {
|
||||||
|
SYS_ERROR(_("Failed to connect to vsock (cid=%1$llu port=%2$u)"),
|
||||||
|
cid, port);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* OpenSSH wants us to send a single byte along with the file descriptor,
|
||||||
|
* hence do so. */
|
||||||
|
if (virSocketSendFD(STDOUT_FILENO, fd) < 0) {
|
||||||
|
SYS_ERROR(_("Failed to send file descriptor %1$d"), fd);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
const char *uri = NULL;
|
||||||
|
const char *domname = NULL;
|
||||||
|
unsigned int port;
|
||||||
|
|
||||||
|
if (virGettextInitialize() < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
if (virInitialize() < 0) {
|
||||||
|
ERROR(_("Failed to initialize libvirt"));
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
virSetErrorFunc(NULL, dummyErrorHandler);
|
||||||
|
|
||||||
|
if (parseArgs(argc, argv, &uri, &domname, &port) < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
if (processVsock(uri, domname, port) < 0)
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user