rotatelogs.c revision ac738faa5ec958960f59f60b0109fb103874b8e3
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek/* Licensed to the Apache Software Foundation (ASF) under one or more
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * contributor license agreements. See the NOTICE file distributed with
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * this work for additional information regarding copyright ownership.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * The ASF licenses this file to You under the Apache License, Version 2.0
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * (the "License"); you may not use this file except in compliance with
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * the License. You may obtain a copy of the License at
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Unless required by applicable law or agreed to in writing, software
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * distributed under the License is distributed on an "AS IS" BASIS,
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * See the License for the specific language governing permissions and
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * limitations under the License.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Simple program to rotate Apache logs without having to kill the server.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Contributed by Ben Laurie <ben algroup.co.uk>
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * 12 Mar 1996
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Ported to APR by Mladen Turk <mturk mappingsoft.com>
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * 23 Sep 2001
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * -l option added 2004-06-11
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * -l causes the use of local time rather than GMT as the base for the
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * interval. NB: Using -l in an environment which changes the GMT offset
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * (such as for BST or DST) can lead to unpredictable results!
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * -f option added Feb, 2008. This causes rotatelog to open/create
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * the logfile as soon as it's started, not as soon as it sees
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * -v option added Feb, 2008. Verbose output of command line parsing.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozekstatic const char *ROTATE_REASONS[] = {
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Open a new file",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Time interval expired",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Maximum size reached",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Forced rotation",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozekstatic void usage(const char *argv0, const char *reason)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-t] [-e] <logfile> "
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "{<rotation time in seconds>|<rotation size>(B|K|M|G)} "
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "[offset minutes from UTC]\n\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "or \n\nTransferLog \"|%s /some/where 5M\"\n\n", argv0);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "to httpd.conf. By default, the generated name will be\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "<logfile>.nnnn where nnnn is the system time at which the log\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "nominally starts (N.B. if using a rotation time, the time will\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "always be a multiple of the rotation time, so you can synchronize\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "cron scripts with it). If <logfile> contains strftime conversion\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "specifications, those will be used instead. At the end of each\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "rotation time or when the file size is reached a new log is\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "started.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Options:\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -v Verbose operation. Messages are written to stderr.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -l Base rotation on local time instead of UTC.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -L path Create hard link from current log to specified path.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -p prog Run specified program after opening a new log file. See below.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -f Force opening of log on program start.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -t Truncate logfile instead of rotating, tail friendly.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek " -e Echo log to stdout for further processing.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "The program is invoked as \"[prog] <curfile> [<prevfile>]\"\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "where <curfile> is the filename of the newly opened logfile, and\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "<prevfile>, if given, is the filename of the previously used logfile.\n"
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Get the unix time with timezone corrections
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * given in the config struct.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek /* Check for our UTC offset before using it, since it might
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * change if there's a switch between standard and daylight
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * savings time.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Close a file and destroy the associated pool.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozekstatic void closeFile(rotate_config_t *config, apr_pool_t *pool, apr_file_t *file)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_status_t rv = apr_file_info_get(&finfo, wanted, file);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek if ((rv == APR_SUCCESS) || (rv == APR_INCOMPLETE)) {
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Closing file %s (%s)\n", finfo.name, finfo.fname);
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek fprintf(stderr, "Closing file %s\n", finfo.fname);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Dump the configuration parsing result to STDERR.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozekstatic void dumpConfig (rotate_config_t *config)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation time interval: %12d\n", config->tRotation);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation size interval: %12d\n", config->sRotation);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation time UTC offset: %12d\n", config->utc_offset);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation based on localtime: %12s\n", config->use_localtime ? "yes" : "no");
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation file date pattern: %12s\n", config->use_strftime ? "yes" : "no");
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation file forced open: %12s\n", config->force_open ? "yes" : "no");
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation verbose: %12s\n", config->verbose ? "yes" : "no");
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Rotation file name: %21s\n", config->szLogRoot);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Post-rotation prog: %21s\n", config->postrotate_prog);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Check whether we need to rotate.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Possible reasons are:
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * - No log file open (ROTATE_NEW)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * - User forces us to rotate (ROTATE_FORCE)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * - Our log file size is already bigger than the
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * allowed maximum (ROTATE_SIZE)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * - The next log time interval expired (ROTATE_TIME)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * When size and time constraints are both given,
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek * it suffices that one of them is fulfilled.
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozekstatic void checkRotate(rotate_config_t *config, rotate_status_t *status)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek if (apr_file_info_get(&finfo, APR_FINFO_SIZE, status->nLogFD) == APR_SUCCESS) {
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek fprintf(stderr, "No rotation time or size specified\n");
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek if (status->rotateReason != ROTATE_NONE && config->verbose) {
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek fprintf(stderr, "File rotation needed, reason: %s\n", ROTATE_REASONS[status->rotateReason]);
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek * Spawn a post-rotate process.
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozekstatic void post_rotate(apr_pool_t *pool, rotate_config_t *config, rotate_status_t *status)
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek /* Collect any zombies from a previous run, but don't wait. */
9a3ba9ca00e73adc3fb17ce8afa532076768023bJakub Hrozek while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, pool) == APR_CHILD_DONE)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek if ((rv = apr_procattr_create(&pattr, pool)) != APR_SUCCESS) {
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "post_rotate: apr_procattr_create failed for '%s': %s\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek rv = apr_procattr_cmdtype_set(pattr, APR_PROGRAM_ENV);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "post_rotate: could not set up process attributes for '%s': %s\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Calling post-rotate program: %s\n", argv[0]);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek rv = apr_proc_create(&proc, argv[0], argv, NULL, pattr, pool);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Could not spawn post-rotate process '%s': %s\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Open a new log file, and if successful
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * also close the old one.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * The timestamp for the calculation of the file
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * name of the new log file will be the actual millisecond
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * timestamp, except when a regular rotation based on a time
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * interval is configured and the previous interval
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * is over. Then the timestamp is the starting time
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * of the actual interval.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozekstatic void doRotate(rotate_config_t *config, rotate_status_t *status)
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek tLogStart = (now / config->tRotation) * config->tRotation;
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Check if rotation was forced and the last rotation
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * interval is not yet over. Use the value of now instead
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * of the time interval boundary for the file name then.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_cpystrn(status->filenameprev, status->filename, MAX_PATH);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_time_t tNow = apr_time_from_sec(tLogStart);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_strftime(status->filename, &rs, sizeof(status->filename), config->szLogRoot, &e);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_snprintf(status->filename, sizeof(status->filename), "%s", config->szLogRoot);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_snprintf(status->filename, sizeof(status->filename), "%s.%010d", config->szLogRoot,
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Opening file %s\n", status->filename);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek rv = apr_file_open(&status->nLogFD, status->filename, APR_WRITE | APR_CREATE | APR_APPEND
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek | (config->truncate ? APR_TRUNCATE : 0), APR_OS_DEFAULT, status->pfile);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek /* Uh-oh. Failed to open the new log file. Try to clear
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * the previous log file, note the lost log entries,
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * and keep on truckin'. */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Could not open log file '%s' (%s)\n", status->filename, error);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek /* Try to keep this error message constant length
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * in case it occurs several times. */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_snprintf(status->errbuf, sizeof status->errbuf,
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "Resetting log file due to error opening "
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek "new log file, %10d messages lost: %-25.25s\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek if (apr_file_write(status->nLogFD, status->errbuf, &nWrite) != APR_SUCCESS) {
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Error writing to the file %s\n", status->filename);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek /* If postrotate configured, run the post-rotate program: */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek closeFile(config, status->pfile_prev, status->nLogFDprev);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_file_remove(config->linkfile, status->pfile);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr,"Linking %s to %s\n", status->filename, config->linkfile);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek rv = apr_file_link(status->filename, config->linkfile);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek fprintf(stderr, "Error linking file %s to %s (%s)\n",
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Get a size or time param from a string.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * Parameter 'last' indicates, whether the
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * argument is the last commadnline argument.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * UTC offset is only allowed as a last argument
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * in order to make is distinguishable from the
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek * rotation interval time.
f232789430a080384188d5da89b19d874cf17513Jakub Hrozekstatic const char *get_time_or_size(rotate_config_t *config,
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek /* Byte multiplier */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek if ((ptr = strchr(arg, 'B')) != NULL) { /* Found KB size */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek else if ((ptr = strchr(arg, 'K')) != NULL) { /* Found KB size */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek else if ((ptr = strchr(arg, 'M')) != NULL) { /* Found MB size */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek else if ((ptr = strchr(arg, 'G')) != NULL) { /* Found GB size */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek return "Rotation size parameter allowed only once";
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek return "Invalid rotation size parameter";
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek else if ((config->sRotation > 0 || config->tRotation > 0) && last) {
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek /* rotation based on elapsed time */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek return "UTC offset parameter is not valid with -l";
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek else { /* rotation based on elapsed time */
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek return "Rotation time parameter allowed only once";
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek return "Invalid rotation time parameter";
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek apr_getopt_init(&opt, status.pool, argc, argv);
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek while ((rv = apr_getopt(opt, "lL:p:ftve", &c, &opt_arg)) == APR_SUCCESS) {
f232789430a080384188d5da89b19d874cf17513Jakub Hrozek switch (c) {
cur_offset = 0;