aboutsummaryrefslogtreecommitdiff
path: root/qga/commands-posix.c
diff options
context:
space:
mode:
Diffstat (limited to 'qga/commands-posix.c')
-rw-r--r--qga/commands-posix.c404
1 files changed, 181 insertions, 223 deletions
diff --git a/qga/commands-posix.c b/qga/commands-posix.c
index 26008db497..7a065c4085 100644
--- a/qga/commands-posix.c
+++ b/qga/commands-posix.c
@@ -76,12 +76,159 @@ static void ga_wait_child(pid_t pid, int *status, Error **errp)
g_assert(rpid == pid);
}
+static ssize_t ga_pipe_read_str(int fd[2], char **str)
+{
+ ssize_t n, len = 0;
+ char buf[1024];
+
+ close(fd[1]);
+ fd[1] = -1;
+ while ((n = read(fd[0], buf, sizeof(buf))) != 0) {
+ if (n < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ len = -errno;
+ break;
+ }
+ }
+ *str = g_realloc(*str, len + n + 1);
+ memcpy(*str + len, buf, n);
+ len += n;
+ *str[len] = '\0';
+ }
+ close(fd[0]);
+ fd[0] = -1;
+
+ return len;
+}
+
+/*
+ * Helper to run command with input/output redirection,
+ * sending string to stdin and taking error message from
+ * stdout/err.
+ */
+static int ga_run_command(const char *argv[], const char *in_str,
+ const char *action, Error **errp)
+{
+ pid_t pid;
+ int status;
+ int retcode = -1;
+ int infd[2] = { -1, -1 };
+ int outfd[2] = { -1, -1 };
+ char *str = NULL;
+ ssize_t len = 0;
+
+ if ((in_str && !g_unix_open_pipe(infd, FD_CLOEXEC, NULL)) ||
+ !g_unix_open_pipe(outfd, FD_CLOEXEC, NULL)) {
+ error_setg(errp, "cannot create pipe FDs");
+ goto out;
+ }
+
+ pid = fork();
+ if (pid == 0) {
+ char *cherr = NULL;
+
+ setsid();
+
+ if (in_str) {
+ /* Redirect stdin to infd. */
+ close(infd[1]);
+ dup2(infd[0], 0);
+ close(infd[0]);
+ } else {
+ reopen_fd_to_null(0);
+ }
+
+ /* Redirect stdout/stderr to outfd. */
+ close(outfd[0]);
+ dup2(outfd[1], 1);
+ dup2(outfd[1], 2);
+ close(outfd[1]);
+
+ execvp(argv[0], (char *const *)argv);
+
+ /* Write the cause of failed exec to pipe for the parent to read it. */
+ cherr = g_strdup_printf("failed to exec '%s'", argv[0]);
+ perror(cherr);
+ g_free(cherr);
+ _exit(EXIT_FAILURE);
+ } else if (pid < 0) {
+ error_setg_errno(errp, errno, "failed to create child process");
+ goto out;
+ }
+
+ if (in_str) {
+ close(infd[0]);
+ infd[0] = -1;
+ if (qemu_write_full(infd[1], in_str, strlen(in_str)) !=
+ strlen(in_str)) {
+ error_setg_errno(errp, errno, "%s: cannot write to stdin pipe",
+ action);
+ goto out;
+ }
+ close(infd[1]);
+ infd[1] = -1;
+ }
+
+ len = ga_pipe_read_str(outfd, &str);
+ if (len < 0) {
+ error_setg_errno(errp, -len, "%s: cannot read from stdout/stderr pipe",
+ action);
+ goto out;
+ }
+
+ ga_wait_child(pid, &status, errp);
+ if (*errp) {
+ goto out;
+ }
+
+ if (!WIFEXITED(status)) {
+ if (len) {
+ error_setg(errp, "child process has terminated abnormally: %s",
+ str);
+ } else {
+ error_setg(errp, "child process has terminated abnormally");
+ }
+ goto out;
+ }
+
+ retcode = WEXITSTATUS(status);
+
+ if (WEXITSTATUS(status)) {
+ if (len) {
+ error_setg(errp, "child process has failed to %s: %s",
+ action, str);
+ } else {
+ error_setg(errp, "child process has failed to %s: exit status %d",
+ action, WEXITSTATUS(status));
+ }
+ goto out;
+ }
+
+out:
+ g_free(str);
+
+ if (infd[0] != -1) {
+ close(infd[0]);
+ }
+ if (infd[1] != -1) {
+ close(infd[1]);
+ }
+ if (outfd[0] != -1) {
+ close(outfd[0]);
+ }
+ if (outfd[1] != -1) {
+ close(outfd[1]);
+ }
+
+ return retcode;
+}
+
void qmp_guest_shutdown(const char *mode, Error **errp)
{
const char *shutdown_flag;
Error *local_err = NULL;
- pid_t pid;
- int status;
#ifdef CONFIG_SOLARIS
const char *powerdown_flag = "-i5";
@@ -110,67 +257,31 @@ void qmp_guest_shutdown(const char *mode, Error **errp)
return;
}
- pid = fork();
- if (pid == 0) {
- /* child, start the shutdown */
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
+ const char *argv[] = {"/sbin/shutdown",
#ifdef CONFIG_SOLARIS
- execl("/sbin/shutdown", "shutdown", shutdown_flag, "-g0", "-y",
- "hypervisor initiated shutdown", (char *)NULL);
+ shutdown_flag, "-g0", "-y",
#elif defined(CONFIG_BSD)
- execl("/sbin/shutdown", "shutdown", shutdown_flag, "+0",
- "hypervisor initiated shutdown", (char *)NULL);
+ shutdown_flag, "+0",
#else
- execl("/sbin/shutdown", "shutdown", "-h", shutdown_flag, "+0",
- "hypervisor initiated shutdown", (char *)NULL);
+ "-h", shutdown_flag, "+0",
#endif
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
+ "hypervisor initiated shutdown", (char *) NULL};
- ga_wait_child(pid, &status, &local_err);
+ ga_run_command(argv, NULL, "shutdown", &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- return;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to shutdown");
- return;
- }
-
/* succeeded */
}
void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
{
int ret;
- int status;
- pid_t pid;
Error *local_err = NULL;
struct timeval tv;
- static const char hwclock_path[] = "/sbin/hwclock";
- static int hwclock_available = -1;
-
- if (hwclock_available < 0) {
- hwclock_available = (access(hwclock_path, X_OK) == 0);
- }
-
- if (!hwclock_available) {
- error_setg(errp, QERR_UNSUPPORTED);
- return;
- }
+ const char *argv[] = {"/sbin/hwclock", has_time ? "-w" : "-s", NULL};
/* If user has passed a time, validate and set it. */
if (has_time) {
@@ -201,37 +312,12 @@ void qmp_guest_set_time(bool has_time, int64_t time_ns, Error **errp)
* just need to synchronize the hardware clock. However, if no time was
* passed, user is requesting the opposite: set the system time from the
* hardware clock (RTC). */
- pid = fork();
- if (pid == 0) {
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- /* Use '/sbin/hwclock -w' to set RTC from the system time,
- * or '/sbin/hwclock -s' to set the system time from RTC. */
- execl(hwclock_path, "hwclock", has_time ? "-w" : "-s", NULL);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
+ ga_run_command(argv, NULL, "set hardware clock to system time",
+ &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "child process has terminated abnormally");
- return;
- }
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "hwclock failed to set hardware clock to system time");
- return;
- }
}
typedef enum {
@@ -650,8 +736,6 @@ static const char *fsfreeze_hook_arg_string[] = {
static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
{
- int status;
- pid_t pid;
const char *hook;
const char *arg_str = fsfreeze_hook_arg_string[arg];
Error *local_err = NULL;
@@ -660,42 +744,15 @@ static void execute_fsfreeze_hook(FsfreezeHookArg arg, Error **errp)
if (!hook) {
return;
}
- if (access(hook, X_OK) != 0) {
- error_setg_errno(errp, errno, "can't access fsfreeze hook '%s'", hook);
- return;
- }
-
- slog("executing fsfreeze hook with arg '%s'", arg_str);
- pid = fork();
- if (pid == 0) {
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
- execl(hook, hook, arg_str, NULL);
- _exit(EXIT_FAILURE);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
+ const char *argv[] = {hook, arg_str, NULL};
- ga_wait_child(pid, &status, &local_err);
+ slog("executing fsfreeze hook with arg '%s'", arg_str);
+ ga_run_command(argv, NULL, "execute fsfreeze hook", &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
-
- if (!WIFEXITED(status)) {
- error_setg(errp, "fsfreeze hook has terminated abnormally");
- return;
- }
-
- status = WEXITSTATUS(status);
- if (status) {
- error_setg(errp, "fsfreeze hook has failed with status %d", status);
- return;
- }
}
/*
@@ -1569,8 +1626,10 @@ static GuestFilesystemInfo *build_guest_fsinfo(struct FsMount *mount,
nonroot_total = used + buf.f_bavail;
fs->used_bytes = used * fr_size;
fs->total_bytes = nonroot_total * fr_size;
+ fs->total_bytes_privileged = buf.f_blocks * fr_size;
fs->has_total_bytes = true;
+ fs->has_total_bytes_privileged = true;
fs->has_used_bytes = true;
}
@@ -1869,52 +1928,21 @@ static bool linux_sys_state_supports_mode(SuspendMode mode, Error **errp)
static void linux_sys_state_suspend(SuspendMode mode, Error **errp)
{
- Error *local_err = NULL;
+ g_autoptr(GError) local_gerr = NULL;
const char *sysfile_strs[3] = {"disk", "mem", NULL};
const char *sysfile_str = sysfile_strs[mode];
- pid_t pid;
- int status;
if (!sysfile_str) {
error_setg(errp, "unknown guest suspend mode");
return;
}
- pid = fork();
- if (!pid) {
- /* child */
- int fd;
-
- setsid();
- reopen_fd_to_null(0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
- fd = open(LINUX_SYS_STATE_FILE, O_WRONLY);
- if (fd < 0) {
- _exit(EXIT_FAILURE);
- }
-
- if (write(fd, sysfile_str, strlen(sysfile_str)) < 0) {
- _exit(EXIT_FAILURE);
- }
-
- _exit(EXIT_SUCCESS);
- } else if (pid < 0) {
- error_setg_errno(errp, errno, "failed to create child process");
- return;
- }
-
- ga_wait_child(pid, &status, &local_err);
- if (local_err) {
- error_propagate(errp, local_err);
+ if (!g_file_set_contents(LINUX_SYS_STATE_FILE, sysfile_str,
+ -1, &local_gerr)) {
+ error_setg(errp, "suspend: cannot write to '%s': %s",
+ LINUX_SYS_STATE_FILE, local_gerr->message);
return;
}
-
- if (WEXITSTATUS(status)) {
- error_setg(errp, "child process has failed to suspend");
- }
-
}
static void guest_suspend(SuspendMode mode, Error **errp)
@@ -2123,14 +2151,8 @@ void qmp_guest_set_user_password(const char *username,
Error **errp)
{
Error *local_err = NULL;
- char *passwd_path = NULL;
- pid_t pid;
- int status;
- int datafd[2] = { -1, -1 };
- char *rawpasswddata = NULL;
+ g_autofree char *rawpasswddata = NULL;
size_t rawpasswdlen;
- char *chpasswddata = NULL;
- size_t chpasswdlen;
rawpasswddata = (char *)qbase64_decode(password, -1, &rawpasswdlen, errp);
if (!rawpasswddata) {
@@ -2141,95 +2163,31 @@ void qmp_guest_set_user_password(const char *username,
if (strchr(rawpasswddata, '\n')) {
error_setg(errp, "forbidden characters in raw password");
- goto out;
+ return;
}
if (strchr(username, '\n') ||
strchr(username, ':')) {
error_setg(errp, "forbidden characters in username");
- goto out;
- }
-
-#ifdef __FreeBSD__
- chpasswddata = g_strdup(rawpasswddata);
- passwd_path = g_find_program_in_path("pw");
-#else
- chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata);
- passwd_path = g_find_program_in_path("chpasswd");
-#endif
-
- chpasswdlen = strlen(chpasswddata);
-
- if (!passwd_path) {
- error_setg(errp, "cannot find 'passwd' program in PATH");
- goto out;
- }
-
- if (!g_unix_open_pipe(datafd, FD_CLOEXEC, NULL)) {
- error_setg(errp, "cannot create pipe FDs");
- goto out;
+ return;
}
- pid = fork();
- if (pid == 0) {
- close(datafd[1]);
- /* child */
- setsid();
- dup2(datafd[0], 0);
- reopen_fd_to_null(1);
- reopen_fd_to_null(2);
-
#ifdef __FreeBSD__
- const char *h_arg;
- h_arg = (crypted) ? "-H" : "-h";
- execl(passwd_path, "pw", "usermod", "-n", username, h_arg, "0", NULL);
+ g_autofree char *chpasswdata = g_strdup(rawpasswddata);
+ const char *crypt_flag = crypted ? "-H" : "-h";
+ const char *argv[] = {"pw", "usermod", "-n", username,
+ crypt_flag, "0", NULL};
#else
- if (crypted) {
- execl(passwd_path, "chpasswd", "-e", NULL);
- } else {
- execl(passwd_path, "chpasswd", NULL);
- }
+ g_autofree char *chpasswddata = g_strdup_printf("%s:%s\n", username,
+ rawpasswddata);
+ const char *crypt_flag = crypted ? "-e" : NULL;
+ const char *argv[] = {"chpasswd", crypt_flag, NULL};
#endif
- _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);
+ ga_run_command(argv, chpasswddata, "set user password", &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]);
+ return;
}
}
#else /* __linux__ || __FreeBSD__ */