hv_vss_daemon.c revision 310735
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
43static int is_daemon = 1;
44static int is_debugging = 0;
45static int g_ufs_suspend_handle = -1;
46
47static const char *dev = "/dev";
48
49static int
50check(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
77static int
78freeze(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 */
123static int
124thaw(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
144static void
145usage(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
154int
155main(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