推荐使用 reboot() 系统调用或 d-bus 接口替代 system():reboot() 需 root 权限或 cap_sys_boot capability 并严格传参;d-bus 方式通过 logind 统一授权,适配 systemd 环境与 polkit 策略。

system() 调用 shutdown 命令最简单但权限受限
直接用 system("shutdown -h now") 或 system("reboot") 是最容易想到的方式,但它依赖 shell 环境和用户权限。普通用户执行会失败,报错类似 Failed to set wall message, ignoring: Permission denied 或直接无响应。
关键点在于:shutdown 和 reboot 默认需要 root 权限,且 systemd 会检查调用者是否在 sudoers 中被显式授权。即使加了 sudo,system() 启动的子进程无法继承终端的 sudo 认证上下文(比如没 TTY),大概率卡住或拒绝执行。
- 不推荐在生产环境用
system()触发关机,尤其服务后台运行时几乎必然失败 - 仅适用于调试场景:程序以 root 运行,且确认
/sbin/shutdown在 PATH 中(注意某些发行版如 Alpine 默认不含shutdown) - 替代命令路径要写全,例如
system("/sbin/shutdown -h now"),避免 PATH 查找失败
用 reboot() 系统调用绕过 shell 更可靠
reboot() 是 Linux 提供的底层系统调用,不经过 shell,权限检查更直接——只要进程有 CAP_SYS_BOOT capability 或以 root 运行即可生效。比 system() 少一层依赖,也更难被 SELinux 或 systemd 的 ExecStartPre 拦截。
注意参数必须严格匹配,常见错误是传错 magic number 导致调用静默失败(返回 -1 且 errno 为 EINVAL):
#include <unistd.h> #include <sys> #include <linux> // 必须先取消锁定(部分内核版本要求) sync(); reboot(LINUX_REBOOT_CMD_RESTART); // 重启 // reboot(LINUX_REBOOT_CMD_POWER_OFF); // 关机(等效于 halt) </linux></sys></unistd.h>
- 头文件顺序不能乱:
<sys></sys>必须在<linux></linux>之前,否则宏定义冲突 - 调用前务必
sync(),否则未刷盘数据可能丢失 -
LINUX_REBOOT_CMD_HALT会停机但不断电,LINUX_REBOOT_CMD_POWER_OFF才真正断电;两者行为因硬件和固件而异
systemd 环境下优先走 logind D-Bus 接口
现代 Linux(systemd ≥ 220)默认禁用传统 reboot 权限,转而通过 logind 统一管理电源操作。直接调用 reboot() 可能被 PolicyKit 拦截,返回 EPERM。此时应走 D-Bus 会话总线或系统总线接口,由 logind 做权限仲裁。
示例用 dbus-send 命令行验证是否可行(可作为 C++ 调用 libdbus 的参考):
dbus-send --system --dest=org.freedesktop.login1 \ /org/freedesktop/login1 \ org.freedesktop.login1.Manager.Reboot \ boolean:true
- 普通用户只要在
power或shutdownpolkit 规则中被允许(如org.freedesktop.login1.power-off),就能成功调用 - C++ 实现需链接
-ldbus-1,构造消息时目标接口为org.freedesktop.login1.Manager,方法名区分Reboot、PowerOff、LockSessions - 若程序无 D-Bus 上下文(如 daemon 无 session bus),必须用 system bus 并确保 service 文件声明了
BusName=org.freedesktop.login1
权限配置和实际部署注意事项
无论选哪种方式,最终都绕不开权限落地。单纯代码写对没用,系统策略才是瓶颈。
- 用
reboot()时,给二进制文件加 capability:sudo setcap cap_sys_boot+ep ./myapp,比加 sudoers 更细粒度 - 用 D-Bus 时,检查
loginctl show-session输出中的Type=wayland或Type=tty,session type 影响 polkit 判定逻辑 - systemd service 中禁止设置
RestrictSUIDSGID=yes或NoNewPrivileges=yes,否则 cap_sys_boot 会被丢弃 - 容器环境(如 Docker)默认 drop 了
sys_bootcapability,且无 D-Bus 系统总线,基本只能靠宿主机暴露 API 或挂载/run/dbus/system_bus_socket
真正麻烦的从来不是怎么写那几行代码,而是让系统相信你有权关机——capability、polkit 规则、dbus 权限、container runtime 配置,漏掉任何一环都会静默失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











