CGI Scripting in C++

I wrote this code. Feel free to use it, at no charge, however you like. --Richard Gillmann

C Language "Hello, World" program for CGI

Yes, really, you can write CGI programs in C or C++. Here's a real simple one to get you started. Compile it, put it in the CGI-BIN directory and off you go.

/*
   hello.c - CGI program for the web
*/

#include <stdio.h>

int main()
{
	printf("content-type: text/html\n\n");

	printf("<html>\n<body>\n");
	printf("<h1> Hello, World! </h1>\n");
	printf("</body>\n</html>\n");

	return 0;
}

C++ Language "Hello, World" program for CGI

OK, let's be a little more modern and do it in C++.

// hellop.cpp - CGI program for the web

#include <iostream>
using namespace std;

int main()
{
	cout << "content-type: text/html" << endl << endl;

	cout << "<html><body>" << endl;
	cout << "<h1> Hello, World! </h1>" << endl;
	cout << "</body></html>" << endl;

	return 0;
}

File Locking

I wrote this function to do file locking in a CGI program for the web. I couldn't find a useful example on the web, so here's mine and I hope you find it handy. It's been tested on Red Hat Linux, for a CGI program running under Apache. For Windows, you'll have to do something different, using _sopen I think.

// assume Unix, use fcntl
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>

// file locking (so multiple users don't interfere with each other)
// bWrite = true for write/exclusive access, else read/shared access
// returns a file descriptor if successful, else -1
// note: locks are released when the files are closed at end of program
int LockPlease(const char * filename,const bool bWrite)
{
    const int MAX_TRY = 10;
    int fd; // file descriptor
    struct flock lck;

    fd = open(filename, (bWrite ? O_WRONLY : O_RDONLY));
    if (fd < 0)
        return -1; // file not found, or could not be opened

    // set up the record locking structure, the address of which
    // is passed to the fcntl system call.
    lck.l_type = (bWrite ? F_WRLCK : F_RDLCK);  // write or read lock
    lck.l_whence = 0;                           // from beginning of file
    lck.l_start = lck.l_len = 0L;               // until end of the file

    // Attempt locking MAX_TRY times before giving up.
    for (int tryno = 0; tryno < MAX_TRY; tryno++)
        if (fcntl(fd, F_SETLK, &lck) == 0)
            return fd; // lock obtained OK
        else if (errno == EAGAIN || errno == EACCES) // file in use
            sleep(2); // wait 2 seconds and try again
        else
            return -1; // fcntl error other than file in use
    return -1; // MAX_TRY exceeded
}

Get Environment

This C program lists the arguments passed to the program, lists all the environment variables it can see, and shows what came in on stdin.

/*
   showenv.c - display arguments and environment variables
*/

#include <stdio.h>

int main(int argc, char *argv[], char*envp[])
{
	int i,c;

	printf("content-type: text/html\n\n");

	printf("<HTML>\n<HEAD>\n");
	printf("<TITLE>Arguments and Environment Variables</TITLE>\n");
	printf("</HEAD>\n<BODY>\n");

	printf("<H1>Arguments</H1>\n<P>\n");
	for (i=0; i<argc; i++)
		printf("%d: %s<BR>\n",i,argv[i]);

	printf("</P>\n<H1>Environment Variables</H1>\n<P>\n");
	for (i=0; envp[i]; i++)
		printf("%s<BR>\n",envp[i]);

	printf("</p>\n<H1>Stdin</h1>\n<pre>\n");
	while ((c=getchar()) != EOF)
		putchar(c);
	
	printf("</pre>\n</BODY>\n</HTML>\n");

	return 0;
}

Test out reading a file

This C++ program reads a text file and displays the first line.


//  readfile.cpp - CGI program for the web
//  test out reading a data file from a cgi program

#include <fstream>
#include <iostream>
using namespace std;

int main()
{
	char buffer[10000];
	ifstream fin;

	cout << "content-type: text/html" << endl << endl;

	cout << "<HTML>" << endl << "<BODY>" << endl;

	// your pathname here
	fin.open("/usr/home/yourlogin/data/testfile.txt",ios::in);
	if (fin.is_open()) {
		memset(buffer,0,sizeof(buffer));
		fin.getline(buffer,sizeof(buffer)-1);
		cout << "<h1> Here is your data: </h1>" << endl;
		cout << "<p> " << buffer << " </p>" << endl;
		fin.close();
	} else
		cout << "<h1> Couldn't open the file. </h1>" << endl;
	
	cout << "</BODY>" << endl << "</HTML>" << endl;

	return 0;
}



Getting a CGI program working on a new ISP

ISPs all do things differently, so it can take a bit of work to move a compiled CGI program to a new one. Here are the steps that I've found work for me.

  1. Get the "hello" and "showenv" programs shown above working on the new system.
    You may need to put it in a special CGI or CGI-BIN directory. You may need to set the permissions on the executable file to allow read and execute by all users. You may need to give the executable a file name with a special extension (like .cgi). Or some combination of these things.
    I've found that the "make" program on FreeBSD is funky, but you can use "gmake" instead. I mention BSD because I haven't yet found a Linux-based ISP that is fully satisfactory for doing CGI programs in C++. Many ISPs even ban development. You can do this stuff on a Windows server, too, but things like file locking and sendmail will be different.
  2. Try a sample form using POST and/or GET and having the action be the showenv program. Make sure the form fields are coming through on stdin for POST.
  3. Get reading a file working.
    Use the readfile program above. For some reason you have to use absolute pathnames in cgi programs, relative ones don't work.
  4. Now you can try your big program, using what you have learned.

© Copyright 2007 by Richard Gillmann Last modified
Richard Gillmann Home Page