Error handling for popen - php

I am using popen in PHP to execute a TCL file .
$cmd='C:/wamp/www/Tcl/bin/tclsh84.exe'; //windows
$ph = popen($cmd,'w')
But if someone restarts the machine or the tclsh84.exe process is killed . How do I know this error condition has occured ?
$ph is not returning 0 in these conditions.
Regards,
Mithun

you see the documentation of the tasklist utility from there. This utility is a kind of task manager in command line (a bit less feature since you can only list process and not affect them). Having that utility accessible on your server, you can use exec function to get list of process and work with that.
a probable code would be something like that:
function isTCLRunning(){
$running = false;
exec('tasklist.exe /fo CSV /fi tclsh84.exe', $output);
return count($output) == 1
}
note: this is completly untested you might want to play a bit with tasklist and make sure it is returning expected output before starting to code your PHP function.

Related

Open Linux terminal command in PHP

I have a server running on Linux that execute commands to 12 nodes (12 computers with Linux running in them). I recently downloaded PHP on the server to create web pages that can execute commands by opening a specific PHP file.
I used exec(), passthru(), shell_​exec(), and system(). system() is the only one that returns a part of my code. I would like PHP to act like open termainal command in linux and I cannot figure out how to do it!
Here is an example of what is happening now (Linux directly vs PHP):
When using linux open terminal command directly:
user#wizard:/home/hyperwall/Desktop> /usr/local/bin/chbg -mt
I get an output:
The following settings will be used:
option = mtsu COLOR = IMAGE = imagehereyouknow!
NODES = LOCAL
and additional code to send it to 12 nodes.
Now with PHP:
switch($_REQUEST['do'])
{ case 'test':
echo system('/usr/local/bin/chbg -mt');
break;
}
Output:
The following settings will be used:
option = mtsu COLOR = IMAGE = imagehereyouknow!
NODES = LOCAL
And stops! Anyone has an explanation of what is happening? And how to fix it? Only system displays part of the code the other functions display nothing!
My First thought is it can be something about std and output error. Some softwares dump some informations on std out and some in std error. When you are not redirecting std error to std out, most of the system calls only returns the stdout part. It sounds thats why you see the whole output in terminal and can't in the system calls.
So try with
/usr/local/bin/chbg -mt 2>&1
Edit:
Also for a temporary work through, you can try some other things. For example redirect the output to file next to the script and read its contents after executing the command, This way you can use the exec:
exec("usr/local/bin/chbg -mt 2>&1 > chbg_out");
//Then start reading chbg_out and see is it work
Edit2
Also it does not make sense why others not working for you.
For example this piece of code written in c, dumps a string in stderr and there is other in stdout.
#include <stdio.h>
#include<stdlib.h>
int main()
{
fputs("\nerr\nrro\nrrr\n",stderr);
fputs("\nou\nuu\nuttt\n",stdout);
return 0;
}
and this php script, tries to run that via exec:
<?php
exec("/tmp/ctest",&$result);
foreach ( $result as $v )
{
echo $v;
}
#output ouuuuttt
?>
See it still dumps out the stdout. But it did not receive the stderr.
Now consider this:
<?php
exec("/tmp/ctest 2>&1",&$result);
foreach ( $result as $v )
{
echo $v;
}
//output: errrrorrrouuuuttt
?>
See, this time we got the whole outputs.
This time the system:
<?php
echo system("/tmp/ctest 2>&1");
//output: err rro rrr ou uu uttt uttt
?>
and so on ...
Maybe your chbg -mt writes additional code to stderr instead of stdout? Try to execute your script inside php like this:
/usr/local/bin/chbg -mt 2>&1
The other responses are good for generic advice. But in this specific case, it appears you are trying to change your background on your desktop. This requires many special considerations because of 'user context':
First, your web server is probably running as a different user, and therefore would not have permissions to change your desktop.
Second, the program probably requires some environmental variables from your user context. For example, X programs need a DISPLAY variable, ssh-agent needs SSH_AGENT_PID and SSH_AUTH_SOCK, etc. I don't know much about changing backgrounds, but I'm guessing it involves D-Bus, which probably requires things like DBUS_SESSION_BUS_ADDRESS, KONSOLE_DBUS_SERVICE, KONSOLE_DBUS_SESSION, and KONSOLE_DBUS_WINDOW. There may be many others. Note that some of these vars change every time you log in, so you can't hard-code them on the PHP side.
For testing, it might be simpler to start your own webserver right from your user session. (i.e. Don't use the system one, it has to run as you. You will need to run it on an alternate port, like 8080). The web server you start manually will have all the 'context' it needs. I'll mention websocketd because it just came out and looks neat.
For "production", you may need to run a daemon in your user context all the time, and have the web server talk to that daemon to 'get stuff done' inside your user context.
PHP's system only returns the last line of execution:
Return Value: Returns the last line of the command output on success, and FALSE on failure.
You will most likely want to use either exec or passthru. exec has an optional parameter to put the output into an array. You could implode the output and use that to echo it.
switch($_REQUEST['do'])
{ case 'test':
exec('/usr/local/bin/chbg -mt', $output);
echo implode('\n', $output); // Could use <br /> if HTML output is desired
break;
}
I think that the result of execution, can changes between users.
First, try to run your PHP script directly into your terminal php yourScript.php
If it runs as expected, go to your Apache service and update it to run with your own credentials
You are trying to change the backgrounds for currently logged in users... While they are using the desktop. Like while I'm typing this message. I minimize my browser and 'ooh my desktop background is different'. Hopefully this is for something important like it turns red when the reactor or overheating.
Anyway to my answer:
Instead of trying to remotely connect and run items as the individual users. Setup each user to run a bash script (in their own account, in their own shell) on a repeating timer. Say every 10 minutes. Have it select the SAME file.. from a network location
/somenetworkshare/backgrounds/images/current.png
Then you can update ALL nodes (1 to a million) just by changing the image itself in /somenetworkshare/backgrounds/images/current.png
I wrote something a while ago that does just this -- you can run a command interpreter (/bin/sh), send it commands, read back responses, send more commands, etc. It uses proc_open() to open a child process and talk to it.
It's at http://github.com/andrasq/quicklib, Quick/Proc/Process.php
Using it would look something like (easier if you have a flexible autoloader; I wrote one of those too in Quicklib):
include 'lib/Quick/Proc/Exception.php';
include 'lib/Quick/Proc/Exists.php';
include 'lib/Quick/Proc/Process.php';
$proc = new Quick_Proc_Process("/bin/sh");
$proc->putInput("pwd\n");
$lines = $proc->getOutputLines($nlines = 10, $timeoutSec = 0.2);
echo $lines[0];
$proc->putInput("date\n");
$lines = $proc->getOutputLines(1, 0.2);
echo $lines[0];
Outputs
/home/andras/quicklib
Sat Feb 21 01:50:39 EST 2015
The unit of communication between php and the process is newline terminated lines. All commands must be newline terminated, and all responses are retrieved in units of lines. Don't forget the newlines, they're hard to identify afterward.
I am working on a project that uses Terminal A on machine A to output to Terminal B on Machine B, both using linux for now. I didnt see it mentioned, but perhaps you can use redirection, something like this in your webserver:
switch($_REQUEST['do'])
{ case 'test':
#process ID on the target (12345, 12346 etc)
echo system('/usr/local/bin/chbg -mt > /proc/<processID>/fd/1');
#OR
#device file on the target (pts/0,tty0, etc)
echo system('/usr/local/bin/chbg -mt > /dev/<TTY-TYPE>/<TTYNUM>');
break;
}
Definitely the permissions need to be set correctly for this to work. The command "mesg y" in a terminal may also assist...Hope that helps.

Parallelism in php

I want to optimize part of my code to improve performance.Since my application make use of commandline tool , i think it would certainly improve performance to execute lines of code in parallel rather than executing code sequentially
<?php
$value = exec("command goes here"); //this takes time
/* Some instructions here that don't depend on $value */
/* Some instructions here that don't depend on $value */
$result = $value*2 ; //this is just a dumb example
?>
I want to execute the codes that don't depend on value at the same time as $value so that the whole script execute faster rather that waiting for exec() to complete
For a quick and dirty way of releasing your php thread from a blocking exec thread, you can simply append the command with a "&" or "& disown". In the example below I also redirected all errors and stdout to /dev/null. (I assume a linux system and just used a simple command that might take some amount of time...)
$command = "mv oldFolder/hugeFile.txt newFolder/hugeFile.txt >> /dev/null 2>&1 &";
$value=exec($command);
If you really need the return value from $command just remove the >> /dev/null 2>&1 bit.
Unfortunately PHP always despond you in parallelism, concurrent programming, ....
And I never know that why PHP doesn't support these important things and WHEN PHP WANT TO SUPPORT THESE.
But maybe you want to use Fork in php (if you know the problems AND Troubles in Fork )
http://php.net/manual/en/function.pcntl-fork.php
https://codereview.stackexchange.com/questions/22919/how-to-fork-with-php-4-different-approaches

How to run a linux command to compile a c program, from php script

I am trying to make an online judge for c programming. When user enters the c code and submits it, my form redirects to judge.php which is the action file for the form.
Here is what I have written in judge.php
<?php
$text=$_POST['code'];
//echo $text;
$var_str = var_export($text, true);
file_put_contents('code.c', $text);
$ans=exec('pwd');
$ans= exec('gcc code.c');
echo $ans;
?>
I have captured user input in $text and wrote it to a c file(code.c). Till now, it is fine.
But exec(gcc code.c) is not working and not giving any output. I tried other linux commnads like pwd, date, etc. They are working fine. What may be the reason for this and how to fix it?
It is not a directory issue i tried exec(pwd) and it gave the output as the same directory in which code is present.
I tried to run same code.c file from terminal and it is running fine. So, it is also not a 'permission' problem.
One more thing, how to echo the error message generated if any exec() command is not working properly?
After getting suggestion from the answer below, i tried
$cmd="gcc -std=c99 code.c -g -Wall mysql_config --libs --cflags -o db_obj.o --pedantic";
exec($cmd,$out,$status);
But it is also not working. The status returned is 1
Most probably it is permission issue. "whoami" says nobody. Please tell how to change the owner from nobody to root or how to assign the permission to execute gcc from nobody
Three main aspects to my answer
improper use of the exec function.
Look at the man pages. First, the exec function's signature is:
string exec ( string $command [, array &$output [, int &$return_var ]] )
So exec can take up to 3 arguments. It returns the last line of the command's output, like the docs state quite clearly:
The last line from the result of the command. If you need to execute a command and have all the data from the command passed directly back without any interference, use the passthru() function.
To get the output of the executed command, be sure to set and use the output parameter.
So in your case:
$lastLine = exec($command, $fullOutput, $status);
Is what you're looking for. If $status is anything else than 0, your command was unsuccessful. That's what you should check to react accordingly.
The full output of any command can be found in $fullOutput as a line-per-line array.
Output like:
all went well
except for this
Will look like this in the $fullOutput array:
array('all went well', 'except for this');
permissions can be an issue, still.
You say permissions aren't likely to be the cause of the problem, because you can run gcc from the command-line. All fine and dandy, but what user is running the PHP script on the server?
In the case of web-servers, that user is often called nobody, apache or something, and that user is very likely not permitted to run gcc. It's PHP that runs a new instance of whatever default shell it has set up (bash, probably), and it's PHP's user that logs in to that shell, and it's that user that is calling gcc...
Know who you are, and what groups you belong to. Try adding this to your script:
echo 'Script is running under user: ', exec('whoami'), '<br>', PHP_EOL;
echo 'member of the following groups: ', exec('groups'), '<br>', PHP_EOL;
And before you ask: yes, those are comma's... no need to concatenate, you can pass multiple variables/values to echo, separated by a comma. It's actually faster (think of it as C++'s std::cout << some_var << another_var;)
general issues + security
This all said and done: compiling C code from a php script isn't as simple as you seem to think it is. Suppose I were to write this:
#include <stdio.h>
#include <time.h>
int main ( void )
{
time_t t = time(NULL);
if (t%2)
{
float val = (float) t/2.0;
//do stuff with float...
}
else
{
unsigned long long val = t/2;
//do stuff with unsigned long long...
}
}
Your gcc test.c command would fail, because you failed to pass the argument -std=c99, for example.
If I wanted a script to compile a given file, I'd also expect that script to allow me to choose which arguments I compiled my code with, too -g, -Wall and, not to mention: cflags and libs (the output of pkg-config or mysql_config --cflags --libs, to name a specific example I recently used).
Basically, your script simply cannot deal with my wanting to compile something with a commind like
gcc -std=c99 code.c -g -Wall `mysql_config --libs --cflags` -o db_obj.o --pedantic
Which still is a simplified version of what many compilation commands look like, especially when debugging code under development. For stable releases, you'd probably drop -g and --pedantic, but you get my point...
Just think of what it means, allowing the user to pass a set of cli arguments, along with the code. They might pass an argument like -DSOME_MACRO or -O0, which means they might also pass -O0 && rm -Rf *. That means you'll have to call escapeshellcmd or escapeshellarg. Both of which will prohibit me from passing a valid argument, being:
`mysql_config --libs --cflags`
Which contains back-ticks, and thus will be escaped.
To be frank, I struggle to see the point of this exercise... and I'm leaving a lot out, still: the dangers of compiling (let alone running) user-provided code on your machine, for example, are not to be overlooked. You can't just compile code, and run it on your server: memory leaks, segfaults... heck, pure evil code is all getting compiled on your server unchecked if this is the code you have. Really, save yourself a lot of tears, and include an iframe that loads codepad or some similar service...
Recap:
always check the man for a function, see if you're getting all information it returns
check the permissions and runtime for the user that is actually executing the commands
Never trust the network, don't blindly assume people will submit valid, harmless code for you to compile.
Don't reinvent the wheel: compilation services exist, just forward those (but ask for permission first)
Try this code to execute c program from PHP file
<?php
// used to compile the c file using exec() in php
exec('gcc helloworld.c -o helloworld', $out, $status);
if (0 === $status) {
var_dump($out);
// used to execute the c file using exec() in php
exec('./helloworld', $out, $status);
if (0 === $status) {
var_dump($out);
} else {
echo "Command failed with status: $status";
}
} else {
echo "Command failed with status: $status";
}
?>

How can I script a 'shutdown -r 1' and return an exit status?

I am writing a program that will at some point call a shell script. I need this shell script (bash, or if necessary PHP 4+ will work) to be called by the program, and return an exit status that I can relay before the 1 minute is reached and the system reboots.
Here's an idea of what I mean, best as I can describe:
Program calls 'reboot' script
Reboot script runs 'shutdown -r 1' and then exits with a status of 0
Program echo's out the exit status
Server reboots
I can get everything to work except the exit status - no matter what I try the program never exits its loop waiting for an exit status, so it never returns anything but the reboot still occurs. This program runs other scripts that return exit statuses, so I need this one to as well to maintain functionality and all that...
Any help is appreciated!
EDIT- The program that calls the reboot script is a PHP script that runs in a loop. When certain events happen, the program runs certain scripts and echos out the exit status. All of them work but this - it never returns an exit status.
Scripts are being called using system($cmd) where $cmd is './scriptname.sh'
Assuming you're opening the process using proc_open, then calling proc_get_status should return an array that has the exit code in it.
You could create a bash script that backgrounds the shutdown process:
#!/bin/bash
shutdown -r 1 &
exit 0
This returns control to the parent shell, which receives "0" as the exit code.
Unfortunately, you can't rely on PHP's system() and exec() functions to retrieve the proper return value, but with a nice little workaround in BASH, it's possible to parse exit code really effectively:
function runthis($command) {
$output = array();
$retcode = -1;
$command .= " &2>1; echo $?";
exec($command, $output, $retcode);
$retcode = intval(array_pop($output));
return $retcode;
}
if (runthis("shutdown -r 1") !== 0) echo "Command failed!\n";
Let me break down what does the code doing:
$command .= " &2>1; echo $?"; - expand the command so we pipe the stderr into stdout, then run echo $?
echo $? - this special bash parameter which expands to the last executed command's exit code.
exec($command, $output, $retcode); - execute the command. ($retcode is just a placeholder here since the returned data isn't trustworthy. We'll overwrite it later.) The command's output will be written in $output as an array. Every element will represent an individual row.
$retcode = intval(array_pop($output)); - parse the last row as an integer. (since the last command will be echo $?, it will be always the actual exitcode.
And that's all you need! Although it's a really crude code, and prone to errors if not used correctly, it's perfect for executing simpler tasks, and it will always give you the proper exit code.
For more professional (and programmatic) approach, you have to dig yourself into PHP's pnctl, posix, stream functions, and also Linux pipe handling.

php exec() error

I'm having a little problem with the following:
When I execute this line:
echo exec(createDir($somevariable));
I get this error:
Warning: exec() [function.exec]: Cannot execute a blank command in /home/mydir/myfile.inc.php on line 32
Any ideas.
Thanks.
exec() expects a string argument, which it would pass on to your operating system to be executed. In other words, this is a portal to the server's command line.
I'm not sure what function createDir() is, but unless it's returning a valid command line string, it's probably failing because of that.
In Linux, you might want to do something like
exec('/usr/bin/mkdir '.$path);
...on the other hand, you should abstain from using exec() at all costs. What you can do here, instead, is take a look at mkdir()
With exec you can execute system calls like if you were using the command line. It hasn't to do anything with executing PHP functions.
To create a directory you could do the following:
exec( 'mkdir [NAME OF DIRECTORY]' );
I'd guess that your createDir() function doesn't return anything. Might also be worth checking that $somevariable is also set to something sensible
You're misunderstanding the purpose of exec(). If all you want to do is create a directory then you should use mkdir().
I think I've derived from other posts and comments what it is you actually want to do:
I think createDir() is a PHP function you've written yourself. It does more than just make a directory - it populates it, and that might take some time.
For some reason you believe that the next command gets run before createDir() has finished working, and you thought that by invoking createDir() using exec() you could avoid this.
Tell me in a comment if this is way out, and I'll delete this answer.
It's seems unlikely that createDir() really does keep working after it's returned (if it does, then we call that 'asynchronous'). It would require the programmer to go out of their way to make it asynchronous. So check that assumption.
Even so, exec() is not for invoking PHP functions. It is for invoking shell commands (the kind of thing you type in at a command prompt). As many of us have observed, it is to be avoided unless you're very careful - the risk being that you allow a user to execute arbitrary shell commands.
If you really do have to wait for an asynchronous function to complete, there are a couple of ways this can be done.
The first way requires that the asynchronous function has been written in an amenable manner. Some APIs let you start an asynchronous job, which will give you a 'handle', then do some other stuff, then get the return status from the handle. Something like:
handle = doThreadedJob(myParam);
# do other stuff
results = getResults(handle);
getResults would wait until the job finished.
The second way isn't as good, and can be used when the API is less helpful. Unfortunately, it's a matter of finding some clue that the job is finished, and polling until it is.
while( checkJobIsDone() == false ) {
sleep(some time interval);
}
I'm guessing createDir() doesn't have a return value.
Try exec("mkdir $somevariable");

Categories