Use source with script that contains sudo command

957 Views Asked by At

I have a script that copies files between servers. I am using the lsof command to make sure that the file is not being written to before being moved. The user running the script and the user writing to the file are different, so I need to sudo to the file owner. Here is the relevant line in the sudoers file :

userA ALL=(userB:userB) NOPASSWD: ALL

In the main script (ran as userA), I have tried calling sudo then the subscript containing the lsof command:

sudo su - userB -c 'source ./getOpenFiles.sh'

getOpenFiles.sh has this one line:

#!/bin/bash 
lsofResult=$(/usr/sbin/lsof "${sourcePath}")

I have also tried calling the subscript:

source ./getOpenFiles.sh

Then have the first line of the subscript be the sudo:

#!/bin/bash
sudo su - banjobs
lsofResult=$(/usr/sbin/lsof "${sourcePath}")`.

Neither solution is working.

1

There are 1 best solutions below

4
On

What you actually want is something more like:

lsofResult=$(sudo -u banjobs lsof "${sourcePath}")

Let's go over why the other approaches didn't work one-at-a-time:

  • Running source under sudo su -c

    sudo su - userB -c 'source ./getOpenFiles.sh'
    

    ...uses sudo to run su, which runs sh -c 'source ./getOpenFiles.sh'. This doesn't work for several independent reasons:

    1. sh -c 'source ./getOpenFiles.sh' relies on the source keyword being available in /bin/sh, but this is a bash extension.

    2. Even if your /bin/sh is provided by bash, this still defeats the purpose of using source: By starting a new copy of /bin/sh and sourcing your script into that, you're defining the variable in the new shell, not in the original shell that started sudo su.

  • Running sudo su - banjobs, followed by lsofResult=$(/usr/sbin/lsof "${sourcePath}")

    ...means that lsofResult=$(...) doesn't run until after sudo su - banjobs exits. If sudo su - banjobs has exited, then the current user isn't banjobs any more, so the sudo command has no effect whatsoever on the lsof.


Demonstrating, in detail, how to test this (for folks who don't have a banoff or userB account on their system):

# place relevant contents in sourceme.bash
# switching from lsof to fuser for this test since OS X lsof does not accept a directory
# as an argument.
cat >sourceme.bash <<'EOF'
lsofResult=$(sudo -u root fuser "${sourcePath}" 2>&1)
EOF

# set sourcePath in the outer shell
sourcePath=$PWD
source sourceme.bash
declare -p lsofResult

...yields, on my system, output akin to the following:

declare -- lsofResult="/Users/chaduffy/tmp: 17165c 17686c 17687c 17688c 17689c 17690c"

...showing that the code in question did in fact work as described.