From http://svn.blinkenlights.nl/viewvc.cgi/vhostlog/trunk/
Makefile
CC = gcc DEBUG = -Wall -g all: vhostlog vhostlog.o: vhostlog.c $(CC) ${DEBUG} -O -c vhostlog.c vhostlog: vhostlog.o $(CC) ${DEBUG} -o vhostlog vhostlog.o clean: rm -f vhostlog *.o core *.core
vhostlog.c
/* vhostlog.c This program is supposed to run from apache via a pipe in a CustomLog statement, or as a daemon under daemontools/ inittab/etc in fifo mode. Configure it as follows: LogFormat "%v %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" " vhostlog #Pipe Version CustomLog "|/usr/local/bin/vhostlog www /var/log/apache" vhostlog #Fifo Version CustomLog "/var/path/to/vhostlog/fifo" vhostlog The first argument is the user to switch to (www), the second is the directory in which logfiles are stored ( /var/log/apache ). The owner of the log directory *has* to be the same as the log user. Written by Johan Mulder <johan@localhost.nl> at november 28th 2001. Modified by Sten Spans <sten@blinkenlights.nl> at $Date$. */ const char cvsid[] = "$Id$"; #include <string.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <sys/param.h> #include <sys/types.h> #include <sys/stat.h> #include <errno.h> #include <pwd.h> #include <grp.h> #define UMASK S_IRWXG | S_IRWXO #define NOLOGGING "/var/service/vhostlog/root/down" #define MAXLINESIZE 16384 #define VHOSTCHARS "abcdefghijklmnopqrstuvwxyz0123456789.-_" /* #define DEBUG */ char hostname[MAXHOSTNAMELEN+1]; static int logline(char *, const char *, unsigned int *); int main(int argc, char **argv) { char line[MAXLINESIZE]; char *logdir; char *user; char *fifo_path = NULL; struct passwd *pwd; unsigned int failed = 0; unsigned int longline = 0; struct stat s; FILE *input = stdin; /* Get the config */ #ifdef DEBUG fprintf(stderr, "Getting the config\n"); #endif if (argc < 3 || argc > 4) { fprintf(stderr, "Usage %s user logdir [fifo]\n", argv[0]); return 1; } user = argv[1]; logdir = argv[2]; if(argc == 4) fifo_path = argv[3]; /* Check if the user to setuid to actually exists */ #ifdef DEBUG fprintf(stderr, "Checking if the user exists\n"); #endif if ((pwd = getpwnam(user)) == NULL) { fprintf(stderr, "User %s does not exist\n", user); return 1; } /* Check if the logdir exists */ #ifdef DEBUG fprintf(stderr, "Checking if the logdir exists\n"); #endif if (stat(logdir, &s) == -1 || chdir(logdir) == -1) { fprintf(stderr, "Unable to change dir to %s: %s\n", logdir, strerror(errno)); return 1; } /* Check the ownership of that directory */ #ifdef DEBUG fprintf(stderr, "Checking logdir ownership\n"); #endif if (s.st_uid != pwd->pw_uid) { fprintf(stderr, "Bad owner on directory %s (should be owner %s).\n", logdir, user); return 1; } /* Change the uid */ #ifdef DEBUG fprintf(stderr, "Changing uid/gid\n"); #endif if(setgid(pwd->pw_gid) == -1){ fprintf(stderr, "Unable to setgid: %s\n", strerror(errno)); return 1; } if(setgroups(0, NULL) == -1){ fprintf(stderr, "Unable to setgroups: %s\n", strerror(errno)); return 1; } if(setuid(pwd->pw_uid) == -1){ fprintf(stderr, "Unable to setuid: %s\n", strerror(errno)); return 1; } /* Get hostname */ if(gethostname(hostname, sizeof(hostname)) == -1){ fprintf(stderr, "Unable to get hostname: %s\n", strerror(errno)); return 1; } /* Change umask */ umask(UMASK); /* Check/Create Fifo */ if(fifo_path != NULL) { #ifdef DEBUG fprintf(stderr, "Checking/Creating the fifo: %s\n", fifo_path); #endif if (stat(fifo_path, &s) == -1) { if(errno != ENOENT) { fprintf(stderr, "Unable to stat fifo %s: %s\n", fifo_path, strerror(errno)); return 1; } else if (mkfifo(fifo_path, S_IRWXU) == -1) { fprintf(stderr, "Unable to create fifo %s: %s\n", fifo_path, strerror(errno)); return 1; } } else if (!S_ISFIFO(s.st_mode)) { fprintf(stderr, "Expected fifo at %s, got something else\n", fifo_path); return 1; } if ((input = fopen(fifo_path, "r+")) == NULL) { fprintf(stderr, "Failed opening fifo %s: %s\n", fifo_path, strerror(errno)); return 1; } } /* Process the logging */ #ifdef DEBUG fprintf(stderr, "Process the logging\n"); #endif while (feof(input) == 0) { if ((fgets(line, MAXLINESIZE, input)) != NULL) if (access(NOLOGGING, F_OK) == -1) if (logline(line, logdir, &longline) == 1) fprintf(stderr, " %u logging failures\n", ++failed); } return 0; } /* This function writes a logline in clf to a logfile */ int logline(char *line, const char *logdir, unsigned int *longline) { char *outline, *vhost; char logfile[PATH_MAX+1]; FILE *outfile; outline = vhost = NULL; /* initial checks */ #ifdef DEBUG fprintf(stderr, "Initial Checks\n"); #endif if (!line || (strlen(line) == 0)) { fprintf(stderr, "Incorrect logline: too short,"); return 1; } else if((strchr(line, '\n') == NULL) && *longline == 0){ fprintf(stderr, "Incorrect logline: too long,"); *longline = 1; return 1; } else if((strchr(line, '\n') == NULL) && *longline == 1){ return 0; } else if((strchr(line, '\n') != NULL) && *longline == 1){ *longline = 0; return 0; }; if(!logdir || (strlen(logdir) == 0)) { fprintf(stderr, "Incorrect logdir: too short,"); return 1; } /* Determine the vhost and logfile location */ #ifdef DEBUG fprintf(stderr, "Getting vhost from logline\n"); #endif vhost = line; outline = index(line, ' '); if(outline == NULL) { fprintf(stderr, "Incorrect logline: no vhost,"); return 1; } *outline = '\0'; outline++; /* If the vhost is ".." or "." then reject it */ if (strcmp(vhost, "..") == 0 || strcmp(vhost, ".") == 0) { fprintf(stderr,"Incorrect vhost: invalid chars,"); return 1; } /* If the "vhost" part contains invalid chars then log the entire line to "hostname" */ if (strspn(vhost, VHOSTCHARS) != strlen(vhost)) { outline[-1] = ' '; outline = line; vhost = hostname; } if (strlen(vhost) < 1 || strlen(outline) < 1) { fprintf(stderr,"Incorrect vhost/line: too short,"); return 1; } #ifdef DEBUG fprintf(stderr, "Got vhost: %s\n", vhost); #endif if ((strlen(logdir) + strlen(vhost)) > PATH_MAX) { fprintf(stderr, "Incorrect logdir/vhost: too long,"); return 1; } if (snprintf(logfile, PATH_MAX+1, "%s/%s", logdir, vhost) < 0) { fprintf(stderr, "Error building logfile string,"); return 1; } #ifdef DEBUG fprintf(stderr, "Got logfile: %s\n", logfile); #endif /* Open logfile, and write the output */ #ifdef DEBUG fprintf(stderr, "Writing output to logfile\n"); #endif if ((outfile = fopen(logfile, "a")) == NULL) { fprintf(stderr, "Failed opening logfile %s: %s,", logfile, strerror(errno)); return 1; } if (fputs(outline, outfile) < 0) { fprintf(stderr, "Failed writing to logfile %s,", logfile); if (fclose(outfile) == EOF) { fprintf(stderr, "Failed closing logfile %s: %s,", logfile, strerror(errno)); return 1; }; }; if (fclose(outfile) == EOF) { fprintf(stderr, "Failed closing logfile %s: %s,", logfile, strerror(errno)); return 1; }; return 0; }