c1955b4ffc82f23c92f5e117243aa46b62ffbf83
2 * Main program for agedu.
15 #include <sys/types.h>
19 #include <sys/ioctl.h>
30 void fatal(const char *fmt
, ...)
33 fprintf(stderr
, "%s: ", PNAME
);
35 vfprintf(stderr
, fmt
, ap
);
37 fprintf(stderr
, "\n");
43 dev_t datafile_dev
, filesystem_dev
;
45 time_t last_output_update
;
46 int progress
, progwidth
;
49 static int gotdata(void *vctx
, const char *pathname
, const struct stat64
*st
)
51 struct ctx
*ctx
= (struct ctx
*)vctx
;
52 struct trie_file file
;
56 * Filter out our own data file.
58 if (st
->st_dev
== ctx
->datafile_dev
&& st
->st_ino
== ctx
->datafile_ino
)
62 * Don't cross the streams^W^Wany file system boundary.
63 * (FIXME: this should be a configurable option.)
65 if (st
->st_dev
!= ctx
->filesystem_dev
)
69 * FIXME: other filtering in gotdata will be needed, when we
70 * implement serious filtering.
73 file
.blocks
= st
->st_blocks
;
74 file
.atime
= st
->st_atime
;
75 triebuild_add(ctx
->tb
, pathname
, &file
);
78 if (t
!= ctx
->last_output_update
) {
80 fprintf(stderr
, "%-*.*s\r", ctx
->progwidth
, ctx
->progwidth
,
84 ctx
->last_output_update
= t
;
90 static void run_query(const void *mappedfile
, const char *rootdir
,
95 unsigned long xi1
, xi2
;
96 unsigned long long s1
, s2
;
98 maxpathlen
= trie_maxpathlen(mappedfile
);
99 pathbuf
= snewn(maxpathlen
+ 1, char);
102 * We want to query everything between the supplied filename
103 * (inclusive) and that filename with a ^A on the end
104 * (exclusive). So find the x indices for each.
106 sprintf(pathbuf
, "%s\001", rootdir
);
107 xi1
= trie_before(mappedfile
, rootdir
);
108 xi2
= trie_before(mappedfile
, pathbuf
);
111 * Now do the lookups in the age index.
113 s1
= index_query(mappedfile
, xi1
, t
);
114 s2
= index_query(mappedfile
, xi2
, t
);
116 /* Display in units of 2 512-byte blocks = 1Kb */
117 printf("%-11llu %s\n", (s2
- s1
) / 2, rootdir
);
121 * Now scan for first-level subdirectories and report
126 trie_getpath(mappedfile
, xi1
, pathbuf
);
127 run_query(mappedfile
, pathbuf
, t
, depth
-1);
128 strcat(pathbuf
, "\001");
129 xi1
= trie_before(mappedfile
, pathbuf
);
134 int main(int argc
, char **argv
)
137 struct ctx actx
, *ctx
= &actx
;
139 off_t totalsize
, realsize
;
143 const struct trie_file
*tf
;
144 char *filename
= "agedu.dat";
145 char *rootdir
= NULL
;
147 enum { QUERY
, HTML
, SCAN
, DUMP
, HTTPD
} mode
= QUERY
;
149 int auth
= HTTPD_AUTH_MAGIC
| HTTPD_AUTH_BASIC
;
156 if (doing_opts
&& *p
== '-') {
157 if (!strcmp(p
, "--")) {
159 } else if (p
[1] == '-') {
160 char *optval
= strchr(p
, '=');
163 if (!strcmp(p
, "--help")) {
164 printf("FIXME: usage();\n");
166 } else if (!strcmp(p
, "--version")) {
167 printf("FIXME: version();\n");
169 } else if (!strcmp(p
, "--licence") ||
170 !strcmp(p
, "--license")) {
171 printf("FIXME: licence();\n");
173 } else if (!strcmp(p
, "--scan")) {
175 } else if (!strcmp(p
, "--dump")) {
177 } else if (!strcmp(p
, "--html")) {
179 } else if (!strcmp(p
, "--httpd") ||
180 !strcmp(p
, "--server")) {
182 } else if (!strcmp(p
, "--progress") ||
183 !strcmp(p
, "--scan-progress")) {
185 } else if (!strcmp(p
, "--no-progress") ||
186 !strcmp(p
, "--no-scan-progress")) {
188 } else if (!strcmp(p
, "--tty-progress") ||
189 !strcmp(p
, "--tty-scan-progress") ||
190 !strcmp(p
, "--progress-tty") ||
191 !strcmp(p
, "--scan-progress-tty")) {
193 } else if (!strcmp(p
, "--file") ||
194 !strcmp(p
, "--auth") ||
195 !strcmp(p
, "--http-auth") ||
196 !strcmp(p
, "--httpd-auth") ||
197 !strcmp(p
, "--server-auth") ||
198 !strcmp(p
, "--minimum-age") ||
199 !strcmp(p
, "--min-age") ||
200 !strcmp(p
, "--age")) {
202 * Long options requiring values.
208 fprintf(stderr
, "%s: option '%s' requires"
209 " an argument\n", PNAME
, p
);
213 if (!strcmp(p
, "--file")) {
215 } else if (!strcmp(p
, "--minimum-age") ||
216 !strcmp(p
, "--min-age") ||
217 !strcmp(p
, "--age")) {
219 } else if (!strcmp(p
, "--auth") ||
220 !strcmp(p
, "--http-auth") ||
221 !strcmp(p
, "--httpd-auth") ||
222 !strcmp(p
, "--server-auth")) {
223 if (!strcmp(optval
, "magic"))
224 auth
= HTTPD_AUTH_MAGIC
;
225 else if (!strcmp(optval
, "basic"))
226 auth
= HTTPD_AUTH_BASIC
;
227 else if (!strcmp(optval
, "none"))
228 auth
= HTTPD_AUTH_NONE
;
229 else if (!strcmp(optval
, "default"))
230 auth
= HTTPD_AUTH_MAGIC
| HTTPD_AUTH_BASIC
;
232 fprintf(stderr
, "%s: unrecognised authentication"
233 " type '%s'\n%*s options are 'magic',"
234 " 'basic', 'none', 'default'\n",
235 PNAME
, optval
, (int)strlen(PNAME
), "");
240 fprintf(stderr
, "%s: unrecognised option '%s'\n",
250 /* Options requiring arguments. */
256 } else if (--argc
> 0) {
259 fprintf(stderr
, "%s: option '-%c' requires"
260 " an argument\n", PNAME
, c
);
264 case 'f': /* data file name */
267 case 'a': /* maximum age */
276 fprintf(stderr
, "%s: unrecognised option '-%c'\n",
286 fprintf(stderr
, "%s: unexpected argument '%s'\n", PNAME
, p
);
297 fd
= open(filename
, O_RDWR
| O_TRUNC
| O_CREAT
, S_IRWXU
);
299 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
304 if (stat(rootdir
, &st
) < 0) {
305 fprintf(stderr
, "%s: %s: stat: %s\n", PNAME
, rootdir
,
309 ctx
->filesystem_dev
= st
.st_dev
;
311 if (fstat(fd
, &st
) < 0) {
312 perror("agedu: fstat");
315 ctx
->datafile_dev
= st
.st_dev
;
316 ctx
->datafile_ino
= st
.st_ino
;
318 ctx
->last_output_update
= time(NULL
);
320 /* progress==1 means report progress only if stderr is a tty */
322 progress
= isatty(2) ?
2 : 0;
323 ctx
->progress
= progress
;
326 if (progress
&& ioctl(2, TIOCGWINSZ
, &ws
) == 0)
327 ctx
->progwidth
= ws
.ws_col
- 1;
333 * Scan the directory tree, and write out the trie component
336 ctx
->tb
= triebuild_new(fd
);
337 du(rootdir
, gotdata
, ctx
);
338 count
= triebuild_finish(ctx
->tb
);
339 triebuild_free(ctx
->tb
);
342 fprintf(stderr
, "%-*s\r", ctx
->progwidth
, "");
347 * Work out how much space the cumulative index trees will
348 * take; enlarge the file, and memory-map it.
350 if (fstat(fd
, &st
) < 0) {
351 perror("agedu: fstat");
355 printf("Built pathname index, %d entries, %ju bytes\n", count
,
356 (intmax_t)st
.st_size
);
358 totalsize
= index_compute_size(st
.st_size
, count
);
360 if (lseek(fd
, totalsize
-1, SEEK_SET
) < 0) {
361 perror("agedu: lseek");
364 if (write(fd
, "\0", 1) < 1) {
365 perror("agedu: write");
369 printf("Upper bound on index file size = %ju bytes\n",
370 (intmax_t)totalsize
);
372 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
|PROT_WRITE
,MAP_SHARED
, fd
, 0);
374 perror("agedu: mmap");
378 ib
= indexbuild_new(mappedfile
, st
.st_size
, count
);
379 tw
= triewalk_new(mappedfile
);
380 while ((tf
= triewalk_next(tw
, NULL
)) != NULL
)
381 indexbuild_add(ib
, tf
);
383 realsize
= indexbuild_realsize(ib
);
386 munmap(mappedfile
, totalsize
);
387 ftruncate(fd
, realsize
);
389 printf("Actual index file size = %ju bytes\n", (intmax_t)realsize
);
390 } else if (mode
== QUERY
) {
399 if (2 != sscanf(minage
, "%d%1[DdWwMmYy]", &nunits
, unit
)) {
400 fprintf(stderr
, "%s: minimum age should be a number followed by"
401 " one of d,w,m,y\n", PNAME
);
405 if (unit
[0] == 'd') {
407 } else if (unit
[0] == 'w') {
408 t
-= 86400 * 7 * nunits
;
413 ym
= tm
.tm_year
* 12 + tm
.tm_mon
;
420 tm
.tm_year
= ym
/ 12;
426 fd
= open(filename
, O_RDONLY
);
428 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
432 if (fstat(fd
, &st
) < 0) {
433 perror("agedu: fstat");
436 totalsize
= st
.st_size
;
437 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
439 perror("agedu: mmap");
444 * Trim trailing slash, just in case.
446 pathlen
= strlen(rootdir
);
447 if (pathlen
> 0 && rootdir
[pathlen
-1] == '/')
448 rootdir
[--pathlen
] = '\0';
450 run_query(mappedfile
, rootdir
, t
, 1);
451 } else if (mode
== HTML
) {
456 fd
= open(filename
, O_RDONLY
);
458 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
462 if (fstat(fd
, &st
) < 0) {
463 perror("agedu: fstat");
466 totalsize
= st
.st_size
;
467 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
469 perror("agedu: mmap");
474 * Trim trailing slash, just in case.
476 pathlen
= strlen(rootdir
);
477 if (pathlen
> 0 && rootdir
[pathlen
-1] == '/')
478 rootdir
[--pathlen
] = '\0';
480 xi
= trie_before(mappedfile
, rootdir
);
481 html
= html_query(mappedfile
, xi
, NULL
);
483 } else if (mode
== DUMP
) {
487 fd
= open(filename
, O_RDONLY
);
489 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
493 if (fstat(fd
, &st
) < 0) {
494 perror("agedu: fstat");
497 totalsize
= st
.st_size
;
498 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
500 perror("agedu: mmap");
504 maxpathlen
= trie_maxpathlen(mappedfile
);
505 buf
= snewn(maxpathlen
, char);
507 tw
= triewalk_new(mappedfile
);
508 while ((tf
= triewalk_next(tw
, buf
)) != NULL
) {
509 printf("%s: %llu %llu\n", buf
, tf
->blocks
, tf
->atime
);
512 } else if (mode
== HTTPD
) {
513 fd
= open(filename
, O_RDONLY
);
515 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
519 if (fstat(fd
, &st
) < 0) {
520 perror("agedu: fstat");
523 totalsize
= st
.st_size
;
524 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
526 perror("agedu: mmap");
530 run_httpd(mappedfile
, auth
);