/* XXX force RRDP if one RPP fails to validate by rsync? */
+typedef int (*dl_cb)(struct cache_node *rpp);
+
struct cached_file {
char *url;
char *path;
return 0;
}
-/*
static int
dl_http(struct cache_node *node)
{
+ char *path;
+ bool changed;
+ int error;
+
if (!config_get_http_enabled()) {
pr_val_debug("HTTP is disabled.");
return 1;
}
- return http_download_cache_node(node);
+ error = cache_tmpfile(&path);
+ if (error)
+ return error;
+
+ error = http_download(node->url, path, node->mtim, &changed);
+ if (error) {
+ free(path);
+ return error;
+ }
+
+ node->flags |= CNF_CACHED | CNF_FRESH; // XXX on notification, preserve node but not file
+ if (changed) {
+ node->flags |= CNF_CHANGED;
+ node->mtim = time(NULL); // XXX catch -1
+ }
+ node->tmpdir = path;
+ return 0;
}
-*/
/* @uri is either a caRepository or a rpkiNotify */
static int
-try_uri(char const *uri, int (*download)(struct cache_node *),
- maps_dl_cb validate, void *arg)
+try_uri(char const *uri, struct cache_node *root,
+ dl_cb download, maps_dl_cb validate, void *arg)
{
struct cache_node *rpp;
int error;
pr_val_debug("Trying %s (%s)...", uri, download ? "online" : "offline");
- rpp = cachent_provide(cache.rsync, uri);
+ rpp = cachent_provide(root, uri);
if (!rpp)
return pr_val_err("Malformed URL: %s", uri);
return error;
}
- /* XXX commit the files (later, during cleanup) */
pr_val_debug("RPP validated successfully.");
return 0;
}
+static int
+try_uris(struct strlist *uris, struct cache_node *root,
+ char const *prefix, dl_cb dl, maps_dl_cb cb, void *arg)
+{
+ char **str;
+ int error;
+
+ ARRAYLIST_FOREACH(uris, str)
+ if (str_starts_with(*str, prefix)) {
+ error = try_uri(*str, root, dl, cb, arg);
+ if (error <= 0)
+ return error;
+ }
+
+ return 1;
+}
+
+int
+cache_download_uri(struct strlist *uris, maps_dl_cb cb, void *arg)
+{
+ char **_str, *str;
+ int error;
+
+ // XXX mutex
+ // XXX review result signs
+
+ /* Online attempts */
+ error = try_uris(uris, cache.https, "https://", dl_http, cb, arg);
+ if (error <= 0)
+ return error;
+ error = try_uris(uris, cache.rsync, "rsync://", dl_rsync, cb, arg);
+ if (error <= 0)
+ return error;
+
+ /* Offline attempts */
+ error = try_uris(uris, cache.https, "https://", NULL, cb, arg);
+ if (error <= 0)
+ return error;
+ return try_uris(uris, cache.rsync, "rsync://", NULL, cb, arg);
+}
+
/**
* XXX outdated comment
*
int error;
// XXX Make sure somewhere validates rpkiManifest matches caRepository.
-
/* XXX mutex */
-// /* XXX if parent is downloaded, child is downloaded. */
-// mft = cachent_provide(cache.rsync, uris->rpkiManifest);
-//
-// if (mft->flags & CNF_FRESH)
-// return cb(mft->rpp, arg); // XXX can rpp be NULL?
/* Online attempts */
// XXX review result signs
// XXX normalize rpkiNotify & caRepository?
- error = try_uri(uris->rpkiNotify, dl_rrdp, cb, arg);
+ error = try_uri(uris->rpkiNotify, cache.https, dl_rrdp, cb, arg);
if (error <= 0)
return error;
- error = try_uri(uris->caRepository, dl_rsync, cb, arg);
+ error = try_uri(uris->caRepository, cache.rsync, dl_rsync, cb, arg);
if (error <= 0)
return error;
/* Offline attempts */
- error = try_uri(uris->rpkiNotify, NULL, cb, arg);
+ error = try_uri(uris->rpkiNotify, cache.https, NULL, cb, arg);
if (error <= 0)
return error;
- return try_uri(uris->caRepository, NULL, cb, arg);
+ return try_uri(uris->caRepository, cache.rsync, NULL, cb, arg);
}
void
}
} else if (S_ISREG(st->st_mode)) {
- if ((msm->flags & CNF_RSYNC) || !pm || (pm->flags & CNF_WITHDRAWN)) {
- clock_gettime(CLOCK_REALTIME, &now); // XXX
- PR_DEBUG_MSG("%ld > %ld", now.tv_sec - st->st_atim.tv_sec, cfg_cache_threshold());
- if (now.tv_sec - st->st_atim.tv_sec > cfg_cache_threshold()) {
- pr_op_debug("Too old; abandoned.");
- goto abandoned;
- }
- pr_op_debug("Still young; preserving.");
+
+// if ((msm->flags & CNF_RSYNC) || !pm || (pm->flags & CNF_WITHDRAWN))
+ clock_gettime(CLOCK_REALTIME, &now); // XXX
+ PR_DEBUG_MSG("%ld > %ld", now.tv_sec - st->st_atim.tv_sec, cfg_cache_threshold());
+ if (now.tv_sec - st->st_atim.tv_sec > cfg_cache_threshold()) {
+ pr_op_debug("Too old; abandoned.");
+ goto abandoned;
}
+ pr_op_debug("Still young; preserving.");
} else {
pr_op_debug("Unknown type; abandoned.");
pr_op_debug("Cleaning up old abandoned and unknown cache files.");
remove_abandoned();
- pr_op_debug("Cleaning up leftover nodes.");
+ pr_op_debug("Cleaning up orphaned nodes.");
remove_leftover_nodes();
cachent_traverse(cache.rsync, remove_orphaned);
cachent_traverse(cache.https, remove_orphaned);
-
- /* XXX delete nodes for which no file exists? */
}
void
}
int
-http_download(char const *url, char const *path, curl_off_t ims, bool *changed)
+http_download(char const *src, char const *dst, curl_off_t ims, bool *changed)
{
char cmd[61];
return dl_error;
}
- ck_assert_int_eq(0, mkdir_p(path, false, 0777));
+ ck_assert_int_eq(0, mkdir_p(dst, false, 0777));
- ck_assert(snprintf(cmd, sizeof(cmd), "touch %s", path) < sizeof(cmd));
+ ck_assert(snprintf(cmd, sizeof(cmd), "touch %s", dst) < sizeof(cmd));
ck_assert_int_eq(0, system(cmd));
*changed = true;
/* Helpers */
static void
-setup_test(bool dl_type)
+setup_test(void)
{
dl_error = 0;
ck_assert_int_eq(0, system("rm -rf tmp/"));
run_dl_rsync(char const *caRepository, int expected_error,
unsigned int expected_calls)
{
- static struct sia_uris sias;
+ struct sia_uris sias;
sias.caRepository = pstrdup(caRepository);
sias.rpkiNotify = NULL;
sias_cleanup(&sias);
}
+static void
+run_dl_https(char const *url, int expected_error, unsigned int expected_calls)
+{
+ struct strlist uris;
+
+ strlist_init(&uris);
+ strlist_add(&uris, pstrdup(url));
+
+ rsync_counter = 0;
+ https_counter = 0;
+ printf("---- Downloading... ----\n");
+ ck_assert_int_eq(expected_error, cache_download_uri(&uris, okay, NULL));
+ printf("---- Downloaded. ----\n");
+ ck_assert_uint_eq(0, rsync_counter);
+ ck_assert_uint_eq(expected_calls, https_counter);
+
+ strlist_cleanup(&uris);
+}
+
static int
print_file(const char *fpath, const struct stat *sb, int typeflag,
struct FTW *ftwbuf)
return true;
}
-//static struct cache_node *
-//cachent_find(struct cache_node *root, char const *url)
-//{
-// struct cache_node *node, *child;
-// struct tokenizer tkn;
-//
-// node = root;
-// token_init(&tkn, url);
-// if (!token_next(&tkn))
-// ck_abort_msg("Path too short: %s", url);
-// if (strncmp(root->name, tkn.str, tkn.len) != 0) {
-// ck_abort_msg("Root doesn't match: %s != %.*s",
-// root->name, (int)tkn.len, tkn.str);
-// }
-//
-// while (token_next(&tkn)) {
-// if (tkn.len == 1 && tkn.str[0] == '.')
-// continue;
-// if (tkn.len == 2 && tkn.str[0] == '.' && tkn.str[1] == '.')
-// node = node->parent;
-//
-// HASH_FIND(hh, node->children, tkn.str, tkn.len, child);
-// if (child == NULL)
-// ck_abort_msg("Child not found: %s > %.*s",
-// node->name, (int)tkn.len, tkn.str);
-//
-// node = child;
-// }
-//
-// return node;
-//}
-
static void
ck_assert_cachent_eq(struct cache_node *expected, struct cache_node *actual)
{
ck_cache(rsync, unode("https", NULL));
}
+static void
+ck_cache_https(struct cache_node *https)
+{
+ ck_cache(unode("rsync", NULL), https);
+}
+
static time_t epoch;
static bool
static void
new_iteration(bool outdate)
{
+ pr_op_debug("--- Unfreshening... ---");
epoch = outdate ? get_days_ago(30) : get_days_ago(1);
cachent_traverse(cache.rsync, unfreshen);
+ cachent_traverse(cache.https, unfreshen);
ck_assert_int_eq(0, nftw("tmp/rsync", nftw_unfreshen, 32, FTW_PHYS));
ck_assert_int_eq(0, nftw("tmp/https", nftw_unfreshen, 32, FTW_PHYS));
+
+ pr_op_debug("---- Tree now stale. ----");
+ cache_print();
}
//static void
START_TEST(test_cache_download_rsync)
{
- setup_test(false);
+ setup_test();
run_dl_rsync("rsync://a.b.c/d", 0, 1);
ck_cache_rsync(
START_TEST(test_cache_download_rsync_error)
{
- setup_test(false);
+ setup_test();
dl_error = 0;
run_dl_rsync("rsync://a.b.c/d", 0, 1);
START_TEST(test_cache_cleanup_rsync)
{
- setup_test(true);
+ setup_test();
/*
* First iteration: Tree is created. No prunes, because nothing's
}
END_TEST
-//START_TEST(test_cache_cleanup_rsync_error)
-//{
-// setup_test();
-//
-// /* Set up */
-// dl_error = false;
-// run_dl_rsync("rsync://a.b.c/d", 0, 1);
-// dl_error = true;
-// run_dl_rsync("rsync://a.b.c/e", -EINVAL, 1);
-// ck_cache(
-// NODE("rsync://a.b.c/d/", 0, 1, true),
-// NODE("rsync://a.b.c/e/", -EINVAL, 0, false),
-// NULL);
-//
-// /* Node gets deleted because cached file doesn't exist */
-// do_cleanup();
-// ck_cache(NODE("rsync://a.b.c/d/", 0, 1, true), NULL);
-//
-// /*
-// * Node and file do not get deleted, because the failure is still not
-// * that old.
-// * Deletion does not depend on success or failure.
-// */
-// new_iteration(false);
-// dl_error = true;
-// run_dl_rsync("rsync://a.b.c/d", -EINVAL, 1);
-// ck_cache(NODE("rsync://a.b.c/d/", -EINVAL, 1, true), NULL);
-//
-// /* Error is old; gets deleted */
-// new_iteration(true);
-// do_cleanup();
-// ck_cache(NULL);
-//
-// cleanup_test();
-//}
-//END_TEST
-//
-//START_TEST(test_cache_download_https)
-//{
-// setup_test();
-//
-// /* Download *file* e. */
-// run_cache_download("https://a.b.c/d/e", 0, 0, 1);
-// ck_cache(NODE("https://a.b.c/d/e", 0, 1, 1), NULL);
-//
-// /* Download something else 1 */
-// run_cache_download("https://a.b.c/e", 0, 0, 1);
-// ck_cache(
-// NODE("https://a.b.c/d/e", 0, 1, 1),
-// NODE("https://a.b.c/e", 0, 1, 1),
-// NULL);
-//
-// /* Download something else 2 */
-// run_cache_download("https://x.y.z/e", 0, 0, 1);
-// ck_cache(
-// NODE("https://a.b.c/d/e", 0, 1, 1),
-// NODE("https://a.b.c/e", 0, 1, 1),
-// NODE("https://x.y.z/e", 0, 1, 1),
-// NULL);
-//
-// cleanup_test();
-//}
-//END_TEST
-//
-//START_TEST(test_cache_download_https_error)
-//{
-// setup_test();
-//
-// dl_error = false;
-// run_cache_download("https://a.b.c/d", 0, 0, 1);
-// dl_error = true;
-// run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
-// ck_cache(
-// NODE("https://a.b.c/d", 0, 1, 1),
-// NODE("https://a.b.c/e", -EINVAL, 0, 0),
-// NULL);
-//
-// /* Regardless of error, not reattempted because same iteration */
-// dl_error = true;
-// run_cache_download("https://a.b.c/d", 0, 0, 0);
-// dl_error = false;
-// run_cache_download("https://a.b.c/e", -EINVAL, 0, 0);
-// ck_cache(
-// NODE("https://a.b.c/d", 0, 1, 1),
-// NODE("https://a.b.c/e", -EINVAL, 0, 0),
-// NULL);
-//
-// cleanup_test();
-//}
-//END_TEST
-//
-//START_TEST(test_cache_cleanup_https)
-//{
-// setup_test();
-//
-// /* First iteration; make a tree and clean it */
-// new_iteration(true);
-// run_cache_download("https://a.b.c/d", 0, 0, 1);
-// run_cache_download("https://a.b.c/e", 0, 0, 1);
-// do_cleanup();
-// ck_cache(
-// NODE("https://a.b.c/d", 0, 1, 1),
-// NODE("https://a.b.c/e", 0, 1, 1),
-// NULL);
-//
-// /* Remove one branch */
-// new_iteration(true);
-// run_cache_download("https://a.b.c/d", 0, 0, 1);
-// do_cleanup();
-// ck_cache(NODE("https://a.b.c/d", 0, 1, 1), NULL);
-//
-// /* Change the one branch */
-// new_iteration(true);
-// run_cache_download("https://a.b.c/e", 0, 0, 1);
-// do_cleanup();
-// ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL);
-//
-// /* Add a child to the same branch, do not update the old one */
-// new_iteration(true);
-// run_cache_download("https://a.b.c/e/f/g", 0, 0, 1);
-// do_cleanup();
-// ck_cache(
-// NODE("https://a.b.c/e/f/g", 0, 1, 1), NULL);
-//
+START_TEST(test_cache_cleanup_rsync_error)
+{
+ setup_test();
+
+ /* Set up */
+ dl_error = 0;
+ run_dl_rsync("rsync://a.b.c/d", 0, 1);
+ dl_error = -EINVAL;
+ run_dl_rsync("rsync://a.b.c/e", -EINVAL, 1);
+ ck_cache_rsync(
+ unode("rsync",
+ unode("rsync/a.b.c",
+ uftnode("rsync/a.b.c/d", FULL, "tmp/tmp/0", NULL),
+ ufnode("rsync/a.b.c/e", FAILED, NULL), NULL), NULL));
+
+ /* Node gets deleted because cached file doesn't exist */
+ run_cleanup();
+ ck_cache_rsync(
+ unode("rsync",
+ unode("rsync/a.b.c",
+ ufnode("rsync/a.b.c/d", FULL, NULL), NULL), NULL));
+
+ /*
+ * Node and file do not get deleted, because the failure is still not
+ * that old.
+ * Deletion does not depend on success or failure.
+ */
+ new_iteration(false);
+ dl_error = -EINVAL;
+ run_dl_rsync("rsync://a.b.c/d", -EINVAL, 1);
+ ck_cache_rsync(
+ unode("rsync",
+ unode("rsync/a.b.c",
+ ufnode("rsync/a.b.c/d", FULL, NULL), NULL), NULL));
+
+ /* Error is old; gets deleted */
+ new_iteration(true);
+ run_cleanup();
+ ck_cache_rsync(unode("rsync", NULL));
+
+ cleanup_test();
+}
+END_TEST
+
+static const int HDOWNLOADED = CNF_CACHED | CNF_FRESH | CNF_CHANGED;
+static const int HVALIDATED = CNF_CACHED | CNF_VALID;
+static const int HFULL = HDOWNLOADED | HVALIDATED;
+static const int HFAILED = CNF_FRESH;
+
+START_TEST(test_cache_download_https)
+{
+ setup_test();
+
+ /* Download *file* e. */
+ run_dl_https("https://a.b.c/d/e", 0, 1);
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ unode("https/a.b.c/d",
+ uftnode("https/a.b.c/d/e", HFULL, "tmp/tmp/0", NULL), NULL), NULL), NULL));
+
+ /* Download something else 1 */
+ run_dl_https("https://a.b.c/e", 0, 1);
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ unode("https/a.b.c/d",
+ uftnode("https/a.b.c/d/e", HFULL, "tmp/tmp/0", NULL), NULL),
+ uftnode("https/a.b.c/e", HFULL, "tmp/tmp/1", NULL), NULL), NULL));
+
+ /* Download something else 2 */
+ run_dl_https("https://x.y.z/e", 0, 1);
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ unode("https/a.b.c/d",
+ uftnode("https/a.b.c/d/e", HFULL, "tmp/tmp/0", NULL), NULL),
+ uftnode("https/a.b.c/e", HFULL, "tmp/tmp/1", NULL), NULL),
+ unode("https/x.y.z",
+ uftnode("https/x.y.z/e", HFULL, "tmp/tmp/2", NULL), NULL), NULL));
+
+ cleanup_test();
+}
+END_TEST
+
+START_TEST(test_cache_download_https_error)
+{
+ setup_test();
+
+ dl_error = 0;
+ run_dl_https("https://a.b.c/d", 0, 1);
+ dl_error = -EINVAL;
+ run_dl_https("https://a.b.c/e", -EINVAL, 1);
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ uftnode("https/a.b.c/d", HFULL, "tmp/tmp/0", NULL),
+ uftnode("https/a.b.c/e", HFAILED, NULL, NULL), NULL), NULL));
+
+ /* Regardless of error, not reattempted because same iteration */
+ dl_error = -EINVAL;
+ run_dl_https("https://a.b.c/d", 0, 0);
+ dl_error = 0;
+ run_dl_https("https://a.b.c/e", -EINVAL, 0);
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ uftnode("https/a.b.c/d", HFULL, "tmp/tmp/0", NULL),
+ uftnode("https/a.b.c/e", HFAILED, NULL, NULL), NULL), NULL));
+
+ cleanup_test();
+}
+END_TEST
+
+// XXX not testing alts so far
+
+START_TEST(test_cache_cleanup_https)
+{
+ setup_test();
+
+ /* First iteration; make a tree and clean it */
+ new_iteration(true);
+ run_dl_https("https://a.b.c/d", 0, 1);
+ run_dl_https("https://a.b.c/e", 0, 1);
+ run_cleanup();
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ ufnode("https/a.b.c/d", HFULL, NULL),
+ ufnode("https/a.b.c/e", HFULL, NULL), NULL), NULL));
+
+ /* Remove one branch */
+ new_iteration(true);
+ run_dl_https("https://a.b.c/d", 0, 1);
+ run_cleanup();
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ ufnode("https/a.b.c/d", HFULL, NULL), NULL), NULL));
+
+ /* Change the one branch */
+ new_iteration(true);
+ run_dl_https("https://a.b.c/e", 0, 1);
+ run_cleanup();
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ ufnode("https/a.b.c/e", HFULL, NULL), NULL), NULL));
+
+ /* Add a child to the same branch, do not update the old one */
+ new_iteration(true);
+ PR_DEBUG;
+ run_dl_https("https://a.b.c/e/f/g", 0, 1);
+ run_cleanup();
+ ck_cache_https(
+ unode("https",
+ unode("https/a.b.c",
+ unode("https/a.b.c/e",
+ unode("https/a.b.c/e/f",
+ ufnode("https/a.b.c/e/f/g", HFULL, NULL), NULL), NULL), NULL), NULL));
+
// /*
// * Download parent, do not update child.
// * Children need to die, because parent is now a file.
// */
// new_iteration(true);
-// run_cache_download("https://a.b.c/e/f", 0, 0, 1);
-// do_cleanup();
+// run_dl_https("https://a.b.c/e/f", 0, 1);
+// run_cleanup();
// ck_cache(NODE("https://a.b.c/e/f", 0, 1, 1), NULL);
//
// /* Do it again. */
// new_iteration(true);
-// run_cache_download("https://a.b.c/e", 0, 0, 1);
-// do_cleanup();
+// run_dl_https("https://a.b.c/e", 0, 1);
+// run_cleanup();
// ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL);
//
// /* Empty the tree */
// new_iteration(true);
-// do_cleanup();
+// run_cleanup();
// ck_cache(NULL);
//
// /* Node exists, but file doesn't */
// new_iteration(true);
-// run_cache_download("https://a.b.c/e", 0, 0, 1);
-// run_cache_download("https://a.b.c/f/g/h", 0, 0, 1);
+// run_dl_https("https://a.b.c/e", 0, 1);
+// run_dl_https("https://a.b.c/f/g/h", 0, 1);
// ck_cache(
// NODE("https://a.b.c/e", 0, 1, 1),
// NODE("https://a.b.c/f/g/h", 0, 1, 1),
// NULL);
// ck_assert_int_eq(0, file_rm_rf("tmp/https/a.b.c/f/g/h"));
-// do_cleanup();
+// run_cleanup();
// ck_cache(NODE("https://a.b.c/e", 0, 1, 1), NULL);
-//
-// cleanup_test();
-//}
-//END_TEST
-//
+
+ cleanup_test();
+}
+END_TEST
+
//START_TEST(test_cache_cleanup_https_error)
//{
// setup_test();
//
// /* Set up */
// dl_error = false;
-// run_cache_download("https://a.b.c/d", 0, 0, 1);
+// run_dl_https("https://a.b.c/d", 0, 1);
// dl_error = true;
-// run_cache_download("https://a.b.c/e", -EINVAL, 0, 1);
+// run_dl_https("https://a.b.c/e", -EINVAL, 1);
// ck_cache(
// NODE("https://a.b.c/d", 0, 1, 1),
// NODE("https://a.b.c/e", -EINVAL, 0, 0),
// NULL);
//
// /* Deleted because file ENOENT. */
-// do_cleanup();
+// run_cleanup();
// ck_cache(
// NODE("https://a.b.c/d", 0, 1, 1),
// NULL);
// /* Fail d */
// new_iteration(false);
// dl_error = true;
-// run_cache_download("https://a.b.c/d", -EINVAL, 0, 1);
+// run_dl_https("https://a.b.c/d", -EINVAL, 1);
// ck_cache(NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
//
// /* Not deleted, because not old */
// new_iteration(false);
-// do_cleanup();
+// run_cleanup();
// ck_cache(NODE("https://a.b.c/d", -EINVAL, 1, 1), NULL);
//
// /* Become old */
// new_iteration(true);
-// do_cleanup();
+// run_cleanup();
// ck_cache(NULL);
//
// cleanup_test();
TCase *rsync, *https, *dot, *meta, *recover;
rsync = tcase_create("rsync");
- tcase_add_test(rsync, test_cache_download_rsync);
- tcase_add_test(rsync, test_cache_download_rsync_error);
- tcase_add_test(rsync, test_cache_cleanup_rsync);
+// tcase_add_test(rsync, test_cache_download_rsync);
+// tcase_add_test(rsync, test_cache_download_rsync_error);
+// tcase_add_test(rsync, test_cache_cleanup_rsync);
// tcase_add_test(rsync, test_cache_cleanup_rsync_error);
https = tcase_create("https");
// tcase_add_test(https, test_cache_download_https);
// tcase_add_test(https, test_cache_download_https_error);
-// tcase_add_test(https, test_cache_cleanup_https);
+ tcase_add_test(https, test_cache_cleanup_https);
// tcase_add_test(https, test_cache_cleanup_https_error);
dot = tcase_create("dot");