Score:0

In a bash script, how do I change from root user to another user using su and then exit?

pw flag

I've looked at How do I run a 'sudo' command inside a script? but this seems to be a different issue.

I want to run a script to change from root to the mastodon user using su, run Rails commands, and then exit back to the root account and restart mastodon.

Manually, I can log into root via ssh root@123.456.78.90 which gives me the root@Mastodon:~# shell. Then I use sudo su - mastodon to change to the mastodon account (I am not prompted for a password) and then cd live. Then I can run the Rails commands, etc., and they work, and then I can exit and run systemctl restart mastodon-*.

But my shell script to do the same thing doesn't work. The restartall script is

#!/bin/bash

sudo su - mastodon

cd live

RAILS_ENV=production bundle exec rake tmp:cache:clear

RAILS_ENV=production bundle exec rails assets:generate_static_pages

RAILS_ENV=production bundle exec rails assets:precompile

exit

systemctl restart mastodon-*

and I run it this way root@Mastodon:~# ./restartall

The terminal user and path change to mastodon@MyMastodon, but that's all; the script fails wth: ./restartall: line 5: cd: live: No such file or directory

I also tried root@Mastodon:~# sudo ./restartall

What am I doing wrong in using su to change to the mastodon user?

Will simply using exit correctly take the script back to root@Mastodon before systemctl restart mastodon-* ?

hr flag
Related: [after su, the script stops working](https://askubuntu.com/questions/1065842/after-su-the-script-stops-working)
BlueDogRanch avatar
pw flag
Thanks, that's interesting. I tried `sudo su -c 'cd live' mastodon` and got "permission denied."
hr flag
Hmm ... well `su` and `su -` are not quite the same thing; you'd need `sudo su -c 'cd live' - mastodon` or (perhaps clearer) `sudo su -l -c 'cd live' mastodon`
hr flag
Another option is to use a here-document to read commands into the su shell via standard input - but that will have issues if any of the commands read stdin themselves. See for example [how to write a script that will run commands after su, without using -c](https://unix.stackexchange.com/questions/155326/how-to-write-a-script-that-will-run-commands-after-su-without-using-c)
BlueDogRanch avatar
pw flag
Ok, `sudo su - mastodon -c "cd live (bundle commands here) exit"` works and it exits correctly and runs `systemctl restart mastodon-*`, but throws "bundle: command not found" errors.
BlueDogRanch avatar
pw flag
Same "bundle: command not found" with `sudo su -l -c 'cd live RAILS_ENV=production bundle exec rake tmp:cache:clear RAILS_ENV=production bundle exec rails assets:generate_static_pages RAILS_ENV=production bundle exec rails assets:precompile exit' mastodon`
Score:2
hr flag

The reason this fails is that sudo su - mastodon starts a new interactive shell as user mastodon. Any commands that follow are not executed until that shell exits.

You could pass the commands to su via its -c option, as described in the previous question after su, the script stops working, so

#!/bin/bash

sudo su -l mastodon -c '
  cd live
  RAILS_ENV=production bundle exec rake tmp:cache:clear
  RAILS_ENV=production bundle exec rails assets:generate_static_pages
  RAILS_ENV=production bundle exec rails assets:precompile
'

systemctl restart mastodon-*

(you don't need an explicit exit at the end of the commands passed via -c since the non-interactive su shell will exit naturally when it runs out of commands to execute).

Alternatively, you could use a here-document to pass the commands to the su shell via standard input, as described in how to write a script that will run commands after su, without using -c but please note the warning about commands that themselves read from stdin.

However you could consider avoiding su altogether, and writing your script so that it checks who is running it, and re-executes itself as the target user if required, like

#!/bin/bash

rails_user=mastodon

if [[ $EUID -ne $(id -u $rails_user) ]]; then
  echo "Switching to user '$rails_user' to perform tasks"
  exec sudo -iu "$rails_user" "$(realpath $0)"
fi

cd live
# etc.
BlueDogRanch avatar
pw flag
Thanks! The first example works. Though I'm still getting "bundle: command not found" errors even though the script completes, exits and runs `systemctl restart mastodon-*`; that must be a different issue?
hr flag
@BlueDogRanch that sounds like an issue with how `PATH` is set for user `mastodon`'s login shell - remember non-interactive bash shells don't process ~/.bashrc so you should be using ~/.bash_login or ~/.profile for things like that
BlueDogRanch avatar
pw flag
Ok, I'm still learning shells. There is a .profile file, and with a file mod date in 2019, it obviously wasn't modified by the mastodon install.
mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.