#include "rc_file2.c"
#include <sys/wait.h>
#include <sys/time.h>
krb5_context ctx;
static krb5_error_code
test_store(const char *filename, uint8_t *tag, krb5_timestamp timestamp,
const uint32_t clockskew)
{
krb5_data tag_data = make_data(tag, TAG_LEN);
ctx->clockskew = clockskew;
(void)krb5_set_debugging_time(ctx, timestamp, 0);
return file2_store(ctx, (void *)filename, &tag_data);
}
static void
expiry_test(const char *filename, int reps)
{
krb5_error_code ret;
struct stat statbuf;
uint8_t tag[TAG_LEN] = { 0 }, seed[K5_HASH_SEED_LEN] = { 0 }, data[4];
uint32_t timestamp;
const uint32_t clockskew = 5, start = 1000;
uint64_t hashval;
int i, st;
assert((uint32_t)reps < (UINT32_MAX - start) / clockskew / 2);
for (i = 0, timestamp = start; i < reps; i++, timestamp += clockskew * 2) {
store_32_be(i, data);
hashval = k5_siphash24(data, 4, seed);
store_64_be(hashval, tag);
ret = test_store(filename, tag, timestamp, clockskew);
assert(ret == 0);
st = stat(filename, &statbuf);
assert(st == 0);
assert(statbuf.st_size <= (FIRST_TABLE_RECORDS + 1) * RECORD_LEN);
}
}
static void
store_records(const char *filename, int id, int reps, int expect_fail)
{
krb5_error_code ret;
uint8_t tag[TAG_LEN] = { 0 };
int i;
store_32_be(id, tag);
for (i = 0; i < reps; i++) {
store_32_be(i, tag + 4);
ret = test_store(filename, tag, 1000, 100);
if (ret != (expect_fail ? KRB5KRB_AP_ERR_REPEAT : 0)) {
fprintf(stderr, "store %d %d %sfail\n", id, i,
expect_fail ? "didn't " : "");
_exit(1);
}
}
}
static void
concurrency_test(const char *filename, int nchildren, int reps)
{
pid_t *pids, pid;
int i, nprocs, status;
pids = calloc(nchildren, sizeof(*pids));
assert(pids != NULL);
for (i = 0; i < nchildren; i++) {
pids[i] = fork();
assert(pids[i] != -1);
if (pids[i] == 0) {
store_records(filename, i, reps, 0);
_exit(0);
}
}
for (nprocs = nchildren; nprocs > 0; nprocs--) {
pid = wait(&status);
assert(pid != -1 && WIFEXITED(status) && WEXITSTATUS(status) == 0);
for (i = 0; i < nchildren; i++) {
if (pids[i] == pid)
store_records(filename, i, reps, 1);
}
}
free(pids);
}
static void
race_test(const char *filename, int nchildren, int reps)
{
int i, j, status, nsuccess;
uint8_t tag[TAG_LEN] = { 0 };
pid_t pid;
for (i = 0; i < reps; i++) {
store_32_be(i, tag);
for (j = 0; j < nchildren; j++) {
pid = fork();
assert(pid != -1);
if (pid == 0)
_exit(test_store(filename, tag, 1000, 100) != 0);
}
nsuccess = 0;
for (j = 0; j < nchildren; j++) {
pid = wait(&status);
assert(pid != -1);
if (WIFEXITED(status) && WEXITSTATUS(status) == 0)
nsuccess++;
}
assert(nsuccess == 1);
}
}
int
main(int argc, char **argv)
{
const char *filename, *cmd;
argv++;
assert(*argv != NULL);
if (krb5_init_context(&ctx) != 0)
abort();
assert(*argv != NULL);
filename = *argv++;
unlink(filename);
assert(*argv != NULL);
cmd = *argv++;
if (strcmp(cmd, "expiry") == 0) {
assert(argv[0] != NULL);
expiry_test(filename, atoi(argv[0]));
} else if (strcmp(cmd, "concurrent") == 0) {
assert(argv[0] != NULL && argv[1] != NULL);
concurrency_test(filename, atoi(argv[0]), atoi(argv[1]));
} else if (strcmp(cmd, "race") == 0) {
assert(argv[0] != NULL && argv[1] != NULL);
race_test(filename, atoi(argv[0]), atoi(argv[1]));
} else {
abort();
}
krb5_free_context(ctx);
return 0;
}