Пример #1
0
/*
 *    Parse the argument to the --log-flags switch.
 */
static int
parseLogFlags(
    const char         *log_flags_str)
{
    char *log_flags_copy = NULL;
    char *flag_next;
    char *flag;
    int rv = -1;

    skpcProbeClearLogFlags(probe);

    if (NULL == log_flags_str) {
        return 0;
    }

    /* create a copy of the input string and maintain a reference to
     * it so we can free it */
    log_flags_copy = strdup(log_flags_str);
    flag_next = log_flags_copy;
    if (NULL == log_flags_copy) {
        skAppPrintOutOfMemory(NULL);
        goto END;
    }

    /* parse the flags as a comma separated list of tokens */
    while ((flag = strsep(&flag_next, ",")) != NULL) {
        /* check for empty token (e.g., double comma) */
        if ('\0' == *flag) {
            continue;
        }
        switch (skpcProbeAddLogFlag(probe, flag)) {
          case 0:
            break;
          case -1:
            skAppPrintErr("Invalid %s: Unrecognized value '%s'",
                          appOptions[OPT_LOG_FLAGS].name, flag);
            goto END;
          case -2:
            skAppPrintErr("Invalid %s: Cannot mix 'none' with other value",
                          appOptions[OPT_LOG_FLAGS].name);
            goto END;
          default:
            skAppPrintErr("Bad return value from skpcProbeAddLogFlag()");
            skAbort();
        }
    }

    rv = 0;

  END:
    free(log_flags_copy);
    return rv;
}
Пример #2
0
/*
 *  status = appOptionsHandler(cData, opt_index, opt_arg);
 *
 *    This function is passed to skOptionsRegister(); it will be called
 *    by skOptionsParse() 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 skOptionsParse() 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)
{
    int rv;

    switch ((appOptionsEnum)opt_index) {
      case OPT_DESTINATION_DIR:
        if (skOptionsCheckDirectory(opt_arg, appOptions[opt_index].name)) {
            return 1;
        }
        destination_dir = opt_arg;
        break;

      case OPT_DUPLICATE_DEST:
        if (skOptionsCheckDirectory(opt_arg, appOptions[opt_index].name)) {
            return 1;
        }
        rv = skDLListPushTail(duplicate_dirs, opt_arg);
        if (rv != 0) {
            skAppPrintOutOfMemory("directory name");
            return 1;
        }
        break;

      case OPT_UNIQUE_DUPLICATES:
        unique_duplicates = 1;
        break;

      case OPT_POST_COMMAND:
        if (verifyCommandString(opt_arg)) {
            return 1;
        }
        post_command = opt_arg;
        break;

#ifdef SK_HAVE_STATVFS
      case OPT_FREESPACE_MINIMUM:
        {
            uint64_t tmp_64;
            rv = skStringParseHumanUint64(&tmp_64, opt_arg, SK_HUMAN_NORMAL);
            if (rv) {
                goto PARSE_ERROR;
            }
            freespace_minimum = (int64_t)tmp_64;
        }
        break;

      case OPT_SPACE_MAXIMUM_PERCENT:
        rv = skStringParseDouble(&space_maximum_percent, opt_arg, 0.0, 100.0);
        if (rv) {
            goto PARSE_ERROR;
        }
        break;
#endif /* SK_HAVE_STATVFS */
    }

    return 0;  /* OK */

#ifdef SK_HAVE_STATVFS
  PARSE_ERROR:
    skAppPrintErr("Invalid %s '%s': %s",
                  appOptions[opt_index].name, opt_arg,
                  skStringParseStrerror(rv));
    return 1;
#endif
}
Пример #3
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.
 */
static void
appSetup(
    int                 argc,
    char              **argv)
{
    SILK_FEATURES_DEFINE_STRUCT(features);
    int arg_index;
    int rv;

    /* check that we have the same number of options entries and help*/
    assert((sizeof(appHelp)/sizeof(char *)) ==
           (sizeof(appOptions)/sizeof(struct option)));

    /* register the application */
    skAppRegister(argv[0]);
    skAppVerifyFeatures(&features, NULL);
    skOptionsSetUsageCallback(&appUsageLong);

    /* initialize globals */
    shuttingdown    = 0;
    destination_dir = NULL;
    duplicate_dirs  = skDLListCreate(NULL);
    if (duplicate_dirs == NULL) {
        skAppPrintOutOfMemory("duplicate directory list");
        exit(EXIT_FAILURE);
    }
    open_file_list  = skDLListCreate(NULL);
    if (open_file_list == NULL) {
        skAppPrintOutOfMemory("open file list");
        exit(EXIT_FAILURE);
    }

    transfers = transferIdentTreeCreate();
    if (transfers == NULL) {
        skAppPrintOutOfMemory("receiver data structure");
        exit(EXIT_FAILURE);
    }
#ifdef SK_HAVE_STATVFS
    {
        uint64_t tmp_64;
        rv = skStringParseHumanUint64(&tmp_64, DEFAULT_FREESPACE_MINIMUM,
                                     SK_HUMAN_NORMAL);
        if (rv) {
            skAppPrintErr("Bad default value for freespace_minimum: '%s': %s",
                          DEFAULT_FREESPACE_MINIMUM,skStringParseStrerror(rv));
            exit(EXIT_FAILURE);
        }
        freespace_minimum = (int64_t)tmp_64;
    }
#endif /* SK_HAVE_STATVFS */

    /* register the options and handler */
    if (skOptionsRegister(appOptions, &appOptionsHandler, NULL))
    {
        skAppPrintErr("Unable to register application options");
        exit(EXIT_FAILURE);
    }

    /* Register the other transfer options */
    if (transferSetup()) {
        exit(EXIT_FAILURE);
    }

    /* rwreceiver runs as a daemon */
    if (skdaemonSetup((SKLOG_FEATURE_LEGACY | SKLOG_FEATURE_SYSLOG),
                      argc, argv))
    {
        exit(EXIT_FAILURE);
    }

    /* register the teardown handler */
    if (atexit(appTeardown) < 0) {
        skAppPrintErr("Unable to register appTeardown() with atexit()");
        appTeardown();
        exit(EXIT_FAILURE);
    }

    /* parse the options */
    arg_index = skOptionsParse(argc, argv);
    if (arg_index < 0) {
        /* options parsing should print error */
        skAppUsage();           /* never returns */
    }

    /* Verify the options */
    rv = rwreceiverVerifyOptions();

    /* verify the required options for logging */
    if (skdaemonOptionsVerify()) {
        rv = -1;
    }

    if (rv) {
        skAppUsage();           /* never returns */
    }

    /* check for extraneous arguments */
    if (arg_index != argc) {
        skAppPrintErr("Too many arguments or unrecognized switch --%s",
                      argv[arg_index]);
        skAppUsage();           /* never returns */
    }

    /* Identify the main thread */
    skthread_init("main");

    return;  /* OK */
}
Пример #4
0
/*
 *  mergeFiles(temp_file_idx)
 *
 *    Merge the temporary files numbered from 0 to 'temp_file_idx'
 *    inclusive into the output file 'out_ios', maintaining sorted
 *    order.  Exits the application if an error occurs.
 */
static void
mergeFiles(
    int                 temp_file_idx)
{
    char errbuf[2 * PATH_MAX];
    skstream_t *fps[MAX_MERGE_FILES];
    uint8_t recs[MAX_MERGE_FILES][NODE_SIZE];
    uint8_t lowest_rec[NODE_SIZE];
    int j;
    uint16_t open_count;
    uint16_t i;
    uint16_t lowest;
    uint16_t *top_heap;
    int tmp_idx_a;
    int tmp_idx_b;
    skstream_t *fp_intermediate = NULL;
    int tmp_idx_intermediate;
    skheap_t *heap;
    uint32_t heap_count;
    int opened_all_temps = 0;
    ssize_t rv;

    /* the index of the first temp file to the merge */
    tmp_idx_a = 0;

    TRACEMSG(("Merging #%d through #%d to '%s'",
              tmp_idx_a, temp_file_idx, skStreamGetPathname(out_rwios)));

    heap = skHeapCreate2(compHeapNodes, MAX_MERGE_FILES, sizeof(uint16_t),
                         NULL, recs);
    if (NULL == heap) {
        skAppPrintOutOfMemory("heap");
        appExit(EXIT_FAILURE);
    }

    /* This loop repeats as long as we haven't read all of the temp
     * files generated in the qsort stage. */
    do {
        assert(SKHEAP_ERR_EMPTY==skHeapPeekTop(heap,(skheapnode_t*)&top_heap));

        /* the index of the list temp file to merge */
        tmp_idx_b = temp_file_idx;

        /* open an intermediate temp file.  The merge-sort will have
         * to write records here if there are not enough file handles
         * available to open all the existing tempoary files. */
        fp_intermediate = skTempFileCreateStream(tmpctx, &tmp_idx_intermediate);
        if (fp_intermediate == NULL) {
            skAppPrintSyserror("Error creating new temporary file");
            appExit(EXIT_FAILURE);
        }

        /* count number of files we open */
        open_count = 0;

        /* Attempt to open up to MAX_MERGE_FILES, though we an open
         * may fail due to lack of resources (EMFILE or ENOMEM) */
        for (j = tmp_idx_a; j <= tmp_idx_b; ++j) {
            fps[open_count] = skTempFileOpenStream(tmpctx, j);
            if (fps[open_count] == NULL) {
                if ((open_count > 0)
                    && ((errno == EMFILE) || (errno == ENOMEM)))
                {
                    /* We cannot open any more files.  Rewind counter
                     * by one to catch this file on the next merge */
                    tmp_idx_b = j - 1;
                    TRACEMSG((("FILE limit hit--"
                               "merging #%d through #%d into #%d: %s"),
                              tmp_idx_a, tmp_idx_b, tmp_idx_intermediate,
                              strerror(errno)));
                    break;
                } else {
                    skAppPrintSyserror(("Error opening existing"
                                        " temporary file '%s'"),
                                       skTempFileGetName(tmpctx, j));
                    appExit(EXIT_FAILURE);
                }
            }

            /* read the first record */
            rv = skStreamRead(fps[open_count], recs[open_count], NODE_SIZE);
            if (NODE_SIZE == rv) {
                /* insert the file index into the heap */
                skHeapInsert(heap, &open_count);
                ++open_count;
                if (open_count == MAX_MERGE_FILES) {
                    /* We've reached the limit for this pass.  Set
                     * tmp_idx_b to the file we just opened. */
                    tmp_idx_b = j;
                    TRACEMSG((("MAX_MERGE_FILES limit hit--"
                           "merging #%d through #%d to #%d"),
                              tmp_idx_a, tmp_idx_b, tmp_idx_intermediate));
                    break;
                }
            } else if (0 == rv) {
                TRACEMSG(("Ignoring empty temporary file '%s'",
                          skTempFileGetName(tmpctx, j)));
                skStreamDestroy(&fps[open_count]);
            } else {
                if (rv > 0) {
                    snprintf(errbuf, sizeof(errbuf),
                             "Short read %" SK_PRIdZ "/%" PRIu32 " from '%s'",
                             rv, NODE_SIZE,
                             skStreamGetPathname(fps[open_count]));
                } else {
                    skStreamLastErrMessage(
                        fps[open_count], rv, errbuf, sizeof(errbuf));
                }
                skAppPrintErr(
                    "Error reading first record from temporary file: %s",
                    errbuf);
                appExit(EXIT_FAILURE);
            }
        }

        /* Here, we check to see if we've opened all temp files.  If
         * so, set a flag so we write data to final destination and
         * break out of the loop after we're done. */
        if (tmp_idx_b == temp_file_idx) {
            opened_all_temps = 1;
            /* no longer need the intermediate temp file */
            skStreamDestroy(&fp_intermediate);
        } else {
            /* we could not open all temp files, so merge all opened
             * temp files into the intermediate file.  Add the
             * intermediate file to the list of files to merge */
            temp_file_idx = tmp_idx_intermediate;
        }

        TRACEMSG((("Merging %" PRIu16 " temporary files"), open_count));

        heap_count = skHeapGetNumberEntries(heap);
        assert(heap_count == open_count);

        /* get the index of the file with the lowest record; which is
         * at the top of the heap */
        if (skHeapPeekTop(heap, (skheapnode_t*)&top_heap) != SKHEAP_OK) {
            skAppPrintErr("Unable to open and read any temporary files.");
            appExit(EXIT_FAILURE);
        }
        lowest = *top_heap;

        /* exit this do...while() once all records for all opened
         * files have been read */
        do {
            /* lowest_rec is the record pointed to by the index at the
             * top of the heap */
            memcpy(lowest_rec, recs[lowest], NODE_SIZE);

            /* write the record */
            if (fp_intermediate) {
                /* write the record to intermediate tmp file */
                rv = skStreamWrite(fp_intermediate, lowest_rec, NODE_SIZE);
                if (NODE_SIZE != rv) {
                    skAppPrintSyserror(
                        "Error writing record to temporary file '%s'",
                        skTempFileGetName(tmpctx, tmp_idx_intermediate));
                    appExit(EXIT_FAILURE);
                }
            } else {
                /* we successfully opened all (remaining) temp files,
                 * write to record to the final destination */
                rv = skStreamWriteRecord(out_rwios, (rwRec*)lowest_rec);
                if (0 != rv) {
                    skStreamPrintLastErr(out_rwios, rv, &skAppPrintErr);
                    if (SKSTREAM_ERROR_IS_FATAL(rv)) {
                        appExit(EXIT_FAILURE);
                    }
                }
            }

            /* replace the record we just processed and loop over all
             * files until we get a record that is not a duplicate */
            do {
                if ((rv = skStreamRead(fps[lowest], recs[lowest], NODE_SIZE))
                    != NODE_SIZE)
                {
                    /* read failed.  there is no more data for this
                     * file; remove it from the heap; if the heap is
                     * empty, exit the loop */
                    skHeapExtractTop(heap, NULL);
                    --heap_count;
#if TRACEMSG_LEVEL > 0
                    if (rv == 0) {
                        TRACEMSG(
                            ("Finished reading file #%u: EOF; %u files remain",
                             (tmp_idx_a + lowest), heap_count));
                    } else if (rv > 0) {
                        TRACEMSG(
                            ("Finished reading file #%u: Short read "
                             "%" SK_PRIdZ "/%" PRIu32 "; %u files remain",
                             tmp_idx_a + lowest, rv, NODE_SIZE, heap_count));
                    } else {
                        skStreamLastErrMessage(
                            fps[open_count], rv, errbuf, sizeof(errbuf));
                        TRACEMSG(
                            ("Finished reading file #%u: %s; %u files remain",
                             (tmp_idx_a + lowest), errbuf, heap_count));
                    }
#endif  /* TRACEMSG_LEVEL */
                    if (0 == heap_count) {
                        break;
                    }

                } else if (rwrecCompare(lowest_rec, recs[lowest])) {
                    /* read succeeded.  new record is not a
                     * duplicate and we insert it into the heap */
                    /* FIXME: This comparison reduces work when the
                     * keys are the same, but it adds another
                     * comparison when the keys are different; is this
                     * an overall win or lose? */
                    skHeapReplaceTop(heap, &lowest, NULL);

                } else {
                    /* read succeeded.  record is a duplicate; ignore
                     * the record and leave the heap unchanged */
                    continue;
                }

                /* get the record at the top of the heap and see if it
                 * is a duplicate; if it is, ignore it. */
                skHeapPeekTop(heap, (skheapnode_t*)&top_heap);
                lowest = *top_heap;
            } while (0 == rwrecCompare(lowest_rec, recs[lowest]));
        } while (heap_count > 0);

        TRACEMSG((("Finished processing #%d through #%d"),
                  tmp_idx_a, tmp_idx_b));

        /* Close all open temp files */
        for (i = 0; i < open_count; ++i) {
            skStreamDestroy(&fps[i]);
        }
        /* Delete all temp files we opened (or attempted to open) this
         * time */
        for (j = tmp_idx_a; j <= tmp_idx_b; ++j) {
            skTempFileRemove(tmpctx, j);
        }

        /* Close the intermediate temp file. */
        if (fp_intermediate) {
            rv = skStreamClose(fp_intermediate);
            if (rv) {
                skStreamLastErrMessage(
                    fp_intermediate, rv, errbuf, sizeof(errbuf));
                skAppPrintErr("Error closing temporary file: %s", errbuf);
                skStreamDestroy(&fp_intermediate);
                appExit(EXIT_FAILURE);
            }
            skStreamDestroy(&fp_intermediate);
        }

        /* Start the next merge with the next input temp file */
        tmp_idx_a = tmp_idx_b + 1;

    } while (!opened_all_temps);

    skHeapFree(heap);
}
Пример #5
0
/*
 *  status = timestampFormatParse(format_string, out_flags);
 *
 *    Parse the comma-separated list of timestamp format strings
 *    contained in 'format_string' and set 'out_flags' to the result
 *    of parsing the string.  Return 0 on success, or -1 if parsing of
 *    the values fails or conflicting values are given.
 */
static int
timestampFormatParse(
    const char         *format,
    uint32_t           *out_flags)
{
    char buf[256];
    char *errmsg;
    sk_stringmap_t *str_map = NULL;
    sk_stringmap_iter_t *iter = NULL;
    sk_stringmap_entry_t *found_entry;
    const sk_stringmap_entry_t *entry;
    int name_seen = 0;
    int zone_seen = 0;
    int rv = -1;

    /* create a stringmap of the available timestamp formats */
    if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
        skAppPrintOutOfMemory(NULL);
        goto END;
    }
    if (skStringMapAddEntries(str_map, -1, timestamp_names) != SKSTRINGMAP_OK){
        skAppPrintOutOfMemory(NULL);
        goto END;
    }
    if (skStringMapAddEntries(str_map, -1, timestamp_zones) != SKSTRINGMAP_OK){
        skAppPrintOutOfMemory(NULL);
        goto END;
    }

    /* attempt to match */
    if (skStringMapParse(str_map, format, SKSTRINGMAP_DUPES_ERROR,
                         &iter, &errmsg))
    {
        skAppPrintErr("Invalid %s: %s",
                      appOptions[OPT_TIMESTAMP_FORMAT].name, errmsg);
        goto END;
    }

    *out_flags = 0;

    while (skStringMapIterNext(iter, &found_entry, NULL) == SK_ITERATOR_OK) {
        *out_flags |= found_entry->id;
        switch (found_entry->id) {
#if 0
          case SKTIMESTAMP_NOMSEC:
            break;
#endif
          case 0:
          case SKTIMESTAMP_EPOCH:
          case SKTIMESTAMP_ISO:
          case SKTIMESTAMP_MMDDYYYY:
            if (name_seen) {
                entry = timestamp_names;
                strncpy(buf, entry->name, sizeof(buf));
                for (++entry; entry->name; ++entry) {
                    strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
                    strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
                }
                skAppPrintErr("Invalid %s: May only specify one of %s",
                              appOptions[OPT_TIMESTAMP_FORMAT].name, buf);
                goto END;
            }
            name_seen = 1;
            break;

          case SKTIMESTAMP_UTC:
          case SKTIMESTAMP_LOCAL:
            if (zone_seen) {
                entry = timestamp_zones;
                strncpy(buf, entry->name, sizeof(buf));
                for (++entry; entry->name; ++entry) {
                    strncat(buf, ",", sizeof(buf)-strlen(buf)-1);
                    strncat(buf, entry->name, sizeof(buf)-strlen(buf)-1);
                }
                skAppPrintErr("Invalid %s: May only specify one of %s",
                              appOptions[OPT_TIMESTAMP_FORMAT].name, buf);
                goto END;
            }
            zone_seen = 1;
            break;

          default:
            skAbortBadCase(found_entry->id);
        }
    }

    rv = 0;

  END:
    if (str_map) {
        skStringMapDestroy(str_map);
    }
    if (iter) {
        skStringMapIterDestroy(iter);
    }
    return rv;
}
Пример #6
0
/*
 *  status = loadschemeParse(scheme_name, load_scheme);
 *
 *    Parse the load-scheme name in 'scheme_name' and set
 *    'load_scheme' to the result of parsing the string.  Return 0 on
 *    success, or -1 if parsing of the value fails.
 */
static int
loadschemeParse(
    const char             *scheme_name,
    bin_load_scheme_enum_t *load_scheme)
{
    char buf[128];
    sk_stringmap_entry_t new_entry;
    sk_stringmap_t *str_map = NULL;
    sk_stringmap_status_t sm_err;
    sk_stringmap_entry_t *sm_entry;
    const sk_stringmap_entry_t *e;
    int rv = -1;

    memset(&new_entry, 0, sizeof(new_entry));
    new_entry.name = buf;

    /* create a stringmap of the available load-scheme names */
    if (SKSTRINGMAP_OK != skStringMapCreate(&str_map)) {
        skAppPrintOutOfMemory(NULL);
        goto END;
    }
    if (skStringMapAddEntries(str_map, -1, load_schemes) != SKSTRINGMAP_OK){
        skAppPrintOutOfMemory(NULL);
        goto END;
    }

    /* allow the integer ID of each load-scheme to work */
    for (e = load_schemes; e->name; ++e) {
        new_entry.id = e->id;
        snprintf(buf, sizeof(buf), "%u", e->id);
        if (skStringMapAddEntries(str_map, 1, &new_entry) != SKSTRINGMAP_OK) {
            skAppPrintOutOfMemory(NULL);
            goto END;
        }
    }

    /* attempt to match */
    sm_err = skStringMapGetByName(str_map, scheme_name, &sm_entry);
    switch (sm_err) {
      case SKSTRINGMAP_OK:
        *load_scheme = (bin_load_scheme_enum_t)sm_entry->id;
        rv = 0;
        break;

      case SKSTRINGMAP_PARSE_AMBIGUOUS:
        skAppPrintErr("Invalid %s: '%s' is ambiguous",
                      appOptions[OPT_LOAD_SCHEME].name, scheme_name);
        break;

      case SKSTRINGMAP_PARSE_NO_MATCH:
        skAppPrintErr("Invalid %s: '%s' is not recognized",
                      appOptions[OPT_LOAD_SCHEME].name, scheme_name);
        break;

      default:
        skAppPrintErr("Unexpected return value from string-map parser (%d)",
                      sm_err);
        break;
    }

  END:
    if (str_map) {
        skStringMapDestroy(str_map);
    }
    return rv;
}