#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "hv_snapshot.h" #define UNDEF_FREEZE_THAW (0) #define FREEZE (1) #define THAW (2) #define VSS_LOG(priority, format, args...) do { \ if (is_debugging == 1) { \ if (is_daemon == 1) \ syslog(priority, format, ## args); \ else \ printf(format, ## args); \ } else { \ if (priority < LOG_DEBUG) { \ if (is_daemon == 1) \ syslog(priority, format, ## args); \ else \ printf(format, ## args); \ } \ } \ } while(0) static int is_daemon = 1; static int is_debugging = 0; static int g_ufs_suspend_handle = -1; static const char *dev = "/dev"; static int check(void) { struct statfs *mntbuf, *statfsp; int mntsize; int i; mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); if (mntsize == 0) { VSS_LOG(LOG_ERR, "There is no mount information\n"); return (EINVAL); } for (i = mntsize - 1; i >= 0; --i) { statfsp = &mntbuf[i]; if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { continue; /* skip to freeze '/dev' */ } else if (statfsp->f_flags & MNT_RDONLY) { continue; /* skip to freeze RDONLY partition */ } else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { return (EPERM); /* only UFS can be freezed */ } } return (0); } static int freeze(void) { struct statfs *mntbuf, *statfsp; int mntsize; int error = 0; int i; g_ufs_suspend_handle = open(_PATH_UFSSUSPEND, O_RDWR); if (g_ufs_suspend_handle == -1) { VSS_LOG(LOG_ERR, "unable to open %s", _PATH_UFSSUSPEND); return (errno); } mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); if (mntsize == 0) { VSS_LOG(LOG_ERR, "There is no mount information\n"); return (EINVAL); } for (i = mntsize - 1; i >= 0; --i) { statfsp = &mntbuf[i]; if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { continue; /* skip to freeze '/dev' */ } else if (statfsp->f_flags & MNT_RDONLY) { continue; /* skip to freeze RDONLY partition */ } else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { continue; /* only UFS can be freezed */ } error = ioctl(g_ufs_suspend_handle, UFSSUSPEND, &statfsp->f_fsid); if (error != 0) { VSS_LOG(LOG_ERR, "error: %d\n", errno); error = errno; } else { VSS_LOG(LOG_INFO, "Successfully suspend fs: %s\n", statfsp->f_mntonname); } } return (error); } /** * close the opened handle will thaw the FS. */ static int thaw(void) { int error = 0; if (g_ufs_suspend_handle != -1) { error = close(g_ufs_suspend_handle); if (!error) { g_ufs_suspend_handle = -1; VSS_LOG(LOG_INFO, "Successfully thaw the fs\n"); } else { error = errno; VSS_LOG(LOG_ERR, "Fail to thaw the fs: " "%d %s\n", errno, strerror(errno)); } } else { VSS_LOG(LOG_INFO, "The fs has already been thawed\n"); } return (error); } static void usage(const char* cmd) { fprintf(stderr, "%s: daemon for UFS file system freeze/thaw\n" " -d : enable debug log printing. Default is disabled.\n" " -n : run as a regular process instead of a daemon. Default is a daemon.\n" " -h : print usage.\n", cmd); exit(1); } int main(int argc, char* argv[]) { struct hv_vss_opt_msg userdata; struct pollfd hv_vss_poll_fd[1]; uint32_t op; int ch, r, error; int hv_vss_dev_fd; while ((ch = getopt(argc, argv, "dnh")) != -1) { switch (ch) { case 'n': /* Run as regular process for debugging purpose. */ is_daemon = 0; break; case 'd': /* Generate debugging output */ is_debugging = 1; break; case 'h': default: usage(argv[0]); break; } } openlog("HV_VSS", 0, LOG_USER); /* Become daemon first. */ if (is_daemon == 1) daemon(1, 0); else VSS_LOG(LOG_DEBUG, "Run as regular process.\n"); VSS_LOG(LOG_INFO, "HV_VSS starting; pid is: %d\n", getpid()); memset(&userdata, 0, sizeof(struct hv_vss_opt_msg)); /* register the daemon */ hv_vss_dev_fd = open(VSS_DEV(FS_VSS_DEV_NAME), O_RDWR); if (hv_vss_dev_fd < 0) { VSS_LOG(LOG_ERR, "Fail to open %s, error: %d %s\n", VSS_DEV(FS_VSS_DEV_NAME), errno, strerror(errno)); exit(EXIT_FAILURE); } hv_vss_poll_fd[0].fd = hv_vss_dev_fd; hv_vss_poll_fd[0].events = POLLIN | POLLRDNORM; while (1) { r = poll(hv_vss_poll_fd, 1, INFTIM); VSS_LOG(LOG_DEBUG, "poll returned r = %d, revent = 0x%x\n", r, hv_vss_poll_fd[0].revents); if (r == 0 || (r < 0 && errno == EAGAIN) || (r < 0 && errno == EINTR)) { /* Nothing to read */ continue; } if (r < 0) { /* * For poll return failure other than EAGAIN, * we want to exit. */ VSS_LOG(LOG_ERR, "Poll failed.\n"); perror("poll"); exit(EIO); } /* Read from character device */ error = ioctl(hv_vss_dev_fd, IOCHVVSSREAD, &userdata); if (error < 0) { VSS_LOG(LOG_ERR, "Read failed.\n"); perror("pread"); exit(EIO); } if (userdata.status != 0) { VSS_LOG(LOG_ERR, "data read error\n"); continue; } /* * We will use the KVP header information to pass back * the error from this daemon. So, first save the op * and pool info to local variables. */ op = userdata.opt; switch (op) { case HV_VSS_CHECK: error = check(); break; case HV_VSS_FREEZE: error = freeze(); break; case HV_VSS_THAW: error = thaw(); break; default: VSS_LOG(LOG_ERR, "Illegal operation: %d\n", op); error = VSS_FAIL; } if (error) userdata.status = VSS_FAIL; else userdata.status = VSS_SUCCESS; error = ioctl(hv_vss_dev_fd, IOCHVVSSWRITE, &userdata); if (error != 0) { VSS_LOG(LOG_ERR, "Fail to write to device\n"); exit(EXIT_FAILURE); } else { VSS_LOG(LOG_INFO, "Send response %d for %s to kernel\n", userdata.status, op == HV_VSS_FREEZE ? "Freeze" : (op == HV_VSS_THAW ? "Thaw" : "Check")); } } }