From patchwork Wed May 1 07:43:32 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650272 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 25FEDC10F1A for ; Wed, 1 May 2024 07:46:00 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24d5-0000pF-NO; Wed, 01 May 2024 03:43:51 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d4-0000og-0A for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:50 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d2-0003XG-0b for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:49 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549426; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=a071wxoJFSMOnEW+thcNMYVdUE4CQQSmwvCAmyMk60E=; b=O7a8TI3ClQ16bEYa7Vi0Y9+S+gyy8noX+7n8CvEtP+yG+7tOwzbRM/XgTMCl6rh/jr1EHN bkvd8Mf+LMly3XfTjZa1Ex1SR5rL681lHWYJJKCuQuFaZLXAGWf2Y4cqfl/pUbKKac9zfX qXFJ3fXuns0lvYIb5j0mIJmH5FC9HTo= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-8-btxkCw0uORahVPX7_Hkz2w-1; Wed, 01 May 2024 03:43:44 -0400 X-MC-Unique: btxkCw0uORahVPX7_Hkz2w-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 9DE8C80017B; Wed, 1 May 2024 07:43:44 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id A5DFC40C5C2; Wed, 1 May 2024 07:43:43 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 1/9] qga: guest-get-fsinfo: add optional 'total-bytes-privileged' field Date: Wed, 1 May 2024 10:43:32 +0300 Message-ID: <20240501074340.19641-2-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.133.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev Since the commit 25b5ff1a86 ("qga: add mountpoint usage info to GuestFilesystemInfo") we have 2 values reported in guest-get-fsinfo: used = (f_blocks - f_bfree), total = (f_blocks - f_bfree + f_bavail) as returned by statvfs(3). While on Windows guests that's all we can get with GetDiskFreeSpaceExA(), on POSIX guests we might also be interested in total file system size, as it's visible for root user. Let's add an optional field 'total-bytes-privileged' to GuestFilesystemInfo struct, which'd only be reported on POSIX and represent f_blocks value as returned by statvfs(3). While here, also tweak the docs to reflect better where those values come from. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-2-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 2 ++ qga/commands-win32.c | 1 + qga/qapi-schema.json | 7 +++++-- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 26008db497..7df2d72e9f 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1569,8 +1569,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; } diff --git a/qga/commands-win32.c b/qga/commands-win32.c index 6242737b00..6fee0e1e6f 100644 --- a/qga/commands-win32.c +++ b/qga/commands-win32.c @@ -1143,6 +1143,7 @@ static GuestFilesystemInfo *build_guest_fsinfo(char *guid, Error **errp) fs = g_malloc(sizeof(*fs)); fs->name = g_strdup(guid); fs->has_total_bytes = false; + fs->has_total_bytes_privileged = false; fs->has_used_bytes = false; if (len == 0) { fs->mountpoint = g_strdup("System Reserved"); diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index d5af155007..1b9039e4f5 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1026,7 +1026,10 @@ # # @used-bytes: file system used bytes (since 3.0) # -# @total-bytes: non-root file system total bytes (since 3.0) +# @total-bytes: filesystem capacity in bytes for unprivileged users (since 3.0) +# +# @total-bytes-privileged: filesystem capacity in bytes for privileged users +# (since 9.1) # # @disk: an array of disk hardware information that the volume lies # on, which may be empty if the disk type is not supported @@ -1036,7 +1039,7 @@ { 'struct': 'GuestFilesystemInfo', 'data': {'name': 'str', 'mountpoint': 'str', 'type': 'str', '*used-bytes': 'uint64', '*total-bytes': 'uint64', - 'disk': ['GuestDiskAddress']} } + '*total-bytes-privileged': 'uint64', 'disk': ['GuestDiskAddress']} } ## # @guest-get-fsinfo: From patchwork Wed May 1 07:43:33 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650267 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 65096C4345F for ; Wed, 1 May 2024 07:44:32 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24d8-0000pw-Fq; Wed, 01 May 2024 03:43:54 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d7-0000pm-Ls for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:53 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d5-0003Xs-PE for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:53 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549431; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=qcSvdu/9ifEVQ6HZsFa7vRWpP1PMefXiZ5wn5zNky6I=; b=DeRZZ00l/odTCDHyDgPfNeVHrtnU7f1Idux3I3vfX4KuP+iYqq5Bg5gwehNClVlplw7VtR qFUlKcaVQs/lT1bhSuQZWEIppcigmlF5rHfcGtu2jIUi1+yuyu2hPa0E8bMDH0iKnUZViG wNiI5PiD096++YEs7WrhcNHpJZ+jPyg= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-327-RlRvHKZ-OuC-Ct4u-34Ihg-1; Wed, 01 May 2024 03:43:46 -0400 X-MC-Unique: RlRvHKZ-OuC-Ct4u-34Ihg-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id DB3641C0513D; Wed, 1 May 2024 07:43:45 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E3F1C40BAA2; Wed, 1 May 2024 07:43:44 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 2/9] qga: introduce ga_run_command() helper for guest cmd execution Date: Wed, 1 May 2024 10:43:33 +0300 Message-ID: <20240501074340.19641-3-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.129.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev When executing guest commands in *nix environment, we repeat the same fork/exec pattern multiple times. Let's just separate it into a single helper which would also be able to feed input data into the launched process' stdin. This way we can avoid code duplication. To keep the history more bisectable, let's replace qmp commands implementations one by one. Also add G_GNUC_UNUSED attribute to the helper and remove it in the next commit. Originally-by: Yuri Pudgorodskiy Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-3-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 150 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 7df2d72e9f..9b1bdf194c 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -76,6 +76,156 @@ 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. + */ +G_GNUC_UNUSED +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; From patchwork Wed May 1 07:43:34 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650273 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 30CEAC41513 for ; Wed, 1 May 2024 07:46:00 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dA-0000qb-6O; Wed, 01 May 2024 03:43:56 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d8-0000pv-CT for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:54 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d6-0003Xr-16 for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:54 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549431; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=xda9vZ/4orUHg9RUs409tFjFC6HKizyWCmMyjF5kot0=; b=d3N2fJY6udmE343LImKJxHdQh1U12nMjHykCR4/YpV/Q1yD2Vzbwt+y/YGuUB+XXMvcO4m H2z2LbY2rNOFMUw2I2Jf7ra06ZSCY9KLU9vTvwGiE428VuWBxJeP1XWTj33M0n+ssqFVLL Q2l4sABJmGKUDOfnTCCtSuAkNJ58bco= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-593-jKoAtm4SMpSzinv62u3C9A-1; Wed, 01 May 2024 03:43:47 -0400 X-MC-Unique: jKoAtm4SMpSzinv62u3C9A-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 24B6018065B2; Wed, 1 May 2024 07:43:47 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 2C7BF477F80; Wed, 1 May 2024 07:43:46 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 3/9] qga/commands-posix: qmp_guest_shutdown: use ga_run_command helper Date: Wed, 1 May 2024 10:43:34 +0300 Message-ID: <20240501074340.19641-4-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.129.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev Also remove the G_GNUC_UNUSED attribute added in the previous commit from the helper. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-4-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 39 ++++++--------------------------------- 1 file changed, 6 insertions(+), 33 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9b1bdf194c..cb9eed9a0b 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -108,7 +108,6 @@ static ssize_t ga_pipe_read_str(int fd[2], char **str) * sending string to stdin and taking error message from * stdout/err. */ -G_GNUC_UNUSED static int ga_run_command(const char *argv[], const char *in_str, const char *action, Error **errp) { @@ -230,8 +229,6 @@ 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"; @@ -260,46 +257,22 @@ 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 */ } From patchwork Wed May 1 07:43:35 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650271 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 1D40AC4345F for ; Wed, 1 May 2024 07:46:00 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dD-0000ro-By; Wed, 01 May 2024 03:43:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d9-0000q4-0Y for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:55 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d7-0003Yo-4a for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:54 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549432; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=5ApRFGbbknxQSccF48FIq+n+vGsbiLpevcGTik4+zII=; b=ah8UjfnXTw8uWjgKQnEVoLzlYQbZ6bV3JuAByw194WFzGE6ZzUx9rPE0w7qo6hGOuXyazP VgFNBOIEiCBgqt+Z5qWfVIsthu0SGJTz/ksOkkUj0ePzf8ghU54tiTTOUI/6AnTiBzMXxd U1GcxbDpoDNzL0GC4bMKCDYFBWnXKH0= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-100-vaHHOTMDPv6FBeeCVnMK3w-1; Wed, 01 May 2024 03:43:48 -0400 X-MC-Unique: vaHHOTMDPv6FBeeCVnMK3w-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6448D800CA2; Wed, 1 May 2024 07:43:48 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 6AC0340BAA2; Wed, 1 May 2024 07:43:47 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 4/9] qga/commands-posix: qmp_guest_set_time: use ga_run_command helper Date: Wed, 1 May 2024 10:43:35 +0300 Message-ID: <20240501074340.19641-5-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.133.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev There's no need to check for the existence of "/sbin/hwclock", the exec() call will do that for us. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-5-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 43 +++---------------------------------------- 1 file changed, 3 insertions(+), 40 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index cb9eed9a0b..545f3c99dc 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -279,21 +279,9 @@ void qmp_guest_shutdown(const char *mode, Error **errp) 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) { @@ -324,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 { From patchwork Wed May 1 07:43:36 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650270 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 4A313C4345F for ; Wed, 1 May 2024 07:45:34 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dM-0000tQ-1d; Wed, 01 May 2024 03:44:08 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dK-0000tI-Gm for qemu-devel@nongnu.org; Wed, 01 May 2024 03:44:06 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d7-0003ZX-Rt for qemu-devel@nongnu.org; Wed, 01 May 2024 03:44:06 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549433; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=QnRftYsFP/MYCrapZD+5MWRcJHZMXvARHgEsjlOLw0Q=; b=H7PQK4a0oJif1dRVjOSpjhBllMJ9udYCAFnayTpGeJZRpOmxD62TizyGswX6D8WuGZaEzw kydeGTW+NeSF7VhVCUFoAMYHZoTGaime90IMRySECiF+RCS15BaTCNI5G3D5aSg0TdYHkB 7WuoRXGEVFpJi3V9z+4XyNflv7MNdCU= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-659-xgyA_vZlPfKKLdUpTNIqJQ-1; Wed, 01 May 2024 03:43:49 -0400 X-MC-Unique: xgyA_vZlPfKKLdUpTNIqJQ-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id A4A0F29AA3AC; Wed, 1 May 2024 07:43:49 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id ABFEA492BC7; Wed, 1 May 2024 07:43:48 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 5/9] qga/commands-posix: execute_fsfreeze_hook: use ga_run_command helper Date: Wed, 1 May 2024 10:43:36 +0300 Message-ID: <20240501074340.19641-6-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.129.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev There's no need to check for the existence of the hook executable, as the exec() call will do that for us. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-6-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 35 +++-------------------------------- 1 file changed, 3 insertions(+), 32 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 545f3c99dc..9b993772f5 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -736,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; @@ -746,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; - } } /* From patchwork Wed May 1 07:43:37 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650268 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 2B60FC4345F for ; Wed, 1 May 2024 07:44:51 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dB-0000r7-FK; Wed, 01 May 2024 03:43:57 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dA-0000qa-45 for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:56 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d7-0003ZS-FT for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:55 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549432; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=v98YNa/eB+VuXpNUsDmwf5/Lc+Hzywry1owXyzt6Ids=; b=VtAvpwW+TgoYccD/vvm1cGJgOg6/uTNoyNVxl4tYRN/hRc0Spfpa7zGPjyLsiBjapUVSvj 7lRYZ5URMuKPGMtDhl/NaQn6iJ68Ssn7vJT9KrTOCV2+JkpKgrx0GBLWbcJc4/9wCMfReO XyRvuqFbkTmxi8YnfKh1AUQgKAZOf1Y= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-18-I_0IzbowNTWZaDAA24Skhg-1; Wed, 01 May 2024 03:43:51 -0400 X-MC-Unique: I_0IzbowNTWZaDAA24Skhg-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id E36D629AA3AC; Wed, 1 May 2024 07:43:50 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id E9E8E40BAA2; Wed, 1 May 2024 07:43:49 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 6/9] qga/commands-posix: don't do fork()/exec() when suspending via sysfs Date: Wed, 1 May 2024 10:43:37 +0300 Message-ID: <20240501074340.19641-7-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.133.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev Since commit 246d76eba ("qga: guest_suspend: decoupling pm-utils and sys logic") pm-utils logic is running in a separate child from the sysfs logic. Now when suspending via sysfs we don't really need to do that in a separate process as we only need to perform one write to /sys/power/state. Let's just use g_file_set_contents() to simplify things here. Suggested-by: Daniel P. Berrangé Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-7-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 41 +++++------------------------------------ 1 file changed, 5 insertions(+), 36 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9b993772f5..9910957ff5 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -1928,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) From patchwork Wed May 1 07:43:38 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650266 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 690B0C19F53 for ; Wed, 1 May 2024 07:44:32 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dB-0000rM-P0; Wed, 01 May 2024 03:43:57 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dA-0000ql-Hj for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:56 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24d8-0003aW-NY for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:56 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549434; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=7ownYj/qlgNbv9VvIzzLsn6x+0ehrNzewTvcneFeFHA=; b=giWd3Bs3fed2C7i0mTHIQE3X9gmt/HraeCBVTA60S+oTSEE7YgJ92A0zz7pMfCwvuuGMgz /oN5NLse3m2PZILVkHjVKGf3kRQ2cpsxgZtbL5iG219uRn211G9G9sQ2eyn/oIbC5S1CVR QNmCLkUPTqVZeJVjkZ1UpNqS8rBGLao= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-647-cp-O5migNWWmGz11JWzTMw-1; Wed, 01 May 2024 03:43:52 -0400 X-MC-Unique: cp-O5migNWWmGz11JWzTMw-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 2D080380621E; Wed, 1 May 2024 07:43:52 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 349B740C5C2; Wed, 1 May 2024 07:43:51 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 7/9] qga/commands-posix: qmp_guest_set_user_password: use ga_run_command helper Date: Wed, 1 May 2024 10:43:38 +0300 Message-ID: <20240501074340.19641-8-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.129.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: Andrey Drobyshev There's no need to check for the existence of the "chpasswd", "pw" executables, as the exec() call will do that for us. Signed-off-by: Andrey Drobyshev Reviewed-by: Daniel P. Berrangé Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240320161648.158226-8-andrey.drobyshev@virtuozzo.com Signed-off-by: Konstantin Kostiuk --- qga/commands-posix.c | 96 ++++++-------------------------------------- 1 file changed, 13 insertions(+), 83 deletions(-) diff --git a/qga/commands-posix.c b/qga/commands-posix.c index 9910957ff5..7a065c4085 100644 --- a/qga/commands-posix.c +++ b/qga/commands-posix.c @@ -2151,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) { @@ -2169,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; + return; } #ifdef __FreeBSD__ - chpasswddata = g_strdup(rawpasswddata); - passwd_path = g_find_program_in_path("pw"); + 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 - chpasswddata = g_strdup_printf("%s:%s\n", username, rawpasswddata); - passwd_path = g_find_program_in_path("chpasswd"); + 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 - 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; - } - - 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); -#else - if (crypted) { - execl(passwd_path, "chpasswd", "-e", NULL); - } else { - execl(passwd_path, "chpasswd", 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__ */ From patchwork Wed May 1 07:43:39 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650274 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id 31030C4345F for ; Wed, 1 May 2024 07:46:03 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dD-0000sQ-U2; Wed, 01 May 2024 03:43:59 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dB-0000rQ-Vw for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:58 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.133.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dA-0003bU-0Y for qemu-devel@nongnu.org; Wed, 01 May 2024 03:43:57 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549435; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=K8UPukpeq7culmPO9Ed8TR0loPb2uyArjRKae+2zKzk=; b=Xp2mvgzIWqp/6C9hHeDJTpfx7akfWZKo+MYkocbk4vuEcauEnH2bgrIYgpK+qMvsAEBFHR QYQybebrpnl85bHpcItQr+JssIkFQnQljgJ0DShY3JWt2zrrdTAliDS0zCkJcOYzVBOHwB wo/4tb2YOvAVsAiVd1AHfzFgUl8YlPk= Received: from mimecast-mx02.redhat.com (mimecast-mx02.redhat.com [66.187.233.88]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-494-BTl6b01sOgm8WIldMvhRKA-1; Wed, 01 May 2024 03:43:53 -0400 X-MC-Unique: BTl6b01sOgm8WIldMvhRKA-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id 6B83E8002EA; Wed, 1 May 2024 07:43:53 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 72B11492BC7; Wed, 1 May 2024 07:43:52 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 8/9] qga: Refactor common SSH functions Date: Wed, 1 May 2024 10:43:39 +0300 Message-ID: <20240501074340.19641-9-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.133.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: aidaleuc Message-Id: <20240424144029.30665-2-aidan_leuck@selinc.com> MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 8bit In preparation of a Windows implementation, move the non-POSIX specific code to commands-common-ssh. Signed-off-by: Aidan Leuck Reviewed-by: Philippe Mathieu-Daudé Tested-by: Dehan Meng Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240424144029.30665-2-aidan_leuck@selinc.com Signed-off-by: Konstantin Kostiuk --- qga/commands-common-ssh.c | 50 +++++++++++++++++++++++++++++++++++++++ qga/commands-common-ssh.h | 10 ++++++++ qga/commands-posix-ssh.c | 47 +----------------------------------- qga/meson.build | 3 ++- 4 files changed, 63 insertions(+), 47 deletions(-) create mode 100644 qga/commands-common-ssh.c create mode 100644 qga/commands-common-ssh.h diff --git a/qga/commands-common-ssh.c b/qga/commands-common-ssh.c new file mode 100644 index 0000000000..537869fb98 --- /dev/null +++ b/qga/commands-common-ssh.c @@ -0,0 +1,50 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include "qapi/error.h" +#include "commands-common-ssh.h" + +GStrv read_authkeys(const char *path, Error **errp) +{ + g_autoptr(GError) err = NULL; + g_autofree char *contents = NULL; + + if (!g_file_get_contents(path, &contents, NULL, &err)) { + error_setg(errp, "failed to read '%s': %s", path, err->message); + return NULL; + } + + return g_strsplit(contents, "\n", -1); +} + +bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) +{ + size_t n = 0; + strList *k; + + for (k = keys; k != NULL; k = k->next) { + if (!check_openssh_pub_key(k->value, errp)) { + return false; + } + n++; + } + + if (nkeys) { + *nkeys = n; + } + return true; +} + +bool check_openssh_pub_key(const char *key, Error **errp) +{ + /* simple sanity-check, we may want more? */ + if (!key || key[0] == '#' || strchr(key, '\n')) { + error_setg(errp, "invalid OpenSSH public key: '%s'", key); + return false; + } + + return true; +} diff --git a/qga/commands-common-ssh.h b/qga/commands-common-ssh.h new file mode 100644 index 0000000000..14d955fa84 --- /dev/null +++ b/qga/commands-common-ssh.h @@ -0,0 +1,10 @@ +/* + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qapi/qapi-builtin-types.h" + +GStrv read_authkeys(const char *path, Error **errp); +bool check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp); +bool check_openssh_pub_key(const char *key, Error **errp); diff --git a/qga/commands-posix-ssh.c b/qga/commands-posix-ssh.c index 236f80de44..dd2ecb453a 100644 --- a/qga/commands-posix-ssh.c +++ b/qga/commands-posix-ssh.c @@ -9,6 +9,7 @@ #include #include +#include "commands-common-ssh.h" #include "qapi/error.h" #include "qga-qapi-commands.h" @@ -80,37 +81,6 @@ mkdir_for_user(const char *path, const struct passwd *p, return true; } -static bool -check_openssh_pub_key(const char *key, Error **errp) -{ - /* simple sanity-check, we may want more? */ - if (!key || key[0] == '#' || strchr(key, '\n')) { - error_setg(errp, "invalid OpenSSH public key: '%s'", key); - return false; - } - - return true; -} - -static bool -check_openssh_pub_keys(strList *keys, size_t *nkeys, Error **errp) -{ - size_t n = 0; - strList *k; - - for (k = keys; k != NULL; k = k->next) { - if (!check_openssh_pub_key(k->value, errp)) { - return false; - } - n++; - } - - if (nkeys) { - *nkeys = n; - } - return true; -} - static bool write_authkeys(const char *path, const GStrv keys, const struct passwd *p, Error **errp) @@ -139,21 +109,6 @@ write_authkeys(const char *path, const GStrv keys, return true; } -static GStrv -read_authkeys(const char *path, Error **errp) -{ - g_autoptr(GError) err = NULL; - g_autofree char *contents = NULL; - - if (!g_file_get_contents(path, &contents, NULL, &err)) { - error_setg(errp, "failed to read '%s': %s", path, err->message); - return NULL; - } - - return g_strsplit(contents, "\n", -1); - -} - void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, bool has_reset, bool reset, diff --git a/qga/meson.build b/qga/meson.build index 46c1d83d7f..bc5ffb54ba 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -66,6 +66,7 @@ qga_ss.add(files( 'guest-agent-command-state.c', 'main.c', 'cutils.c', + 'commands-common-ssh.c' )) if host_os == 'windows' qga_ss.add(files( @@ -186,7 +187,7 @@ test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) # this when an alternative is implemented or when the underlying glib # issue is identified/fix if host_os != 'windows' and not get_option('fuzzing') - srcs = [files('commands-posix-ssh.c')] + srcs = [files('commands-common-ssh.c', 'commands-posix-ssh.c')] i = 0 foreach output: qga_qapi_outputs if output.startswith('qga-qapi-types') or output.startswith('qga-qapi-visit') From patchwork Wed May 1 07:43:40 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Konstantin Kostiuk X-Patchwork-Id: 13650269 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from lists.gnu.org (lists.gnu.org [209.51.188.17]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.lore.kernel.org (Postfix) with ESMTPS id C0C4DC4345F for ; Wed, 1 May 2024 07:45:28 +0000 (UTC) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1s24dG-0000sr-Gk; Wed, 01 May 2024 03:44:02 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dE-0000sf-Qh for qemu-devel@nongnu.org; Wed, 01 May 2024 03:44:00 -0400 Received: from us-smtp-delivery-124.mimecast.com ([170.10.129.124]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1s24dB-0003cb-K1 for qemu-devel@nongnu.org; Wed, 01 May 2024 03:44:00 -0400 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=redhat.com; s=mimecast20190719; t=1714549436; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references; bh=V95VvNUKcwklW5/m2XQRtVT4kf9oS3rT1sG4LJMpqEI=; b=bSp47fN+ME4CqIpN0lBsj2pqvCTXQoCJB+4Aq316+7Q/q2/P9/Wy4nn+WUD0FBHTMHVCyt e6g+i88yEmvb4F66qmRd1taTXwNywBobuP+ss8wHKUsvnoyXe8+XsuY+opW3dB705mosUX pj0Ti5mZVZej6BY4Dp92cAhVRnrfghg= Received: from mimecast-mx02.redhat.com (mx-ext.redhat.com [66.187.233.73]) by relay.mimecast.com with ESMTP with STARTTLS (version=TLSv1.3, cipher=TLS_AES_256_GCM_SHA384) id us-mta-549-pGVWgkkRMDe9q53wY22LMQ-1; Wed, 01 May 2024 03:43:55 -0400 X-MC-Unique: pGVWgkkRMDe9q53wY22LMQ-1 Received: from smtp.corp.redhat.com (int-mx10.intmail.prod.int.rdu2.redhat.com [10.11.54.10]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by mimecast-mx02.redhat.com (Postfix) with ESMTPS id F17DB380621F; Wed, 1 May 2024 07:43:54 +0000 (UTC) Received: from srv1.redhat.com (unknown [10.45.224.97]) by smtp.corp.redhat.com (Postfix) with ESMTPS id B157A40C5C2; Wed, 1 May 2024 07:43:53 +0000 (UTC) From: Konstantin Kostiuk To: qemu-devel@nongnu.org, Peter Maydell , Stefan Hajnoczi Subject: [PULL 9/9] qga: Implement SSH commands for Windows Date: Wed, 1 May 2024 10:43:40 +0300 Message-ID: <20240501074340.19641-10-kkostiuk@redhat.com> In-Reply-To: <20240501074340.19641-1-kkostiuk@redhat.com> References: <20240501074340.19641-1-kkostiuk@redhat.com> MIME-Version: 1.0 X-Scanned-By: MIMEDefang 3.4.1 on 10.11.54.10 Received-SPF: pass client-ip=170.10.129.124; envelope-from=kkostiuk@redhat.com; helo=us-smtp-delivery-124.mimecast.com X-Spam_score_int: -30 X-Spam_score: -3.1 X-Spam_bar: --- X-Spam_report: (-3.1 / 5.0 requ) BAYES_00=-1.9, DKIMWL_WL_HIGH=-0.987, DKIM_SIGNED=0.1, DKIM_VALID=-0.1, DKIM_VALID_AU=-0.1, DKIM_VALID_EF=-0.1, RCVD_IN_DNSWL_NONE=-0.0001, RCVD_IN_MSPIKE_H4=0.001, RCVD_IN_MSPIKE_WL=0.001, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org Sender: qemu-devel-bounces+qemu-devel=archiver.kernel.org@nongnu.org From: aidaleuc Signed-off-by: Aidan Leuck Tested-by: Dehan Meng Reviewed-by: Konstantin Kostiuk Link: https://lore.kernel.org/r/20240424144029.30665-3-aidan_leuck@selinc.com Signed-off-by: Konstantin Kostiuk --- qga/commands-windows-ssh.c | 712 +++++++++++++++++++++++++++++++++++++ qga/commands-windows-ssh.h | 26 ++ qga/meson.build | 5 +- qga/qapi-schema.json | 17 +- 4 files changed, 749 insertions(+), 11 deletions(-) create mode 100644 qga/commands-windows-ssh.c create mode 100644 qga/commands-windows-ssh.h diff --git a/qga/commands-windows-ssh.c b/qga/commands-windows-ssh.c new file mode 100644 index 0000000000..6a642e3ba8 --- /dev/null +++ b/qga/commands-windows-ssh.c @@ -0,0 +1,712 @@ +/* + * QEMU Guest Agent win32-specific command implementations for SSH keys. + * The implementation is opinionated and expects the SSH implementation to + * be OpenSSH. + * + * Copyright Schweitzer Engineering Laboratories. 2024 + * + * Authors: + * Aidan Leuck + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include "qemu/osdep.h" +#include +#include + +#include "commands-common-ssh.h" +#include "commands-windows-ssh.h" +#include "guest-agent-core.h" +#include "limits.h" +#include "lmaccess.h" +#include "lmapibuf.h" +#include "lmerr.h" +#include "qapi/error.h" + +#include "qga-qapi-commands.h" +#include "sddl.h" +#include "shlobj.h" +#include "userenv.h" + +#define AUTHORIZED_KEY_FILE "authorized_keys" +#define AUTHORIZED_KEY_FILE_ADMIN "administrators_authorized_keys" +#define LOCAL_SYSTEM_SID "S-1-5-18" +#define ADMIN_SID "S-1-5-32-544" + +/* + * Frees userInfo structure. This implements the g_auto cleanup + * for the structure. + */ +void free_userInfo(PWindowsUserInfo info) +{ + g_free(info->sshDirectory); + g_free(info->authorizedKeyFile); + LocalFree(info->SSID); + g_free(info->username); + g_free(info); +} + +/* + * Gets the admin SSH folder for OpenSSH. OpenSSH does not store + * the authorized_key file in the users home directory for security reasons and + * instead stores it at %PROGRAMDATA%/ssh. This function returns the path to + * that directory on the users machine + * + * parameters: + * errp -> error structure to set when an error occurs + * returns: The path to the ssh folder in %PROGRAMDATA% or NULL if an error + * occurred. + */ +static char *get_admin_ssh_folder(Error **errp) +{ + /* Allocate memory for the program data path */ + g_autofree char *programDataPath = NULL; + char *authkeys_path = NULL; + PWSTR pgDataW = NULL; + g_autoptr(GError) gerr = NULL; + + /* Get the KnownFolderPath on the machine. */ + HRESULT folderResult = + SHGetKnownFolderPath(&FOLDERID_ProgramData, 0, NULL, &pgDataW); + if (folderResult != S_OK) { + error_setg(errp, "Failed to retrieve ProgramData folder"); + return NULL; + } + + /* Convert from a wide string back to a standard character string. */ + programDataPath = g_utf16_to_utf8(pgDataW, -1, NULL, NULL, &gerr); + CoTaskMemFree(pgDataW); + if (!programDataPath) { + error_setg(errp, + "Failed converting ProgramData folder path to UTF-16 %s", + gerr->message); + return NULL; + } + + /* Build the path to the file. */ + authkeys_path = g_build_filename(programDataPath, "ssh", NULL); + return authkeys_path; +} + +/* + * Gets the path to the SSH folder for the specified user. If the user is an + * admin it returns the ssh folder located at %PROGRAMDATA%/ssh. If the user is + * not an admin it returns %USERPROFILE%/.ssh + * + * parameters: + * username -> Username to get the SSH folder for + * isAdmin -> Whether the user is an admin or not + * errp -> Error structure to set any errors that occur. + * returns: path to the ssh folder as a string. + */ +static char *get_ssh_folder(const char *username, const bool isAdmin, + Error **errp) +{ + DWORD maxSize = MAX_PATH; + g_autofree char *profilesDir = g_new0(char, maxSize); + + if (isAdmin) { + return get_admin_ssh_folder(errp); + } + + /* If not an Admin the SSH key is in the user directory. */ + /* Get the user profile directory on the machine. */ + BOOL ret = GetProfilesDirectory(profilesDir, &maxSize); + if (!ret) { + error_setg_win32(errp, GetLastError(), + "failed to retrieve profiles directory"); + return NULL; + } + + /* Builds the filename */ + return g_build_filename(profilesDir, username, ".ssh", NULL); +} + +/* + * Creates an entry for the user so they can access the ssh folder in their + * userprofile. + * + * parameters: + * userInfo -> Information about the current user + * pACL -> Pointer to an ACL structure + * errp -> Error structure to set any errors that occur + * returns -> 1 on success, 0 otherwise + */ +static bool create_acl_user(PWindowsUserInfo userInfo, PACL *pACL, Error **errp) +{ + const int aclSize = 1; + PACL newACL = NULL; + EXPLICIT_ACCESS eAccess[1]; + PSID userPSID = NULL; + + /* Get a pointer to the internal SID object in Windows */ + bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID", + userInfo->username); + goto error; + } + + /* Set the permissions for the user. */ + eAccess[0].grfAccessPermissions = GENERIC_ALL; + eAccess[0].grfAccessMode = SET_ACCESS; + eAccess[0].grfInheritance = NO_INHERITANCE; + eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER; + eAccess[0].Trustee.ptstrName = (LPTSTR)userPSID; + + /* Set the ACL entries */ + DWORD setResult; + + /* + * If we are given a pointer that is already initialized, then we can merge + * the existing entries instead of overwriting them. + */ + if (*pACL) { + setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &newACL); + } else { + setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &newACL); + } + + if (setResult != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set ACL entries for user %s %lu", + userInfo->username, setResult); + goto error; + } + + /* Free any old memory since we are going to overwrite the users pointer. */ + LocalFree(*pACL); + *pACL = newACL; + + LocalFree(userPSID); + return true; +error: + LocalFree(userPSID); + return false; +} + +/* + * Creates a base ACL for both normal users and admins to share + * pACL -> Pointer to an ACL structure + * errp -> Error structure to set any errors that occur + * returns: 1 on success, 0 otherwise + */ +static bool create_acl_base(PACL *pACL, Error **errp) +{ + PSID adminGroupPSID = NULL; + PSID systemPSID = NULL; + + const int aclSize = 2; + EXPLICIT_ACCESS eAccess[2]; + + /* Create an entry for the system user. */ + const char *systemSID = LOCAL_SYSTEM_SID; + bool converted = ConvertStringSidToSid(systemSID, &systemPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve system SID"); + goto error; + } + + /* set permissions for system user */ + eAccess[0].grfAccessPermissions = GENERIC_ALL; + eAccess[0].grfAccessMode = SET_ACCESS; + eAccess[0].grfInheritance = NO_INHERITANCE; + eAccess[0].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[0].Trustee.TrusteeType = TRUSTEE_IS_USER; + eAccess[0].Trustee.ptstrName = (LPTSTR)systemPSID; + + /* Create an entry for the admin user. */ + const char *adminSID = ADMIN_SID; + converted = ConvertStringSidToSid(adminSID, &adminGroupPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve Admin SID"); + goto error; + } + + /* Set permissions for admin group. */ + eAccess[1].grfAccessPermissions = GENERIC_ALL; + eAccess[1].grfAccessMode = SET_ACCESS; + eAccess[1].grfInheritance = NO_INHERITANCE; + eAccess[1].Trustee.TrusteeForm = TRUSTEE_IS_SID; + eAccess[1].Trustee.TrusteeType = TRUSTEE_IS_GROUP; + eAccess[1].Trustee.ptstrName = (LPTSTR)adminGroupPSID; + + /* Put the entries in an ACL object. */ + PACL pNewACL = NULL; + DWORD setResult; + + /* + *If we are given a pointer that is already initialized, then we can merge + *the existing entries instead of overwriting them. + */ + if (*pACL) { + setResult = SetEntriesInAcl(aclSize, eAccess, *pACL, &pNewACL); + } else { + setResult = SetEntriesInAcl(aclSize, eAccess, NULL, &pNewACL); + } + + if (setResult != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set base ACL entries for system user and " + "admin group %lu", + setResult); + goto error; + } + + LocalFree(adminGroupPSID); + LocalFree(systemPSID); + + /* Free any old memory since we are going to overwrite the users pointer. */ + LocalFree(*pACL); + + *pACL = pNewACL; + + return true; + +error: + LocalFree(adminGroupPSID); + LocalFree(systemPSID); + return false; +} + +/* + * Sets the access control on the authorized_keys file and any ssh folders that + * need to be created. For administrators the required permissions on the + * file/folders are that only administrators and the LocalSystem account can + * access the folders. For normal user accounts only the specified user, + * LocalSystem and Administrators can have access to the key. + * + * parameters: + * userInfo -> pointer to structure that contains information about the user + * PACL -> pointer to an access control structure that will be set upon + * successful completion of the function. + * errp -> error structure that will be set upon error. + * returns: 1 upon success 0 upon failure. + */ +static bool create_acl(PWindowsUserInfo userInfo, PACL *pACL, Error **errp) +{ + /* + * Creates a base ACL that both admins and users will share + * This adds the Administrators group and the SYSTEM group + */ + if (!create_acl_base(pACL, errp)) { + return false; + } + + /* + * If the user is not an admin give the user creating the key permission to + * access the file. + */ + if (!userInfo->isAdmin) { + if (!create_acl_user(userInfo, pACL, errp)) { + return false; + } + + return true; + } + + return true; +} +/* + * Create the SSH directory for the user and d sets appropriate permissions. + * In general the directory will be %PROGRAMDATA%/ssh if the user is an admin. + * %USERPOFILE%/.ssh if not an admin + * + * parameters: + * userInfo -> Contains information about the user + * errp -> Structure that will contain errors if the function fails. + * returns: zero upon failure, 1 upon success + */ +static bool create_ssh_directory(WindowsUserInfo *userInfo, Error **errp) +{ + PACL pNewACL = NULL; + g_autofree PSECURITY_DESCRIPTOR pSD = NULL; + + /* Gets the appropriate ACL for the user */ + if (!create_acl(userInfo, &pNewACL, errp)) { + goto error; + } + + /* Allocate memory for a security descriptor */ + pSD = g_malloc(SECURITY_DESCRIPTOR_MIN_LENGTH); + if (!InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION)) { + error_setg_win32(errp, GetLastError(), + "Failed to initialize security descriptor"); + goto error; + } + + /* Associate the security descriptor with the ACL permissions. */ + if (!SetSecurityDescriptorDacl(pSD, TRUE, pNewACL, FALSE)) { + error_setg_win32(errp, GetLastError(), + "Failed to set security descriptor ACL"); + goto error; + } + + /* Set the security attributes on the folder */ + SECURITY_ATTRIBUTES sAttr; + sAttr.bInheritHandle = FALSE; + sAttr.nLength = sizeof(SECURITY_ATTRIBUTES); + sAttr.lpSecurityDescriptor = pSD; + + /* Create the directory with the created permissions */ + BOOL created = CreateDirectory(userInfo->sshDirectory, &sAttr); + if (!created) { + error_setg_win32(errp, GetLastError(), "failed to create directory %s", + userInfo->sshDirectory); + goto error; + } + + /* Free memory */ + LocalFree(pNewACL); + return true; +error: + LocalFree(pNewACL); + return false; +} + +/* + * Sets permissions on the authorized_key_file that is created. + * + * parameters: userInfo -> Information about the user + * errp -> error structure that will contain errors upon failure + * returns: 1 upon success, zero upon failure. + */ +static bool set_file_permissions(PWindowsUserInfo userInfo, Error **errp) +{ + PACL pACL = NULL; + PSID userPSID; + + /* Creates the access control structure */ + if (!create_acl(userInfo, &pACL, errp)) { + goto error; + } + + /* Get the PSID structure for the user based off the string SID. */ + bool converted = ConvertStringSidToSid(userInfo->SSID, &userPSID); + if (!converted) { + error_setg_win32(errp, GetLastError(), "failed to retrieve user %s SID", + userInfo->username); + goto error; + } + + /* Prevents permissions from being inherited and use the DACL provided. */ + const SE_OBJECT_TYPE securityBitFlags = + DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION; + + /* Set the ACL on the file. */ + if (SetNamedSecurityInfo(userInfo->authorizedKeyFile, SE_FILE_OBJECT, + securityBitFlags, userPSID, NULL, pACL, + NULL) != ERROR_SUCCESS) { + error_setg_win32(errp, GetLastError(), + "failed to set file security for file %s", + userInfo->authorizedKeyFile); + goto error; + } + + LocalFree(pACL); + LocalFree(userPSID); + return true; + +error: + LocalFree(pACL); + LocalFree(userPSID); + + return false; +} + +/* + * Writes the specified keys to the authenticated keys file. + * parameters: + * userInfo: Information about the user we are writing the authkeys file to. + * authkeys: Array of keys to write to disk + * errp: Error structure that will contain any errors if they occur. + * returns: 1 if successful, 0 otherwise. + */ +static bool write_authkeys(WindowsUserInfo *userInfo, GStrv authkeys, + Error **errp) +{ + g_autofree char *contents = NULL; + g_autoptr(GError) err = NULL; + + contents = g_strjoinv("\n", authkeys); + + if (!g_file_set_contents(userInfo->authorizedKeyFile, contents, -1, &err)) { + error_setg(errp, "failed to write to '%s': %s", + userInfo->authorizedKeyFile, err->message); + return false; + } + + if (!set_file_permissions(userInfo, errp)) { + return false; + } + + return true; +} + +/* + * Retrieves information about a Windows user by their username + * + * parameters: + * userInfo -> Double pointer to a WindowsUserInfo structure. Upon success, it + * will be allocated with information about the user and need to be freed. + * username -> Name of the user to lookup. + * errp -> Contains any errors that occur. + * returns: 1 upon success, 0 upon failure. + */ +static bool get_user_info(PWindowsUserInfo *userInfo, const char *username, + Error **errp) +{ + DWORD infoLevel = 4; + LPUSER_INFO_4 uBuf = NULL; + g_autofree wchar_t *wideUserName = NULL; + g_autoptr(GError) gerr = NULL; + PSID psid = NULL; + + /* + * Converts a string to a Windows wide string since the GetNetUserInfo + * function requires it. + */ + wideUserName = g_utf8_to_utf16(username, -1, NULL, NULL, &gerr); + if (!wideUserName) { + goto error; + } + + /* allocate data */ + PWindowsUserInfo uData = g_new0(WindowsUserInfo, 1); + + /* Set pointer so it can be cleaned up by the callee, even upon error. */ + *userInfo = uData; + + /* Find the information */ + NET_API_STATUS result = + NetUserGetInfo(NULL, wideUserName, infoLevel, (LPBYTE *)&uBuf); + if (result != NERR_Success) { + /* Give a friendlier error message if the user was not found. */ + if (result == NERR_UserNotFound) { + error_setg(errp, "User %s was not found", username); + goto error; + } + + error_setg(errp, + "Received unexpected error when asking for user info: Error " + "Code %lu", + result); + goto error; + } + + /* Get information from the buffer returned by NetUserGetInfo. */ + uData->username = g_strdup(username); + uData->isAdmin = uBuf->usri4_priv == USER_PRIV_ADMIN; + psid = uBuf->usri4_user_sid; + + char *sidStr = NULL; + + /* + * We store the string representation of the SID not SID structure in + * memory. Callees wanting to use the SID structure should call + * ConvertStringSidToSID. + */ + if (!ConvertSidToStringSid(psid, &sidStr)) { + error_setg_win32(errp, GetLastError(), + "failed to get SID string for user %s", username); + goto error; + } + + /* Store the SSID */ + uData->SSID = sidStr; + + /* Get the SSH folder for the user. */ + char *sshFolder = get_ssh_folder(username, uData->isAdmin, errp); + if (sshFolder == NULL) { + goto error; + } + + /* Get the authorized key file path */ + const char *authorizedKeyFile = + uData->isAdmin ? AUTHORIZED_KEY_FILE_ADMIN : AUTHORIZED_KEY_FILE; + char *authorizedKeyPath = + g_build_filename(sshFolder, authorizedKeyFile, NULL); + uData->sshDirectory = sshFolder; + uData->authorizedKeyFile = authorizedKeyPath; + + /* Free */ + NetApiBufferFree(uBuf); + return true; +error: + if (uBuf) { + NetApiBufferFree(uBuf); + } + + return false; +} + +/* + * Gets the list of authorized keys for a user. + * + * parameters: + * username -> Username to retrieve the keys for. + * errp -> Error structure that will display any errors through QMP. + * returns: List of keys associated with the user. + */ +GuestAuthorizedKeys *qmp_guest_ssh_get_authorized_keys(const char *username, + Error **errp) +{ + GuestAuthorizedKeys *keys = NULL; + g_auto(GStrv) authKeys = NULL; + g_autoptr(GuestAuthorizedKeys) ret = NULL; + g_auto(PWindowsUserInfo) userInfo = NULL; + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return NULL; + } + + /* Reads authkeys for the user */ + authKeys = read_authkeys(userInfo->authorizedKeyFile, errp); + if (authKeys == NULL) { + return NULL; + } + + /* Set the GuestAuthorizedKey struct with keys from the file */ + ret = g_new0(GuestAuthorizedKeys, 1); + for (int i = 0; authKeys[i] != NULL; i++) { + g_strstrip(authKeys[i]); + if (!authKeys[i][0] || authKeys[i][0] == '#') { + continue; + } + + QAPI_LIST_PREPEND(ret->keys, g_strdup(authKeys[i])); + } + + /* + * Steal the pointer because it is up for the callee to deallocate the + * memory. + */ + keys = g_steal_pointer(&ret); + return keys; +} + +/* + * Adds an ssh key for a user. + * + * parameters: + * username -> User to add the SSH key to + * strList -> Array of keys to add to the list + * has_reset -> Whether the keys have been reset + * reset -> Boolean to reset the keys (If this is set the existing list will be + * cleared) and the other key reset. errp -> Pointer to an error structure that + * will get returned over QMP if anything goes wrong. + */ +void qmp_guest_ssh_add_authorized_keys(const char *username, strList *keys, + bool has_reset, bool reset, Error **errp) +{ + g_auto(PWindowsUserInfo) userInfo = NULL; + g_auto(GStrv) authkeys = NULL; + strList *k; + size_t nkeys, nauthkeys; + + /* Make sure the keys given are valid */ + if (!check_openssh_pub_keys(keys, &nkeys, errp)) { + return; + } + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return; + } + + /* Determine whether we should reset the keys */ + reset = has_reset && reset; + if (!reset) { + /* Read existing keys into memory */ + authkeys = read_authkeys(userInfo->authorizedKeyFile, NULL); + } + + /* Check that the SSH key directory exists for the user. */ + if (!g_file_test(userInfo->sshDirectory, G_FILE_TEST_IS_DIR)) { + BOOL success = create_ssh_directory(userInfo, errp); + if (!success) { + return; + } + } + + /* Reallocates the buffer to fit the new keys. */ + nauthkeys = authkeys ? g_strv_length(authkeys) : 0; + authkeys = g_realloc_n(authkeys, nauthkeys + nkeys + 1, sizeof(char *)); + + /* zero out the memory for the reallocated buffer */ + memset(authkeys + nauthkeys, 0, (nkeys + 1) * sizeof(char *)); + + /* Adds the keys */ + for (k = keys; k != NULL; k = k->next) { + /* Check that the key doesn't already exist */ + if (g_strv_contains((const gchar *const *)authkeys, k->value)) { + continue; + } + + authkeys[nauthkeys++] = g_strdup(k->value); + } + + /* Write the authkeys to the file. */ + write_authkeys(userInfo, authkeys, errp); +} + +/* + * Removes an SSH key for a user + * + * parameters: + * username -> Username to remove the key from + * strList -> List of strings to remove + * errp -> Contains any errors that occur. + */ +void qmp_guest_ssh_remove_authorized_keys(const char *username, strList *keys, + Error **errp) +{ + g_auto(PWindowsUserInfo) userInfo = NULL; + g_autofree struct passwd *p = NULL; + g_autofree GStrv new_keys = NULL; /* do not own the strings */ + g_auto(GStrv) authkeys = NULL; + GStrv a; + size_t nkeys = 0; + + /* Validates the keys passed in by the user */ + if (!check_openssh_pub_keys(keys, NULL, errp)) { + return; + } + + /* Gets user information */ + if (!get_user_info(&userInfo, username, errp)) { + return; + } + + /* Reads the authkeys for the user */ + authkeys = read_authkeys(userInfo->authorizedKeyFile, errp); + if (authkeys == NULL) { + return; + } + + /* Create a new buffer to hold the keys */ + new_keys = g_new0(char *, g_strv_length(authkeys) + 1); + for (a = authkeys; *a != NULL; a++) { + strList *k; + + /* Filters out keys that are equal to ones the user specified. */ + for (k = keys; k != NULL; k = k->next) { + if (g_str_equal(k->value, *a)) { + break; + } + } + + if (k != NULL) { + continue; + } + + new_keys[nkeys++] = *a; + } + + /* Write the new authkeys to the file. */ + write_authkeys(userInfo, new_keys, errp); +} diff --git a/qga/commands-windows-ssh.h b/qga/commands-windows-ssh.h new file mode 100644 index 0000000000..40ac67c4d9 --- /dev/null +++ b/qga/commands-windows-ssh.h @@ -0,0 +1,26 @@ +/* + * Header file for commands-windows-ssh.c + * + * Copyright Schweitzer Engineering Laboratories. 2024 + * + * Authors: + * Aidan Leuck + * + * This work is licensed under the terms of the GNU GPL, version 2 or later. + * See the COPYING file in the top-level directory. + */ + +#include +#include +typedef struct WindowsUserInfo { + char *sshDirectory; + char *authorizedKeyFile; + char *username; + char *SSID; + bool isAdmin; +} WindowsUserInfo; + +typedef WindowsUserInfo *PWindowsUserInfo; + +void free_userInfo(PWindowsUserInfo info); +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(PWindowsUserInfo, free_userInfo, NULL); diff --git a/qga/meson.build b/qga/meson.build index bc5ffb54ba..587ec4e5e8 100644 --- a/qga/meson.build +++ b/qga/meson.build @@ -73,7 +73,8 @@ if host_os == 'windows' 'channel-win32.c', 'commands-win32.c', 'service-win32.c', - 'vss-win32.c' + 'vss-win32.c', + 'commands-windows-ssh.c' )) else qga_ss.add(files( @@ -94,7 +95,7 @@ gen_tlb = [] qga_libs = [] if host_os == 'windows' qga_libs += ['-lws2_32', '-lwinmm', '-lpowrprof', '-lwtsapi32', '-lwininet', '-liphlpapi', '-lnetapi32', - '-lsetupapi', '-lcfgmgr32'] + '-lsetupapi', '-lcfgmgr32', '-luserenv'] if have_qga_vss qga_libs += ['-lole32', '-loleaut32', '-lshlwapi', '-lstdc++', '-Wl,--enable-stdcall-fixup'] subdir('vss-win32') diff --git a/qga/qapi-schema.json b/qga/qapi-schema.json index 1b9039e4f5..b3de1fb6b3 100644 --- a/qga/qapi-schema.json +++ b/qga/qapi-schema.json @@ -1570,9 +1570,8 @@ { 'struct': 'GuestAuthorizedKeys', 'data': { 'keys': ['str'] - }, - 'if': 'CONFIG_POSIX' } - + } +} ## # @guest-ssh-get-authorized-keys: @@ -1588,8 +1587,8 @@ ## { 'command': 'guest-ssh-get-authorized-keys', 'data': { 'username': 'str' }, - 'returns': 'GuestAuthorizedKeys', - 'if': 'CONFIG_POSIX' } + 'returns': 'GuestAuthorizedKeys' +} ## # @guest-ssh-add-authorized-keys: @@ -1607,8 +1606,8 @@ # Since: 5.2 ## { 'command': 'guest-ssh-add-authorized-keys', - 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' }, - 'if': 'CONFIG_POSIX' } + 'data': { 'username': 'str', 'keys': ['str'], '*reset': 'bool' } +} ## # @guest-ssh-remove-authorized-keys: @@ -1625,8 +1624,8 @@ # Since: 5.2 ## { 'command': 'guest-ssh-remove-authorized-keys', - 'data': { 'username': 'str', 'keys': ['str'] }, - 'if': 'CONFIG_POSIX' } + 'data': { 'username': 'str', 'keys': ['str'] } +} ## # @GuestDiskStats: