I have two files:
wget.php
<?php
include 'theme.php';
/*ceklogin();*/
css();
if($_POST['wget-send'])
{
$dir=$_POST['dir'];
$link=$_POST['link'];
exec('touch /tmp/wget-download-link.txt',$out);
exec('echo "'.$link.'" >> /tmp/wget-download-link.txt',$out);
exec('/www/wget_download.sh,$out);
echo $out[2];
exit();
}
echo "<form action=\"".$PHP_SELF."\" method=\"post\">";
echo "Download directory:<br><input type=\"text\" name=\"dir\" size=\"15\" value=\"/mnt/usb/\"/><br>";
echo '<br>Download link (one URL per line):<br>';
echo ("<textarea name=\"link\" rows=\"13\" cols=\"60\"></textarea><br><br>");
echo '<input type="submit" name="wget-send" value="Send" />';
echo "</form></div>";
foot();
echo '
</div>
</body>
</div>
</html>';
?>
wget_download.sh
while [ true ] ; do
urlfile=$( ls /tmp/wget-download-link.txt | head -n 1 )
if [ "$urlfile" = "" ] ; then
sleep 30
continue
fi
url=$( head -n 1 $urlfile )
if [ "$url" = "" ] ; then
mv $urlfile $urlfile.invalid
continue
fi
mv $urlfile $urlfile.busy
wget $url -o /tmp/wget.log -P $dir
mv $urlfile.busy $urlfile.done
done
How do I pass the variable from $dir in PHP to $dir in shell? so for example the $dir in my PHP is:
/mnt/usb
I want /mnt/usb to be executed in the wget_download.sh so it's gonna be like this:
wget $url -o /tmp/wget.log -P /mnt/usb
How do I do that?
First off, this is very very dangerous. Make sure you fully trust anyone able to access your site at all with running arbitrary commands on your server. I'd suggest ensuring SSL and proper password authentication.
With that caveat out of the way, you can us putenv("DIR=".$dir); See "http://php.net/manual/en/function.putenv.php" for a list of restrictions and configurations that might be required. You may need to prefix the variable names with "PHP_" in both php and in the shell script.
I second Daniels warning. That said I would just pass it as an argument:
dir="$1"
while [ true ] ; do
urlfile=$( ls /tmp/wget-download-link.txt | head -n 1 )
if [ "$urlfile" = "" ] ; then
sleep 30
continue
fi
url=$( head -n 1 $urlfile )
if [ "$url" = "" ] ; then
mv $urlfile $urlfile.invalid
continue
fi
mv $urlfile $urlfile.busy
wget $url -o /tmp/wget.log -P $dir
mv $urlfile.busy $urlfile.done
done
Which would change you exec call:
// the command would actually look like:
// /www/wget_download.sh /path/to/dir
exec('/www/wget_download.sh ' . escapeshellarg($dir),$out);
Related
I create and run a shell script from a php script. The script has an if. Commands on then don't run when they should, but instead commands in else run when they shouldn't. When I run the same script from the command line, it works as expected.
php file:
<?php
.....
$msg = "#! /bin/sh\n\n";
file_put_contents($scriptfile, $msg, LOCK_EX);
$cmd = "/usr/bin/pdftohtml -noframes -q -p -s -i $renfilenc\n";
file_put_contents($scriptfile, $cmd, FILE_APPEND | LOCK_EX);
$noExtfile = preg_replace('"\.(pdf|PDF)$"', '', $renfilenc);
$cmd = "if [[ \$(grep -c -o \"</p>\" $noExtfile.html) -le 5 ]];
then
/usr/bin/gs -dNOPAUSE -q -r500 -sDEVICE=tiffg4 -dBATCH -sOutputFile=$noExtfile.tiff $renfilenc
/usr/bin/tesseract $noExtfile.tiff $noExtfile -c tessedit_create_hocr=1 -c hocr_font_info=1
else
/usr/bin/unoconv -f docx $noExtfile.html
/usr/bin/curl -i -F \"Profile=jsi\" -F \"Output=url\" -F \"Language=en\" -F \"infile=#$noExtfile.docx\" -F \"submit=Submit\" http://nl.ijs.si/tei/cgi/convert.pl > TEIconvert.log
grep -E -o \"http://.*\" TEIconvert.log > wgetline.log
sed -r \"s/^(.*).$/wget \\1\/tei.xml -O $noExtfile.xml/g\" wgetline.log > wgetTEIfile.sh
/bin/sh wgetTEIfile.sh
fi";
file_put_contents($scriptfile, $cmd, FILE_APPEND | LOCK_EX);
$cmd = "/bin/sh $scriptfile > /dev/null 2>&1 &";
shell_exec($cmd);
.....
?>
created script file:
#! /bin/sh
/usr/bin/pdftohtml -noframes -q -p -s -i USNews_Bostonian_image.pdf
if [[ $(grep -c -o "</p>" USNews_Bostonian_image.html) -le 5 ]];
then
/usr/bin/gs -dNOPAUSE -q -r500 -sDEVICE=tiffg4 -dBATCH -sOutputFile=USNews_Bostonian_image.tiff USNews_Bostonian_image.pdf
/usr/bin/tesseract USNews_Bostonian_image.tiff USNews_Bostonian_image -c tessedit_create_hocr=1 -c hocr_font_info=1
else
/usr/bin/unoconv -f docx USNews_Bostonian_image.html
/usr/bin/curl -i -F "Profile=jsi" -F "Output=url" -F "Language=en" -F "infile=#USNews_Bostonian_image.docx" -F "submit=Submit" http://nl.ijs.si/tei/cgi/convert.pl > TEIconvert.log
grep -E -o "http://.*" TEIconvert.log > wgetline.log
sed -r "s/^(.*).$/wget \1\/tei.xml -O USNews_Bostonian_image.xml/g" wgetline.log > wgetTEIfile.sh
/bin/sh wgetTEIfile.sh
fi
In this case the commands on then should run, not the ones on else.
Any ideas if something is wrong or missing?
I have found the answer here: https://superuser.com/questions/374406/why-do-i-get-not-found-when-running-a-script
So in my php I changed:
$msg = "#! /bin/sh\n\n";
and
$cmd = "/bin/sh $scriptfile > /dev/null 2>&1 &";
to
$msg = "#!/bin/bash \n\n";
and
$cmd = "/bin/bash $scriptfile > /dev/null 2>&1 &";
respectively
I am trying to start a php script as a daemon in Debian. I also would like it to start on boot as well.
I have been starting using /path/to/php /path/to/script/Insert.php & without issue, and can shell_exec("nohup /path/to/php /path/to/script/Insert.php >/dev/null &") as well. I have tried using the below script, but it does not take the script into an operational state.
Am copying the file to /etc/init.d/ and using update-rc.d without issues. I can use service congen-insert startto 'start' the script, but it doesn't seem to actually run, and it doesn't start doing any work.
What am I missing, or where have I gone wrong with the scripts?
I know there are several ways to work around this, but I am really just trying to understand what I am doing incorrectly or why what I am doing is not working.
Any help or suggestions is extremely appreciated! If there is anything else you need or anything I have missed in my description, please let me know so I can correct it.
Thanks in advance.
Service script
#! /bin/sh
### BEGIN INIT INFO
# Provides: congen-insert
# Required-Start: $local_fs $network
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: congen-insert
# Description: DB Insert Daemon
### END INIT INFO
NAME="congen-insert"
DESC=" DB Insert Daemon"
PIDFILE="/var/run/${NAME}.pid"
LOGFILE="/var/log/${NAME}.log"
DAEMON="/path/to/php"
DAEMON_OPTS="/path/to/script/Insert.php"
START_OPTS="--start --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} ${DAEMON_OPTS}"
STOP_OPTS="--stop --pidfile ${PIDFILE}"
test -x $DAEMON || exit 0
set -e
case "$1" in
start)
echo -n "Starting ${DESC}: "
start-stop-daemon $START_OPTS >> $LOGFILE
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon $STOP_OPTS
echo "$NAME."
rm -f $PIDFILE
;;
restart|force-reload)
echo -n "Restarting $DESC: "
start-stop-daemon $STOP_OPTS
sleep 1
start-stop-daemon $START_OPTS >> $LOGFILE
echo "$NAME."
;;
status)
echo -n "Sorry, this isn't implemented yet"
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
Script I am trying to run:
const LoaderPath = __DIR__ . DIRECTORY_SEPARATOR . ".." .DIRECTORY_SEPARATOR . "includes.php";
require_once LoaderPath;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Message\AMQPMessage;
use requests\InsertRequest;
$connection = GetRabbitConnection();
$channel = $connection->channel();
$RedisClient = GetRedisClient();
DeclareQueues($connection, $RedisClient);
$MySQLHost = $RedisClient->get(MySQLHostKey);
$MySQLUser = $RedisClient->get(MySQLUserKey);
$MySQLPassword = $RedisClient->get(MySQLPasswordKey);
$MySQLDatabase = $RedisClient->get(MySQLDatabaseKey);
$InsertExchange = $RedisClient->get(Insert.":".Exchange);
$InsertQueue = $RedisClient->get(Insert.":".Queue);
$Prefetch = $RedisClient->get(Insert.":".Prefetch);
$RedisClient->disconnect();
$RedisClient = null;
$mysql= new mysqli($MySQLHost, $MySQLUser, $MySQLPassword, $MySQLDatabase);
$channel->basic_qos(0,$Prefetch,false);
$channel->basic_consume($InsertQueue, $InsertExchange, false, false, false, false, "callback");
echo "Consuming on Exchange $InsertExchange with Queue $InsertQueue\n";
while(true) {
$channel->wait();
}
$channel->close();
function callback(AMQPMessage $message){
global $mysql;
echo "Message received", "\n";
$InsertRequest = new InsertRequest($message->body);
echo "Running Insert Statement\n";
if (!$mysql->query($InsertRequest->SQL)){
echo "Error: ".$mysql->error;
}
/** #type AMQPChannel $channel */
$channel = $message->delivery_info['channel'];
$channel->basic_ack($message->delivery_info['delivery_tag']);
echo "Insert Complete\n";
}
The issue was in the redirection of the output. I also modified the php file with a header for bash so it does not show as multiple php processes in top, but shows the file name instead:
Revised Service Script:
#! /bin/sh
### BEGIN INIT INFO
# Provides: congen-insert
# Required-Start: $local_fs $network
# Required-Stop: $local_fs
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: congen-insert
# Description: ConGen DB Insert Daemon
### END INIT INFO
NAME="congen-insert"
DESC="DB Insert Process for ConGen"
PIDFILE="/var/run/${NAME}.pid"
LOGFILE="/var/log/${NAME}.log"
DAEMON="/var/congen/php/controllers/congen-insert"
DAEMON_OPTS="> /dev/null 2>&1"
START_OPTS="--start --background --make-pidfile --pidfile ${PIDFILE} --exec ${DAEMON} ${DAEMON_OPTS}"
STOP_OPTS="--stop --pidfile ${PIDFILE}"
test -x $DAEMON || exit 0
set -e
case "$1" in
start)
echo -n "Starting ${DESC}: "
start-stop-daemon $START_OPTS >> $LOGFILE
echo "$NAME."
;;
stop)
echo -n "Stopping $DESC: "
start-stop-daemon $STOP_OPTS
echo "$NAME."
rm -f $PIDFILE
;;
restart|force-reload)
echo -n "Restarting $DESC: "
start-stop-daemon $STOP_OPTS
sleep 1
start-stop-daemon $START_OPTS >> $LOGFILE
echo "$NAME."
;;
status)
echo -n "Sorry, this isn't implemented yet"
;;
*)
N=/etc/init.d/$NAME
echo "Usage: $N {start|stop|restart|force-reload}" >&2
exit 1
;;
esac
exit 0
Revised PHP Script to run:
#!/php52/php-5.6.6/bin/php
<?php
const LoaderPath = __DIR__ . DIRECTORY_SEPARATOR . ".." . DIRECTORY_SEPARATOR . "includes.php";
require_once LoaderPath;
use PhpAmqpLib\Channel\AMQPChannel;
use PhpAmqpLib\Message\AMQPMessage;
use requests\InsertRequest;
$connection = GetRabbitConnection();
$channel = $connection->channel();
$RedisClient = GetRedisClient();
DeclareQueues($connection, $RedisClient);
$InsertExchange = $RedisClient->get(Insert.":".Exchange);
$InsertQueue = $RedisClient->get(Insert.":".Queue);
$Prefetch = $RedisClient->get(Insert.":".Prefetch);
$RedisClient->disconnect();
$RedisClient = null;
$mysql= ConnectionBuilder::GetMySQLi();
$channel->basic_qos(0,$Prefetch,false);
$channel->basic_consume($InsertQueue, $InsertExchange, false, false, false, false, "callback");
echo "Consuming on Exchange $InsertExchange with Queue $InsertQueue\n";
while(true) {
$channel->wait();
}
$channel->close();
function callback(AMQPMessage $message){
global $mysql;
echo "Message received", "\n";
$InsertRequest = new InsertRequest($message->body);
echo "Running Insert Statement\n";
if (!$mysql->query($InsertRequest->SQL)){
echo "Error: ".$mysql->error;
}
/** #type AMQPChannel $channel */
$channel = $message->delivery_info['channel'];
$channel->basic_ack($message->delivery_info['delivery_tag']);
echo "Insert Complete\n";
}
After adding the file to /etc/init.d/ and making both the php script and service script executable, I can start the service using service congen-insert start and use the rest of the commands just like any other init.d service.
It should be noted that I am redirecting the console to /dev/null, but you could also redirect to a file by replacing the /dev/null with a writable path.
An explanation of the 2>&1 quoted from another SO post "2 is the stream number for stderr (error messages), 1 is represents [sic] the stdout stream (the standard non-error output stream)." as such I am essentially redirecting stdout to /dev/null and redirecting stderr to stdout
Write a script using interactive shell commands to execute your php script as shown in the php from terminal example. This only works if PHP is compiled to include the --with-readline option
Setup a cron task (the linux task scheduler) to run this script as shown in the cron setup example.
I have inherited a magento site and the cron schedule does not work. It was set in magento with */5**** and the same in cpanel. I installed AOE Scheduler and all the emails that had been stuck in the queue came through but now I just get a message "Last heartbeat is older than XX". If I run the scheduled tasks manually the emails come through but I get a "405 Error - Not Allowed" page and I find all my file permissions have been changed to 666 (the number of the beast). I have been setting my file permissions to d - 755 and f - 644 but depending on which forum you read this could be wrong. Please help, I have been on this for days.
This is my cron.php
<?php
// Change current directory to the directory of current script
chdir(dirname(__FILE__));
require 'app/Mage.php';
if (!Mage::isInstalled()) {
echo "Application is not installed yet, please complete install wizard first.";
exit;
}
// Only for urls
// Don't remove this
$_SERVER['SCRIPT_NAME'] = str_replace(basename(__FILE__), 'index.php', $_SERVER['SCRIPT_NAME']);
$_SERVER['SCRIPT_FILENAME'] = str_replace(basename(__FILE__), 'index.php', $_SERVER['SCRIPT_FILENAME']);
Mage::app('admin')->setUseSessionInUrl(false);
umask(0);
$disabledFuncs = explode(',', ini_get('disable_functions'));
$isShellDisabled = is_array($disabledFuncs) ? in_array('shell_exec', $disabledFuncs) : true;
$isShellDisabled = (stripos(PHP_OS, 'win') === false) ? $isShellDisabled : true;
try {
if (stripos(PHP_OS, 'win') === false) {
$options = getopt('m::');
if (isset($options['m'])) {
if ($options['m'] == 'always') {
$cronMode = 'always';
} elseif ($options['m'] == 'default') {
$cronMode = 'default';
} else {
Mage::throwException('Unrecognized cron mode was defined');
}
} else if (!$isShellDisabled) {
$fileName = basename(__FILE__);
$baseDir = dirname(__FILE__);
shell_exec("/bin/sh $baseDir/cron.sh $fileName -mdefault 1 > /dev/null 2>&1 &");
shell_exec("/bin/sh $baseDir/cron.sh $fileName -malways 1 > /dev/null 2>&1 &");
exit;
}
}
Mage::getConfig()->init()->loadEventObservers('crontab');
Mage::app()->addEventArea('crontab');
if ($isShellDisabled) {
Mage::dispatchEvent('always');
Mage::dispatchEvent('default');
} else {
Mage::dispatchEvent($cronMode);
}
} catch (Exception $e) {
Mage::printException($e);
exit(1);
}
This is my cron.sh
#!/bin/sh
# location of the php binary
if [ ! "$1" = "" ] ; then
CRONSCRIPT=$1
else
CRONSCRIPT=cron.php
fi
MODE=""
if [ ! "$2" = "" ] ; then
MODE=" $2"
fi
PHP_BIN=`which php`
# absolute path to magento installation
INSTALLDIR=`echo $0 | sed 's/cron\.sh//g'`
# prepend the intallation path if not given an absolute path
if [ "$INSTALLDIR" != "" -a "`expr index $CRONSCRIPT /`" != "1" ];then
if ! ps auxwww | grep "$INSTALLDIR$CRONSCRIPT$MODE" | grep -v grep 1>/dev/null 2>/dev/null ; then
$PHP_BIN $INSTALLDIR$CRONSCRIPT$MODE &
fi
else
if ! ps auxwww | grep "$CRONSCRIPT$MODE" | grep -v grep | grep -v cron.sh 1>/dev/null 2>/dev/null ; then
$PHP_BIN $CRONSCRIPT$MODE &
fi
fi
I have an SSH command that I'd like to execute with libssh2 in PHP:
sh -c '
rm -f /tmp/command.log
sleep 3 &
screen -p 0 -X stuff "\
script -a -c \"ls -l\" /tmp/command.log; kill -USR1 $!
"
wait
cat /tmp/command.log
'
I can't seem to escape it properly though, so SSH receives it exactly as above. I need to wrap it in double quotes so I can get PHP variables in there as well (ls -l will become $command).
I have tried:
"sh -c '
rm -f /tmp/command.log
sleep 3 &
screen -p 0 -X stuff \"\
script -a -c \\"ls -l\\" /tmp/command.log; kill -USR1 $!
\"
wait
cat /tmp/command.log
'"
as well as:
"sh -c '
rm -f /tmp/command.log
sleep 3 &
screen -p 0 -X stuff \"\
script -a -c \\\"ls -l\\\" /tmp/command.log; kill -USR1 $!
\"
wait
cat /tmp/command.log
'"
The first of which returns a PHP error and the second of which doesn't run the command.
The whole function (after the edit Morgan Wilde suggested):
function runShellCommand($command, $host, $user, $pass, $port){
if (!function_exists("ssh2_connect")) die("Fail: function ssh2_connect doesn't exist");
if(!($con = ssh2_connect($host, $port))){
return "Unable to establish connection. Is your server offline?";
} else {
if(!ssh2_auth_password($con, $user, $pass)) {
return "Failed to authenticate. Please ensure your server's password matches our records.";
} else {
$run = <<<HEREDOC
sh -c '
rm -f /tmp/command.log
sleep 3 &
screen -p 0 -X stuff "\
script -a -c \"touch /root/test234\" /tmp/command.log; kill -USR1 $!
"
wait
cat /tmp/command.log
'
HEREDOC;
if (!($stream = ssh2_exec($con, $run ))) {
return "Could not run command.";
} else {
stream_set_blocking($stream, true);
$data = "";
while ($buf = fread($stream,4096)) {
$data .= $buf;
}
fclose($stream);
if(empty($data)){
return "sh-4.1# $command\n\n";
} else {
return "sh-4.1# $command\n$data\n";
}
}
}
}
}
How about using the HEREDOC string quoting? I haven't tried it, but it works for other use cases.
$command = <<<HEREDOC
sh -c '
rm -f /tmp/command.log
sleep 3 &
screen -p 0 -X stuff "\
script -a -c \"ls -l\" /tmp/command.log; kill -USR1 $!
"
wait
cat /tmp/command.log
'
HEREDOC;
More on that here - http://php.net/manual/en/language.types.string.php
Try phpseclib, a pure PHP SSH implementation. eg.
<?php
include('Net/SSH2.php');
$ssh = new Net_SSH2('www.domain.tld');
if (!$ssh->login('username', 'password')) {
exit('Login Failed');
}
echo $ssh->read('username#username:~$');
$ssh->write("rm -f /tmp/command.log\n");
echo $ssh->read('username#username:~$');
$ssh->write("sleep 3 &\n");
echo $ssh->read('username#username:~$');
$ssh->write("screen -p 0 -X stuff \"\
script -a -c \\\"ls -l\\\" /tmp/command.log; kill -USR1 $!
\"");
...
?>
I need to read the output from ffmpeg in order to even try the solution to my question from yesterday. This is a separate issue from my problem there, so I made a new question.
How the heck do I get the output from an ffmpeg -i command in PHP?
This is what I've been trying:
<?PHP
error_reporting(E_ALL);
$src = "/var/videos/video1.wmv";
$command = "/usr/bin/ffmpeg -i " . $src;
echo "<B>",$command,"</B><br/>";
$command = escapeshellcmd($command);
echo "backtick:<br/><pre>";
`$command`;
echo "</pre><br/>system:<br/><pre>";
echo system($command);
echo "</pre><br/>shell_exec:<br/><pre>";
echo shell_exec($command);
echo "</pre><br/>passthru:<br/><pre>";
passthru($command);
echo "</pre><br/>exec:<br/><pre>";
$output = array();
exec($command,$output,$status);
foreach($output AS $o)
{
echo $o , "<br/>";
}
echo "</pre><br/>popen:<br/><pre>";
$handle = popen($command,'r');
echo fread($handle,1048576);
pclose($handle);
echo "</pre><br/>";
?>
This is my output:
<B>/usr/bin/ffmpeg -i /var/videos/video1.wmv</B><br/>
backtick:<br/>
<pre></pre><br/>
system:<br/>
<pre></pre><br/>
shell_exec:<br/>
<pre></pre><br/>
passthru:<br/>
<pre></pre><br/>
exec:<br/>
<pre></pre><br/>
popen:<br/>
<pre></pre><br/>
I don't get it. safe_mode is off. There's nothing in disable_functions. The directory is owned by www-data (the apache user on my Ubuntu system). I get a valid status back from exec() and system() and running the same command from the command line give me tons of output. I feel like I must be missing something obvious but I have no idea what it is.
The problem is you catch only stdout and not stderr (see Standard Streams).
Change this line:
$command = "/usr/bin/ffmpeg -i " . $src;
into
$command = "/usr/bin/ffmpeg -i " . $src . " 2>&1";
and give it another try :)
Use ffprobe instead, it's much quicker and supports JSON output.
$output = shell_exec('ffprobe -v quiet -print_format json -show_format -show_streams "path/to/yourfile.ext"');
$parsed = json_decode($output, true);
And you have all your video info in a php array! This is much faster than ffmpeg -i for some reason.
To get output status and output:
exec("ffmpeg -i input.avi output.mp4 2>&1", $output, $returnStatus);
print_r($output);
if($returnStatus === 0){
// success
}
else {
//fail
}
You can use exec and print_r the output...
exec("ffmpeg -i input.avi -vcodec h264 -acodec aac -strict -2 output.mp4 2>&1",$output);
echo "<pre>";
print_r($output);
echo "</pre>";