diff options
Diffstat (limited to 'tools/hv')
| -rw-r--r-- | tools/hv/Makefile | 13 | ||||
| -rw-r--r-- | tools/hv/hv_fcopy_daemon.c | 197 | ||||
| -rw-r--r-- | tools/hv/hv_kvp_daemon.c | 218 | ||||
| -rwxr-xr-x | tools/hv/hv_set_ifconfig.sh | 24 | ||||
| -rw-r--r-- | tools/hv/hv_vss_daemon.c | 267 |
5 files changed, 619 insertions, 100 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 d25a46925e6..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,17 +88,22 @@ 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; /* * The location of the interface configuration file. */ -#define KVP_CONFIG_LOC "/var/opt/" +#define KVP_CONFIG_LOC "/var/lib/hyperv" #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); } } @@ -151,10 +154,11 @@ static void kvp_update_file(int pool) */ kvp_acquire_lock(pool); - filep = fopen(kvp_file_info[pool].fname, "w"); + 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); } @@ -182,10 +186,11 @@ static void kvp_update_mem_state(int pool) kvp_acquire_lock(pool); - filep = fopen(kvp_file_info[pool].fname, "r"); + 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 (;;) { @@ -234,9 +239,10 @@ static int kvp_file_init(void) int i; int alloc_unit = sizeof(struct kvp_record) * ENTRIES_PER_BLOCK; - if (access("/var/opt/hyperv", F_OK)) { - if (mkdir("/var/opt/hyperv", S_IRUSR | S_IWUSR | S_IROTH)) { - syslog(LOG_ERR, " Failed to create /var/opt/hyperv"); + if (access(KVP_CONFIG_LOC, F_OK)) { + if (mkdir(KVP_CONFIG_LOC, 0755 /* rwxr-xr-x */)) { + syslog(LOG_ERR, "Failed to create '%s'; error: %d %s", KVP_CONFIG_LOC, + errno, strerror(errno)); exit(EXIT_FAILURE); } } @@ -245,20 +251,23 @@ static int kvp_file_init(void) fname = kvp_file_info[i].fname; records_read = 0; num_blocks = 1; - sprintf(fname, "/var/opt/hyperv/.kvp_pool_%d", i); - fd = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IROTH); + sprintf(fname, "%s/.kvp_pool_%d", KVP_CONFIG_LOC, i); + fd = open(fname, O_RDWR | O_CREAT | O_CLOEXEC, 0644 /* rw-r--r-- */); if (fd == -1) return 1; - filep = fopen(fname, "r"); - if (!filep) + filep = fopen(fname, "re"); + 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; } @@ -1162,16 +1175,13 @@ static int process_ip_string(FILE *f, char *ip_string, int type) snprintf(str, sizeof(str), "%s", "DNS"); break; } - if (i != 0) { - if (type != DNS) { - snprintf(sub_str, sizeof(sub_str), - "_%d", i++); - } else { - snprintf(sub_str, sizeof(sub_str), - "%d", ++i); - } - } else if (type == DNS) { + + if (type == DNS) { snprintf(sub_str, sizeof(sub_str), "%d", ++i); + } else if (type == GATEWAY && i == 0) { + ++i; + } else { + snprintf(sub_str, sizeof(sub_str), "%d", i++); } @@ -1191,17 +1201,13 @@ static int process_ip_string(FILE *f, char *ip_string, int type) snprintf(str, sizeof(str), "%s", "DNS"); break; } - if ((j != 0) || (type == DNS)) { - if (type != DNS) { - snprintf(sub_str, sizeof(sub_str), - "_%d", j++); - } else { - snprintf(sub_str, sizeof(sub_str), - "%d", ++i); - } - } else if (type == DNS) { - snprintf(sub_str, sizeof(sub_str), - "%d", ++i); + + if (type == DNS) { + snprintf(sub_str, sizeof(sub_str), "%d", ++i); + } else if (j == 0) { + ++j; + } else { + snprintf(sub_str, sizeof(sub_str), "_%d", j++); } } else { return HV_INVALIDARG; @@ -1244,18 +1250,19 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * Here is the format of the ip configuration file: * * HWADDR=macaddr - * IF_NAME=interface name - * DHCP=yes (This is optional; if yes, DHCP is configured) + * DEVICE=interface name + * BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured + * or "none" if no boot-time protocol should be used) * - * IPADDR=ipaddr1 - * IPADDR_1=ipaddr2 - * IPADDR_x=ipaddry (where y = x + 1) + * IPADDR0=ipaddr1 + * IPADDR1=ipaddr2 + * IPADDRx=ipaddry (where y = x + 1) * - * NETMASK=netmask1 - * NETMASK_x=netmasky (where y = x + 1) + * NETMASK0=netmask1 + * NETMASKx=netmasky (where y = x + 1) * * GATEWAY=ipaddr1 - * GATEWAY_x=ipaddry (where y = x + 1) + * GATEWAYx=ipaddry (where y = x + 1) * * DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) * @@ -1271,12 +1278,13 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) */ snprintf(if_file, sizeof(if_file), "%s%s%s", KVP_CONFIG_LOC, - "hyperv/ifcfg-", if_name); + "/ifcfg-", if_name); 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; } @@ -1291,15 +1299,16 @@ 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; - error = kvp_write_file(file, "IF_NAME", "", if_name); + error = kvp_write_file(file, "DEVICE", "", if_name); if (error) goto setval_error; if (new_val->dhcp_enabled) { - error = kvp_write_file(file, "DHCP", "", "yes"); + error = kvp_write_file(file, "BOOTPROTO", "", "dhcp"); if (error) goto setval_error; @@ -1307,6 +1316,11 @@ static int kvp_set_ip_info(char *if_name, struct hv_kvp_ipaddr_value *new_val) * We are done!. */ goto setval_done; + + } else { + error = kvp_write_file(file, "BOOTPROTO", "", "none"); + if (error) + goto setval_error; } /* @@ -1331,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); /* @@ -1340,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 ; @@ -1365,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; @@ -1408,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; @@ -1422,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"); @@ -1438,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); + } + 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); } - sock_opt = addr.nl_groups; - setsockopt(fd, 270, 1, &sock_opt, sizeof(sock_opt)); + /* * 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; @@ -1469,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); } @@ -1481,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) { @@ -1500,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; @@ -1628,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: @@ -1688,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_set_ifconfig.sh b/tools/hv/hv_set_ifconfig.sh index 3e9427e08d8..735aafd64a3 100755 --- a/tools/hv/hv_set_ifconfig.sh +++ b/tools/hv/hv_set_ifconfig.sh @@ -20,18 +20,19 @@ # Here is the format of the ip configuration file: # # HWADDR=macaddr -# IF_NAME=interface name -# DHCP=yes (This is optional; if yes, DHCP is configured) +# DEVICE=interface name +# BOOTPROTO=<protocol> (where <protocol> is "dhcp" if DHCP is configured +# or "none" if no boot-time protocol should be used) # -# IPADDR=ipaddr1 -# IPADDR_1=ipaddr2 -# IPADDR_x=ipaddry (where y = x + 1) +# IPADDR0=ipaddr1 +# IPADDR1=ipaddr2 +# IPADDRx=ipaddry (where y = x + 1) # -# NETMASK=netmask1 -# NETMASK_x=netmasky (where y = x + 1) +# NETMASK0=netmask1 +# NETMASKx=netmasky (where y = x + 1) # # GATEWAY=ipaddr1 -# GATEWAY_x=ipaddry (where y = x + 1) +# GATEWAYx=ipaddry (where y = x + 1) # # DNSx=ipaddrx (where first DNS address is tagged as DNS1 etc) # @@ -53,11 +54,6 @@ echo "NM_CONTROLLED=no" >> $1 echo "PEERDNS=yes" >> $1 echo "ONBOOT=yes" >> $1 -dhcp=$(grep "DHCP" $1 2>/dev/null) -if [ "$dhcp" != "" ]; -then -echo "BOOTPROTO=dhcp" >> $1; -fi cp $1 /etc/sysconfig/network-scripts/ @@ -65,4 +61,4 @@ cp $1 /etc/sysconfig/network-scripts/ interface=$(echo $1 | awk -F - '{ print $2 }') /sbin/ifdown $interface 2>/dev/null -/sbin/ifup $interfac 2>/dev/null +/sbin/ifup $interface 2>/dev/null 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); + } + } + +} |
