Mischiefblog
I WATN 2 MAEK GAEM!

rotate.c

Posted by Chris Jones
On April 18th, 2005 at 10:26

Permalink | Trackback | Links In |

Comments (1) |
Posted in Tech, Work

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);
}

One Response to “rotate.c”

  1. Mischiefblog » Blog Archive » stat(2) and 2GB+ filesizes Says:

    [...] 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. [...]