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