1/*	$NetBSD$	*/
2
3/*++
4/* NAME
5/*	fsstone 1
6/* SUMMARY
7/*	measure directory operation overhead
8/* SYNOPSIS
9/* .fi
10/*	\fBfsstone\fR [\fB-cr\fR] [\fB-s \fIsize\fR]
11/*		\fImsg_count files_per_dir\fR
12/* DESCRIPTION
13/*	The \fBfsstone\fR command measures the cost of creating, renaming
14/*	and deleting queue files versus appending messages to existing
15/*	files and truncating them after use.
16/*
17/*	The program simulates the arrival of \fImsg_count\fR short messages,
18/*	and arranges for at most \fIfiles_per_dir\fR simultaneous files
19/*	in the same directory.
20/*
21/*	Options:
22/* .IP \fB-c\fR
23/*	Create and delete files.
24/* .IP \fB-r\fR
25/*	Rename files twice (requires \fB-c\fR).
26/* .IP \fB-s \fIsize\fR
27/*	Specify the file size in kbytes.
28/* DIAGNOSTICS
29/*	Problems are reported to the standard error stream.
30/* BUGS
31/*	The \fB-r\fR option renames files within the same directory.
32/*	For a more realistic simulation, the program should rename files
33/*	<i>between</i> directories, and should also have an option to use
34/*	<i>hashed</i> directories as implemented with, for example, the
35/*	\fBdir_forest\fR(3) module.
36/* LICENSE
37/* .ad
38/* .fi
39/*	The Secure Mailer license must be distributed with this software.
40/* AUTHOR(S)
41/*	Wietse Venema
42/*	IBM T.J. Watson Research
43/*	P.O. Box 704
44/*	Yorktown Heights, NY 10598, USA
45/*--*/
46
47/* System library. */
48
49#include <sys_defs.h>
50#include <stdio.h>
51#include <stdlib.h>
52#include <unistd.h>
53#include <string.h>
54#include <sys/time.h>
55
56/* Utility library. */
57
58#include <msg.h>
59#include <msg_vstream.h>
60
61/* Global directory. */
62
63#include <mail_version.h>
64
65/* rename_file - rename a file */
66
67static void rename_file(int old, int new)
68{
69    char    new_path[BUFSIZ];
70    char    old_path[BUFSIZ];
71
72    sprintf(new_path, "%06d", new);
73    sprintf(old_path, "%06d", old);
74    if (rename(old_path, new_path))
75	msg_fatal("rename %s to %s: %m", old_path, new_path);
76}
77
78/* make_file - create a little file and use it */
79
80static void make_file(int seqno, int size)
81{
82    char    path[BUFSIZ];
83    char    buf[1024];
84    FILE   *fp;
85    int     i;
86
87    sprintf(path, "%06d", seqno);
88    if ((fp = fopen(path, "w")) == 0)
89	msg_fatal("open %s: %m", path);
90    memset(buf, 'x', sizeof(buf));
91    for (i = 0; i < size; i++)
92	if (fwrite(buf, 1, sizeof(buf), fp) != sizeof(buf))
93	    msg_fatal("fwrite: %m");
94    if (fsync(fileno(fp)))
95	msg_fatal("fsync: %m");
96    if (fclose(fp))
97	msg_fatal("fclose: %m");
98    if ((fp = fopen(path, "r")) == 0)
99	msg_fatal("open %s: %m", path);
100    while (fgets(path, sizeof(path), fp))
101	 /* void */ ;
102    if (fclose(fp))
103	msg_fatal("fclose: %m");
104}
105
106/* use_file - use existing file */
107
108static void use_file(int seqno)
109{
110    char    path[BUFSIZ];
111    FILE   *fp;
112    int     i;
113
114    sprintf(path, "%06d", seqno);
115    if ((fp = fopen(path, "w")) == 0)
116	msg_fatal("open %s: %m", path);
117    for (i = 0; i < 400; i++)
118	fprintf(fp, "hello");
119    if (fsync(fileno(fp)))
120	msg_fatal("fsync: %m");
121    if (fclose(fp))
122	msg_fatal("fclose: %m");
123    if ((fp = fopen(path, "r+")) == 0)
124	msg_fatal("open %s: %m", path);
125    while (fgets(path, sizeof(path), fp))
126	 /* void */ ;
127    if (ftruncate(fileno(fp), (off_t) 0))
128	msg_fatal("ftruncate: %m");;
129    if (fclose(fp))
130	msg_fatal("fclose: %m");
131}
132
133/* remove_file - delete specified file */
134
135static void remove_file(int seq)
136{
137    char    path[BUFSIZ];
138
139    sprintf(path, "%06d", seq);
140    if (remove(path))
141	msg_fatal("remove %s: %m", path);
142}
143
144/* remove_silent - delete specified file, silently */
145
146static void remove_silent(int seq)
147{
148    char    path[BUFSIZ];
149
150    sprintf(path, "%06d", seq);
151    (void) remove(path);
152}
153
154/* usage - explain */
155
156static void usage(char *myname)
157{
158    msg_fatal("usage: %s [-cr] [-s size] messages directory_entries", myname);
159}
160
161MAIL_VERSION_STAMP_DECLARE;
162
163int     main(int argc, char **argv)
164{
165    int     op_count;
166    int     max_file;
167    struct timeval start, end;
168    int     do_rename = 0;
169    int     do_create = 0;
170    int     seq;
171    int     ch;
172    int     size = 2;
173
174    /*
175     * Fingerprint executables and core dumps.
176     */
177    MAIL_VERSION_STAMP_ALLOCATE;
178
179    msg_vstream_init(argv[0], VSTREAM_ERR);
180    while ((ch = GETOPT(argc, argv, "crs:")) != EOF) {
181	switch (ch) {
182	case 'c':
183	    do_create++;
184	    break;
185	case 'r':
186	    do_rename++;
187	    break;
188	case 's':
189	    if ((size = atoi(optarg)) <= 0)
190		usage(argv[0]);
191	    break;
192	default:
193	    usage(argv[0]);
194	}
195    }
196
197    if (argc - optind != 2 || (do_rename && !do_create))
198	usage(argv[0]);
199    if ((op_count = atoi(argv[optind])) <= 0)
200	usage(argv[0]);
201    if ((max_file = atoi(argv[optind + 1])) <= 0)
202	usage(argv[0]);
203
204    /*
205     * Populate the directory with little files.
206     */
207    for (seq = 0; seq < max_file; seq++)
208	make_file(seq, size);
209
210    /*
211     * Simulate arrival and delivery of mail messages.
212     */
213    GETTIMEOFDAY(&start);
214    while (op_count > 0) {
215	seq %= max_file;
216	if (do_create) {
217	    remove_file(seq);
218	    make_file(seq, size);
219	    if (do_rename) {
220		rename_file(seq, seq + max_file);
221		rename_file(seq + max_file, seq);
222	    }
223	} else {
224	    use_file(seq);
225	}
226	seq++;
227	op_count--;
228    }
229    GETTIMEOFDAY(&end);
230    if (end.tv_usec < start.tv_usec) {
231	end.tv_sec--;
232	end.tv_usec += 1000000;
233    }
234    printf("elapsed time: %ld.%06ld\n",
235	   (long) (end.tv_sec - start.tv_sec),
236	   (long) (end.tv_usec - start.tv_usec));
237
238    /*
239     * Clean up directory fillers.
240     */
241    for (seq = 0; seq < max_file; seq++)
242	remove_silent(seq);
243    return (0);
244}
245