/* ** Copyright (C) 2001-2007 by Carnegie Mellon University. ** ** @OPENSOURCE_HEADER_START@ ** ** Use of the SILK system and related source code is subject to the terms ** of the following licenses: ** ** GNU Public License (GPL) Rights pursuant to Version 2, June 1991 ** Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013 ** ** NO WARRANTY ** ** ANY INFORMATION, MATERIALS, SERVICES, INTELLECTUAL PROPERTY OR OTHER ** PROPERTY OR RIGHTS GRANTED OR PROVIDED BY CARNEGIE MELLON UNIVERSITY ** PURSUANT TO THIS LICENSE (HEREINAFTER THE "DELIVERABLES") ARE ON AN ** "AS-IS" BASIS. CARNEGIE MELLON UNIVERSITY MAKES NO WARRANTIES OF ANY ** KIND, EITHER EXPRESS OR IMPLIED AS TO ANY MATTER INCLUDING, BUT NOT ** LIMITED TO, WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE, ** MERCHANTABILITY, INFORMATIONAL CONTENT, NONINFRINGEMENT, OR ERROR-FREE ** OPERATION. CARNEGIE MELLON UNIVERSITY SHALL NOT BE LIABLE FOR INDIRECT, ** SPECIAL OR CONSEQUENTIAL DAMAGES, SUCH AS LOSS OF PROFITS OR INABILITY ** TO USE SAID INTELLECTUAL PROPERTY, UNDER THIS LICENSE, REGARDLESS OF ** WHETHER SUCH PARTY WAS AWARE OF THE POSSIBILITY OF SUCH DAMAGES. ** LICENSEE AGREES THAT IT WILL NOT MAKE ANY WARRANTY ON BEHALF OF ** CARNEGIE MELLON UNIVERSITY, EXPRESS OR IMPLIED, TO ANY PERSON ** CONCERNING THE APPLICATION OF OR THE RESULTS TO BE OBTAINED WITH THE ** DELIVERABLES UNDER THIS LICENSE. ** ** Licensee hereby agrees to defend, indemnify, and hold harmless Carnegie ** Mellon University, its trustees, officers, employees, and agents from ** all claims or demands made against them (and any related losses, ** expenses, or attorney's fees) arising out of, or relating to Licensee's ** and/or its sub licensees' negligent use or willful misuse of or ** negligent conduct or willful misconduct regarding the Software, ** facilities, or other rights or assistance granted by Carnegie Mellon ** University under this License, including, but not limited to, any ** claims of product liability, personal injury, death, damage to ** property, or violation of any laws or regulations. ** ** Carnegie Mellon University Software Engineering Institute authored ** documents are sponsored by the U.S. Department of Defense under ** Contract F19628-00-C-0003. Carnegie Mellon University retains ** copyrights in all material produced under this contract. The U.S. ** Government retains a non-exclusive, royalty-free license to publish or ** reproduce these documents, or allow others to do so, for U.S. ** Government purposes only pursuant to the copyright license under the ** contract clause at 252.227.7013. ** ** @OPENSOURCE_HEADER_END@ */ /* ** rwcountutils.c ** ** utility routines for rwcount.c ** ** Michael P. Collins ** */ #include "silk.h" RCSIDENT("$SiLK: rwcountutils.c 8269 2007-08-03 18:54:48Z mthomas $"); #include "rwcount.h" /* LOCAL DEFINES AND TYPEDEFS */ /* where to send --help output */ #define USAGE_FH stdout /* LOCAL FUNCTIONS */ static void appUsageLong(void); static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg); /* LOCAL VARIABLES */ /* whether to use legacy (1), new-style (0), or default (-1) timestamps */ static int legacy_timestamps = -1; /* OPTIONS SETUP */ typedef enum { OPT_BIN_SIZE, OPT_LOAD_SCHEME, OPT_SKIP_ZEROES, OPT_EPOCH_SLOTS, OPT_BIN_SLOTS, OPT_START_EPOCH, OPT_NO_TITLES, OPT_NO_COLUMNS, OPT_COLUMN_SEPARATOR, OPT_DELIMITED, OPT_PRINT_FILENAMES, OPT_COPY_INPUT, OPT_OUTPUT_PATH, OPT_PAGER, OPT_LEGACY_TIMESTAMPS } appOptionsEnum; static struct option appOptions[] = { /* Size of the bins in seconds */ {"bin-size", REQUIRED_ARG, 0, OPT_BIN_SIZE}, /* Bin splitting scheme */ {"load-scheme", REQUIRED_ARG, 0, OPT_LOAD_SCHEME}, {"skip-zeroes", NO_ARG, 0, OPT_SKIP_ZEROES}, {"epoch-slots", NO_ARG, 0, OPT_EPOCH_SLOTS}, {"bin-slots", NO_ARG, 0, OPT_BIN_SLOTS}, {"start-epoch", REQUIRED_ARG, 0, OPT_START_EPOCH}, {"no-titles", NO_ARG, 0, OPT_NO_TITLES}, {"no-columns", NO_ARG, 0, OPT_NO_COLUMNS}, {"column-separator", REQUIRED_ARG, 0, OPT_COLUMN_SEPARATOR}, {"delimited", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"print-filenames", NO_ARG, 0, OPT_PRINT_FILENAMES}, {"copy-input", REQUIRED_ARG, 0, OPT_COPY_INPUT}, {"output-path", REQUIRED_ARG, 0, OPT_OUTPUT_PATH}, {"pager", REQUIRED_ARG, 0, OPT_PAGER}, {"legacy-timestamps", OPTIONAL_ARG, 0, OPT_LEGACY_TIMESTAMPS}, {0,0,0,0} /* sentinel entry */ }; static const char *appHelp[] = { "Size of bins in seconds. Def. 30 seconds", ("Defines bin loading, options are:\n" "\t0: split volume evenly across the BINS the record covers\n" "\t1: fill FIRST appropriate bin with complete volume\n" "\t2: fill LAST appropriate bin with complete volume\n" "\t3: fill CENTERMOST bin with complete volume\n" "\t4: split volume into bins proportional to time ACTIVE in bin [Def]"), "Do not print bins that have no flows. Def. Print all", "Print bin labels using epoch time. Def. Human readable", "Print bin labels using the internal bin index. Def. No", "Print bins from this time forward. Def. First nonzero bin", "Do not print column titles. Def. Print titles", "Disable fixed-width columnar output. Def. Columnar", "Use specified character between columns. Def. '|'", "Shortcut for --no-columns --column-sep=CHAR", "Print names of input files as they are opened. Def. No", "Copy all input SiLK Flows to given pipe or file. Def. No", "Send output to given file path. Def. stdout", "Program to invoke to page output. Def. $SILK_PAGER or $PAGER", ("Timestamp format. Choices:\n" "\t0==(new)\"YYYY/MM/DDThh:mm:ss\"; 1==(legacy)\"MM/DD/YYYY hh:mm:ss\".\n" "\tDef. " #ifdef SK_ENABLE_LEGACY_TIMESTAMP "1" #else "0" #endif " when switch not provided; 1 when switch provided with no value"), (char *)NULL }; /* * DO NOT PRINT THE FOLLOWING * * These let --delimeted replace --delimiter, without forcing the user * to fully type the switch name; and therefore will not breaking any * existing scripts or finger memory. */ static struct option legacyOptions[] = { {"d", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"de", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"del", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"deli", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"delim", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"delimi", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"delimit", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"delimite", OPTIONAL_ARG, 0, OPT_DELIMITED}, {"delimiter", REQUIRED_ARG, 0, OPT_COLUMN_SEPARATOR}, {0,0,0,0} /* sentinel entry */ }; /* FUNCTION DEFINITIONS */ /* * appUsageLong(); * * Print complete usage information to USAGE_FH. Pass this * function to skOptionsSetUsageCallback(); optionsParse() will * call this funciton and then exit the program when the --help * option is given. */ static void appUsageLong(void) { #define USAGE_MSG \ ("[SWITCHES] [FILES]\n" \ "\tSummarize SiLK Flow records across time, producing textual output\n" \ "\twith counts of bytes, packets, and flow records for each time bin.\n" \ "\tWhen no files given on command line, flows are read from STDIN.\n") FILE *fh = USAGE_FH; skAppStandardUsage(fh, USAGE_MSG, appOptions, appHelp); sksiteOptionsUsage(fh); } /* * appTeardown() * * Teardown all modules, close all files, and tidy up all * application state. * * This function is idempotent. */ void appTeardown(void) { static uint8_t teardownFlag = 0; if (teardownFlag) { return; } teardownFlag = 1; /* close the output-path */ if (ioISP->passCount && ioISP->passFD[0] && ioISP->passFD[0] != stdout) { if (ioISP->passIsPipe[0]) { if (-1 == pclose(ioISP->passFD[0])) { skAppPrintErr("Error closing output pipe %s", ioISP->passFPath[0]); } } else { if (EOF == fclose(ioISP->passFD[0])) { skAppPrintSyserror("Error closing output file %s", ioISP->passFPath[0]); } } ioISP->passFD[0] = NULL; } iochecksTeardown(ioISP); if (rwIOS) { rwCloseFile(rwIOS); } rwIOS = NULL; /* free our memory */ if (countData.fileHeader) { free(countData.fileHeader); } if (countData.bins) { free(countData.bins); } skAppUnregister(); } /* * appSetup(argc, argv); * * Perform all the setup for this application include setting up * required modules, parsing options, etc. This function should be * passed the same arguments that were passed into main(). * * Returns to the caller if all setup succeeds. If anything fails, * this function will cause the application to exit with a FAILURE * exit status. */ void appSetup(int argc, char **argv) { /* make sure count of option's declarations and help-strings match */ assert((sizeof(appOptions)/sizeof(struct option)) == (sizeof(appHelp)/sizeof(char *))); /* register the application */ skAppRegister(argv[0]); skOptionsSetUsageCallback(&appUsageLong); /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL) || optionsRegister(legacyOptions, (optHandler)appOptionsHandler, NULL) || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE)) { skAppPrintErr("unable to register options"); exit(EXIT_FAILURE); } ioISP = iochecksSetup(1, 0, argc, argv); /* parse options; print usage if error */ ioISP->firstFile = optionsParse(argc, argv); if (ioISP->firstFile < 0) { skAppUsage(); } /* try to load site config file; if it fails, we will not be able * to resolve flowtype and sensor from input file names */ sksiteConfigure(0); /* Use a particular timestamp if requested */ if (legacy_timestamps == 0) { sktimestampSetMode(SK_TIMESTAMP_YYYYMMDD, SK_TIME_FRACSEC_OFF); } else if (legacy_timestamps == 1) { sktimestampSetMode(SK_TIMESTAMP_MMDDYYYY, SK_TIME_FRACSEC_OFF); } else { sktimestampSetMode(SK_TIMESTAMP_DEFAULT, SK_TIME_FRACSEC_OFF); } /* Use STDIN as an input stream if it is not a TTY; make certain * we have some input and we are either reading from STDIN or * using files listed the command line, but not both. */ if (iochecksAcceptFromStdin(ioISP) || iochecksInputs(ioISP, 0)) { skAppUsage(); } /* if no destination was specified, use stdout */ if ((0 == ioISP->passCount) && iochecksPassDestinations(ioISP, "stdout", 1)) { exit(EXIT_FAILURE); } /* looks good, open the --copy-input destination */ if (iochecksOpenCopyDest(ioISP)) { exit(EXIT_FAILURE); } if (atexit(appTeardown) < 0) { skAppPrintErr("unable to register appTeardown() with atexit()"); appTeardown(); exit(EXIT_FAILURE); } return; /* OK */ } /* * status = appOptionsHandler(cData, opt_index, opt_arg); * * Called by optionsParse(), this handles a user-specified switch * that the application has registered, typically by setting global * variables. Returns 1 if the switch processing failed or 0 if it * succeeded. Returning a non-zero from from the handler causes * optionsParse() to return a negative value. * * The clientData in 'cData' is typically ignored; 'opt_index' is * the index number that was specified as the last value for each * struct option in appOptions[]; 'opt_arg' is the user's argument * to the switch for options that have a REQUIRED_ARG or an * OPTIONAL_ARG. */ static int appOptionsHandler( clientData UNUSED(cData), int opt_index, char *opt_arg) { uint32_t opt_value; struct timeval opt_time; switch ((appOptionsEnum)opt_index) { case OPT_LOAD_SCHEME: if (skStringParseUint32(&opt_value, opt_arg, 0, MAX_LOAD_SCHEME)) { skAppPrintErr(("unrecognized %s value '%s'." " Choose a value from 0 to %u"), appOptions[opt_index].name, opt_arg, (unsigned int)MAX_LOAD_SCHEME); return 1; } countData.fileHeader->bin_load_scheme = opt_value; break; case OPT_BIN_SIZE: if (0 != skStringParseUint32(&opt_value, opt_arg, 1, 0)) { skAppPrintErr("Invalid value for %s: '%s'.", appOptions[opt_index].name, opt_arg); return 1; } countData.fileHeader->binSize = opt_value; break; case OPT_SKIP_ZEROES: countData.fileHeader->skip_zeroes = 1; break; case OPT_NO_TITLES: countData.fileHeader->no_titles = 1; break; case OPT_NO_COLUMNS: countData.fileHeader->no_columns = 1; break; case OPT_COLUMN_SEPARATOR: countData.fileHeader->delimiter = opt_arg[0]; break; case OPT_DELIMITED: if (opt_arg) { countData.fileHeader->delimiter = opt_arg[0]; } countData.fileHeader->no_columns = 1; break; case OPT_PRINT_FILENAMES: printFNameFlag = 1; break; case OPT_EPOCH_SLOTS: countData.fileHeader->bin_label_flag = BIN_LABEL_EPOCH; break; case OPT_BIN_SLOTS: countData.fileHeader->bin_label_flag = BIN_LABEL_INDEX; break; case OPT_START_EPOCH: /* try to parse as epoch time; if that fails try as YYYY/MM/DD */ if (0 == skStringParseUint32(&opt_value, opt_arg, 1, 0)) { countData.fileHeader->start_epoch = opt_value; } else if (0 == skStringParseDatetime(&opt_time, opt_arg, NULL)) { countData.fileHeader->start_epoch = (uint32_t)opt_time.tv_sec; } else { skAppPrintErr("Invalid value for %s: '%s'.", appOptions[opt_index].name, opt_arg); return 1; } break; case OPT_COPY_INPUT: if (iochecksAllDestinations(ioISP, opt_arg)) { return 1; } break; case OPT_OUTPUT_PATH: if (iochecksPassDestinations(ioISP, opt_arg, 1)) { return 1; } break; case OPT_PAGER: pager = opt_arg; break; case OPT_LEGACY_TIMESTAMPS: if (!opt_arg || !opt_arg[0]) { legacy_timestamps = 1; } else if (opt_arg[0] == '1') { legacy_timestamps = 1; } else if (opt_arg[0] == '0') { legacy_timestamps = 0; } else { skAppPrintErr("Invalid value for %s: '%s'.", appOptions[opt_index].name, opt_arg); return 1; } break; } return 0; /* OK */ } /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */