Hello!

I have a setup where logfiles etc. are periodically transferred
(rsynced) over a network-link at runtime, and disk space as well as
transfer time/bandwidth are relevant.

I didn't find a gzipping filter for APR, and didn't want to implement
yet another specialized gzip-implementation like mod_deflate for it, so
I took rotatelogs and converted it to use zlib, stripping some unneeded
and (in this setup) hard-to-support functionality as well.

It might be useful to someone else, so please find it attached for
inclusion. I hereby place it under the Apache License 2.0 and transfer
all relevant rights to the Apache Software Foundation.

Best regards,

   Andreas

-- 
"God is a comedian playing to an audience too afraid to laugh." -- H.L.Mencken
diff -Nur rotatelogz.orig/Makefile rotatelogz/Makefile
--- rotatelogz.orig/Makefile    1970-01-01 01:00:00.000000000 +0100
+++ rotatelogz/Makefile 2007-04-16 15:41:55.000000000 +0200
@@ -0,0 +1,10 @@
+CC ?= gcc
+LDFLAGS += -lz
+
+all: rotatelogz
+
+install: rotatelogz
+       install -m 755 rotatelogz $(DESTDIR)${prefix}/bin/rotatelogz
+
+clean:
+       rm rotatelogz *~
diff -Nur rotatelogz.orig/rotatelogz.c rotatelogz/rotatelogz.c
--- rotatelogz.orig/rotatelogz.c        1970-01-01 01:00:00.000000000 +0100
+++ rotatelogz/rotatelogz.c     2007-04-16 14:44:00.000000000 +0200
@@ -0,0 +1,246 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Simple program to rotate Apache logs without having to kill the server.
+ *
+ * Contributed by Ben Laurie <ben algroup.co.uk>
+ * Ported to APR by Mladen Turk <mturk mappingsoft.com>
+ *
+ * 2007-04-16
+ *
+ * Heavily modified by Andreas Kotes <count flatline.de>
+ * - Removed APR again
+ * - Removed localtime-support
+ * - Removed filesize-support
+ * - Removed strftime-support for logfile names
+ * - Changed file handling to always use zlib with maximum compression
+ * - Added signal handler so files are closed on forced exit
+ * - Dropped file truncation on errors
+ * - Copy previous file if file exists on startup
+ *   (this has the penalty to block shortly on startup!)
+ * - Tested on Linux only
+ *
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <zlib.h>
+
+#define BUFSIZE         65536
+#define ERRMSGSZ        128
+
+#ifndef MAX_PATH
+#define MAX_PATH        1024
+#endif
+
+gzFile nLogFD = NULL;
+void termination_handler (int signum) {
+  gzclose(nLogFD);
+}
+
+int main (int argc, const char * const argv[])
+{
+    char buf[BUFSIZE], filename[MAX_PATH], oldfilename[MAX_PATH], 
errbuf[ERRMSGSZ];
+    int tLogEnd = 0, tRotation = 0;
+    int nMessCount = 0;
+    size_t nRead, nWrite;
+    int use_strftime = 0;
+    int now = 0;
+    const char *szLogRoot;
+    int f_stdin;
+    gzFile nLogFDprev = NULL;
+    gzFile nLogFDold = NULL;
+    char *ptr = NULL;
+    int argBase = 0;
+    int argFile = 1;
+    int argIntv = 2;
+    int argOffset = 3;
+    struct sigaction new_action, old_action;
+    struct stat statbuf;
+
+    if (argc < (argBase + 3) || argc > (argBase + 4)) {
+        fprintf(stderr,
+                "Usage: %s <logfile> <rotation time in seconds> "
+                "[offset minutes from UTC]\n\n",
+                argv[0]);
+        fprintf(stderr,
+                "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n",
+                argv[0]);
+        fprintf(stderr,
+                "to httpd.conf. The generated name will be /some/where.nnnn "
+                "where nnnn is the\nsystem time at which the log nominally "
+                "starts (N.B. if using a rotation time,\nthe time will always "
+                "be a multiple of the rotation time, so you can synchronize\n"
+                "cron scripts with it). At the end of each rotation time or "
+                "when the file size\nis reached a new log is started.\n");
+        exit(1);
+    }
+
+    szLogRoot = argv[argFile];
+
+    tRotation = atoi(argv[argIntv]);
+    if (tRotation <= 0) {
+        fprintf(stderr, "Rotation time must be > 0\n");
+        exit(6);
+    }
+
+    use_strftime = (strchr(szLogRoot, '%') != NULL);
+    f_stdin = fileno(stdin);
+    if (f_stdin < 0) {
+        perror("Unable to work with stdin");
+        exit(1);
+    }
+
+    /* Close logfile correctly when being killed */
+    new_action.sa_handler = termination_handler;
+    sigemptyset (&new_action.sa_mask);
+    new_action.sa_flags = 0;
+    sigaction (SIGINT, NULL, &old_action);
+    if (old_action.sa_handler != SIG_IGN)
+        sigaction (SIGINT, &new_action, NULL);
+    sigaction (SIGHUP, NULL, &old_action);
+    if (old_action.sa_handler != SIG_IGN)
+        sigaction (SIGHUP, &new_action, NULL);
+    sigaction (SIGTERM, NULL, &old_action);
+    if (old_action.sa_handler != SIG_IGN)
+        sigaction (SIGTERM, &new_action, NULL);
+
+    for (;;) {
+        nRead = sizeof(buf);
+        nRead = read(f_stdin, buf, nRead);
+        if (nRead == -1) {
+            exit(3);
+        }
+        if (tRotation) {
+            now = (int)time(NULL);
+            if (nLogFD != NULL && now >= tLogEnd) {
+                nLogFDprev = nLogFD;
+                nLogFD = NULL;
+            }
+        }
+        else {
+            fprintf(stderr, "No rotation time specified\n");
+            exit(2);
+        }
+
+        if (nLogFD == NULL) {
+            int tLogStart;
+            if (tRotation) {
+                tLogStart = (now / tRotation) * tRotation;
+            }
+            else {
+                tLogStart = (int)time(NULL);
+            }
+
+            sprintf(filename, "%s.%010d.gz", szLogRoot, tLogStart);
+            tLogEnd = tLogStart + tRotation;
+
+            /* test for file existence, and copy the contents, if necessary */
+            if ((stat(filename,&statbuf) == 0) && statbuf.st_size) {
+                sprintf(oldfilename, "%s.%010d.gz.old", szLogRoot, tLogStart);
+                if (rename(filename,oldfilename) == -1) {
+                  /* silently ignore problems */
+                  nLogFD = gzopen(filename,"wb9");
+                } else {
+                  nLogFD = gzopen(filename,"wb9");
+                  if (nLogFD == NULL) {
+                    char error[120];
+                    strerror_r(errno, error, sizeof error);
+                    fprintf(stderr, "Could not open new log file '%s' (%s) 
during copy.\n", filename, error);
+                    exit(2);
+                  }
+                  nLogFDold = gzopen(oldfilename,"rb");
+                  if (nLogFDold == NULL) {
+                    char error[120];
+                    strerror_r(errno, error, sizeof error);
+                    fprintf(stderr, "Could not open old log file '%s' (%s) 
during copy.\n", oldfilename, error);
+                    /* continue with regular logging in this case */
+                  } else {
+                    char copybuf[BUFSIZE];
+                    int bytes;
+                    while (!gzeof(nLogFDold)) {
+                      bytes = gzread(nLogFDold,copybuf,BUFSIZE);
+                      if (bytes)
+                        if (gzwrite(nLogFD,copybuf,bytes) != bytes)
+                          exit(2);
+                    }
+                    gzclose(nLogFDold);
+                    nLogFDold=NULL;
+                    unlink(oldfilename);
+                  }
+                }
+            } else {
+              nLogFD = gzopen(filename,"wb9");
+            }
+            if (nLogFD == NULL) {
+                char error[120];
+
+                strerror_r(errno, error, sizeof error);
+
+                /* Uh-oh. Failed to open the new log file. Try to clear
+                 * the previous log file, note the lost log entries,
+                 * and keep on truckin'. */
+                if (nLogFDprev == NULL) {
+                    fprintf(stderr, "Could not open log file '%s' (%s)\n", 
filename, error);
+                    exit(2);
+                }
+                else {
+                    nLogFD = nLogFDprev;
+                    /* Try to keep this error message constant length
+                     * in case it occurs several times. */
+                    snprintf(errbuf, sizeof errbuf,
+                                 "Continuing log file due to error opening "
+                                 "new log file, %10d messages lost: 
%-25.25s\n",
+                                 nMessCount, error);
+                    nWrite = strlen(errbuf);
+                    if (gzwrite(nLogFD, errbuf, nWrite) != nWrite) {
+                        fprintf(stderr, "Error writing to the file %s\n", 
filename);
+                        exit(2);
+                    }
+                }
+            }
+            else if (nLogFDprev) {
+                gzclose(nLogFDprev);
+            }
+            nMessCount = 0;
+        }
+        nWrite = nRead;
+        if (gzwrite(nLogFD, buf, nWrite) != nRead) {
+            nMessCount++;
+            sprintf(errbuf,
+                    "Error writing to log file. "
+                    "%10d messages lost.\n",
+                    nMessCount);
+            nWrite = strlen(errbuf);
+            if (gzwrite(nLogFD, errbuf, nWrite) != nWrite) {
+                fprintf(stderr, "Error writing to the file %s\n", filename);
+                exit(2);
+            }
+        }
+        else {
+            nMessCount++;
+        }
+    }
+    /* Of course we never, but prevent compiler warnings */
+    return 0;
+}

Attachment: signature.asc
Description: Digital signature

Reply via email to