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

I’m just beginning to learn perl and am trying to create a login for my website. My login form takes a username and password and passes that to the login processor (login.pl). I want login.pl solely to determine the success or failure of the login credentials, then, if successful, pass along some user info to another part of the web app. I.e., I don’t want to combine the credential checking and another part of the app, I want to separate the function.

I’ve tried using the exec function, but that runs the script in command line mode instead of CGI, which results in a long delay and a broswer error “The connection was reset”.

How do I end the perl script called by the web form and initiate another script without interaction from the user?

Any help you can provide is greatly appreciated.

System information:
Perl version: v5.8.8 built for MSWin32-x86-Multi-Thread (ActivePerl)
Web server: Apache
This is an XAMPP installation all running local

  • Comment on How to call perl CGI script from another perl CGI script

Replies are listed 'Best First'.
Re: How to call perl CGI script from another perl CGI script
by jettero (Monsignor) on Apr 24, 2007 at 16:58 UTC

    This problem is usually solved by passing a "cookie" to the user with some kind of identifying code or number in it. You'd keep a lookup table of some kind that contained their user information or "session" or whatever else.

    I highly recommend CGI::Session for this task. It handles all the mindless tedium for you and provides a familiar API ($session->param("username")) so it drops right into your existing scripts.

    I think you might be looking to execute your script with something like LWP or WWW::Mechanize though. You can get this to work, but it'll be a maintenance nightmare in the long run and most likely you'd wish you "did it right in the first place" a few years down the road.

    -Paul

      Thanks. I'm taking a look at CGI::Session now.
Re: How to call perl CGI script from another perl CGI script
by friedo (Prior) on Apr 24, 2007 at 17:00 UTC

    Does login.pl need to be another CGI script? Why not just write a module to handle your login functions?

    exec causes your script to time out because it never returns. You should be able to execute the other script with system, but it's going to spit its data to STDOUT. You can capture that using something like IPC::Open3, but it hardly seems worth the trouble.

      I thought about using a module, but then I've got an unrelated function call at the top of a script that has nothing to do with logging in.

      Here's the flow of the app:
      login->select a project->do something with that project

      But "select a project" might be initiated from somewhere else (not login) within the web app as well.

      E.g., I want to list all projects the user is associated with, but I might want to call this project list from 1) the login screen; 2) some other area within my application. This script shouldn't expect to process login information every time it's called or expect to receive login credentials every time it's called.

      It seems like this would be a common problem in perl/CGI. Should I assume that most work done in perl/CGI is either 1) user initiated through a form; or 2) executed with a single script calling functions from other modules?

        Should I assume that most work done in perl/CGI is either 1) user initiated through a form; or 2) executed with a single script calling functions from other modules?

        You've got it - both. :)

        Re 1) Everything CGI is always in response to an http request, so in that sense it's user initiated (unless the page is being spidered by a bot or whatever, but you get the point). It doesn't make any difference to the script itself whether it gets its parameters from a form, or from a URL with extra stuff on the end (or maybe it doesn't take any parameters at all...)

        Re 2) It's certainly normal practise to put code that you'll need in more than one script inside a module. That's pretty much what modules are for. You can use fork() and so on to go out to other scripts, but it's not a great idea - apart from anything it's pretty slow. If you need to start something running but you don't want to wait for it to finish before sending the page back to the user, then personally I'm a fan of Apache's callback system, which lets you run some more code after the page has been sent back to the user.

        So anyway, yes, it seems to me that by far the better plan is to put the credentials-checking code in a separate module. Then you can call it from anywhere you like.

        One approach to credentials-checking (and there are others) is to do an initial username/password login, then place a cookie in the user's browser that keeps them logged in for that session (the cookie should not contain the password - usually it's an MD5 hash or something like that).

        The book 'Writing Apache modules with Perl and C' has lots of useful stuff on this kind of thing. Obviously it's more relevant to Apache than to other web servers. <grin>

        One approach that can be used is to put the credentials-checking stuff in the Authentication/Authorization stages of the Apache request cycle. This is good because:

        • It's a lot easier than it sounds
        • It happens automatically for every request - so your normal scripts can get on with doing whatever they do, without needing to worry about whether the user is authenticated.

        There's more info about how to do that, and some great examples, here (a chapter from the book named above). The basic example looks like this:

        Listing 6.1: Simple access control package Apache::GateKeeper; # file: Apache/GateKeeper.pm use strict; use Apache::Constants qw(:common); sub handler { my $r = shift; my $gate = $r->dir_config("Gate"); return DECLINED unless defined $gate; return OK if lc $gate eq 'open'; if (lc $gate eq 'closed') { $r->log_reason("Access forbidden unless the gate is open", $r +->filename); return FORBIDDEN; } $r->log_error($r->uri, ": Invalid value for Gate ($gate)"); return SERVER_ERROR; } 1; __END__ The .htaccess file that goes with it PerlAccessHandler Apache::GateKeeper PerlSetVar Gate closed
        (by Lincoln Stein and Doug MacEachern)
        with this explanation for the codes passed to Apache:
        DECLINED means that this handler doesn't deal with the request (instead control is passed to the next handler, if there is one)
        OK means sure, let the user in
        FORBIDDEN returns the 'access forbidden' error to the user's browser
        SERVER_ERROR is obvious.

        So in the real world you might first check whether the URL is one to which you want to control access (if it isn't then return DECLINED), check whether the user has the right credentials, if they do then return OK else return FORBIDDEN.

        HTH!

        Best wishes, andye

        You might be able to handle that by having separate modules for handling logins and project lists. Then any script can get a list of projects or perform a login as it sees fit.
Re: How to call perl CGI script from another perl CGI script
by wjw (Priest) on Apr 24, 2007 at 17:10 UTC
    I think you might be helped by looking at something like This based on your experience with Perl. Sometimes the little examples provided are just enough to trigger an understandinig of what you want to do.

    Good luck

    • ...the majority is always wrong, and always the last to know about it...
    • The Spice must flow...
    • ..by my will, and by will alone.. I set my mind in motion
      Thanks. I've got Learning Perl, Programming Perl, and Custom CGI Scripting with Perl. The first two don't address CGI and are very Unix centric, while the last is not very deep. Maybe need to look into a more comprehensive perl/CGI book.
        You ought to take a look at Ovid's CGI tutorial, too. It's very good.


        TGI says moo

        Learning Perl unix-centric and doesn't address CGI? That means you must have the first edition (the pink Llama), and that's from a long, long time ago. Or, you have the third or fourth edition, which are platform agnostic, but you didn't realize that.
Re: How to call perl CGI script from another perl CGI script
by scorpio17 (Canon) on Apr 24, 2007 at 21:10 UTC
    You might also want to take a look at CGI::Application. There are plugins available for many things, including sessions, authentication, authorization, etc. It makes it easy to seperate the login/logout code from the web app code. It also makes it possible to write multiple web apps, and have them all share the same login/logout code. There's a bit of a learning curve, but once you get the hang of it, it will save you lots of time in the long run.
Re: How to call perl CGI script from another perl CGI script
by Calm (Acolyte) on Apr 24, 2007 at 21:50 UTC
    All of the responses have been great and I appreciate them all. I've stumbled across another possible way to achieve what I was looking for: URL redirection through the HTML output. Instead of outputting HTML content like this:

    print "Content-type: text/html\n\n";

    I can do a redirect to another static HTML page or script like this:

    print "Location: projectlist.pl\n\n";

    It appears that the downside is I'm not doing a POST so I'm not passing any data between the two pages, which means I still need to set a cookie or something to maintain some state information.

    Anyway, just another potential option. Now I need to get in the habit of using CGI.pm instead of writing my own HTML...