/* ** 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@ */ /* ** sku-options.c ** ** Suresh L Konda ** ** 12/4/2001 ** ** Routines to support long option parsing with multiple sets of options. ** ** Four functions are exported: ** optionsSetup(); ** optionsTeardown(); ** optionsRegister(); ** optionsParse(); ** ** Each client calls optionsRegister with: ** 1. a pointer to struct option [] ** 2. a handler to process the option. The handler will be called with two ** arguments: ** 1. the clientData ** 2. the original val value passed to the registry via ** options associated with this option ** 3. the optarg returned by getopt(); ** 3. an opaque pointer to arbitrary data called clientData which ** Error Return : 1 ** ** Once all clients have registered, then call optionsParse with argc, argv ** which parses the options and calls the handler as required. ** ** It returns -1 on error or optind if OK. Thus, argv[optind] is the first ** non-option argument given to the application. ** ** Currently, we do NOT do flag versus val handling: flag is always ** assumed to be NULL and val is the appropriate unique entity that ** allows the handler to deal with the option to be parsed. It is ** suggested that the caller use a distinct index value in the val part. ** ** */ #include "silk.h" RCSIDENT("$SiLK: sku-options.c 7800 2007-07-06 15:32:43Z mthomas $"); #include "utils.h" #include "sksite.h" #include "libutils_priv.h" /* Start options at this offset to avoid having an option with index * of '?' (63) which is the value used to signal an error. */ #define OPTION_OFFSET 64 /* Where to write --version information */ #define VERS_FH stdout /* * struct option has the following definition: * * struct option { * char *name; * int has_arg; * int *flag; * int val; * }; * */ typedef enum { OPT_VAL_HELP, OPT_VAL_VERSION } defaultOptionsEnum; /* options that everyone gets */ static struct option defaultOptions[] = { {"help", NO_ARG, 0, OPT_VAL_HELP}, {"version", NO_ARG, 0, OPT_VAL_VERSION}, {0,0,0,0} /* sentinel */ }; static char *defaultHelp[] = { "Print this usage output and exit. Def. No", "Print this program's version and exit. Def. No", (char*)NULL /* sentinel */ }; void skOptionsDefaultUsage(FILE *fh) { int i; for (i = 0; defaultOptions[i].name; ++i) { fprintf(fh, "--%s %s. %s\n", defaultOptions[i].name, SK_OPTION_HAS_ARG(defaultOptions[i]), defaultHelp[i]); } } /* * printVersion(); * * Print version information and information about how SiLK was * configured. */ static void printVersion(void) { #define COPYRIGHT_LICENSE \ ("Copyright (C) 2001-2007 by Carnegie Mellon University\n" \ "GNU Public License (GPL) Rights pursuant to Version 2, June 1991\n" \ "Government Purpose License Rights (GPLR) pursuant to DFARS 252.225-7013") struct timeval now = {0x40000000, 123456}; uint8_t default_compmethod; uint8_t i; char comp_name[SK_MAX_STRLEN_FILE_FORMAT+1]; fprintf(VERS_FH, "%s: part of %s %s; configuration settings:\n", skAppName(), PACKAGE_NAME, PACKAGE_VERSION); fprintf(VERS_FH, " * %-32s %s\n", "Site:", SILK_SITE); fprintf(VERS_FH, " * %-32s %s\n", "Root of packed data tree:", SILK_DATA_ROOTDIR); fprintf(VERS_FH, " * %-32s %s\n", "Timezone support:", #ifdef SK_ENABLE_LOCALTIME "local" #else "UTC" #endif ); fprintf(VERS_FH, " * %-32s %s\n", "Default timestamp format:", sktimestamp(&now, 0)); default_compmethod = sksiteCompmethodGetDefault(); sksiteCompmethodGetName(comp_name, sizeof(comp_name), default_compmethod); fprintf(VERS_FH, " * %-32s %s [default]", "Available compression methods:", comp_name); for (i = 0; sksiteCompmethodIsValid(i); ++i) { if (i == default_compmethod) { continue; } if (!sksiteCompmethodIsAvailable(i)) { continue; } sksiteCompmethodGetName(comp_name, sizeof(comp_name), i); fprintf(VERS_FH, ", %s", comp_name); } fprintf(VERS_FH, "\n"); fprintf(VERS_FH, " * %-32s %s\n", "IPFIX collection support:", #ifdef HAVE_FIXBUF_PUBLIC_H "yes" #else "no" #endif ); fprintf(VERS_FH, " * %-32s %s\n", "Initial TCP flag support:", #ifdef SK_ENABLE_INITIAL_TCPFLAGS "yes" #else "no" #endif ); fprintf(VERS_FH, " * %-32s %s\n", "Transport encryption:", #ifdef HAVE_GNUTLS_GNUTLS_H "GnuTLS" #else "no" #endif ); fprintf(VERS_FH, " * %-32s %s\n", "Enable assert():", #ifndef NDEBUG "yes" #else "no" #endif ); fprintf(VERS_FH, ("%s\n" "Send bug reports, feature requests, and comments to %s\n"), COPYRIGHT_LICENSE, PACKAGE_BUGREPORT); } /* * status = defaultOptionsHandler(cData, opt_index, opt_arg); * * Called by optionsParse() to handle the default/global options * defined inthe defaultOptions[] array. This handler will exit * the application. */ static int defaultOptionsHandler( clientData UNUSED(cData), int optIndex, char UNUSED(*optArg)) { switch ((defaultOptionsEnum)optIndex) { case OPT_VAL_HELP: app_context->usageFunction(); break; case OPT_VAL_VERSION: printVersion(); break; } exit(EXIT_SUCCESS); return 0; /* NOTREACHED */ } static void defaultHelpOutput(void) { skAppStandardUsage(stdout, "", NULL, NULL); } void skOptionsSetup(void) { /* already called */ if (app_context->usageFunction) { return; } /* tell getopt_long() that it should print errors */ opterr = 1; /* set a default usage function */ skOptionsSetUsageCallback(&defaultHelpOutput); /* add default switches */ if (optionsRegister(defaultOptions, defaultOptionsHandler, NULL)) { skAppPrintErr("unable to set default options"); exit(EXIT_FAILURE); } } void skOptionsSetUsageCallback(usage_fn_t fn) { app_context->usageFunction = fn; } int optionsSetup(usage_fn_t fn) { skOptionsSetup(); skOptionsSetUsageCallback(fn); return 0; } void skOptionsTeardown(void) { if ( app_context->gOptions == 0) { return; } free(app_context->gOptions); free(app_context->oMap); app_context->gOptions = /* (struct option (*)[1]) */ NULL; app_context->oMap = /* (mapOptionsStruct (*)[1]) */ NULL; return; } void optionsTeardown(void) { skOptionsTeardown(); } int optionsRegister( void *inOptions, optHandler handler, clientData cData) { struct option (*options)[1] = (struct option (*)[1])inOptions; int i,j; int optCount; int old_options_count; int new_total_options; void *old_mem; if (app_context->usageFunction == NULL) { skAppPrintErr("Must call optionsSetup() before registering options"); return 0; } /* count the options that were passed in */ for (j = 0; (*options)[j].name; j++) ; optCount = j; if (0 == optCount) { /* empty options list */ return 0; } /* Get the current total number of options */ old_options_count = app_context->gCount; /* New total number of options will be the current value plus the * number of options passed in. */ new_total_options = old_options_count + optCount; /* The app_context->gCount value, if nonzero, is actually one * less than the total number of spaces we have allocated, since * we have an extra slot for the sentinel, so we need to add one * to new_total_options to account for that. */ ++new_total_options; /* * Get or grow the space for the arrays. */ old_mem = app_context->gOptions; app_context->gOptions = realloc(app_context->gOptions, (new_total_options*sizeof(struct option))); if (app_context->gOptions == NULL) { app_context->gOptions = old_mem; skAppPrintErr("Cannot realloc gOptions"); return 1; } old_mem = app_context->oMap; app_context->oMap = realloc(app_context->oMap, (new_total_options*sizeof(mapOptionsStruct))); if (app_context->oMap == NULL) { app_context->oMap = old_mem; skAppPrintErr("Cannot realloc oMap"); return 1; } for (j = 0; j < optCount; j++) { /* check for dups */ for(i = 0; i < old_options_count; i++) { if (strcmp(app_context->gOptions[i].name, (*options)[j].name)==0) { /* a real name clash */ skAppPrintErr("optionsRegister: name clash: '%s' already used", (*options)[j].name); return 1; /* failure */ } } /* a clean new entry. record it. */ app_context->gOptions[app_context->gCount].name = (*options)[j].name; app_context->gOptions[app_context->gCount].has_arg = (*options)[j].has_arg; app_context->gOptions[app_context->gCount].flag = (*options)[j].flag; /* the 'val' used internally is the OPTION_OFFSET plus the * index into the 'oMap' array; the oMap array will be used to * get the 'val' the called handed us. */ app_context->gOptions[app_context->gCount].val = OPTION_OFFSET + app_context->gCount; /* original val to be returned with handler */ app_context->oMap[app_context->gCount].oIndex = (*options)[j].val; app_context->oMap[app_context->gCount].handler = handler; app_context->oMap[app_context->gCount].cData = cData; app_context->gCount++; } /* set the sentinel for gOptions */ memset(&app_context->gOptions[app_context->gCount], 0, sizeof(struct option)); app_context->numClients++; return 0; } /* * optionsParse: * Adjust the global options array to allow for the help * option. If help is selected by the user, call the stashed * usageFunction. Parse input options given a set of * pre-registered options and their handlers. For each * legitimate option, call the handler. * SideEffects: * The individual handlers update whatever datastruture they wish * to via the clientData argument to the handler. * Return: * optind which points at the first non-option argument passed if * all is OK. If not OK, the return -1 for error. */ int optionsParse(int argc, char **argv) { int done = 0; int c; int idx; while (! done) { int option_index; #ifdef HAVE_GETOPT_LONG_ONLY c = getopt_long_only(argc, argv, "", (const struct option *)app_context->gOptions, &option_index); #else c = _getopt_internal(argc, argv, "", (const struct option *)app_context->gOptions, &option_index, 1); #endif switch (c) { case '?': /*skAppPrintErr("Invalid or ambiguous option"); */ return -1; case -1: done = 1; break; default: /* a legit value: call the handler */ idx = c - OPTION_OFFSET; if (app_context->oMap[idx].handler(app_context->oMap[idx].cData, app_context->oMap[idx].oIndex, optarg)) { /* handler signaled error */ return -1; } break; } } return optind; } #if 0 /* * moveDynaLibsToFront * Rearrange argv so that dynamic library options appear at front * Arguments: * argc - int - argument count * argv - char** - arguments * Returns: * A char** to use in place of argv as the argument array. If no * dynamic libraries were found, the returned argv will be same * argv that was passed into the function; otherwise, a new argv * array is created and returned, but it still references the * values in the argv passed into the function. * Side Effects: * None * Notes: * Only a single option name (but multiple occurrences of it) are * moved by this function. The name of that option is given in * the OPT_DYNAMIC_LIBRARY macro defined in utils.h. This * function may malloc memory for the returned argv that never * gets freed. */ static char **moveDynaLibsToFront(const int argc, char **argv) { int dynlib_count = 0; /* count of options to be moved */ int dynlib_moved = 0; /* count of options actually moved */ uint8_t is_dynlib[256]; /* flags options to be moved */ /* max number of args that may be moved */ const int max_args = (int)(sizeof(is_dynlib)/sizeof(uint8_t)); char **new_argv; /* return value */ int c; char *p; char *eq_pos; /* location of = in option */ const int arg_limit = (argc > max_args ? max_args : argc); memset(is_dynlib, 0, sizeof(is_dynlib)); /* in this first pass, find the options that need to move and set * their values in is_dynlib[] to 1 */ for (c = 1; c < arg_limit; ++c) { p = argv[c]; if ('-' != *p) { /* ignore arguments that don't begin with a - */ continue; } ++p; if ('\0' == *p) { /* ignore options that are only a - */ continue; } if ('-' == *p) { /* options may begin with a - or -- */ ++p; if ('\0' == *p) { /* getopt stops processing on a -- */ break; } } if ('=' == *p) { /* ignore --= */ continue; } eq_pos = strchr(p, '='); if (eq_pos) { /* maybe need to move a single argument */ if (0 == strncmp(OPT_DYNAMIC_LIBRARY, p, eq_pos-p)) { is_dynlib[c] = 1; ++dynlib_count; } } else { /* maybe need to move two arguments */ if ((0 == strncmp(OPT_DYNAMIC_LIBRARY, p, strlen(p))) && (c+1 < arg_limit)) { is_dynlib[c] = 1; is_dynlib[c+1] = 1; ++c; dynlib_count += 2; } } } /* nothing to do */ if (0 == dynlib_count) { return argv; } /* make a second pass thru argv and copy the options into new_argv */ new_argv = (char**)calloc(argc, sizeof(char*)); if (new_argv == NULL) { /* alloc failed; return current argv */ return argv; } new_argv[0] = argv[0]; for (c = 1; c < arg_limit; ++c) { if (is_dynlib[c]) { ++dynlib_moved; new_argv[dynlib_moved] = argv[c]; } else { new_argv[dynlib_count - dynlib_moved + c] = argv[c]; } } /* if we have more than max_args args, copy them into new_argv[] * as well */ if (argc > max_args) { memcpy(new_argv + dynlib_count - dynlib_moved + arg_limit, argv + arg_limit, (argc - max_args)*sizeof(char*)); } return new_argv; } #endif /* 0 */ /* * readline: * Read a line (including newline) from a file. Will also read a * last line (terminated by EOF) properly. * SideEffects: * Moves the read position of file to the next line. * Return: * A newly allocated string containing the next line. NULL at * EOF, or if there is a problem. */ static char *readline(FILE *file) { static int gapsize = 64; char *line; int blocksize = 1; int writepoint = 0; char *retval = NULL; if (file == NULL) { return NULL; } /* Initial allocation for line */ line = (char *)malloc(sizeof(char) * gapsize); if (line == NULL) { return NULL; } while (1) { /* How many chars are left? */ size_t empty = gapsize * blocksize - writepoint; char *wp = &line[writepoint]; /* Get chars */ if (fgets(wp, empty, file) == NULL) { if (writepoint != 0) { /* End of file */ retval = strdup(line); } goto end; } /* If we haven't reached the end of the line, realloc. */ if ((strlen(wp) == empty - 1) && (wp[empty - 2] != '\n')) { char *tmpline; writepoint = gapsize * blocksize - 1; tmpline = realloc(line, sizeof(char) * (gapsize * (++blocksize))); if (tmpline) { line = tmpline; } else { goto end; } } else { /* We've reached the end of the line. */ break; } } /* Allocate only enough space for the line. */ retval = strdup(line); end: /* Cleanup */ free(line); return retval; } /* * optionsHandleConfFile: * * Loads a configuration file. The configuration file consists of * a series of newline-terminated lines. A line consisting of * only whitespace, or whose first non-whitespace character is a * `#' character is ignored. All other lines should consist of a * single option name followed by the option's value (if any), * seperated by whitespace. Whitespace at the beginning and end * of the line is ignored. * * BUGS: * If you intersperse switches (options) and arguments, arguments * before the configuration file is parsed will not be seen. * * Return: * 0 if ok. -1 else */ int optionsHandleConfFile(char *filename) { static int gapsize = 10; int num_lines = 0; int num_alloc = 0; char **lines = NULL; char *line = NULL; FILE *file; int retval = -1; int i; char **argv = NULL; int argc = 0; int saved_optind; if (filename == NULL) { skAppPrintErr("NULL configuration filename"); return -1; } /* Open the file */ file = fopen(filename, "r"); if (file == NULL) { skAppPrintErr("Could not open \"%s\" for reading.", filename); return -1; } /* Alloc the line buffer */ num_alloc = gapsize; lines = (char **)malloc(sizeof(char *) * num_alloc); if (lines == NULL) { skAppPrintErr("Memory allocation error."); goto end; } /* Read in the lines */ while ((line = readline(file))) { char *newline; size_t len; char *c; /* Strip it */ len = strip(line); /* Elide commented or empty lines. */ if (line[0] == '\0' || line[0] == '#') { free(line); continue; } /* Allocate space for the line, plus two characters. */ c = newline = (char *)malloc(sizeof(char) * (len + 3)); /* Copy the line, prepending hyphens */ *c++ = '-'; *c++ = '-'; strcpy(c, line); free(line); lines[num_lines++] = newline; /* Allocate more space, if necessary */ if (num_lines > num_alloc) { char **tmp; num_alloc += gapsize; tmp = realloc(lines, sizeof(char *) * num_alloc); if (tmp == NULL) { goto end; } lines = tmp; } } /* Allocate space for argv-style pointer */ argv = (char **)malloc(sizeof(char *) * num_lines * 2 + 1); if (argv == NULL) { goto end; } /* First operand is program name, ignored */ argv[argc++] = ""; /* Parse the lines. */ for (i = 0; i < num_lines; i++) { /* Set the next argument to the beginning of the line */ char *c = argv[argc++] = lines[i]; /* Find a space */ while (*c && !isspace((int)*c)) { c++; } if (*c) { /* If we found a space, end the first arg, and find the option value. */ *c++ = '\0'; while (isspace((int)*c)) { /* Don't need to check for 0 due to strip */ c++; } /* Set the next argument to the option value. */ argv[argc++] = c; } } saved_optind = optind; #ifdef SK_USE_OPTRESET optreset = 1; #endif #ifdef HAVE_GETOPT_LONG_ONLY optind = 1; #else optind = 0; #endif /* Parse the options */ if (optionsParse(argc, argv) != -1) { retval = 0; } optind = saved_optind; #ifdef SK_USE_OPTRESET optreset = 1; #endif end: /* Cleanup */ if (file) { fclose(file); } if (argv) { free(argv); } if (lines) { for (i = 0; i < num_lines; i++) { free(lines[i]); } free(lines); } return retval; } /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */