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