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