]> CyberLeo.Net >> Repos - FreeBSD/stable/10.git/blob - contrib/hyperv/tools/hv_vss_daemon.c
MFC 308664,308742,308743
[FreeBSD/stable/10.git] / contrib / hyperv / tools / hv_vss_daemon.c
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 }