Example #1
0
static void core_gen_tx_numeric_field(ib_tx_t *tx,
                                      const char *name,
                                      ib_num_t val)
{
    ib_field_t *f;

    assert(tx != NULL);
    assert(name != NULL);

    ib_num_t num = val;
    ib_status_t rc = ib_field_create(&f, tx->mp,
                                     name, strlen(name),
                                     IB_FTYPE_NUM,
                                     &num);
    if (rc != IB_OK) {
        ib_log_warning(tx->ib, "Failed to create \"%s\" field: %s",
                       name, ib_status_to_string(rc));
        return;
    }

    rc = ib_data_add(tx->data, f);
    if (rc != IB_OK) {
        ib_log_warning_tx(tx,
            "Failed add \"%s\" field to transaction data store: %s",
            name, ib_status_to_string(rc)
        );
    }
}
Example #2
0
static void core_gen_tx_bytestr_alias_field(ib_tx_t *tx,
                                            const char *name,
                                            ib_bytestr_t *val)
{
    ib_field_t *f;

    assert(tx != NULL);
    assert(name != NULL);
    assert(val != NULL);

    ib_status_t rc = ib_field_create_no_copy(&f, tx->mp,
                                             name, strlen(name),
                                             IB_FTYPE_BYTESTR,
                                             val);
    if (rc != IB_OK) {
        ib_log_warning(tx->ib, "Failed to create \"%s\" field: %s",
                       name, ib_status_to_string(rc));
        return;
    }

    rc = ib_data_add(tx->data, f);
    if (rc != IB_OK) {
        ib_log_warning_tx(tx,
            "Failed add \"%s\" field to transaction data store: %s",
            name, ib_status_to_string(rc)
        );
    }
}
Example #3
0
/**
 * Called to initialize the user agent module (when the module is loaded).
 *
 * Registers a handler for the request_header_finished_event event.
 *
 * @param[in,out] ib IronBee object
 * @param[in] m Module object
 * @param[in] cbdata (unused)
 *
 * @returns Status code
 */
static ib_status_t modua_init(ib_engine_t *ib, ib_module_t *m, void *cbdata)
{
    ib_status_t  rc;
    modua_match_rule_t *failed_rule;
    unsigned int failed_frule_num;

    /* Register the user agent callback */
    rc = ib_hook_tx_register(ib, request_header_finished_event,
                             modua_user_agent,
                             NULL);
    if (rc != IB_OK) {
        ib_log_error(ib, "Hook register returned %s", ib_status_to_string(rc));
    }

    /* Register the remote address callback */
    rc = ib_hook_tx_register(ib, request_header_finished_event,
                             modua_remoteip,
                             NULL);
    if (rc != IB_OK) {
        ib_log_error(ib, "Hook register returned %s", ib_status_to_string(rc));
    }

    /* Initializations */
    rc = modua_ruleset_init(&failed_rule, &failed_frule_num);
    if (rc != IB_OK) {
        ib_log_error(ib,
                     "User agent rule initialization failed"
                     " on rule %s field rule #%d: %s",
                     failed_rule->label, failed_frule_num, ib_status_to_string(rc));
    }

    /* Get the rules */
    modua_match_ruleset = modua_ruleset_get( );
    if (modua_match_ruleset == NULL) {
        ib_log_error(ib, "Failed to get user agent rule list: %s", ib_status_to_string(rc));
        return rc;
    }
    ib_log_debug(ib,
                 "Found %d match rules",
                 modua_match_ruleset->num_rules);

    rc = ib_data_register_indexed(ib_engine_data_config_get(ib), "remote_addr");
    if (rc != IB_OK) {
        ib_log_warning(ib,
                       "User agent failed to register \"remote_addr\" as indexed: %s",
                       ib_status_to_string(rc)
                      );
        /* Continue. */
    }

    rc = ib_data_register_indexed(ib_engine_data_config_get(ib), "UA");
    if (rc != IB_OK) {
        ib_log_warning(ib,
                       "User agent failed to register \"UA\" as indexed: %s",
                       ib_status_to_string(rc)
                      );
        /* Continue. */
    }

    return IB_OK;
}
Example #4
0
/* Called when module is loaded. */
static ib_status_t geoip_init(ib_engine_t *ib, ib_module_t *m, void *cbdata)
{
    ib_status_t    rc;
    GeoIP         *geoip_db = NULL;
    module_data_t *mod_data;

    mod_data = ib_mpool_calloc(ib_engine_pool_main_get(ib),
                               sizeof(*mod_data), 1);
    if (mod_data == NULL) {
        return IB_EALLOC;
    }

    ib_log_debug(ib, "Initializing default GeoIP database...");
    geoip_db = GeoIP_new(GEOIP_MMAP_CACHE);
    if (geoip_db == NULL) {
        ib_log_debug(ib, "Failed to initialize GeoIP database.");
        return IB_EUNKNOWN;
    }

    ib_log_debug(ib, "Initializing GeoIP database complete.");
    ib_log_debug(ib, "Registering handler...");

    /* Store off pointer to our module data structure */
    mod_data->geoip_db = geoip_db;

    /* And point the generic module data at it */
    m->data = mod_data;

    rc = ib_hook_tx_register(ib,
                             handle_context_tx_event,
                             geoip_lookup,
                             mod_data);
    if (rc != IB_OK) {
        ib_log_debug(
            ib,
            "Failed to register tx hook: %s",
            ib_status_to_string(rc));
        return rc;
    }

    ib_log_debug(ib, "Done registering handler.");

    rc = ib_var_source_register(
        &(mod_data->geoip_source),
        ib_engine_var_config_get(ib),
        IB_S2SL("GEOIP"),
        IB_PHASE_NONE, IB_PHASE_NONE
    );
    if (rc != IB_OK) {
        ib_log_warning(ib,
            "GeoIP failed to register \"GEOIP\" var: %s",
            ib_status_to_string(rc)
        );
        /* Continue */
    }

    if (rc != IB_OK) {
        ib_log_debug(ib, "Failed to load GeoIP module.");
        return rc;
    }

    geoip_directive_map[0].cbdata_cb = mod_data;

    ib_log_debug(ib, "GeoIP module loaded.");
    return IB_OK;
}
Example #5
0
File: pcre.c Project: niubl/ironbee
/**
 * Internal compilation of the modpcre pattern.
 *
 * @param[in] ib IronBee engine for logging.
 * @param[in] pool The memory pool to allocate memory out of.
 * @param[in] config Module configuration
 * @param[in] is_dfa Set to true for DFA
 * @param[out] pcpdata Pointer to new struct containing the compilation.
 * @param[in] patt The uncompiled pattern to match.
 * @param[out] errptr Pointer to an error message describing the failure.
 * @param[out] erroffset The location of the failure, if this fails.
 *
 * @returns IronBee status. IB_EINVAL if the pattern is invalid,
 *          IB_EALLOC if memory allocation fails or IB_OK.
 */
static ib_status_t pcre_compile_internal(ib_engine_t *ib,
                                         ib_mpool_t *pool,
                                         const modpcre_cfg_t *config,
                                         bool is_dfa,
                                         modpcre_cpat_data_t **pcpdata,
                                         const char *patt,
                                         const char **errptr,
                                         int *erroffset)
{
    assert(ib != NULL);
    assert(pool != NULL);
    assert(config != NULL);
    assert(pcpdata != NULL);
    assert(patt != NULL);

    /* Pattern data structure we'll create */
    modpcre_cpat_data_t *cpdata;

    /* Compiled pattern. */
    pcre *cpatt = NULL;

    /* Compiled pattern size. Used to copy cpatt. */
    size_t cpatt_sz;

    /* Extra data structure. This contains the study_data pointer. */
    pcre_extra *edata = NULL;

    /* Size of edata->study_data. */
    size_t study_data_sz;

    /* How cpatt is produced. */
    const int compile_flags = PCRE_DOTALL | PCRE_DOLLAR_ENDONLY;

    /* Are we using JIT? */
    bool use_jit = !is_dfa;

#ifdef PCRE_HAVE_JIT
    if (config->use_jit == 0) {
        use_jit = false;
    }

    /* Do we want to be using JIT? */
    const bool want_jit = use_jit;
#else
    use_jit = false;
#endif /* PCRE_HAVE_JIT */

    cpatt = pcre_compile(patt, compile_flags, errptr, erroffset, NULL);

    if (*errptr != NULL) {
        ib_log_error(ib, "PCRE compile error for \"%s\": %s at offset %d",
                     patt, *errptr, *erroffset);
        return IB_EINVAL;
    }

    if (config->study) {
        if (use_jit) {
#ifdef PCRE_HAVE_JIT
            edata = pcre_study(cpatt, PCRE_STUDY_JIT_COMPILE, errptr);
            if (*errptr != NULL)  {
                pcre_free(cpatt);
                use_jit = false;
                ib_log_warning(ib, "PCRE-JIT study failed: %s", *errptr);
            }
#endif
        }
        else {
            edata = pcre_study(cpatt, 0, errptr);
            if (*errptr != NULL)  {
                pcre_free(cpatt);
                ib_log_error(ib, "PCRE study failed: %s", *errptr);
            }
        }
    }
    else if (use_jit) {
        ib_log_warning(ib, "PCRE: Disabling JIT because study disabled");
        use_jit = false;
    }

#ifdef PCRE_HAVE_JIT
    /* The check to see if JIT compilation was a success changed in 8.20RC1
       now uses pcre_fullinfo see doc/pcrejit.3 */
    if (use_jit) {
        int rc;
        int pcre_jit_ret;

        rc = pcre_fullinfo(cpatt, edata, PCRE_INFO_JIT, &pcre_jit_ret);
        if (rc != 0) {
            ib_log_error(ib, "PCRE-JIT failed to get pcre_fullinfo");
            use_jit = false;
        }
        else if (pcre_jit_ret != 1) {
            ib_log_info(ib, "PCRE-JIT compiler does not support: %s", patt);
            use_jit = false;
        }
        else { /* Assume pcre_jit_ret == 1. */
            /* Do nothing */
        }
    }
    if (want_jit && !use_jit) {
        ib_log_info(ib, "Falling back to normal PCRE");
    }
#endif /*PCRE_HAVE_JIT*/

    /* Compute the size of the populated values of cpatt. */
    pcre_fullinfo(cpatt, edata, PCRE_INFO_SIZE, &cpatt_sz);
    if (edata != NULL) {
        pcre_fullinfo(cpatt, edata, PCRE_INFO_STUDYSIZE, &study_data_sz);
    }
    else {
        study_data_sz = 0;
    }

    /**
     * Below is only allocation and copy operations to pass the PCRE results
     * back to the output variable cpdata.
     */

    cpdata = (modpcre_cpat_data_t *)ib_mpool_calloc(pool, sizeof(*cpdata), 1);
    if (cpdata == NULL) {
        pcre_free(cpatt);
        pcre_free(edata);
        ib_log_error(ib,
                     "Failed to allocate cpdata of size: %zd",
                     sizeof(*cpdata));
        return IB_EALLOC;
    }

    cpdata->is_dfa = is_dfa;
    cpdata->is_jit = use_jit;
    cpdata->cpatt_sz = cpatt_sz;
    cpdata->study_data_sz = study_data_sz;

    /* Copy pattern. */
    cpdata->patt = ib_mpool_strdup(pool, patt);
    if (cpdata->patt == NULL) {
        pcre_free(cpatt);
        pcre_free(edata);
        ib_log_error(ib, "Failed to duplicate pattern string: %s", patt);
        return IB_EALLOC;
    }

    /* Copy compiled pattern. */
    cpdata->cpatt = ib_mpool_memdup(pool, cpatt, cpatt_sz);
    pcre_free(cpatt);
    if (cpdata->cpatt == NULL) {
        pcre_free(edata);
        ib_log_error(ib, "Failed to duplicate pattern of size: %zd", cpatt_sz);
        return IB_EALLOC;
    }
    ib_log_debug(ib, "PCRE copied cpatt @ %p -> %p (%zd bytes)",
                 (void *)cpatt, (void *)cpdata->cpatt, cpatt_sz);

    /* Copy extra data (study data). */
    if (edata != NULL) {

        /* Copy edata. */
        cpdata->edata = ib_mpool_memdup(pool, edata, sizeof(*edata));
        if (cpdata->edata == NULL) {
            pcre_free(edata);
            ib_log_error(ib, "Failed to duplicate edata.");
            return IB_EALLOC;
        }

        /* Copy edata->study_data. */
        if (edata->study_data != NULL) {
            cpdata->edata->study_data =
                ib_mpool_memdup(pool, edata->study_data, study_data_sz);

            if (cpdata->edata->study_data == NULL) {
                ib_log_error(ib, "Failed to study data of size: %zd",
                             study_data_sz);
                pcre_free(edata);
                return IB_EALLOC;
            }
        }
        pcre_free(edata);
    }
    else {
        cpdata->edata = ib_mpool_calloc(pool, 1, sizeof(*edata));
        if (cpdata->edata == NULL) {
            pcre_free(edata);
            ib_log_error(ib, "Failed to allocate edata.");
            return IB_EALLOC;
        }
    }

    /* Set the PCRE limits for non-DFA patterns */
    if (! is_dfa) {
        cpdata->edata->flags |=
            (PCRE_EXTRA_MATCH_LIMIT | PCRE_EXTRA_MATCH_LIMIT_RECURSION);
        cpdata->edata->match_limit =
            (unsigned long)config->match_limit;
        cpdata->edata->match_limit_recursion =
            (unsigned long)config->match_limit_recursion;
        cpdata->dfa_ws_size = 0;
    }
    else {
        cpdata->edata->match_limit = 0U;
        cpdata->edata->match_limit_recursion = 0U;
        cpdata->dfa_ws_size = (int)config->dfa_workspace_size;
    }

    /* Set stack limits for JIT */
    if (cpdata->is_jit) {
#ifdef PCRE_HAVE_JIT
        if (config->jit_stack_start == 0U) {
            cpdata->jit_stack_start =
                PCRE_JIT_STACK_START_MULT * config->match_limit_recursion;
        }
        else {
            cpdata->jit_stack_start = (int)config->jit_stack_start;
        }
        if (config->jit_stack_max == 0U) {
            cpdata->jit_stack_max =
                PCRE_JIT_STACK_MAX_MULT * config->match_limit_recursion;
        }
        else {
            cpdata->jit_stack_max = (int)config->jit_stack_max;
        }
#endif
    }
    else {
        cpdata->jit_stack_start = 0;
        cpdata->jit_stack_max = 0;
    }

    ib_log_trace(ib,
                 "Compiled pcre pattern \"%s\": "
                 "cpatt=%p edata=%p limit=%ld rlimit=%ld study=%p "
                 "dfa=%s dfa-ws-sz=%d "
                 "jit=%s jit-stack: start=%d max=%d",
                 patt,
                 (void *)cpdata->cpatt,
                 (void *)cpdata->edata,
                 cpdata->edata->match_limit,
                 cpdata->edata->match_limit_recursion,
                 cpdata->edata->study_data,
                 cpdata->is_dfa ? "yes" : "no",
                 cpdata->dfa_ws_size,
                 cpdata->is_jit ? "yes" : "no",
                 cpdata->jit_stack_start,
                 cpdata->jit_stack_max);
    *pcpdata = cpdata;

    return IB_OK;
}
Example #6
0
/**
 * Called to initialize the user agent module (when the module is loaded).
 *
 * Registers a handler for the request_header_finished_event event.
 *
 * @param[in,out] ib IronBee object
 * @param[in] m Module object
 * @param[in] cbdata (unused)
 *
 * @returns Status code
 */
static ib_status_t modua_init(ib_engine_t *ib, ib_module_t *m, void *cbdata)
{
    ib_status_t  rc;
    modua_match_rule_t *failed_rule;
    unsigned int failed_frule_num;

    /* Register the user agent callback */
    rc = ib_hook_tx_register(ib, request_header_finished_event,
                             modua_user_agent,
                             m);
    if (rc != IB_OK) {
        ib_log_error(ib, "Hook register returned %s", ib_status_to_string(rc));
    }

    /* Register the remote address callback */
    rc = ib_hook_tx_register(ib, request_header_finished_event,
                             modua_remoteip,
                             m);
    if (rc != IB_OK) {
        ib_log_error(ib, "Hook register returned %s", ib_status_to_string(rc));
    }

    /* Initializations */
    rc = modua_ruleset_init(&failed_rule, &failed_frule_num);
    if (rc != IB_OK) {
        ib_log_error(ib,
                     "User agent rule initialization failed"
                     " on rule %s field rule #%d: %s",
                     failed_rule->label, failed_frule_num, ib_status_to_string(rc));
    }

    /* Get the rules */
    modua_match_ruleset = modua_ruleset_get( );
    if (modua_match_ruleset == NULL) {
        ib_log_error(ib, "Failed to get user agent rule list: %s", ib_status_to_string(rc));
        return rc;
    }
    ib_log_debug(ib,
                 "Found %d match rules",
                 modua_match_ruleset->num_rules);

    rc = ib_var_source_register(
        NULL,
        ib_engine_var_config_get(ib),
        IB_S2SL("remote_addr"),
        IB_PHASE_NONE, IB_PHASE_NONE
    );
    if (rc != IB_OK && rc != IB_EEXIST) {
        ib_log_warning(ib,
            "User agent failed to register \"remote_addr\": %s",
            ib_status_to_string(rc)
        );
        /* Continue. */
    }

    rc = ib_var_source_register(
        NULL,
        ib_engine_var_config_get(ib),
        IB_S2SL("UA"),
        IB_PHASE_NONE, IB_PHASE_NONE
    );
    if (rc != IB_OK) {
        ib_log_warning(ib,
            "User agent failed to register \"UA\": %s",
            ib_status_to_string(rc)
        );
        /* Continue. */
    }

    rc = ib_hook_context_register(ib, context_close_event,
                                  modua_ctx_close, m);
    if (rc != IB_OK) {
        ib_log_error(ib,
                     "Could not register context close hook: %s",
                     ib_status_to_string(rc));
        return rc;
    }

    return IB_OK;
}
/**
 * Handle managed collection: JSON file persist function
 *
 * @param[in] ib Engine
 * @param[in] tx Transaction to select a context for (or NULL)
 * @param[in] module Collection manager's module object
 * @param[in] manager The collection manager object
 * @param[in] collection_name Name of the collection to populate
 * @param[in] collection Collection to populate
 * @param[in] manager_inst_data Manager instance data
 * @param[in] persist_data Persist callback data
 *
 * @returns
 *   - IB_OK on success or when @a collection_data is length 0.
 *   - IB_DECLINED if not configured to persist
 *   - IB_EUNKNOWN for file open/write/close errors
 *   - Errors returned by ib_json_encode()
 */
static ib_status_t core_managed_collection_jsonfile_persist_fn(
    const ib_engine_t             *ib,
    const ib_tx_t                 *tx,
    const ib_module_t             *module,
    const ib_collection_manager_t *manager,
    const char                    *collection_name,
    const ib_list_t               *collection,
    void                          *manager_inst_data,
    void                          *persist_data)
{
    assert(ib != NULL);
    assert(tx != NULL);
    assert(module != NULL);
    assert(manager != NULL);
    assert(collection_name != NULL);
    assert(collection != NULL);
    assert(manager_inst_data != NULL);

    const core_json_file_t *json_file =
        (const core_json_file_t *)manager_inst_data;
    int fd;
    ssize_t remain;
    char *buf;
    size_t bufsize;
    uint8_t *bufp;
    ib_status_t rc;

    if (! json_file->persist) {
        return IB_DECLINED;
    }

    rc = ib_json_encode(tx->mp, collection, true, &buf, &bufsize);
    if (rc != IB_OK) {
        ib_log_warning(ib,
                       "JSON file: failed to encode collection \"%s\": %s",
                       collection_name, strerror(errno));
        return IB_EUNKNOWN;
    }

    fd = open(json_file->path, O_WRONLY | O_CREAT | O_TRUNC,
              S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
    if (fd < 0) {
        ib_log_warning(ib, "JSON file persist: open(\"%s\") failed: %s",
                       json_file->path, strerror(errno));
        return IB_EUNKNOWN;
    }
    remain = (ssize_t)bufsize;
    bufp = (uint8_t *)buf;
    while(remain > 0) {
        ssize_t bytes = write(fd, bufp, remain);
        if (bytes <= 0) {
            if (errno == EINTR) {
                continue;
            }
            ib_log_warning(ib, "JSON file: write(\"%s\") failed: %s",
                           json_file->path, strerror(errno));
            close(fd);
            return IB_EUNKNOWN;
        }
        else {
            remain -= bytes;
            bufp += bytes;
            assert(remain >= 0);
        }
    }
    close(fd);

    return IB_OK;
}
/**
 * Handle managed collection: JSON file populate function
 *
 * @param[in] ib Engine
 * @param[in] tx Transaction to populate
 * @param[in] module Collection manager's module object
 * @param[in] collection_name The name of the collection.
 * @param[in] collection An ib_list_t of fields copied into @a collection.
 * @param[in,out] manager_inst_data Collection to populate with fields in @a
 *                collection_data.
 * @param[in] populate_data Populate callback data
 *
 * @returns
 *   - IB_OK on success or when @a collection_data is length 0.
 *   - IB_EUNKNOWN for errors from file I/O
 *   - IB_EALLOC for allocation errors
 *   - Errors from ib_json_decode_ex()
 */
static ib_status_t core_managed_collection_jsonfile_populate_fn(
    const ib_engine_t             *ib,
    const ib_tx_t                 *tx,
    const ib_module_t             *module,
    const ib_collection_manager_t *manager,
    const char                    *collection_name,
    ib_list_t                     *collection,
    void                          *manager_inst_data,
    void                          *populate_data)
{
    assert(ib != NULL);
    assert(tx != NULL);
    assert(module != NULL);
    assert(manager != NULL);
    assert(collection_name != NULL);
    assert(collection != NULL);
    assert(manager_inst_data != NULL);

    const core_json_file_t *json_file =
        (const core_json_file_t *)manager_inst_data;
    int fd;
    ssize_t filesize;
    ssize_t remain;
    uint8_t *buf;
    uint8_t *bufp;
    struct stat sbuf;
    ib_status_t rc;
    const char *error;

    /* Get the file's size */
    if (stat(json_file->path, &sbuf) < 0) {
        ib_log_warning(ib, "JSON file: stat(\"%s\") failed: %s",
                       json_file->path, strerror(errno));
        return json_file->persist ? IB_OK : IB_DECLINED;
    }
    else {
        filesize = sbuf.st_size;
        if (filesize == 0) {
            return IB_OK;
        }
    }

    fd = open(json_file->path, O_RDONLY);
    if (fd < 0) {
        ib_log_warning(ib, "JSON file: open(\"%s\") failed: %s",
                       json_file->path, strerror(errno));
        return IB_DECLINED;
    }
    buf = ib_mpool_alloc(tx->mp, filesize);
    if (buf == NULL) {
        return IB_EALLOC;
    }
    remain = filesize;
    bufp = buf;
    while(remain > 0) {
        ssize_t bytes = read(fd, bufp, remain);
        if (bytes < 0) {
            if (errno == EINTR) {
                continue;
            }
            ib_log_warning(ib, "JSON file: read(\"%s\") failed: %s",
                           json_file->path, strerror(errno));
            close(fd);
            return IB_EUNKNOWN;
        }
        else if (bytes == 0) {
            ib_log_warning(ib, "JSON file: \"%s\": end of file reached",
                           json_file->path);
            close(fd);
            return IB_EUNKNOWN;
        }
        else {
            remain -= bytes;
            bufp += bytes;
            assert(remain >= 0);
        }
    }
    close(fd);

    /* Now, decode the JSON buffer */
    rc = ib_json_decode_ex(tx->mp, buf, filesize, collection, &error);
    if (rc != IB_OK) {
        ib_log_error(ib,
                     "Error decoding JSON buffer for \"%s\": \"%s\"",
                     collection_name,
                     error == NULL ? ib_status_to_string(rc) : error);
    }
    else {
        ib_log_debug(ib,
                     "Populated collection \"%s\" from JSON file \"%s\"",
                     collection_name, json_file->path);
    }

    return rc;
}
/**
 * Handle managed collection: register for JSON file
 *
 * Examines the incoming parameters; if if it looks like a JSON file,
 * take it; otherwise do nothing (decline)
 *
 * @param[in] ib Engine
 * @param[in] module Collection manager's module object
 * @param[in] mp Memory pool to use for allocations
 * @param[in] collection_name Name of the collection
 * @param[in] uri Full collection URI
 * @param[in] uri_scheme URI scheme (unused)
 * @param[in] uri_data Hierarchical/data part of the URI (typically a path)
 * @param[in] params List of parameter strings
 * @param[in] register_data Selection callback data
 * @param[out] pmanager_inst_data Pointer to manager specific data
 *
 * @returns Status code:
 *   - IB_DECLINED Parameters not recognized
 *   - IB_OK All OK, parameters recognized
 *   - IB_Exxx Other error
 */
static ib_status_t core_managed_collection_jsonfile_register_fn(
    const ib_engine_t              *ib,
    const ib_module_t              *module,
    const ib_collection_manager_t  *manager,
    ib_mpool_t                     *mp,
    const char                     *collection_name,
    const char                     *uri,
    const char                     *uri_scheme,
    const char                     *uri_data,
    const ib_list_t                *params,
    void                           *register_data,
    void                          **pmanager_inst_data)
{
    assert(ib != NULL);
    assert(module != NULL);
    assert(mp != NULL);
    assert(collection_name != NULL);
    assert(params != NULL);
    assert(pmanager_inst_data != NULL);

    const ib_list_node_t *node;
    const char *path;
    const char *param;
    const char *path_start = uri_data;
    bool persist = false;
    core_json_file_t *json_file;

    /* Get the first element in the list */
    if (ib_list_elements(params) > 1) {
        return IB_EINVAL;
    }

    /* Look at the first param (if it exists) */
    node = ib_list_first_const(params);
    if (node != NULL) {
        param = (const char *)node->data;
        if (strcasecmp(param, "persist") == 0) {
            persist = true;
        }
        else {
            ib_log_warning(ib, "JSON file: \"%s\"; unknown parameter \"%s\"",
                           uri, param);
            return IB_EINVAL;
        }
    }

    /* Try to stat the file */
    if (!persist) {
        struct stat sbuf;
        if (stat(path_start, &sbuf) < 0) {
            ib_log_warning(ib,
                           "JSON file: Declining \"%s\"; "
                           "stat(\"%s\") failed: %s",
                           uri, path_start, strerror(errno));
            return IB_DECLINED;
        }
        if (! S_ISREG(sbuf.st_mode)) {
            ib_log_warning(ib,
                           "JSON file: Declining \"%s\"; \"%s\" is not a file",
                           uri, path_start);
            return IB_DECLINED;
        }
    }

    /* Happy now, copy the file name, be done */
    path = ib_mpool_strdup(mp, path_start);
    if (path == NULL) {
        return IB_EALLOC;
    }
    json_file = ib_mpool_alloc(mp, sizeof(*json_file));
    if (json_file == NULL) {
        return IB_EALLOC;
    }
    json_file->path    = path;
    json_file->persist = persist;

    /* Store the file object as the manager specific collection data */
    *pmanager_inst_data = json_file;

    return IB_OK;
}