You are not logged in.

#1 2014-05-28 11:28:44

kokoko3k
Member
Registered: 2008-11-14
Posts: 2,390

Start any X application through udev

What i wanted, was to popup a scan application as i connect my scanner, but thing are way more complex than i thought.

The first problem is that udev processes events in sequential order, so every program you run through it will be killed after a timeout; this could be solved using a systemd service that runs the program you want.
The second one is that the gui application needs to know what is the active display and who is the user logged into it.

Here's what i did:

An udev rule starts a systemd service i named "xrunit" as soon as a scanner is connected.
In my particular case, i want to start the app "gbscan", so i start the service: xrunit@gbscan:

# cat /etc/udev/rules.d/90-scanner-gbscan.rules

SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", ENV{libsane_matched}="yes" , ACTION=="add", RUN+="/usr/bin/systemctl start xrunit@gbscan" 

The systemd service is very basic, it runs an application by using Xrun.sh wrapper:

# cat /etc/systemd/system/xrunit@.service

[Unit]
Description=Execute the application %i through Xrun.sh

[Service]
Type=simple
ExecStart=/usr/local/bin/Xrun.sh %i

[Install]
WantedBy=multi-user.target

The trickier part, Xrun.sh script, guesses what is the active X display and the user who is using it, then executes the application ($1) trying to pass the same environment variables of the active user.

# cat /usr/local/bin/Xrun.sh 

#!/bin/bash

#Try to guess the active Xorg display and User
#Then start a program by impersonating it.

function GetActiveDisplay()
{
  #Guess the user logged on the current X server
  ACTIVE_CONSOLE=vt$(fgconsole)
  ACTIVE_XORG=$(ps -eo cmd|grep "bin/X" | grep $ACTIVE_CONSOLE)
  echo $ACTIVE_XORG|grep -o " :[0-9] "|tr -d ' '
}

function GetXuser()
{
  #Guess the user logged on server $1
  w|grep " $1 "|cut -d " " -f 1|head -n 1
}


function GetEnvironmentOf()
{
  #get the environment of the user $1"
  user=$1
  userprocs=`ps -u $user |grep sh|cut -f 1 -d " " |sort -u |grep -e [0..9]`
  tmpfile=/var/tmp/$$_$$_$$.tmp
  for proc in $userprocs ; do
          strings /proc/$proc/environ >> $tmpfile
  done
  cat $tmpfile | sort -u |grep \=
  rm $tmpfile
}

ACTIVE_DISPLAY=$(GetActiveDisplay)
XUSER=$(GetXuser $ACTIVE_DISPLAY)
XUSERENV=$(GetEnvironmentOf $XUSER)

export $XUSERENV &>/dev/null
export DISPLAY=$ACTIVE_DISPLAY
sudo -E -H -u $XUSER $1

All works as expected!

I tried with two concurrent users on different X servers and the application pops up with correct style/theme/fonts, however i know that Xrun.sh script is a bit hacky and may fail.
1-  GetActiveDisplay():
* to identify the active Xorg process, it parses the output of "ps" expecting that the "X" executable is located under a parent directory named "*bin/"
* to identify the active display, it blindely search for a pattern that matches " :[0-9] " (:0, :1 ... :9)
2- GetXuser()
* if multiple users are logged to the same display, it may pick the wrong one.
3- GetEnvironmentOf()
* To fetch the environment variables of the user, it walks through
+</proc/any process containing the word sh>,
put all things togheter by eliminating dupes and finally export them.
i chose the pattern "sh", because every shell i know contains "sh" in his name; this also avoid walking through "su, sudo" and the likes that may compromise the user environment.

That's it; what do you think? Do you have some better way to accomplish this "simple" task?

Thanks!

Last edited by kokoko3k (2014-05-28 11:59:01)


Help me to improve ssh-rdp !
Retroarch User? Try my koko-aio shader !

Offline

#2 2014-06-10 04:40:53

hobarrera
Member
From: The Netherlands
Registered: 2011-04-12
Posts: 355
Website

Re: Start any X application through udev

Interesting. The wiki might be a nice place to share this, so it doesn't get lots in time here. smile

Offline

#3 2014-06-10 05:35:09

WonderWoofy
Member
From: Los Gatos, CA
Registered: 2012-05-19
Posts: 8,414

Re: Start any X application through udev

Instead of using RUN+="/usr/bin/systemctl... " you should use ENV{SYSTEMD_WANTS}+="xrunit@gbscan.service"

Offline

Board footer

Powered by FluxBB