1

I have a makeself script which I expect to be run as root; It's a desktop installer.

At the end of the script, the software which was recently installed to the filesystem tries to launch in user-space.

This works well using sudo -u $(logname) /path/to/application (or alternately sudo -u $SUDO_USER ... in Ubuntu 16.04) however a critical environmental variable from the user is missing:

GNOME_DESKTOP_SESSION_ID

I need GNOME_DESKTOP_SESSION_ID because the child process belongs to Java and Java uses this environmental variable for detecting the GtkLookAndFeel.

However attempts to use sudo -i have failed.

From some basic tests, the GNOME_DESKTOP_SESSION_ID doesn't appear to be a natural environmental variable when this users logs in. For example, if I CTRL+ALT+F1 to a terminal, env |grep GNOME yields nothing whereas XTerm and gnome-terminal both yield GNOME_DESKTOP_SESSION_ID.

How can I get a hold of this GNOME_DESKTOP_SESSION_ID variable from within the installer script without requiring users to pass something such as the -E parameter to the sudo command?

Note, although GtkLookAndFeel is the primary look and feel for Linux, I prefer not to hard-code the export JAVA_OPTS either, I prefer to continue to fallback onto Oracle's detection techniques for support, longevity and scalability reasons.

Update: In Ubuntu, GNOME_DESKTOP_SESSION_ID lives in /usr/share/upstart/sessions/xsession-init.conf

 initctl set-env --global GNOME_DESKTOP_SESSION_ID=this-is-deprecated

Which leads to using initctl get-env to retrieve it. Unfortunately this does not help within a new sudo shell, nor does any (optimistic) attempt at dbus-launch.

Community
  • 1
  • 1
tresf
  • 7,103
  • 6
  • 40
  • 101

2 Answers2

1

It turns out this is a two-step process...

  1. Read the user's UPSTART_SESSION environmental variables from /proc/$pid/environ
  2. Then export UPSTART_SESSION and call initctl --user get-env GNOME_DESKTOP_SESSION_ID

To make this a bit more scalable to other variables, I've wrapped this into a bash helper function. This function should assist fetching other user-environment variables as well. Word of caution, it won't work if the variable's value has a space in the name.

In the below example, only UPSTART_SESSION and GNOME_DESKTOP_SESSION_ID are required to answer the question.

Once sudo_env is called, the next call to sudo -u ... must be changed to sudo -E -u .... The -E will import the newly exported variables for use by a child process.

# 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" |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 "$i=${!i}"
    done
}
tresf
  • 7,103
  • 6
  • 40
  • 101
  • I'd like to specifically mention @dyorgio's help on this. He provided the `/proc/$pid/environ` logic to me on another help forum which is 50% of the above solution. Thanks! – tresf Feb 09 '18 at 18:58
  • @dyorgio, FYI, `GNOME_DESKTOP_SESSION_ID` seems to no longer be readable on Ubuntu 18.04 by using the `cat "/proc/$pid/environ" |tr '\0' '\n'` trick. – tresf Feb 03 '19 at 07:12
0

You need to create a new file on /etc/sudoers.d with this content:

Defaults env_keep+=GNOME_DESKTOP_SESSION_ID

But, there is a problem, if you already are inside sudo, it will not been read again.

So, the complete solution is use sudo inside your script to create this file and then execute your command in another sudo:

#!/bin/bash
# ignore sudo
if [[ -z $SUDO_USER ]]; then
    #save current dir
    DIR="$(pwd)"
    #generate random string (file name compatible)
    NEW_UUID=$(cat /dev/urandom | tr -dc 'a-zA-Z0-9' | fold -w 32 | head -n 1)
    #create env_keep file
    sudo -i -- <<EOF0
        echo "Defaults env_keep+=GNOME_DESKTOP_SESSION_ID" > /etc/sudoers.d/"$NEW_UUID"_keep_java_laf
    EOF0
    sudo -u YOUR_USER -i -- <<EOF
        #go to original directory
        cd "$DIR"
        #execute your java command
        java YOUR_COMMAND
    EOF
    #clean file
    sudo rm -f /etc/sudoers.d/"$NEW_UUID"_keep_java_laf
else
    echo "sudo not allowed!";exit 1;
fi
Dyorgio
  • 1,114
  • 13
  • 23
  • Thanks! How would this work in combination with `sudo -u foo`? – tresf Sep 20 '17 at 19:23
  • The logic helps but it doesn't fix the problem. The proposed fix doesn't seem to help unless I comment out the `rm -f` line, hard code the file name and run the installer a second time. This is an installer that already has root permissions and itself is already inside a `sudo` call. On a side note... according to the sudoers `README`, you should use `umask 0337` before calling echo, but it seems to be getting set too late. The installer is already launched with `sudo` and the environmental variable is lost (I assume). – tresf Sep 25 '17 at 20:47