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/poll.h>
  23#include <sys/ioctl.h>
  24#include <fcntl.h>
  25#include <stdio.h>
  26#include <mntent.h>
  27#include <stdlib.h>
  28#include <unistd.h>
  29#include <string.h>
  30#include <ctype.h>
  31#include <errno.h>
  32#include <linux/fs.h>
  33#include <linux/hyperv.h>
  34#include <syslog.h>
  35#include <getopt.h>
  36
  37/* Don't use syslog() in the function since that can cause write to disk */
  38static int vss_do_freeze(char *dir, unsigned int cmd)
  39{
  40        int ret, fd = open(dir, O_RDONLY);
  41
  42        if (fd < 0)
  43                return 1;
  44
  45        ret = ioctl(fd, cmd, 0);
  46
  47        /*
  48         * If a partition is mounted more than once, only the first
  49         * FREEZE/THAW can succeed and the later ones will get
  50         * EBUSY/EINVAL respectively: there could be 2 cases:
  51         * 1) a user may mount the same partition to differnt directories
  52         *  by mistake or on purpose;
  53         * 2) The subvolume of btrfs appears to have the same partition
  54         * mounted more than once.
  55         */
  56        if (ret) {
  57                if ((cmd == FIFREEZE && errno == EBUSY) ||
  58                    (cmd == FITHAW && errno == EINVAL)) {
  59                        close(fd);
  60                        return 0;
  61                }
  62        }
  63
  64        close(fd);
  65        return !!ret;
  66}
  67
  68static int vss_operate(int operation)
  69{
  70        char match[] = "/dev/";
  71        FILE *mounts;
  72        struct mntent *ent;
  73        char errdir[1024] = {0};
  74        unsigned int cmd;
  75        int error = 0, root_seen = 0, save_errno = 0;
  76
  77        switch (operation) {
  78        case VSS_OP_FREEZE:
  79                cmd = FIFREEZE;
  80                break;
  81        case VSS_OP_THAW:
  82                cmd = FITHAW;
  83                break;
  84        default:
  85                return -1;
  86        }
  87
  88        mounts = setmntent("/proc/mounts", "r");
  89        if (mounts == NULL)
  90                return -1;
  91
  92        while ((ent = getmntent(mounts))) {
  93                if (strncmp(ent->mnt_fsname, match, strlen(match)))
  94                        continue;
  95                if (hasmntopt(ent, MNTOPT_RO) != NULL)
  96                        continue;
  97                if (strcmp(ent->mnt_type, "vfat") == 0)
  98                        continue;
  99                if (strcmp(ent->mnt_dir, "/") == 0) {
 100                        root_seen = 1;
 101                        continue;
 102                }
 103                error |= vss_do_freeze(ent->mnt_dir, cmd);
 104                if (error && operation == VSS_OP_FREEZE)
 105                        goto err;
 106        }
 107
 108        endmntent(mounts);
 109
 110        if (root_seen) {
 111                error |= vss_do_freeze("/", cmd);
 112                if (error && operation == VSS_OP_FREEZE)
 113                        goto err;
 114        }
 115
 116        goto out;
 117err:
 118        save_errno = errno;
 119        if (ent) {
 120                strncpy(errdir, ent->mnt_dir, sizeof(errdir)-1);
 121                endmntent(mounts);
 122        }
 123        vss_operate(VSS_OP_THAW);
 124        /* Call syslog after we thaw all filesystems */
 125        if (ent)
 126                syslog(LOG_ERR, "FREEZE of %s failed; error:%d %s",
 127                       errdir, save_errno, strerror(save_errno));
 128        else
 129                syslog(LOG_ERR, "FREEZE of / failed; error:%d %s", save_errno,
 130                       strerror(save_errno));
 131out:
 132        return error;
 133}
 134
 135void print_usage(char *argv[])
 136{
 137        fprintf(stderr, "Usage: %s [options]\n"
 138                "Options are:\n"
 139                "  -n, --no-daemon        stay in foreground, don't daemonize\n"
 140                "  -h, --help             print this help\n", argv[0]);
 141}
 142
 143int main(int argc, char *argv[])
 144{
 145        int vss_fd, len;
 146        int error;
 147        struct pollfd pfd;
 148        int     op;
 149        struct hv_vss_msg vss_msg[1];
 150        int daemonize = 1, long_index = 0, opt;
 151        int in_handshake = 1;
 152        __u32 kernel_modver;
 153
 154        static struct option long_options[] = {
 155                {"help",        no_argument,       0,  'h' },
 156                {"no-daemon",   no_argument,       0,  'n' },
 157                {0,             0,                 0,  0   }
 158        };
 159
 160        while ((opt = getopt_long(argc, argv, "hn", long_options,
 161                                  &long_index)) != -1) {
 162                switch (opt) {
 163                case 'n':
 164                        daemonize = 0;
 165                        break;
 166                case 'h':
 167                default:
 168                        print_usage(argv);
 169                        exit(EXIT_FAILURE);
 170                }
 171        }
 172
 173        if (daemonize && daemon(1, 0))
 174                return 1;
 175
 176        openlog("Hyper-V VSS", 0, LOG_USER);
 177        syslog(LOG_INFO, "VSS starting; pid is:%d", getpid());
 178
 179        vss_fd = open("/dev/vmbus/hv_vss", O_RDWR);
 180        if (vss_fd < 0) {
 181                syslog(LOG_ERR, "open /dev/vmbus/hv_vss failed; error: %d %s",
 182                       errno, strerror(errno));
 183                exit(EXIT_FAILURE);
 184        }
 185        /*
 186         * Register ourselves with the kernel.
 187         */
 188        vss_msg->vss_hdr.operation = VSS_OP_REGISTER1;
 189
 190        len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
 191        if (len < 0) {
 192                syslog(LOG_ERR, "registration to kernel failed; error: %d %s",
 193                       errno, strerror(errno));
 194                close(vss_fd);
 195                exit(EXIT_FAILURE);
 196        }
 197
 198        pfd.fd = vss_fd;
 199
 200        while (1) {
 201                pfd.events = POLLIN;
 202                pfd.revents = 0;
 203
 204                if (poll(&pfd, 1, -1) < 0) {
 205                        syslog(LOG_ERR, "poll failed; error:%d %s", errno, strerror(errno));
 206                        if (errno == EINVAL) {
 207                                close(vss_fd);
 208                                exit(EXIT_FAILURE);
 209                        }
 210                        else
 211                                continue;
 212                }
 213
 214                len = read(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
 215
 216                if (in_handshake) {
 217                        if (len != sizeof(kernel_modver)) {
 218                                syslog(LOG_ERR, "invalid version negotiation");
 219                                exit(EXIT_FAILURE);
 220                        }
 221                        kernel_modver = *(__u32 *)vss_msg;
 222                        in_handshake = 0;
 223                        syslog(LOG_INFO, "VSS: kernel module version: %d",
 224                               kernel_modver);
 225                        continue;
 226                }
 227
 228                if (len != sizeof(struct hv_vss_msg)) {
 229                        syslog(LOG_ERR, "read failed; error:%d %s",
 230                               errno, strerror(errno));
 231                        close(vss_fd);
 232                        return EXIT_FAILURE;
 233                }
 234
 235                op = vss_msg->vss_hdr.operation;
 236                error =  HV_S_OK;
 237
 238                switch (op) {
 239                case VSS_OP_FREEZE:
 240                case VSS_OP_THAW:
 241                        error = vss_operate(op);
 242                        syslog(LOG_INFO, "VSS: op=%s: %s\n",
 243                                op == VSS_OP_FREEZE ? "FREEZE" : "THAW",
 244                                error ? "failed" : "succeeded");
 245
 246                        if (error) {
 247                                error = HV_E_FAIL;
 248                                syslog(LOG_ERR, "op=%d failed!", op);
 249                                syslog(LOG_ERR, "report it with these files:");
 250                                syslog(LOG_ERR, "/etc/fstab and /proc/mounts");
 251                        }
 252                        break;
 253                default:
 254                        syslog(LOG_ERR, "Illegal op:%d\n", op);
 255                }
 256                vss_msg->error = error;
 257                len = write(vss_fd, vss_msg, sizeof(struct hv_vss_msg));
 258                if (len != sizeof(struct hv_vss_msg)) {
 259                        syslog(LOG_ERR, "write failed; error: %d %s", errno,
 260                               strerror(errno));
 261                        exit(EXIT_FAILURE);
 262                }
 263        }
 264
 265        close(vss_fd);
 266        exit(0);
 267}
 268