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
check(void)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
freeze(void)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
thaw(void)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
usage(const char * cmd)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
main(int argc,char * argv[])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