virsh setuserpassword
的基本功能
<domain>
:虚拟机名称或 UUID。<username>
:虚拟机内需要修改密码的用户名。<password>
:新密码(传递时会被加密)。--crypt
(可选):指示密码已使用合适算法加密(如 SHA-256)。
该命令通过 libvirt 与虚拟机的管理工具(如 QEMU 或其他 hypervisor)交互,最终调用虚拟机内的 QEMU Guest Agent 来完成密码修改。
2. 流程概览
- 命令解析与参数处理:
virsh
作为 libvirt 的 CLI 工具,接收并解析命令行参数,调用 libvirt 的virDomainSetUserPassword
API。 - API 调用与 XML-RPC 传递: libvirt 的
virDomainSetUserPassword
API 将请求封装成内部通信协议(如 RPC),发送到 libvirtd 守护进程。 - 守护进程处理: libvirtd 解析请求,匹配虚拟机域信息,找到对应 hypervisor 的驱动程序(如 QEMU)。
- 与虚拟机通信: libvirt 通过 hypervisor 的工具(如 QEMU Guest Agent)与虚拟机内部通信,传递密码修改请求。
- QEMU Guest Agent 执行: QEMU Guest Agent 在虚拟机内执行操作,修改指定用户的密码,并返回结果。
3. 源码解析
以下以 libvirt 源码为例,逐步分析 virsh setuserpassword
的内部实现。
3.1 virsh
的参数解析
源码位置:tools/virsh-domain.c
virsh setuserpassword
命令对应的实现:
解析后,virDomainSetUserPassword
函数被调用,负责将请求传递给 libvirtd。
3.2 libvirt API 的实现
源码位置:src/libvirt-domain.c
virDomainSetUserPassword
的实现:
这一步将请求转交给连接对象(virConnectPtr
)所关联的驱动程序。对于 QEMU hypervisor,最终会调用 QEMU 驱动。
3.3 QEMU 驱动处理
源码位置:src/qemu/qemu_driver.c
QEMU 驱动实现 domainSetUserPassword
:
此处检查 QEMU Guest Agent 是否启用,并调用 qemuAgentSetUserPassword
发送请求。
3.4 与 QEMU Guest Agent 通信
源码位置:src/qemu/qemu_agent.c
qemuAgentSetUserPassword
的实现:
QEMU Guest Agent 使用 JSON 格式命令与虚拟机内的代理进程通信。例如,构建的命令可能如下:
3.5 QEMU Guest Agent 的操作
在虚拟机内,QEMU Guest Agent 接收到上述 JSON 命令后,调用系统工具(如 passwd
或其他用户管理命令)完成密码修改,并返回结果。
virsh setuserpassword
是 libvirt 提供的一个命令,用于在运行的虚拟机内部修改用户的密码。这一命令触发了 libvirt 的多层逻辑,包括 API 调用、主机-虚拟机通信机制、以及对 QEMU Guest Agent 的交互。以下是深入了解其工作流程及源码的详细分析。
1. virsh setuserpassword
的基本功能
命令格式:
bash<button class="flex gap-1 items-center py-1"><svg width="24" height="24" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-sm"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制代码</button>virsh setuserpassword <domain> <username> <password> [--crypt]
<domain>
:虚拟机名称或 UUID。<username>
:虚拟机内需要修改密码的用户名。<password>
:新密码(传递时会被加密)。--crypt
(可选):指示密码已使用合适算法加密(如 SHA-256)。
该命令通过 libvirt 与虚拟机的管理工具(如 QEMU 或其他 hypervisor)交互,最终调用虚拟机内的 QEMU Guest Agent 来完成密码修改。
2. 流程概览
- 命令解析与参数处理:
virsh
作为 libvirt 的 CLI 工具,接收并解析命令行参数,调用 libvirt 的virDomainSetUserPassword
API。 - API 调用与 XML-RPC 传递: libvirt 的
virDomainSetUserPassword
API 将请求封装成内部通信协议(如 RPC),发送到 libvirtd 守护进程。 - 守护进程处理: libvirtd 解析请求,匹配虚拟机域信息,找到对应 hypervisor 的驱动程序(如 QEMU)。
- 与虚拟机通信: libvirt 通过 hypervisor 的工具(如 QEMU Guest Agent)与虚拟机内部通信,传递密码修改请求。
- QEMU Guest Agent 执行: QEMU Guest Agent 在虚拟机内执行操作,修改指定用户的密码,并返回结果。
3. 源码解析
以下以 libvirt 源码为例,逐步分析 virsh setuserpassword
的内部实现。
3.1 virsh
的参数解析
源码位置:tools/virsh-domain.c
virsh setuserpassword
命令对应的实现:
c<button class="flex gap-1 items-center py-1"><svg width="24" height="24" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-sm"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制代码</button>static bool cmdSetUserPassword(vshControl *ctl, const vshCmd *cmd) { const char *domName; const char *user; const char *password; bool crypt = false; domName = vshCommandOptString(cmd, "domain", NULL); user = vshCommandOptString(cmd, "user", NULL); password = vshCommandOptString(cmd, "password", NULL); crypt = vshCommandOptBool(cmd, "crypt"); // 调用 libvirt API if (virDomainSetUserPassword(domName, user, password, crypt, 0) < 0) { vshError(ctl, _("Failed to set user password")); return false; } return true; }
解析后,virDomainSetUserPassword
函数被调用,负责将请求传递给 libvirtd。
3.2 libvirt API 的实现
源码位置:src/libvirt-domain.c
virDomainSetUserPassword
的实现:
c<button class="flex gap-1 items-center py-1"><svg width="24" height="24" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-sm"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制代码</button>int virDomainSetUserPassword(virDomainPtr domain, const char *user, const char *password, unsigned int flags) { virConnectPtr conn; int ret; VIR_DOMAIN_DEBUG(domain, "user=%s, flags=%x", user, flags); conn = domain->conn; ret = conn->driver->domainSetUserPassword(domain, user, password, flags); return ret; }
这一步将请求转交给连接对象(virConnectPtr
)所关联的驱动程序。对于 QEMU hypervisor,最终会调用 QEMU 驱动。
3.3 QEMU 驱动处理
源码位置:src/qemu/qemu_driver.c
QEMU 驱动实现 domainSetUserPassword
:
c<button class="flex gap-1 items-center py-1"><svg width="24" height="24" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-sm"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制代码</button>static int qemuDomainSetUserPassword(virDomainPtr domain, const char *user, const char *password, unsigned int flags) { qemuDomainObjPrivatePtr priv; virQEMUDriverPtr driver; priv = QEMU_DOMAIN_PRIVATE(domain); driver = priv->driver; if (!priv->agent) { virReportError(VIR_ERR_OPERATION_INVALID, "%s", _("QEMU Guest Agent is not available")); return -1; } // 调用 QEMU Guest Agent 发送命令 return qemuAgentSetUserPassword(priv->agent, user, password, flags); }
此处检查 QEMU Guest Agent 是否启用,并调用 qemuAgentSetUserPassword
发送请求。
3.4 与 QEMU Guest Agent 通信
源码位置:src/qemu/qemu_agent.c
qemuAgentSetUserPassword
的实现:
c<button class="flex gap-1 items-center py-1"><svg width="24" height="24" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-sm"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制代码</button>int qemuAgentSetUserPassword(qemuAgentPtr agent, const char *user, const char *password, unsigned int flags) { qemuAgentCommand agentCmd; char *cmd; // 构建 QEMU Guest Agent JSON 命令 cmd = qemuAgentMakeCommand("guest-set-user-password", "s:user", user, "s:password", password, "b:crypted", crypt); // 发送命令到虚拟机内 return qemuAgentCommandRun(agent, cmd, NULL); }
QEMU Guest Agent 使用 JSON 格式命令与虚拟机内的代理进程通信。例如,构建的命令可能如下:
json<button class="flex gap-1 items-center py-1"><svg width="24" height="24" viewbox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class="icon-sm"><path fill-rule="evenodd" clip-rule="evenodd" d="M7 5C7 3.34315 8.34315 2 10 2H19C20.6569 2 22 3.34315 22 5V14C22 15.6569 20.6569 17 19 17H17V19C17 20.6569 15.6569 22 14 22H5C3.34315 22 2 20.6569 2 19V10C2 8.34315 3.34315 7 5 7H7V5ZM9 7H14C15.6569 7 17 8.34315 17 10V15H19C19.5523 15 20 14.5523 20 14V5C20 4.44772 19.5523 4 19 4H10C9.44772 4 9 4.44772 9 5V7ZM5 9C4.44772 9 4 9.44772 4 10V19C4 19.5523 4.44772 20 5 20H14C14.5523 20 15 19.5523 15 19V10C15 9.44772 14.5523 9 14 9H5Z" fill="currentColor"></path></svg>复制代码</button>{ "execute": "guest-set-user-password", "arguments": { "user": "testuser", "password": "newpassword", "crypted": false } }
3.5 QEMU Guest Agent 的操作
在虚拟机内,QEMU Guest Agent 接收到上述 JSON 命令后,调用系统工具(如 passwd
或其他用户管理命令)完成密码修改,并返回结果。
4. 流程总结
- virsh 命令行工具: 解析用户输入,调用 libvirt API。
- libvirt API: 封装请求,交由对应 hypervisor 驱动(如 QEMU)。
- QEMU 驱动: 调用 QEMU Guest Agent,通过 JSON 命令与虚拟机内部通信。
- QEMU Guest Agent: 执行具体的用户管理操作,并返回执行结果。