web-dev-qa-db-ja.com

SDLアプリ(rootとして実行されていない)にコンソールを使用させるには

SDLベースのプログラムを使用して、コンソールからログオンする必要がなく、プログラムをrootとして実行することなく、コンソールにグラフィックを表示したいと考えています。たとえば、ssh経由で実行できるようにしたいと考えています。ターゲットOSはraspbianです。

問題を説明するためのpythonの短い例を以下に示します。

import os, pygame
os.environ['SDL_VIDEODRIVER'] = 'fbcon'
pygame.init()
s = pygame.display.set_mode()
print "Success"

これは、コンソールから実行すると機能します(完了するまで実行され、例外はスローされません)。ルートとして実行すると、sshを介して機能します。

ユーザーがオーディオグルー​​プとビデオグループに属していることを確認しました。

私はstraceを使用して、コンソールから実行する(機能する)、sshを介してrootとして実行する(機能する)、およびsshを介して通常のユーザーとして実行する(機能しない)の違いを確認しました。

最初の違いは、私のユーザーが/ dev/tty0にアクセスする権限を持っていなかったことです。新しいグループ(tty0)を作成し、ユーザーをそのグループに入れ、udevルールを追加して、そのグループに/ dev/tty0へのアクセスを許可しました。

Straceの出力は、このioctl呼び出しで分岐します-失敗はここに表示されます。プログラムがコンソールから実行されるか、sshからrootとして実行されると、ioctlは0を返します。

open("/dev/tty", O_RDWR)                = 4
ioctl(4, VT_GETSTATE, 0xbeaa01f8)       = -1 EINVAL (Invalid argument)

(アドレスも異なりますが、それは重要ではありません。)

Rootとして実行してもプログラムが機能するので、これはアクセス許可の問題があることを意味すると思います。コンソールにログオンせずに(およびrootとして実行せずに)このプログラムを実行できるようにするには、ユーザーに必要なアクセス許可をどのように付与しますか?

14
michael

私の目的は元のポスターと同じでしたが、1つ異なる点があります。それは、systemdデーモンとしてSDLアプリケーションを実行する必要があったことです。私のLinuxマシンはRaspberry Pi 3で、オペレーティングシステムはRaspbian Jessieです。 RPiに接続されているキーボードまたはマウスはありません。 SSHを使用して接続します。私のSDLアプリは実際には Pygame ベースのアプリです。 SDL_VIDEODRIVER環境変数を介して「fbcon」フレームバッファードライバーを使用するようにpygame/SDLを設定しました。ぼくの systemd --version出力は次のとおりです。

systemd 215 + PAM + AUDIT + SELINUX + IMA + SYSVINIT + LIBCRYPTSETUP + GCRYPT + ACL + XZ -SECCOMP -APPARMOR

私のpygameパッケージのバージョンは:(aptitude show python-pygame):

1.9.2〜pre〜r3348-2〜bpo8 + rpi1

私のlibSDL 1.2バージョンは(aptitude show libsdl1.2debian-ご使用のマシンではパッケージ名が異なる場合があります):

1.2.15-10 + rpi1

レシピ

  1. UDudeの回答に記載されているように、/ dev/ttyおよび/ dev/fb0ファイルの権限を設定します。 Raspbian Jessieでは/ dev/console権限の変更は必要ないことを発見しました。
  2. デーモンの.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
    
  3. コマンドプロンプトで次のコマンドを発行します(pyscopefb.serviceファイルは、systemdが検出できる正しい場所に既に配置されていると想定しています)。

    Sudo systemctl daemon-reload
    Sudo systemctl start pyscopefb
    

これは私のために働いています。 pygameアプリケーションがキーボードとマウスのイベントを受信できるかどうかはテストしていません。

ボーナス

また、興味深い2つの問題も解決する必要がありました。

  1. 画面の下部に、フレームバッファグラフィックスのテキストカーソルが点滅していました。これを解決するために、アプリケーションに次の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
    
  2. 約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
    
3
Roman Me

あなたの質問は少しあいまいですが(コンソールの意味)、私は最も一般的なケースである/ 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

行動する

/ dev/console

グループは「ルート」ですが、グループアクセスは許可されません。ルートグループに権限を追加するのは好きではないので、代わりにグループを作成してファイルを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> 

sermodコマンドを使用して、上記のすべてのグループにユーザーを追加することもできます(必要な場合)。

2
UDude

私の最近の経験から、(前述のように)ttyデバイスに許可を与えることに加えて、さらに2つのことを行う必要があります。

  • 実行可能ファイルにcap_sys_tty_config機能を許可します。 pythonプログラムを使用している場合は、setcap cap_sys_tty_config+eip /usr/bin/python3.5(pythonをあなたのパスに置き換えてください。)もちろん、pythonスクリプトにこの機能を付与することを考慮してください。
  • 新しい仮想端末でプロセスを実行します。 openvtを使用:openvt ./your_script.py
0
Eriks Dobelis