12

Background

I'm trying to restart some programs (mail-notification and stalonetray) regularly, as they appear to die frequently. I want to set restart them whenever NetworkManager reconnects. Hence, I have them triggered by a script in /etc/NetworkManager/dispatcher.d/.

Scripting

I can create a script as follows.

#!/bin/bash
sudo -u foo_user pkill mail-notificati -x
sudo -u foo_user DISPLAY=:0 mail-notification &

This works fine if I run it directly as a user. However, if I call it from root's script, it fails. I am prompted to enter the passwords for mail-notification; it cannot read Gnome Keyring.

How can I run this program as foo_user in every way?

Sparhawk
  • 19,941
  • What does "in every way" mean? Every program that a user runs can have different environment, so saying that (for example) DISPLAY should be set for it to be "in every way" doesn't make much sense. You'd need to define this question more for it to make sense. – Chris Down Sep 29 '15 at 05:14
  • @ChrisDown I mean I want it to work when running the script as root as it does when running the script as foo_user. I appreciate that DISPLAY isn't necessarily relevant here, but included it as an example of what I was doing. – Sparhawk Sep 29 '15 at 05:16
  • That still doesn't clarify, because "doing the same thing when run as root as when run as a user" doesn't make sense -- an environment is per-process, not per-user. – Chris Down Sep 29 '15 at 05:17
  • @ChrisDown Sorry, I'm afraid I don't understand the distinction in this case. Here, I'm asking to run the mail-notification process as in foo_user's environment. – Sparhawk Sep 29 '15 at 05:18
  • How do you know foo_user is logged in, and on which display? On a single-user system it's perhaps reasonable to assume that it's always :0.0 but it is not reasonable to assume that the user is logged in at all times. Anyway, this makes more sense to run within the X session script of foo_user, which will remove both your original problem and the complications it caused you to want to try to solve. – tripleee Sep 29 '15 at 06:48
  • @tripleee In this particular case, I presume that foo_user is logged in, and the DISPLAY is correct (and it works fine with stalonetray). I suspect I'm missing something else. Could you please provide more information on how to run it within the X session script? That sounds promising. – Sparhawk Sep 29 '15 at 07:08
  • Different distros and different desktop managers have different defaults etc. Traditionally you would add something to the user's $HOME/.xsession script; modern desktop managers typically allow you to click and drool an executable script to be added to the user's startup actions. The script itself could just be a simple loop which wakes up every n seconds and checks if something needs to be relaunched. – tripleee Sep 29 '15 at 08:16
  • Tangentially related, hopefully useful: https://www.debian.org/doc/manuals/debian-reference/ch07.en.html esp. 7.5.3. – tripleee Sep 29 '15 at 08:21
  • The point is that users don't have environments, processes do. Without knowing what process should be mimicked, it's not possible to know what it means to "have the same environment" as another user. – Chris Down Sep 29 '15 at 08:21
  • Maybe Gnome Keyring needs (another) environment variable set -- one that's in the shell startup files that you could get with sudo -i? – Jeff Schaller Sep 29 '15 at 12:28
  • @tripleee Ah, I understand now. Yes, that could be another option. Alternatively I could put it in foo_user's crontab. However, I was more curious in a general solution, which might be triggered by root activities such as NetworkManager hooks, udev, pm-suspend (resume), etc. – Sparhawk Sep 29 '15 at 22:29
  • @ChrisDown But in this case I know the specific process that I'm trying to make work. Perhaps I just need to make my title more specific? – Sparhawk Sep 29 '15 at 22:38
  • @JeffSchaller Thanks, but that didn't work. I tried sudo sudo -i -u foo_user my_script, but it still couldn't access Gnome Keyring – Sparhawk Sep 29 '15 at 22:39
  • sudo strips environment variables by default (env_reset); maybe Gnome Keyring needs one that's getting stripped out? Compare env as foo_user versus sudo -i -u foo_user env. – Jeff Schaller Sep 30 '15 at 17:29

4 Answers4

12

In 2021

In short:

To run a command as another user you can use this commands:

runuser -u user -- command
can be used only by root to run commands as another user.
do not require authentication.
do not create log messages.
has permission limitations and issues.

su - user -c command
can be used by any user.
require authentication as target user.
create message in /var/log/auth.log or /var/log/secure.

sudo -u user command
can be used by the user with root privileges or the user from the sudoers file.
require authentication as current user (you).
create message in /var/log/auth.log or /var/log/secure.

pkexec --user user command
can be used by any user.
require authentication as target user.
create message in /var/log/auth.log or /var/log/secure.
replacement for GUI tools such as gksu or gksudo.

More info:

Linux Run Command As Another User

Run a GUI application as another user:

If you want to run a GUI application as another user, you need first allow to the target user connecting to your display:
xhost +si:localuser:user
then use runuser/su/sudo/pkexec to run the application,
and then use xhost to prevent the subsequent connections:
xhost -si:localuser:user

  • 2
    Excellent answer! I tried to use this to run spotify as a non-privileged system user. Your answers, especially for running a GUI app, worked for me. However, I did neet to pass in my DISPLAY var through sudo using the -E flag. Also, I was ignorant and didn't realize localuser is that literal string. Finally, a gui app may fail to various other issues with expecting a normal user's profile. HTH – labyrinth Mar 03 '22 at 03:13
  • Regarding GUI: Is this safe though? Can user send arbitrary input after having been given access to the display? – php_nub_qq May 23 '23 at 13:51
3

You can always use good old su :

man 1 su

This command opens a sub-shell as the user you want to impersonate. As root you can use it without being prompted for a password.

su foo_user -c whatevercommandyouwant

Works from scripts too.

runlevel0
  • 1,609
  • I'm not sure what -x is (I get su: invalid option -- 'x'), but after removing that, it still fails as per the question. – Sparhawk Oct 02 '15 at 06:03
  • Hi, sorry for the confusion, but my aim is not to provide a one shot solution but to tell you how the command su works. If you did a direct cut and paste you may have got an error. But, that's exactly why I added a link to the su man page as the first thing. Please cut and paste this into the shell and see what it says ;) – runlevel0 Oct 05 '15 at 14:20
  • Sorry, I still don't understand. Are you suggesting that su will provide a different env to sudo that should fix my problem? If so, then that doesn't seem to be the case. – Sparhawk Oct 05 '15 at 21:57
  • Yes. Again: http://man7.org/linux/man-pages/man1/su.1.html

    " su allows to run commands with a substitute user and group ID.

       When called without arguments, su defaults to running an interactive
       shell as root.
    
       For backward compatibility, su defaults to not change the current
       directory and to only set the environment variables HOME and SHELL
       (plus USER and LOGNAME if the target user is not root). "
    
    

    anyway sudo has nothing to do with that, sudo su does. sudo grants permissions to run commands that require specific privileges to your user.

    – runlevel0 Oct 07 '15 at 08:19
1

If you want to interact with a GUI from a process that isn't started from that GUI, you need to set a few environment variables: at least DISPLAY, possibly also XAUTHORITY if it isn't in the default location, and for many modern programs you need to set DBUS_SESSION_BUS_ADDRESS.

But a more reliable approach for your problem would be to not restart those programs from NetworkManager. In addition to the difficulty of successfully launching them, you also need to worry about whether you're logged in at all, and if there might be other users and other displays to consider, and so on. Instead, kill those programs, but don't restart them. In your normal session, instead of starting them directly, start them from a supervisor that restarts them if they die. I think systemd includes this functionality (but I don't know how to use it); or you can use dedicated supervisor programs such as monit, supervise, …

0

Read, copy and install run-as, a Bash and a Python script which wrap up usage of machinectl, xhost and of managing running dbus and setting variables to run a graphical application for you:

run-as <user> <command>

To run a graphical application do

run-as -X <user> <command>

References