-1

I have a bash script that will log on to remote device and copy a file. It works perfectly from the command line either if logged in as the user (support) or by running\

sudo -u support -i '/opt/IIS/getScreenShot' <parameter>

The support user has keys set up on every device so that the password is not asked on the scp command in the getScreenShot bash script.

I now need for this script to be able to be called/run by Apache. I have a PHP script that executes the following:

$output = shell_exec("sudo -u support -i '$command' $serial");

where $command is /opt/IIS/getScreenShot.

The command will start running (outputs back to the browser). Commands such as if [ ! -d /tmp/$1 ]; or mkdir -p /tmp/$1 do not seem to execute inside of the bash script if it is called through the Apache server and I am not sure why.

I would have thought the sudo command would have run it as the support user just as it did from the command line but it isn't. Ultimately I need for /opt/IIS/getScreenShot to be able to run from the command line and from the Apache call. Any suggestions?

Edit:

sudoers entry:

apache ALL=(ALL) NOPASSWD: ALL

/opt/IIS/getScreenShot will run as echo statements will show in the browser. Just certain commands in the script will not though. getScreenShot is:

set -e
#set -x
#Check runnable
if [ $# -lt 1 ] ; then #Check there is enough command line parameters.
    echo "Usage: $0 [<Serial#> | <Socket#>] "
    echo "      Example:  $0 GESC637005W1 "
    exit 1
fi

_socket=$(/opt/Allure/socketdata "$1" |awk -F '[(/: ]'+ '{print $10}')
#echo "socket $_socket"
if [ $_socket = "400" ];
then
   echo "400 Serial number not found"
   exit 400
fi
echo "Checking for dir /tmp/$1"
if [ ! -d /tmp/$1 ]; then
  echo "mkdir -p /tmp/$1"
  mkdir -p /tmp/$1;
fi
echo "test"
echo "scp -P$_socket support@localhost:/srv/samba/share/clarity-client/client-apps/digital-poster/screens/screenshot.png /tmp/$1/screenshot.png"
scp -P$_socket support@localhost:/srv/samba/share/clarity-client/client-apps/digital-poster/screens/screenshot.png /tmp/$1/screenshot.png
return=$?
if [ $return -ne 0 ];
then
   echo "401 scp1 failed $return"
   exit 401
fi
echo "test2"
scp /tmp/$1/screenshot.png support@172.24.16.37:/opt/digital_media/dm_content/screenshots/$1.png
return=$?
if [ $return -ne 0 ];
then
   echo "402 scp2 failed $return"
fi

A quick update to clarify my actual question. The script will actually run and the parameter gets passed to it correctly. The problem is that certain of the 'commands' in the script will not run correctly and it seems mostly tied to the test/creating of the directory. If the directory does not exist, the 'test' for it does not work. Separately, if I remove the test but issue the mkdir command directly (after making sure the directory is not there - or deleting it if it is), mkdir shows the error : mkdir: cannot create directory '/tmp/GESC637005UH': File exists. I have checked the directory directly and it isn't there. I have run the locate command on the system and can't find it so I am not sure why the mkdir command thinks it is there - and might also explain why the 'test' seems to 'fail' (i.e. it thinks it exists).

  • Which user is Apache running as? – tripleee Dec 20 '17 at 14:27
  • it is running under the user apache. – Kent Scott Dec 21 '17 at 13:10
  • And presumably the `sudoers` file does have a permission for `apache` to run `getScreenShot` as user `support`? Please [edit] your question to show that the obvious basic configuration is correct. – tripleee Dec 21 '17 at 13:13
  • question has been updated. – Kent Scott Jan 04 '18 at 13:50
  • What sort of parameter is it receiving? The quoting errors would break this if you have a parameter which contains more than a single token. Try http://shellcheck.net/ for detailed diagnostics. – tripleee Jan 04 '18 at 13:55
  • The parameter is just a string - it is the hardware serial number or service tag of the device. For testing I have been using one of the devices serial number which is in the format of XXXX111111XX where X is a letter and 1 is a number. – Kent Scott Jan 05 '18 at 15:26
  • ok, i have been trying to remove some complexity just to try and find out what may be happening. I have create test.sh and put it in /var/www/cg-bin. It is very simple: – Kent Scott Jan 05 '18 at 16:48
  • if [ ! -d /tmp/"$1" ]; then echo "Content-type: text/html" echo "" echo "Demo" echo "creating directory /tmp/$1
    " echo "" #echo "mkdir -p /tmp/$1" mkdir -p /tmp/"$1"; fi
    – Kent Scott Jan 05 '18 at 16:48
  • oops, I left out: OUTPUT="$(date)" echo "Content-type: text/html" echo "" echo "Demo" echo "Today is $OUTPUT
    "
    – Kent Scott Jan 05 '18 at 16:51
  • everything works except the if test. Do I have to do something different with that because I cam calling it through apache? – Kent Scott Jan 05 '18 at 16:51
  • I would look at your PHP code in more detail. Can you change it to `shell_exec("sudo -u support -i sh -x '$command' $serial");` just to see that it is receiving the parameter correctly? – tripleee Jan 06 '18 at 09:57
  • ... though if your PHP script isn't able to capture the output, maybe put in `sh -x ... 2>&1 | tee /tmp/support.$$` to get the output in a temporary file you can examine. (Don't forget to remove the temporary files when you are done debugging.) – tripleee Jan 06 '18 at 10:07
  • @triplee - This is the output that I get:+ set -e + '[' 1 -lt 1 ']' ++ /opt/Allure/socketdata GESC742006Q4 ++ awk -F '[(/: ]+' '{print $10}' + _socket=4445 + '[' 4445 = 400 ']' + '[' '!' -d /tmp/GESC742006Q4 ']' + scp -P4445 support@localhost:/srv/samba/share/clarity-client/client-apps/digital-poster/screens/screenshot.png /tmp/GESC742006Q4/screenshot.png + return=0 + '[' 0 -ne 0 ']' + scp /tmp/GESC742006Q4/screenshot.png support@172.24.16.37:/opt/digital_media/dm_content/screenshots/GESC742006Q4.png + return=0 + '[' 0 -ne 0 ']' – Kent Scott Jan 08 '18 at 13:07
  • That looks precisely like it's working with no issues. The argument `GESC742006Q4` was successfully processed right through the end of the script. (The script is clumsy but that's outside the scope of this question.) Could you clarify which part of that transcript exactly is problematic, and maybe [edit] your question with these details? – tripleee Jan 08 '18 at 13:19
  • @tripleee - edited original post about the exact problem. – Kent Scott Jan 08 '18 at 17:35
  • `mkdir -p` quite specifically does not care whether the directory already exists. You can take out the `if ! [ -d "/tmp/$1" ]` completely and just create the directory unconditionally and atomically. – tripleee Jan 08 '18 at 18:00
  • Your problem description reads like a race codiition - could multiple instances of this script have been running at basically the same time when you tested? For production you definitely need to make this script robust for that scenario - maybe look into using `mktemp` to create a unique temporary directory for each run if clients with the same serial number mustn't be able to clobber each others' results. – tripleee Jan 08 '18 at 18:02
  • @tripleee - quick update. It isn't a racing condition as this is not in production yet. I am the only one running it - but point well taken for once I get past this step. As for an update, I have modified my code to try and gleam more information. I added 'ls -lt' and 'pwd' to the script to see what it it would report. PWD is showing /tmp but ls -lt is showing files that I do not see when running ls -lt by hand. I thought maybe I am not on the server I think I am but I checked the IP to make sure. Would apache create some type of 'temp' file system in memory? – Kent Scott Jan 08 '18 at 18:14
  • No, but many files in `/tmp` are very short-lived. `mktemp` shields you nicely from this detail so I would simply suggest you switch to that and forget about this problem. – tripleee Jan 08 '18 at 18:33
  • @tripleee - thanks for you help on this. I think I should close this since it does not seem related to my original issue that I stated. – Kent Scott Jan 08 '18 at 18:52
  • If you like I'll try to find the time to post a refactored and updated version of your script as an answer, with some sort of hand-waving explanation of what I think was wrong. – tripleee Jan 08 '18 at 20:28

1 Answers1

0

Here is an attempt to refactor your script. I have added some comments in-line but the main beef here is using a guaranteed-unique file using mktemp and cleaning it up when we are done with trap. Some other minor remarks:

  • Exit codes are in the range 0-255 so I have subtracted 200 from yours.
  • Diagnostic messages should be sent to standard error.
  • The purpose of if and the other flow control statements of the shell is precisely to run a command and examine its result code. You should very rarely need to explicitly examine $? in a conditional (though including it in a diagnostic message, for example, is of course and obviously useful).

I have removed your comments and added some of my own which are probably not useful to keep in a production script.

#!/bin/sh

set -e

if [ $# -lt 1 ] ; then
    # >&2 sends diagnostics to standard error
    echo "Usage: $0 [<Serial#> | <Socket#>] " >&2
    echo "      Example:  $0 GESC637005W1 " >&2
    exit 1
fi

# Encapsulate error exit in a convenient function
die () {
    rc=$1
    shift
    # include $0 in all diagnostic messages
    echo "$0: $@" >&2
    exit "$rc"
}

_socket="$(/opt/Allure/socketdata "$1" |awk -F '[(/: ]'+ '{print $10}')"
[ $_socket = "400" ] || die 200 "Serial number $1 not found (socket $_socket)"

# Create a temporary file for this script
t=$(mktemp -t screenshot.XXXXXXXXXX) || exit
# Remove the temporary file on regular or error exit or signal
trap 'rm -f $t' EXIT ERROR HUP INT TERM

scp -P$_socket support@localhost:/srv/samba/share/clarity-client/client-apps/digital-poster/screens/screenshot.png "$t" ||
die 201 "401 scp1 failed $?"

scp "$t" support@172.24.16.37:/opt/digital_media/dm_content/screenshots/"$1".png ||
die 202 "402 scp2 failed $?"

The die function is explained in more detail in a separate Stack Overflow question

foo || bar is just a convenient shorthand for

if foo; then
    : nothing here
else
    bar
fi

You could almost equivalently say if ! foo; then bar; fi but that loses the failure exit code if foo doesn't run successfully. (Similarly, foo && bar is equivalent to if foo; then bar; fi)

tripleee
  • 175,061
  • 34
  • 275
  • 318