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