dorkbox / SystemTray

Cross-platform SystemTray support for Swing/AWT, macOS, GtkStatusIcon, and AppIndicator on Java 8+

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

QUESTION: make lib works when run program as another user.

dyorgio opened this issue · comments

I'm trying to start my program with root to another users on machine.

I already tried "runuser"(-l and -u) and "su username", but no way to make it works.

The best try, for now was:

sudo runuser -l username -c "DISPLAY=:0 XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP program-name"

The program works, without error, but no icon appears :(, somebody have any glue who to make it works?

Almost done with:

sudo su username /bin/sh -c "XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP program-name"

But this warning appears:

18:29:30.061 [main] WARN dorkbox.systemTray.SystemTray - Attempting to load the SystemTray as the 'root/sudo' user. This will likely not work because of dbus restrictions. Using the Swing Tray type instead.

But process is running as my user:
captura de tela 2017-09-17 as 18 30 31

What OS and window manager?

In most cases, because of the way dbus works, the application running must also be the same user that the desktop is running under. There are ways around it, but involve managing the dbus directly, which is far to much effort for me.

See if you can get it to work without using sudo at all, since the check for running as root is via System.getenv("SUDO_USER").

Since it's super easy to add a "root override", where all root checks are disabled I'll do that real quick. If it works, it works, if not - there's nothing I know of to make it work.

I'm testing in Ubuntu 16.04 with Unity.

Sadly it doesn't work with:

dorkbox.systemTray.SystemTray.AUTO_FIX_INCONSISTENCIES = false;

I tried only with su, but before I need to enter in a bash with sudo (same thing at the end).
:(.

My application needs a "service" that start my program on users sessions if it is not open (crashed or terminated by user), like a auto-restart (keep alive).

Can you give me some advices about manage bus directly?

(this specifically has to do with app-indicators. I just pushed version 3.7, which has SystemTray.ENABLE_ROOT_CHECK

I have no idea how to manage the dbus directly. My research ended when I discovered that it is not easy. However, I've tested against Ubuntu 16.04 + Unity, and the fallback "Swing" tray type worked in a VM.

Yes, I can confirm that swing is working here too :).

Ah, good to know swing works in more than a VM. Can you try 3.7 and tell me if SystemTray.ENABLE_ROOT_CHECK=false works in your situation? All it does is disable the warning + swing fallback (since SystemTray.AUTO_FIX_INCONSISTENCIES does a lot of things, it's best to leave that true)

As far as DBUS stuff goes, If you can get a C example running (I have some test examples in the test directory), I can probably port that to java.

I got it!

Using this command it works (with root warning):

sudo su username /bin/sh -c "DBUS_SESSION_BUS_ADDRESS='unix:abstract=/tmp/dbus-cLtEoBPmgC' XDG_CURRENT_DESKTOP=$XDG_CURRENT_DESKTOP program-name"

I get "unix:abstract=/tmp/dbus-cLtEoBPmgC" from /run/user/{uid}/dbus-session.

What do you think? it is a good start point to include into lib?

EDIT: it works without sudo(root) but my service runs as root like user.

Awesome! Thanks for figuring it out - i'll definitely include that info.

If you want, you can disable the root warning/checks via SystemTray.ENABLE_ROOT_CHECK=false now.

Example to run as user with root:

        // Get dbus-daemon PID
        Process process = Runtime.getRuntime().exec(new String[]{//
            "/bin/sh", "-c", //
            "ps aux | grep \"^" + user + " \" | grep \"dbus-daemon\" | grep \"unix:\" | awk '{print $2}'" //
        });
        BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String pid = reader.readLine();
        reader.close();

        // Read DBUS env values
        Map<String, String> dbusEnv = new HashMap();
        process = Runtime.getRuntime().exec(new String[]{//
            "/bin/sh", "-c", //
            "cat /proc/" + pid + "/environ | tr '\\0' '\\n'" //
        });
        
        reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
        String line;
        int splitIndex;
        while ((line = reader.readLine()) != null) {
            splitIndex = line.indexOf('=');
            if (splitIndex != -1) {
                dbusEnv.put(line.substring(0, splitIndex), line.substring(splitIndex + 1));
            }
        }
        reader.close();

        String xdgDesktop = dbusEnv.get("XDG_CURRENT_DESKTOP");
        if (xdgDesktop == null) {
            xdgDesktop = "Unity"; // Ubuntu Rules!
        }

        String display = dbusEnv.get("DISPLAY");
        if (display == null) {
            display = ":0"; // display default
        }

        String dbusAddress = dbusEnv.get("DBUS_SESSION_BUS_ADDRESS");

        process = Runtime.getRuntime().exec(new String[]{//
            "runuser", //
            user,//
            "-c",//
            "/bin/sh -c \"DISPLAY=" + display + " DBUS_SESSION_BUS_ADDRESS=" + dbusAddress + " XDG_CURRENT_DESKTOP=" + xdgDesktop + " YOUR-COMMAND\""//
        });

Perfect! May I add this as a Utility method under Apache 2.0?

@tresf, if GNOME_DESKTOP_SESSION_ID is present in dbus-daemon env vars yes, you just need to include in your version this:

dbusEnv.get("GNOME_DESKTOP_SESSION_ID");

In my Ubuntu I don't have this, only theses:

XDG_SESSION_TYPE=x11
DESKTOP_SESSION=ubuntu
DBUS_SESSION_BUS_ADDRESS=unix:abstract=/tmp/dbus-cLtEoBPmgC
SESSIONTYPE=gnome-session
XDG_SESSION_ID=c2
XDG_SESSION_PATH=/org/freedesktop/DisplayManager/Session0
XDG_SESSION_DESKTOP=ubuntu
GDMSESSION=ubuntu
DESKTOP_SESSION=ubuntu
XDG_CURRENT_DESKTOP=Unity
XDG_SESSION_DESKTOP=ubuntu

EDIT: @tresf, looking at your linked issue I think that the problem is <3.7 fallback to swing, if you use this solution and version +3.7 it will be solved.

@dyorgio thanks. If you think you can tackle the linked stackoverflow article, we'd be willing to put a bounty on it.

@tresf Done :), please, tell us if it works or not.

@dorkbox thanks to the help of @dyorgio, we've shimmed some logic into another project which can read environment variables for the user which is running the sudo command...

This is a bash script -- and one which makes quite a bit of assumptions -- but it seems to work rather well. Feel free to use any portions of it under whichever license you choose.

#!/bin/bash

# Provide user environmental variables to the sudo environment
function sudo_env() {
    userid="$(logname 2>/dev/null || echo $SUDO_USER)"
    pid=$(ps aux |grep "^$userid" |grep "dbus-daemon" | grep "unix:" |awk '{print $2}')
    # Replace null delimiters with newline for grep
    envt=$(cat "/proc/$pid/environ" 2> /dev/null |tr '\0' '\n')

    # List of environmental variables to use; adjust as needed
    # UPSTART_SESSION must come before GNOME_DESKTOP_SESSION_ID
    exports=( "UPSTART_SESSION" "DISPLAY" "DBUS_SESSION_BUS_ADDRESS" "XDG_CURRENT_DESKTOP" "GNOME_DESKTOP_SESSION_ID" )

    for i in "${exports[@]}"; do
        # Re-set the variable within this session by name
        # Careful, this technique won't yet work with spaces
        if echo "$envt" | grep "^$i=" > /dev/null 2>&1; then
            eval "$(echo "$envt" | grep "^$i=")" > /dev/null 2>&1
	    export $i > /dev/null 2>&1
	elif initctl --user get-env $i > /dev/null 2>&1; then
            eval "$i=$(initctl --user get-env $i)" > /dev/null 2>&1
	    export $i > /dev/null 2>&1
	fi

        echo -e "        $i=${!i}"
    done
}

sudo_env

Hi guys,

In last fedora (30) dbus-daemon was replaced (I guess) by dbus-broker-launch, but I can't make it works with SystemTray Lib, no appindicator is showing and if I pass mouse over tray area, process is killed by SIGTRAP signal....

If I run same application as user, everything works... Anybody tried to run an app that uses this lib with root as another user with success on Fedora 30?