Let a cgi script write a server page file with proper permissions

Web servers should run under a user who does have minimal rights and files accessible by the server should be owned by and writeable only by another user, preferably root. So, what to do if the cgi script should update a file that is accessible from the web?
One simple way is to let the cgi script write the file somewhere the web server cannot reach, e.g. in the /tmp directory. Then a dæmon running as root can copy the file to the server directory. In my case this doesn’t happen very often, so a dæmon constantly polling the file is not a good solution. I chose to use the inotify mechanism present in recent (>= 2.6.13) Linux kernels.
This C-code was adapted from  http://www.ibm.com/developerworks/linux/library/l-ubuntu-inotify/index.html and actually reads a file generated by a subversion commit. This file contains nothing but a (version) number. The program reads the number and writes an include file with a version string. This file is then available at the site as ver.h.(My client wanted to have the last version number available without installing subversion). Subversion calls the script hooks/post-commit after each commit, the second command line argument being the new version number. My script looks like this:

#!/bin/sh REV="$2"echo -e "$REV\n" > /tmp/ver.txt

That’s all. The following program, mkver.c, registers an inotify for this file with the kernel and just goes to sleep until /tmp/ver.txt is modified. Then it wakes up, reads the file and generates ver.h.

#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include "inotify.h"
#include "inotify-syscalls.h"

#define EVENT_SIZE ( sizeof (struct inotify_event) )
#define BUF_LEN ( 1024 * ( EVENT_SIZE + 16 ) )

int main( int argc, char **argv )
{
int length;
int fd;
int wd;
char buffer[BUF_LEN], s1[60], s2[60];
FILE *fi, *fo;
char verstr[20];
int ver;
fd = inotify_init();
if ( fd < 0 ) {
      perror( "inotify_init" );
}
wd = inotify_add_watch( fd, "/opt/tmp/ver.txt",
        IN_CLOSE_WRITE ); /* trigger when closed after a write */
while(1) {
        length = read( fd, buffer, BUF_LEN );
        if ( length < 0 ) {
            perror( "read" );
        }
        struct inotify_event *event = ( struct inotify_event * ) &buffer[0];
        fflush(stdout);
        fi= fopen("/tmp/ver.txt", "r");
        if (fi == NULL) continue;
        if (fgets(verstr, 10, fi) == NULL) continue;
        fclose(fi);
        ver= atoi(verstr);
        fo= fopen("/var/www/ver.h", "w");
        strcpy(s1, "/* generated by svn post-commit */\r\n");
        sprintf(s2, "char versionString[]= {\"%d\"};\r\n", ver);
        fprintf(fo, "%s%s", s1, s2);
        fclose(fo);
        printf("event: %d %d\n", event->mask, ver);
        fflush(stdout);
    }
}

The dæmon is started at boot time by this entry in /etc/rc.local:

/usr/local/sbin/mkver >>/var/log/mkver &

The printf-output will thus be logged in /var/log/mkver.

0 Responses to “Let a cgi script write a server page file with proper permissions”


  • No Comments

Leave a Reply