0

I know I can run this command to spawn a background process and get the PID:

PID=`$SCRIPT > /dev/null 2>&1 & echo $!`

and to run a command under different user:

su - $USER -c "$COMMAND"

I don't want the script to run as root and I can't quite figure out how to combine the two and get the PID of the spawned process.

Thanks!

t0x13
  • 351
  • 2
  • 10
  • 24
  • Why login as _root_ and then `su` as _non-root_ user to run the process? Why login as _root_ in the first-place? – devnull Feb 06 '14 at 05:33
  • this is for init script – t0x13 Feb 06 '14 at 05:37
  • See the following: - http://stackoverflow.com/questions/9119885/how-to-get-the-process-id-of-a-bash-subprocess-on-command-line - http://stackoverflow.com/questions/5163144/what-are-the-special-dollar-sign-shell-variables – kingolego Feb 06 '14 at 06:52
  • @t0x13: Could your problem be related to premature expansion of the `$!` reference, i.e., that it is expanded before the actual command is executed by `su`? If I run the following simple test on CentOS 6.5, I consistently get non-empty results: `PID=$(su $USER -c ': & echo ${!}'); echo $PID` - note how the command is protected by single quotes to prevent premature expansion. Inside a _double_-quoted string, use `\$!`. – mklement0 Feb 08 '14 at 03:16

3 Answers3

1

I think you want the runuser command. General syntax:

runuser -l  userNameHere -c 'command'

I suspect that if you set your $SCRIPT variable to the above (with appropriate changes), your first command will do what you want.

Floris
  • 45,857
  • 6
  • 70
  • 122
  • I get `runuser: command not found` and sorry I still don't understand what you're suggesting? – t0x13 Feb 06 '14 at 05:59
  • just to be clear I can do something like PID=`su - $USER -c "$SCRIPT > /dev/null 2>&1 & echo $!"` and the process will spawn just fine but I can't get the PID (it's empty), which I'd get with "$!" – t0x13 Feb 06 '14 at 06:02
  • Some background: `runuser` apparently only comes with [Red Hat Linux derivatives](https://en.wikipedia.org/wiki/Red_Hat_Enterprise_Linux_derivatives#List_of_Red_Hat_Enterprise_Linux_derivatives), such as RHEL, Fedora, and CentOS (though the OP appears to have a possibly older CentOS version that doesn't have it). It's a simplified `su` that requires no password, but must be run as root; some more info [here](http://comments.gmane.org/gmane.comp.security.selinux/15848). – mklement0 Feb 07 '14 at 23:18
0

To elaborate on: See the following: - stackoverflow.com/questions/9119885/…

See particularly the following quote from Chris Dodd:

Unfortunately there's no easy way to do this prior to bash version 4, when $BASHPID was introduced. One thing you can do is to write a tiny program that prints its parent PID:...

If you have bash 4 and BASHPID, see $$ in a script vs $$ in a subshell

I don't have version 4, so I can't provide an example of it's usage.

Or write a tiny C program which execvs it's arguments and make it setuid to USER. Or even make a setuid shell script (not generally recommended). Hopefully the USER is fixed; if not, get the source for runuser, this is essentially what runuser (not a POSIX command) does.

PID=`su - $USER -c "$SCRIPT > /dev/null 2>&1 & echo $!"`

The problems with the your use of su (above) include:

  • the $! is being executed in the context of the -c sub-shell of su, not the current shell where PID is,
  • you're requesting that your SCRIPT be run as a login shell, so you don't even know if USER's shell supports $!,
  • you have no control over the parent-child process chain that su (and the user's shell) create.

IOW, when you use

PID=`$SCRIPT > /dev/null 2>&1 & echo $!`

there's only one program involved, bash, and two (maybe three?) processes that you pretty much have complete control over. When you throw su into the mix, that changes things much more than is apparent on the surface -- bash and su support similar arguments, right?!? For obvious reasons, su does mucho magic to protect it and its' children's environment from attacks; it doesn't even like being put in the background....

Community
  • 1
  • 1
kingolego
  • 145
  • 9
  • Thank you for your explanation. I will check out the links. – t0x13 Feb 07 '14 at 05:04
  • @kingolego: The immediate problem with your `su` command is that the `$!` is expanded right away by the _original_ shell, due to enclosure in `"`. To truly pass it through to the shell that `su` invokes (as the target user), you'd have to use `\$!` instead (alternatively, use single quotes). You can verify the problem in a simplified scenario without `su` by comparing the following two commands interactively: `bash -c ": & echo ${!}"` vs. `bash -c ": & echo \${!}"` - only the latter will work as intended. – mklement0 Feb 08 '14 at 03:08
0

It's kind of late, but here is a two liner will work, seems to need to be two so that it doesn't wait for the $SCRIPT to complete:

su $USER -c "$SCRIPT 2>&1 & >> $LogOrNull echo $! > /some/writeable/path"
PID="$(cat /some/writeable/path)"

/some/writeable/path will need to be writeable by $USER And the user running these commands will need to have read access

Speedy
  • 326
  • 2
  • 7