/* ** 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@ */ /* ** ** rwappend.c ** ** Suresh L Konda ** 8/10/2002 ** Append f2..fn to f1. */ #include "silk.h" RCSIDENT("$SiLK: rwappend.c 6791 2007-04-04 18:10:34Z mthomas $"); #include "rwpack.h" #include "utils.h" #include "sksite.h" /* LOCAL DEFINES AND TYPEDEFS */ /* where to write output from --help */ #define USAGE_FH stdout /* where to write output from --print-stat */ #define STATISTICS_FH stderr /* LOCAL VARIABLES */ /* file to append to */ static rwIOStruct *output_ios = NULL; /* whether to create the output_ios file if it does not exist */ static int allow_create = 0; /* if creating the output_ios file, this is the name of the file to * use as the template for the new file. */ static const char *create_format = NULL; /* whether to print the statistics */ static int print_statistics = 0; /* index into argv[]; used to loop over filenames */ static int arg_index; /* OPTIONS SETUP */ typedef enum { OPT_CREATE, OPT_PRINT_STATISTICS } appOptionsEnum; static struct option appOptions[] = { {"create", OPTIONAL_ARG, 0, OPT_CREATE}, {"print-statistics", OPTIONAL_ARG, 0, OPT_PRINT_STATISTICS}, {0,0,0,0} /* sentinel entry */ }; static const char *appHelp[] = { ("Create the TARGET-FILE if it does not exist. Uses the\n" "\toptional SiLK file argument to determine the format of TARGET-FILE.\n" "\tDef. Exit when TARGET-FILE nonexistent; use default format"), ("Print count of records read from each SOURCE-FILE\n" "\tand total records added to the TARGET-FILE. Def. No"), (char *)NULL }; /* LOCAL FUNCTION DECLARATIONS */ static void appUsageLong(void); /* never returns */ static void appTeardown(void); static void appSetup(int argc, char **argv); /* never returns when error */ static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg); static int createFromTemplate(const char *new_path, const char *template); /* 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] TARGET-FILE SOURCE-FILE1 [SOURCE-FILE2...]\n" \ "\tAppend the SiLK Flow records contained in the second through\n" \ "\tfinal filename arguments to the records contained in the\n" \ "\tfirst filename argument. All files must be SiLK flow files;\n" \ "\tthe TARGET-FILE must not be compressed.\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. */ static void appTeardown(void) { static int teardownFlag = 0; int rv; if (teardownFlag) { return; } teardownFlag = 1; /* close appended-to file */ if (output_ios) { rv = rwioDestroy(&output_ios); if (rv) { rwioPrintLastErr(output_ios, rv, &skAppPrintErr); } } 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. */ static void appSetup(int argc, char **argv) { const char *output_path; int did_create = 0; int rv; /* verify same number of options and help strings */ assert((sizeof(appHelp)/sizeof(char *)) == (sizeof(appOptions)/sizeof(struct option))); /* register the application */ skAppRegister(argv[0]); skOptionsSetUsageCallback(&appUsageLong); /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL) || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE)) { skAppPrintErr("unable to register options"); exit(EXIT_FAILURE); } /* parse options */ arg_index = optionsParse(argc, argv); if (arg_index < 0) { skAppUsage(); /* never returns */ } /* 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); /* get the output file */ if (argc == arg_index) { skAppPrintErr("Missing name of TARGET-FILE"); skAppUsage(); /* never returns */ } /* get the target file */ output_path = argv[arg_index]; ++arg_index; /* If the target does not exist, complain or create it. */ if ( !fileExists(output_path)) { if (0 == allow_create) { skAppPrintErr(("Target file '%s' does not exist" " and --%s not specified"), output_path, appOptions[OPT_CREATE].name); exit(EXIT_FAILURE); } did_create = 1; if (createFromTemplate(output_path, create_format)) { exit(EXIT_FAILURE); } } /* open the target file for append */ if ((rv = rwioCreate(&output_ios, output_path, SK_RWIO_APPEND)) || (rv = rwioOpen(output_ios))) { rwioPrintLastErr(output_ios, rv, &skAppPrintErr); rwioDestroy(&output_ios); if (did_create) { /* delete the target file if we created it but cannot open * it as a SiLK flow file. */ unlink(output_path); } exit(EXIT_FAILURE); } if (atexit(appTeardown) < 0) { skAppPrintErr("registering appTeardown() failed"); appTeardown(); exit(EXIT_FAILURE); } return; /* OK */ } /* * status = appOptionsHandler(cData, opt_index, opt_arg); * * This function is passed to optionsRegister(); it will be called * by optionsParse() for each user-specified switch that the * application has registered; it should handle the switch as * required---typically by setting global variables---and return 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) { switch ((appOptionsEnum)opt_index) { case OPT_CREATE: allow_create = 1; if (opt_arg) { if ( !fileExists(opt_arg)) { skAppPrintErr("filename argument to --%s does not exist: %s", appOptions[opt_index].name, opt_arg); return 1; } create_format = opt_arg; } break; case OPT_PRINT_STATISTICS: print_statistics = 1; break; } return 0; } /* * status = createFromTemplate(new_path, template); * * Create a SiLK flow file at 'new_path'. It should have the same * format, version, and byte order as 'template'. If 'template' is * NULL, create 'new_file' in the default format. Return 0 on * success; non-zero otherwise. */ static int createFromTemplate(const char *new_path, const char *template) { rwIOStruct *new_ios = NULL; rwIOStruct *ios = NULL; int rv, rv_temp; /* open the target file for write---this will create the file */ rv = rwioCreate(&new_ios, new_path, SK_RWIO_WRITE); if (rv != LIBRW_OK) { goto END; } /* set file attributes based on the template if given */ if (template) { /* open the template file */ if ((rv_temp = rwioCreate(&ios, template, SK_RWIO_READ)) || (rv_temp = rwioOpen(ios))) { rwioPrintLastErr(ios, rv_temp, &skAppPrintErr); skAppPrintErr("error: cannot open template file '%s'", template); rwioDestroy(&ios); rwioDestroy(&new_ios); return 1; } rv = rwioSetHeaderFromFile(new_ios, ios); if (rv != LIBRW_OK) { goto END; } } /* open the target file, write the header, then close it */ if ((rv = rwioOpen(new_ios)) || (rv = rwioWriteHeader(new_ios)) || (rv = rwioClose(new_ios))) { goto END; } END: if (rv != LIBRW_OK) { rwioPrintLastErr(new_ios, rv, &skAppPrintErr); skAppPrintErr("error: cannot create output file '%s'", new_path); } rwioDestroy(&ios); rwioDestroy(&new_ios); return rv; } int main(int argc, char **argv) { const char *input_path; rwIOStruct *input_ios; rwRec rwrec; int file_count = 0; int rv; appSetup(argc, argv); /* never returns on error */ /* loop over the source files */ for ( ; arg_index < argc; ++arg_index) { input_path = argv[arg_index]; /* skip files that are identical to the target or that we * cannot open */ if (0 == strcmp(input_path, rwGetFileName(output_ios))) { skAppPrintErr(("warning: skipping source-file%d:" " identical to target file '%s'"), file_count, input_path); continue; } if ((rv = rwioCreate(&input_ios, input_path, SK_RWIO_READ)) || (rv = rwioOpen(input_ios))) { rwioPrintLastErr(input_ios, rv, &skAppPrintErr); rwioDestroy(&input_ios); continue; } while (!LIBRW_ERROR_IS_FATAL(rv) && rwRead(input_ios, &rwrec)) { rv = rwWrite(output_ios, &rwrec); } if (print_statistics) { ++file_count; fprintf(STATISTICS_FH, ("%s: appended %" PRIu64 " records from %s to %s\n"), skAppName(), rwGetRecordCount(input_ios), rwGetFileName(input_ios), rwGetFileName(output_ios)); } rwioDestroy(&input_ios); } /* close target */ rv = rwioClose(output_ios); if (rv) { rwioPrintLastErr(output_ios, rv, &skAppPrintErr); } if (print_statistics) { fprintf(STATISTICS_FH, ("%s: appended %" PRIu64 " records from %d file%s to %s\n"), skAppName(), rwGetRecordCount(output_ios), file_count, ((file_count == 1) ? "" : "s"), rwGetFileName(output_ios)); } rwioDestroy(&output_ios); appTeardown(); return 0; } /* ** Local variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */