/*
* sc_sorter
*
* This is a reimplementation of sksorter.cc to use the scamper file API.
*
* $Id: sc_sorter.c,v 1.26 2007/05/14 04:11:02 mjl Exp $
*
* Copyright (C) 2006-2007 The University of Waikato
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, version 2.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#if defined(__APPLE__)
#include <stdint.h>
#endif
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <errno.h>
#if defined(DMALLOC)
#include <dmalloc.h>
#endif
#include "scamper_tlv.h"
#include "scamper_addr.h"
#include "scamper_list.h"
#include "scamper_trace.h"
#include "scamper_ping.h"
#include "scamper_file.h"
#include "utils.h"
#include "mjl_splaytree.h"
static uint32_t options = 0;
static char *prefix = NULL;
static char *infile = NULL;
static splaytree_t *cyclesets = NULL;
static splaytree_t *openfiles = NULL;
static scamper_file_filter_t *filter = NULL;
typedef struct openfile
{
char *filename;
scamper_file_t *file;
int active_cycles;
} openfile_t;
typedef struct cycleset
{
scamper_cycle_t *cycle;
time_t epoch;
openfile_t **segments;
int segment_cnt;
} cycleset_t;
#define OPT_PREFIX 0x00000001 /* P: */
#define OPT_HELP 0x00000002 /* ?: */
static void usage(const char *argv0, uint32_t opt_mask)
{
fprintf(stderr, "usage: %s [-?] [-F prefix] <infile>\n", argv0);
if(opt_mask == 0) return;
fprintf(stderr, "\n");
if(opt_mask & OPT_HELP)
fprintf(stderr, " -? give an overview of the usage of sc_sorter\n");
if(opt_mask & OPT_PREFIX)
fprintf(stderr, " -F root path prefix to use\n");
return;
}
static int check_options(int argc, char *argv[])
{
char ch;
size_t len;
char *opts = "F:?";
char *opt_prefix = NULL;
while((ch = getopt(argc, argv, opts)) != -1)
{
switch(ch)
{
case 'F':
options |= OPT_PREFIX;
opt_prefix = optarg;
break;
case '?':
default:
usage(argv[0], 0xffffffff);
return -1;
}
}
/*
* if a prefix has been specified, then ensure that the string has a
* trailing forward slash
*/
if(options & OPT_PREFIX)
{
if((len = strlen(opt_prefix)) == 0)
{
usage(argv[0], OPT_PREFIX);
return -1;
}
if(opt_prefix[len-1] != '/')
{
if((prefix = malloc(len + 2)) == NULL)
{
return -1;
}
memcpy(prefix, opt_prefix, len);
prefix[len] = '/';
prefix[len+1] = '\0';
}
else
{
if((prefix = strdup(opt_prefix)) == NULL)
{
return -1;
}
}
}
/*
* ensure that an input file is specified
*/
if(argc - optind != 1)
{
usage(argv[0], 0);
return -1;
}
infile = argv[optind];
return 0;
}
/*
* create_filename
*
* this function creates a filename (and full path to it) given a cycle and
* segment value. the filename is created as follows:
*
* <PREFIX>/<LIST>/<MONITOR>/YYYY/MM/<MONITOR>.<LIST>.YYYYMMDD_<SEGMENT>
*/
static char *create_filename(char *filename, size_t len,
scamper_cycle_t *cycle, int segment)
{
struct tm *tm;
time_t tt;
tt = cycle->start_time;
tm = gmtime(&tt);
snprintf(filename, len, "%s%s/%s/%d/%02d/%s.%03d.%d%02d%02d_%03d",
prefix != NULL ? prefix : "",
cycle->list->name,
cycle->list->monitor,
tm->tm_year + 1900,
tm->tm_mon + 1,
cycle->list->monitor,
cycle->list->id,
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
segment);
return filename;
}
/*
* openfile_alloc
*
* allocate a record that allows us to associate a filename with an open
* scamper file, which can then be shared as necessary.
*
* caller is responsible for putting the structure into splaytree.
*/
static openfile_t *openfile_alloc(char *filename)
{
openfile_t *openfile = NULL;
if((openfile = malloc_zero(sizeof(openfile_t))) == NULL)
{
goto err;
}
if((openfile->filename = strdup(filename)) == NULL)
{
goto err;
}
return openfile;
err:
if(openfile != NULL)
{
if(openfile->file != NULL) scamper_file_close(openfile->file);
free(openfile);
}
return NULL;
}
/*
* openfile_close
*
*
*/
static void openfile_close(void *vopenfile)
{
openfile_t *openfile = (openfile_t *)vopenfile;
char a[1024], b[1024];
struct stat sb;
mode_t mode;
/* close the file */
if(openfile->file != NULL)
{
scamper_file_close(openfile->file);
openfile->file = NULL;
}
if(openfile->active_cycles == 0)
{
snprintf(a, sizeof(a), "%s.warts", openfile->filename);
snprintf(b, sizeof(b), "%s.inprogress", openfile->filename);
/* make sure there is no .warts file there currently */
if(stat(a, &sb) != 0 && errno == ENOENT)
{
rename(b, a);
mode = S_IRUSR | S_IRGRP | S_IROTH;
chmod(a, mode);
}
}
if(openfile->filename != NULL) free(openfile->filename);
free(openfile);
return;
}
static int openfile_cmp(const void *va, const void *vb)
{
return strcmp(((const openfile_t *)va)->filename,
((const openfile_t *)vb)->filename);
}
/*
* file_open
*
* open a warts file for appending, using the .inprogress extension. if
* an .inprogress file does not exist, but a .warts one does, then rename
* the .warts file to .inprogress and open that.
*/
static scamper_file_t *file_open(char *filename)
{
struct stat sb;
char a[1024], b[1024];
int rv;
/* if the .inprogress file exists, use that */
snprintf(a, sizeof(a), "%s.inprogress", filename);
if(stat(a, &sb) == 0)
{
return scamper_file_open(a, 'a', "warts");
}
else if(errno != ENOENT)
{
return NULL;
}
/*
* if the .inprogress file does not exist, check if one ending in .warts
* does. if one does exist, then rename it to .inprogress and open it.
*/
snprintf(b, sizeof(b), "%s.warts", filename);
if((rv = stat(b, &sb)) == 0 || errno == ENOENT)
{
if(rv == 0 && (rename(b, a) != 0 || chmod(a, S_IRUSR | S_IWUSR) != 0))
{
return NULL;
}
return scamper_file_open(a, 'a', "warts");
}
return NULL;
}
/*
* openfile_get
*
* return an openfile structure corresponding to the segment number for
* this cycle's cycleset.
*/
static openfile_t *openfile_get(cycleset_t *cycleset, time_t sec)
{
openfile_t **segments, findme, *openfile = NULL;
int segment;
int segment_cnt;
char *delim, filename[1024];
/*
* if the timestamp is before the epoch, just write the record into
* segment zero.
*/
if(sec <= cycleset->epoch)
{
segment = 0;
}
else
{
segment = (sec - cycleset->epoch) / 86400;
}
/* if we need to increase the size of the segment table, then do so */
if(cycleset->segment_cnt <= (segment_cnt = segment+1))
{
segments = (openfile_t **)realloc(cycleset->segments,
segment_cnt * sizeof(openfile_t *));
if(segments == NULL)
{
return NULL;
}
cycleset->segments = segments;
memset(cycleset->segments + cycleset->segment_cnt,
0, (segment_cnt - cycleset->segment_cnt) * sizeof(openfile_t *));
cycleset->segment_cnt = segment_cnt;
}
/*
* if there is no open file recorded for this segment, then try and
* acquire one
*/
if(cycleset->segments[segment] == NULL)
{
/* this is the full path and name of file we'll be opening */
create_filename(filename, sizeof(filename), cycleset->cycle, segment);
/* see if we already have the file open */
findme.filename = filename;
if((openfile = splaytree_find(openfiles, &findme)) != NULL)
{
cycleset->segments[segment] = openfile;
return openfile;
}
/*
* get a pointer to the last / in the filename, and ensure the
* full directory path has been created.
*/
delim = string_lastof_char(filename, '/');
*delim = '\0';
if(mkdir_wrap(filename, 0755) == -1)
{
goto err;
}
*delim = '/';
/*
* allocate an 'openfile' structure that wraps the filename and points
* to an open scamper_file for it
*/
if((openfile = openfile_alloc(filename)) == NULL)
{
goto err;
}
/* save the record of this open file in the splaytree */
if(splaytree_insert(openfiles, openfile) == NULL)
{
goto err;
}
/* now actually try and open the file */
if((openfile->file = file_open(filename)) == NULL)
{
perror("could not open file");
goto err;
}
cycleset->segments[segment] = openfile;
}
return cycleset->segments[segment];
err:
if(openfile != NULL) openfile_close(openfile);
return NULL;
}
/*
* cycleset_alloc
*
* allocate a cycleset structure, and determine the 24-hour epoch for the
* cycle
*/
static cycleset_t *cycleset_alloc(scamper_cycle_t *cycle)
{
cycleset_t *cycleset;
struct tm tm;
time_t tt;
if((cycleset = malloc_zero(sizeof(cycleset_t))) == NULL)
{
return NULL;
}
cycleset->cycle = scamper_cycle_use(cycle);
/* determine the 24-hour epoch for the cycle */
tt = cycle->start_time;
memcpy(&tm, gmtime(&tt), sizeof(tm));
cycleset->epoch = tt - (3600 * tm.tm_hour) - (60 * tm.tm_min) - tm.tm_sec;
return cycleset;
}
static int cycleset_cmp(const void *va, const void *vb)
{
const cycleset_t *a = (const cycleset_t *)va;
const cycleset_t *b = (const cycleset_t *)vb;
if(a->cycle < b->cycle) return -1;
if(a->cycle > b->cycle) return 1;
return 0;
}
static void cycleset_free(cycleset_t *cycleset)
{
if(cycleset == NULL)
{
return;
}
/* free our reference of the cycle */
if(cycleset->cycle != NULL)
{
scamper_cycle_free(cycleset->cycle);
}
/* free any open files related to this cycleset */
if(cycleset->segments != NULL)
{
free(cycleset->segments);
}
free(cycleset);
return;
}
static cycleset_t *cycleset_get(scamper_cycle_t *cycle)
{
cycleset_t findme, *cycleset;
findme.cycle = cycle;
if((cycleset = splaytree_find(cyclesets, &findme)) == NULL)
{
if((cycleset = cycleset_alloc(cycle)) == NULL)
{
return NULL;
}
if(splaytree_insert(cyclesets, cycleset) == NULL)
{
cycleset_free(cycleset);
return NULL;
}
}
return cycleset;
}
/*
* cycle_openfile_get
*
* given a cycle and a time since the cycle start point, return an openfile
* record.
*/
static openfile_t *cycle_openfile_get(scamper_cycle_t *cycle, time_t sec)
{
cycleset_t *cycleset;
/* first, get the corresponding cycleset */
if((cycleset = cycleset_get(cycle)) == NULL)
{
return NULL;
}
/* second, get the actual output file for this segment */
return openfile_get(cycleset, sec);
}
/*
* handle_trace
*
* write the trace to the appropriate output file.
*/
static int handle_trace(scamper_trace_t *trace)
{
openfile_t *openfile;
int rc = 0;
/* skip over traces without a cycle attached */
if(trace->cycle == NULL)
{
goto done;
}
if((openfile = cycle_openfile_get(trace->cycle,trace->start.tv_sec)) == NULL)
{
rc = -1; goto done;
}
if(scamper_file_write_trace(openfile->file, trace) == -1)
{
rc = -1; goto done;
}
done:
scamper_trace_free(trace);
return rc;
}
/*
* handle_ping
*
* write the ping object to the appropriate output file.
*/
static int handle_ping(scamper_ping_t *ping)
{
openfile_t *openfile;
int rc = 0;
/* skip over traces without a cycle attached */
if(ping->cycle == NULL)
{
goto done;
}
if((openfile = cycle_openfile_get(ping->cycle, ping->start.tv_sec)) == NULL)
{
rc = -1; goto done;
}
if(scamper_file_write_ping(openfile->file, ping) == -1)
{
rc = -1; goto done;
}
done:
scamper_ping_free(ping);
return rc;
}
/*
* handle_cycle_start
*
* write the start record to the output file. since this is a newly active
* cycle, increment the active cycles parameter for the openfile.
*/
static int handle_cycle_start(scamper_cycle_t *cycle)
{
openfile_t *openfile;
int rc = 0;
if((openfile = cycle_openfile_get(cycle, cycle->start_time)) == NULL)
{
rc = -1; goto done;
}
if(scamper_file_write_cycle_start(openfile->file, cycle) == -1)
{
rc = -1; goto done;
}
openfile->active_cycles++;
done:
scamper_cycle_free(cycle);
return rc;
}
/*
* handle_cycle_stop
*
* a cycle has concluded. write the cycle_stop record, and reduce the
* active-cycles count.
*/
static int handle_cycle_stop(scamper_cycle_t *cycle)
{
openfile_t *openfile;
int rc = 0;
if((openfile = cycle_openfile_get(cycle, cycle->stop_time)) == NULL)
{
rc = -1; goto done;
}
if(scamper_file_write_cycle_stop(openfile->file, cycle) == -1)
{
rc = -1; goto done;
}
openfile->active_cycles--;
done:
scamper_cycle_free(cycle);
return rc;
}
/*
* handle_cycle_def
*
* a cycle definition in an input file probably means that a cycle was
* defined, but that the current input file was first used in the middle
* of the cycle. the only thing sc_sorter has to do is increment the
* number of known active cycles for the openfile.
*/
static int handle_cycle_def(scamper_cycle_t *cycle)
{
openfile_t *openfile;
int rc = 0;
if((openfile = cycle_openfile_get(cycle, cycle->start_time)) == NULL)
{
rc = -1; goto done;
}
openfile->active_cycles++;
done:
scamper_cycle_free(cycle);
return rc;
}
static void cleanup(void)
{
if(prefix != NULL)
{
free(prefix);
prefix = NULL;
}
if(filter != NULL)
{
scamper_file_filter_free(filter);
filter = NULL;
}
if(cyclesets != NULL)
{
splaytree_free(cyclesets, (splaytree_free_t)cycleset_free);
cyclesets = NULL;
}
if(openfiles != NULL)
{
splaytree_free(openfiles, NULL);
openfiles = NULL;
}
return;
}
int main(int argc, char *argv[])
{
uint16_t filter_types[] = {
SCAMPER_FILE_OBJ_TRACE,
SCAMPER_FILE_OBJ_PING,
SCAMPER_FILE_OBJ_CYCLE_START,
SCAMPER_FILE_OBJ_CYCLE_STOP,
SCAMPER_FILE_OBJ_CYCLE_DEF,
};
uint16_t filter_cnt = sizeof(filter_types)/sizeof(uint16_t);
scamper_file_t *file;
void *data;
uint16_t type;
#if defined(DMALLOC)
free(malloc(1));
#endif
atexit(cleanup);
if(check_options(argc, argv) == -1)
{
return -1;
}
if((cyclesets = splaytree_alloc(cycleset_cmp)) == NULL)
{
return -1;
}
if((openfiles = splaytree_alloc(openfile_cmp)) == NULL)
{
return -1;
}
if((file = scamper_file_open(infile, 'r', NULL)) == NULL)
{
usage(argv[0], 0);
fprintf(stderr, "could not open infile %s\n", infile);
return -1;
}
if((filter = scamper_file_filter_alloc(filter_types, filter_cnt)) == NULL)
{
fprintf(stderr, "could not allocate filter\n");
return -1;
}
while(scamper_file_read(file, filter, &type, &data) == 0)
{
if(data == NULL)
{
/* EOF */
break;
}
if(type == SCAMPER_FILE_OBJ_TRACE)
{
if(handle_trace(data) != 0)
{
return -1;
}
}
else if(type == SCAMPER_FILE_OBJ_PING)
{
if(handle_ping(data) != 0)
{
return -1;
}
}
else if(type == SCAMPER_FILE_OBJ_CYCLE_START)
{
if(handle_cycle_start(data) != 0)
{
return -1;
}
}
else if(type == SCAMPER_FILE_OBJ_CYCLE_STOP)
{
if(handle_cycle_stop(data) != 0)
{
return -1;
}
}
else if(type == SCAMPER_FILE_OBJ_CYCLE_DEF)
{
if(handle_cycle_def(data) != 0)
{
return -1;
}
}
}
scamper_file_close(file);
/* close all open files */
splaytree_free(openfiles, openfile_close);
openfiles = NULL;
return 0;
}
syntax highlighted by Code2HTML, v. 0.9.1