/*++
/* NAME
/*	fsstone 1
/* SUMMARY
/*	measure directory operation overhead
/* SYNOPSIS
/* .fi
/*	\fBfsstone\fR [\fB-cr\fR] [\fB-s \fIsize\fR]
/*		\fImsg_count files_per_dir\fR
/* DESCRIPTION
/*	The \fBfsstone\fR command measures the cost of creating, renaming
/*	and deleting queue files versus appending messages to existing
/*	files and truncating them after use.
/*
/*	The program simulates the arrival of \fImsg_count\fR short messages,
/*	and arranges for at most \fIfiles_per_dir\fR simultaneous files
/*	in the same directory.
/*
/*	Options:
/* .IP \fB-c\fR
/*	Create and delete files.
/* .IP \fB-r\fR
/*	Rename files twice (requires \fB-c\fR).
/* .IP \fB-s \fIsize\fR
/*	Specify the file size in kbytes.
/* DIAGNOSTICS
/*	Problems are reported to the standard error stream.
/* BUGS
/*	The \fB-r\fR option renames files within the same directory.
/*	For a more realistic simulation, the program should rename files
/*	<i>between</i> directories, and should also have an option to use
/*	<i>hashed</i> directories as implemented with, for example, the
/*	\fBdir_forest\fR(3) module.
/* LICENSE
/* .ad
/* .fi
/*	The Secure Mailer license must be distributed with this software.
/* AUTHOR(S)
/*	Wietse Venema
/*	IBM T.J. Watson Research
/*	P.O. Box 704
/*	Yorktown Heights, NY 10598, USA
/*--*/

/* System library. */

#include <sys_defs.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/time.h>

/* Utility library. */

#include <msg.h>
#include <msg_vstream.h>

/* Global directory. */

#include <mail_version.h>

/* rename_file - rename a file */

static void rename_file(int old, int new)
{
    char    new_path[BUFSIZ];
    char    old_path[BUFSIZ];

    sprintf(new_path, "%06d", new);
    sprintf(old_path, "%06d", old);
    if (rename(old_path, new_path))
	msg_fatal("rename %s to %s: %m", old_path, new_path);
}

/* make_file - create a little file and use it */

static void make_file(int seqno, int size)
{
    char    path[BUFSIZ];
    char    buf[1024];
    FILE   *fp;
    int     i;

    sprintf(path, "%06d", seqno);
    if ((fp = fopen(path, "w")) == 0)
	msg_fatal("open %s: %m", path);
    memset(buf, 'x', sizeof(buf));
    for (i = 0; i < size; i++)
	if (fwrite(buf, 1, sizeof(buf), fp) != sizeof(buf))
	    msg_fatal("fwrite: %m");
    if (fsync(fileno(fp)))
	msg_fatal("fsync: %m");
    if (fclose(fp))
	msg_fatal("fclose: %m");
    if ((fp = fopen(path, "r")) == 0)
	msg_fatal("open %s: %m", path);
    while (fgets(path, sizeof(path), fp))
	 /* void */ ;
    if (fclose(fp))
	msg_fatal("fclose: %m");
}

/* use_file - use existing file */

static void use_file(int seqno)
{
    char    path[BUFSIZ];
    FILE   *fp;
    int     i;

    sprintf(path, "%06d", seqno);
    if ((fp = fopen(path, "w")) == 0)
	msg_fatal("open %s: %m", path);
    for (i = 0; i < 400; i++)
	fprintf(fp, "hello");
    if (fsync(fileno(fp)))
	msg_fatal("fsync: %m");
    if (fclose(fp))
	msg_fatal("fclose: %m");
    if ((fp = fopen(path, "r+")) == 0)
	msg_fatal("open %s: %m", path);
    while (fgets(path, sizeof(path), fp))
	 /* void */ ;
    if (ftruncate(fileno(fp), (off_t) 0))
	msg_fatal("ftruncate: %m");;
    if (fclose(fp))
	msg_fatal("fclose: %m");
}

/* remove_file - delete specified file */

static void remove_file(int seq)
{
    char    path[BUFSIZ];

    sprintf(path, "%06d", seq);
    if (remove(path))
	msg_fatal("remove %s: %m", path);
}

/* remove_silent - delete specified file, silently */

static void remove_silent(int seq)
{
    char    path[BUFSIZ];

    sprintf(path, "%06d", seq);
    (void) remove(path);
}

/* usage - explain */

static void usage(char *myname)
{
    msg_fatal("usage: %s [-cr] [-s size] messages directory_entries", myname);
}

MAIL_VERSION_STAMP_DECLARE;

int     main(int argc, char **argv)
{
    int     op_count;
    int     max_file;
    struct timeval start, end;
    int     do_rename = 0;
    int     do_create = 0;
    int     seq;
    int     ch;
    int     size = 2;

    /*
     * Fingerprint executables and core dumps.
     */
    MAIL_VERSION_STAMP_ALLOCATE;

    msg_vstream_init(argv[0], VSTREAM_ERR);
    while ((ch = GETOPT(argc, argv, "crs:")) != EOF) {
	switch (ch) {
	case 'c':
	    do_create++;
	    break;
	case 'r':
	    do_rename++;
	    break;
	case 's':
	    if ((size = atoi(optarg)) <= 0)
		usage(argv[0]);
	    break;
	default:
	    usage(argv[0]);
	}
    }

    if (argc - optind != 2 || (do_rename && !do_create))
	usage(argv[0]);
    if ((op_count = atoi(argv[optind])) <= 0)
	usage(argv[0]);
    if ((max_file = atoi(argv[optind + 1])) <= 0)
	usage(argv[0]);

    /*
     * Populate the directory with little files.
     */
    for (seq = 0; seq < max_file; seq++)
	make_file(seq, size);

    /*
     * Simulate arrival and delivery of mail messages.
     */
    GETTIMEOFDAY(&start);
    while (op_count > 0) {
	seq %= max_file;
	if (do_create) {
	    remove_file(seq);
	    make_file(seq, size);
	    if (do_rename) {
		rename_file(seq, seq + max_file);
		rename_file(seq + max_file, seq);
	    }
	} else {
	    use_file(seq);
	}
	seq++;
	op_count--;
    }
    GETTIMEOFDAY(&end);
    if (end.tv_usec < start.tv_usec) {
	end.tv_sec--;
	end.tv_usec += 1000000;
    }
    printf("elapsed time: %ld.%06ld\n",
	   (long) (end.tv_sec - start.tv_sec),
	   (long) (end.tv_usec - start.tv_usec));

    /*
     * Clean up directory fillers.
     */
    for (seq = 0; seq < max_file; seq++)
	remove_silent(seq);
    return (0);
}
