#include <ctype.h>
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <glob.h>
#include <grp.h>
#include <popt.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
#include <assert.h>
#include "basenames.h"
#include "log.h"
#include "logrotate.h"
#if !defined(GLOB_ABORTED) && defined(GLOB_ABEND)
#define GLOB_ABORTED GLOB_ABEND
#endif
#define REALLOC_STEP 10
#if defined(SunOS) && !defined(isblank)
#define isblank(c) ( (c) == ' ' || (c) == '\t' ) ? 1 : 0
#endif
static char * defTabooExts[] = { ".rpmsave", ".rpmorig", "~", ",v",
".rpmnew", ".swp" };
static int defTabooCount = sizeof(defTabooExts) / sizeof(char *);
/* I shouldn't use globals here :-( */
static char ** tabooExts = NULL;
int tabooCount = 0;
static int readConfigFile(const char * configFile, logInfo * defConfig,
logInfo ** logsPtr, int * numLogsPtr);
static int globerr(const char * pathname, int theerr);
static struct rotatePatternElement * parsePattern(const char * pattern,
const char * configFile, int lineNum);
static int isolateValue(const char * fileName, int lineNum, char * key,
char ** startPtr, char ** endPtr) {
char * chptr = *startPtr;
while (isblank(*chptr)) chptr++;
if (*chptr == '=') {
chptr++;
while (*chptr && isblank(*chptr)) chptr++;
}
if (*chptr == '\n') {
message(MESS_ERROR, "%s:%d argument expected after %s\n",
fileName, lineNum, key);
return 1;
}
*startPtr = chptr;
while (*chptr != '\n') chptr++;
while (isspace(*chptr)) chptr--;
*endPtr = chptr + 1;
return 0;
}
static char *readPath(const char *configFile, int lineNum, char *key,
char **startPtr) {
char oldchar;
char *endtag, *chptr;
char *start = *startPtr;
char *path;
if (!isolateValue(configFile, lineNum, key, &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
chptr = start;
/* this is technically too restrictive -- let's see if anyone
complains */
while (*chptr && isprint(*chptr) && *chptr != ' ')
chptr++;
if (*chptr) {
message(MESS_ERROR, "%s:%d bad %s path %s\n",
configFile, lineNum, key, start);
return NULL;
}
path = strdup(start);
*endtag = oldchar, start = endtag;
*startPtr = start;
return path;
} else
return NULL;
}
static char * readAddress(const char * configFile, int lineNum, char * key,
char ** startPtr) {
char oldchar;
char * endtag, * chptr;
char * start = *startPtr;
char * address;
if (!isolateValue(configFile, lineNum, key, &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
chptr = start;
while (*chptr && isprint(*chptr) && *chptr != ' ')
chptr++;
if (*chptr) {
message(MESS_ERROR, "%s:%d bad %s address %s\n",
configFile, lineNum, key, start);
return NULL;
}
address = strdup(start);
*endtag = oldchar, start = endtag;
*startPtr = start;
return address;
} else
return NULL;
}
static int checkFile(const char * fname) {
int i;
/* Check if fname is '.' or '..'; if so, return false */
if (fname[0] == '.' &&
(!fname[1] || (fname[1] == '.' && !fname[2])))
return 0;
/* Check if fname is ending in a taboo-extension; if so, return
false */
for (i = 0; i < tabooCount; i++) {
if (!strcmp(fname + strlen(fname) - strlen(tabooExts[i]),
tabooExts[i])) {
message(MESS_ERROR, "Ignoring %s, because of %s "
"ending\n", fname, tabooExts[i]);
return 0;
}
}
/* All checks have been passed; return true */
return 1;
}
/* Used by qsort to sort filelist */
static int compar(const void *p, const void *q) {
return strcoll(*((char **)p), *((char **)q));
}
/* Free memory blocks pointed to by pointers in namelist and namelist itself */
static void free_namelist (char **namelist, int files_count)
{
int i;
for (i=0; i<files_count; ++i)
free(namelist[i]);
free(namelist);
}
static struct rotatePatternElement * parsePattern(const char * pattern,
const char * configFile, int lineNum) {
struct rotatePatternElement * head, * item;
struct rotatePatternElement new;
const char * field;
const char * start;
/* dummy head node; we build off of it */
head = alloca(sizeof(*head));
item = head;
start = field = pattern;
memset(&new, 0, sizeof(new));
while (*field) {
if (*field != '%') {
field++;
continue;
}
switch (*field) {
case '%':
break;
case 'f':
new.type = RP_FILENAME;
break;
case 'c':
new.type = RP_COUNT;
break;
default:
message(MESS_ERROR, "%s:%d unknown element %c in pattern %s",
configFile, lineNum, *field, pattern);
return NULL;
}
if (new.type != RP_NONE) {
if (start != field) {
/* fixed string */
item->next = malloc(sizeof(*item->next));
item->type = RP_STRING;
item->arg = malloc(field - start + 1);
strncpy(item->arg, start, field - start);
item->arg[field-start] = '\0';
item = item->next;
}
item->next = malloc(sizeof(*item->next));
*item->next = new;
item = item->next;
memset(&new, 0, sizeof(new));
}
field++;
}
if (start != field) {
/* fixed string */
item->next = malloc(sizeof(*item->next));
item->type = RP_STRING;
item->arg = malloc(field - start + 1);
strncpy(item->arg, start, field-start);
item->arg[field-start] = '\0';
item = item->next;
}
return NULL;
}
int readConfigPath(const char * path, logInfo * defConfig,
logInfo ** logsPtr, int * numLogsPtr) {
struct stat sb;
int here;
if (!tabooExts) {
tabooExts = malloc(sizeof(*tabooExts) * defTabooCount);
memcpy(tabooExts, defTabooExts, sizeof(*tabooExts) * defTabooCount);
tabooCount = defTabooCount;
}
if (stat(path, &sb)) {
message(MESS_ERROR, "cannot stat %s: %s\n", path, strerror(errno));
return 1;
}
if (S_ISDIR(sb.st_mode)) {
char **namelist, **p;
struct dirent *dp;
int files_count,i;
DIR *dirp;
here = open(".", O_RDONLY);
if (here < 0) {
message(MESS_ERROR, "cannot open current directory: %s\n",
strerror(errno));
return 1;
}
if ( (dirp = opendir(path)) == NULL) {
message(MESS_ERROR, "cannot open directory %s: %s\n", path,
strerror(errno));
return 1;
}
files_count = 0;
namelist = NULL;
while ((dp = readdir(dirp)) != NULL) {
if (checkFile(dp->d_name)) {
/* Realloc memory for namelist array if necessary */
if (files_count % REALLOC_STEP == 0) {
p = (char **) realloc(namelist, (files_count + REALLOC_STEP) * sizeof(char *));
if (p) {
namelist = p;
memset(namelist + files_count, '\0', REALLOC_STEP * sizeof(char *));
} else {
free_namelist(namelist, files_count);
message(MESS_ERROR, "cannot realloc: %s\n", strerror(errno));
return 1;
}
}
/* Alloc memory for file name */
if ( (namelist[files_count] = (char *) malloc( strlen(dp->d_name) + 1)) ) {
strcpy(namelist[files_count], dp->d_name);
files_count++;
} else {
free_namelist(namelist, files_count);
message(MESS_ERROR, "cannot realloc: %s\n", strerror(errno));
return 1;
}
}
}
closedir( dirp );
if (files_count > 0) {
qsort(namelist, files_count, sizeof(char *), compar);
} else {
return 0;
}
if (chdir(path)) {
message(MESS_ERROR, "error in chdir(\"%s\"): %s\n", path,
strerror(errno));
close(here);
free_namelist(namelist, files_count);
return 1;
}
for (i=0; i<files_count; ++i) {
assert(namelist[i] != NULL);
if (readConfigFile(namelist[i], defConfig, logsPtr,
numLogsPtr)) {
fchdir(here);
close(here);
free_namelist(namelist, files_count);
return 1;
}
};
fchdir(here);
close(here);
free_namelist(namelist, files_count);
} else {
return readConfigFile(path, defConfig, logsPtr, numLogsPtr);
}
return 0;
}
static int globerr(const char * pathname, int theerr) {
message(MESS_ERROR, "error accessing %s: %s\n", pathname,
strerror(theerr));
/* We want the glob operation to abort on error, so return 1 */
return 1;
}
static int readConfigFile(const char * configFile, logInfo * defConfig,
logInfo ** logsPtr, int * numLogsPtr) {
int fd;
char * buf, * endtag;
char oldchar, foo;
int length;
int lineNum = 1;
int multiplier;
int i, j, k;
char * scriptStart = NULL;
char ** scriptDest = NULL;
logInfo * newlog = defConfig;
char * start, * chptr;
char * dirName;
struct group * group;
struct passwd * pw;
int rc;
char createOwner[200], createGroup[200];
int createMode;
struct stat sb, sb2;
glob_t globResult;
const char ** argv;
int argc, argNum;
/* FIXME: createOwner and createGroup probably shouldn't be fixed
length arrays -- of course, if we aren't run setuid it doesn't
matter much */
fd = open(configFile, O_RDONLY);
if (fd < 0) {
message(MESS_ERROR, "failed to open config file %s: %s\n",
configFile, strerror(errno));
return 1;
}
if (fstat(fd, &sb)) {
message(MESS_ERROR, "fstat of %s failed: %s\n", configFile,
strerror(errno));
close(fd);
return 1;
}
if (!S_ISREG(sb.st_mode)) {
message(MESS_DEBUG, "Ignoring %s because it's not a regular file.\n",
configFile);
close(fd);
return 0;
}
length = lseek(fd, 0, SEEK_END);
lseek(fd, 0, SEEK_SET);
buf = alloca(length + 2);
if (!buf) {
message(MESS_ERROR, "alloca() of %d bytes failed\n", length);
close(fd);
return 1;
}
if (read(fd, buf, length) != length) {
message(MESS_ERROR, "failed to read %s: %s\n", configFile,
strerror(errno));
close(fd);
return 1;
}
close(fd);
/* knowing the buffer ends with a newline makes things (a bit) cleaner */
buf[length + 1] = '\0';
buf[length] = '\n';
message(MESS_DEBUG, "reading config file %s\n", configFile);
start = buf;
while (*start) {
while (isblank(*start) && (*start)) start++;
if (*start == '#') {
while (*start != '\n') start++;
}
if (*start == '\n') {
start++;
lineNum++;
continue;
}
if (scriptStart) {
if (!strncmp(start, "endscript", 9)) {
chptr = start + 9;
while (isblank(*chptr)) chptr++;
if (*chptr == '\n') {
endtag = start;
while (*endtag != '\n') endtag--;
endtag++;
*scriptDest = malloc(endtag - scriptStart + 1);
strncpy(*scriptDest, scriptStart, endtag - scriptStart);
(*scriptDest)[endtag - scriptStart] = '\0';
start = chptr + 1;
lineNum++;
scriptDest = NULL;
scriptStart = NULL;
}
}
if (scriptStart) {
while (*start != '\n') start++;
lineNum++;
start++;
}
} else if (isalpha(*start)) {
endtag = start;
while (isalpha(*endtag)) endtag++;
oldchar = *endtag;
*endtag = '\0';
if (!strcmp(start, "compress")) {
newlog->flags |= LOG_FLAG_COMPRESS;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nocompress")) {
newlog->flags &= ~LOG_FLAG_COMPRESS;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "delaycompress")) {
newlog->flags |= LOG_FLAG_DELAYCOMPRESS;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nodelaycompress")) {
newlog->flags &= ~LOG_FLAG_DELAYCOMPRESS;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "sharedscripts")) {
newlog->flags |= LOG_FLAG_SHAREDSCRIPTS;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nosharedscripts")) {
newlog->flags &= ~LOG_FLAG_SHAREDSCRIPTS;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "copytruncate")) {
newlog->flags |= LOG_FLAG_COPYTRUNCATE;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nocopytruncate")) {
newlog->flags &= ~LOG_FLAG_COPYTRUNCATE;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "copy")) {
newlog->flags |= LOG_FLAG_COPY;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nocopy")) {
newlog->flags &= ~LOG_FLAG_COPY;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "ifempty")) {
newlog->flags |= LOG_FLAG_IFEMPTY;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "notifempty")) {
newlog->flags &= ~LOG_FLAG_IFEMPTY;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "dateext")) {
newlog->flags |= LOG_FLAG_DATEEXT;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nodateext")) {
newlog->flags &= ~LOG_FLAG_DATEEXT;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "noolddir")) {
newlog->oldDir = NULL;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "mailfirst")) {
newlog->flags |= LOG_FLAG_MAILFIRST;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "maillast")) {
newlog->flags &= ~LOG_FLAG_MAILFIRST;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "create")) {
*endtag = oldchar, start = endtag;
endtag = start;
while (*endtag != '\n') endtag++;
while (isspace(*endtag)) endtag--;
endtag++;
oldchar = *endtag, *endtag = '\0';
rc = sscanf(start, "%o %s %s%c", &createMode,
createOwner, createGroup, &foo);
if (rc == 4) {
message(MESS_ERROR, "%s:%d extra arguments for "
"create\n", configFile, lineNum);
return 1;
}
if (rc > 0)
newlog->createMode = createMode;
if (rc > 1) {
pw = getpwnam(createOwner);
if (!pw) {
message(MESS_ERROR, "%s:%d unknown user '%s'\n",
configFile, lineNum, createOwner);
return 1;
}
newlog->createUid = pw->pw_uid;
endpwent();
}
if (rc > 2) {
group = getgrnam(createGroup);
if (!group) {
message(MESS_ERROR, "%s:%d unknown group '%s'\n",
configFile, lineNum, createGroup);
return 1;
}
newlog->createGid = group->gr_gid;
endgrent();
}
newlog->flags |= LOG_FLAG_CREATE;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nocreate")) {
newlog->flags &= ~LOG_FLAG_CREATE;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "size")) {
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "size", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
length = strlen(start) - 1;
if (start[length] == 'k') {
start[length] = '\0';
multiplier = 1024;
} else if (start[length] == 'M') {
start[length] = '\0';
multiplier = 1024 * 1024;
} else if (start[length] == 'G') {
start[length] = '\0';
multiplier = 1024 * 1024 * 1024;
} else if (!isdigit(start[length])) {
message(MESS_ERROR, "%s:%d unknown unit '%c'\n",
configFile, lineNum, start[length]);
return 1;
} else {
multiplier = 1;
}
newlog->threshhold = multiplier * strtoul(start, &chptr, 0);
if (*chptr) {
message(MESS_ERROR, "%s:%d bad size '%s'\n",
configFile, lineNum, start);
return 1;
}
newlog->criterium = ROT_SIZE;
*endtag = oldchar, start = endtag;
}
#if 0 /* this seems like such a good idea :-( */
} else if (!strcmp(start, "days")) {
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "size", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
newlog->threshhold = strtoul(start, &chptr, 0);
if (*chptr) {
message(MESS_ERROR, "%s:%d bad number of days'%s'\n",
configFile, lineNum, start);
return 1;
}
newlog->criterium = ROT_DAYS;
*endtag = oldchar, start = endtag;
}
#endif
} else if (!strcmp(start, "daily")) {
*endtag = oldchar, start = endtag;
newlog->criterium = ROT_DAYS;
newlog->threshhold = 1;
} else if (!strcmp(start, "monthly")) {
*endtag = oldchar, start = endtag;
newlog->criterium = ROT_MONTHLY;
} else if (!strcmp(start, "weekly")) {
*endtag = oldchar, start = endtag;
newlog->criterium = ROT_WEEKLY;
} else if (!strcmp(start, "rotate")) {
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "rotate count", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
newlog->rotateCount = strtoul(start, &chptr, 0);
if (*chptr || newlog->rotateCount < 0) {
message(MESS_ERROR, "%s:%d bad rotation count '%s'\n",
configFile, lineNum, start);
return 1;
}
*endtag = oldchar, start = endtag;
}
} else if (!strcmp(start, "start")) {
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "start count", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
newlog->logStart = strtoul(start, &chptr, 0);
if (*chptr || newlog->logStart < 0) {
message(MESS_ERROR, "%s:%d bad start count '%s'\n",
configFile, lineNum, start);
return 1;
}
*endtag = oldchar, start = endtag;
}
} else if (!strcmp(start, "maxage")) {
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "maxage count", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
newlog->rotateAge = strtoul(start, &chptr, 0);
if (*chptr || newlog->rotateAge < 0) {
message(MESS_ERROR, "%s:%d bad maximum age '%s'\n",
configFile, lineNum, start);
return 1;
}
*endtag = oldchar, start = endtag;
}
} else if (!strcmp(start, "errors")) {
message(MESS_DEBUG, "%s: %d: the errors directive is deprecated and no longer used.\n",
configFile, lineNum);
} else if (!strcmp(start, "mail")) {
*endtag = oldchar, start = endtag;
if (!(newlog->logAddress = readAddress(configFile, lineNum,
"mail", &start))) {
return 1;
}
} else if (!strcmp(start, "nomail")) {
/* hmmm, we could lose memory here, but not much */
newlog->logAddress = NULL;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "missingok")) {
newlog->flags |= LOG_FLAG_MISSINGOK;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "nomissingok")) {
newlog->flags &= ~LOG_FLAG_MISSINGOK;
*endtag = oldchar, start = endtag;
} else if (!strcmp(start, "prerotate")) {
*endtag = oldchar, start = endtag;
scriptStart = start;
scriptDest = &newlog->pre;
while (*start != '\n') start++;
} else if (!strcmp(start, "firstaction")) {
*endtag = oldchar, start = endtag;
scriptStart = start;
scriptDest = &newlog->first;
while (*start != '\n') start++;
} else if (!strcmp(start, "postrotate")) {
*endtag = oldchar, start = endtag;
scriptStart = start;
scriptDest = &newlog->post;
while (*start != '\n') start++;
} else if (!strcmp(start, "lastaction")) {
*endtag = oldchar, start = endtag;
scriptStart = start;
scriptDest = &newlog->last;
while (*start != '\n') start++;
} else if (!strcmp(start, "tabooext")) {
if (newlog != defConfig) {
message(MESS_ERROR, "%s:%d tabooext may not appear inside "
"of log file definition\n", configFile, lineNum);
return 1;
}
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "tabooext", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
if (*start == '+') {
start++;
while (isspace(*start) && *start) start++;
} else {
free(tabooExts);
tabooCount = 0;
tabooExts = malloc(1);
}
while (*start) {
chptr = start;
while (!isspace(*chptr) && *chptr != ',' && *chptr)
chptr++;
tabooExts = realloc(tabooExts, sizeof(*tabooExts) *
(tabooCount + 1));
/* this is a memory leak if the list gets reset */
tabooExts[tabooCount] = malloc(chptr - start + 1);
strncpy(tabooExts[tabooCount], start, chptr - start);
tabooExts[tabooCount][chptr - start] = '\0';
tabooCount++;
start = chptr;
if (*start == ',') start++;
while (isspace(*start) && *start) start++;
}
*endtag = oldchar, start = endtag;
}
} else if (!strcmp(start, "include")) {
if (newlog != defConfig) {
message(MESS_ERROR, "%s:%d include may not appear inside "
"of log file definition\n", configFile, lineNum);
return 1;
}
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "include", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
message(MESS_DEBUG, "including %s\n", start);
if (readConfigPath(start, defConfig, logsPtr,
numLogsPtr))
return 1;
*endtag = oldchar, start = endtag;
}
} else if (!strcmp(start, "pattern")) {
char * patternString;
*endtag = oldchar, start = endtag;
if (!(patternString = readPath(configFile, lineNum,
"pattern", &start))) {
return 1;
}
newlog->rotatePattern = parsePattern(patternString,
configFile, lineNum);
if (!newlog->rotatePattern) return 1;
message(MESS_DEBUG, "pattern is now %s\n", patternString);
} else if (!strcmp(start, "olddir")) {
*endtag = oldchar, start = endtag;
if (!(newlog->oldDir = readPath(configFile, lineNum,
"olddir", &start))) {
return 1;
}
#if 0
if (stat(newlog->oldDir, &sb)) {
message(MESS_ERROR, "%s:%d error verifying olddir "
"path %s: %s\n", configFile, lineNum,
newlog->oldDir, strerror(errno));
return 1;
}
if (!S_ISDIR(sb.st_mode)) {
message(MESS_ERROR, "%s:%d olddir path %s is not a "
"directory\n", configFile, lineNum,
newlog->oldDir);
return 1;
}
#endif
message(MESS_DEBUG, "olddir is now %s\n", newlog->oldDir);
} else if (!strcmp(start, "extension")) {
*endtag = oldchar, start = endtag;
if (!isolateValue(configFile, lineNum, "extension name", &start,
&endtag)) {
oldchar = *endtag, *endtag = '\0';
newlog->extension = strdup(start);
*endtag = oldchar, start = endtag;
}
message(MESS_DEBUG, "extension is now %s\n", newlog->extension);
} else if (!strcmp(start, "compresscmd")) {
*endtag = oldchar, start = endtag;
if (!(newlog->compress_prog = readPath(configFile, lineNum, "compress", &start))) {
return 1;
}
if (access(newlog->compress_prog, X_OK)) {
message(MESS_ERROR, "%s:%d compression program %s is not an executable file\n", configFile, lineNum,
newlog->compress_prog);
return 1;
}
message(MESS_DEBUG, "compress_prog is now %s\n", newlog->compress_prog);
} else if (!strcmp(start, "uncompresscmd")) {
*endtag = oldchar, start = endtag;
if (!(newlog->uncompress_prog = readPath(configFile, lineNum, "uncompress", &start))) {
return 1;
}
if (access(newlog->uncompress_prog, X_OK)) {
message(MESS_ERROR, "%s:%d uncompression program %s is not an executable file\n", configFile, lineNum,
newlog->uncompress_prog);
return 1;
}
message(MESS_DEBUG, "uncompress_prog is now %s\n", newlog->uncompress_prog);
} else if (!strcmp(start, "compressoptions")) {
char * options;
*endtag = oldchar, start = endtag;
if (!(options = readPath(configFile, lineNum, "compressoptions", &start))) {
return 1;
}
if (poptParseArgvString(options,
&newlog->compress_options_count,
&newlog->compress_options_list)) {
message(MESS_ERROR, "%s:%d invalid compression options\n",
configFile, lineNum);
return 1;
}
message(MESS_DEBUG, "compress_options is now %s\n", options);
} else if (!strcmp(start, "compressext")) {
*endtag = oldchar, start = endtag;
if (!(newlog->compress_ext = readPath(configFile, lineNum, "compress-ext", &start))) {
return 1;
}
message(MESS_DEBUG, "compress_ext is now %s\n", newlog->compress_ext);
} else {
message(MESS_ERROR, "%s:%d unknown option '%s' "
"-- ignoring line\n", configFile, lineNum, start);
*endtag = oldchar, start = endtag;
}
while (isblank(*start)) start++;
if (*start != '\n') {
message(MESS_ERROR, "%s:%d unexpected text\n", configFile,
lineNum);
while (*start != '\n') start++;
}
lineNum++;
start++;
} else if (*start == '/' || *start == '"' || *start == '\'') {
if (newlog != defConfig) {
message(MESS_ERROR, "%s:%d unexpected log filename\n",
configFile, lineNum);
return 1;
}
(*numLogsPtr)++;
*logsPtr = realloc(*logsPtr, sizeof(**logsPtr) * *numLogsPtr);
newlog = *logsPtr + *numLogsPtr - 1;
memcpy(newlog, defConfig, sizeof(*newlog));
endtag = start;
while (*endtag != '{' && *endtag != '\0') endtag++;
if (*endtag != '{') {
message(MESS_ERROR, "%s:%d missing end of line\n",
configFile, lineNum);
}
*endtag = '\0';
if (poptParseArgvString(start, &argc, &argv)) {
message(MESS_ERROR, "%s:%d error parsing filename\n",
configFile, lineNum);
return 1;
} else if (argc < 1) {
message(MESS_ERROR, "%s:%d { expected after log file name(s)\n",
configFile, lineNum);
return 1;
}
/* XXX this leaks the result of the glob <shrug> */
newlog->files = NULL;
newlog->numFiles = 0;
for (argNum = 0; argNum < argc; argNum++) {
rc = glob(argv[argNum], GLOB_NOCHECK, globerr, &globResult);
if (rc == GLOB_ABORTED) {
if(newlog->flags & LOG_FLAG_MISSINGOK)
continue;
message(MESS_ERROR, "%s:%d glob failed for %s\n",
configFile, lineNum, argv[argNum]);
return 1;
}
newlog->files = realloc(newlog->files, sizeof(*newlog->files) *
(newlog->numFiles + globResult.gl_pathc));
for (i = 0; i < globResult.gl_pathc; i++) {
/* if we glob directories we can get false matches */
if (!lstat(globResult.gl_pathv[i], &sb) &&
S_ISDIR(sb.st_mode))
continue;
for (j = 0; j < *numLogsPtr - 1; j++) {
for (k = 0; k < (*logsPtr)[j].numFiles; k++) {
if (!strcmp((*logsPtr)[j].files[k],
globResult.gl_pathv[i])) {
message(MESS_ERROR,
"%s:%d duplicate log entry for %s\n",
configFile, lineNum,
globResult.gl_pathv[i]);
return 1;
}
}
}
newlog->files[newlog->numFiles] =
globResult.gl_pathv[i];
newlog->numFiles++;
}
}
newlog->pattern = strdup(start);
message(MESS_DEBUG, "reading config info for %s\n", start);
free(argv);
start = endtag + 1;
} else if (*start == '}') {
if (newlog == defConfig) {
message(MESS_ERROR, "%s:%d unxpected }\n", configFile, lineNum);
return 1;
}
if (newlog->oldDir) {
for (i = 0; i < newlog->numFiles; i++) {
char *ld;
dirName = ourDirName(newlog->files[i]);
if (stat(dirName, &sb2)) {
message(MESS_ERROR, "%s:%d error verifying log file "
"path %s: %s\n", configFile, lineNum,
dirName, strerror(errno));
free(dirName);
return 1;
}
ld = alloca(strlen(dirName) + strlen(newlog->oldDir) + 2);
sprintf(ld, "%s/%s", dirName, newlog->oldDir);
free(dirName);
if(newlog->oldDir[0] != '/') dirName = ld;
else dirName = newlog->oldDir;
if(stat(dirName, &sb)) {
message(MESS_ERROR, "%s:%d error verifying olddir "
"path %s: %s\n", configFile, lineNum,
dirName, strerror(errno));
return 1;
}
if (sb.st_dev != sb2.st_dev) {
message(MESS_ERROR, "%s:%d olddir %s and log file %s "
"are on different devices\n", configFile,
lineNum, newlog->oldDir, newlog->files[i]);
return 1;
}
}
}
newlog = defConfig;
start++;
while (isblank(*start)) start++;
if (*start != '\n') {
message(MESS_ERROR, "%s:%d, unexpected text after {\n",
configFile, lineNum);
}
} else {
message(MESS_ERROR, "%s:%d lines must begin with a keyword "
"or a filename (possibly in double quotes)\n",
configFile, lineNum);
while (*start != '\n') start++;
lineNum++;
start++;
}
}
if(scriptStart) {
message(MESS_ERROR, "%s:prerotate or postrotate without endscript\n",
configFile);
return 1;
}
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1