13

I want to use an SDL-based program to display graphics on the console, without having to log on from the console, and without running the program as root. For example, I want to be able to run it via ssh. The target OS is raspbian.

Here is a short example in python to illustrate the problem:

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

This works (runs to completion, does not throw exceptions) if I run it from the console, and it works via ssh if I run it as root.

I have checked that my user is in the audio and video groups.

I have used strace to see what is different between running it from the console (which works), running it as root via ssh (also works), and running it as a regular user via ssh (doesn't work).

The first difference was that my user did not have permission to access /dev/tty0. I created a new group (tty0), put my user in that group, and added a udev rule to give that group access to /dev/tty0.

The strace output diverges at this ioctl call - the failure is show here; ioctl returns 0 when the program is run from the console or run from ssh as root:

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

(The addresses also differ, but that isn't important.)

Given that my program works when it runs as root, I think this means I have a permissions problem. How do I give the necessary permissions to my user to be able to run this program without logging on at the console (and without running as root)?

3

My aim was the same as original poster’s one, but with one difference: I needed to run SDL application as a systemd daemon. My Linux machine is Raspberry Pi 3 and operating system is Raspbian Jessie. There is no keyboard or mouse connected to RPi. I connect to it using SSH. My SDL app is actually a Pygame-based app. I set pygame/SDL to use the “fbcon” framebuffer driver via the SDL_VIDEODRIVER environment variable. My systemd --version output is:

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

My pygame package version is: (aptitude show python-pygame):

1.9.2~pre~r3348-2~bpo8+rpi1

My libSDL 1.2 version is: (aptitude show libsdl1.2debian - on your machine package name can be different):

1.2.15-10+rpi1

The recipe

  1. Set up permissions for /dev/tty and /dev/fb0 files as described in UDude’s answer. I discovered that /dev/console permission changes are not necessary in Raspbian Jessie.
  2. Add these lines to the [Service] section of your daemon’s .service file:

    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
    

    In case anyone interested, here is the complete pyscopefb.service file I used:

    [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. Issue these commands in the command prompt (I assume pyscopefb.service file is already placed to the correct location where systemd can find it):

    sudo systemctl daemon-reload
    sudo systemctl start pyscopefb
    

This is working for me. Please note I didn’t test whether pygame application is able to receive keyboard and mouse events or not.

Bonus

I also had to solve another 2 problems which may be of interest as well

  1. There was blinking text cursor at the bottom of the screen with framebuffer graphics. To resolve that, I added to my application the following Python code which runs in my app before Pygame/SDL initialization:

    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. After about 10 minutes screen connected to Raspberry Pi’s HDMI output turned black (but not powered off) and my graphics didn’t display, though Pygame reported no errors. This turned out to be a power saving feature. To disable that, I added the following Python code which also runs before Pygame/SDL initialization:

    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
    
  • This was extremely helpful to me to accomplish starting pygame without having a keyboard connected to my Pi, so thank you! I wanted to mention that I found it simple enough to run pygame on /dev/tty7 and issue an ExecStartPre=/bin/chvt 7 to avoid the cursor thing, and it has the bonus of not colliding with agetty which runs by default on tty1–tty6. – dctucker Dec 13 '18 at 19:23
2

Although you question is slightly ambiguous (what is meant by console), I will attempt to answer for the most common cases: /dev/console, /dev/tty, /dev/fb0 ...adapt this to the devices you need. We assume the username is "myuser."

Look at the permissions of the device (this is 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

Take action

/dev/console

the group is "root" but no group access is allowed. I don't like just adding permissions to the root group, so instead I create a group and chgrp the file and change the permissions

$ 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> 

You can use the usermod command to add your user to all the above groups, too, if that is your need.

0

From my recent experience, besides granting permission to your tty device (as mentioned before) you need to do 2 additional things:

  • granting cap_sys_tty_config capability for the executable. If you are using python program you can do it like setcap cap_sys_tty_config+eip /usr/bin/python3.5 (substitute the path for python with your's). Of course, take into account that you are granting this capability for any python script.
  • running the process in a new virtual terminal, e.g. using openvt: openvt ./your_script.py

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.