aboutsummaryrefslogtreecommitdiff
path: root/tools/hv
diff options
context:
space:
mode:
Diffstat (limited to 'tools/hv')
-rw-r--r--tools/hv/Makefile13
-rw-r--r--tools/hv/hv_fcopy_daemon.c197
-rw-r--r--tools/hv/hv_kvp_daemon.c141
-rw-r--r--tools/hv/hv_vss_daemon.c267
4 files changed, 571 insertions, 47 deletions
diff --git a/tools/hv/Makefile b/tools/hv/Makefile
new file mode 100644
index 00000000000..bd22f786a60
--- /dev/null
+++ b/tools/hv/Makefile
@@ -0,0 +1,13 @@
+# Makefile for Hyper-V tools
+
+CC = $(CROSS_COMPILE)gcc
+PTHREAD_LIBS = -lpthread
+WARNINGS = -Wall -Wextra
+CFLAGS = $(WARNINGS) -g $(PTHREAD_LIBS)
+
+all: hv_kvp_daemon hv_vss_daemon
+%: %.c
+ $(CC) $(CFLAGS) -o $@ $^
+
+clean:
+ $(RM) hv_kvp_daemon hv_vss_daemon
diff --git a/tools/hv/hv_fcopy_daemon.c b/tools/hv/hv_fcopy_daemon.c
new file mode 100644
index 00000000000..fba1c75aa48
--- /dev/null
+++ b/tools/hv/hv_fcopy_daemon.c
@@ -0,0 +1,197 @@
+/*
+ * An implementation of host to guest copy functionality for Linux.
+ *
+ * Copyright (C) 2014, Microsoft, Inc.
+ *
+ * Author : K. Y. Srinivasan <kys@microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <linux/hyperv.h>
+#include <syslog.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+static int target_fd;
+static char target_fname[W_MAX_PATH];
+
+static int hv_start_fcopy(struct hv_start_fcopy *smsg)
+{
+ int error = HV_E_FAIL;
+ char *q, *p;
+
+ /*
+ * If possile append a path seperator to the path.
+ */
+ if (strlen((char *)smsg->path_name) < (W_MAX_PATH - 2))
+ strcat((char *)smsg->path_name, "/");
+
+ p = (char *)smsg->path_name;
+ snprintf(target_fname, sizeof(target_fname), "%s/%s",
+ (char *)smsg->path_name, smsg->file_name);
+
+ syslog(LOG_INFO, "Target file name: %s", target_fname);
+ /*
+ * Check to see if the path is already in place; if not,
+ * create if required.
+ */
+ while ((q = strchr(p, '/')) != NULL) {
+ if (q == p) {
+ p++;
+ continue;
+ }
+ *q = '\0';
+ if (access((char *)smsg->path_name, F_OK)) {
+ if (smsg->copy_flags & CREATE_PATH) {
+ if (mkdir((char *)smsg->path_name, 0755)) {
+ syslog(LOG_ERR, "Failed to create %s",
+ (char *)smsg->path_name);
+ goto done;
+ }
+ } else {
+ syslog(LOG_ERR, "Invalid path: %s",
+ (char *)smsg->path_name);
+ goto done;
+ }
+ }
+ p = q + 1;
+ *q = '/';
+ }
+
+ if (!access(target_fname, F_OK)) {
+ syslog(LOG_INFO, "File: %s exists", target_fname);
+ if (!(smsg->copy_flags & OVER_WRITE)) {
+ error = HV_ERROR_ALREADY_EXISTS;
+ goto done;
+ }
+ }
+
+ target_fd = open(target_fname, O_RDWR | O_CREAT | O_CLOEXEC, 0744);
+ if (target_fd == -1) {
+ syslog(LOG_INFO, "Open Failed: %s", strerror(errno));
+ goto done;
+ }
+
+ error = 0;
+done:
+ return error;
+}
+
+static int hv_copy_data(struct hv_do_fcopy *cpmsg)
+{
+ ssize_t bytes_written;
+
+ bytes_written = pwrite(target_fd, cpmsg->data, cpmsg->size,
+ cpmsg->offset);
+
+ if (bytes_written != cpmsg->size)
+ return HV_E_FAIL;
+
+ return 0;
+}
+
+static int hv_copy_finished(void)
+{
+ close(target_fd);
+ return 0;
+}
+static int hv_copy_cancel(void)
+{
+ close(target_fd);
+ unlink(target_fname);
+ return 0;
+
+}
+
+int main(void)
+{
+ int fd, fcopy_fd, len;
+ int error;
+ int version = FCOPY_CURRENT_VERSION;
+ char *buffer[4096 * 2];
+ struct hv_fcopy_hdr *in_msg;
+
+ if (daemon(1, 0)) {
+ syslog(LOG_ERR, "daemon() failed; error: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ openlog("HV_FCOPY", 0, LOG_USER);
+ syslog(LOG_INFO, "HV_FCOPY starting; pid is:%d", getpid());
+
+ fcopy_fd = open("/dev/vmbus/hv_fcopy", O_RDWR);
+
+ if (fcopy_fd < 0) {
+ syslog(LOG_ERR, "open /dev/vmbus/hv_fcopy failed; error: %d %s",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ /*
+ * Register with the kernel.
+ */
+ if ((write(fcopy_fd, &version, sizeof(int))) != sizeof(int)) {
+ syslog(LOG_ERR, "Registration failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+
+ while (1) {
+ /*
+ * In this loop we process fcopy messages after the
+ * handshake is complete.
+ */
+ len = pread(fcopy_fd, buffer, (4096 * 2), 0);
+ if (len < 0) {
+ syslog(LOG_ERR, "pread failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ in_msg = (struct hv_fcopy_hdr *)buffer;
+
+ switch (in_msg->operation) {
+ case START_FILE_COPY:
+ error = hv_start_fcopy((struct hv_start_fcopy *)in_msg);
+ break;
+ case WRITE_TO_FILE:
+ error = hv_copy_data((struct hv_do_fcopy *)in_msg);
+ break;
+ case COMPLETE_FCOPY:
+ error = hv_copy_finished();
+ break;
+ case CANCEL_FCOPY:
+ error = hv_copy_cancel();
+ break;
+
+ default:
+ syslog(LOG_ERR, "Unknown operation: %d",
+ in_msg->operation);
+
+ }
+
+ if (pwrite(fcopy_fd, &error, sizeof(int), 0) != sizeof(int)) {
+ syslog(LOG_ERR, "pwrite failed: %s", strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+}
diff --git a/tools/hv/hv_kvp_daemon.c b/tools/hv/hv_kvp_daemon.c
index c800ea4c8bf..4088b816a3e 100644
--- a/tools/hv/hv_kvp_daemon.c
+++ b/tools/hv/hv_kvp_daemon.c
@@ -26,7 +26,6 @@
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/utsname.h>
-#include <linux/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
@@ -79,8 +78,6 @@ enum {
DNS
};
-static char kvp_send_buffer[4096];
-static char kvp_recv_buffer[4096 * 2];
static struct sockaddr_nl addr;
static int in_hand_shake = 1;
@@ -91,6 +88,7 @@ static char *processor_arch;
static char *os_build;
static char *os_version;
static char *lic_version = "Unknown version";
+static char full_domain_name[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
static struct utsname uts_buf;
/*
@@ -102,6 +100,10 @@ static struct utsname uts_buf;
#define MAX_FILE_NAME 100
#define ENTRIES_PER_BLOCK 50
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
struct kvp_record {
char key[HV_KVP_EXCHANGE_MAX_KEY_SIZE];
char value[HV_KVP_EXCHANGE_MAX_VALUE_SIZE];
@@ -123,7 +125,8 @@ static void kvp_acquire_lock(int pool)
fl.l_pid = getpid();
if (fcntl(kvp_file_info[pool].fd, F_SETLKW, &fl) == -1) {
- syslog(LOG_ERR, "Failed to acquire the lock pool: %d", pool);
+ syslog(LOG_ERR, "Failed to acquire the lock pool: %d; error: %d %s", pool,
+ errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
@@ -134,8 +137,8 @@ static void kvp_release_lock(int pool)
fl.l_pid = getpid();
if (fcntl(kvp_file_info[pool].fd, F_SETLK, &fl) == -1) {
- perror("fcntl");
- syslog(LOG_ERR, "Failed to release the lock pool: %d", pool);
+ syslog(LOG_ERR, "Failed to release the lock pool: %d; error: %d %s", pool,
+ errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
@@ -153,8 +156,9 @@ static void kvp_update_file(int pool)
filep = fopen(kvp_file_info[pool].fname, "we");
if (!filep) {
+ syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,
+ errno, strerror(errno));
kvp_release_lock(pool);
- syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
exit(EXIT_FAILURE);
}
@@ -184,8 +188,9 @@ static void kvp_update_mem_state(int pool)
filep = fopen(kvp_file_info[pool].fname, "re");
if (!filep) {
+ syslog(LOG_ERR, "Failed to open file, pool: %d; error: %d %s", pool,
+ errno, strerror(errno));
kvp_release_lock(pool);
- syslog(LOG_ERR, "Failed to open file, pool: %d", pool);
exit(EXIT_FAILURE);
}
for (;;) {
@@ -236,7 +241,8 @@ static int kvp_file_init(void)
if (access(KVP_CONFIG_LOC, F_OK)) {
if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) {
- syslog(LOG_ERR, " Failed to create %s", KVP_CONFIG_LOC);
+ syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC,
+ errno, strerror(errno));
exit(EXIT_FAILURE);
}
}
@@ -253,12 +259,15 @@ static int kvp_file_init(void)
filep = fopen(fname, "re");
- if (!filep)
+ if (!filep) {
+ close(fd);
return 1;
+ }
record = malloc(alloc_unit * num_blocks);
if (record == NULL) {
fclose(filep);
+ close(fd);
return 1;
}
for (;;) {
@@ -282,6 +291,7 @@ static int kvp_file_init(void)
num_blocks);
if (record == NULL) {
fclose(filep);
+ close(fd);
return 1;
}
continue;
@@ -761,7 +771,9 @@ static void kvp_process_ipconfig_file(char *cmd,
break;
x = strchr(p, '\n');
- *x = '\0';
+ if (x)
+ *x = '\0';
+
strcat(config_buf, p);
strcat(config_buf, ";");
}
@@ -1012,9 +1024,10 @@ kvp_get_ip_info(int family, char *if_name, int op,
if (sn_offset == 0)
strcpy(sn_str, cidr_mask);
- else
+ else {
+ strcat((char *)ip_buffer->sub_net, ";");
strcat(sn_str, cidr_mask);
- strcat((char *)ip_buffer->sub_net, ";");
+ }
sn_offset += strlen(sn_str) + 1;
}
@@ -1270,7 +1283,8 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
file = fopen(if_file, "w");
if (file == NULL) {
- syslog(LOG_ERR, "Failed to open config file");
+ syslog(LOG_ERR, "Failed to open config file; error: %d %s",
+ errno, strerror(errno));
return HV_E_FAIL;
}
@@ -1285,6 +1299,7 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
}
error = kvp_write_file(file, "HWADDR", "", mac_addr);
+ free(mac_addr);
if (error)
goto setval_error;
@@ -1330,7 +1345,6 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val)
goto setval_error;
setval_done:
- free(mac_addr);
fclose(file);
/*
@@ -1339,18 +1353,21 @@ setval_done:
*/
snprintf(cmd, sizeof(cmd), "%s %s", "hv_set_ifconfig", if_file);
- system(cmd);
+ if (system(cmd)) {
+ syslog(LOG_ERR, "Failed to execute cmd '%s'; error: %d %s",
+ cmd, errno, strerror(errno));
+ return HV_E_FAIL;
+ }
return 0;
setval_error:
syslog(LOG_ERR, "Failed to write config file");
- free(mac_addr);
fclose(file);
return error;
}
-static int
+static void
kvp_get_domain_name(char *buffer, int length)
{
struct addrinfo hints, *info ;
@@ -1364,34 +1381,29 @@ kvp_get_domain_name(char *buffer, int length)
error = getaddrinfo(buffer, NULL, &hints, &info);
if (error != 0) {
- strcpy(buffer, "getaddrinfo failed\n");
- return error;
+ snprintf(buffer, length, "getaddrinfo failed: 0x%x %s",
+ error, gai_strerror(error));
+ return;
}
- strcpy(buffer, info->ai_canonname);
+ snprintf(buffer, length, "%s", info->ai_canonname);
freeaddrinfo(info);
- return error;
}
static int
netlink_send(int fd, struct cn_msg *msg)
{
- struct nlmsghdr *nlh;
+ struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
unsigned int size;
struct msghdr message;
- char buffer[64];
struct iovec iov[2];
- size = NLMSG_SPACE(sizeof(struct cn_msg) + msg->len);
+ size = sizeof(struct cn_msg) + msg->len;
- nlh = (struct nlmsghdr *)buffer;
- nlh->nlmsg_seq = 0;
- nlh->nlmsg_pid = getpid();
- nlh->nlmsg_type = NLMSG_DONE;
- nlh->nlmsg_len = NLMSG_LENGTH(size - sizeof(*nlh));
- nlh->nlmsg_flags = 0;
+ nlh.nlmsg_pid = getpid();
+ nlh.nlmsg_len = NLMSG_LENGTH(size);
- iov[0].iov_base = nlh;
- iov[0].iov_len = sizeof(*nlh);
+ iov[0].iov_base = &nlh;
+ iov[0].iov_len = sizeof(nlh);
iov[1].iov_base = msg;
iov[1].iov_len = size;
@@ -1407,7 +1419,7 @@ netlink_send(int fd, struct cn_msg *msg)
int main(void)
{
- int fd, len, sock_opt;
+ int fd, len, nl_group;
int error;
struct cn_msg *message;
struct pollfd pfd;
@@ -1421,14 +1433,29 @@ int main(void)
int pool;
char *if_name;
struct hv_kvp_ipaddr_value *kvp_ip_val;
+ char *kvp_recv_buffer;
+ size_t kvp_recv_buffer_len;
- daemon(1, 0);
+ if (daemon(1, 0))
+ return 1;
openlog("KVP", 0, LOG_USER);
syslog(LOG_INFO, "KVP starting; pid is:%d", getpid());
+
+ kvp_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_kvp_msg);
+ kvp_recv_buffer = calloc(1, kvp_recv_buffer_len);
+ if (!kvp_recv_buffer) {
+ syslog(LOG_ERR, "Failed to allocate netlink buffer");
+ exit(EXIT_FAILURE);
+ }
/*
* Retrieve OS release information.
*/
kvp_get_os_info();
+ /*
+ * Cache Fully Qualified Domain Name because getaddrinfo takes an
+ * unpredictable amount of time to finish.
+ */
+ kvp_get_domain_name(full_domain_name, sizeof(full_domain_name));
if (kvp_file_init()) {
syslog(LOG_ERR, "Failed to initialize the pools");
@@ -1437,27 +1464,34 @@ int main(void)
fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
if (fd < 0) {
- syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd);
+ syslog(LOG_ERR, "netlink socket creation failed; error: %d %s", errno,
+ strerror(errno));
exit(EXIT_FAILURE);
}
addr.nl_family = AF_NETLINK;
addr.nl_pad = 0;
addr.nl_pid = 0;
- addr.nl_groups = CN_KVP_IDX;
+ addr.nl_groups = 0;
error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
if (error < 0) {
- syslog(LOG_ERR, "bind failed; error:%d", error);
+ syslog(LOG_ERR, "bind failed; error: %d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
- sock_opt = addr.nl_groups;
- setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt));
+ nl_group = CN_KVP_IDX;
+
+ if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) {
+ syslog(LOG_ERR, "setsockopt failed; error: %d %s", errno, strerror(errno));
+ close(fd);
+ exit(EXIT_FAILURE);
+ }
+
/*
* Register ourselves with the kernel.
*/
- message = (struct cn_msg *)kvp_send_buffer;
+ message = (struct cn_msg *)kvp_recv_buffer;
message->id.idx = CN_KVP_IDX;
message->id.val = CN_KVP_VAL;
@@ -1468,7 +1502,7 @@ int main(void)
len = netlink_send(fd, message);
if (len < 0) {
- syslog(LOG_ERR, "netlink_send failed; error:%d", len);
+ syslog(LOG_ERR, "netlink_send failed; error: %d %s", errno, strerror(errno));
close(fd);
exit(EXIT_FAILURE);
}
@@ -1480,9 +1514,18 @@ int main(void)
socklen_t addr_l = sizeof(addr);
pfd.events = POLLIN;
pfd.revents = 0;
- poll(&pfd, 1, -1);
- len = recvfrom(fd, kvp_recv_buffer, sizeof(kvp_recv_buffer), 0,
+ if (poll(&pfd, 1, -1) < 0) {
+ syslog(LOG_ERR, "poll failed; error: %d %s", errno, strerror(errno));
+ if (errno == EINVAL) {
+ close(fd);
+ exit(EXIT_FAILURE);
+ }
+ else
+ continue;
+ }
+
+ len = recvfrom(fd, kvp_recv_buffer, kvp_recv_buffer_len, 0,
addr_p, &addr_l);
if (len < 0) {
@@ -1499,6 +1542,10 @@ int main(void)
}
incoming_msg = (struct nlmsghdr *)kvp_recv_buffer;
+
+ if (incoming_msg->nlmsg_type != NLMSG_DONE)
+ continue;
+
incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
hv_msg = (struct hv_kvp_msg *)incoming_cn_msg->data;
@@ -1627,8 +1674,7 @@ int main(void)
switch (hv_msg->body.kvp_enum_data.index) {
case FullyQualifiedDomainName:
- kvp_get_domain_name(key_value,
- HV_KVP_EXCHANGE_MAX_VALUE_SIZE);
+ strcpy(key_value, full_domain_name);
strcpy(key_name, "FullyQualifiedDomainName");
break;
case IntegrationServicesVersion:
@@ -1687,7 +1733,8 @@ kvp_done:
len = netlink_send(fd, incoming_cn_msg);
if (len < 0) {
- syslog(LOG_ERR, "net_link send failed; error:%d", len);
+ syslog(LOG_ERR, "net_link send failed; error: %d %s", errno,
+ strerror(errno));
exit(EXIT_FAILURE);
}
}
diff --git a/tools/hv/hv_vss_daemon.c b/tools/hv/hv_vss_daemon.c
new file mode 100644
index 00000000000..6a213b8cd7b
--- /dev/null
+++ b/tools/hv/hv_vss_daemon.c
@@ -0,0 +1,267 @@
+/*
+ * An implementation of the host initiated guest snapshot for Hyper-V.
+ *
+ *
+ * Copyright (C) 2013, Microsoft, Inc.
+ * Author : K. Y. Srinivasan <kys@microsoft.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation.
+ *
+ * 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, GOOD TITLE or
+ * NON INFRINGEMENT. See the GNU General Public License for more
+ * details.
+ *
+ */
+
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/poll.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <mntent.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <linux/fs.h>
+#include <linux/connector.h>
+#include <linux/hyperv.h>
+#include <linux/netlink.h>
+#include <syslog.h>
+
+static struct sockaddr_nl addr;
+
+#ifndef SOL_NETLINK
+#define SOL_NETLINK 270
+#endif
+
+
+static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op)
+{
+ int ret, fd = open(dir, O_RDONLY);
+
+ if (fd < 0)
+ return 1;
+ ret = ioctl(fd, cmd, 0);
+ syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno));
+ close(fd);
+ return !!ret;
+}
+
+static int vss_operate(int operation)
+{
+ char *fs_op;
+ char match[] = "/dev/";
+ FILE *mounts;
+ struct mntent *ent;
+ unsigned int cmd;
+ int error = 0, root_seen = 0;
+
+ switch (operation) {
+ case VSS_OP_FREEZE:
+ cmd = FIFREEZE;
+ fs_op = "freeze";
+ break;
+ case VSS_OP_THAW:
+ cmd = FITHAW;
+ fs_op = "thaw";
+ break;
+ default:
+ return -1;
+ }
+
+ mounts = setmntent("/proc/mounts", "r");
+ if (mounts == NULL)
+ return -1;
+
+ while ((ent = getmntent(mounts))) {
+ if (strncmp(ent->mnt_fsname, match, strlen(match)))
+ continue;
+ if (strcmp(ent->mnt_type, "iso9660") == 0)
+ continue;
+ if (strcmp(ent->mnt_type, "vfat") == 0)
+ continue;
+ if (strcmp(ent->mnt_dir, "/") == 0) {
+ root_seen = 1;
+ continue;
+ }
+ error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op);
+ }
+ endmntent(mounts);
+
+ if (root_seen) {
+ error |= vss_do_freeze("/", cmd, fs_op);
+ }
+
+ return error;
+}
+
+static int netlink_send(int fd, struct cn_msg *msg)
+{
+ struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
+ unsigned int size;
+ struct msghdr message;
+ struct iovec iov[2];
+
+ size = sizeof(struct cn_msg) + msg->len;
+
+ nlh.nlmsg_pid = getpid();
+ nlh.nlmsg_len = NLMSG_LENGTH(size);
+
+ iov[0].iov_base = &nlh;
+ iov[0].iov_len = sizeof(nlh);
+
+ iov[1].iov_base = msg;
+ iov[1].iov_len = size;
+
+ memset(&message, 0, sizeof(message));
+ message.msg_name = &addr;
+ message.msg_namelen = sizeof(addr);
+ message.msg_iov = iov;
+ message.msg_iovlen = 2;
+
+ return sendmsg(fd, &message, 0);
+}
+
+int main(void)
+{
+ int fd, len, nl_group;
+ int error;
+ struct cn_msg *message;
+ struct pollfd pfd;
+ struct nlmsghdr *incoming_msg;
+ struct cn_msg *incoming_cn_msg;
+ int op;
+ struct hv_vss_msg *vss_msg;
+ char *vss_recv_buffer;
+ size_t vss_recv_buffer_len;
+
+ if (daemon(1, 0))
+ return 1;
+
+ openlog("Hyper-V VSS", 0, LOG_USER);
+ syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
+
+ vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
+ vss_recv_buffer = calloc(1, vss_recv_buffer_len);
+ if (!vss_recv_buffer) {
+ syslog(LOG_ERR, "Failed to allocate netlink buffers");
+ exit(EXIT_FAILURE);
+ }
+
+ fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
+ if (fd < 0) {
+ syslog(LOG_ERR, "netlink socket creation failed; error:%d %s",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ addr.nl_family = AF_NETLINK;
+ addr.nl_pad = 0;
+ addr.nl_pid = 0;
+ addr.nl_groups = 0;
+
+
+ error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
+ if (error < 0) {
+ syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno));
+ close(fd);
+ exit(EXIT_FAILURE);
+ }
+ nl_group = CN_VSS_IDX;
+ if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) {
+ syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno));
+ close(fd);
+ exit(EXIT_FAILURE);
+ }
+ /*
+ * Register ourselves with the kernel.
+ */
+ message = (struct cn_msg *)vss_recv_buffer;
+ message->id.idx = CN_VSS_IDX;
+ message->id.val = CN_VSS_VAL;
+ message->ack = 0;
+ vss_msg = (struct hv_vss_msg *)message->data;
+ vss_msg->vss_hdr.operation = VSS_OP_REGISTER;
+
+ message->len = sizeof(struct hv_vss_msg);
+
+ len = netlink_send(fd, message);
+ if (len < 0) {
+ syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno));
+ close(fd);
+ exit(EXIT_FAILURE);
+ }
+
+ pfd.fd = fd;
+
+ while (1) {
+ struct sockaddr *addr_p = (struct sockaddr *) &addr;
+ socklen_t addr_l = sizeof(addr);
+ pfd.events = POLLIN;
+ pfd.revents = 0;
+
+ if (poll(&pfd, 1, -1) < 0) {
+ syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
+ if (errno == EINVAL) {
+ close(fd);
+ exit(EXIT_FAILURE);
+ }
+ else
+ continue;
+ }
+
+ len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0,
+ addr_p, &addr_l);
+
+ if (len < 0) {
+ syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
+ addr.nl_pid, errno, strerror(errno));
+ close(fd);
+ return -1;
+ }
+
+ if (addr.nl_pid) {
+ syslog(LOG_WARNING,
+ "Received packet from untrusted pid:%u",
+ addr.nl_pid);
+ continue;
+ }
+
+ incoming_msg = (struct nlmsghdr *)vss_recv_buffer;
+
+ if (incoming_msg->nlmsg_type != NLMSG_DONE)
+ continue;
+
+ incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
+ vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data;
+ op = vss_msg->vss_hdr.operation;
+ error = HV_S_OK;
+
+ switch (op) {
+ case VSS_OP_FREEZE:
+ case VSS_OP_THAW:
+ error = vss_operate(op);
+ if (error)
+ error = HV_E_FAIL;
+ break;
+ default:
+ syslog(LOG_ERR, "Illegal op:%d\n", op);
+ }
+ vss_msg->error = error;
+ len = netlink_send(fd, incoming_cn_msg);
+ if (len < 0) {
+ syslog(LOG_ERR, "net_link send failed; error:%d %s",
+ errno, strerror(errno));
+ exit(EXIT_FAILURE);
+ }
+ }
+
+}