/* ** Copyright (C) 2003-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@ */ #include "silk.h" RCSIDENT("$SiLK: flowcapsetup.c 7193 2007-05-16 19:48:05Z mthomas $"); #include "flowcap.h" #include "utils.h" #include "fcinternals.h" #include "sksite.h" #include "skdeque.h" #include "sklog.h" #include "skdaemon.h" #include "sktransfer.h" /* TYPEDEFS AND DEFINES */ /* Where to print --help output */ #define USAGE_FH stdout /* LOCAL FUNCTIONS */ static void appUsageLong(void);/* never returns */ static int appOptionsHandler(clientData cData, int opt_index, char *opt_arg); static int appServerOptionsHandler(clientData cData, int opt_index, char *opt_arg); static void validateOptions(void); static void parseSensorList(const char *sensor_list); static int optionsDirCheck(const char *opt_name, const char *opt_arg); /* LOCAL VARIABLES */ /* Name of sensor configuration file */ static const char *sensor_configuration = NULL; /* Amount of disk space allocated for flowcap files {DISK, RAM} */ static uint64_t maxSpace[2]; /* Stashed sensor list */ static char *sensor_list = NULL; /* OPTIONS SETUP */ typedef enum { OPT_SENSOR_CONFIG, OPT_MAX_FILE_SIZE, OPT_TIMEOUT, OPT_SENSORS, OPT_FC_VERSION, OPT_DESTINATION_DIR #ifdef HAVE_STATVFS , OPT_FREESPACE_MINIMUM, OPT_SPACE_MAXIMUM_PERCENT #endif } appOptionsEnum; static struct option appOptions[] = { {"sensor-configuration", REQUIRED_ARG, 0, OPT_SENSOR_CONFIG}, {"max-file-size", REQUIRED_ARG, 0, OPT_MAX_FILE_SIZE}, {"timeout", REQUIRED_ARG, 0, OPT_TIMEOUT}, {"sensors", REQUIRED_ARG, 0, OPT_SENSORS}, {"fc-version", REQUIRED_ARG, 0, OPT_FC_VERSION}, {"destination-directory", REQUIRED_ARG, 0, OPT_DESTINATION_DIR}, #ifdef HAVE_STATVFS {"freespace-minimum", REQUIRED_ARG, 0, OPT_FREESPACE_MINIMUM}, {"space-maximum-percent", REQUIRED_ARG, 0, OPT_SPACE_MAXIMUM_PERCENT}, #endif {0,0,0,0} /* sentinel entry */ }; static appOptionsEnum firstNonServerOption = OPT_DESTINATION_DIR; static char *appHelp[] = { ("Read sensor configuration from named file."), ("Close the aggregated flow file when it reaches this\n" "\tsize (in bytes) so it can be sent to the packer. Append k, m, g, t\n" "\tfor kilo-, mega-, giga-, tera-bytes, respectively."), ("Close the aggregated flow file when it reaches this\n" "\tage (in seconds) so it can be sent to the packer. Def. 60"), ("Ignore all sensors in the sensor-configuration file except\n" "\tfor these sensor(s), a comma separated list. Def. Use all sensors"), NULL, /* generate dynamically */ ("Store aggregated packed flow files in this\n" "\tdirectory for processing by rwsender."), #ifdef HAVE_STATVFS ("Set the minimum free space (in bytes) to maintain\n" "\ton the filesystem. Accepts k,m,g,t suffix. Def. " DEFAULT_FREESPACE_MINIMUM), ("Set the maximum percentage of the disk to\n" "\tuse. Def." /* valued added in usage */), #endif /* HAVE_STATVFS */ (char *)NULL }; static sk_bitmap_t *appOptUsed; typedef enum { OPT_DISK_DIRECTORY, OPT_DISK_SPACE, OPT_FC_PORT, OPT_CLIENT_ADDRESS, OPT_RAM_DIRECTORY, OPT_RAM_SPACE, OPT_ACK_TIMEOUT } appServerOptionsEnum; static struct option appServerOptions[] = { {"disk-directory", REQUIRED_ARG, 0, OPT_DISK_DIRECTORY}, {"disk-space", REQUIRED_ARG, 0, OPT_DISK_SPACE}, {"fc-port", REQUIRED_ARG, 0, OPT_FC_PORT}, {"client-address", REQUIRED_ARG, 0, OPT_CLIENT_ADDRESS}, {"ram-directory", REQUIRED_ARG, 0, OPT_RAM_DIRECTORY}, {"ram-space", REQUIRED_ARG, 0, OPT_RAM_SPACE}, {"ack-timeout", REQUIRED_ARG, 0, OPT_ACK_TIMEOUT}, {0,0,0,0} /* sentinel entry */ }; static char *appServerHelp[] = { ("Store aggregated flow files in this directory\n" "\twhile they are waiting to be requested by rwflowpack."), ("Allocate this amount of space for disk-based files, in\n" "\tbytes. Append k, m, g, t for kilo-, mega-, giga-, tera-bytes."), ("Listen on this port for connections from rwflowpack."), ("Allow this internet address to connect to the flowcap\n" "\tport. Def. any"), ("Use this mount point for a RAM disk. Def. none"), ("Allocate this amount of space allotted for ramdisk-based\n" "\tfile. Accepts k,m,g,t suffix. Def. 0"), ("How long (in seconds) to wait for an ack from\n" "\trwflowpack. Def. 10"), (char *)NULL }; static sk_bitmap_t *appServerOptUsed; /* 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 \ ("\n" \ "\tflowcap is a daemon which listens to devices which produce flow\n" \ "\tdata (flow sources), homogenizes the data, stores it, and\n" \ "\tforwards as a compressed stream to a flowcap client program.\n") FILE *fh = USAGE_FH; int i; fprintf(fh, "%s %s", skAppName(), USAGE_MSG); fprintf(fh, "\nSWITCHES:\n"); skOptionsDefaultUsage(fh); sksiteOptionsUsage(fh); for (i = 0; appOptions[i].name; i++ ) { if (appOptions[i].val == OPT_DESTINATION_DIR) { fprintf(fh, ("\n" "LOCAL STORAGE MODE: Write files locally for" " pickup by separate rwsender\n")); } fprintf(fh, "--%s %s. ", appOptions[i].name, SK_OPTION_HAS_ARG(appOptions[i])); switch ((appOptionsEnum)appOptions[i].val) { case OPT_FC_VERSION: fprintf(fh, "Version of flowcap protocol to use: %d-%d. Def. %d", FC_VERSION_MIN, FC_VERSIONS, flowcap_version); break; #ifdef HAVE_STATVFS case OPT_SPACE_MAXIMUM_PERCENT: fprintf(fh, "%s %.2f%%", appHelp[i], DEFAULT_SPACE_MAXIMUM_PERCENT); break; #endif default: fprintf(fh, "%s", appHelp[i]); break; } fprintf(fh, "\n"); } fprintf(fh, ("\n" "SERVER MODE: Send files directly to an rwflowpack" " client that connects\n")); for (i = 0; appServerOptions[i].name; i++ ) { fprintf(fh, "--%s %s. ", appServerOptions[i].name, SK_OPTION_HAS_ARG(appServerOptions[i])); fprintf(fh, "%s", appServerHelp[i]); fprintf(fh, "\n"); } fprintf(fh, "\nLogging and daemonization switches:\n"); skdaemonOptionsUsage(fh); } /* * 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 tmp_32; uint64_t tmp_64; skBitmapSetBit(appOptUsed, (uint32_t)opt_index); switch ((appOptionsEnum)opt_index) { case OPT_SENSOR_CONFIG: sensor_configuration = opt_arg; break; case OPT_MAX_FILE_SIZE: if (skStringParseHumanUint64(&tmp_64, opt_arg, SK_HUMAN_NORMAL)) { skAppPrintErr("Invalid argument to --%s switch: %s", appOptions[opt_index].name, opt_arg); return 1; } if (tmp_64 > 0xFFFFFFFF) { skAppPrintErr("Argument to --%s switch, %s, is too large", appOptions[opt_index].name, opt_arg); } maxFileSize = tmp_64; break; case OPT_TIMEOUT: if (skStringParseUint32(&tmp_32, opt_arg, 0, 0xFFFFFFFE)) { skAppPrintErr("Invalid argument to --%s switch: %s", appOptions[opt_index].name, opt_arg); return 1; } writeTimeout = tmp_32; break; case OPT_SENSORS: sensor_list = opt_arg; break; case OPT_FC_VERSION: if (skStringParseUint32(&tmp_32, opt_arg, FC_VERSION_MIN, FC_VERSIONS)) { skAppPrintErr(("Invalid argument to --%s switch: %s\n" "\tValid arguments are 1-%d"), appOptions[opt_index].name, opt_arg, FC_VERSIONS); return 1; } flowcap_version = tmp_32; break; case OPT_DESTINATION_DIR: if (optionsDirCheck(appOptions[opt_index].name, opt_arg)) { return 1; } if (strlen(opt_arg) > PATH_MAX - FC_NAME_MAX) { skAppPrintErr("The --%s name is too long '%s'", appOptions[opt_index].name, opt_arg); return 1; } destDirs[FC_DISK] = strdup(opt_arg); break; #ifdef HAVE_STATVFS case OPT_FREESPACE_MINIMUM: if (skStringParseHumanUint64(&tmp_64, opt_arg, SK_HUMAN_NORMAL)) { skAppPrintErr("Invalid argument to --%s switch: %s", appOptions[opt_index].name, opt_arg); return 1; } freespace_minimum = (int64_t)tmp_64; break; case OPT_SPACE_MAXIMUM_PERCENT: if (skStringParseDouble(&space_maximum_percent, opt_arg, 0.0, 100.0)) { skAppPrintErr("Invalid argument to --%s switch: %s", appOptions[opt_index].name, opt_arg); return 1; } break; #endif /* HAVE_STATVFS */ } /* switch */ return 0; } /* * status = appServerOptionsHandler(cData, opt_index, opt_arg); * * As appOptionsHandler(), except for appServerOptions[]. */ static int appServerOptionsHandler( clientData UNUSED(cData), int opt_index, char *opt_arg) { const char *errmsg; struct in_addr addr; uint32_t tmp_number; uint64_t tmp_64; skBitmapSetBit(appServerOptUsed, (uint32_t)opt_index); switch ((appServerOptionsEnum)opt_index) { case OPT_CLIENT_ADDRESS: errmsg = skNameToAddr(opt_arg, &addr); if (errmsg) { skAppPrintErr("Illegal --%s address '%s': %s", appServerOptions[opt_index].name, opt_arg, errmsg); return 1; } memcpy(&fc_client_addr, &(addr.s_addr), sizeof(in_addr_t)); break; case OPT_FC_PORT: if (skStringParseUint32(&tmp_number, opt_arg, 0, 0xFFFE)) { skAppPrintErr("Invalid argument to --%s switch: %s", appServerOptions[opt_index].name, opt_arg); return 1; } fc_listen_port = (uint16_t)tmp_number; break; case OPT_RAM_DIRECTORY: if (optionsDirCheck(appServerOptions[opt_index].name, opt_arg)) { return 1; } if (strlen(opt_arg) > PATH_MAX - FC_NAME_MAX) { skAppPrintErr("The --%s name is too long '%s'", appServerOptions[opt_index].name, opt_arg); return 1; } destDirs[FC_RAM] = strdup(opt_arg); break; case OPT_DISK_DIRECTORY: if (optionsDirCheck(appServerOptions[opt_index].name, opt_arg)) { return 1; } if (strlen(opt_arg) > PATH_MAX - FC_NAME_MAX) { skAppPrintErr("The --%s name is too long '%s'", appServerOptions[opt_index].name, opt_arg); return 1; } destDirs[FC_DISK] = strdup(opt_arg); break; case OPT_DISK_SPACE: if (skStringParseHumanUint64(&tmp_64, opt_arg, SK_HUMAN_NORMAL)) { skAppPrintErr("Invalid argument to --%s switch: %s", appServerOptions[opt_index].name, opt_arg); return 1; } maxSpace[FC_DISK] = tmp_64; break; case OPT_RAM_SPACE: if (skStringParseHumanUint64(&tmp_64, opt_arg, SK_HUMAN_NORMAL)) { skAppPrintErr("Invalid argument to --%s switch: %s", appServerOptions[opt_index].name, opt_arg); return 1; } maxSpace[FC_RAM] = tmp_64; break; case OPT_ACK_TIMEOUT: if (skStringParseUint32(&ack_timeout, opt_arg, 0, 0)) { skAppPrintErr("Invalid argument to --%s switch: %s", appServerOptions[opt_index].name, opt_arg); return 1; } break; } /* switch */ return 0; } /* * 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(int32_t argc, char **argv) { int32_t arg_index; int rv; /* verify same number of options and help strings */ assert((sizeof(appHelp)/sizeof(char *)) == (sizeof(appOptions)/sizeof(struct option))); assert((sizeof(appServerHelp)/sizeof(char *)) == (sizeof(appServerOptions)/sizeof(struct option))); /* register the application */ skAppRegister(argv[0]); skOptionsSetUsageCallback(&appUsageLong); /* register the options */ if (optionsRegister(appOptions, (optHandler)appOptionsHandler, NULL) || optionsRegister(appServerOptions, (optHandler)appServerOptionsHandler, NULL) || sksiteOptionsRegister(SK_SITE_FLAG_CONFIG_FILE)) { skAppPrintErr("unable to register options"); exit(EXIT_FAILURE); } /* flowcap runs as a daemon; use the threaded logger */ if (skdaemonSetup((SKLOG_FEATURE_LEGACY | SKLOG_FEATURE_SYSLOG), argc, argv) || sklogEnableThreadedLogging()) { exit(EXIT_FAILURE); } /* initialize globals */ memset(maxSpace, 0, sizeof(maxSpace)); memset(unallocated_space, 0, sizeof(unallocated_space)); rv = skBitmapCreate(&appOptUsed, sizeof(appOptions)/sizeof(struct option)); if (rv != 0) { skAppPrintErr("Could not allocate bitmap"); exit(EXIT_FAILURE); } rv = skBitmapCreate(&appServerOptUsed, sizeof(appServerOptions)/sizeof(struct option)); if (rv != 0) { skAppPrintErr("Could not allocate bitmap"); exit(EXIT_FAILURE); } #ifdef HAVE_STATVFS { uint64_t tmp_64; if (skStringParseHumanUint64(&tmp_64, DEFAULT_FREESPACE_MINIMUM, SK_HUMAN_NORMAL)) { skAppPrintErr("Bad default value for freespace_minimum: '%s'", DEFAULT_FREESPACE_MINIMUM); assert(0); exit(EXIT_FAILURE); } freespace_minimum = (int64_t)tmp_64; } #endif /* HAVE_STATVFS */ /* parse options. If OK, arg_index is the first arg not used up */ arg_index = optionsParse(argc, argv); if (arg_index < 0) { skAppUsage();/* never returns */ } validateOptions(); /* Check that there aren't any extraneous arguments */ if (arg_index != argc) { skAppPrintErr("too many or unrecognized argument specified '%s'", argv[arg_index]); skAppUsage(); /* never returns */ } probe_vec = skVectorNew(sizeof(probe_def_t *)); if (probe_vec == NULL) { skAppPrintErr("vector create failed"); exit(EXIT_FAILURE); } if (sensor_list) { parseSensorList(sensor_list); } else { size_t i; for (i = 0; i < probeConfProbeCount(); i++) { const probe_def_t *probe = probeConfProbeGetByPosition(i); int rv = skVectorAppendValue(probe_vec, &probe); if (rv == -1) { skAppPrintErr("vector append failed"); exit(EXIT_FAILURE); } } } if (skVectorGetCount(probe_vec) < 1) { skAppPrintErr("No sensors with probes were specified"); exit(EXIT_FAILURE); } skBitmapDestroy(&appOptUsed); skBitmapDestroy(&appServerOptUsed); return; } /* appSetup */ static void validateOptions(void) { unsigned int i; /* ensure the site config is available */ if (sksiteConfigure(1)) { exit(EXIT_FAILURE); } /* check for required arguments */ if (sensor_configuration == NULL) { skAppPrintErr("The --%s option is required", appOptions[OPT_SENSOR_CONFIG].name); exit(EXIT_FAILURE); } if (!skBitmapGetBit(appOptUsed, OPT_MAX_FILE_SIZE)) { skAppPrintErr("The --%s switch is required", appOptions[OPT_MAX_FILE_SIZE].name); exit(EXIT_FAILURE); } allocFileSize = maxFileSize + (LZO_MAX_COMP_SIZE - LZO_MAX_BUF_SIZE); if ( !skBitmapGetBit(appOptUsed, OPT_DESTINATION_DIR) && !skBitmapGetBit(appServerOptUsed, OPT_DISK_DIRECTORY)) { skAppPrintErr("Either the --%s or --%s switch\n\tis required", appOptions[OPT_DESTINATION_DIR].name, appServerOptions[OPT_DISK_DIRECTORY].name); exit(EXIT_FAILURE); } if (skBitmapGetHighCount(appServerOptUsed)) { int required_switches[] = {OPT_DISK_DIRECTORY, OPT_FC_PORT, OPT_DISK_SPACE, -1/* sentinel */}; /* use send_files mode */ server_mode_flag = 1; /* check for incompatible switches */ for (i = firstNonServerOption; appOptions[i].name; ++i) { if (skBitmapGetBit(appOptUsed, i)) { unsigned int j; for (j = 0; appServerOptions[j].name; ++j) { if (skBitmapGetBit(appServerOptUsed, j)) { skAppPrintErr(("Cannot mix the local storage mode" " with the server mode switches:\n" "\tBoth --%s and --%s given"), appOptions[i].name, appServerOptions[j].name); } } exit(EXIT_FAILURE); } } /* make certain we got our required switches */ for (i = 0; required_switches[i] != -1; ++i) { if (!skBitmapGetBit(appServerOptUsed, i)) { skAppPrintErr("The --%s switch is required in Server Mode", appServerOptions[i].name); exit(EXIT_FAILURE); } } unallocated_space[FC_DISK] = maxSpace[FC_DISK]; unallocated_space[FC_RAM] = maxSpace[FC_RAM]; if (skBitmapGetBit(appServerOptUsed, OPT_RAM_SPACE) && !skBitmapGetBit(appServerOptUsed, OPT_RAM_DIRECTORY)) { skAppPrintErr("Use of --%s requires the --%s switch", appServerOptions[OPT_RAM_SPACE].name, appServerOptions[OPT_RAM_DIRECTORY].name); exit(EXIT_FAILURE); } if (maxSpace[FC_RAM] > maxSpace[FC_DISK]) { skAppPrintErr("Space given by --%s is greater than --%s", appServerOptions[OPT_RAM_SPACE].name, appServerOptions[OPT_DISK_SPACE].name); exit(EXIT_FAILURE); } maxSpace[FC_DISK] -= maxSpace[FC_RAM]; /* Adjust */ } else { /* we are not sending files */ int required_switches[] = {OPT_DESTINATION_DIR, -1/* sentinel */}; /* make certain we got our required switches */ for (i = 0; required_switches[i] != -1; ++i) { if (!skBitmapGetBit(appOptUsed, i)) { skAppPrintErr(("The --%s switch is required in" " Local Storage Mode"), appOptions[i].name); exit(EXIT_FAILURE); } } } if (probeConfSetup() != 0) { skAppPrintErr("unable to setup probe configuration handler"); exit(EXIT_FAILURE); } if (probeConfParse(sensor_configuration) != 0) { skAppPrintErr("Error in sensor configuration file."); exit(EXIT_FAILURE); } if (probeConfProbeCount() == 0) { skAppPrintErr("The sensor configuration file has no probes within it"); exit(EXIT_FAILURE); } /* verify the required options for logging */ if (skdaemonOptionsVerify()) { exit(EXIT_FAILURE); } } /* Parse a sensor list and add the probes associated with it into an already allocated probe_vec. */ static void parseSensorList(const char *sensor_list) { sk_vector_t *sensor_vec; int count; int i; sensor_vec = skVectorNew(sizeof(sensorID_t)); if (sensor_vec == NULL) { skAppPrintErr("vector create failed"); exit(EXIT_FAILURE); } count = sksiteParseSensorList(sensor_vec, sensor_list); if (count < 1) { skAppPrintErr("Invalid argument to --%s switch: %s", appOptions[OPT_SENSORS].name, sensor_list); exit(EXIT_FAILURE); } for (i = 0; i < count; i++) { sensorID_t sensor_id; int num_probes; char sensor_name[SK_MAX_STRLEN_SENSOR+1]; skVectorGetValue(&sensor_id, sensor_vec, i); num_probes = probeConfGetProbesForSensor(probe_vec, sensor_id); if (num_probes == -1) { sksiteSensorGetName(sensor_name, sizeof(sensor_name), sensor_id); skAppPrintErr("Error adding probes for %s.", sensor_name); exit(EXIT_FAILURE); } } skVectorDestroy(sensor_vec); } /* * dir_exists = optionsDirCheck(opt_name, opt_arg); * * Verify that the directory in 'opt_arg' exists and that we have a * full path to the directory. If so, return 0; otherwise, print * an error that the option named by 'opt_name' was bad and return * -1; */ static int optionsDirCheck(const char *opt_name, const char *opt_arg) { if (!opt_arg || !opt_arg[0]) { skAppPrintErr("The directory argument to %s is empty", opt_name); return -1; } if (!dirExists(opt_arg)) { skAppPrintErr("The %s '%s' does not exist", opt_name, opt_arg); return -1; } if (opt_arg[0] != '/') { skAppPrintErr(("Must use complete path to %s.\n" "\t('%s' does not begin with slash)"), opt_name, opt_arg); return -1; } return 0; } /* ** Local Variables: ** mode:c ** indent-tabs-mode:nil ** c-basic-offset:4 ** End: */