tags:

views:

3595

answers:

12

I'm looking for way to PHP to detect if a script was run from a manual invocation on a shell (me logging in and running it), or if it was run from the crontab entry.

I have various maintenance type scripts written in php that i have set to run in my crontab. Occasionally, and I need to run them manually ahead of schedule or if something failed/broken, i need to run them a couple times.

The problem with this is that I also have some external notifications set into the tasks (posting to twitter, sending an email, etc) that I DONT want to happen everytime I run the script manually.

I'm using php5 (if it matters), its a fairly standard linux server environment.

Any ideas?

+8  A: 

Not that I know of - probably the simplest solution is providing an extra parameter yourself to tell the script how it was invoked.

Dominic Rodger
+1  A: 

I would look into $_ENV (var_dump() it) and check if you notice a difference when you run it vs. when the cronjob runs it. Aside from that I don't think there is an "official" switch that tells you what happened.

Till
+1  A: 

In the cron command, add ?source=cron to the end of the script path. Then, in your script, check $_GET['source']

EDIT: sorry, it's a shell script so can't use qs. You can, i think, pass arguments in the form php script.php arg1 arg2 and then read them with $argv

adam
It's a shell script, not a webpage.
Matthew Scharley
+14  A: 

You can setup an extra parameter, or add a line in your crontab, perhaps:

CRON=running

And then you can check your environment variables for "CRON". Also, try checking the $SHELL variable, I'm not sure if/what cron sets it to.

Matthew Scharley
i never knew you could put other commands/variable lines in the crontab like that. i only ever learned the syntax for the timed commands. this plus the php $_ENV and $_SERVER, will help what i'm trying to achieve. thanks
Uberfuzzy
"man 5 crontab" for all the gritty details. But yes, you can set environment variables. Normally this is used to set PATH or perhaps SHELL, but you can set whatever you like.
Matthew Scharley
+5  A: 

I don't know about PHP specifically but you could walk up the process tree until you found either init or cron.

Assuming PHP can get it's own process ID and run external commands, it should be a matter of executing ps -ef | grep pid where pid is your own process ID and extract the parent process ID (PPID) from it.

Then do the same to that PPID until you either reach cron as a parent or init as a parent.

For example, this is my process tree and you can see the ownership chain, 1 -> 6386 -> 6390 -> 6408.

UID     PID  PPID  C  STIME  TTY        TIME  CMD
root      1     0  0  16:21  ?      00:00:00  /sbin/init
allan  6386     1  0  19:04  ?      00:00:00  gnome-terminal --geom...
allan  6390  6386  0  19:04  pts/0  00:00:00  bash
allan  6408  6390  0  19:04  pts/0  00:00:00  ps -ef

The same processes run under cron would look like:

UID     PID  PPID  C  STIME  TTY        TIME  CMD
root      1     0  0  16:21  ?      00:00:00  /sbin/init
root   5704     1  0  16:22  ?      00:00:00  /usr/sbin/cron
allan  6390  5704  0  19:04  pts/0  00:00:00  bash
allan  6408  6390  0  19:04  pts/0  00:00:00  ps -ef

This "walking up the process tree" solution means you don't have to worry about introducing an artificial parameter to indicate whether you're running under cron or not - you may forget to do it in your interactive session and stuff things up.

paxdiablo
Presumably, you'd set it up in such a way that 'no parameter' means you're running interactively, that way you couldn't forget it. Interesting solution though.
Matthew Scharley
Yes, very interesting approach. Could have uses for other non-php things too.
Uberfuzzy
+8  A: 

Instead of detecting when the script is run from the crontab, it's probably easier to detect when you're running it manually.

There are a lot of environment variables (in the $_ENV array) that are set when you run a script from the command line. What these are will vary depending on your sever setup and how you log in. In my environment, the following environment variables are set when running a script manually that aren't present when running from cron:

  • TERM
  • SSH_CLIENT
  • SSH_TTY
  • SSH_CONNECTION

There are others too. So for example if you always use SSH to access the box, then the following line would detect if the script is running from cron:

$cron = !isset($_ENV['SSH_CLIENT']);

Paul Stone
after looking into it, this seem like the best method. between _ENV + _SERVER, i'm pretty sure i can confidently detect who/where/who is running it, and act accordingly. the CRON=running line in crontab will help too.
Uberfuzzy
A: 

$_SERVER['SESSIONNAME'] contains 'Console' if run form the CLI. Maybe that helps.

bouke
I'd use php_sapi_name() - but that still won't differentiate between cron and interactive CLI execution.
Sean McSomething
A: 

if(!$_SERVER['HTTP_HOST']) { blabla(); }

This one makes the most sense to me, except I would use if (!isset($_SERVER['HTTP_HOST'])) { blah(); }.
Stacey Richards
I'm pretty sure $_SERVER['HTTP_HOST'] isn't present when running both Cron- OR CLI-run scripts. The original poster is after a way to differentiate between Cron and CLI, not CLI and Web.
Rob Howard
A: 

I think it would be better to run the cron commmand with an additional option at the command line that you wouldn't run manually.

cron would do:

command ext_updates=1

manual would do:

command

Just add an option in the script itself to have the ext_updates param to have a default value of false.

A: 

In my environment, I found that TERM was set in $_SERVER if run from the command line, but not set if run via Apache as a web request. I put this at the top of my script that I might run from the command line, or might access via a web browser:

if (isset($_SERVER{'TERM'}))
{
    class::doStuffShell();
}
else
{
    class::doStuffWeb();
}
Drew Stephens
A: 

The right approach is to use the posix_isatty() function on e.g. the stdout file descriptor, like so:

if (posix_isatty(STDOUT))
    /* do interactive terminal stuff here */
Wouter Bolsterlee