]> granicus.if.org Git - apache/blobdiff - support/htcacheclean.c
Make sure the pidfile is deleted on close.
[apache] / support / htcacheclean.c
index 6b4a0b8ca61a0d25f20409d5acf9ec1a0720444d..43d5117dd2f021916e592b32c50fe0e9d468f428 100644 (file)
@@ -1,8 +1,9 @@
-/* Copyright 2001-2004 The Apache Software Foundation
- *
- * Licensed 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
+/* 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
  *
@@ -17,7 +18,7 @@
  * htcacheclean.c: simple program for cleaning of
  * the disk cache of the Apache HTTP server
  *
- * Contributed by Andreas Steinmetz <ast@domdv.de>
+ * Contributed by Andreas Steinmetz <ast domdv.de>
  * 8 Oct 2004
  */
 
@@ -33,6 +34,8 @@
 #include "apr_getopt.h"
 #include "apr_ring.h"
 #include "apr_date.h"
+#include "apr_buckets.h"
+#include "../modules/cache/mod_disk_cache.h"
 
 #if APR_HAVE_UNISTD_H
 #include <unistd.h>
 #include <stdlib.h>
 #endif
 
-/* mod_disk_cache.c extract start */
-
-#define DISK_FORMAT_VERSION 0
-typedef struct {
-    /* Indicates the format of the header struct stored on-disk. */
-    int format;
-    /* The HTTP status code returned for this response.  */
-    int status;
-    /* The size of the entity name that follows. */
-    apr_size_t name_len;
-    /* The number of times we've cached this entity. */
-    apr_size_t entity_version;
-    /* Miscellaneous time values. */
-    apr_time_t date;
-    apr_time_t expire;
-    apr_time_t request_time;
-    apr_time_t response_time;
-} disk_cache_info_t;
-
-#define CACHE_HEADER_SUFFIX ".header"
-#define CACHE_DATA_SUFFIX   ".data"
-/* mod_disk_cache.c extract end */
-
-/* mod_disk_cache.c related definitions start */
-
-/*
- * this is based on #define AP_TEMPFILE "/aptmpXXXXXX"
- *
- * the above definition could be reworked into the following:
- *
- * #define AP_TEMPFILE_PREFIX "/"
- * #define AP_TEMPFILE_BASE   "aptmp"
- * #define AP_TEMPFILE_SUFFIX "XXXXXX"
- * #define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
- * #define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
- * #define AP_TEMPFILE AP_TEMPFILE_PREFIX AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX
- *
- * these definitions would then match the definitions below:
- */
-
-#define AP_TEMPFILE_BASE    "aptmp"
-#define AP_TEMPFILE_SUFFIX  "XXXXXX"
-#define AP_TEMPFILE_BASELEN strlen(AP_TEMPFILE_BASE)
-#define AP_TEMPFILE_NAMELEN strlen(AP_TEMPFILE_BASE AP_TEMPFILE_SUFFIX)
-
-/* mod_disk_cache.c related definitions end */
-
 /* define the following for debugging */
 #undef DEBUG
 
@@ -108,6 +64,7 @@ typedef struct {
 #define SECS_PER_MIN  60
 #define KBYTE         1024
 #define MBYTE         1048576
+#define GBYTE         1073741824
 
 #define DIRINFO (APR_FINFO_MTIME|APR_FINFO_SIZE|APR_FINFO_TYPE|APR_FINFO_LINK)
 
@@ -140,6 +97,7 @@ static int verbose;     /* flag: true means print statistics */
 static int benice;      /* flag: true means nice mode is activated */
 static int dryrun;      /* flag: true means dry run, don't actually delete
                                  anything */
+static int deldirs;     /* flag: true means directories should be deleted */
 static int baselen;     /* string length of the path to the proxy directory */
 static apr_time_t now;  /* start time of this processing run */
 
@@ -148,6 +106,8 @@ static apr_off_t unsolicited; /* file size summary for deleted unsolicited
                                  files */
 static APR_RING_ENTRY(_entry) root; /* ENTRY ring anchor */
 
+/* short program name as called */
+static const char *shortname = "htcacheclean";
 
 #ifdef DEBUG
 /*
@@ -161,7 +121,7 @@ static void fake_file_remove(char *pathname, apr_pool_t *p)
     /* stat and printing to simulate some deletion system load and to
        display what would actually have happened */
     apr_stat(&info, pathname, DIRINFO, p);
-    apr_file_printf(errfile, "would delete %s\n", pathname);
+    apr_file_printf(errfile, "would delete %s" APR_EOL_STR, pathname);
 }
 #endif
 
@@ -171,7 +131,7 @@ static void fake_file_remove(char *pathname, apr_pool_t *p)
 static void setterm(int unused)
 {
 #ifdef DEBUG
-    apr_file_printf(errfile, "interrupt\n");
+    apr_file_printf(errfile, "interrupt" APR_EOL_STR);
 #endif
     interrupted = 1;
 }
@@ -229,7 +189,7 @@ static void printstats(apr_off_t total, apr_off_t sum, apr_off_t max,
         max /= KBYTE;
     }
 
-    apr_file_printf(errfile, "Statistics:\n");
+    apr_file_printf(errfile, "Statistics:" APR_EOL_STR);
     if (unsolicited) {
         utype = 'K';
         ufrag = ((unsolicited * 10) / KBYTE) % 10;
@@ -242,16 +202,17 @@ static void printstats(apr_off_t total, apr_off_t sum, apr_off_t max,
         if (!unsolicited && !ufrag) {
             ufrag = 1;
         }
-        apr_file_printf(errfile, "unsolicited size %d.%d%c\n",
+        apr_file_printf(errfile, "unsolicited size %d.%d%c" APR_EOL_STR,
                         (int)(unsolicited), (int)(ufrag), utype);
      }
-     apr_file_printf(errfile, "size limit %d.0%c\n", (int)(max), mtype);
+     apr_file_printf(errfile, "size limit %d.0%c" APR_EOL_STR,
+                     (int)(max), mtype);
      apr_file_printf(errfile, "total size was %d.%d%c, total size now "
-                              "%d.%d%c\n",
+                              "%d.%d%c" APR_EOL_STR,
                      (int)(total), (int)(tfrag), ttype, (int)(sum),
                      (int)(sfrag), stype);
-     apr_file_printf(errfile, "total entries was %d, total entries now %d\n",
-                     (int)(etotal), (int)(entries));
+     apr_file_printf(errfile, "total entries was %d, total entries now %d"
+                              APR_EOL_STR, (int)(etotal), (int)(entries));
 }
 
 /*
@@ -326,7 +287,7 @@ static int process_dir(char *path, apr_pool_t *pool)
     apr_finfo_t info;
     apr_size_t len;
     apr_time_t current, deviation;
-    char *nextpath, *base, *ext;
+    char *nextpath, *base, *ext, *orig_basename;
     APR_RING_ENTRY(_direntry) anchor;
     DIRENTRY *d, *t, *n;
     ENTRY *e;
@@ -345,9 +306,7 @@ static int process_dir(char *path, apr_pool_t *pool)
     }
 
     while (apr_dir_read(&info, 0, dir) == APR_SUCCESS && !interrupted) {
-        /* skip first two entries which will always be '.' and '..' */
-        if (skip < 2) {
-            skip++;
+        if (!strcmp(info.name, ".") || !strcmp(info.name, "..")) {
             continue;
         }
         d = apr_pcalloc(p, sizeof(DIRENTRY));
@@ -383,7 +342,7 @@ static int process_dir(char *path, apr_pool_t *pool)
             }
         }
 
-        /* this may look strange but apr_stat() may return errno which
+        /* this may look strange but apr_stat() may return an error which
          * is system dependent and there may be transient failures,
          * so just blindly retry for a short while
          */
@@ -409,9 +368,18 @@ static int process_dir(char *path, apr_pool_t *pool)
         }
 
         if (info.filetype == APR_DIR) {
+            /* Make a copy of the basename, as process_dir modifies it */
+            orig_basename = apr_pstrdup(pool, d->basename);
             if (process_dir(d->basename, pool)) {
                 return 1;
             }
+
+            /* If asked to delete dirs, do so now. We don't care if it fails.
+             * If it fails, it likely means there was something else there.
+             */
+            if (deldirs && !dryrun) {
+                apr_dir_remove(orig_basename, pool);
+            }
             continue;
         }
 
@@ -473,35 +441,61 @@ static int process_dir(char *path, apr_pool_t *pool)
     path[baselen] = '\0';
 
     for (i = apr_hash_first(p, h); i && !interrupted; i = apr_hash_next(i)) {
-         apr_hash_this(i, NULL, NULL, (void **)(&d));
+        void *hvalue;
+        apr_uint32_t format;
+
+        apr_hash_this(i, NULL, NULL, &hvalue);
+        d = hvalue;
+
         switch(d->type) {
         case HEADERDATA:
             nextpath = apr_pstrcat(p, path, "/", d->basename,
                                    CACHE_HEADER_SUFFIX, NULL);
-            if (apr_file_open(&fd, nextpath, APR_READ, APR_OS_DEFAULT,
-                              p) == APR_SUCCESS) {
-                len = sizeof(disk_cache_info_t);
-                if (apr_file_read_full(fd, &disk_info, len,
+            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
+                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
+                len = sizeof(format);
+                if (apr_file_read_full(fd, &format, len,
                                        &len) == APR_SUCCESS) {
-                    apr_file_close(fd);
-                    if (disk_info.format == DISK_FORMAT_VERSION) {
-                        e = apr_palloc(pool, sizeof(ENTRY));
-                        APR_RING_INSERT_TAIL(&root, e, _entry, link);
-                        e->expire = disk_info.expire;
-                        e->response_time = disk_info.response_time;
-                        e->htime = d->htime;
-                        e->dtime = d->dtime;
-                        e->hsize = d->hsize;
-                        e->dsize = d->dsize;
-                        e->basename = apr_palloc(pool,
-                                                 strlen(d->basename) + 1);
-                        strcpy(e->basename, d->basename);
+                    if (format == DISK_FORMAT_VERSION) {
+                        apr_off_t offset = 0;
+
+                        apr_file_seek(fd, APR_SET, &offset);
+
+                        len = sizeof(disk_cache_info_t);
+
+                        if (apr_file_read_full(fd, &disk_info, len,
+                                               &len) == APR_SUCCESS) {
+                            apr_file_close(fd);
+                            e = apr_palloc(pool, sizeof(ENTRY));
+                            APR_RING_INSERT_TAIL(&root, e, _entry, link);
+                            e->expire = disk_info.expire;
+                            e->response_time = disk_info.response_time;
+                            e->htime = d->htime;
+                            e->dtime = d->dtime;
+                            e->hsize = d->hsize;
+                            e->dsize = d->dsize;
+                            e->basename = apr_pstrdup(pool, d->basename);
+                            break;
+                        }
+                        else {
+                            apr_file_close(fd);
+                        }
+                    }
+                    else if (format == VARY_FORMAT_VERSION) {
+                        /* This must be a URL that added Vary headers later,
+                         * so kill the orphaned .data file
+                         */
+                        apr_file_close(fd);
+                        apr_file_remove(apr_pstrcat(p, path, "/", d->basename,
+                                                    CACHE_DATA_SUFFIX, NULL),
+                                        p);
                         break;
                     }
                 }
                 else {
                     apr_file_close(fd);
                 }
+
             }
             /* we have a somehow unreadable headers file which is associated
              * with a data file. this may be caused by apache currently
@@ -527,6 +521,33 @@ static int process_dir(char *path, apr_pool_t *pool)
          */
         case HEADER:
             current = apr_time_now();
+            nextpath = apr_pstrcat(p, path, "/", d->basename,
+                                   CACHE_HEADER_SUFFIX, NULL);
+            if (apr_file_open(&fd, nextpath, APR_FOPEN_READ | APR_FOPEN_BINARY,
+                              APR_OS_DEFAULT, p) == APR_SUCCESS) {
+                len = sizeof(format);
+                if (apr_file_read_full(fd, &format, len,
+                                       &len) == APR_SUCCESS) {
+                    if (format == VARY_FORMAT_VERSION) {
+                        apr_time_t expires;
+
+                        len = sizeof(expires);
+
+                        if (apr_file_read_full(fd, &expires, len,
+                                               &len) == APR_SUCCESS) {
+
+                            apr_file_close(fd);
+
+                            if (expires < current) {
+                                delete_entry(path, d->basename, p);
+                            }
+                            break;
+                        }
+                    }
+                }
+                apr_file_close(fd);
+            }
+
             if (realclean || d->htime < current - deviation
                 || d->htime > current + deviation) {
                 delete_entry(path, d->basename, p);
@@ -679,46 +700,63 @@ static void purge(char *path, apr_pool_t *pool, apr_off_t max)
 /*
  * usage info
  */
-static void usage(void)
+#define NL APR_EOL_STR
+static void usage(const char *error)
 {
-    apr_file_printf(errfile, "htcacheclean -- program for cleaning the "
-                             "disk cache.\n");
-    apr_file_printf(errfile, "Usage: htcacheclean [-Dvrn] -pPATH -lLIMIT\n");
-    apr_file_printf(errfile, "       htcacheclean [-Dvrn] -pPATH -LLIMIT\n");
-    apr_file_printf(errfile, "       htcacheclean [-ni] -dINTERVAL -pPATH "
-                             "-lLIMIT\n");
-    apr_file_printf(errfile, "       htcacheclean [-ni] -dINTERVAL -pPATH "
-                             "-LLIMIT\n");
-    apr_file_printf(errfile, "Options:\n");
-    apr_file_printf(errfile, "   -d   Daemonize and repeat cache cleaning "
-                             "every INTERVAL minutes. This\n"
-                             "        option is mutually exclusive with "
-                             "the -D, -v and -r options.\n");
-    apr_file_printf(errfile, "   -D   Do a dry run and don't delete anything. "
-                             "This option is mutually\n"
-                             "        exclusive with the -d option.\n");
-    apr_file_printf(errfile, "   -v   Be verbose and print statistics. "
-                             "This option is mutually exclusive\n"
-                             "        with the -d option.\n");
-    apr_file_printf(errfile, "   -r   Clean thoroughly. This assumes that "
-                             "the Apache web server\n"
-                             "        is not running. This option is "
-                             "mutually exclusive with the -d option.\n");
-    apr_file_printf(errfile, "   -n   Be nice. This causes slower processing "
-                             "in favour of other processes.\n");
-    apr_file_printf(errfile, "   -p   Specify PATH as the root directory of "
-                             "the disk cache.\n");
-    apr_file_printf(errfile, "   -l   Specify LIMIT as the total disk cache "
-                             "size limit in KBytes.\n");
-    apr_file_printf(errfile, "   -L   Specify LIMIT as the total disk cache "
-                             "size limit in MBytes.\n");
-    apr_file_printf(errfile, "   -i   Be intelligent and run only when there "
-                             "was a modification\n"
-                             "        of the disk cache. This option is only "
-                             "possible together with\n"
-                             "        the -d option.\n");
+    if (error) {
+        apr_file_printf(errfile, "%s error: %s\n", shortname, error);
+    }
+    apr_file_printf(errfile,
+    "%s -- program for cleaning the disk cache."                             NL
+    "Usage: %s [-Dvtrn] -pPATH -lLIMIT [-PPIDFILE]"                          NL
+    "       %s [-nti] -dINTERVAL -pPATH -lLIMIT [-PPIDFILE]"                 NL
+                                                                             NL
+    "Options:"                                                               NL
+    "  -d   Daemonize and repeat cache cleaning every INTERVAL minutes."     NL
+    "       This option is mutually exclusive with the -D, -v and -r"        NL
+    "       options."                                                        NL
+                                                                             NL
+    "  -D   Do a dry run and don't delete anything. This option is mutually" NL
+    "       exclusive with the -d option."                                   NL
+                                                                             NL
+    "  -v   Be verbose and print statistics. This option is mutually"        NL
+    "       exclusive with the -d option."                                   NL
+                                                                             NL
+    "  -r   Clean thoroughly. This assumes that the Apache web server is "   NL
+    "       not running. This option is mutually exclusive with the -d"      NL
+    "       option and implies -t."                                          NL
+                                                                             NL
+    "  -n   Be nice. This causes slower processing in favour of other"       NL
+    "       processes."                                                      NL
+                                                                             NL
+    "  -t   Delete all empty directories. By default only cache files are"   NL
+    "       removed, however with some configurations the large number of"   NL
+    "       directories created may require attention."                      NL
+                                                                             NL
+    "  -p   Specify PATH as the root directory of the disk cache."           NL
+                                                                             NL
+    "  -P   Specify PIDFILE as the file to write the pid to."                NL
+                                                                             NL
+    "  -l   Specify LIMIT as the total disk cache size limit. Attach 'K'"    NL
+    "       or 'M' to the number for specifying KBytes or MBytes."           NL
+                                                                             NL
+    "  -i   Be intelligent and run only when there was a modification of"    NL
+    "       the disk cache. This option is only possible together with the"  NL
+    "       -d option."                                                      NL,
+    shortname,
+    shortname,
+    shortname
+    );
+
     exit(1);
 }
+#undef NL
+
+static void usage_repeated_arg(apr_pool_t *pool, char option) {
+    usage(apr_psprintf(pool, 
+                       "The option '%c' cannot be specified more than once",
+                       option));
+}
 
 /*
  * main
@@ -734,7 +772,8 @@ int main(int argc, const char * const argv[])
     int retries, isdaemon, limit_found, intelligent, dowork;
     char opt;
     const char *arg;
-    char *proxypath, *path;
+    char *proxypath, *path, *pidfile;
+    char errmsg[1024];
 
     interrupted = 0;
     repeat = 0;
@@ -745,15 +784,21 @@ int main(int argc, const char * const argv[])
     verbose = 0;
     realclean = 0;
     benice = 0;
+    deldirs = 0;
     intelligent = 0;
     previous = 0; /* avoid compiler warning */
     proxypath = NULL;
+    pidfile = NULL;
 
     if (apr_app_initialize(&argc, &argv, NULL) != APR_SUCCESS) {
         return 1;
     }
     atexit(apr_terminate);
 
+    if (argc) {
+        shortname = apr_filepath_name_get(argv[0]);
+    }
+
     if (apr_pool_create(&pool, NULL) != APR_SUCCESS) {
         return 1;
     }
@@ -765,53 +810,61 @@ int main(int argc, const char * const argv[])
     apr_getopt_init(&o, pool, argc, argv);
 
     while (1) {
-        status = apr_getopt(o, "iDnvrd:l:L:p:", &opt, &arg);
+        status = apr_getopt(o, "iDnvrtd:l:L:p:P:", &opt, &arg);
         if (status == APR_EOF) {
             break;
         }
         else if (status != APR_SUCCESS) {
-            usage();
+            usage(NULL);
         }
         else {
             switch (opt) {
             case 'i':
                 if (intelligent) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 intelligent = 1;
                 break;
 
             case 'D':
                 if (dryrun) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 dryrun = 1;
                 break;
 
             case 'n':
                 if (benice) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 benice = 1;
                 break;
 
+            case 't':
+                if (deldirs) {
+                    usage_repeated_arg(pool, opt);
+                }
+                deldirs = 1;
+                break;
+
             case 'v':
                 if (verbose) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 verbose = 1;
                 break;
 
             case 'r':
                 if (realclean) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 realclean = 1;
+                deldirs = 1;
                 break;
 
             case 'd':
                 if (isdaemon) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 isdaemon = 1;
                 repeat = apr_atoi64(arg);
@@ -821,53 +874,90 @@ int main(int argc, const char * const argv[])
 
             case 'l':
                 if (limit_found) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 limit_found = 1;
-                max = apr_atoi64(arg);
-                max *= KBYTE;
-                break;
 
-            case 'L':
-                if (limit_found) {
-                    usage();
-                }
-                limit_found = 1;
-                max = apr_atoi64(arg);
-                max *= MBYTE;
+                do {
+                    apr_status_t rv;
+                    char *end;
+
+                    rv = apr_strtoff(&max, arg, &end, 10);
+                    if (rv == APR_SUCCESS) {
+                        if ((*end == 'K' || *end == 'k') && !end[1]) {
+                            max *= KBYTE;
+                        }
+                        else if ((*end == 'M' || *end == 'm') && !end[1]) {
+                            max *= MBYTE;
+                        }
+                        else if ((*end == 'G' || *end == 'g') && !end[1]) {
+                            max *= GBYTE;
+                        }
+                        else if (*end &&        /* neither empty nor [Bb] */
+                                 ((*end != 'B' && *end != 'b') || end[1])) {
+                            rv = APR_EGENERAL;
+                        }
+                    }
+                    if (rv != APR_SUCCESS) {
+                        usage(apr_psprintf(pool, "Invalid limit: %s"
+                                                 APR_EOL_STR APR_EOL_STR, arg));
+                    }
+                } while(0);
                 break;
 
             case 'p':
                 if (proxypath) {
-                    usage();
+                    usage_repeated_arg(pool, opt);
                 }
                 proxypath = apr_pstrdup(pool, arg);
-                if (apr_filepath_set(proxypath, pool) != APR_SUCCESS) {
-                    usage();
+                if ((status = apr_filepath_set(proxypath, pool)) != APR_SUCCESS) {
+                    usage(apr_psprintf(pool, "Could not set filepath to '%s': %s",
+                                       proxypath, apr_strerror(status, errmsg, sizeof errmsg)));
                 }
                 break;
+
+            case 'P':
+                if (pidfile) {
+                    usage_repeated_arg(pool, opt);
+                }
+                pidfile = apr_pstrdup(pool, arg);
+                break;
+
             } /* switch */
         } /* else */
     } /* while */
 
+    if (argc <= 1) {
+        usage(NULL);
+    }
+
     if (o->ind != argc) {
-         usage();
+         usage("Additional parameters specified on the command line, aborting");
+    }
+
+    if (isdaemon && repeat <= 0) {
+         usage("Option -d must be greater than zero");
     }
 
-    if (isdaemon && (repeat <= 0 || verbose || realclean || dryrun)) {
-         usage();
+    if (isdaemon && (verbose || realclean || dryrun)) {
+         usage("Option -d cannot be used with -v, -r or -D");
     }
 
     if (!isdaemon && intelligent) {
-         usage();
+         usage("Option -i cannot be used without -d");
+    }
+
+    if (!proxypath) {
+         usage("Option -p must be specified");
     }
 
-    if (!proxypath || max <= 0) {
-         usage();
+    if (max <= 0) {
+         usage("Option -l must be greater than zero");
     }
 
     if (apr_filepath_get(&path, 0, pool) != APR_SUCCESS) {
-        usage();
+        usage(apr_psprintf(pool, "Could not get the filepath: %s",
+                           apr_strerror(status, errmsg, sizeof errmsg)));
     }
     baselen = strlen(path);
 
@@ -878,6 +968,21 @@ int main(int argc, const char * const argv[])
     }
 #endif
 
+    if (pidfile) {
+        apr_file_t *file;
+        pid_t mypid = getpid();
+        if (APR_SUCCESS == (status = apr_file_open(&file, pidfile, APR_WRITE
+                | APR_CREATE | APR_TRUNCATE | APR_DELONCLOSE,
+                APR_UREAD | APR_UWRITE | APR_GREAD, pool))) {
+            apr_file_printf(file, "%" APR_PID_T_FMT APR_EOL_STR, mypid);
+        }
+        else if (!isdaemon) {
+            apr_file_printf(errfile,
+                    "Could not write the pid file '%s': %s" APR_EOL_STR,
+                    pidfile, apr_strerror(status, errmsg, sizeof errmsg));
+        }
+    }
+
     do {
         apr_pool_create(&instance, pool);
 
@@ -938,8 +1043,8 @@ int main(int argc, const char * const argv[])
                 purge(path, instance, max);
             }
             else if (!isdaemon && !interrupted) {
-                apr_file_printf(errfile,
-                     "An error occurred, cache cleaning aborted.\n");
+                apr_file_printf(errfile, "An error occurred, cache cleaning "
+                                         "aborted." APR_EOL_STR);
                 return 1;
             }
 
@@ -997,8 +1102,8 @@ int main(int argc, const char * const argv[])
     } while (isdaemon && !interrupted);
 
     if (!isdaemon && interrupted) {
-        apr_file_printf(errfile,
-                        "Cache cleaning aborted due to user request.\n");
+        apr_file_printf(errfile, "Cache cleaning aborted due to user "
+                                 "request." APR_EOL_STR);
         return 1;
     }