我想使用基于 SDL 的程序在控制台上显示图形,而无需从控制台登录,也无需以 root 身份运行该程序。例如,我希望能够通过 ssh 运行它。目标操作系统是raspbian。
这是一个用Python编写的简短例子来说明这个问题:
import os, pygame
os.environ['SDL_VIDEODRIVER'] = 'fbcon'
pygame.init()
s = pygame.display.set_mode()
print "Success"
如果我从控制台运行它,它可以工作(运行到完成,不会抛出异常),如果我以 root 身份运行它,它可以通过 ssh 工作。
我已检查我的用户是否位于音频和视频组中。
我使用 strace 来查看从控制台运行它(有效)、通过 ssh 以 root 身份运行它(也有效)和通过 ssh 作为普通用户运行它(不起作用)之间有什么不同。
第一个区别是我的用户没有访问 /dev/tty0 的权限。我创建了一个新组 (tty0),将我的用户放入该组,并添加了一条 udev 规则以授予该组对 /dev/tty0 的访问权限。
strace 输出在此 ioctl 调用处出现分歧 - 此处显示失败;当程序从控制台运行或以 root 身份从 ssh 运行时,ioctl 返回 0:
open("/dev/tty", O_RDWR) = 4
ioctl(4, VT_GETSTATE, 0xbeaa01f8) = -1 EINVAL (Invalid argument)
(地址也不同,但这并不重要。)
鉴于我的程序在以 root 身份运行时可以正常工作,我认为这意味着我遇到了权限问题。如何向我的用户授予必要的权限,以便能够在不登录控制台(并且不以 root 身份运行)的情况下运行该程序?
答案1
我的目标与原始发布者的目标相同,但有一个区别:我需要将 SDL 应用程序作为 systemd 守护进程运行。我的Linux机器是Raspberry Pi 3,操作系统是Raspbian Jessie。没有键盘或鼠标连接到 RPi。我使用 SSH 连接到它。我的 SDL 应用程序实际上是pygame基于应用程序。我通过 SDL_VIDEODRIVER 环境变量将 pygame/SDL 设置为使用“fbcon”帧缓冲区驱动程序。我的systemd --version
输出是:
systemd 215 +PAM +AUDIT +SELINUX +IMA +SYSVINIT +LIBCRYPTSETUP +GCRYPT +ACL +XZ -SECCOMP -APPARMOR
我的pygame包版本是:( aptitude show python-pygame
):
1.9.2~前~r3348-2~bpo8+rpi1
我的 libSDL 1.2 版本是:(aptitude show libsdl1.2debian
- 在你的机器上包名称可以不同):
1.2.15-10+rpi1
食谱
- 按照 UDude 的回答中所述设置 /dev/tty 和 /dev/fb0 文件的权限。我发现在 Raspbian Jessie 中不需要更改 /dev/console 权限。
将这些行添加到守护程序的 .service 文件的 [Service] 部分:
User=pi #Your limited user name goes here StandardInput=tty StandardOutput=tty TTYPath=/dev/tty2 # I also tried /dev/tty1 and that didn't work for me
如果有人感兴趣,这里是我使用的完整 pyscopefb.service 文件:
[Unit] Description=Pyscopefb test service Wants=network-online.target After=rsyslog.service After=network-online.target [Service] Restart=no ExecStart=/home/pi/Soft/Test/pygame/pyscopefb ExecStop=/bin/kill -INT $MAINPID OOMScoreAdjust=-100 TimeoutStopSec=10s User=pi WorkingDirectory=/home/pi/Soft/Test/pygame StandardInput=tty StandardOutput=tty TTYPath=/dev/tty2 [Install] WantedBy=multi-user.target
在命令提示符中发出这些命令(我假设 pyscopefb.service 文件已放置到 systemd 可以找到它的正确位置):
sudo systemctl daemon-reload sudo systemctl start pyscopefb
这对我有用。请注意,我没有测试 pygame 应用程序是否能够接收键盘和鼠标事件。
奖金
我还必须解决另外两个可能也有趣的问题
屏幕底部有闪烁的文本光标,带有帧缓冲区图形。为了解决这个问题,我在应用程序中添加了以下 Python 代码,该代码在 Pygame/SDL 初始化之前在我的应用程序中运行:
def _disable_text_cursor_blinking(self): command_to_run = ["/usr/bin/sudo", "sh", "-c", "echo 0 > /sys/class/graphics/fbcon/cursor_blink"] try: output = subprocess32.check_output(command_to_run, universal_newlines = True) self._log.info("_disable_text_cursor_blinking succeeded! Output was:\n{output}", output = output) except subprocess32.CalledProcessError: self._log.failure("_disable_text_cursor_blinking failed!") raise
大约 10 分钟后,连接到 Raspberry Pi 的 HDMI 输出的屏幕变黑(但未关闭),并且我的图形没有显示,尽管 Pygame 报告没有错误。事实证明这是一项省电功能。为了禁用它,我添加了以下 Python 代码,该代码也在 Pygame/SDL 初始化之前运行:
def _disable_screen_blanking(self): command_to_run = ["/usr/bin/setterm", "--blank", "0"] try: output = subprocess32.check_output(command_to_run, universal_newlines = True) self._log.info("_disable_screen_blanking succeeded! Output was:\n{output}", output = output) except subprocess32.CalledProcessError: self._log.failure("_disable_screen_blanking failed!") raise
答案2
虽然您的问题有点含糊(控制台是什么意思),但我将尝试回答最常见的情况:/dev/console、/dev/tty、/dev/fb0 ...将此适应您需要的设备。我们假设用户名是“myuser”。
查看设备的权限(这里是ubuntu 15.04)
odroid@mbrxu3:~/projects/sc$ ls -l /dev/console
crw------- 1 root root 5, 1 Oct 23 17:49 /dev/console
odroid@mbrxu3:~/projects/sc$ ls -l /dev/tty
crw-rw-rw- 1 root tty 5, 0 Oct 24 17:50 /dev/tty
odroid@mbrxu3:~/projects/sc$ ls -l /dev/fb0
crw-rw---- 1 root video 29, 0 Jan 1 2000 /dev/fb0
采取行动
/开发/控制台
该组是“root”,但不允许任何组访问。我不喜欢只向根组添加权限,因此我创建一个组并 chgrp 文件并更改权限
$ sudo addgroup --system console
$ sudo chgrp console /dev/console
$ sudo chmod g+rw /dev/console
$ sudo usermod -a -G console <myuser> <==== replace <myuser>
/dev/tty
$ sudo usermod -a -G tty <myuser>
/dev/fb0
$ sudo usermod -a -G video <myuser>
您可以使用用户模式如果您需要的话,也可以使用命令将您的用户添加到上述所有组中。
答案3
根据我最近的经验,除了向您的 tty 设备授予权限(如前所述)之外,您还需要做两件事:
- 为可执行文件授予 cap_sys_tty_config 功能。如果您使用的是 python 程序,您可以这样做
setcap cap_sys_tty_config+eip /usr/bin/python3.5
(用您的路径替换 python 的路径)。当然,请考虑到您正在为任何 python 脚本授予此功能。 - 在新的虚拟终端中运行该进程,例如使用 openvt:
openvt ./your_script.py