Blame src/tests/unlockiter.c

Packit fd8b60
/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
Packit fd8b60
/* tests/unlockiter.c - test program for unlocked iteration */
Packit fd8b60
/*
Packit fd8b60
 * Copyright (C) 2014 by the Massachusetts Institute of Technology.
Packit fd8b60
 * All rights reserved.
Packit fd8b60
 *
Packit fd8b60
 * Redistribution and use in source and binary forms, with or without
Packit fd8b60
 * modification, are permitted provided that the following conditions
Packit fd8b60
 * are met:
Packit fd8b60
 *
Packit fd8b60
 * * Redistributions of source code must retain the above copyright
Packit fd8b60
 *   notice, this list of conditions and the following disclaimer.
Packit fd8b60
 *
Packit fd8b60
 * * Redistributions in binary form must reproduce the above copyright
Packit fd8b60
 *   notice, this list of conditions and the following disclaimer in
Packit fd8b60
 *   the documentation and/or other materials provided with the
Packit fd8b60
 *   distribution.
Packit fd8b60
 *
Packit fd8b60
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit fd8b60
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit fd8b60
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
Packit fd8b60
 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
Packit fd8b60
 * COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
Packit fd8b60
 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
Packit fd8b60
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
Packit fd8b60
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
Packit fd8b60
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
Packit fd8b60
 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
Packit fd8b60
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
Packit fd8b60
 * OF THE POSSIBILITY OF SUCH DAMAGE.
Packit fd8b60
 */
Packit fd8b60
Packit fd8b60
/*
Packit fd8b60
 * Test unlocked KDB iteration.
Packit fd8b60
 */
Packit fd8b60
Packit fd8b60
#include <sys/types.h>
Packit fd8b60
#include <sys/select.h>
Packit fd8b60
#include <sys/time.h>
Packit fd8b60
#include <sys/wait.h>
Packit fd8b60
Packit fd8b60
#include <errno.h>
Packit fd8b60
#include <krb5.h>
Packit fd8b60
#include <kadm5/admin.h>
Packit fd8b60
#include <signal.h>
Packit fd8b60
#include <stdlib.h>
Packit fd8b60
#include <string.h>             /* Some platforms need memset() for FD_ZERO */
Packit fd8b60
#include <unistd.h>
Packit fd8b60
Packit fd8b60
struct cb_arg {
Packit fd8b60
    int inpipe;
Packit fd8b60
    int outpipe;
Packit fd8b60
    int timeout;
Packit fd8b60
    int done;
Packit fd8b60
};
Packit fd8b60
Packit fd8b60
/* Helper function for cb(): read a sync byte (with possible timeout), then
Packit fd8b60
 * write a sync byte. */
Packit fd8b60
static int
Packit fd8b60
syncpair_rw(const char *name, struct cb_arg *arg, char *cp, int timeout)
Packit fd8b60
{
Packit fd8b60
    struct timeval tv;
Packit fd8b60
    fd_set rset;
Packit fd8b60
    int nfds;
Packit fd8b60
Packit fd8b60
    FD_ZERO(&rset);
Packit fd8b60
    FD_SET(arg->inpipe, &rset);
Packit fd8b60
    tv.tv_sec = timeout;
Packit fd8b60
    tv.tv_usec = 0;
Packit fd8b60
Packit fd8b60
    printf("cb: waiting for %s sync pair\n", name);
Packit fd8b60
    nfds = select(arg->inpipe + 1, &rset,
Packit fd8b60
                  NULL, NULL, (timeout == 0) ? NULL : &tv;;
Packit fd8b60
    if (nfds < 0)
Packit fd8b60
        return -1;
Packit fd8b60
    if (nfds == 0) {
Packit fd8b60
        errno = ETIMEDOUT;
Packit fd8b60
        return -1;
Packit fd8b60
    }
Packit fd8b60
    if (read(arg->inpipe, cp, 1) < 0)
Packit fd8b60
        return -1;
Packit fd8b60
    printf("cb: writing %s sync pair\n", name);
Packit fd8b60
    if (write(arg->outpipe, cp, 1) < 0)
Packit fd8b60
        return -1;
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* On the first iteration only, receive and send sync bytes to the locking
Packit fd8b60
 * child to drive its locking activities. */
Packit fd8b60
static krb5_error_code
Packit fd8b60
cb(void *argin, krb5_db_entry *ent)
Packit fd8b60
{
Packit fd8b60
    struct cb_arg *arg = argin;
Packit fd8b60
    char c = '\0';
Packit fd8b60
Packit fd8b60
    if (arg->done)
Packit fd8b60
        return 0;
Packit fd8b60
Packit fd8b60
    if (syncpair_rw("first", arg, &c, 0) < 0) {
Packit fd8b60
        com_err("cb", errno, "first sync pair");
Packit fd8b60
        return errno;
Packit fd8b60
    }
Packit fd8b60
    if (syncpair_rw("second", arg, &c, arg->timeout) < 0) {
Packit fd8b60
        com_err("cb", errno, "second sync pair");
Packit fd8b60
        return errno;
Packit fd8b60
    }
Packit fd8b60
    printf("cb: waiting for final sync byte\n");
Packit fd8b60
    if (read(arg->inpipe, &c, 1) < 0) {
Packit fd8b60
        com_err("cb", errno, "final sync byte");
Packit fd8b60
        return errno;
Packit fd8b60
    }
Packit fd8b60
    arg->done = 1;
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Parent process: iterate over the KDB, using a callback that synchronizes
Packit fd8b60
 * with the locking child. */
Packit fd8b60
static int
Packit fd8b60
iterator(struct cb_arg *cb_arg, char **db_args, pid_t child)
Packit fd8b60
{
Packit fd8b60
    krb5_error_code retval;
Packit fd8b60
    krb5_context ctx;
Packit fd8b60
Packit fd8b60
    retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx;;
Packit fd8b60
    if (retval)
Packit fd8b60
        goto cleanup;
Packit fd8b60
Packit fd8b60
    retval = krb5_db_open(ctx, db_args,
Packit fd8b60
                          KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
Packit fd8b60
    if (retval)
Packit fd8b60
        goto cleanup;
Packit fd8b60
Packit fd8b60
    retval = krb5_db_iterate(ctx, NULL, cb, cb_arg, 0);
Packit fd8b60
    if (retval)
Packit fd8b60
        goto cleanup;
Packit fd8b60
Packit fd8b60
    retval = krb5_db_fini(ctx);
Packit fd8b60
Packit fd8b60
cleanup:
Packit fd8b60
    krb5_free_context(ctx);
Packit fd8b60
    if (retval) {
Packit fd8b60
        com_err("iterator", retval, "");
Packit fd8b60
        kill(child, SIGTERM);
Packit fd8b60
        exit(1);
Packit fd8b60
    }
Packit fd8b60
    return retval;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Helper function for locker(): write, then receive a sync byte. */
Packit fd8b60
static int
Packit fd8b60
syncpair_wr(const char *name, int inpipe, int outpipe, unsigned char *cp)
Packit fd8b60
{
Packit fd8b60
    printf("locker: writing %s sync pair\n", name);
Packit fd8b60
    if (write(outpipe, cp, 1) < 0)
Packit fd8b60
        return -1;
Packit fd8b60
    printf("locker: waiting for %s sync pair\n", name);
Packit fd8b60
    if (read(inpipe, cp, 1) < 0)
Packit fd8b60
        return -1;
Packit fd8b60
    return 0;
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
/* Child process: acquire and release a write lock on the KDB, synchronized
Packit fd8b60
 * with parent. */
Packit fd8b60
static int
Packit fd8b60
locker(int inpipe, int outpipe, char **db_args)
Packit fd8b60
{
Packit fd8b60
    krb5_error_code retval;
Packit fd8b60
    unsigned char c = '\0';
Packit fd8b60
    krb5_context ctx;
Packit fd8b60
Packit fd8b60
    retval = krb5_init_context_profile(NULL, KRB5_INIT_CONTEXT_KDC, &ctx;;
Packit fd8b60
    if (retval)
Packit fd8b60
        goto cleanup;
Packit fd8b60
Packit fd8b60
    retval = krb5_db_open(ctx, db_args,
Packit fd8b60
                          KRB5_KDB_OPEN_RW | KRB5_KDB_SRV_TYPE_ADMIN);
Packit fd8b60
    if (retval)
Packit fd8b60
        goto cleanup;
Packit fd8b60
Packit fd8b60
    if (syncpair_wr("first", inpipe, outpipe, &c) < 0) {
Packit fd8b60
        retval = errno;
Packit fd8b60
        goto cleanup;
Packit fd8b60
    }
Packit fd8b60
    printf("locker: acquiring lock...\n");
Packit fd8b60
    retval = krb5_db_lock(ctx, KRB5_DB_LOCKMODE_EXCLUSIVE);
Packit fd8b60
    if (retval)
Packit fd8b60
        goto cleanup;
Packit fd8b60
    printf("locker: acquired lock\n");
Packit fd8b60
    if (syncpair_wr("second", inpipe, outpipe, &c) < 0) {
Packit fd8b60
        retval = errno;
Packit fd8b60
        goto cleanup;
Packit fd8b60
    }
Packit fd8b60
    krb5_db_unlock(ctx);
Packit fd8b60
    printf("locker: released lock\n");
Packit fd8b60
    printf("locker: writing final sync byte\n");
Packit fd8b60
    if (write(outpipe, &c, 1) < 0) {
Packit fd8b60
        retval = errno;
Packit fd8b60
        goto cleanup;
Packit fd8b60
    }
Packit fd8b60
    retval = krb5_db_fini(ctx);
Packit fd8b60
cleanup:
Packit fd8b60
    if (retval)
Packit fd8b60
        com_err("locker", retval, "");
Packit fd8b60
Packit fd8b60
    krb5_free_context(ctx);
Packit fd8b60
    exit(retval != 0);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
static void
Packit fd8b60
usage(const char *prog)
Packit fd8b60
{
Packit fd8b60
    fprintf(stderr, "usage: %s [-lu] [-t timeout]\n", prog);
Packit fd8b60
    exit(1);
Packit fd8b60
}
Packit fd8b60
Packit fd8b60
int
Packit fd8b60
main(int argc, char *argv[])
Packit fd8b60
{
Packit fd8b60
    struct cb_arg cb_arg;
Packit fd8b60
    pid_t child;
Packit fd8b60
    char *db_args[2] = { NULL, NULL };
Packit fd8b60
    int c;
Packit fd8b60
    int cstatus;
Packit fd8b60
    int pipe_to_locker[2], pipe_to_iterator[2];
Packit fd8b60
Packit fd8b60
    cb_arg.timeout = 1;
Packit fd8b60
    cb_arg.done = 0;
Packit fd8b60
    while ((c = getopt(argc, argv, "lt:u")) != -1) {
Packit fd8b60
        switch (c) {
Packit fd8b60
        case 'l':
Packit fd8b60
            db_args[0] = "lockiter";
Packit fd8b60
            break;
Packit fd8b60
        case 't':
Packit fd8b60
            cb_arg.timeout = atoi(optarg);
Packit fd8b60
            break;
Packit fd8b60
        case 'u':
Packit fd8b60
            db_args[0] = "unlockiter";
Packit fd8b60
            break;
Packit fd8b60
        default:
Packit fd8b60
            usage(argv[0]);
Packit fd8b60
        }
Packit fd8b60
    }
Packit fd8b60
    if (pipe(pipe_to_locker) < 0) {
Packit fd8b60
        com_err(argv[0], errno, "pipe(p_il)");
Packit fd8b60
        exit(1);
Packit fd8b60
    }
Packit fd8b60
    if (pipe(pipe_to_iterator) < 0) {
Packit fd8b60
        com_err(argv[0], errno, "pipe(p_li)");
Packit fd8b60
        exit(1);
Packit fd8b60
    }
Packit fd8b60
    cb_arg.inpipe = pipe_to_iterator[0];
Packit fd8b60
    cb_arg.outpipe = pipe_to_locker[1];
Packit fd8b60
    child = fork();
Packit fd8b60
    switch (child) {
Packit fd8b60
    case -1:
Packit fd8b60
        com_err(argv[0], errno, "fork");
Packit fd8b60
        exit(1);
Packit fd8b60
        break;
Packit fd8b60
    case 0:
Packit fd8b60
        locker(pipe_to_locker[0], pipe_to_iterator[1], db_args);
Packit fd8b60
        break;
Packit fd8b60
    default:
Packit fd8b60
        if (iterator(&cb_arg, db_args, child))
Packit fd8b60
            exit(1);
Packit fd8b60
        if (wait(&cstatus) < 0) {
Packit fd8b60
            com_err(argv[0], errno, "wait");
Packit fd8b60
            exit(1);
Packit fd8b60
        }
Packit fd8b60
        if (WIFSIGNALED(cstatus))
Packit fd8b60
            exit(1);
Packit fd8b60
        if (WIFEXITED(cstatus) && WEXITSTATUS(cstatus) != 0) {
Packit fd8b60
            exit(1);
Packit fd8b60
        }
Packit fd8b60
    }
Packit fd8b60
    exit(0);
Packit fd8b60
}