VK has asked for the wisdom of the Perl Monks concerning the following question:

Hi, I didn't work with Perl for ages but recently I needed it for a small task. So basically the script will run once every day by a cronjob (no HTML output). It also will have some info to show when called from a browser like https://www.example.com/cgi-bin/myscript.cgi

So my question is if it is possible to reliable distinguish - inside the script - if it was called by crontab or by HTTP request (some envs or something)? It will be running at Apache.

(Update) Thank you all for your help. I eventually went by if (exists $ENV{REQUEST_URI}) {web call} else {not web call} By "some definition of "reliable" it is plenty good as I see it. Unless some sysadmin goes nuts with system configs - but it is in any case a battle one can fight but cannot win. Because if a sysadmin mangles with REQUEST_URI or GATEWAY_INTERFACE - he/she equally may install Perl to /usr/bin/foo/bar/current_day to make the shebang dynamic - or anything equally crazy. My current hosting provider is not of kind, so everything is fine.

A more bulletproof solution as I read it: to have 2 separate scripts (modules). One outside of /public_html or its equivalent so cannot be reached by a web-call (for crontab), other inside /public_html (for web access). Also some file for interscript communication and maybe some higher level script for the main business logic. The pay back for this: a violation of the KISS principle - instead of of one place where something may go wrong there will be 3-4 places where something may go wrong. And for outside of /public_html something may go really wrong. So the pay off doesn't cover the back back, IMO.

Replies are listed 'Best First'.
Re: How to distinguish a crontab call and a web call
by choroba (Cardinal) on Aug 29, 2019 at 15:05 UTC
    The easiest thing would be to specify a parameter in the crontab call.
    /opt/perl /home/www/cgi-bin/myscript.cgi CRON

    If the crontab runs under a different user than the webserver, you can check $>.

    But the best solution would be to put the common code into a module and use it from two different scripts, one running from crontab and one running in the webserver.

    map{substr$_->[0],$_->[1]||0,1}[\*||{},3],[[]],[ref qr-1,-,-1],[{}],[sub{}^*ARGV,3]
Re: How to distinguish a crontab call and a web call
by davido (Cardinal) on Aug 29, 2019 at 16:58 UTC

    Is your web server calling this code as the same user as the cron? If not, then explore $< (real UID), $> (effective UID), $( (real GID), and $) (effective GID) in perlvar.

    But creating user-specific behavior smells bad. The better approach is to load the appropriate configuration for a specific use case. Then design config loading possibly around the paradigm of global, environment, user, command-line: Global loads first, then environment (production versus development, for example) overlays global config, then user config overlays, and finally command-line configuration options overlay. Configurable feature sets based on the user's config are easier to reason about than peppering code with if($user eq '...').

    Additionally, if your business logic is supported by modules, and the calling of the business logic module is done by a minimal script, it's not hard to create one script that loads its configuration and options, and then invokes the module for crons, and another script that loads its configuration and options, and then invokes the module for web services. This strategy also leads to much more testable code.


    Dave

Re: How to distinguish a crontab call and a web call
by hippo (Archbishop) on Aug 29, 2019 at 15:34 UTC
    So my question is if it is possible to reliable distinguish - inside the script - if it was called by crontab or by HTTP request (some envs or something)?

    For at least some definition of "reliable":

    print "called from " . (exists $ENV{REQUEST_URI} ? "web\n" : "non-web\ +n");
Re: How to distinguish a crontab call and a web call
by Fletch (Bishop) on Aug 29, 2019 at 15:30 UTC

    Going from scratch the module solution mentioned above is probably the "best" way, but if this is something legacy you can't monkey with too much you could look for the presence of $ENV{GATEWAY_INTERFACE}. Unless your cron implementation's really weird it shouldn't be set there, but mod_cgi for apache (or any CGI host environment honoring the spec) should set it in a CGI context.

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

Re: How to distinguish a crontab call and a web call
by Popov (Initiate) on Aug 29, 2019 at 23:40 UTC

    You can make two separate scripts, one called by the crontab and one called by the CGI, let's say `cli.pl` and `index.pl`. They can call a common `do-my-work.pm` script with the business logic.

    This will help you to separate the input params and the presentation in the calling scripts and the domain in the separate module. You can have the complete HTML or json output for the web call only in `index.pl`.

Re: How to distinguish a crontab call and a web call
by Anonymous Monk on Aug 31, 2019 at 02:43 UTC
    You could use a symlink or hard link, invoke the different instances with different names, and check the value of $0.