From 215a2771a7b6b29037ee8deba484815d816b6fdd Mon Sep 17 00:00:00 2001 From: "Daniel P. Berrange" Date: Wed, 11 Feb 2015 11:26:12 +0000 Subject: qga: add guest-set-user-password command Add a new 'guest-set-user-password' command for changing the password of guest OS user accounts. This command is needed to enable OpenStack to support its API for changing the admin password of guests running on KVM/QEMU. It is not practical to provide a command at the QEMU level explicitly targetting administrator account password change only, since different guest OS have different names for the admin account. While UNIX systems use 'root', Windows systems typically use 'Administrator' and even that can be renamed. Higher level apps like OpenStack have the ability to figure out the correct admin account name since they have info that QEMU/libvirt do not. The command accepts either the clear text password string, encoded in base64 to make it 8-bit safe in JSON: $ echo -n "123456" | base64 MTIzNDU2 $ virsh -c qemu:///system qemu-agent-command f21x86_64 \ '{ "execute": "guest-set-user-password", "arguments": { "crypted": false, "username": "root", "password": "MTIzNDU2" } }' {"return":{}} Or a password that has already been run though a crypt(3) like algorithm appropriate for the guest, again then base64 encoded: $ echo -n '$6$n01A2Tau$e...snip...DfMOP7of9AJ1I8q0' | base64 JDYkb...snip...YT2Ey $ virsh -c qemu:///system qemu-agent-command f21x86_64 \ '{ "execute": "guest-set-user-password", "arguments": { "crypted": true, "username": "root", "password": "JDYkb...snip...YT2Ey" } }' NB windows support is desirable, but not implemented in this patch. Signed-off-by: Daniel P. Berrange Signed-off-by: Michael Roth --- qga/commands-posix.c | 110 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 110 insertions(+) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index f6f3e3cd8e..57409d0ea6 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1875,6 +1875,108 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return processed; } +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + Error *local_err = NULL; + char *passwd_path = NULL; + pid_t pid; + int status; + int datafd[2] = { -1, -1 }; + char *rawpasswddata = NULL; + size_t rawpasswdlen; + char *chpasswddata = NULL; + size_t chpasswdlen; + + rawpasswddata = (char *)g_base64_decode(password, &rawpasswdlen); + rawpasswddata = g_renew(char, rawpasswddata, rawpasswdlen + 1); + rawpasswddata[rawpasswdlen] = '\0'; + + if (strchr(rawpasswddata, '\n')) { + error_setg(errp, "forbidden characters in raw password"); + goto out; + } + + if (strchr(username, '\n') || + strchr(username, ':')) { + error_setg(errp, "forbidden characters in username"); + goto out; + } + + chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); + chpasswdlen = strlen(chpasswddata); + + passwd_path = g_find_program_in_path("chpasswd"); + + if (!passwd_path) { + error_setg(errp, "cannot find 'passwd' program in PATH"); + goto out; + } + + if (pipe(datafd) < 0) { + error_setg(errp, "cannot create pipe FDs"); + goto out; + } + + pid = fork(); + if (pid == 0) { + close(datafd[1]); + /* child */ + setsid(); + dup2(datafd[0], 0); + reopen_fd_to_null(1); + reopen_fd_to_null(2); + + if (crypted) { + execle(passwd_path, "chpasswd", "-e", NULL, environ); + } else { + execle(passwd_path, "chpasswd", NULL, environ); + } + _exit(EXIT_FAILURE); + } else if (pid < 0) { + error_setg_errno(errp, errno, "failed to create child process"); + goto out; + } + close(datafd[0]); + datafd[0] = -1; + + if (qemu_write_full(datafd[1], chpasswddata, chpasswdlen) != chpasswdlen) { + error_setg_errno(errp, errno, "cannot write new account password"); + goto out; + } + close(datafd[1]); + datafd[1] = -1; + + ga_wait_child(pid, &status, &local_err); + if (local_err) { + error_propagate(errp, local_err); + goto out; + } + + if (!WIFEXITED(status)) { + error_setg(errp, "child process has terminated abnormally"); + goto out; + } + + if (WEXITSTATUS(status)) { + error_setg(errp, "child process has failed to set user password"); + goto out; + } + +out: + g_free(chpasswddata); + g_free(rawpasswddata); + g_free(passwd_path); + if (datafd[0] != -1) { + close(datafd[0]); + } + if (datafd[1] != -1) { + close(datafd[1]); + } +} + #else /* defined(__linux__) */ void qmp_guest_suspend_disk(Error **errp) @@ -1910,6 +2012,14 @@ int64_t qmp_guest_set_vcpus(GuestLogicalProcessorList *vcpus, Error **errp) return -1; } +void qmp_guest_set_user_password(const char *username, + const char *password, + bool crypted, + Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); +} + #endif #if !defined(CONFIG_FSFREEZE) -- cgit 1.4.1 From 85b6f6f53596fd29eee0a6d2475c6a4322eede6b Mon Sep 17 00:00:00 2001 From: Simon Zolin Date: Fri, 6 Feb 2015 20:59:54 +0300 Subject: guest agent: guest-file-open: refactoring Moved the code that sets non-blocking flag on fd into a separate function. Signed-off-by: Simon Zolin Reviewed-by: Roman Kagan Signed-off-by: Denis V. Lunev CC: Michael Roth CC: Eric Blake Signed-off-by: Michael Roth --- qga/commands-posix.c | 31 +++++++++++++++++++++++-------- 1 file changed, 23 insertions(+), 8 deletions(-) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 57409d0ea6..ed527a3d3d 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -376,13 +376,33 @@ safe_open_or_create(const char *path, const char *mode, Error **errp) return NULL; } +static int guest_file_toggle_flags(int fd, int flags, bool set, Error **err) +{ + int ret, old_flags; + + old_flags = fcntl(fd, F_GETFL); + if (old_flags == -1) { + error_set_errno(err, errno, QERR_QGA_COMMAND_FAILED, + "failed to fetch filehandle flags"); + return -1; + } + + ret = fcntl(fd, F_SETFL, set ? (old_flags | flags) : (old_flags & ~flags)); + if (ret == -1) { + error_set_errno(err, errno, QERR_QGA_COMMAND_FAILED, + "failed to set filehandle flags"); + return -1; + } + + return ret; +} + int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, Error **errp) { FILE *fh; Error *local_err = NULL; - int fd; - int64_t ret = -1, handle; + int64_t handle; if (!has_mode) { mode = "r"; @@ -397,12 +417,7 @@ int64_t qmp_guest_file_open(const char *path, bool has_mode, const char *mode, /* set fd non-blocking to avoid common use cases (like reading from a * named pipe) from hanging the agent */ - fd = fileno(fh); - ret = fcntl(fd, F_GETFL); - ret = fcntl(fd, F_SETFL, ret | O_NONBLOCK); - if (ret == -1) { - error_setg_errno(errp, errno, "failed to make file '%s' non-blocking", - path); + if (guest_file_toggle_flags(fileno(fh), O_NONBLOCK, true, errp) < 0) { fclose(fh); return -1; } -- cgit 1.4.1 From a065aaa9204ecd4a0d18f5eae49aa350a5f76b63 Mon Sep 17 00:00:00 2001 From: zhanghailiang Date: Thu, 22 Jan 2015 10:40:02 +0800 Subject: qga: introduce three guest memory block commmands with stubs Introduce three new guest commands: guest-get-memory-blocks, guest-set-memory-blocks, guest-get-memory-block-size. With these three commands, we can support online/offline guest's memory block (logical memory hotplug/unplug) as required from host. Signed-off-by: zhanghailiang *generalized guest-get-memory-block-size to get-get-memory-block-info for future extensibility Signed-off-by: Michael Roth --- qga/commands-posix.c | 38 ++++++++++++++++ qga/commands-win32.c | 19 ++++++++ qga/qapi-schema.json | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 180 insertions(+) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index ed527a3d3d..b7b7efe2ec 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1992,6 +1992,25 @@ out: } } +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + #else /* defined(__linux__) */ void qmp_guest_suspend_disk(Error **errp) @@ -2035,6 +2054,25 @@ void qmp_guest_set_user_password(const char *username, error_set(errp, QERR_UNSUPPORTED); } +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + #endif #if !defined(CONFIG_FSFREEZE) diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 9af7950aef..990a8ddf53 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -684,6 +684,25 @@ void qmp_guest_set_user_password(const char *username, error_set(errp, QERR_UNSUPPORTED); } +GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockResponseList * +qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + +GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) +{ + error_set(errp, QERR_UNSUPPORTED); + return NULL; +} + /* add unsupported commands to the blacklist */ GList *ga_command_blacklist_init(GList *blacklist) { diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 5117b65b26..33e7d0676d 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -765,3 +765,126 @@ ## { 'command': 'guest-set-user-password', 'data': { 'username': 'str', 'password': 'str', 'crypted': 'bool' } } + +# @GuestMemoryBlock: +# +# @phys-index: Arbitrary guest-specific unique identifier of the MEMORY BLOCK. +# +# @online: Whether the MEMORY BLOCK is enabled in guest. +# +# @can-offline: #optional Whether offlining the MEMORY BLOCK is possible. +# This member is always filled in by the guest agent when the +# structure is returned, and always ignored on input (hence it +# can be omitted then). +# +# Since: 2.3 +## +{ 'type': 'GuestMemoryBlock', + 'data': {'phys-index': 'uint64', + 'online': 'bool', + '*can-offline': 'bool'} } + +## +# @guest-get-memory-blocks: +# +# Retrieve the list of the guest's memory blocks. +# +# This is a read-only operation. +# +# Returns: The list of all memory blocks the guest knows about. +# Each memory block is put on the list exactly once, but their order +# is unspecified. +# +# Since: 2.3 +## +{ 'command': 'guest-get-memory-blocks', + 'returns': ['GuestMemoryBlock'] } + +## +# @GuestMemoryBlockResponseType +# +# An enumeration of memory block operation result. +# +# @sucess: the operation of online/offline memory block is successful. +# @not-found: can't find the corresponding memoryXXX directory in sysfs. +# @operation-not-supported: for some old kernels, it does not support +# online or offline memory block. +# @operation-failed: the operation of online/offline memory block fails, +# because of some errors happen. +# +# Since: 2.3 +## +{ 'enum': 'GuestMemoryBlockResponseType', + 'data': ['success', 'not-found', 'operation-not-supported', + 'operation-failed'] } + +## +# @GuestMemoryBlockResponse: +# +# @phys-index: same with the 'phys-index' member of @GuestMemoryBlock. +# +# @response: the result of memory block operation. +# +# @error-code: #optional the error number. +# When memory block operation fails, we assign the value of +# 'errno' to this member, it indicates what goes wrong. +# When the operation succeeds, it will be omitted. +# +# Since: 2.3 +## +{ 'type': 'GuestMemoryBlockResponse', + 'data': { 'phys-index': 'uint64', + 'response': 'GuestMemoryBlockResponseType', + '*error-code': 'int' }} + +## +# @guest-set-memory-blocks: +# +# Attempt to reconfigure (currently: enable/disable) state of memory blocks +# inside the guest. +# +# The input list is processed node by node in order. In each node @phys-index +# is used to look up the guest MEMORY BLOCK, for which @online specifies the +# requested state. The set of distinct @phys-index's is only required to be a +# subset of the guest-supported identifiers. There's no restriction on list +# length or on repeating the same @phys-index (with possibly different @online +# field). +# Preferably the input list should describe a modified subset of +# @guest-get-memory-blocks' return value. +# +# Returns: The operation results, it is a list of @GuestMemoryBlockResponse, +# which is corresponding to the input list. +# +# Note: it will return NULL if the @mem-blks list was empty on input, +# or there is an error, and in this case, guest state will not be +# changed. +# +# Since: 2.3 +## +{ 'command': 'guest-set-memory-blocks', + 'data': {'mem-blks': ['GuestMemoryBlock'] }, + 'returns': ['GuestMemoryBlockResponse'] } + +# @GuestMemoryBlockInfo: +# +# @size: the size (in bytes) of the guest memory blocks, +# which are the minimal units of memory block online/offline +# operations (also called Logical Memory Hotplug). +# +# Since: 2.3 +## +{ 'type': 'GuestMemoryBlockInfo', + 'data': {'size': 'uint64'} } + +## +# @guest-get-memory-block-info: +# +# Get information relating to guest memory blocks. +# +# Returns: memory block size in bytes. +# Returns: @GuestMemoryBlockInfo +# +# Since 2.3 +## +{ 'command': 'guest-get-memory-block-info', + 'returns': 'GuestMemoryBlockInfo' } -- cgit 1.4.1 From bd240fca42d5f072fb758a71720d9de9990ac553 Mon Sep 17 00:00:00 2001 From: zhanghailiang Date: Thu, 22 Jan 2015 10:40:03 +0800 Subject: qga: implement qmp_guest_get_memory_blocks() for Linux with sysfs We can get guest's memory block information by using command "guest-get-memory-blocks", the returned value contains a list of memory block info, such as phys-index, online state, can-offline info. Signed-off-by: zhanghailiang *replaced guest-triggerable assertion with an error msg Signed-off-by: Michael Roth --- qga/commands-posix.c | 233 ++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 232 insertions(+), 1 deletion(-) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index b7b7efe2ec..5d4101dc79 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1992,9 +1992,240 @@ out: } } +static void ga_read_sysfs_file(int dirfd, const char *pathname, char *buf, + int size, Error **errp) +{ + int fd; + int res; + + errno = 0; + fd = openat(dirfd, pathname, O_RDONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + res = pread(fd, buf, size, 0); + if (res == -1) { + error_setg_errno(errp, errno, "pread sysfs file \"%s\"", pathname); + } else if (res == 0) { + error_setg(errp, "pread sysfs file \"%s\": unexpected EOF", pathname); + } + close(fd); +} + +static void ga_write_sysfs_file(int dirfd, const char *pathname, + const char *buf, int size, Error **errp) +{ + int fd; + + errno = 0; + fd = openat(dirfd, pathname, O_WRONLY); + if (fd == -1) { + error_setg_errno(errp, errno, "open sysfs file \"%s\"", pathname); + return; + } + + if (pwrite(fd, buf, size, 0) == -1) { + error_setg_errno(errp, errno, "pwrite sysfs file \"%s\"", pathname); + } + + close(fd); +} + +/* Transfer online/offline status between @mem_blk and the guest system. + * + * On input either @errp or *@errp must be NULL. + * + * In system-to-@mem_blk direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - W: mem_blk->online + * - W: mem_blk->can_offline + * + * In @mem_blk-to-system direction, the following @mem_blk fields are accessed: + * - R: mem_blk->phys_index + * - R: mem_blk->online + *- R: mem_blk->can_offline + * Written members remain unmodified on error. + */ +static void transfer_memory_block(GuestMemoryBlock *mem_blk, bool sys2memblk, + GuestMemoryBlockResponse *result, + Error **errp) +{ + char *dirpath; + int dirfd; + char *status; + Error *local_err = NULL; + + if (!sys2memblk) { + DIR *dp; + + if (!result) { + error_setg(errp, "Internal error, 'result' should not be NULL"); + return; + } + errno = 0; + dp = opendir("/sys/devices/system/memory/"); + /* if there is no 'memory' directory in sysfs, + * we think this VM does not support online/offline memory block, + * any other solution? + */ + if (!dp && errno == ENOENT) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + goto out1; + } + closedir(dp); + } + + dirpath = g_strdup_printf("/sys/devices/system/memory/memory%" PRId64 "/", + mem_blk->phys_index); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + if (sys2memblk) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + } else { + if (errno == ENOENT) { + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_NOT_FOUND; + } else { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + g_free(dirpath); + goto out1; + } + g_free(dirpath); + + status = g_malloc0(10); + ga_read_sysfs_file(dirfd, "state", status, 10, &local_err); + if (local_err) { + /* treat with sysfs file that not exist in old kernel */ + if (errno == ENOENT) { + error_free(local_err); + if (sys2memblk) { + mem_blk->online = true; + mem_blk->can_offline = false; + } else if (!mem_blk->online) { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_NOT_SUPPORTED; + } + } else { + if (sys2memblk) { + error_propagate(errp, local_err); + } else { + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + } + } + goto out2; + } + + if (sys2memblk) { + char removable = '0'; + + mem_blk->online = (strncmp(status, "online", 6) == 0); + + ga_read_sysfs_file(dirfd, "removable", &removable, 1, &local_err); + if (local_err) { + /* if no 'removable' file, it does't support offline mem blk */ + if (errno == ENOENT) { + error_free(local_err); + mem_blk->can_offline = false; + } else { + error_propagate(errp, local_err); + } + } else { + mem_blk->can_offline = (removable != '0'); + } + } else { + if (mem_blk->online != (strncmp(status, "online", 6) == 0)) { + char *new_state = mem_blk->online ? g_strdup("online") : + g_strdup("offline"); + + ga_write_sysfs_file(dirfd, "state", new_state, strlen(new_state), + &local_err); + g_free(new_state); + if (local_err) { + error_free(local_err); + result->response = + GUEST_MEMORY_BLOCK_RESPONSE_TYPE_OPERATION_FAILED; + goto out2; + } + + result->response = GUEST_MEMORY_BLOCK_RESPONSE_TYPE_SUCCESS; + result->has_error_code = false; + } /* otherwise pretend successful re-(on|off)-lining */ + } + g_free(status); + close(dirfd); + return; + +out2: + g_free(status); + close(dirfd); +out1: + if (!sys2memblk) { + result->has_error_code = true; + result->error_code = errno; + } +} + GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) { - error_set(errp, QERR_UNSUPPORTED); + GuestMemoryBlockList *head, **link; + Error *local_err = NULL; + struct dirent *de; + DIR *dp; + + head = NULL; + link = &head; + + dp = opendir("/sys/devices/system/memory/"); + if (!dp) { + error_setg_errno(errp, errno, "Can't open directory" + "\"/sys/devices/system/memory/\"\n"); + return NULL; + } + + /* Note: the phys_index of memory block may be discontinuous, + * this is because a memblk is the unit of the Sparse Memory design, which + * allows discontinuous memory ranges (ex. NUMA), so here we should + * traverse the memory block directory. + */ + while ((de = readdir(dp)) != NULL) { + GuestMemoryBlock *mem_blk; + GuestMemoryBlockList *entry; + + if ((strncmp(de->d_name, "memory", 6) != 0) || + !(de->d_type & DT_DIR)) { + continue; + } + + mem_blk = g_malloc0(sizeof *mem_blk); + /* The d_name is "memoryXXX", phys_index is block id, same as XXX */ + mem_blk->phys_index = strtoul(&de->d_name[6], NULL, 10); + mem_blk->has_can_offline = true; /* lolspeak ftw */ + transfer_memory_block(mem_blk, true, NULL, &local_err); + + entry = g_malloc0(sizeof *entry); + entry->value = mem_blk; + + *link = entry; + link = &entry->next; + } + + closedir(dp); + if (local_err == NULL) { + /* there's no guest with zero memory blocks */ + if (head == NULL) { + error_setg(errp, "guest reported zero memory blocks!"); + } + return head; + } + + qapi_free_GuestMemoryBlockList(head); + error_propagate(errp, local_err); return NULL; } -- cgit 1.4.1 From 32ca7927c7d66371abafb4cabfab9438a5905784 Mon Sep 17 00:00:00 2001 From: zhanghailiang Date: Thu, 22 Jan 2015 10:40:04 +0800 Subject: qga: implement qmp_guest_set_memory_blocks() for Linux with sysfs We can change guest's online/offline state of memory blocks, by using command 'guest-set-memory-blocks'. Signed-off-by: zhanghailiang Signed-off-by: Michael Roth --- qga/commands-posix.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 5d4101dc79..0fd5d959d2 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2232,7 +2232,35 @@ GuestMemoryBlockList *qmp_guest_get_memory_blocks(Error **errp) GuestMemoryBlockResponseList * qmp_guest_set_memory_blocks(GuestMemoryBlockList *mem_blks, Error **errp) { - error_set(errp, QERR_UNSUPPORTED); + GuestMemoryBlockResponseList *head, **link; + Error *local_err = NULL; + + head = NULL; + link = &head; + + while (mem_blks != NULL) { + GuestMemoryBlockResponse *result; + GuestMemoryBlockResponseList *entry; + GuestMemoryBlock *current_mem_blk = mem_blks->value; + + result = g_malloc0(sizeof(*result)); + result->phys_index = current_mem_blk->phys_index; + transfer_memory_block(current_mem_blk, false, result, &local_err); + if (local_err) { /* should never happen */ + goto err; + } + entry = g_malloc0(sizeof *entry); + entry->value = result; + + *link = entry; + link = &entry->next; + mem_blks = mem_blks->next; + } + + return head; +err: + qapi_free_GuestMemoryBlockResponseList(head); + error_propagate(errp, local_err); return NULL; } -- cgit 1.4.1 From ef82b60be13b18198b84a2157f59c50fd53f5408 Mon Sep 17 00:00:00 2001 From: zhanghailiang Date: Thu, 22 Jan 2015 10:40:05 +0800 Subject: qga: implement qmp_guest_get_memory_block_info() for Linux with sysfs This conveys general information about guest memory blocks. Currently, just the memory block size. The size of a memory block is architecture dependent, it represents the logical unit upon which memory online/offline operations are to be performed. Signed-off-by: zhanghailiang *generalized guest-get-memory-block-size to get-get-memory-block-info for future extensibility Signed-off-by: Michael Roth --- qga/commands-posix.c | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 0fd5d959d2..6575c49191 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2266,8 +2266,35 @@ err: GuestMemoryBlockInfo *qmp_guest_get_memory_block_info(Error **errp) { - error_set(errp, QERR_UNSUPPORTED); - return NULL; + Error *local_err = NULL; + char *dirpath; + int dirfd; + char *buf; + GuestMemoryBlockInfo *info; + + dirpath = g_strdup_printf("/sys/devices/system/memory/"); + dirfd = open(dirpath, O_RDONLY | O_DIRECTORY); + if (dirfd == -1) { + error_setg_errno(errp, errno, "open(\"%s\")", dirpath); + g_free(dirpath); + return NULL; + } + g_free(dirpath); + + buf = g_malloc0(20); + ga_read_sysfs_file(dirfd, "block_size_bytes", buf, 20, &local_err); + if (local_err) { + g_free(buf); + error_propagate(errp, local_err); + return NULL; + } + + info = g_new0(GuestMemoryBlockInfo, 1); + info->size = strtol(buf, NULL, 16); /* the unit is bytes */ + + g_free(buf); + + return info; } #else /* defined(__linux__) */ -- cgit 1.4.1 From 0dd38a03f5e1498aabf7d053a9fab792a5eeec5c Mon Sep 17 00:00:00 2001 From: zhanghailiang Date: Thu, 22 Jan 2015 10:40:06 +0800 Subject: qga: add memory block command that unsupported For memory block command, we only support for linux with sysfs. Signed-off-by: zhanghailiang Reviewed-by: Michael Roth Signed-off-by: Michael Roth --- qga/commands-posix.c | 4 +++- qga/commands-win32.c | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'qga/commands-posix.c') diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 6575c49191..d5bb5cb5da 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2415,7 +2415,9 @@ GList *ga_command_blacklist_init(GList *blacklist) const char *list[] = { "guest-suspend-disk", "guest-suspend-ram", "guest-suspend-hybrid", "guest-network-get-interfaces", - "guest-get-vcpus", "guest-set-vcpus", NULL}; + "guest-get-vcpus", "guest-set-vcpus", + "guest-get-memory-blocks", "guest-set-memory-blocks", + "guest-get-memory-block-size", NULL}; char **p = (char **)list; while (*p) { diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 990a8ddf53..0238fbbcad 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -710,6 +710,8 @@ GList *ga_command_blacklist_init(GList *blacklist) "guest-suspend-hybrid", "guest-network-get-interfaces", "guest-get-vcpus", "guest-set-vcpus", "guest-set-user-password", + "guest-get-memory-blocks", "guest-set-memory-blocks", + "guest-get-memory-block-size", "guest-fsfreeze-freeze-list", "guest-get-fsinfo", "guest-fstrim", NULL}; char **p = (char **)list_unsupported; -- cgit 1.4.1