Search code examples
node.jsunixubuntuforeversetuid

Executing Jobs as other user


Usually executing Jobs as other user we can do sudo -u <user> <cmd>. However in case of forever process we have to do this way:

exec su -s /bin/sh -c 'exec "$0" "$@"' username -- /path/to/command [parameters...]

Ref: https://superuser.com/questions/213416/running-upstart-jobs-as-unprivileged-users

How is this different? What does this mean? Furthermore:

su -s /bin/bash -c bash username -- /path/to/command [parameters...]

does not seem to work!


Solution

  • The first one:

    exec su -s /bin/sh -c 'exec "$0" "$@"' username -- /path/to/command [parameters]
    

    for starting upstart jobs as a user other than root is intended to allow changing the userid without leaving intermediate processes in the way; so it runs in the following way:

    • exec su -s sh
      replaces the process that invokes this command with su, running the shell sh
    • -c 'exec "$0" "$@"'
      from the invocation of the shell sh exec the command and parameters being passed after the -- (the $0 is the first parameter on the command line, after the --, the $@ is everything after that)

    The end product of this is running /path/to/command as if it was invoked directly from the command as the username specified; leaving a process tree looking like:

    su [as root] -> /path/to/command [as username]
    

    If you didn't invoke it using exec, then you would end up with a process tree looking like:

    upstart_launcher [as root] -> su [as root] -> sh [as username] -> /path/to/command [as username]
    

    (I don't know what the upstart_launcher process will look like at this point; I don't have a system with upstart on it to check this; but there will be a process left over)

    Now, an important element of this is that it just calls exec /path/to/command [arguments…], as if it was typed like that from the command line.

    When we compare this with the second command line; most of what's happening is similar, but not quite the same:

    su -s /bin/bash -c bash username -- /path/to/command [parameters...]
    

    Why doesn't it work? Well you've asked it to do something different; in this case; you're asking it to run the command bash from the shell bash.

    Because you didn't pass in the $0 or the $@, everything after the -- is ignored, because it's not passed into the -c for the shell being invoked.

    Examples of with and without exec

    This collapses the process tree, removing the intermediate sh - it's a housekeeping mechanism to prevent deep process trees.

    skipping all the execs (su -s /bin/sh -c '"$0" "$@"' proxy -- pstree -aApl):

    bash,1
      `-bash,1014
          `-su,1017 -s /bin/sh -c "$0" "$@" proxy -- pstree -aApl
              `-sh,1018 -c "$0" "$@" pstree -aApl
                  `-pstree,1019 -aApl
    

    Adding the internal exec (su -s /bin/sh -c 'exec "$0" "$@"' proxy -- pstree -aApl) - note the missing second level sh:

    bash,1
      `-bash,1014
          `-su,1020 -s /bin/sh -c exec "$0" "$@" proxy -- pstree -aApl
              `-pstree,1021 -aApl
    

    Adding the internal and external exec (exec su -s /bin/sh -c 'exec "$0" "$@"' proxy -- pstree -aApl) - note the missing outer level bash, and the fact that the 1014 pid is the same as the pid that had been present in the previous bash invocation:

    bash,1
      `-su,1014 -s /bin/sh -c exec "$0" "$@" proxy -- pstree -aApl
          `-pstree,1022 -aApl