I didn’t want to lose track of this code. It’s not anything special, but it’s useful and rotates log files the way I like them to be rotated.
Hopefully, while converting the code to HTML, I didn’t erase any critical characters.
/*
* rotate.c
*
* Rotates files based on date only. Similar to logrotate on OpenBSD.
*
* Usage:
* rotate [-t | --test] -n <file_base_name> -d <file_directory> [-j <days>]
*
* -t test mode
* -n file base name (something like "imi.log.")
* -d file directory to search for log files
* -j number of days a file can exist before being cleaned up (def. 7)
*
* Erases all files older than <days> that have the base name.
*
* Problems: It's not written securely (aka, vulnerable to buffer overflows).
* Command-line handling is primitive--replace it with GNU's.
*
* Author: Chris Jones, Cardinal Solutions <c j o n e s a t c a r d i n a l s o l u t i o n s d o t c o m>
* Date: September 7, 2004
*/
#include <dirent.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>
void print_usage(char *);
int main(int argc, char * argv[])
{
struct stat filestat;
time_t time_then;
struct dirent * filedir;
DIR * directory;
char * logname;
char * logdirname;
char * dirsep = "/";
char * filename = NULL;
size_t sz_filename;
int i;
int test = 0;
int days = 7;
int d_found = 0;
int n_found = 0;
// limit log file name and log directory name to 256 characters
logname = malloc(256);
if (logname == NULL)
{
perror("Couldn't allocate logname");
}
logdirname = malloc(256);
if (logdirname == NULL)
{
perror("Couldn't allocate logdirname");
}
// process command line switches
for (i = 1; i < argc; i++)
{
if (strcmp("-d", argv[i]) == 0)
{
// set the log directory name
strcpy(logdirname, argv[++i]);
d_found = 1;
}
else if (strcmp("-n", argv[i]) == 0)
{
// set the log file name
strcpy(logname, argv[++i]);
n_found = 1;
}
else if (strcmp("-j", argv[i]) == 0)
{
// set the number of days
days = atoi(argv[++i]);
}
else if (strcmp("-t", argv[i]) == 0)
{
test = 1;
}
else if (strcmp("--test", argv[i]) == 0)
{
test = 1;
}
else
{
print_usage(argv[0]);
}
}
if (!n_found || !d_found)
{
print_usage(argv[0]);
}
if (test)
{
printf("Checking directory %s for files older than %d days that start with '%s'.\n", logdirname, days, logname);
}
// allocate the file directory structure space
filedir = malloc(sizeof(struct dirent));
if (filedir == NULL)
{
perror("Can't malloc() filedir");
exit(3);
}
// compute the comparison date for log files
time_then = time(NULL) - (time_t)(60 * 60 * 24 * days);
// open the log file directory
if ((directory = opendir(logdirname)) == NULL)
{
perror("Problem opening logfile directory");
exit(1);
}
// get each file in the log file directory
while ((filedir = readdir(directory)) != NULL) {
// only operate on files that start with log file name
if (strncmp(filedir->d_name, logname, sizeof(logname)) == 0) {
// compute the size of the filename
sz_filename = sizeof(logdirname) + sizeof(dirsep) +
sizeof(filedir->d_name);
// reallocate space for the filename
filename = (char *)realloc(filename, sz_filename);
if (filename == NULL)
{
perror("realloc() failed");
exit(3);
}
// null it out--probably not necessary, but safer
for (i = 0; i < sz_filename; i++)
{
filename[i] = '\0';
}
// build the path of the file to examine
strcpy(filename, logdirname);
strcat(filename, dirsep);
strcat(filename, filedir->d_name);
// check each file for the changed date
if (stat(filename, &filestat) == 0)
{
// if the date is older than time_then...
if (filestat.st_mtime < time_then)
{
// delete the file
if (test)
{
printf("Test: Deleting %s\n", filename);
}
else
{
// really delete it
if (unlink(filename) != 0)
{
perror("log file delete failed");
exit(4);
}
}
}
}
else
{
perror("stat() failed");
exit(2);
}
}
// get the next directory entry
seekdir(directory, telldir(directory));
}
// done looping through the directory scructure
if (errno != 0) {
perror("Dirscan done");
}
// free up the memory we used
if (filename != NULL)
{
free(filename);
}
if (logname != NULL)
{
free(logname);
}
if (logdirname != NULL)
{
free(logdirname);
}
// close the directory
if (closedir(directory) != 0)
{
perror("closedir() failed");
exit(1);
}
exit(0);
}
void print_usage(char * name)
{
printf("Usage: %s [-t | --test ] [[ -h | --help ] | \n -n <base_file_name> -d <file_directory> [-j <days>]]\n", name);
exit(1);
}

May 1st, 2006 at 1:23 pm
[...] A co-worker using my simple log rotation program for his project had an odd problem: files weren’t being deleted, even though we had both tested the configuration and had seen the expected behavior. Of course, this was happening through a cron job that sends output to /dev/null, so we had no diagnostic messages, and the ever-helpful sysadmin removed the logfiles from the test environment before we could check them. [...]