1168fce73SSepherosa Ziehau #include <string.h>
2168fce73SSepherosa Ziehau #include <stdio.h>
3168fce73SSepherosa Ziehau #include <sys/ioctl.h>
4168fce73SSepherosa Ziehau #include <sys/param.h>
5168fce73SSepherosa Ziehau #include <sys/ucred.h>
6168fce73SSepherosa Ziehau #include <sys/mount.h>
7168fce73SSepherosa Ziehau #include <sys/types.h>
8168fce73SSepherosa Ziehau 
9168fce73SSepherosa Ziehau #include <unistd.h>
10168fce73SSepherosa Ziehau #include <stdlib.h>
11168fce73SSepherosa Ziehau #include <poll.h>
12168fce73SSepherosa Ziehau #include <stdint.h>
13168fce73SSepherosa Ziehau #include <syslog.h>
14168fce73SSepherosa Ziehau #include <errno.h>
15168fce73SSepherosa Ziehau #include <err.h>
16168fce73SSepherosa Ziehau #include <fcntl.h>
17168fce73SSepherosa Ziehau #include <ufs/ffs/fs.h>
18168fce73SSepherosa Ziehau #include <paths.h>
19168fce73SSepherosa Ziehau #include <sysexits.h>
20168fce73SSepherosa Ziehau 
21168fce73SSepherosa Ziehau #include "hv_snapshot.h"
22168fce73SSepherosa Ziehau 
23168fce73SSepherosa Ziehau #define UNDEF_FREEZE_THAW       (0)
24168fce73SSepherosa Ziehau #define FREEZE                  (1)
25168fce73SSepherosa Ziehau #define THAW                    (2)
26168fce73SSepherosa Ziehau 
27168fce73SSepherosa Ziehau #define	VSS_LOG(priority, format, args...) do	{				\
28168fce73SSepherosa Ziehau 		if (is_debugging == 1) {					\
29168fce73SSepherosa Ziehau 			if (is_daemon == 1)					\
30168fce73SSepherosa Ziehau 				syslog(priority, format, ## args);		\
31168fce73SSepherosa Ziehau 			else							\
32168fce73SSepherosa Ziehau 				printf(format, ## args);			\
33168fce73SSepherosa Ziehau 		} else {							\
34168fce73SSepherosa Ziehau 			if (priority < LOG_DEBUG) {				\
35168fce73SSepherosa Ziehau 				if (is_daemon == 1)				\
36168fce73SSepherosa Ziehau 					syslog(priority, format, ## args);	\
37168fce73SSepherosa Ziehau 				else						\
38168fce73SSepherosa Ziehau 					printf(format, ## args);		\
39168fce73SSepherosa Ziehau 			}							\
40168fce73SSepherosa Ziehau 		}								\
41168fce73SSepherosa Ziehau 	} while(0)
42168fce73SSepherosa Ziehau 
43168fce73SSepherosa Ziehau static int is_daemon = 1;
44168fce73SSepherosa Ziehau static int is_debugging = 0;
45168fce73SSepherosa Ziehau static int g_ufs_suspend_handle = -1;
46168fce73SSepherosa Ziehau 
47168fce73SSepherosa Ziehau static const char *dev = "/dev";
48168fce73SSepherosa Ziehau 
49168fce73SSepherosa Ziehau static int
check(void)50168fce73SSepherosa Ziehau check(void)
51168fce73SSepherosa Ziehau {
52168fce73SSepherosa Ziehau 	struct statfs *mntbuf, *statfsp;
53168fce73SSepherosa Ziehau 	int mntsize;
54168fce73SSepherosa Ziehau 	int i;
55168fce73SSepherosa Ziehau 
56168fce73SSepherosa Ziehau 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
57168fce73SSepherosa Ziehau 	if (mntsize == 0) {
58168fce73SSepherosa Ziehau 		VSS_LOG(LOG_ERR, "There is no mount information\n");
59168fce73SSepherosa Ziehau 		return (EINVAL);
60168fce73SSepherosa Ziehau 	}
61168fce73SSepherosa Ziehau 	for (i = mntsize - 1; i >= 0; --i)
62168fce73SSepherosa Ziehau 	{
63168fce73SSepherosa Ziehau 		statfsp = &mntbuf[i];
64168fce73SSepherosa Ziehau 
65168fce73SSepherosa Ziehau 		if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) {
66168fce73SSepherosa Ziehau 			continue; /* skip to freeze '/dev' */
67168fce73SSepherosa Ziehau 		} else if (statfsp->f_flags & MNT_RDONLY) {
68168fce73SSepherosa Ziehau 			continue; /* skip to freeze RDONLY partition */
69168fce73SSepherosa Ziehau 		} else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) {
70168fce73SSepherosa Ziehau 			return (EPERM); /* only UFS can be freezed */
71168fce73SSepherosa Ziehau 		}
72168fce73SSepherosa Ziehau 	}
73168fce73SSepherosa Ziehau 
74168fce73SSepherosa Ziehau 	return (0);
75168fce73SSepherosa Ziehau }
76168fce73SSepherosa Ziehau 
77168fce73SSepherosa Ziehau static int
freeze(void)78168fce73SSepherosa Ziehau freeze(void)
79168fce73SSepherosa Ziehau {
80168fce73SSepherosa Ziehau 	struct statfs *mntbuf, *statfsp;
81168fce73SSepherosa Ziehau 	int mntsize;
82168fce73SSepherosa Ziehau 	int error = 0;
83168fce73SSepherosa Ziehau 	int i;
84168fce73SSepherosa Ziehau 
85168fce73SSepherosa Ziehau 	g_ufs_suspend_handle = open(_PATH_UFSSUSPEND, O_RDWR);
86168fce73SSepherosa Ziehau 	if (g_ufs_suspend_handle == -1) {
87168fce73SSepherosa Ziehau 		VSS_LOG(LOG_ERR, "unable to open %s", _PATH_UFSSUSPEND);
88168fce73SSepherosa Ziehau 		return (errno);
89168fce73SSepherosa Ziehau 	}
90168fce73SSepherosa Ziehau 
91168fce73SSepherosa Ziehau 	mntsize = getmntinfo(&mntbuf, MNT_NOWAIT);
92168fce73SSepherosa Ziehau 	if (mntsize == 0) {
93168fce73SSepherosa Ziehau 		VSS_LOG(LOG_ERR, "There is no mount information\n");
94168fce73SSepherosa Ziehau 		return (EINVAL);
95168fce73SSepherosa Ziehau 	}
96168fce73SSepherosa Ziehau 	for (i = mntsize - 1; i >= 0; --i)
97168fce73SSepherosa Ziehau 	{
98168fce73SSepherosa Ziehau 		statfsp = &mntbuf[i];
99168fce73SSepherosa Ziehau 
100168fce73SSepherosa Ziehau 		if (strncmp(statfsp->f_mntonname, dev, strlen(dev)) == 0) {
101168fce73SSepherosa Ziehau 			continue; /* skip to freeze '/dev' */
102168fce73SSepherosa Ziehau 		} else if (statfsp->f_flags & MNT_RDONLY) {
103168fce73SSepherosa Ziehau 			continue; /* skip to freeze RDONLY partition */
104168fce73SSepherosa Ziehau 		} else if (strncmp(statfsp->f_fstypename, "ufs", 3) != 0) {
105168fce73SSepherosa Ziehau 			continue; /* only UFS can be freezed */
106168fce73SSepherosa Ziehau 		}
107168fce73SSepherosa Ziehau 		error = ioctl(g_ufs_suspend_handle, UFSSUSPEND, &statfsp->f_fsid);
108168fce73SSepherosa Ziehau 		if (error != 0) {
109168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "error: %d\n", errno);
110168fce73SSepherosa Ziehau 			error = errno;
111168fce73SSepherosa Ziehau 		} else {
112168fce73SSepherosa Ziehau 			VSS_LOG(LOG_INFO, "Successfully suspend fs: %s\n",
113168fce73SSepherosa Ziehau 			    statfsp->f_mntonname);
114168fce73SSepherosa Ziehau 		}
115168fce73SSepherosa Ziehau 	}
116168fce73SSepherosa Ziehau 
117168fce73SSepherosa Ziehau 	return (error);
118168fce73SSepherosa Ziehau }
119168fce73SSepherosa Ziehau 
120168fce73SSepherosa Ziehau /**
121168fce73SSepherosa Ziehau  * close the opened handle will thaw the FS.
122168fce73SSepherosa Ziehau  */
123168fce73SSepherosa Ziehau static int
thaw(void)124168fce73SSepherosa Ziehau thaw(void)
125168fce73SSepherosa Ziehau {
126168fce73SSepherosa Ziehau 	int error = 0;
127168fce73SSepherosa Ziehau 	if (g_ufs_suspend_handle != -1) {
128168fce73SSepherosa Ziehau 		error = close(g_ufs_suspend_handle);
129168fce73SSepherosa Ziehau 		if (!error) {
130168fce73SSepherosa Ziehau 			g_ufs_suspend_handle = -1;
131168fce73SSepherosa Ziehau 			VSS_LOG(LOG_INFO, "Successfully thaw the fs\n");
132168fce73SSepherosa Ziehau 		} else {
133168fce73SSepherosa Ziehau 			error = errno;
134168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "Fail to thaw the fs: "
135168fce73SSepherosa Ziehau 			    "%d %s\n", errno, strerror(errno));
136168fce73SSepherosa Ziehau 		}
137168fce73SSepherosa Ziehau 	} else {
138168fce73SSepherosa Ziehau 		VSS_LOG(LOG_INFO, "The fs has already been thawed\n");
139168fce73SSepherosa Ziehau 	}
140168fce73SSepherosa Ziehau 
141168fce73SSepherosa Ziehau 	return (error);
142168fce73SSepherosa Ziehau }
143168fce73SSepherosa Ziehau 
144168fce73SSepherosa Ziehau static void
usage(const char * cmd)145168fce73SSepherosa Ziehau usage(const char* cmd)
146168fce73SSepherosa Ziehau {
147168fce73SSepherosa Ziehau 	fprintf(stderr, "%s: daemon for UFS file system freeze/thaw\n"
148168fce73SSepherosa Ziehau 	    " -d : enable debug log printing. Default is disabled.\n"
149168fce73SSepherosa Ziehau 	    " -n : run as a regular process instead of a daemon. Default is a daemon.\n"
150168fce73SSepherosa Ziehau 	    " -h : print usage.\n", cmd);
151168fce73SSepherosa Ziehau 	exit(1);
152168fce73SSepherosa Ziehau }
153168fce73SSepherosa Ziehau 
154168fce73SSepherosa Ziehau int
main(int argc,char * argv[])155168fce73SSepherosa Ziehau main(int argc, char* argv[])
156168fce73SSepherosa Ziehau {
157168fce73SSepherosa Ziehau 	struct hv_vss_opt_msg  userdata;
158168fce73SSepherosa Ziehau 
159168fce73SSepherosa Ziehau 	struct pollfd hv_vss_poll_fd[1];
160168fce73SSepherosa Ziehau 	uint32_t op;
161bedddda0SSepherosa Ziehau 	int ch, r, error;
162168fce73SSepherosa Ziehau 	int hv_vss_dev_fd;
163168fce73SSepherosa Ziehau 
164168fce73SSepherosa Ziehau 	while ((ch = getopt(argc, argv, "dnh")) != -1) {
165168fce73SSepherosa Ziehau 		switch (ch) {
166168fce73SSepherosa Ziehau 		case 'n':
167168fce73SSepherosa Ziehau 			/* Run as regular process for debugging purpose. */
168168fce73SSepherosa Ziehau 			is_daemon = 0;
169168fce73SSepherosa Ziehau 			break;
170168fce73SSepherosa Ziehau 		case 'd':
171168fce73SSepherosa Ziehau 			/* Generate debugging output */
172168fce73SSepherosa Ziehau 			is_debugging = 1;
173168fce73SSepherosa Ziehau 			break;
174168fce73SSepherosa Ziehau 		case 'h':
175168fce73SSepherosa Ziehau 		default:
176168fce73SSepherosa Ziehau 			usage(argv[0]);
177168fce73SSepherosa Ziehau 			break;
178168fce73SSepherosa Ziehau 		}
179168fce73SSepherosa Ziehau 	}
180168fce73SSepherosa Ziehau 
181168fce73SSepherosa Ziehau 	openlog("HV_VSS", 0, LOG_USER);
182168fce73SSepherosa Ziehau 
183168fce73SSepherosa Ziehau 	/* Become daemon first. */
184168fce73SSepherosa Ziehau 	if (is_daemon == 1)
185168fce73SSepherosa Ziehau 		daemon(1, 0);
186168fce73SSepherosa Ziehau 	else
187168fce73SSepherosa Ziehau 		VSS_LOG(LOG_DEBUG, "Run as regular process.\n");
188168fce73SSepherosa Ziehau 
189168fce73SSepherosa Ziehau 	VSS_LOG(LOG_INFO, "HV_VSS starting; pid is: %d\n", getpid());
190168fce73SSepherosa Ziehau 
191168fce73SSepherosa Ziehau 	memset(&userdata, 0, sizeof(struct hv_vss_opt_msg));
192168fce73SSepherosa Ziehau 	/* register the daemon */
193168fce73SSepherosa Ziehau 	hv_vss_dev_fd = open(VSS_DEV(FS_VSS_DEV_NAME), O_RDWR);
194168fce73SSepherosa Ziehau 
195168fce73SSepherosa Ziehau 	if (hv_vss_dev_fd < 0) {
196168fce73SSepherosa Ziehau 		VSS_LOG(LOG_ERR, "Fail to open %s, error: %d %s\n",
197168fce73SSepherosa Ziehau 		    VSS_DEV(FS_VSS_DEV_NAME), errno, strerror(errno));
198168fce73SSepherosa Ziehau 		exit(EXIT_FAILURE);
199168fce73SSepherosa Ziehau 	}
200168fce73SSepherosa Ziehau 	hv_vss_poll_fd[0].fd = hv_vss_dev_fd;
201168fce73SSepherosa Ziehau 	hv_vss_poll_fd[0].events = POLLIN | POLLRDNORM;
202168fce73SSepherosa Ziehau 
203168fce73SSepherosa Ziehau 	while (1) {
204168fce73SSepherosa Ziehau 		r = poll(hv_vss_poll_fd, 1, INFTIM);
205168fce73SSepherosa Ziehau 
206168fce73SSepherosa Ziehau 		VSS_LOG(LOG_DEBUG, "poll returned r = %d, revent = 0x%x\n",
207168fce73SSepherosa Ziehau 		    r, hv_vss_poll_fd[0].revents);
208168fce73SSepherosa Ziehau 
209168fce73SSepherosa Ziehau 		if (r == 0 || (r < 0 && errno == EAGAIN) ||
210168fce73SSepherosa Ziehau 		    (r < 0 && errno == EINTR)) {
211168fce73SSepherosa Ziehau 			/* Nothing to read */
212168fce73SSepherosa Ziehau 			continue;
213168fce73SSepherosa Ziehau 		}
214168fce73SSepherosa Ziehau 
215168fce73SSepherosa Ziehau 		if (r < 0) {
216168fce73SSepherosa Ziehau 			/*
217168fce73SSepherosa Ziehau 			 * For poll return failure other than EAGAIN,
218168fce73SSepherosa Ziehau 			 * we want to exit.
219168fce73SSepherosa Ziehau 			 */
220168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "Poll failed.\n");
221168fce73SSepherosa Ziehau 			perror("poll");
222168fce73SSepherosa Ziehau 			exit(EIO);
223168fce73SSepherosa Ziehau 		}
224168fce73SSepherosa Ziehau 
225168fce73SSepherosa Ziehau 		/* Read from character device */
226168fce73SSepherosa Ziehau 		error = ioctl(hv_vss_dev_fd, IOCHVVSSREAD, &userdata);
227168fce73SSepherosa Ziehau 		if (error < 0) {
228168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "Read failed.\n");
229168fce73SSepherosa Ziehau 			perror("pread");
230168fce73SSepherosa Ziehau 			exit(EIO);
231168fce73SSepherosa Ziehau 		}
232168fce73SSepherosa Ziehau 
233168fce73SSepherosa Ziehau 		if (userdata.status != 0) {
234168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "data read error\n");
235168fce73SSepherosa Ziehau 			continue;
236168fce73SSepherosa Ziehau 		}
237168fce73SSepherosa Ziehau 
238168fce73SSepherosa Ziehau 		/*
239168fce73SSepherosa Ziehau 		 * We will use the KVP header information to pass back
240168fce73SSepherosa Ziehau 		 * the error from this daemon. So, first save the op
241168fce73SSepherosa Ziehau 		 * and pool info to local variables.
242168fce73SSepherosa Ziehau 		 */
243168fce73SSepherosa Ziehau 
244168fce73SSepherosa Ziehau 		op = userdata.opt;
245168fce73SSepherosa Ziehau 
246168fce73SSepherosa Ziehau 		switch (op) {
247168fce73SSepherosa Ziehau 		case HV_VSS_CHECK:
248168fce73SSepherosa Ziehau 			error = check();
249168fce73SSepherosa Ziehau 			break;
250168fce73SSepherosa Ziehau 		case HV_VSS_FREEZE:
251168fce73SSepherosa Ziehau 			error = freeze();
252168fce73SSepherosa Ziehau 			break;
253168fce73SSepherosa Ziehau 		case HV_VSS_THAW:
254168fce73SSepherosa Ziehau 			error = thaw();
255168fce73SSepherosa Ziehau 			break;
256168fce73SSepherosa Ziehau 		default:
257168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "Illegal operation: %d\n", op);
258168fce73SSepherosa Ziehau 			error = VSS_FAIL;
259168fce73SSepherosa Ziehau 		}
260168fce73SSepherosa Ziehau 		if (error)
261168fce73SSepherosa Ziehau 			userdata.status = VSS_FAIL;
262168fce73SSepherosa Ziehau 		else
263168fce73SSepherosa Ziehau 			userdata.status = VSS_SUCCESS;
264168fce73SSepherosa Ziehau 		error = ioctl(hv_vss_dev_fd, IOCHVVSSWRITE, &userdata);
265168fce73SSepherosa Ziehau 		if (error != 0) {
266168fce73SSepherosa Ziehau 			VSS_LOG(LOG_ERR, "Fail to write to device\n");
267168fce73SSepherosa Ziehau 			exit(EXIT_FAILURE);
268168fce73SSepherosa Ziehau 		} else {
269168fce73SSepherosa Ziehau 			VSS_LOG(LOG_INFO, "Send response %d for %s to kernel\n",
270168fce73SSepherosa Ziehau 			    userdata.status, op == HV_VSS_FREEZE ? "Freeze" :
271168fce73SSepherosa Ziehau 			    (op == HV_VSS_THAW ? "Thaw" : "Check"));
272168fce73SSepherosa Ziehau 		}
273168fce73SSepherosa Ziehau 	}
274168fce73SSepherosa Ziehau }
275