/*
 * Copyright 2011 Kirk J. Russell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include <unistd.h>
#include <assert.h>
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include <sys/wait.h>

static char * bstg_pathstore[] = {
    "/mnt/111/z",
    "/mnt/111/aaaa",
    "/mnt/111/bbbbb",
    "/mnt/111/ccccc",
    "/mnt/111/d",
    "/mnt/111/e",
    "/mnt/111/ffffff.fff.f",
    "/mnt/111/gggggggggggg",
    "/mnt/111/hhhh",
    "/mnt/111/iiiii.ii",
    "/mnt/111/jjjj.jj.jjjjjjjj",
    "/mnt/111/kkkk.kkkkkkkk",
    "/mnt/111/lllll",
    "/mnt/222/z",
    "/mnt/222/aaaa",
    "/mnt/222/bbbbb",
    "/mnt/222/ccccc",
    "/mnt/222/d",
    "/mnt/222/e",
    "/mnt/222/ffffff.fff.f",
    "/mnt/222/gggggggggggg",
    "/mnt/222/hhhh",
    "/mnt/222/iiiii.ii",
    "/mnt/222/jjjj.jj.jjjjjjjj",
    "/mnt/222/kkkk.kkkkkkkk",
    "/mnt/222/lllll",
    "/mnt/333/z",
    "/mnt/333/aaaa",
    "/mnt/333/bbbbb",
    "/mnt/333/ccccc",
    "/mnt/333/d",
    "/mnt/333/e",
    "/mnt/333/ffffff.fff.f",
    "/mnt/333/gggggggggggg",
    "/mnt/333/hhhh",
    "/mnt/333/iiiii.ii",
    "/mnt/333/jjjj.jj.jjjjjjjj",
    "/mnt/333/kkkk.kkkkkkkk",
    "/mnt/333/lllll",
    "/mnt/444/z",
    "/mnt/444/aaaa",
    "/mnt/444/bbbbb",
    "/mnt/444/ccccc",
    "/mnt/444/d",
    "/mnt/444/e",
    "/mnt/444/ffffff.fff.f",
    "/mnt/444/gggggggggggg",
    "/mnt/444/hhhh",
    "/mnt/444/iiiii.ii",
    "/mnt/444/jjjj.jj.jjjjjjjj",
    "/mnt/444/kkkk.kkkkkkkk",
    "/mnt/444/lllll",
    "/mnt/555/z",
    "/mnt/555/aaaa",
    "/mnt/555/bbbbb",
    "/mnt/555/ccccc",
    "/mnt/555/d",
    "/mnt/555/e",
    "/mnt/555/ffffff.fff.f",
    "/mnt/555/gggggggggggg",
    "/mnt/555/hhhh",
    "/mnt/555/iiiii.ii",
    "/mnt/555/jjjj.jj.jjjjjjjj",
    "/mnt/555/kkkk.kkkkkkkk",
    "/mnt/555/lllll",
    "/mnt/666/z",
    "/mnt/666/aaaa",
    "/mnt/666/bbbbb",
    "/mnt/666/ccccc",
    "/mnt/666/d",
    "/mnt/666/e",
    "/mnt/666/ffffff.fff.f",
    "/mnt/666/gggggggggggg",
    "/mnt/666/hhhh",
    "/mnt/666/iiiii.ii",
    "/mnt/666/jjjj.jj.jjjjjjjj",
    "/mnt/666/kkkk.kkkkkkkk",
    "/mnt/666/lllll",
    "/mnt/777/z",
    "/mnt/777/aaaa",
    "/mnt/777/bbbbb",
    "/mnt/777/ccccc",
    "/mnt/777/d",
    "/mnt/777/e",
    "/mnt/777/ffffff.fff.f",
    "/mnt/777/gggggggggggg",
    "/mnt/777/hhhh",
    "/mnt/777/iiiii.ii",
    "/mnt/777/jjjj.jj.jjjjjjjj",
    "/mnt/777/kkkk.kkkkkkkk",
    "/mnt/777/lllll",
    "/mnt/888/z",
    "/mnt/888/aaaa",
    "/mnt/888/bbbbb",
    "/mnt/888/ccccc",
    "/mnt/888/d",
    "/mnt/888/e",
    "/mnt/888/ffffff.fff.f",
    "/mnt/888/gggggggggggg",
    "/mnt/888/hhhh",
    "/mnt/888/iiiii.ii",
    "/mnt/888/jjjj.jj.jjjjjjjj",
    "/mnt/888/kkkk.kkkkkkkk",
    "/mnt/888/lllll",
    "/mnt/999/z",
    "/mnt/999/aaaa",
    "/mnt/999/bbbbb",
    "/mnt/999/ccccc",
    "/mnt/999/d",
    "/mnt/999/e",
    "/mnt/999/ffffff.fff.f",
    "/mnt/999/gggggggggggg",
    "/mnt/999/hhhh",
    "/mnt/999/iiiii.ii",
    "/mnt/999/jjjj.jj.jjjjjjjj",
    "/mnt/999/kkkk.kkkkkkkk",
    "/mnt/999/lllll",
    "/mnt/aaa/z",
    "/mnt/aaa/aaaa",
    "/mnt/aaa/bbbbb",
    "/mnt/aaa/ccccc",
    "/mnt/aaa/d",
    "/mnt/aaa/e",
    "/mnt/aaa/ffffff.fff.f",
    "/mnt/aaa/gggggggggggg",
    "/mnt/aaa/hhhh",
    "/mnt/aaa/iiiii.ii",
    "/mnt/aaa/jjjj.jj.jjjjjjjj",
    "/mnt/aaa/kkkk.kkkkkkkk",
    "/mnt/aaa/lllll",
    "/mnt/bbb/z",
    "/mnt/bbb/aaaa",
    "/mnt/bbb/bbbbb",
    "/mnt/bbb/ccccc",
    "/mnt/bbb/d",
    "/mnt/bbb/e",
    "/mnt/bbb/ffffff.fff.f",
    "/mnt/bbb/gggggggggggg",
    "/mnt/bbb/hhhh",
    "/mnt/bbb/iiiii.ii",
    "/mnt/bbb/jjjj.jj.jjjjjjjj",
    "/mnt/bbb/kkkk.kkkkkkkk",
    "/mnt/bbb/lllll",
    "/mnt/ccc/z",
    "/mnt/ccc/aaaa",
    "/mnt/ccc/bbbbb",
    "/mnt/ccc/ccccc",
    "/mnt/ccc/d",
    "/mnt/ccc/e",
    "/mnt/ccc/ffffff.fff.f",
    "/mnt/ccc/gggggggggggg",
    "/mnt/ccc/hhhh",
    "/mnt/ccc/iiiii.ii",
    "/mnt/ccc/jjjj.jj.jjjjjjjj",
    "/mnt/ccc/kkkk.kkkkkkkk",
    "/mnt/ccc/lllll",
    "/mnt/ddd/z",
    "/mnt/ddd/aaaa",
    "/mnt/ddd/bbbbb",
    "/mnt/ddd/ccccc",
    "/mnt/ddd/d",
    "/mnt/ddd/e",
    "/mnt/ddd/ffffff.fff.f",
    "/mnt/ddd/gggggggggggg",
    "/mnt/ddd/hhhh",
    "/mnt/ddd/iiiii.ii",
    "/mnt/ddd/jjjj.jj.jjjjjjjj",
    "/mnt/ddd/kkkk.kkkkkkkk",
    "/mnt/ddd/lllll",
    "/mnt/eee/z",
    "/mnt/eee/aaaa",
    "/mnt/eee/bbbbb",
    "/mnt/eee/ccccc",
    "/mnt/eee/d",
    "/mnt/eee/e",
    "/mnt/eee/ffffff.fff.f",
    "/mnt/eee/gggggggggggg",
    "/mnt/eee/hhhh",
    "/mnt/eee/iiiii.ii",
    "/mnt/eee/jjjj.jj.jjjjjjjj",
    "/mnt/eee/kkkk.kkkkkkkk",
    "/mnt/eee/lllll",
    "/mnt/fff/z",
    "/mnt/fff/aaaa",
    "/mnt/fff/bbbbb",
    "/mnt/fff/ccccc",
    "/mnt/fff/d",
    "/mnt/fff/e",
    "/mnt/fff/ffffff.fff.f",
    "/mnt/fff/gggggggggggg",
    "/mnt/fff/hhhh",
    "/mnt/fff/iiiii.ii",
    "/mnt/fff/jjjj.jj.jjjjjjjj",
    "/mnt/fff/kkkk.kkkkkkkk",
    "/mnt/fff/lllll"
};

char *
bstg_pathstore_get()
{
    return bstg_pathstore[rand() %
        ((sizeof(bstg_pathstore)/sizeof(bstg_pathstore[0])))];
}

void
dogcore()
{
    pid_t sleepchild, gcorechild;
    extern char **environ;

    /* create a child for the gcore target */
    if ((sleepchild = fork()) == 0) {
        sleep(30);
        _exit(1);
    } else if (sleepchild > 0) {
        char *token[] = { NULL, NULL, NULL, NULL, NULL };
        char buf[64];
        int status;

        /* use the first process as the target */
        snprintf(buf, sizeof(buf), "%d", sleepchild);
        token[0] = "gcore";
        token[1] = "-c";
        token[2] = bstg_pathstore_get();
        token[3] = buf;
        assert(token[4] == NULL);

        if ((gcorechild = fork()) > 0) {
            waitpid(gcorechild, &status, 0);
        } else if (gcorechild == 0) {
            execve("/usr/bin/gcore", token, environ);
            _exit(1);
        }

        kill(sleepchild, SIGKILL);
        waitpid(sleepchild, &status, 0);
    }
}

void
dowrite()
{
    struct iovec data[] = {
        { "12", 2 },
        { NULL, 0 },
        { "12345678", 8},
    };
    static int fd = -1;

    if (fd == -1) {
        /* keep existing file open during life of this process */
        fd = open(bstg_pathstore_get(), O_RDWR|O_NONBLOCK|O_NOCTTY);
    }

    data[1].iov_base = bstg_pathstore_get();
    data[1].iov_len = strlen((char*)data[1].iov_base);
    ftruncate(fd, 0);
    pwritev(fd, data, 3, 0);
}

void
dounlink()
{
    unlink(bstg_pathstore_get());
}

void
dolink()
{
    link(bstg_pathstore_get(), bstg_pathstore_get());
}

void
domkdir()
{
    char **pdir;
    static char * bstg_dirs[] = {
        "/mnt/111", "/mnt/222", "/mnt/333", "/mnt/444",
        "/mnt/555", "/mnt/666", "/mnt/777", "/mnt/888",
        "/mnt/999", "/mnt/aaa", "/mnt/bbb", "/mnt/ccc",
        "/mnt/ddd", "/mnt/eee", "/mnt/fff", NULL
    };

    for (pdir = bstg_dirs; *pdir; pdir++) {
        mkdir(*pdir, 0777);
    }
}

void
dosync()
{
    sync();
}

int
main()
{
    unsigned x;
    int status;
    void (*funcs[])() = {
        dogcore,
        dowrite,
        dounlink,
        dolink,
        dowrite,
        dounlink,
        dolink,
        dowrite,
        dosync,
        dowrite,
        dounlink,
        dolink,
        dowrite,
        dounlink,
        dolink,
        dowrite,
    };

    /* we only can domkdir() once at startup */
    domkdir();

    /* create 128 children that loop forever running 4 operations */
    dosync();
    for (x = 0; x < 128; x++) {
        if (fork() == 0) {
            /* give child a new seed for the pathname selection */
            srand(x);

            for (;;) {
                /* each child will start looping at different function */
                (*funcs[x++ % 16]) ();
            }
            /* we never expect this code to run */
            _exit(1);
        }
    }

    /* block forever for all our children */
    while(wait(&status) > 0);
    return 0;
}

