CRON shell script only runs correctly on command line

Hi, I'm new to these forums, and I'm hoping that someone can solve this problem...

To make things short:

I have DD-wrt set up on a router.

I'm trying to run a script in CRON that fetches the daily password from my database using SSH.

CRON is set like so(in web interface):

* * * * * root /tmp/root/password-query >/tmp/root/log 2>&1 

(I set it to run every minute so I could constantly test it)

And the script goes something like this:

#!/bin/sh 
export SQL_QUERY="SELECT code FROM wireless WHERE id=1;" 
export REMOTE_COMMAND="mysql -u login --password=xxxxxxxx squid -e '$SQL_QUERY' | tail -c 11" 
export DROPBEAR_PASSWORD="xxxxxxxx" 
export CODE=`/usr/bin/ssh user@xx.xx.xx.xx "$REMOTE_COMMAND"` 
echo "Done" 
echo $CODE 
nvram set wl0_wpa_psk=$CODE 
nvram commit 

And run from the command line it DOES SET THE CODE, because it outputs to the log file:
"Done
xxxxxxxx
"

When cron DOES run, the code in nvram is blank and the log file looks like this:
"Done

"

If I change the backticks to quotes and output $CODE, it's exactly the what I want. I have been spending about 8 hours trying to figure this out. What is happening and how can I fix it?

Reward to whoever can suggest a fix: status as a diety
Thanks so much in advance

What I do know:
> The script is executable
> Cron is running and it does run the script every minute and shown by the log file output

The error message(s) should be found in unix mail for the user owning the cron.

Quick tip.
Try a one-off cron containing only a unix "env" command redirected to a logfile. This will tell you how limited the environment for cron is.

What I expect you to find is that the value of $PATH when running from cron does not include the directories containing "mysql" and "nvram".
Also any specific environment variables which are set in your ".profile" will not be set unless you set them in your script.

Best guess. Absolute pathnames for "mysql" and "nvram" and set any relevant environment variables in your script.

Addendum:

If this is the actual line in a crontab it is invalid. The sixth field should not be "root" (the word should not be there at all). However if your "web interface" (whatever that is) does not use crontab format please disregard this commment.

Well, because of the firmware, though it is unix based, it requires that you specify a user in the cron file.
Check it out here: CRON - DD-WRT Wiki (Darn thing won't let me post real URLs yet)

That been my holy book for this project.

I think you might be right about the PATH thing. (I also have a theory that something with the quotes is messing it up) Whatever it is, I realized ssh doesn't output when run from cron. With that being said:

I actually managed to get some sort of workaround that does just fine.

  • I made a script on the remote machine that does the query and then outputs the code to a file named 'codelog'
  • The script on the router runs, sshes, and executes the script on the remote machine
  • Then, the script runs 'scp' to copy the 'codelog' file from the remote machine to the router
  • Finally, I set the output of 'cat codelog' as my variable.
  • And well, it works!

Thanks so much!

One thing to remember about cron is that it runs with almost no environment, so your script may not know where mysql or even tail reside. It does when you run from the command line as $PATH has been set. You can source a profile explicitly or use absolute paths to your executables to make sure that's not causing your problems.
Hope that helps.

Jerry

I hit same problem and found source of the problem after playing around. It appears that SSH starts remote command asynchronously when is started from cron. Don't ask me reason for that I really don't know :slight_smile: My workaround for this in the script below.

ssh -i "/.ssh/id_rsa" -y root@host.domain.lan "script.sh 2>&1 > /tmp/script.log" 2>&1
# Now we will scp log file from remote host and look for string "Done" in it because ssh executes
# asynchronously when run from cron
rm -f /tmp/ssh.log
timeout=60  # 1 minute time-out
elapsed=0
RC=1

while [ $elapsed -lt $timeout ] && [ $RC -eq 1 ]; do   # We should wait for limited time so that it would not loop forever
   scp -i "/.ssh/id_rsa" root@host.domain.lan:/tmp/script.log /tmp/ssh.log > /dev/null 2>&1

   grep -i "Done" /tmp/ssh.log > /dev/null
   if [ $? -eq 0 ]; then
      RC=0
   else
     sleep 5s
     elapsed=$(($elapsed + 5))
   fi
done
if  [ $elapsed -ge $timeout ]; then
   echo "script failed (time-out after $elapsed seconds)"
fi
Messages=`cat /tmp/ssh.log`
echo "${Messages}"