enum node_state state;
/* Result code of recent dl attempt (DLS_FRESH only) */
validation_verdict verdict;
- time_t attempt_ts; /* Refresh: Dl attempt. Fallback: Unused */
+ time_t attempt_ts; /* Refresh: Dl attempt. Fallback: Commit */
time_t success_ts; /* Refresh: Dl success. Fallback: Commit */
struct mft_meta mft; /* RPP fallbacks only */
init_cachedir_tag(void)
{
static char const *filename = "CACHEDIR.TAG";
- if (file_exists(filename) == ENOENT)
+ if (file_stat_errno(filename) == ENOENT)
file_write_txt(filename,
"Signature: 8a477f597d28d172789f06886806bc55\n"
"# This file is a cache directory tag created by Fort.\n"
return VV_CONTINUE;
}
-static char const *
+/* Result needs free() */
+static char *
node2file(struct cache_node const *node, struct uri const *url)
{
if (node == NULL)
return NULL;
- // XXX RRDP is const, rsync needs to be freed
return (node->rrdp)
- ? /* RRDP */ rrdp_file(node->rrdp, url)
+ ? /* RRDP */ pstrdup(rrdp_file(node->rrdp, url))
: /* rsync */ path_join(node->path, strip_rsync_module(uri_str(url)));
}
-char const *
+/* Result needs free() */
+char *
cage_map_file(struct cache_cage *cage, struct uri const *url)
{
/*
* modified either.
*/
- char const *file;
+ char *file;
file = node2file(cage->refresh, url);
if (!file)
fb = provide_node(&cache.fallback,
&commit->rpkiNotify,
&commit->caRepository);
+ fb->attempt_ts = now;
fb->success_ts = now;
pr_op_debug("mkdir -f %s", fb->path);
uri_str(&map->url));
fb = provide_node(&cache.fallback, &map->url, NULL);
+ fb->attempt_ts = now;
fb->success_ts = now;
if (is_fallback(map->path))
goto freshen;
remove_orphaned_nodes(struct cache_table *table, struct cache_node *node,
void *arg)
{
- if (file_exists(node->path) == ENOENT) {
+ if (file_stat_errno(node->path) == ENOENT) {
pr_op_debug("Missing file; deleting node: %s", node->path);
delete_node(table, node, NULL);
}
struct cache_cage;
validation_verdict cache_refresh_by_uris(struct extension_uris *,
struct cache_cage **);
-char const *cage_map_file(struct cache_cage *, struct uri const *);
+char *cage_map_file(struct cache_cage *, struct uri const *);
bool cage_downgrade(struct cache_cage *);
struct mft_meta const *cage_mft_fallback(struct cache_cage *);
void cache_commit_rpp(struct uri const *, struct uri const *, struct rpp *);
return process_dir_files(location, file_ext, empty_err, cb, arg);
}
-bool
-valid_file_or_dir(char const *location, bool check_file)
-{
- struct stat attr;
- bool is_file, is_dir;
- bool result;
-
- if (stat(location, &attr) == -1) {
- pr_op_err("stat(%s) failed: %s", location, strerror(errno));
- return false;
- }
-
- is_file = check_file && S_ISREG(attr.st_mode);
- is_dir = S_ISDIR(attr.st_mode);
-
- result = is_file || is_dir;
- if (!result)
- pr_op_err("'%s' does not seem to be a %s", location,
- check_file ? "file or directory" : "directory");
-
- return result;
-}
-
time_t
time_nonfatal(void)
{
typedef int (*foreach_file_cb)(char const *, void *);
int foreach_file(char const *, char const *, bool, foreach_file_cb, void *);
-// XXX
-bool valid_file_or_dir(char const *, bool);
-
time_t time_nonfatal(void);
time_t time_fatal(void);
#include <syslog.h>
#include "alloc.h"
-#include "common.h"
#include "config/boolean.h"
#include "config/incidences.h"
#include "config/str.h"
#include "config/work_offline.h"
#include "configure_ac.h"
#include "daemon.h"
+#include "file.h"
#include "init.h"
#include "json_handler.h"
#include "log.h"
* To add a member to this structure,
*
* 1. Add it.
- * 2. Add its metadata somewhere in @groups.
+ * 2. Add its metadata somewhere in @options.
* 3. Add default value to set_default_values().
* 4. Create the getter.
*
struct rpki_config {
/** TAL file name or directory. */
char *tal;
- /** Path of our local clone of the repository */
- char *local_repository;
+
+ struct {
+ /* Path of our local clone of the repository */
+ char *path;
+ /* Cache content expiration seconds (after last refresh) */
+ unsigned int threshold;
+ } cache;
+
/* Deprecated; does nothing. */
bool shuffle_tal_uris;
/**
.id = 'r',
.name = "local-repository",
.type = >_string,
- .offset = offsetof(struct rpki_config, local_repository),
+ .offset = offsetof(struct rpki_config, cache.path),
.doc = "Directory where the repository local cache will be stored/read",
.arg_doc = "<directory>",
.json_null_allowed = false,
+ }, {
+ .id = 1001,
+ .name = "cache.threshold",
+ .type = >_uint,
+ .offset = offsetof(struct rpki_config, cache.threshold),
+ .doc = "Cache content expiration seconds (after last refresh)",
}, {
.id = 2001,
.name = "shuffle-uris",
*/
rpki_config.tal = NULL;
- rpki_config.local_repository = pstrdup("/tmp/fort/repository");
+ rpki_config.cache.path = pstrdup("/tmp/fort/repository");
+ rpki_config.cache.threshold = 86400;
rpki_config.shuffle_tal_uris = false;
rpki_config.maximum_certificate_depth = 32;
rpki_config.slurm = NULL;
return pr_op_err("The TAL(s) location (--tal) is mandatory.");
/* A file location at --tal isn't valid when --init-tals is set */
- if (!valid_file_or_dir(rpki_config.tal, !rpki_config.init_tals))
+ if (!file_is_valid(rpki_config.tal, !rpki_config.init_tals))
return pr_op_err("Invalid TAL(s) location.");
/* Ignore the other checks */
rpki_config.server.interval.retry)
return pr_op_err("Expire interval must be greater than refresh and retry intervals");
- if (rpki_config.slurm != NULL && !valid_file_or_dir(rpki_config.slurm, true))
+ if (rpki_config.slurm != NULL && !file_is_valid(rpki_config.slurm, true))
return pr_op_err("Invalid slurm location.");
return 0;
char const *
config_get_local_repository(void)
{
- return rpki_config.local_repository;
+ return rpki_config.cache.path;
}
time_t
cfg_cache_threshold(void)
{
- return 86400; // XXX
+ return rpki_config.cache.threshold;
}
unsigned int
}
/* Wrapper for stat(), mostly for the sake of unit test mocking. */
-/* XXX needs a rename, because it returns errno. */
int
-file_exists(char const *path)
+file_stat_errno(char const *path)
{
struct stat meta;
return (stat(path, &meta) == 0) ? 0 : errno;
}
+bool
+file_is_valid(char const *location, bool allow_file)
+{
+ struct stat attr;
+ bool is_file, is_dir;
+ bool result;
+
+ if (stat(location, &attr) == -1) {
+ pr_op_err("stat(%s) failed: %s", location, strerror(errno));
+ return false;
+ }
+
+ is_file = allow_file && S_ISREG(attr.st_mode);
+ is_dir = S_ISDIR(attr.st_mode);
+
+ result = is_file || is_dir;
+ if (!result)
+ pr_op_err("'%s' does not seem to be a %s", location,
+ allow_file ? "file or directory" : "directory");
+
+ return result;
+}
+
/*
* Like remove(), but don't care if the file is already deleted.
*/
int file_load(char const *, struct file_contents *, bool);
void file_free(struct file_contents *);
-int file_exists(char const *);
+int file_stat_errno(char const *);
+bool file_is_valid(char const *, bool);
int file_rm_f(char const *);
int file_rm_rf(char const *);
print_stack_trace(strerror(error)); /* Same as above. */
}
+static void
+print_time(struct level const *lvl)
+{
+ time_t now;
+ struct tm components;
+ char str[16];
+
+ now = time(NULL);
+ if (now == ((time_t) -1))
+ return;
+ if (localtime_r(&now, &components) == NULL)
+ return;
+ if (strftime(str, sizeof(str), "%m-%d %H:%M:%S", &components) == 0)
+ return;
+
+ fprintf(lvl->stream, "%s ", str);
+}
+
static void
__vfprintf(int level, struct log_config *cfg, char const *format, va_list args)
{
- char time_buff[20];
struct level const *lvl;
- time_t now;
- struct tm stm_buff;
char const *file_name;
lvl = level2struct(level);
if (cfg->color)
fprintf(lvl->stream, "%s", lvl->color);
- now = time(NULL);
- if (now != ((time_t) -1)) {
- // XXX not catching any errors
- localtime_r(&now, &stm_buff);
- strftime(time_buff, sizeof(time_buff), "%b %e %T", &stm_buff);
- fprintf(lvl->stream, "%s ", time_buff);
- }
+ print_time(lvl);
fprintf(lvl->stream, "%s", lvl->label);
if (cfg->tag)
atomic_fetch_add(&parent->refcount, 1);
}
+static void
+__cer_cleanup(struct rpki_certificate *cer)
+{
+ map_cleanup(&cer->map);
+ if (cer->x509 != NULL)
+ X509_free(cer->x509);
+ resources_destroy(cer->resources);
+ exturis_cleanup(&cer->uris);
+ rpp_cleanup(&cer->rpp);
+}
+
void
-cer_cleanup(struct rpki_certificate *cert)
+cer_cleanup(struct rpki_certificate *cer)
{
- map_cleanup(&cert->map);
- if (cert->x509 != NULL)
- X509_free(cert->x509);
- resources_destroy(cert->resources);
- exturis_cleanup(&cert->uris);
- // XXX Recursive. Try refcounting the resources.
- if (cert->parent)
- cer_free(cert->parent);
- rpp_cleanup(&cert->rpp);
+ __cer_cleanup(cer);
+ if (cer->parent)
+ cer_free(cer->parent);
}
void
-cer_free(struct rpki_certificate *cert)
+cer_free(struct rpki_certificate *cer)
{
- if (atomic_fetch_sub(&cert->refcount, 1) == 1) {
- cer_cleanup(cert);
- free(cert);
- }
+ struct rpki_certificate *parent;
+
+ do {
+ if (atomic_fetch_sub(&cer->refcount, 1) != 1)
+ return;
+
+ __cer_cleanup(cer);
+ parent = cer->parent;
+ free(cer);
+ cer = parent;
+ } while (cer != NULL);
}
/*
struct cache_mapping *map;
unsigned int queued;
validation_verdict vv;
+ int error;
if (!ca->x509) {
if (validate_certificate(ca) != 0)
}
mft.url = ca->uris.rpkiManifest;
-retry: mft.path = (char *)cage_map_file(cage, &mft.url); /* Will not edit */
+retry: mft.path = cage_map_file(cage, &mft.url);
if (!mft.path) {
if (cage_downgrade(cage))
goto retry;
goto end;
}
- if (manifest_traverse(&mft, cage, ca) != 0) {
+ error = manifest_traverse(&mft, cage, ca);
+ free(mft.path);
+ if (error) {
if (cage_downgrade(cage))
goto retry;
vv = VV_FAIL;
struct FileAndHash *src;
struct cache_mapping *dst;
char const *ext;
- char const *path;
int error;
if (mft->fileList.list.count == 0)
uri_child(&rpp_url, (char const *)src->file.buf, src->file.size,
&dst->url);
- path = cage_map_file(cage, &dst->url);
- if (!path) {
+ dst->path = cage_map_file(cage, &dst->url);
+ if (!dst->path) {
error = pr_val_err(
"Manifest file '%s' is absent from the cache.",
uri_str(&dst->url));
goto revert;
}
- dst->path = pstrdup(path);
error = check_file_and_hash(src, dst->path);
if (error)
if (error)
goto end;
error = parse_snapshot(&new->session, tmppath, state);
-// delete_file(tmppath); XXX
+ file_rm_f(tmppath);
end: fnstack_pop();
return error;
if (error)
goto end;
error = parse_delta(notif, delta, tmppath, state);
-// delete_file(tmppath); XXX
+ file_rm_f(tmppath);
end: fnstack_pop();
return error;
init_tables();
for (d = dirs; *d; d++) {
- if (file_exists(*d) == 0)
+ if (file_stat_errno(*d) == 0)
ck_assert_int_eq(0, file_rm_rf(*d));
ck_assert_int_eq(0, mkdir(*d, CACHE_FILEMODE));
}
refresh = cage->refresh;
fallback = cage->fallback;
- ck_assert_pstr_eq(opt1, cage_map_file(cage, &uri));
+ ck_assert_pstr_eq_free(opt1, cage_map_file(cage, &uri));
ck_assert_uint_eq(!!opt2, cage_downgrade(cage));
- ck_assert_pstr_eq(opt2, cage_map_file(cage, &uri));
+ ck_assert_pstr_eq_free(opt2, cage_map_file(cage, &uri));
cage->refresh = refresh;
cage->fallback = fallback;
ck_assert_ptr_eq(NULL, uri_init(&sias.caRepository, CA_REPOSITORY));
ck_assert_str_eq(VV_CONTINUE, cache_refresh_by_uris(&sias, &cage));
ck_assert_str_eq(RPKI_NOTIFY, uri_str(&cage->rpkiNotify));
- ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free(FILE_RRDP_PATH, cage_map_file(cage, &file_url));
ck_assert_int_eq(false, cage_downgrade(cage));
- ck_assert_ptr_eq(NULL, cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free(NULL, cage_map_file(cage, &file_url));
printf("2. 2nd CA points to the same caRepository,\n");
printf(" but does not provide RRDP as an option.\n");
ck_assert_str_eq(VV_CONTINUE, cache_refresh_by_uris(&sias, &cage));
ck_assert_ptr_eq(NULL, uri_str(&cage->rpkiNotify));
- ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free(FILE_RSYNC_PATH, cage_map_file(cage, &file_url));
ck_assert_int_eq(false, cage_downgrade(cage));
- ck_assert_ptr_eq(NULL, cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free(NULL, cage_map_file(cage, &file_url));
printf("3. Commit\n");
print_tree();
ck_assert_str_eq(VV_CONTINUE, cache_refresh_by_uris(&sias, &cage));
ck_assert_ptr_eq(NULL, uri_str(&cage->rpkiNotify));
- ck_assert_str_eq(FILE_RSYNC_PATH, cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free(FILE_RSYNC_PATH, cage_map_file(cage, &file_url));
ck_assert_int_eq(true, cage_downgrade(cage));
- ck_assert_str_eq("fallback/1/0", cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free("fallback/1/0", cage_map_file(cage, &file_url));
ck_assert_ptr_eq(NULL, uri_init(&sias.rpkiNotify, RPKI_NOTIFY));
ck_assert_str_eq(VV_CONTINUE, cache_refresh_by_uris(&sias, &cage));
ck_assert_str_eq(RPKI_NOTIFY, uri_str(&cage->rpkiNotify));
- ck_assert_str_eq(FILE_RRDP_PATH, cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free(FILE_RRDP_PATH, cage_map_file(cage, &file_url));
ck_assert_int_eq(true, cage_downgrade(cage));
- ck_assert_str_eq("fallback/0/0", cage_map_file(cage, &file_url));
+ ck_assert_pstr_eq_free("fallback/0/0", cage_map_file(cage, &file_url));
uri_cleanup(&sias.rpkiNotify);
uri_cleanup(&sias.caRepository);
{
create_test_sandbox();
- ck_assert_int_eq(0, file_exists("tmp/file/abc"));
+ ck_assert_int_eq(0, file_stat_errno("tmp/file/abc"));
ck_assert_int_eq(0, file_rm_rf("tmp/file/abc"));
- ck_assert_int_eq(ENOENT, file_exists("tmp/file/abc"));
+ ck_assert_int_eq(ENOENT, file_stat_errno("tmp/file/abc"));
}
END_TEST
#include <errno.h>
#include <arpa/inet.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/stat.h>
#include <time.h>
-#include "config.h"
+
+#include "common.h"
#include "log.h"
-#include "thread_var.h"
+#include "types/map.h"
/* Some core functions, as linked from unit tests. */
MOCK_NULL(config_get_slurm, char const *, void)
MOCK(config_get_tal, char const *, "tal/", void)
MOCK(cfg_cache_threshold, time_t, 2, void)
-MOCK(config_get_mode, enum mode, STANDALONE, void)
MOCK_UINT(config_get_rrdp_delta_threshold, 5, void)
MOCK_TRUE(config_get_rsync_enabled, void)
MOCK_UINT(config_get_rsync_priority, 50, void)
MOCK_UINT(config_get_http_priority, 60, void)
MOCK_NULL(config_get_output_roa, char const *, void)
MOCK_NULL(config_get_output_bgpsec, char const *, void)
-MOCK(config_get_op_log_file_format, enum filename_format, FNF_NAME, void)
-MOCK(config_get_val_log_file_format, enum filename_format, FNF_NAME, void)
MOCK(logv_filename, char const *, path, char const *path)
MOCK_VOID(free_rpki_config, void)
MOCK_VOID(fnstack_pop, void)
MOCK_VOID(fnstack_cleanup, void)
+void
+ck_assert_pstr_eq_free(char const *expected, char *actual)
+{
+ ck_assert_pstr_eq(expected, actual);
+ free(actual);
+}
+
void
ck_assert_uri(char const *expected, struct uri const *actual)
{
void
touch_dir(char const *dir)
{
- ck_assert_int_eq(0, file_mkdir(dir, true));
+ ck_assert(mkdir(dir, CACHE_FILEMODE) == 0 || errno == EEXIST);
}
void
static unsigned int deltas_lifetime = 5;
+MOCK(config_get_mode, enum mode, STANDALONE, void)
MOCK_UINT(config_get_deltas_lifetime, deltas_lifetime, void)
MOCK_ABORT_ENUM(config_get_output_format, output_format, void)
MOCK_ABORT_VOID(db_slurm_destroy, struct db_slurm *db)
MOCK_ABORT_VOID(db_slurm_destroy, struct db_slurm *db)
MOCK_VOID(output_print_data, struct db_table const *db)
__MOCK_ABORT(config_get_local_repository, char const *, "tmp/pdu", void)
+MOCK(config_get_mode, enum mode, STANDALONE, void)
/* Mocks end */