1 #include <string.h> 2 #include <stdio.h> 3 #include <sys/ioctl.h> 4 #include <sys/param.h> 5 #include <sys/ucred.h> 6 #include <sys/mount.h> 7 #include <sys/types.h> 8 9 #include <unistd.h> 10 #include <stdlib.h> 11 #include <poll.h> 12 #include <stdint.h> 13 #include <syslog.h> 14 #include <errno.h> 15 #include <err.h> 16 #include <fcntl.h> 17 #include <ufs/ffs/fs.h> 18 #include <paths.h> 19 #include <sysexits.h> 20 21 #include "hv_snapshot.h" 22 23 #define UNDEF_FREEZE_THAW (0) 24 #define FREEZE (1) 25 #define THAW (2) 26 27 #define VSS_LOG(priority, format, args...) do { \ 28 if (is_debugging == 1) { \ 29 if (is_daemon == 1) \ 30 syslog(priority, format, ## args); \ 31 else \ 32 printf(format, ## args); \ 33 } else { \ 34 if (priority < LOG_DEBUG) { \ 35 if (is_daemon == 1) \ 36 syslog(priority, format, ## args); \ 37 else \ 38 printf(format, ## args); \ 39 } \ 40 } \ 41 } while(0) 42 43 static int is_daemon = 1; 44 static int is_debugging = 0; 45 static int g_ufs_suspend_handle = -1; 46 47 static const char *dev = "/dev"; 48 49 static int 50 check(void) 51 { 52 struct statfs *mntbuf, *statfsp; 53 int mntsize; 54 int i; 55 56 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 57 if (mntsize == 0) { 58 VSS_LOG(LOG_ERR, "There is no mount information\n"); 59 return (EINVAL); 60 } 61 for (i = mntsize - 1; i >= 0; --i) 62 { 63 statfsp = &mntbuf[i]; 64 65 if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { 66 continue; /* skip to freeze '/dev' */ 67 } else if (statfsp->f_flags & MNT_RDONLY) { 68 continue; /* skip to freeze RDONLY partition */ 69 } else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { 70 return (EPERM); /* only UFS can be freezed */ 71 } 72 } 73 74 return (0); 75 } 76 77 static int 78 freeze(void) 79 { 80 struct statfs *mntbuf, *statfsp; 81 int mntsize; 82 int error = 0; 83 int i; 84 85 g_ufs_suspend_handle = open(_PATH_UFSSUSPEND, O_RDWR); 86 if (g_ufs_suspend_handle == -1) { 87 VSS_LOG(LOG_ERR, "unable to open %s", _PATH_UFSSUSPEND); 88 return (errno); 89 } 90 91 mntsize = getmntinfo(&mntbuf, MNT_NOWAIT); 92 if (mntsize == 0) { 93 VSS_LOG(LOG_ERR, "There is no mount information\n"); 94 return (EINVAL); 95 } 96 for (i = mntsize - 1; i >= 0; --i) 97 { 98 statfsp = &mntbuf[i]; 99 100 if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) { 101 continue; /* skip to freeze '/dev' */ 102 } else if (statfsp->f_flags & MNT_RDONLY) { 103 continue; /* skip to freeze RDONLY partition */ 104 } else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) { 105 continue; /* only UFS can be freezed */ 106 } 107 error = ioctl(g_ufs_suspend_handle, UFSSUSPEND, &statfsp->f_fsid); 108 if (error != 0) { 109 VSS_LOG(LOG_ERR, "error: %d\n", errno); 110 error = errno; 111 } else { 112 VSS_LOG(LOG_INFO, "Successfully suspend fs: %s\n", 113 statfsp->f_mntonname); 114 } 115 } 116 117 return (error); 118 } 119 120 /** 121 * close the opened handle will thaw the FS. 122 */ 123 static int 124 thaw(void) 125 { 126 int error = 0; 127 if (g_ufs_suspend_handle != -1) { 128 error = close(g_ufs_suspend_handle); 129 if (!error) { 130 g_ufs_suspend_handle = -1; 131 VSS_LOG(LOG_INFO, "Successfully thaw the fs\n"); 132 } else { 133 error = errno; 134 VSS_LOG(LOG_ERR, "Fail to thaw the fs: " 135 "%d %s\n", errno, strerror(errno)); 136 } 137 } else { 138 VSS_LOG(LOG_INFO, "The fs has already been thawed\n"); 139 } 140 141 return (error); 142 } 143 144 static void 145 usage(const char* cmd) 146 { 147 fprintf(stderr, "%s: daemon for UFS file system freeze/thaw\n" 148 " -d : enable debug log printing. Default is disabled.\n" 149 " -n : run as a regular process instead of a daemon. Default is a daemon.\n" 150 " -h : print usage.\n", cmd); 151 exit(1); 152 } 153 154 int 155 main(int argc, char* argv[]) 156 { 157 struct hv_vss_opt_msg userdata; 158 159 struct pollfd hv_vss_poll_fd[1]; 160 uint32_t op; 161 int ch, r, error; 162 int hv_vss_dev_fd; 163 164 while ((ch = getopt(argc, argv, "dnh")) != -1) { 165 switch (ch) { 166 case 'n': 167 /* Run as regular process for debugging purpose. */ 168 is_daemon = 0; 169 break; 170 case 'd': 171 /* Generate debugging output */ 172 is_debugging = 1; 173 break; 174 case 'h': 175 default: 176 usage(argv[0]); 177 break; 178 } 179 } 180 181 openlog("HV_VSS", 0, LOG_USER); 182 183 /* Become daemon first. */ 184 if (is_daemon == 1) 185 daemon(1, 0); 186 else 187 VSS_LOG(LOG_DEBUG, "Run as regular process.\n"); 188 189 VSS_LOG(LOG_INFO, "HV_VSS starting; pid is: %d\n", getpid()); 190 191 memset(&userdata, 0, sizeof(struct hv_vss_opt_msg)); 192 /* register the daemon */ 193 hv_vss_dev_fd = open(VSS_DEV(FS_VSS_DEV_NAME), O_RDWR); 194 195 if (hv_vss_dev_fd < 0) { 196 VSS_LOG(LOG_ERR, "Fail to open %s, error: %d %s\n", 197 VSS_DEV(FS_VSS_DEV_NAME), errno, strerror(errno)); 198 exit(EXIT_FAILURE); 199 } 200 hv_vss_poll_fd[0].fd = hv_vss_dev_fd; 201 hv_vss_poll_fd[0].events = POLLIN | POLLRDNORM; 202 203 while (1) { 204 r = poll(hv_vss_poll_fd, 1, INFTIM); 205 206 VSS_LOG(LOG_DEBUG, "poll returned r = %d, revent = 0x%x\n", 207 r, hv_vss_poll_fd[0].revents); 208 209 if (r == 0 || (r < 0 && errno == EAGAIN) || 210 (r < 0 && errno == EINTR)) { 211 /* Nothing to read */ 212 continue; 213 } 214 215 if (r < 0) { 216 /* 217 * For poll return failure other than EAGAIN, 218 * we want to exit. 219 */ 220 VSS_LOG(LOG_ERR, "Poll failed.\n"); 221 perror("poll"); 222 exit(EIO); 223 } 224 225 /* Read from character device */ 226 error = ioctl(hv_vss_dev_fd, IOCHVVSSREAD, &userdata); 227 if (error < 0) { 228 VSS_LOG(LOG_ERR, "Read failed.\n"); 229 perror("pread"); 230 exit(EIO); 231 } 232 233 if (userdata.status != 0) { 234 VSS_LOG(LOG_ERR, "data read error\n"); 235 continue; 236 } 237 238 /* 239 * We will use the KVP header information to pass back 240 * the error from this daemon. So, first save the op 241 * and pool info to local variables. 242 */ 243 244 op = userdata.opt; 245 246 switch (op) { 247 case HV_VSS_CHECK: 248 error = check(); 249 break; 250 case HV_VSS_FREEZE: 251 error = freeze(); 252 break; 253 case HV_VSS_THAW: 254 error = thaw(); 255 break; 256 default: 257 VSS_LOG(LOG_ERR, "Illegal operation: %d\n", op); 258 error = VSS_FAIL; 259 } 260 if (error) 261 userdata.status = VSS_FAIL; 262 else 263 userdata.status = VSS_SUCCESS; 264 error = ioctl(hv_vss_dev_fd, IOCHVVSSWRITE, &userdata); 265 if (error != 0) { 266 VSS_LOG(LOG_ERR, "Fail to write to device\n"); 267 exit(EXIT_FAILURE); 268 } else { 269 VSS_LOG(LOG_INFO, "Send response %d for %s to kernel\n", 270 userdata.status, op == HV_VSS_FREEZE ? "Freeze" : 271 (op == HV_VSS_THAW ? "Thaw" : "Check")); 272 } 273 } 274 } 275