11

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,561
  • 18
  • 86
  • 152
  • 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

8

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

almaceleste
  • 548
  • 2
  • 5
  • 17
  • 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,468
  • 1
  • 11
  • 17
  • 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, …

Gilles 'SO- stop being evil'
  • 807,993
  • 194
  • 1,674
  • 2,175
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

Scrooge McDuck
  • 996
  • 1
  • 9
  • 23