Previous Section | Table of Contents | Next Section

Running "Hello, world!" as a CGI Script

This section of the tutorial covers:

Content-type headers

Now let's modify hello.pl so it will run as a CGI script. Every CGI script needs to output a special header as the first thing the script outputs. This header line is checked by the Web server, then passed on to the remote user invoking the script in order to tell that user's browser what type of file to expect. Most of the time, your script is going to output an HTML file, which means you'll need to output the following header:

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

You need to output it exactly like that, including the capital "C" and the lowercase everything else. Please note that there are two newline characters (\n\n) at the end of the header. CGI novices tend to forget that, but it's really important, since the header needs to be followed by a blank line.

So, adding that line to our hello.pl script gives us the following:

#!/usr/bin/perl

# hello.pl -- my first perl script!

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

print "Hello, world!\n";

Return to the top of the page

Here-document quoting

As long as we're claiming this is HTML that we're outputting, let's go ahead and make our output a valid HTML file:

#!/usr/bin/perl

# hello.pl -- my first perl script!

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

print <<"EOF";
<HTML>

<HEAD>
<TITLE>Hello, world!</TITLE>
</HEAD>

<BODY>
<H1>Hello, world!</H1>
</BODY>

</HTML>
EOF

Take a careful look at the stuff that replaced the "" characters used to quote the original "Hello, world!\n" line. That <<"EOF"; thing, and the EOF all alone on a line by itself at the end, is being used to quote a multi-line string. Basically, it's being used to indicate what the "print" command should print. This is sometimes called "here-document" quoting; you can call it whatever you want, but it's a real time-saver in CGI scripts.

There's nothing special about the "EOF" string I used to delimit my output, by the way; you can use anything you like, as long as it's the exact same at the beginning and end of the quoted string (including capitalization). So I could have said:

print <<"Walnuts";
Some stuff
I want to have printed...
Walnuts

and it would have worked fine. Just make sure, again, that "Walnuts" is all by itself on the last line. Even a space character after it will screw things up, as will anything in front of it. It needs to be right at the left margin, with nothing after it but a newline.

Return to the top of the page

File locations/extensions for running CGI scripts

There's one more thing we need to do in order to run hello.pl as a CGI script: we need to let the Web server know it's a CGI script. How you do this depends on how your ISP has configured their Web server. The two most common ways are to change the file's name so it ends with a ".cgi" extension, or to place the file in a special directory on the server called "/cgi" or "/cgi-bin". Ask your ISP how the server is configured, and proceed accordingly.

If you want to go ahead and try sticking a ".cgi" extension on the end of your script be my guest; the worst that will happen if your Web server doesn't support that convention is that it will simply deliver the text of the script to your browser, rather than executing it. Like I said, ask your ISP.

For this discussion, I'm going to assume that you can run a CGI script in any directory in your Web space, as long as the script has a ".cgi" extension. So let's copy the script to an appropriate directory, and change its extension to ".cgi":

catlow:/u1/j/jbc> mkdir /w1/l/lies/begperl
catlow:/u1/j/jbc> chmod 755 /w1/l/lies/begperl
catlow:/u1/j/jbc> cp hello.pl /w1/l/lies/begperl
catlow:/u1/j/jbc> cd /w1/l/lies/begperl
catlow:/w1/l/lies/begperl> mv hello.pl hello.cgi

Did you follow that? First I used the Unix "mkdir" command to create a new directory called "begperl" in my Web space, which in my particular case is located at "/w1/l/lies". (Trivia: The Unix mkdir command is the only one I can think of that is actually longer than the equivalent DOS command, "md".) I chmodded the directory to permissions 755, then used the "cp" command to copy hello.pl from my home directory to the new directory in my Web space. Then I used the "cd" command to change to that directory, and used the "mv" command to change the file's name from "hello.pl" to "hello.cgi", so the Web server will know that it's a CGI script.

A couple of additional things about directories and Web space and so on: My Unix command prompt has been customized to show my current directory; if yours hasn't, you can use the command "pwd" to list the current directory any time you lose track. Another handy tool is the "~" symbol; when you use that in a command in the Unix shell, it is automatically replaced by the path of your "home" directory, which is the directory you start off in when you first log into the server.

Many ISPs, by the way, use a convention of having users' Web stuff go in a directory called "public_html" beneath their home directory. If that's the case with your ISP, then you'll need to substitute directory names accordingly in the instructions given above, and use a Web address of the form "http://www.your-isp.com/~username/" or "http://www.your-isp.com/username/" to access documents in that public_html directory.

Return to the top of the page

Testing from the command line

Before we try to run the script via the Web server, let's try running it from the command line, just to make sure everything works the way we want it to:

catlow:/w1/l/lies/begperl> ./hello.cgi
Content-type: text/html

<HTML>

<HEAD>
<TITLE>Hello, world!</TITLE>
</HEAD>

<BODY>
<H1>Hello, world!</H1>
</BODY>

</HTML>

Did you get that? Great! If not, what happened? Did you get a "permission denied" error, by any chance? Then check your permissions: You probably lost the "execute" permission somewhere along the way.

Return to the top of the page

Testing from the Web server

Assuming your script did print out from the command line, let's go to the final step: testing the script via the Web server. In my case, that means typing the following into my Web browser's Location box:

http://www.lies.com/begperl/hello.cgi

In your case, it might be something like "http://www.your_isp.com/~your_username/hello.cgi". Whatever it is, go ahead and try it.

Internal Server Error

The server encountered an internal error or misconfiguration
and was unable to complete your request.

Please contact the server administrator, jbc@cyberverse.com and
inform them of the time the error occurred, and anything you might
have done that may have caused the error.

Ack. The dreaded "internal server error". You will see messages like this a lot when you are learning to run CGI scripts. What it means is, something (probably some sort of error message) got printed out by your script before the "Content-type: text-html\n\n" header.

It sure would be helpful if you could see that error message. Fortunately, since your ISP is enlightened enough to let you run your own CGI scripts, they're almost certainly enlightened enough to give you access to the Web server's error log. In my case, it's at /w1/l/lies/.logs/error.log.

One way to check that error log is to open up a second telnet window, log into a second shell session on your Unix server, and enter the command "tail -f /path/to/error.log". This will cause your window to display new entries in the error log as they are added. Then you can just pop back and forth from one window to the other to check the error log as you work on your script.

In this case, I'm just going to use my existing shell session, and issue the "tail" command without the -f switch to print out the last 10 lines of the error log, looking for the problem that caused my script to fail. Lo and behold, there it is:

catlow:/w1/l/lies/begperl> tail /w1/l/lies/.logs/error.log

(stuff deleted)

exec of /w1/l/lies/begperl//hello.cgi failed, reason: Permission denied
(errno = 13)
[Fri Sep 11 23:50:18 1998] access to /w1/l/lies/begperl//hello.cgi failed
for 207.71.222.193, reason: Premature end of script headers

So it was another "Permission denied" error. But wait; the script ran fine when I ran it manually from the command line. What gives?

Return to the top of the page

CGI script file permissions

What gives is that when I ran it from the command line I ran it as me, the file's owner. But when I ran it as a CGI script using the Web server, I ran it as somebody else.

This is a key point to understand: When a Web server runs your script, it's not the same thing as you running your script. That CGI script, by definition, is accessible to every obnoxious would-be hoodlum on the Internet, so when the Web server runs it, the assumption is that it is being run by someone who is malicious, stupid, or both. For this reason, Web servers are configured to run CGI scripts as a special, underprivileged user (often called "nobody"; another quaint Unixism that strikes me as funny) in order to minimize any damage that might be done.

In point of fact there isn't any real harm that this particular script can do, so we don't need to worry about the security implications of letting John Q. Public run it. To let him do so, though, we need to turn on the "read" and "execute" permissions for the "everybody else in the world" category of user. And we may as well turn on the same permissions for the file's group while we're at it, yielding a permissions setting of "755", which is the setting we're going to use for most of the CGI scripts we create from here on out:

catlow:/w1/l/lies/begperl> chmod 755 hello.cgi
catlow:/w1/l/lies/begperl> ls -l hello.cgi
-rwxr-xr-x   1 lies     www           217 Sep 11 23:42 hello.cgi

Now let's try running hello.cgi via the Web server again, either by hitting the "Reload" button in our browser, or just typing the script's address into the Location box again and hitting "Enter":

Hello, world!

All right! Take a break and pour yourself a tall, frosty one. You've earned it.

Return to the top of the page

Previous Section | Table of Contents | Next Section


John Callender
jbc@jbcsystems.com
http://www.lies.com/jbc/