242865e4afefb9dcd633665a448bcf6eb7d59ed7
2 * Main program for agedu.
15 #include <sys/types.h>
28 void fatal(const char *fmt
, ...)
31 fprintf(stderr
, "%s: ", PNAME
);
33 vfprintf(stderr
, fmt
, ap
);
35 fprintf(stderr
, "\n");
41 dev_t datafile_dev
, filesystem_dev
;
43 time_t last_output_update
;
46 static int gotdata(void *vctx
, const char *pathname
, const struct stat64
*st
)
48 struct ctx
*ctx
= (struct ctx
*)vctx
;
49 struct trie_file file
;
53 * Filter out our own data file.
55 if (st
->st_dev
== ctx
->datafile_dev
&& st
->st_ino
== ctx
->datafile_ino
)
59 * Don't cross the streams^W^Wany file system boundary.
60 * (FIXME: this should be a configurable option.)
62 if (st
->st_dev
!= ctx
->filesystem_dev
)
66 * FIXME: other filtering in gotdata will be needed, when we
67 * implement serious filtering.
70 file
.blocks
= st
->st_blocks
;
71 file
.atime
= st
->st_atime
;
72 triebuild_add(ctx
->tb
, pathname
, &file
);
75 if (t
!= ctx
->last_output_update
) {
76 fprintf(stderr
, "%-79.79s\r", pathname
);
78 ctx
->last_output_update
= t
;
84 static void run_query(const void *mappedfile
, const char *rootdir
,
89 unsigned long xi1
, xi2
;
90 unsigned long long s1
, s2
;
92 maxpathlen
= trie_maxpathlen(mappedfile
);
93 pathbuf
= snewn(maxpathlen
+ 1, char);
96 * We want to query everything between the supplied filename
97 * (inclusive) and that filename with a ^A on the end
98 * (exclusive). So find the x indices for each.
100 sprintf(pathbuf
, "%s\001", rootdir
);
101 xi1
= trie_before(mappedfile
, rootdir
);
102 xi2
= trie_before(mappedfile
, pathbuf
);
105 * Now do the lookups in the age index.
107 s1
= index_query(mappedfile
, xi1
, t
);
108 s2
= index_query(mappedfile
, xi2
, t
);
110 /* Display in units of 2 512-byte blocks = 1Kb */
111 printf("%-11llu %s\n", (s2
- s1
) / 2, rootdir
);
115 * Now scan for first-level subdirectories and report
120 trie_getpath(mappedfile
, xi1
, pathbuf
);
121 run_query(mappedfile
, pathbuf
, t
, depth
-1);
122 strcat(pathbuf
, "\001");
123 xi1
= trie_before(mappedfile
, pathbuf
);
128 int main(int argc
, char **argv
)
131 struct ctx actx
, *ctx
= &actx
;
133 off_t totalsize
, realsize
;
137 const struct trie_file
*tf
;
138 char *filename
= "agedu.dat";
139 char *rootdir
= NULL
;
141 enum { QUERY
, HTML
, SCAN
, DUMP
, HTTPD
} mode
= QUERY
;
143 int auth
= HTTPD_AUTH_MAGIC
| HTTPD_AUTH_BASIC
;
149 if (doing_opts
&& *p
== '-') {
150 if (!strcmp(p
, "--")) {
152 } else if (p
[1] == '-') {
153 char *optval
= strchr(p
, '=');
156 if (!strcmp(p
, "--help")) {
157 printf("FIXME: usage();\n");
159 } else if (!strcmp(p
, "--version")) {
160 printf("FIXME: version();\n");
162 } else if (!strcmp(p
, "--licence") ||
163 !strcmp(p
, "--license")) {
164 printf("FIXME: licence();\n");
166 } else if (!strcmp(p
, "--scan")) {
168 } else if (!strcmp(p
, "--dump")) {
170 } else if (!strcmp(p
, "--html")) {
172 } else if (!strcmp(p
, "--httpd") ||
173 !strcmp(p
, "--server")) {
175 } else if (!strcmp(p
, "--file") ||
176 !strcmp(p
, "--auth") ||
177 !strcmp(p
, "--http-auth") ||
178 !strcmp(p
, "--httpd-auth") ||
179 !strcmp(p
, "--server-auth") ||
180 !strcmp(p
, "--minimum-age") ||
181 !strcmp(p
, "--min-age") ||
182 !strcmp(p
, "--age")) {
184 * Long options requiring values.
190 fprintf(stderr
, "%s: option '%s' requires"
191 " an argument\n", PNAME
, p
);
195 if (!strcmp(p
, "--file")) {
197 } else if (!strcmp(p
, "--minimum-age") ||
198 !strcmp(p
, "--min-age") ||
199 !strcmp(p
, "--age")) {
201 } else if (!strcmp(p
, "--auth") ||
202 !strcmp(p
, "--http-auth") ||
203 !strcmp(p
, "--httpd-auth") ||
204 !strcmp(p
, "--server-auth")) {
205 if (!strcmp(optval
, "magic"))
206 auth
= HTTPD_AUTH_MAGIC
;
207 else if (!strcmp(optval
, "basic"))
208 auth
= HTTPD_AUTH_BASIC
;
209 else if (!strcmp(optval
, "none"))
210 auth
= HTTPD_AUTH_NONE
;
211 else if (!strcmp(optval
, "default"))
212 auth
= HTTPD_AUTH_MAGIC
| HTTPD_AUTH_BASIC
;
214 fprintf(stderr
, "%s: unrecognised authentication"
215 " type '%s'\n%*s options are 'magic',"
216 " 'basic', 'none', 'default'\n",
217 PNAME
, optval
, (int)strlen(PNAME
), "");
222 fprintf(stderr
, "%s: unrecognised option '%s'\n",
232 /* Options requiring arguments. */
238 } else if (--argc
> 0) {
241 fprintf(stderr
, "%s: option '-%c' requires"
242 " an argument\n", PNAME
, c
);
246 case 'f': /* data file name */
249 case 'a': /* maximum age */
258 fprintf(stderr
, "%s: unrecognised option '-%c'\n",
268 fprintf(stderr
, "%s: unexpected argument '%s'\n", PNAME
, p
);
279 fd
= open(filename
, O_RDWR
| O_TRUNC
| O_CREAT
, S_IRWXU
);
281 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
286 if (stat(rootdir
, &st
) < 0) {
287 fprintf(stderr
, "%s: %s: stat: %s\n", PNAME
, rootdir
,
291 ctx
->filesystem_dev
= st
.st_dev
;
293 if (fstat(fd
, &st
) < 0) {
294 perror("agedu: fstat");
297 ctx
->datafile_dev
= st
.st_dev
;
298 ctx
->datafile_ino
= st
.st_ino
;
300 ctx
->last_output_update
= time(NULL
);
303 * Scan the directory tree, and write out the trie component
306 ctx
->tb
= triebuild_new(fd
);
307 du(rootdir
, gotdata
, ctx
);
308 count
= triebuild_finish(ctx
->tb
);
309 triebuild_free(ctx
->tb
);
311 fprintf(stderr
, "%-79s\r", "");
315 * Work out how much space the cumulative index trees will
316 * take; enlarge the file, and memory-map it.
318 if (fstat(fd
, &st
) < 0) {
319 perror("agedu: fstat");
323 printf("Built pathname index, %d entries, %ju bytes\n", count
,
324 (intmax_t)st
.st_size
);
326 totalsize
= index_compute_size(st
.st_size
, count
);
328 if (lseek(fd
, totalsize
-1, SEEK_SET
) < 0) {
329 perror("agedu: lseek");
332 if (write(fd
, "\0", 1) < 1) {
333 perror("agedu: write");
337 printf("Upper bound on index file size = %ju bytes\n",
338 (intmax_t)totalsize
);
340 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
|PROT_WRITE
,MAP_SHARED
, fd
, 0);
342 perror("agedu: mmap");
346 ib
= indexbuild_new(mappedfile
, st
.st_size
, count
);
347 tw
= triewalk_new(mappedfile
);
348 while ((tf
= triewalk_next(tw
, NULL
)) != NULL
)
349 indexbuild_add(ib
, tf
);
351 realsize
= indexbuild_realsize(ib
);
354 munmap(mappedfile
, totalsize
);
355 ftruncate(fd
, realsize
);
357 printf("Actual index file size = %ju bytes\n", (intmax_t)realsize
);
358 } else if (mode
== QUERY
) {
367 if (2 != sscanf(minage
, "%d%1[DdWwMmYy]", &nunits
, unit
)) {
368 fprintf(stderr
, "%s: minimum age should be a number followed by"
369 " one of d,w,m,y\n", PNAME
);
373 if (unit
[0] == 'd') {
375 } else if (unit
[0] == 'w') {
376 t
-= 86400 * 7 * nunits
;
381 ym
= tm
.tm_year
* 12 + tm
.tm_mon
;
388 tm
.tm_year
= ym
/ 12;
394 fd
= open(filename
, O_RDONLY
);
396 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
400 if (fstat(fd
, &st
) < 0) {
401 perror("agedu: fstat");
404 totalsize
= st
.st_size
;
405 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
407 perror("agedu: mmap");
412 * Trim trailing slash, just in case.
414 pathlen
= strlen(rootdir
);
415 if (pathlen
> 0 && rootdir
[pathlen
-1] == '/')
416 rootdir
[--pathlen
] = '\0';
418 run_query(mappedfile
, rootdir
, t
, 1);
419 } else if (mode
== HTML
) {
424 fd
= open(filename
, O_RDONLY
);
426 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
430 if (fstat(fd
, &st
) < 0) {
431 perror("agedu: fstat");
434 totalsize
= st
.st_size
;
435 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
437 perror("agedu: mmap");
442 * Trim trailing slash, just in case.
444 pathlen
= strlen(rootdir
);
445 if (pathlen
> 0 && rootdir
[pathlen
-1] == '/')
446 rootdir
[--pathlen
] = '\0';
448 xi
= trie_before(mappedfile
, rootdir
);
449 html
= html_query(mappedfile
, xi
, NULL
);
451 } else if (mode
== DUMP
) {
455 fd
= open(filename
, O_RDONLY
);
457 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
461 if (fstat(fd
, &st
) < 0) {
462 perror("agedu: fstat");
465 totalsize
= st
.st_size
;
466 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
468 perror("agedu: mmap");
472 maxpathlen
= trie_maxpathlen(mappedfile
);
473 buf
= snewn(maxpathlen
, char);
475 tw
= triewalk_new(mappedfile
);
476 while ((tf
= triewalk_next(tw
, buf
)) != NULL
) {
477 printf("%s: %llu %llu\n", buf
, tf
->blocks
, tf
->atime
);
480 } else if (mode
== HTTPD
) {
481 fd
= open(filename
, O_RDONLY
);
483 fprintf(stderr
, "%s: %s: open: %s\n", PNAME
, filename
,
487 if (fstat(fd
, &st
) < 0) {
488 perror("agedu: fstat");
491 totalsize
= st
.st_size
;
492 mappedfile
= mmap(NULL
, totalsize
, PROT_READ
, MAP_SHARED
, fd
, 0);
494 perror("agedu: mmap");
498 run_httpd(mappedfile
, auth
);