xref: /freebsd/contrib/hyperv/tools/hv_vss_daemon.c (revision e0c4386e7e71d93b0edc0c8fa156263fc4a8b0b6)
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