linux/tools/hv/hv_vss_daemon.c
<<
>>
Prefs
   1/*
   2 * An implementation of the host initiated guest snapshot for Hyper-V.
   3 *
   4 *
   5 * Copyright (C) 2013, Microsoft, Inc.
   6 * Author : K. Y. Srinivasan <kys@microsoft.com>
   7 *
   8 * This program is free software; you can redistribute it and/or modify it
   9 * under the terms of the GNU General Public License version 2 as published
  10 * by the Free Software Foundation.
  11 *
  12 * This program is distributed in the hope that it will be useful, but
  13 * WITHOUT ANY WARRANTY; without even the implied warranty of
  14 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or
  15 * NON INFRINGEMENT.  See the GNU General Public License for more
  16 * details.
  17 *
  18 */
  19
  20
  21#include <sys/types.h>
  22#include <sys/socket.h>
  23#include <sys/poll.h>
  24#include <sys/ioctl.h>
  25#include <fcntl.h>
  26#include <stdio.h>
  27#include <mntent.h>
  28#include <stdlib.h>
  29#include <unistd.h>
  30#include <string.h>
  31#include <ctype.h>
  32#include <errno.h>
  33#include <arpa/inet.h>
  34#include <linux/fs.h>
  35#include <linux/connector.h>
  36#include <linux/hyperv.h>
  37#include <linux/netlink.h>
  38#include <syslog.h>
  39
  40static struct sockaddr_nl addr;
  41
  42#ifndef SOL_NETLINK
  43#define SOL_NETLINK 270
  44#endif
  45
  46
  47static int vss_do_freeze(char *dir, unsigned int cmd, char *fs_op)
  48{
  49        int ret, fd = open(dir, O_RDONLY);
  50
  51        if (fd < 0)
  52                return 1;
  53        ret = ioctl(fd, cmd, 0);
  54        syslog(LOG_INFO, "VSS: %s of %s: %s\n", fs_op, dir, strerror(errno));
  55        close(fd);
  56        return !!ret;
  57}
  58
  59static int vss_operate(int operation)
  60{
  61        char *fs_op;
  62        char match[] = "/dev/";
  63        FILE *mounts;
  64        struct mntent *ent;
  65        unsigned int cmd;
  66        int error = 0, root_seen = 0;
  67
  68        switch (operation) {
  69        case VSS_OP_FREEZE:
  70                cmd = FIFREEZE;
  71                fs_op = "freeze";
  72                break;
  73        case VSS_OP_THAW:
  74                cmd = FITHAW;
  75                fs_op = "thaw";
  76                break;
  77        default:
  78                return -1;
  79        }
  80
  81        mounts = setmntent("/proc/mounts", "r");
  82        if (mounts == NULL)
  83                return -1;
  84
  85        while ((ent = getmntent(mounts))) {
  86                if (strncmp(ent->mnt_fsname, match, strlen(match)))
  87                        continue;
  88                if (strcmp(ent->mnt_type, "iso9660") == 0)
  89                        continue;
  90                if (strcmp(ent->mnt_type, "vfat") == 0)
  91                        continue;
  92                if (strcmp(ent->mnt_dir, "/") == 0) {
  93                        root_seen = 1;
  94                        continue;
  95                }
  96                error |= vss_do_freeze(ent->mnt_dir, cmd, fs_op);
  97        }
  98        endmntent(mounts);
  99
 100        if (root_seen) {
 101                error |= vss_do_freeze("/", cmd, fs_op);
 102        }
 103
 104        return error;
 105}
 106
 107static int netlink_send(int fd, struct cn_msg *msg)
 108{
 109        struct nlmsghdr nlh = { .nlmsg_type = NLMSG_DONE };
 110        unsigned int size;
 111        struct msghdr message;
 112        struct iovec iov[2];
 113
 114        size = sizeof(struct cn_msg) + msg->len;
 115
 116        nlh.nlmsg_pid = getpid();
 117        nlh.nlmsg_len = NLMSG_LENGTH(size);
 118
 119        iov[0].iov_base = &nlh;
 120        iov[0].iov_len = sizeof(nlh);
 121
 122        iov[1].iov_base = msg;
 123        iov[1].iov_len = size;
 124
 125        memset(&message, 0, sizeof(message));
 126        message.msg_name = &addr;
 127        message.msg_namelen = sizeof(addr);
 128        message.msg_iov = iov;
 129        message.msg_iovlen = 2;
 130
 131        return sendmsg(fd, &message, 0);
 132}
 133
 134int main(void)
 135{
 136        int fd, len, nl_group;
 137        int error;
 138        struct cn_msg *message;
 139        struct pollfd pfd;
 140        struct nlmsghdr *incoming_msg;
 141        struct cn_msg   *incoming_cn_msg;
 142        int     op;
 143        struct hv_vss_msg *vss_msg;
 144        char *vss_recv_buffer;
 145        size_t vss_recv_buffer_len;
 146
 147        if (daemon(1, 0))
 148                return 1;
 149
 150        openlog("Hyper-V VSS", 0, LOG_USER);
 151        syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
 152
 153        vss_recv_buffer_len = NLMSG_LENGTH(0) + sizeof(struct cn_msg) + sizeof(struct hv_vss_msg);
 154        vss_recv_buffer = calloc(1, vss_recv_buffer_len);
 155        if (!vss_recv_buffer) {
 156                syslog(LOG_ERR, "Failed to allocate netlink buffers");
 157                exit(EXIT_FAILURE);
 158        }
 159
 160        fd = socket(AF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);
 161        if (fd < 0) {
 162                syslog(LOG_ERR, "netlink socket creation failed; error:%d %s",
 163                                errno, strerror(errno));
 164                exit(EXIT_FAILURE);
 165        }
 166        addr.nl_family = AF_NETLINK;
 167        addr.nl_pad = 0;
 168        addr.nl_pid = 0;
 169        addr.nl_groups = 0;
 170
 171
 172        error = bind(fd, (struct sockaddr *)&addr, sizeof(addr));
 173        if (error < 0) {
 174                syslog(LOG_ERR, "bind failed; error:%d %s", errno, strerror(errno));
 175                close(fd);
 176                exit(EXIT_FAILURE);
 177        }
 178        nl_group = CN_VSS_IDX;
 179        if (setsockopt(fd, SOL_NETLINK, NETLINK_ADD_MEMBERSHIP, &nl_group, sizeof(nl_group)) < 0) {
 180                syslog(LOG_ERR, "setsockopt failed; error:%d %s", errno, strerror(errno));
 181                close(fd);
 182                exit(EXIT_FAILURE);
 183        }
 184        /*
 185         * Register ourselves with the kernel.
 186         */
 187        message = (struct cn_msg *)vss_recv_buffer;
 188        message->id.idx = CN_VSS_IDX;
 189        message->id.val = CN_VSS_VAL;
 190        message->ack = 0;
 191        vss_msg = (struct hv_vss_msg *)message->data;
 192        vss_msg->vss_hdr.operation = VSS_OP_REGISTER;
 193
 194        message->len = sizeof(struct hv_vss_msg);
 195
 196        len = netlink_send(fd, message);
 197        if (len < 0) {
 198                syslog(LOG_ERR, "netlink_send failed; error:%d %s", errno, strerror(errno));
 199                close(fd);
 200                exit(EXIT_FAILURE);
 201        }
 202
 203        pfd.fd = fd;
 204
 205        while (1) {
 206                struct sockaddr *addr_p = (struct sockaddr *) &addr;
 207                socklen_t addr_l = sizeof(addr);
 208                pfd.events = POLLIN;
 209                pfd.revents = 0;
 210
 211                if (poll(&pfd, 1, -1) < 0) {
 212                        syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
 213                        if (errno == EINVAL) {
 214                                close(fd);
 215                                exit(EXIT_FAILURE);
 216                        }
 217                        else
 218                                continue;
 219                }
 220
 221                len = recvfrom(fd, vss_recv_buffer, vss_recv_buffer_len, 0,
 222                                addr_p, &addr_l);
 223
 224                if (len < 0) {
 225                        syslog(LOG_ERR, "recvfrom failed; pid:%u error:%d %s",
 226                                        addr.nl_pid, errno, strerror(errno));
 227                        close(fd);
 228                        return -1;
 229                }
 230
 231                if (addr.nl_pid) {
 232                        syslog(LOG_WARNING,
 233                                "Received packet from untrusted pid:%u",
 234                                addr.nl_pid);
 235                        continue;
 236                }
 237
 238                incoming_msg = (struct nlmsghdr *)vss_recv_buffer;
 239
 240                if (incoming_msg->nlmsg_type != NLMSG_DONE)
 241                        continue;
 242
 243                incoming_cn_msg = (struct cn_msg *)NLMSG_DATA(incoming_msg);
 244                vss_msg = (struct hv_vss_msg *)incoming_cn_msg->data;
 245                op = vss_msg->vss_hdr.operation;
 246                error =  HV_S_OK;
 247
 248                switch (op) {
 249                case VSS_OP_FREEZE:
 250                case VSS_OP_THAW:
 251                        error = vss_operate(op);
 252                        if (error)
 253                                error = HV_E_FAIL;
 254                        break;
 255                default:
 256                        syslog(LOG_ERR, "Illegal op:%d\n", op);
 257                }
 258                vss_msg->error = error;
 259                len = netlink_send(fd, incoming_cn_msg);
 260                if (len < 0) {
 261                        syslog(LOG_ERR, "net_link send failed; error:%d %s",
 262                                        errno, strerror(errno));
 263                        exit(EXIT_FAILURE);
 264                }
 265        }
 266
 267}
 268