/* 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.
*/
staticconstchar *const ROTATE_REASONS[] = { "None", "Open a new file", "Time interval expired", "Maximum size reached", "Forced rotation",
NULL
};
typedefstruct rotate_config rotate_config_t;
struct rotate_config { unsignedint sRotation; int tRotation; int utc_offset; int use_localtime; int use_strftime; int force_open; int verbose; int echo; char *szLogRoot; int truncate; int truncate_rotated_only; constchar *linkfile; constchar *postrotate_prog; #if APR_FILES_AS_SOCKETS int create_empty; #endif int num_files; int create_path;
};
typedefstruct rotate_status rotate_status_t;
/* "adjusted_time_t" is used to store Unix time (seconds since epoch) * which has been adjusted for some timezone fudge factor. It should * be used for storing the return values from get_now(). A typedef is
* used since this type is similar to time_t, but different. */ typedeflong adjusted_time_t;
/* Structure to contain relevant logfile state: fd, pool and
* filename. */ struct logfile {
apr_pool_t *pool;
apr_file_t *fd; char name[APR_PATH_MAX];
};
struct rotate_status { struct logfile current; /* current logfile. */
apr_pool_t *pool; /* top-level pool */ int rotateReason;
adjusted_time_t tLogEnd; int nMessCount; int fileNum;
};
staticvoid usage(constchar *argv0, constchar *reason)
{ if (reason) {
fprintf(stderr, "%s\n", reason);
}
fprintf(stderr, #if APR_FILES_AS_SOCKETS "Usage: %s [-vlfDtTec] [-L linkname] [-p prog] [-n number] <logfile> " #else "Usage: %s [-vlfDtTe] [-L linkname] [-p prog] [-n number] <logfile> " #endif "{<rotation time in seconds>|<rotation size>(B|K|M|G)} " "[offset minutes from UTC]\n\n",
argv0); #ifdef OS2
fprintf(stderr, "Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n",
argv0); #else
fprintf(stderr, "Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n",
argv0);
fprintf(stderr, "or \n\nTransferLog \"|%s /some/where 5M\"\n\n", argv0); #endif
fprintf(stderr, "to httpd.conf. By default, the generated name will be\n" "<logfile>.nnnn where nnnn is the system time at which the log\n" "nominally starts (N.B. if using a rotation time, the time will\n" "always be a multiple of the rotation time, so you can synchronize\n" "cron scripts with it). If <logfile> contains strftime conversion\n" "specifications, those will be used instead. At the end of each\n" "rotation time or when the file size is reached a new log is\n" "started.\n" "\n" "Options:\n" " -v Verbose operation. Messages are written to stderr.\n" " -l Base rotation on local time instead of UTC.\n" " -L path Create hard link from current log to specified path.\n" " -p prog Run specified program after opening a new log file. See below.\n" " -f Force opening of log on program start.\n" " -D Create parent directories of log file.\n" " -t Truncate logfile instead of rotating, tail friendly.\n" " -T Truncate logfiles opened for rotation, but not the initial logfile.\n" " -e Echo log to stdout for further processing.\n" #if APR_FILES_AS_SOCKETS " -c Create log even if it is empty.\n" #endif " -n num Rotate file by adding suffixes '.1', '.2', ..., '.num'.\n" "\n" "The program for '-p' is invoked as \"[prog] <curfile> [<prevfile>]\"\n" "where <curfile> is the filename of the newly opened logfile, and\n" "<prevfile>, if given, is the filename of the previously used logfile.\n" "\n"); exit(1);
}
/* This function returns the current Unix time (time_t) adjusted for * any configured or derived local time offset. The offset applied is
* returned via *offset. */ static adjusted_time_t get_now(rotate_config_t *config, apr_int32_t *offset)
{
apr_time_t tNow = apr_time_now();
apr_int32_t utc_offset;
if (config->use_localtime) { /* Check for our UTC offset before using it, since it might * change if there's a switch between standard and daylight * savings time.
*/
apr_time_exp_t lt;
apr_time_exp_lt(<, tNow);
utc_offset = lt.tm_gmtoff;
} else {
utc_offset = config->utc_offset;
}
if (offset)
*offset = utc_offset;
return apr_time_sec(tNow) + utc_offset;
}
/* * Close a file and destroy the associated pool.
*/ staticvoid close_logfile(rotate_config_t *config, struct logfile *logfile)
{ if (config->verbose) {
fprintf(stderr, "Closing file %s\n", logfile->name);
}
apr_file_close(logfile->fd);
apr_pool_destroy(logfile->pool);
}
/* * Check whether we need to rotate. * Possible reasons are: * - No log file open (ROTATE_NEW) * - User forces us to rotate (ROTATE_FORCE) * - Our log file size is already bigger than the * allowed maximum (ROTATE_SIZE) * - The next log time interval expired (ROTATE_TIME) * * When size and time constraints are both given, * it suffices that one of them is fulfilled.
*/ staticvoid checkRotate(rotate_config_t *config, rotate_status_t *status)
{ if (status->current.fd == NULL) {
status->rotateReason = ROTATE_NEW;
} elseif (config->sRotation) {
apr_finfo_t finfo;
apr_off_t current_size = -1;
/* Handle link file, if configured. */ if (config->linkfile) {
apr_file_remove(config->linkfile, newlog->pool); if (config->verbose) {
fprintf(stderr, "Linking %s to %s\n", newlog->name, config->linkfile);
}
rv = apr_file_link(newlog->name, config->linkfile); if (rv != APR_SUCCESS) {
apr_strerror(rv, error, sizeof error);
fprintf(stderr, "Error linking file %s to %s (%s)\n",
newlog->name, config->linkfile, error); exit(2);
}
}
if (!config->postrotate_prog) { /* Nothing more to do. */ return;
}
/* Collect any zombies from a previous run, but don't wait. */ while (apr_proc_wait_all_procs(&proc, NULL, NULL, APR_NOWAIT, pool) == APR_CHILD_DONE) /* noop */;
if (rv != APR_SUCCESS) {
fprintf(stderr, "post_rotate: could not set up process attributes for '%s': %s\n",
config->postrotate_prog,
apr_strerror(rv, error, sizeof(error))); return;
}
if (config->verbose)
fprintf(stderr, "Calling post-rotate program: %s\n", argv[0]);
rv = apr_proc_create(&proc, argv[0], argv, NULL, pattr, pool); if (rv != APR_SUCCESS) {
fprintf(stderr, "Could not spawn post-rotate process '%s': %s\n",
config->postrotate_prog,
apr_strerror(rv, error, sizeof(error))); return;
}
}
/* After a error, truncate the current file and write out an error * message, which must be contained in message. The process is
* terminated on failure. */ staticvoid truncate_and_write_error(rotate_status_t *status, constchar *message)
{
apr_size_t buflen = strlen(message);
if (apr_file_trunc(status->current.fd, 0) != APR_SUCCESS) {
fprintf(stderr, "Error truncating the file %s\n", status->current.name); exit(2);
} if (apr_file_write_full(status->current.fd, message, buflen, NULL) != APR_SUCCESS) {
fprintf(stderr, "Error writing error (%s) to the file %s\n",
message, status->current.name); exit(2);
}
}
/* * Open a new log file, and if successful * also close the old one. * * The timestamp for the calculation of the file * name of the new log file will be the actual millisecond * timestamp, except when a regular rotation based on a time * interval is configured and the previous interval * is over. Then the timestamp is the starting time * of the actual interval.
*/ staticvoid doRotate(rotate_config_t *config, rotate_status_t *status)
{
apr_int32_t offset;
adjusted_time_t now, tLogStart;
apr_status_t rv; struct logfile newlog; int thisLogNum = -1; int oldreason = status->rotateReason; int truncate = config->truncate;
/* Retrieve local-time-adjusted-Unix-time. */
now = get_now(config, &offset);
status->rotateReason = ROTATE_NONE;
if (config->tRotation) {
adjusted_time_t tLogEnd;
tLogStart = (now / config->tRotation) * config->tRotation;
tLogEnd = tLogStart + config->tRotation; /* * Check if rotation was forced and the last rotation * interval is not yet over. Use the value of now instead * of the time interval boundary for the file name then.
*/ if (tLogStart < status->tLogEnd) {
tLogStart = now;
}
status->tLogEnd = tLogEnd;
} else {
tLogStart = now;
}
status->fileNum = thisLogNum; /* Close out old (previously 'current') logfile, if any. */ if (status->current.fd) {
close_logfile(config, &status->current);
}
/* New log file is now 'current'. */
status->current = newlog;
/* The first write to the initial file hasn't checked for size. * In the normalized timestamp case and the custom strftime case with * any reasonable accuracy, it's futile as the rotation will pick the * same filename again. * For -n, when not truncating, check and rotate.
*/ if (config->num_files > 0 && oldreason == ROTATE_NEW && !config->truncate) {
checkRotate(config, status); if (status->rotateReason != ROTATE_NONE) {
doRotate(config, status);
}
}
} else { char *error = apr_psprintf(newlog.pool, "%pm", &rv); char *message;
/* 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 (status->current.fd == NULL) {
fprintf(stderr, "Could not open log file '%s' (%s)\n", newlog.name, error); exit(2);
}
/* Try to keep this error message constant length
* in case it occurs several times. */
message = apr_psprintf(newlog.pool, "Resetting log file due to error opening " "new log file, %10d messages lost: %-25.25s\n",
status->nMessCount, error);
truncate_and_write_error(status, message);
/* Throw away new state; it isn't going to be used. */
apr_pool_destroy(newlog.pool);
}
status->nMessCount = 0;
}
/* * Get a size or time param from a string. * Parameter 'last' indicates, whether the * argument is the last commandline argument. * UTC offset is only allowed as a last argument * in order to make is distinguishable from the * rotation interval time.
*/ staticconstchar *get_time_or_size(rotate_config_t *config, constchar *arg, int last) { char *ptr = NULL; /* Byte multiplier */ unsignedint mult = 1; if ((ptr = strchr(arg, 'B')) != NULL) { /* Found KB size */
mult = 1;
} elseif ((ptr = strchr(arg, 'K')) != NULL) { /* Found KB size */
mult = 1024;
} elseif ((ptr = strchr(arg, 'M')) != NULL) { /* Found MB size */
mult = 1024 * 1024;
} elseif ((ptr = strchr(arg, 'G')) != NULL) { /* Found GB size */
mult = 1024 * 1024 * 1024;
} if (ptr) { /* rotation based on file size */ if (config->sRotation > 0) { return"Rotation size parameter allowed only once";
} if (*(ptr+1) == '\0') {
config->sRotation = atoi(arg) * mult;
} if (config->sRotation == 0) { return"Invalid rotation size parameter";
}
} elseif ((config->sRotation > 0 || config->tRotation > 0) && last) { /* rotation based on elapsed time */ if (config->use_localtime) { return"UTC offset parameter is not valid with -l";
}
config->utc_offset = atoi(arg) * 60;
} else { /* rotation based on elapsed time */ if (config->tRotation > 0) { return"Rotation time parameter allowed only once";
}
config->tRotation = atoi(arg); if (config->tRotation <= 0) { return"Invalid rotation time parameter";
}
} return NULL;
}
if (rv != APR_EOF) {
usage(argv[0], NULL /* specific error message already issued */ );
}
/* * After the initial flags we need 2 to 4 arguments, * the file name, either the rotation interval time or size * or both of them, and optionally the UTC offset.
*/ if ((argc - opt->ind < 2) || (argc - opt->ind > 4) ) {
usage(argv[0], "Incorrect number of arguments");
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.