I am having a script which can be launched with either cron, HTTP request (browser) or from another script. How can I determine from where a script was launched?
You can check a few pieces of information to find out:
The constant PHP_SAPI will tell you which PHP interpreter/interface is running - the names of common SAPI's are documented on the page for php_sapi_name
For command line scripts, you should be able to achieve what you want my using a combination of the posix_isatty function and checking for the existence of $_SERVER['TERM']
The most simple way to solve this is to pass an argument to your script. This will give you high control over the format of the information and it is (reasonably) safe.
Not sure, is this what you wanted ..
Cron:
php -q /dir/my_script.php cron
HTTP:
http://dir/my_script.php?arg=web
PHP:
if (!empty($argv[0]))
{
echo "local";
}
elseif(isset($_GET['arg']))
{
echo "web";
}
else
{
echo "Others"
}
Related
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.
Basically I'd like to know of a very simple method for making a php script check if a specific program is running on my nix box.
Something like :
<?php
#some detection function here.
if($detectprogram == 1){
echo("It's running.");
}
else{
echo("It's not running.");
}
?>
Or even if I had to execute the program via a .sh (as I already do) and an -outputphp /path/to/htdocs/. Or similar. Thanks.
Maybe you need this?
exec("ps auxwww|grep sample.php|grep -v grep", $output);
Source: How to get list of running php scripts using PHP exec()?
What would be the cleanest method to tell if PHP has been invoked via a POST for example and not from html (under a web broser)
Thanks,
If I understand your question correctly, it's not about http vs. command line call, but rather browser vs. "non-browser" (e.g. via curl, wget etc) call. There's no way to check this, because wget etc are technically browsers, they just don't happen to have a GUI. You can try checking HTTP_USER_AGENT, but this is totally unreliable, because there's no way to enforce a client to identify itself correctly.
This has been answered in another Q: What is the canonical way to determine commandline vs. http execution of a PHP script?
function __get_started_from_cli_state()
{
return substr(php_sapi_name(), 0, 3) == 'cli';
}
function __get_started_from_cgi_state()
{
return substr(php_sapi_name(), 0, 3) == 'cgi';
}
function __get_started_from_browser_state()
{
return !(__get_started_from_cli_state() ||
__get_started_from_cgi_state());
}
I need to write a script that will give users info on a given Unix account (the same Unix server that the script lives on). Mostly thing kinds of things that are in the passwd file or available via finger.
PHP is in safe-mode, so I can't access the passwd file via something built into php like file_get_contents(). Also, because it's in safe mode, various other command-line functions are disabled.
I thought I could get the info via a socket (no clue yet what that means, but I thought I'd try) but I get a fatal error that socket_create() is an unknown function. I pulled up the php-config file (which I can't change, FYI), and sure enough, sockets are not enabled.
However, while I was in there, I saw the line '--with-exec-dir=' with no actual directory set.
So then I remembered that when I was trying EVERY command line function, that some threw "not allowed in safe-mode" type errors, while others did nothing at all. If I put something like:
echo "[[";
exec("finger user");
echo "]]";
I'd end up with [[]]. So no errors, just no results either.
Bottom line:
Is there something I haven't tried? (in general)
Is there a runtime config option I can set to make exec() work?
quick note: I tried passthru() as well, specifically passthru("pwd") with still no output.
update
based on feedback, I tried both of the following:
$stuff = exec("pwd", $return);
echo "stuff=".$stuff."\n";
echo "return=";
print_r($return);
which results in:
stuff=
return=Array
(
)
and
$stuff = passthru("pwd", $return);
echo "stuff=".$stuff."\n";
echo "return=";
print_r($return);
which results in:
stuff=
return=1
The 1 sounds hopeful, but not what I want yet.
Idea
So this is actually an update of an already existing script that (please don't ask) I don't have access to. It's a perl script that's called via cgi. Is there a way to do php via cgi (so I don't have to deal with perl or rely on the older code)?
I'm afraid you can't do that in safe-mode. You have to remove the safe-mode if you have control of the server configuration.
I think you can't rely on sockets to read local files, sockets are used for network related things.
exec doesn't inherently return any data.
Try something like,
exec("finger user",$output);
echo "[[";
foreach($output as $key => $value){
echo $value;
}
echo "]]";
Exec returns a value, so do:
$var = exec("finger user");
and then parse the output to get what you want. You can get return status by adding in an optional variable thus:
exec("finger user", $var, $return_status);
or just:
echo exec("finger user");
if all you want is to see the output.
Thanks to all that responded, the following is what finally worked:
Create a cgi-bin folder
Add the following to the top of the php script:
#!/usr/local/bin/php-cgi
I don't know if this is something special on my server configuration, but I can run exec() and get what I'm after.
I need to determine whether the current invocation of PHP is from the command line (CLI) or from the web server (in my case, Apache with mod_php).
Any recommended methods?
php_sapi_name is the function you will want to use as it returns a lowercase string of the interface type. In addition, there is the PHP constant PHP_SAPI.
Documentation can be found here: http://php.net/php_sapi_name
For example, to determine if PHP is being run from the CLI, you could use this function:
function isCommandLineInterface()
{
return (php_sapi_name() === 'cli');
}
I have been using this function for a few years
function is_cli()
{
if ( defined('STDIN') )
{
return true;
}
if ( php_sapi_name() === 'cli' )
{
return true;
}
if ( array_key_exists('SHELL', $_ENV) ) {
return true;
}
if ( empty($_SERVER['REMOTE_ADDR']) and !isset($_SERVER['HTTP_USER_AGENT']) and count($_SERVER['argv']) > 0)
{
return true;
}
if ( !array_key_exists('REQUEST_METHOD', $_SERVER) )
{
return true;
}
return false;
}
php_sapi_name() is really not the best way to perform this check because it depends on checking against many possible values. The php-cgi binary can be called from the command line, from a shell script or as a cron job and (in most cases) these should also be treated as 'cli' but php_sapi_name() will return different values for these (note that this isn't the case with the plain version of PHP but you want your code to work anywhere, right?). Not to mention that next year there may be new ways to use PHP that we can't possibly know now. I'd rather not think about it when all I care about is weather I should wrap my output in HTML or not.
Fortunately, PHP has a way to check for this specifically. Just use http_response_code() without any parameters and it'll return TRUE if ran from a web server type environment and FALSE if ran from a CLI type environment. Here is the code:
$is_web=http_response_code()!==FALSE;
This will even work if you accidentally(?) set a response code from a script running from the CLI (or something like the CLI) before you call this.
I think he means if PHP CLI is being invoked or if it is a response from a web request. The best way would be to use php_sapi_name() which if it was running a web request would echo Apache if that is what it was running.
To list of a few taken from the php docs on php_sapi_name():
aolserver
apache
apache2filter
apache2handler
caudium
cgi (until PHP 5.3)
cgi-fcgi
cli
cli-server (Built-in web server as of PHP 5.4)
continuity
embed
fpm-fcgi
isapi
litespeed
milter
nsapi
phttpd
pi3web
roxen
thttpd
tux
webjames
This should handle all the cases (including php-cgi)
return (php_sapi_name() === 'cli' OR defined('STDIN'));
function is_cli() {
return !http_response_code();
}
example:
if (is_cli()) {
echo 'command line';
} else {
echo 'browser';
}
Try
isset($_SERVER['REQUEST_METHOD'])
if it's set, you're in a browser.
Alternatlely, you could check if
isset($_SERVER['argv'])
but that might not be true on windows CLI, IDK.
I used this:
php_sapi_name() == 'cli' || (is_numeric($_SERVER['argc']) && $_SERVER['argc'] > 0)
This is from Drush codebase, environment.inc where they have similar check to make.
I would suggest to check if some of the entries of the $_SERVER array are set.
E.g.:
if (isset($_SERVER['REQUEST_METHOD'])) {
print "HTTP request\n";
} else {
print "CLI invocation\n";
}
According to http://jp2.php.net/manual/en/features.commandline.php There are a number of constants set only when running from the CLI. These constants are STDIN, STDOUT and STDERR. Testing for one of those will tell you if it is in cli mode
joomla way
if (array_key_exists('REQUEST_METHOD', $_SERVER)) die();
An easy way is to interrogate the $argv variable, (Which you will probably do for command line parameters anyway). Even if there are no parameters $argv returns an empty array.
If it is set, then cli was used. You may then assume all other invocations are via some web server or other.
eg:
if (isset($argv)) {
// Do the cli thing.
}
The correct answer to this question depends on the real intent behind it:
Is the SAPI the deciding factor (web-context or not)?
Or is the information interpreted as 'running in a tty'?
If the former the answers given and comments written are enough to find a solution that works.
If the latter, the recipes given here will fail if the tool is run as cronjob, or as background-job from another daemon -- in that case I suggest to further test if STDIN is a TTY:
function at_tty() {
return defined("\STDIN") && posix_isatty(\STDIN);
}
How, so many complicated solutions. How about ...
if($_SERVER['REQUEST_SCHEME']=="http" or $_SERVER['REQUEST_SCHEME']=="https"){
// must be browser :)
}
// Detect CLI calls
define("IS_CLI_CALL",( strcmp(php_sapi_name(),'cli') == 0 ));
if(IS_CLI_CALL){
//do you stuff here
}
Based off Silver Moon's answer above, I'm using this function for returning correct linebreaks:
/**
* Linebreak function
* #return "/n" if cli, else return <br>
*/
protected static function lb(){
return (defined('STDIN') || php_sapi_name() === 'cli' || isset($_ENV['SHELL']) ||
(empty($_SERVER['REMOTE_ADDR']) && !isset($_SERVER['HTTP_USER_AGENT']) && count($_SERVER['argv']) > 0) ||
!isset($_SERVER['REQUEST_METHOD'])) ? "\n" : "<br>";
}
A practical hint
The official way (as told by many) is PHP_SAPI as a constant, or php_sapi_name() as a function, they both return cli when you're in a command line situation. They're right.
But!...
Consider using $_SERVER["argv"] (also $argv in most cases) which is null when you run in a browser, and an array when you've been called from command line. The advantage of this approach (or using both) is that you can simulate a terminal run in a browser, by just giving a (fake) value to the $argv / $_SERVER["argv"] variable. This comes in handy when you test on an outside server (prod, staging, etc) where you typically won't get SSH access.
The best way to do this is keeping in mind whether you may or may not need a CLI simulation, and use both $argv and PHP_SAPI to coordinate this - e.g. you may need to output an extra <pre> tag beforehand if PHP_SAPI is not "cli" but $argv has a value.
My preferred method:
if (array_key_exists('SHELL', $_ENV)) {
echo "Console invocation";
}
else {
echo "HTTP invocation";
}
I'd try:
echo exec('whoami');
Usually webservers are run under a different username, so that should be telling.