rotatelogs.c revision 33ce841bd19de25ff3317e4be422cc5be0781ab8
/* 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
*
*
* 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.
*/
#include "apr.h"
#include "apr_lib.h"
#include "apr_strings.h"
#include "apr_errno.h"
#include "apr_file_io.h"
#include "apr_file_info.h"
#include "apr_general.h"
#include "apr_time.h"
#include "apr_getopt.h"
#include "apr_thread_proc.h"
#include "apr_poll.h"
#endif
#include <stdlib.h>
#endif
#define APR_WANT_STRFUNC
#include "apr_want.h"
#define BUFSIZE 65536
#define ERRMSGSZ 256
#define ROTATE_NONE 0
#define ROTATE_NEW 1
#define ROTATE_TIME 2
#define ROTATE_SIZE 3
#define ROTATE_FORCE 4
static const char *const ROTATE_REASONS[] = {
"None",
"Open a new file",
"Time interval expired",
"Maximum size reached",
"Forced rotation",
};
typedef struct rotate_config rotate_config_t;
struct rotate_config {
unsigned int sRotation;
int tRotation;
int utc_offset;
int use_localtime;
int use_strftime;
int force_open;
int verbose;
int echo;
char *szLogRoot;
int truncate;
const char *linkfile;
const char *postrotate_prog;
int create_empty;
#endif
int num_files;
int create_path;
};
typedef struct rotate_status rotate_status_t;
/* Structure to contain relevant logfile state: fd, pool and
* filename. */
struct logfile {
apr_file_t *fd;
char name[APR_PATH_MAX];
};
struct rotate_status {
int rotateReason;
int tLogEnd;
int nMessCount;
int fileNum;
};
static rotate_config_t config;
static rotate_status_t status;
{
if (reason) {
}
"Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-d] [-t] [-e] [-c] [-n number] <logfile> "
#else
"Usage: %s [-v] [-l] [-L linkname] [-p prog] [-f] [-d] [-t] [-e] [-n number] <logfile> "
#endif
"{<rotation time in seconds>|<rotation size>(B|K|M|G)} "
"[offset minutes from UTC]\n\n",
argv0);
#ifdef OS2
argv0);
#else
argv0);
#endif
"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"
" -e Echo log to stdout for further processing.\n"
" -c Create log even if it is empty.\n"
#endif
"\n"
"The program 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) plus any
* configured or derived local time offset. The offset applied is
* returned via *offset. */
{
int 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.
*/
}
else {
}
if (offset)
*offset = utc_offset;
}
/*
* Close a file and destroy the associated pool.
*/
{
}
}
/*
* Dump the configuration parsing result to STDERR.
*/
{
#endif
}
/*
* 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.
*/
{
}
}
}
}
}
}
}
}
else {
exit(2);
}
}
}
/*
* Handle post-rotate processing.
*/
{
const char *argv[4];
/* Handle link file, if configured. */
}
if (rv != APR_SUCCESS) {
exit(2);
}
}
if (!config->postrotate_prog) {
/* Nothing more to do. */
return;
}
/* Collect any zombies from a previous run, but don't wait. */
/* noop */;
return;
}
if (rv == APR_SUCCESS)
if (rv != APR_SUCCESS) {
&rv);
return;
}
}
else {
}
if (rv != APR_SUCCESS) {
return;
}
}
/* After a error, truncate the current file and write out an error
* message, which must be contained in status->errbuf. The process is
* terminated on failure. */
{
exit(2);
}
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.
*/
{
int now;
int tLogStart;
int thisLogNum = -1;
/* Retrieve local-time-adjusted-Unix-time. */
int tLogEnd;
/*
* 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.
*/
}
}
else {
}
if (config->use_strftime) {
/* Explode the local-time-adjusted-Unix-time into a struct tm,
* first *reversing* local-time-adjustment applied by
* get_now() if we are using localtime. */
if (config->use_localtime)
else
apr_time_exp_gmt(&e, tNow);
}
else {
}
thisLogNum = 0;
}
else {
}
}
else {
}
}
if (config->create_path) {
}
if (rv != APR_SUCCESS) {
"Could not create directory '%s' (%pm)\n",
exit(2);
}
}
}
}
if (rv == APR_SUCCESS) {
/* Handle post-rotate processing. */
/* Close out old (previously 'current') logfile, if any. */
}
/* New log file is now 'current'. */
}
else {
char error[120];
/* 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'. */
exit(2);
}
/* Throw away new state; it isn't going to be used. */
/* Try to keep this error message constant length
* in case it occurs several times. */
"Resetting log file due to error opening "
"new log file, %10d messages lost: %-25.25s\n",
}
status->nMessCount = 0;
}
/*
* Get a size or time param from a string.
* Parameter 'last' indicates, whether the
* argument is the last commadnline argument.
* UTC offset is only allowed as a last argument
* in order to make is distinguishable from the
* rotation interval time.
*/
/* Byte multiplier */
unsigned int mult = 1;
mult = 1;
}
mult = 1024;
}
}
}
if (ptr) { /* rotation based on file size */
return "Rotation size parameter allowed only once";
}
}
return "Invalid rotation size parameter";
}
}
/* rotation based on elapsed time */
if (config->use_localtime) {
return "UTC offset parameter is not valid with -l";
}
}
else { /* rotation based on elapsed time */
return "Rotation time parameter allowed only once";
}
return "Invalid rotation time parameter";
}
}
return NULL;
}
{
char c;
const char *opt_arg;
apr_pollfd_t pollfd = { 0 };
int polltimeout;
#endif
#else
#endif
switch (c) {
case 'l':
break;
case 'L':
break;
case 'p':
break;
case 'f':
break;
case 'd':
break;
case 't':
break;
case 'v':
break;
case 'e':
break;
case 'c':
break;
#endif
case 'n':
break;
}
}
}
/*
* 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.
*/
}
}
/* Read in the remaining flags, namely time, size and UTC offset. */
}
}
exit(1);
}
exit(1);
}
exit(1);
}
exit(1);
}
/*
* Write out result of config parsing if verbose is set.
*/
dumpConfig(&config);
}
}
#endif
/*
* Immediately open the logfile as we start, if we were forced
* to do so via '-f'.
*/
if (config.force_open) {
}
for (;;) {
if (polltimeout <= 0) {
}
else {
}
}
if (pollret == APR_SUCCESS) {
if (APR_STATUS_IS_EOF(rv)) {
break;
}
else if (rv != APR_SUCCESS) {
exit(3);
}
}
else if (pollret == APR_TIMEUP) {
*buf = 0;
nRead = 0;
}
else {
exit(5);
}
#else /* APR_FILES_AS_SOCKETS */
if (APR_STATUS_IS_EOF(rv)) {
break;
}
else if (rv != APR_SUCCESS) {
exit(3);
}
#endif /* APR_FILES_AS_SOCKETS */
}
cur_offset = 0;
cur_offset = -1;
}
status.nMessCount++;
"%10d messages lost (%pm)\n",
}
else {
status.nMessCount++;
}
exit(4);
}
}
}
return 0; /* reached only at stdin EOF. */
}