diff --git a/hyper-v.changes b/hyper-v.changes index 2bc1ec1..900f544 100644 --- a/hyper-v.changes +++ b/hyper-v.changes @@ -1,3 +1,9 @@ +------------------------------------------------------------------- +Fri Mar 22 17:23:36 CET 2013 - ohering@suse.de + +- add hv_vss_daemon (fate#314921) + helper to support host initiated backup + ------------------------------------------------------------------- Fri Mar 22 16:56:57 CET 2013 - ohering@suse.de diff --git a/hyper-v.include.linux.hyperv.h b/hyper-v.include.linux.hyperv.h index 2e9f44a..d120550 100644 --- a/hyper-v.include.linux.hyperv.h +++ b/hyper-v.include.linux.hyperv.h @@ -27,6 +27,63 @@ #include + +/* + * Implementation of host controlled snapshot of the guest. + */ + +#define VSS_OP_REGISTER 128 + +enum hv_vss_op { + VSS_OP_CREATE = 0, + VSS_OP_DELETE, + VSS_OP_HOT_BACKUP, + VSS_OP_GET_DM_INFO, + VSS_OP_BU_COMPLETE, + /* + * Following operations are only supported with IC version >= 5.0 + */ + VSS_OP_FREEZE, /* Freeze the file systems in the VM */ + VSS_OP_THAW, /* Unfreeze the file systems */ + VSS_OP_AUTO_RECOVER, + VSS_OP_COUNT /* Number of operations, must be last */ +}; + + +/* + * Header for all VSS messages. + */ +struct hv_vss_hdr { + __u8 operation; + __u8 reserved[7]; +} __attribute__((packed)); + + +/* + * Flag values for the hv_vss_check_feature. Linux supports only + * one value. + */ +#define VSS_HBU_NO_AUTO_RECOVERY 0x00000005 + +struct hv_vss_check_feature { + __u32 flags; +} __attribute__((packed)); + +struct hv_vss_check_dm_info { + __u32 flags; +} __attribute__((packed)); + +struct hv_vss_msg { + union { + struct hv_vss_hdr vss_hdr; + int error; + }; + union { + struct hv_vss_check_feature vss_cf; + struct hv_vss_check_dm_info dm_info; + }; +} __attribute__((packed)); + /* * An implementation of HyperV key value pair (KVP) functionality for Linux. * diff --git a/hyper-v.init.vss.sh b/hyper-v.init.vss.sh new file mode 100644 index 0000000..a714976 --- /dev/null +++ b/hyper-v.init.vss.sh @@ -0,0 +1,82 @@ +#!/bin/sh +# +# LSB compatible service control script; see http://www.linuxbase.org/spec/ +# +### BEGIN INIT INFO +# Provides: hv_vss_daemon +# Required-Start: $null +# Should-Start: $syslog $remote_fs $time +# Required-Stop: $null +# Should-Stop: $syslog $remote_fs $time +# Default-Start: 3 5 +# Default-Stop: 0 1 2 6 +# Short-Description: hv_vss_daemon assists with host initiated backup +# Description: Start hv_vss_daemon to allow the host to snapshot this guest +### END INIT INFO + +# Check for missing binaries (stale symlinks should not happen) +# Note: Special treatment of stop for LSB conformance +HV_VSS_BIN=/usr/sbin/hv_vss_daemon +test -x $HV_VSS_BIN || { echo "$HV_VSS_BIN not installed"; + if [ "$1" = "stop" ]; then exit 0; + else exit 5; fi; } + +. /etc/rc.status + +# Reset status of this service +rc_reset + +case "$1" in + start) + echo -n "Starting Hyper-V VSS daemon " + env PATH=/usr/lib/hyper-v/bin:$PATH \ + startproc $HV_VSS_BIN + rc_status -v + ;; + stop) + echo -n "Shutting down Hyper-V VSS daemon " + killproc -TERM $HV_VSS_BIN + rc_status -v + ;; + try-restart|condrestart) + if test "$1" = "condrestart"; then + echo "${attn} Use try-restart ${done}(LSB)${attn} rather than condrestart ${warn}(RH)${norm}" + fi + $0 status + if test $? = 0; then + $0 restart + else + rc_reset # Not running is not a failure. + fi + # Remember status and be quiet + rc_status + ;; + restart) + ## Stop the service and regardless of whether it was + ## running or not, start it again. + $0 stop + $0 start + + # Remember status and be quiet + rc_status + ;; + force-reload) + echo -n "Reload service Hyper-V VSS daemon " + $0 try-restart + rc_status + ;; + reload) + rc_failed 3 + rc_status -v + ;; + status) + echo -n "Checking for service Hyper-V VSS daemon " + checkproc $HV_VSS_BIN + rc_status -v + ;; + *) + echo "Usage: $0 {start|stop|status|try-restart|restart|force-reload|reload}" + exit 1 + ;; +esac +rc_exit diff --git a/hyper-v.spec b/hyper-v.spec index ab63cd1..f872518 100644 --- a/hyper-v.spec +++ b/hyper-v.spec @@ -17,6 +17,7 @@ %define hv_kvp_daemon hv_kvp_daemon +%define hv_vss_daemon hv_vss_daemon Name: hyper-v ExclusiveArch: %ix86 x86_64 @@ -34,6 +35,8 @@ Source5: hyper-v.kvptest.ps1.txt Source9: hyper-v.include.linux.hyperv.h Source10: hyper-v.tools.hv.hv_kvp_daemon.c Source11: hyper-v.init.sh +Source12: hyper-v.tools.hv.hv_vss_daemon.c +Source13: hyper-v.init.vss.sh Source20: hyper-v.tools.hv.hv_get_dhcp_info.sh Source21: hyper-v.tools.hv.hv_get_dns_info.sh Source22: hyper-v.tools.hv.hv_set_ifconfig.sh @@ -48,9 +51,11 @@ This package contains the Microsoft Hyper-V tools. cp -avL %{S:5} kvptest.ps1.txt cp -vL %{S:9} %{hv_kvp_daemon}.h cp -vL %{S:10} %{hv_kvp_daemon}.c +cp -vL %{S:12} %{hv_vss_daemon}.c %build sed -i~ '/#include /d' %{hv_kvp_daemon}.c +sed -i~ '/#include /d' %{hv_vss_daemon}.c gcc \ $RPM_OPT_FLAGS \ -Wno-unused-variable \ @@ -62,10 +67,22 @@ gcc \ -DCN_KVP_IDX=0x9 \ -DCN_KVP_VAL=0x1 \ -o %{hv_kvp_daemon} +gcc \ + $RPM_OPT_FLAGS \ + -Wno-unused-variable \ + -Wno-pointer-sign \ + -D_GNU_SOURCE \ + -g \ + %{hv_vss_daemon}.c \ + -include %{hv_kvp_daemon}.h \ + -DCN_VSS_IDX=0xa \ + -DCN_VSS_VAL=0x1 \ + -o %{hv_vss_daemon} %install mkdir -p $RPM_BUILD_ROOT/usr/sbin install -m755 %{hv_kvp_daemon} $RPM_BUILD_ROOT/usr/sbin +install -m755 %{hv_vss_daemon} $RPM_BUILD_ROOT/usr/sbin mkdir -p $RPM_BUILD_ROOT/usr/lib/%{name}/bin cp -avL %{S:20} $RPM_BUILD_ROOT/usr/lib/%{name}/bin/hv_get_dhcp_info cp -avL %{S:21} $RPM_BUILD_ROOT/usr/lib/%{name}/bin/hv_get_dns_info @@ -74,13 +91,14 @@ chmod 755 $RPM_BUILD_ROOT/usr/lib/%{name}/bin/* mkdir -p $RPM_BUILD_ROOT/etc/init.d install -m755 %{S:11} $RPM_BUILD_ROOT/etc/init.d/%{hv_kvp_daemon} ln -sfvbn ../../etc/init.d/%{hv_kvp_daemon} $RPM_BUILD_ROOT/usr/sbin/rc%{hv_kvp_daemon} +install -m755 %{S:13} $RPM_BUILD_ROOT/etc/init.d/%{hv_vss_daemon} +ln -sfvbn ../../etc/init.d/%{hv_vss_daemon} $RPM_BUILD_ROOT/usr/sbin/rc%{hv_vss_daemon} %files %defattr (-,root,root) %doc kvptest.ps1.txt -/etc/init.d/%{hv_kvp_daemon} -/usr/sbin/rc%{hv_kvp_daemon} -/usr/sbin/%{hv_kvp_daemon} +/etc/init.d/* +/usr/sbin/* /usr/lib/%{name} %pre @@ -121,10 +139,13 @@ if test "${board_vendor}" = "Microsoft Corporation" -a "${product_name}" = "Virt then echo "Enabling %{hv_kvp_daemon} on '${product_name}' from '${board_vendor}'" %{insserv_force_if_yast %{hv_kvp_daemon}} + echo "Enabling %{hv_vss_daemon} on '${product_name}' from '${board_vendor}'" + %{insserv_force_if_yast %{hv_vss_daemon}} fi %preun %stop_on_removal %{hv_kvp_daemon} +%stop_on_removal %{hv_vss_daemon} %postun # no restart on update because the daemon can not be restarted diff --git a/hyper-v.tools.hv.hv_vss_daemon.c b/hyper-v.tools.hv.hv_vss_daemon.c new file mode 100644 index 0000000..cd4f5c0 --- /dev/null +++ b/hyper-v.tools.hv.hv_vss_daemon.c @@ -0,0 +1,225 @@ +/* + * An implementation of the host initiated guest snapshot for Hyper-V. + * + * + * Copyright (C) 2013, Microsoft, Inc. + * Author : K. Y. Srinivasan + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static char vss_recv_buffer[4096]; +static char vss_send_buffer[4096]; +static struct sockaddr_nl addr; + +#ifndef SOL_NETLINK +#define SOL_NETLINK 270 +#endif + + +static int vss_operate(int operation) +{ + char *fs_op; + char cmd[512]; + char buf[512]; + FILE *file; + char *p; + char *x; + int error = -1; + + switch (operation) { + case VSS_OP_FREEZE: + fs_op = "-f "; + break; + case VSS_OP_THAW: + fs_op = "-u "; + break; + default: + goto out; + } + + file = popen("mount | awk '/^\\/dev\\// { print $3}'", "r"); + if (file == NULL) + goto out; + + while ((p = fgets(buf, sizeof(buf), file)) != NULL) { + x = strchr(p, '\n'); + *x = '\0'; + if (!strncmp(p, "/", sizeof("/"))) + continue; + + sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, p); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error = system(cmd); + } + pclose(file); + + sprintf(cmd, "%s %s %s", "fsfreeze ", fs_op, "/"); + syslog(LOG_INFO, "VSS cmd is %s\n", cmd); + error = system(cmd); + +out: + return error; +} + +static int netlink_send(int fd, struct cn_msg *msg) +{ + struct nlmsghdr *nlh; + unsigned int size; + struct msghdr message; + char buffer[64]; + struct iovec iov[2]; + + size = NLMSG_SPACE(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; + + 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; + + if (daemon(1, 0)) + return 1; + + openlog("Hyper-V VSS", 0, LOG_USER); + syslog(LOG_INFO, "VSS starting; pid is:%d", getpid()); + + fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR); + if (fd < 0) { + syslog(LOG_ERR, "netlink socket creation failed; error:%d", fd); + 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", error); + close(fd); + exit(EXIT_FAILURE); + } + nl_group = CN_VSS_IDX; + setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)); + /* + * Register ourselves with the kernel. + */ + message = (struct cn_msg *)vss_send_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", len); + 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; + poll(&pfd, 1, -1); + + len = recvfrom(fd, vss_recv_buffer, sizeof(vss_recv_buffer), 0, + addr_p, &addr_l); + + if (len < 0 || addr.nl_pid) { + syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s", + addr.nl_pid, errno, strerror(errno)); + close(fd); + return -1; + } + + 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", len); + exit(EXIT_FAILURE); + } + } + +}