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    /* APPLE */
71    snprintf(new_path, sizeof new_path, "%06d", new);
72    snprintf(old_path, sizeof old_path, "%06d", old);
73    if (rename(old_path, new_path))
74	msg_fatal("rename %s to %s: %m", old_path, new_path);
75}
76
77/* make_file - create a little file and use it */
78
79static void make_file(int seqno, int size)
80{
81    char    path[BUFSIZ];
82    char    buf[1024];
83    FILE   *fp;
84    int     i;
85
86    /* APPLE */
87    snprintf(path, sizeof 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    /* APPLE */
115    snprintf(path, sizeof path, "%06d", seqno);
116    if ((fp = fopen(path, "w")) == 0)
117	msg_fatal("open %s: %m", path);
118    for (i = 0; i < 400; i++)
119	fprintf(fp, "hello");
120    if (fsync(fileno(fp)))
121	msg_fatal("fsync: %m");
122    if (fclose(fp))
123	msg_fatal("fclose: %m");
124    if ((fp = fopen(path, "r+")) == 0)
125	msg_fatal("open %s: %m", path);
126    while (fgets(path, sizeof(path), fp))
127	 /* void */ ;
128    if (ftruncate(fileno(fp), (off_t) 0))
129	msg_fatal("ftruncate: %m");;
130    if (fclose(fp))
131	msg_fatal("fclose: %m");
132}
133
134/* remove_file - delete specified file */
135
136static void remove_file(int seq)
137{
138    char    path[BUFSIZ];
139
140    /* APPLE */
141    snprintf(path, sizeof path, "%06d", seq);
142    if (remove(path))
143	msg_fatal("remove %s: %m", path);
144}
145
146/* remove_silent - delete specified file, silently */
147
148static void remove_silent(int seq)
149{
150    char    path[BUFSIZ];
151
152    /* APPLE */
153    snprintf(path, sizeof path, "%06d", seq);
154    (void) remove(path);
155}
156
157/* usage - explain */
158
159static void usage(char *myname)
160{
161    msg_fatal("usage: %s [-cr] [-s size] messages directory_entries", myname);
162}
163
164MAIL_VERSION_STAMP_DECLARE;
165
166int     main(int argc, char **argv)
167{
168    int     op_count;
169    int     max_file;
170    struct timeval start, end;
171    int     do_rename = 0;
172    int     do_create = 0;
173    int     seq;
174    int     ch;
175    int     size = 2;
176
177    /*
178     * Fingerprint executables and core dumps.
179     */
180    MAIL_VERSION_STAMP_ALLOCATE;
181
182    msg_vstream_init(argv[0], VSTREAM_ERR);
183    while ((ch = GETOPT(argc, argv, "crs:")) != EOF) {
184	switch (ch) {
185	case 'c':
186	    do_create++;
187	    break;
188	case 'r':
189	    do_rename++;
190	    break;
191	case 's':
192	    if ((size = atoi(optarg)) <= 0)
193		usage(argv[0]);
194	    break;
195	default:
196	    usage(argv[0]);
197	}
198    }
199
200    if (argc - optind != 2 || (do_rename && !do_create))
201	usage(argv[0]);
202    if ((op_count = atoi(argv[optind])) <= 0)
203	usage(argv[0]);
204    if ((max_file = atoi(argv[optind + 1])) <= 0)
205	usage(argv[0]);
206
207    /*
208     * Populate the directory with little files.
209     */
210    for (seq = 0; seq < max_file; seq++)
211	make_file(seq, size);
212
213    /*
214     * Simulate arrival and delivery of mail messages.
215     */
216    GETTIMEOFDAY(&start);
217    while (op_count > 0) {
218	seq %= max_file;
219	if (do_create) {
220	    remove_file(seq);
221	    make_file(seq, size);
222	    if (do_rename) {
223		rename_file(seq, seq + max_file);
224		rename_file(seq + max_file, seq);
225	    }
226	} else {
227	    use_file(seq);
228	}
229	seq++;
230	op_count--;
231    }
232    GETTIMEOFDAY(&end);
233    if (end.tv_usec < start.tv_usec) {
234	end.tv_sec--;
235	end.tv_usec += 1000000;
236    }
237    printf("elapsed time: %ld.%06ld\n",
238	   (long) (end.tv_sec - start.tv_sec),
239	   (long) (end.tv_usec - start.tv_usec));
240
241    /*
242     * Clean up directory fillers.
243     */
244    for (seq = 0; seq < max_file; seq++)
245	remove_silent(seq);
246    return (0);
247}
248