root / qga / guest-agent-commands.c @ aa82ba54
History | View | Annotate | Download (13.7 kB)
1 | e3d4d252 | Michael Roth | /*
|
---|---|---|---|
2 | e3d4d252 | Michael Roth | * QEMU Guest Agent commands
|
3 | e3d4d252 | Michael Roth | *
|
4 | e3d4d252 | Michael Roth | * Copyright IBM Corp. 2011
|
5 | e3d4d252 | Michael Roth | *
|
6 | e3d4d252 | Michael Roth | * Authors:
|
7 | e3d4d252 | Michael Roth | * Michael Roth <mdroth@linux.vnet.ibm.com>
|
8 | e3d4d252 | Michael Roth | *
|
9 | e3d4d252 | Michael Roth | * This work is licensed under the terms of the GNU GPL, version 2 or later.
|
10 | e3d4d252 | Michael Roth | * See the COPYING file in the top-level directory.
|
11 | e3d4d252 | Michael Roth | */
|
12 | e3d4d252 | Michael Roth | |
13 | e3d4d252 | Michael Roth | #include <glib.h> |
14 | 4eb36d40 | Anthony Liguori | |
15 | 4eb36d40 | Anthony Liguori | #if defined(__linux__)
|
16 | e3d4d252 | Michael Roth | #include <mntent.h> |
17 | 7006b9cf | Anthony Liguori | #include <linux/fs.h> |
18 | 4eb36d40 | Anthony Liguori | |
19 | 4eb36d40 | Anthony Liguori | #if defined(__linux__) && defined(FIFREEZE)
|
20 | 4eb36d40 | Anthony Liguori | #define CONFIG_FSFREEZE
|
21 | 7006b9cf | Anthony Liguori | #endif
|
22 | 4eb36d40 | Anthony Liguori | #endif
|
23 | 4eb36d40 | Anthony Liguori | |
24 | e3d4d252 | Michael Roth | #include <sys/types.h> |
25 | e3d4d252 | Michael Roth | #include <sys/ioctl.h> |
26 | e3d4d252 | Michael Roth | #include "qga/guest-agent-core.h" |
27 | e3d4d252 | Michael Roth | #include "qga-qmp-commands.h" |
28 | e3d4d252 | Michael Roth | #include "qerror.h" |
29 | e3d4d252 | Michael Roth | #include "qemu-queue.h" |
30 | e3d4d252 | Michael Roth | |
31 | e3d4d252 | Michael Roth | static GAState *ga_state;
|
32 | e3d4d252 | Michael Roth | |
33 | e3d4d252 | Michael Roth | /* Note: in some situations, like with the fsfreeze, logging may be
|
34 | e3d4d252 | Michael Roth | * temporarilly disabled. if it is necessary that a command be able
|
35 | e3d4d252 | Michael Roth | * to log for accounting purposes, check ga_logging_enabled() beforehand,
|
36 | e3d4d252 | Michael Roth | * and use the QERR_QGA_LOGGING_DISABLED to generate an error
|
37 | e3d4d252 | Michael Roth | */
|
38 | e3d4d252 | Michael Roth | static void slog(const char *fmt, ...) |
39 | e3d4d252 | Michael Roth | { |
40 | e3d4d252 | Michael Roth | va_list ap; |
41 | e3d4d252 | Michael Roth | |
42 | e3d4d252 | Michael Roth | va_start(ap, fmt); |
43 | e3d4d252 | Michael Roth | g_logv("syslog", G_LOG_LEVEL_INFO, fmt, ap);
|
44 | e3d4d252 | Michael Roth | va_end(ap); |
45 | e3d4d252 | Michael Roth | } |
46 | e3d4d252 | Michael Roth | |
47 | e3d4d252 | Michael Roth | int64_t qmp_guest_sync(int64_t id, Error **errp) |
48 | e3d4d252 | Michael Roth | { |
49 | e3d4d252 | Michael Roth | return id;
|
50 | e3d4d252 | Michael Roth | } |
51 | e3d4d252 | Michael Roth | |
52 | e3d4d252 | Michael Roth | void qmp_guest_ping(Error **err)
|
53 | e3d4d252 | Michael Roth | { |
54 | e3d4d252 | Michael Roth | slog("guest-ping called");
|
55 | e3d4d252 | Michael Roth | } |
56 | e3d4d252 | Michael Roth | |
57 | e3d4d252 | Michael Roth | struct GuestAgentInfo *qmp_guest_info(Error **err)
|
58 | e3d4d252 | Michael Roth | { |
59 | 7267c094 | Anthony Liguori | GuestAgentInfo *info = g_malloc0(sizeof(GuestAgentInfo));
|
60 | e3d4d252 | Michael Roth | |
61 | e3d4d252 | Michael Roth | info->version = g_strdup(QGA_VERSION); |
62 | e3d4d252 | Michael Roth | |
63 | e3d4d252 | Michael Roth | return info;
|
64 | e3d4d252 | Michael Roth | } |
65 | e3d4d252 | Michael Roth | |
66 | e3d4d252 | Michael Roth | void qmp_guest_shutdown(bool has_mode, const char *mode, Error **err) |
67 | e3d4d252 | Michael Roth | { |
68 | e3d4d252 | Michael Roth | int ret;
|
69 | e3d4d252 | Michael Roth | const char *shutdown_flag; |
70 | e3d4d252 | Michael Roth | |
71 | e3d4d252 | Michael Roth | slog("guest-shutdown called, mode: %s", mode);
|
72 | e3d4d252 | Michael Roth | if (!has_mode || strcmp(mode, "powerdown") == 0) { |
73 | e3d4d252 | Michael Roth | shutdown_flag = "-P";
|
74 | e3d4d252 | Michael Roth | } else if (strcmp(mode, "halt") == 0) { |
75 | e3d4d252 | Michael Roth | shutdown_flag = "-H";
|
76 | e3d4d252 | Michael Roth | } else if (strcmp(mode, "reboot") == 0) { |
77 | e3d4d252 | Michael Roth | shutdown_flag = "-r";
|
78 | e3d4d252 | Michael Roth | } else {
|
79 | e3d4d252 | Michael Roth | error_set(err, QERR_INVALID_PARAMETER_VALUE, "mode",
|
80 | e3d4d252 | Michael Roth | "halt|powerdown|reboot");
|
81 | e3d4d252 | Michael Roth | return;
|
82 | e3d4d252 | Michael Roth | } |
83 | e3d4d252 | Michael Roth | |
84 | e3d4d252 | Michael Roth | ret = fork(); |
85 | e3d4d252 | Michael Roth | if (ret == 0) { |
86 | e3d4d252 | Michael Roth | /* child, start the shutdown */
|
87 | e3d4d252 | Michael Roth | setsid(); |
88 | e3d4d252 | Michael Roth | fclose(stdin); |
89 | e3d4d252 | Michael Roth | fclose(stdout); |
90 | e3d4d252 | Michael Roth | fclose(stderr); |
91 | e3d4d252 | Michael Roth | |
92 | e3d4d252 | Michael Roth | ret = execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0", |
93 | e3d4d252 | Michael Roth | "hypervisor initiated shutdown", (char*)NULL); |
94 | e3d4d252 | Michael Roth | if (ret) {
|
95 | e3d4d252 | Michael Roth | slog("guest-shutdown failed: %s", strerror(errno));
|
96 | e3d4d252 | Michael Roth | } |
97 | e3d4d252 | Michael Roth | exit(!!ret); |
98 | e3d4d252 | Michael Roth | } else if (ret < 0) { |
99 | e3d4d252 | Michael Roth | error_set(err, QERR_UNDEFINED_ERROR); |
100 | e3d4d252 | Michael Roth | } |
101 | e3d4d252 | Michael Roth | } |
102 | e3d4d252 | Michael Roth | |
103 | e3d4d252 | Michael Roth | typedef struct GuestFileHandle { |
104 | e3d4d252 | Michael Roth | uint64_t id; |
105 | e3d4d252 | Michael Roth | FILE *fh; |
106 | e3d4d252 | Michael Roth | QTAILQ_ENTRY(GuestFileHandle) next; |
107 | e3d4d252 | Michael Roth | } GuestFileHandle; |
108 | e3d4d252 | Michael Roth | |
109 | e3d4d252 | Michael Roth | static struct { |
110 | e3d4d252 | Michael Roth | QTAILQ_HEAD(, GuestFileHandle) filehandles; |
111 | e3d4d252 | Michael Roth | } guest_file_state; |
112 | e3d4d252 | Michael Roth | |
113 | e3d4d252 | Michael Roth | static void guest_file_handle_add(FILE *fh) |
114 | e3d4d252 | Michael Roth | { |
115 | e3d4d252 | Michael Roth | GuestFileHandle *gfh; |
116 | e3d4d252 | Michael Roth | |
117 | 7267c094 | Anthony Liguori | gfh = g_malloc0(sizeof(GuestFileHandle));
|
118 | e3d4d252 | Michael Roth | gfh->id = fileno(fh); |
119 | e3d4d252 | Michael Roth | gfh->fh = fh; |
120 | e3d4d252 | Michael Roth | QTAILQ_INSERT_TAIL(&guest_file_state.filehandles, gfh, next); |
121 | e3d4d252 | Michael Roth | } |
122 | e3d4d252 | Michael Roth | |
123 | e3d4d252 | Michael Roth | static GuestFileHandle *guest_file_handle_find(int64_t id)
|
124 | e3d4d252 | Michael Roth | { |
125 | e3d4d252 | Michael Roth | GuestFileHandle *gfh; |
126 | e3d4d252 | Michael Roth | |
127 | e3d4d252 | Michael Roth | QTAILQ_FOREACH(gfh, &guest_file_state.filehandles, next) |
128 | e3d4d252 | Michael Roth | { |
129 | e3d4d252 | Michael Roth | if (gfh->id == id) {
|
130 | e3d4d252 | Michael Roth | return gfh;
|
131 | e3d4d252 | Michael Roth | } |
132 | e3d4d252 | Michael Roth | } |
133 | e3d4d252 | Michael Roth | |
134 | e3d4d252 | Michael Roth | return NULL; |
135 | e3d4d252 | Michael Roth | } |
136 | e3d4d252 | Michael Roth | |
137 | e3d4d252 | Michael Roth | int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **err) |
138 | e3d4d252 | Michael Roth | { |
139 | e3d4d252 | Michael Roth | FILE *fh; |
140 | e3d4d252 | Michael Roth | int fd;
|
141 | e3d4d252 | Michael Roth | int64_t ret = -1;
|
142 | e3d4d252 | Michael Roth | |
143 | e3d4d252 | Michael Roth | if (!has_mode) {
|
144 | e3d4d252 | Michael Roth | mode = "r";
|
145 | e3d4d252 | Michael Roth | } |
146 | e3d4d252 | Michael Roth | slog("guest-file-open called, filepath: %s, mode: %s", path, mode);
|
147 | e3d4d252 | Michael Roth | fh = fopen(path, mode); |
148 | e3d4d252 | Michael Roth | if (!fh) {
|
149 | e3d4d252 | Michael Roth | error_set(err, QERR_OPEN_FILE_FAILED, path); |
150 | e3d4d252 | Michael Roth | return -1; |
151 | e3d4d252 | Michael Roth | } |
152 | e3d4d252 | Michael Roth | |
153 | e3d4d252 | Michael Roth | /* set fd non-blocking to avoid common use cases (like reading from a
|
154 | e3d4d252 | Michael Roth | * named pipe) from hanging the agent
|
155 | e3d4d252 | Michael Roth | */
|
156 | e3d4d252 | Michael Roth | fd = fileno(fh); |
157 | e3d4d252 | Michael Roth | ret = fcntl(fd, F_GETFL); |
158 | e3d4d252 | Michael Roth | ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); |
159 | e3d4d252 | Michael Roth | if (ret == -1) { |
160 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, "fcntl() failed");
|
161 | e3d4d252 | Michael Roth | fclose(fh); |
162 | e3d4d252 | Michael Roth | return -1; |
163 | e3d4d252 | Michael Roth | } |
164 | e3d4d252 | Michael Roth | |
165 | e3d4d252 | Michael Roth | guest_file_handle_add(fh); |
166 | e3d4d252 | Michael Roth | slog("guest-file-open, handle: %d", fd);
|
167 | e3d4d252 | Michael Roth | return fd;
|
168 | e3d4d252 | Michael Roth | } |
169 | e3d4d252 | Michael Roth | |
170 | e3d4d252 | Michael Roth | void qmp_guest_file_close(int64_t handle, Error **err)
|
171 | e3d4d252 | Michael Roth | { |
172 | e3d4d252 | Michael Roth | GuestFileHandle *gfh = guest_file_handle_find(handle); |
173 | e3d4d252 | Michael Roth | int ret;
|
174 | e3d4d252 | Michael Roth | |
175 | e3d4d252 | Michael Roth | slog("guest-file-close called, handle: %ld", handle);
|
176 | e3d4d252 | Michael Roth | if (!gfh) {
|
177 | e3d4d252 | Michael Roth | error_set(err, QERR_FD_NOT_FOUND, "handle");
|
178 | e3d4d252 | Michael Roth | return;
|
179 | e3d4d252 | Michael Roth | } |
180 | e3d4d252 | Michael Roth | |
181 | e3d4d252 | Michael Roth | ret = fclose(gfh->fh); |
182 | e3d4d252 | Michael Roth | if (ret == -1) { |
183 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, "fclose() failed");
|
184 | e3d4d252 | Michael Roth | return;
|
185 | e3d4d252 | Michael Roth | } |
186 | e3d4d252 | Michael Roth | |
187 | e3d4d252 | Michael Roth | QTAILQ_REMOVE(&guest_file_state.filehandles, gfh, next); |
188 | 7267c094 | Anthony Liguori | g_free(gfh); |
189 | e3d4d252 | Michael Roth | } |
190 | e3d4d252 | Michael Roth | |
191 | e3d4d252 | Michael Roth | struct GuestFileRead *qmp_guest_file_read(int64_t handle, bool has_count, |
192 | e3d4d252 | Michael Roth | int64_t count, Error **err) |
193 | e3d4d252 | Michael Roth | { |
194 | e3d4d252 | Michael Roth | GuestFileHandle *gfh = guest_file_handle_find(handle); |
195 | e3d4d252 | Michael Roth | GuestFileRead *read_data = NULL;
|
196 | e3d4d252 | Michael Roth | guchar *buf; |
197 | e3d4d252 | Michael Roth | FILE *fh; |
198 | e3d4d252 | Michael Roth | size_t read_count; |
199 | e3d4d252 | Michael Roth | |
200 | e3d4d252 | Michael Roth | if (!gfh) {
|
201 | e3d4d252 | Michael Roth | error_set(err, QERR_FD_NOT_FOUND, "handle");
|
202 | e3d4d252 | Michael Roth | return NULL; |
203 | e3d4d252 | Michael Roth | } |
204 | e3d4d252 | Michael Roth | |
205 | e3d4d252 | Michael Roth | if (!has_count) {
|
206 | e3d4d252 | Michael Roth | count = QGA_READ_COUNT_DEFAULT; |
207 | e3d4d252 | Michael Roth | } else if (count < 0) { |
208 | e3d4d252 | Michael Roth | error_set(err, QERR_INVALID_PARAMETER, "count");
|
209 | e3d4d252 | Michael Roth | return NULL; |
210 | e3d4d252 | Michael Roth | } |
211 | e3d4d252 | Michael Roth | |
212 | e3d4d252 | Michael Roth | fh = gfh->fh; |
213 | 7267c094 | Anthony Liguori | buf = g_malloc0(count+1);
|
214 | e3d4d252 | Michael Roth | read_count = fread(buf, 1, count, fh);
|
215 | e3d4d252 | Michael Roth | if (ferror(fh)) {
|
216 | e3d4d252 | Michael Roth | slog("guest-file-read failed, handle: %ld", handle);
|
217 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, "fread() failed");
|
218 | e3d4d252 | Michael Roth | } else {
|
219 | e3d4d252 | Michael Roth | buf[read_count] = 0;
|
220 | 7267c094 | Anthony Liguori | read_data = g_malloc0(sizeof(GuestFileRead));
|
221 | e3d4d252 | Michael Roth | read_data->count = read_count; |
222 | e3d4d252 | Michael Roth | read_data->eof = feof(fh); |
223 | e3d4d252 | Michael Roth | if (read_count) {
|
224 | e3d4d252 | Michael Roth | read_data->buf_b64 = g_base64_encode(buf, read_count); |
225 | e3d4d252 | Michael Roth | } |
226 | e3d4d252 | Michael Roth | } |
227 | 7267c094 | Anthony Liguori | g_free(buf); |
228 | e3d4d252 | Michael Roth | clearerr(fh); |
229 | e3d4d252 | Michael Roth | |
230 | e3d4d252 | Michael Roth | return read_data;
|
231 | e3d4d252 | Michael Roth | } |
232 | e3d4d252 | Michael Roth | |
233 | e3d4d252 | Michael Roth | GuestFileWrite *qmp_guest_file_write(int64_t handle, const char *buf_b64, |
234 | e3d4d252 | Michael Roth | bool has_count, int64_t count, Error **err)
|
235 | e3d4d252 | Michael Roth | { |
236 | e3d4d252 | Michael Roth | GuestFileWrite *write_data = NULL;
|
237 | e3d4d252 | Michael Roth | guchar *buf; |
238 | e3d4d252 | Michael Roth | gsize buf_len; |
239 | e3d4d252 | Michael Roth | int write_count;
|
240 | e3d4d252 | Michael Roth | GuestFileHandle *gfh = guest_file_handle_find(handle); |
241 | e3d4d252 | Michael Roth | FILE *fh; |
242 | e3d4d252 | Michael Roth | |
243 | e3d4d252 | Michael Roth | if (!gfh) {
|
244 | e3d4d252 | Michael Roth | error_set(err, QERR_FD_NOT_FOUND, "handle");
|
245 | e3d4d252 | Michael Roth | return NULL; |
246 | e3d4d252 | Michael Roth | } |
247 | e3d4d252 | Michael Roth | |
248 | e3d4d252 | Michael Roth | fh = gfh->fh; |
249 | e3d4d252 | Michael Roth | buf = g_base64_decode(buf_b64, &buf_len); |
250 | e3d4d252 | Michael Roth | |
251 | e3d4d252 | Michael Roth | if (!has_count) {
|
252 | e3d4d252 | Michael Roth | count = buf_len; |
253 | e3d4d252 | Michael Roth | } else if (count < 0 || count > buf_len) { |
254 | 7267c094 | Anthony Liguori | g_free(buf); |
255 | e3d4d252 | Michael Roth | error_set(err, QERR_INVALID_PARAMETER, "count");
|
256 | e3d4d252 | Michael Roth | return NULL; |
257 | e3d4d252 | Michael Roth | } |
258 | e3d4d252 | Michael Roth | |
259 | e3d4d252 | Michael Roth | write_count = fwrite(buf, 1, count, fh);
|
260 | e3d4d252 | Michael Roth | if (ferror(fh)) {
|
261 | e3d4d252 | Michael Roth | slog("guest-file-write failed, handle: %ld", handle);
|
262 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, "fwrite() error");
|
263 | e3d4d252 | Michael Roth | } else {
|
264 | 7267c094 | Anthony Liguori | write_data = g_malloc0(sizeof(GuestFileWrite));
|
265 | e3d4d252 | Michael Roth | write_data->count = write_count; |
266 | e3d4d252 | Michael Roth | write_data->eof = feof(fh); |
267 | e3d4d252 | Michael Roth | } |
268 | 7267c094 | Anthony Liguori | g_free(buf); |
269 | e3d4d252 | Michael Roth | clearerr(fh); |
270 | e3d4d252 | Michael Roth | |
271 | e3d4d252 | Michael Roth | return write_data;
|
272 | e3d4d252 | Michael Roth | } |
273 | e3d4d252 | Michael Roth | |
274 | e3d4d252 | Michael Roth | struct GuestFileSeek *qmp_guest_file_seek(int64_t handle, int64_t offset,
|
275 | e3d4d252 | Michael Roth | int64_t whence, Error **err) |
276 | e3d4d252 | Michael Roth | { |
277 | e3d4d252 | Michael Roth | GuestFileHandle *gfh = guest_file_handle_find(handle); |
278 | e3d4d252 | Michael Roth | GuestFileSeek *seek_data = NULL;
|
279 | e3d4d252 | Michael Roth | FILE *fh; |
280 | e3d4d252 | Michael Roth | int ret;
|
281 | e3d4d252 | Michael Roth | |
282 | e3d4d252 | Michael Roth | if (!gfh) {
|
283 | e3d4d252 | Michael Roth | error_set(err, QERR_FD_NOT_FOUND, "handle");
|
284 | e3d4d252 | Michael Roth | return NULL; |
285 | e3d4d252 | Michael Roth | } |
286 | e3d4d252 | Michael Roth | |
287 | e3d4d252 | Michael Roth | fh = gfh->fh; |
288 | e3d4d252 | Michael Roth | ret = fseek(fh, offset, whence); |
289 | e3d4d252 | Michael Roth | if (ret == -1) { |
290 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); |
291 | e3d4d252 | Michael Roth | } else {
|
292 | 7267c094 | Anthony Liguori | seek_data = g_malloc0(sizeof(GuestFileRead));
|
293 | e3d4d252 | Michael Roth | seek_data->position = ftell(fh); |
294 | e3d4d252 | Michael Roth | seek_data->eof = feof(fh); |
295 | e3d4d252 | Michael Roth | } |
296 | e3d4d252 | Michael Roth | clearerr(fh); |
297 | e3d4d252 | Michael Roth | |
298 | e3d4d252 | Michael Roth | return seek_data;
|
299 | e3d4d252 | Michael Roth | } |
300 | e3d4d252 | Michael Roth | |
301 | e3d4d252 | Michael Roth | void qmp_guest_file_flush(int64_t handle, Error **err)
|
302 | e3d4d252 | Michael Roth | { |
303 | e3d4d252 | Michael Roth | GuestFileHandle *gfh = guest_file_handle_find(handle); |
304 | e3d4d252 | Michael Roth | FILE *fh; |
305 | e3d4d252 | Michael Roth | int ret;
|
306 | e3d4d252 | Michael Roth | |
307 | e3d4d252 | Michael Roth | if (!gfh) {
|
308 | e3d4d252 | Michael Roth | error_set(err, QERR_FD_NOT_FOUND, "handle");
|
309 | e3d4d252 | Michael Roth | return;
|
310 | e3d4d252 | Michael Roth | } |
311 | e3d4d252 | Michael Roth | |
312 | e3d4d252 | Michael Roth | fh = gfh->fh; |
313 | e3d4d252 | Michael Roth | ret = fflush(fh); |
314 | e3d4d252 | Michael Roth | if (ret == EOF) { |
315 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, strerror(errno)); |
316 | e3d4d252 | Michael Roth | } |
317 | e3d4d252 | Michael Roth | } |
318 | e3d4d252 | Michael Roth | |
319 | e3d4d252 | Michael Roth | static void guest_file_init(void) |
320 | e3d4d252 | Michael Roth | { |
321 | e3d4d252 | Michael Roth | QTAILQ_INIT(&guest_file_state.filehandles); |
322 | e3d4d252 | Michael Roth | } |
323 | e3d4d252 | Michael Roth | |
324 | 7006b9cf | Anthony Liguori | #if defined(CONFIG_FSFREEZE)
|
325 | 7006b9cf | Anthony Liguori | static void disable_logging(void) |
326 | 7006b9cf | Anthony Liguori | { |
327 | 7006b9cf | Anthony Liguori | ga_disable_logging(ga_state); |
328 | 7006b9cf | Anthony Liguori | } |
329 | 7006b9cf | Anthony Liguori | |
330 | 7006b9cf | Anthony Liguori | static void enable_logging(void) |
331 | 7006b9cf | Anthony Liguori | { |
332 | 7006b9cf | Anthony Liguori | ga_enable_logging(ga_state); |
333 | 7006b9cf | Anthony Liguori | } |
334 | 7006b9cf | Anthony Liguori | |
335 | e3d4d252 | Michael Roth | typedef struct GuestFsfreezeMount { |
336 | e3d4d252 | Michael Roth | char *dirname;
|
337 | e3d4d252 | Michael Roth | char *devtype;
|
338 | e3d4d252 | Michael Roth | QTAILQ_ENTRY(GuestFsfreezeMount) next; |
339 | e3d4d252 | Michael Roth | } GuestFsfreezeMount; |
340 | e3d4d252 | Michael Roth | |
341 | e3d4d252 | Michael Roth | struct {
|
342 | e3d4d252 | Michael Roth | GuestFsfreezeStatus status; |
343 | e3d4d252 | Michael Roth | QTAILQ_HEAD(, GuestFsfreezeMount) mount_list; |
344 | e3d4d252 | Michael Roth | } guest_fsfreeze_state; |
345 | e3d4d252 | Michael Roth | |
346 | e3d4d252 | Michael Roth | /*
|
347 | e3d4d252 | Michael Roth | * Walk the mount table and build a list of local file systems
|
348 | e3d4d252 | Michael Roth | */
|
349 | e3d4d252 | Michael Roth | static int guest_fsfreeze_build_mount_list(void) |
350 | e3d4d252 | Michael Roth | { |
351 | e3d4d252 | Michael Roth | struct mntent *ment;
|
352 | e3d4d252 | Michael Roth | GuestFsfreezeMount *mount, *temp; |
353 | e3d4d252 | Michael Roth | char const *mtab = MOUNTED; |
354 | e3d4d252 | Michael Roth | FILE *fp; |
355 | e3d4d252 | Michael Roth | |
356 | e3d4d252 | Michael Roth | QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { |
357 | e3d4d252 | Michael Roth | QTAILQ_REMOVE(&guest_fsfreeze_state.mount_list, mount, next); |
358 | 7267c094 | Anthony Liguori | g_free(mount->dirname); |
359 | 7267c094 | Anthony Liguori | g_free(mount->devtype); |
360 | 7267c094 | Anthony Liguori | g_free(mount); |
361 | e3d4d252 | Michael Roth | } |
362 | e3d4d252 | Michael Roth | |
363 | e3d4d252 | Michael Roth | fp = setmntent(mtab, "r");
|
364 | e3d4d252 | Michael Roth | if (!fp) {
|
365 | e3d4d252 | Michael Roth | g_warning("fsfreeze: unable to read mtab");
|
366 | e3d4d252 | Michael Roth | return -1; |
367 | e3d4d252 | Michael Roth | } |
368 | e3d4d252 | Michael Roth | |
369 | e3d4d252 | Michael Roth | while ((ment = getmntent(fp))) {
|
370 | e3d4d252 | Michael Roth | /*
|
371 | e3d4d252 | Michael Roth | * An entry which device name doesn't start with a '/' is
|
372 | e3d4d252 | Michael Roth | * either a dummy file system or a network file system.
|
373 | e3d4d252 | Michael Roth | * Add special handling for smbfs and cifs as is done by
|
374 | e3d4d252 | Michael Roth | * coreutils as well.
|
375 | e3d4d252 | Michael Roth | */
|
376 | e3d4d252 | Michael Roth | if ((ment->mnt_fsname[0] != '/') || |
377 | e3d4d252 | Michael Roth | (strcmp(ment->mnt_type, "smbfs") == 0) || |
378 | e3d4d252 | Michael Roth | (strcmp(ment->mnt_type, "cifs") == 0)) { |
379 | e3d4d252 | Michael Roth | continue;
|
380 | e3d4d252 | Michael Roth | } |
381 | e3d4d252 | Michael Roth | |
382 | 7267c094 | Anthony Liguori | mount = g_malloc0(sizeof(GuestFsfreezeMount));
|
383 | 7267c094 | Anthony Liguori | mount->dirname = g_strdup(ment->mnt_dir); |
384 | 7267c094 | Anthony Liguori | mount->devtype = g_strdup(ment->mnt_type); |
385 | e3d4d252 | Michael Roth | |
386 | e3d4d252 | Michael Roth | QTAILQ_INSERT_TAIL(&guest_fsfreeze_state.mount_list, mount, next); |
387 | e3d4d252 | Michael Roth | } |
388 | e3d4d252 | Michael Roth | |
389 | e3d4d252 | Michael Roth | endmntent(fp); |
390 | e3d4d252 | Michael Roth | |
391 | e3d4d252 | Michael Roth | return 0; |
392 | e3d4d252 | Michael Roth | } |
393 | e3d4d252 | Michael Roth | |
394 | e3d4d252 | Michael Roth | /*
|
395 | e3d4d252 | Michael Roth | * Return status of freeze/thaw
|
396 | e3d4d252 | Michael Roth | */
|
397 | e3d4d252 | Michael Roth | GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) |
398 | e3d4d252 | Michael Roth | { |
399 | e3d4d252 | Michael Roth | return guest_fsfreeze_state.status;
|
400 | e3d4d252 | Michael Roth | } |
401 | e3d4d252 | Michael Roth | |
402 | e3d4d252 | Michael Roth | /*
|
403 | e3d4d252 | Michael Roth | * Walk list of mounted file systems in the guest, and freeze the ones which
|
404 | e3d4d252 | Michael Roth | * are real local file systems.
|
405 | e3d4d252 | Michael Roth | */
|
406 | e3d4d252 | Michael Roth | int64_t qmp_guest_fsfreeze_freeze(Error **err) |
407 | e3d4d252 | Michael Roth | { |
408 | e3d4d252 | Michael Roth | int ret = 0, i = 0; |
409 | e3d4d252 | Michael Roth | struct GuestFsfreezeMount *mount, *temp;
|
410 | e3d4d252 | Michael Roth | int fd;
|
411 | e3d4d252 | Michael Roth | char err_msg[512]; |
412 | e3d4d252 | Michael Roth | |
413 | e3d4d252 | Michael Roth | slog("guest-fsfreeze called");
|
414 | e3d4d252 | Michael Roth | |
415 | e3d4d252 | Michael Roth | if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
|
416 | e3d4d252 | Michael Roth | return 0; |
417 | e3d4d252 | Michael Roth | } |
418 | e3d4d252 | Michael Roth | |
419 | e3d4d252 | Michael Roth | ret = guest_fsfreeze_build_mount_list(); |
420 | e3d4d252 | Michael Roth | if (ret < 0) { |
421 | e3d4d252 | Michael Roth | return ret;
|
422 | e3d4d252 | Michael Roth | } |
423 | e3d4d252 | Michael Roth | |
424 | e3d4d252 | Michael Roth | /* cannot risk guest agent blocking itself on a write in this state */
|
425 | e3d4d252 | Michael Roth | disable_logging(); |
426 | e3d4d252 | Michael Roth | |
427 | e3d4d252 | Michael Roth | QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { |
428 | e3d4d252 | Michael Roth | fd = qemu_open(mount->dirname, O_RDONLY); |
429 | e3d4d252 | Michael Roth | if (fd == -1) { |
430 | e3d4d252 | Michael Roth | sprintf(err_msg, "failed to open %s, %s", mount->dirname, strerror(errno));
|
431 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); |
432 | e3d4d252 | Michael Roth | goto error;
|
433 | e3d4d252 | Michael Roth | } |
434 | e3d4d252 | Michael Roth | |
435 | e3d4d252 | Michael Roth | /* we try to cull filesytems we know won't work in advance, but other
|
436 | e3d4d252 | Michael Roth | * filesytems may not implement fsfreeze for less obvious reasons.
|
437 | e3d4d252 | Michael Roth | * these will report EOPNOTSUPP, so we simply ignore them. when
|
438 | e3d4d252 | Michael Roth | * thawing, these filesystems will return an EINVAL instead, due to
|
439 | e3d4d252 | Michael Roth | * not being in a frozen state. Other filesystem-specific
|
440 | e3d4d252 | Michael Roth | * errors may result in EINVAL, however, so the user should check the
|
441 | e3d4d252 | Michael Roth | * number * of filesystems returned here against those returned by the
|
442 | e3d4d252 | Michael Roth | * thaw operation to determine whether everything completed
|
443 | e3d4d252 | Michael Roth | * successfully
|
444 | e3d4d252 | Michael Roth | */
|
445 | e3d4d252 | Michael Roth | ret = ioctl(fd, FIFREEZE); |
446 | e3d4d252 | Michael Roth | if (ret < 0 && errno != EOPNOTSUPP) { |
447 | e3d4d252 | Michael Roth | sprintf(err_msg, "failed to freeze %s, %s", mount->dirname, strerror(errno));
|
448 | e3d4d252 | Michael Roth | error_set(err, QERR_QGA_COMMAND_FAILED, err_msg); |
449 | e3d4d252 | Michael Roth | close(fd); |
450 | e3d4d252 | Michael Roth | goto error;
|
451 | e3d4d252 | Michael Roth | } |
452 | e3d4d252 | Michael Roth | close(fd); |
453 | e3d4d252 | Michael Roth | |
454 | e3d4d252 | Michael Roth | i++; |
455 | e3d4d252 | Michael Roth | } |
456 | e3d4d252 | Michael Roth | |
457 | e3d4d252 | Michael Roth | guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_FROZEN; |
458 | e3d4d252 | Michael Roth | return i;
|
459 | e3d4d252 | Michael Roth | |
460 | e3d4d252 | Michael Roth | error:
|
461 | e3d4d252 | Michael Roth | if (i > 0) { |
462 | e3d4d252 | Michael Roth | qmp_guest_fsfreeze_thaw(NULL);
|
463 | e3d4d252 | Michael Roth | } |
464 | e3d4d252 | Michael Roth | return 0; |
465 | e3d4d252 | Michael Roth | } |
466 | e3d4d252 | Michael Roth | |
467 | e3d4d252 | Michael Roth | /*
|
468 | e3d4d252 | Michael Roth | * Walk list of frozen file systems in the guest, and thaw them.
|
469 | e3d4d252 | Michael Roth | */
|
470 | e3d4d252 | Michael Roth | int64_t qmp_guest_fsfreeze_thaw(Error **err) |
471 | e3d4d252 | Michael Roth | { |
472 | e3d4d252 | Michael Roth | int ret;
|
473 | e3d4d252 | Michael Roth | GuestFsfreezeMount *mount, *temp; |
474 | e3d4d252 | Michael Roth | int fd, i = 0; |
475 | e3d4d252 | Michael Roth | bool has_error = false; |
476 | e3d4d252 | Michael Roth | |
477 | e3d4d252 | Michael Roth | QTAILQ_FOREACH_SAFE(mount, &guest_fsfreeze_state.mount_list, next, temp) { |
478 | e3d4d252 | Michael Roth | fd = qemu_open(mount->dirname, O_RDONLY); |
479 | e3d4d252 | Michael Roth | if (fd == -1) { |
480 | e3d4d252 | Michael Roth | has_error = true;
|
481 | e3d4d252 | Michael Roth | continue;
|
482 | e3d4d252 | Michael Roth | } |
483 | e3d4d252 | Michael Roth | ret = ioctl(fd, FITHAW); |
484 | e3d4d252 | Michael Roth | if (ret < 0 && errno != EOPNOTSUPP && errno != EINVAL) { |
485 | e3d4d252 | Michael Roth | has_error = true;
|
486 | e3d4d252 | Michael Roth | close(fd); |
487 | e3d4d252 | Michael Roth | continue;
|
488 | e3d4d252 | Michael Roth | } |
489 | e3d4d252 | Michael Roth | close(fd); |
490 | e3d4d252 | Michael Roth | i++; |
491 | e3d4d252 | Michael Roth | } |
492 | e3d4d252 | Michael Roth | |
493 | e3d4d252 | Michael Roth | if (has_error) {
|
494 | e3d4d252 | Michael Roth | guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_ERROR; |
495 | e3d4d252 | Michael Roth | } else {
|
496 | e3d4d252 | Michael Roth | guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; |
497 | e3d4d252 | Michael Roth | } |
498 | e3d4d252 | Michael Roth | enable_logging(); |
499 | e3d4d252 | Michael Roth | return i;
|
500 | e3d4d252 | Michael Roth | } |
501 | e3d4d252 | Michael Roth | |
502 | e3d4d252 | Michael Roth | static void guest_fsfreeze_init(void) |
503 | e3d4d252 | Michael Roth | { |
504 | e3d4d252 | Michael Roth | guest_fsfreeze_state.status = GUEST_FSFREEZE_STATUS_THAWED; |
505 | e3d4d252 | Michael Roth | QTAILQ_INIT(&guest_fsfreeze_state.mount_list); |
506 | e3d4d252 | Michael Roth | } |
507 | e3d4d252 | Michael Roth | |
508 | e3d4d252 | Michael Roth | static void guest_fsfreeze_cleanup(void) |
509 | e3d4d252 | Michael Roth | { |
510 | e3d4d252 | Michael Roth | int64_t ret; |
511 | e3d4d252 | Michael Roth | Error *err = NULL;
|
512 | e3d4d252 | Michael Roth | |
513 | e3d4d252 | Michael Roth | if (guest_fsfreeze_state.status == GUEST_FSFREEZE_STATUS_FROZEN) {
|
514 | e3d4d252 | Michael Roth | ret = qmp_guest_fsfreeze_thaw(&err); |
515 | e3d4d252 | Michael Roth | if (ret < 0 || err) { |
516 | e3d4d252 | Michael Roth | slog("failed to clean up frozen filesystems");
|
517 | e3d4d252 | Michael Roth | } |
518 | e3d4d252 | Michael Roth | } |
519 | e3d4d252 | Michael Roth | } |
520 | 7006b9cf | Anthony Liguori | #else
|
521 | 7006b9cf | Anthony Liguori | /*
|
522 | 7006b9cf | Anthony Liguori | * Return status of freeze/thaw
|
523 | 7006b9cf | Anthony Liguori | */
|
524 | 7006b9cf | Anthony Liguori | GuestFsfreezeStatus qmp_guest_fsfreeze_status(Error **err) |
525 | 7006b9cf | Anthony Liguori | { |
526 | 9af99f1d | Michael Roth | error_set(err, QERR_UNSUPPORTED); |
527 | 7006b9cf | Anthony Liguori | |
528 | 7006b9cf | Anthony Liguori | return 0; |
529 | 7006b9cf | Anthony Liguori | } |
530 | 7006b9cf | Anthony Liguori | |
531 | 7006b9cf | Anthony Liguori | /*
|
532 | 7006b9cf | Anthony Liguori | * Walk list of mounted file systems in the guest, and freeze the ones which
|
533 | 7006b9cf | Anthony Liguori | * are real local file systems.
|
534 | 7006b9cf | Anthony Liguori | */
|
535 | 7006b9cf | Anthony Liguori | int64_t qmp_guest_fsfreeze_freeze(Error **err) |
536 | 7006b9cf | Anthony Liguori | { |
537 | 9af99f1d | Michael Roth | error_set(err, QERR_UNSUPPORTED); |
538 | 7006b9cf | Anthony Liguori | |
539 | 7006b9cf | Anthony Liguori | return 0; |
540 | 7006b9cf | Anthony Liguori | } |
541 | 7006b9cf | Anthony Liguori | |
542 | 7006b9cf | Anthony Liguori | /*
|
543 | 7006b9cf | Anthony Liguori | * Walk list of frozen file systems in the guest, and thaw them.
|
544 | 7006b9cf | Anthony Liguori | */
|
545 | 7006b9cf | Anthony Liguori | int64_t qmp_guest_fsfreeze_thaw(Error **err) |
546 | 7006b9cf | Anthony Liguori | { |
547 | 9af99f1d | Michael Roth | error_set(err, QERR_UNSUPPORTED); |
548 | 7006b9cf | Anthony Liguori | |
549 | 7006b9cf | Anthony Liguori | return 0; |
550 | 7006b9cf | Anthony Liguori | } |
551 | 7006b9cf | Anthony Liguori | #endif
|
552 | e3d4d252 | Michael Roth | |
553 | e3d4d252 | Michael Roth | /* register init/cleanup routines for stateful command groups */
|
554 | e3d4d252 | Michael Roth | void ga_command_state_init(GAState *s, GACommandState *cs)
|
555 | e3d4d252 | Michael Roth | { |
556 | e3d4d252 | Michael Roth | ga_state = s; |
557 | 7006b9cf | Anthony Liguori | #if defined(CONFIG_FSFREEZE)
|
558 | e3d4d252 | Michael Roth | ga_command_state_add(cs, guest_fsfreeze_init, guest_fsfreeze_cleanup); |
559 | 7006b9cf | Anthony Liguori | #endif
|
560 | e3d4d252 | Michael Roth | ga_command_state_add(cs, guest_file_init, NULL);
|
561 | e3d4d252 | Michael Roth | } |