multitape_stats.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483
  1. #include "platform.h"
  2. #include <fcntl.h>
  3. #include <stdint.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <string.h>
  7. #include <time.h>
  8. #include <unistd.h>
  9. #include "chunks.h"
  10. #include "ctassert.h"
  11. #include "hexify.h"
  12. #include "multitape_internal.h"
  13. #include "storage.h"
  14. #include "sysendian.h"
  15. #include "warnp.h"
  16. #include "multitape.h"
  17. /* Buffer length for printing dates. */
  18. #define DATEBUFLEN 100
  19. /*
  20. * "Cookie" structure created by statstape_open and passed to other functions.
  21. */
  22. struct multitape_stats_internal {
  23. uint64_t machinenum;
  24. CHUNKS_S * C;
  25. STORAGE_R * SR;
  26. };
  27. static int callback_print(void *, struct chunkheader *);
  28. /**
  29. * callback_print(cookie, ch):
  30. * Call chunks_stats_addchunk on the chunk stats cookie ${cookie} and the
  31. * chunk header ${ch}.
  32. */
  33. static int
  34. callback_print(void * cookie, struct chunkheader * ch)
  35. {
  36. CHUNKS_S * C = cookie;
  37. size_t len, zlen;
  38. int rc;
  39. /* Decode chunk header. */
  40. len = le32dec(ch->len);
  41. zlen = le32dec(ch->zlen);
  42. if ((rc = chunks_stats_addchunk(C, ch->hash, len, zlen)) == 1) {
  43. warn0("Directory is not consistent with archive: Run --fsck");
  44. rc = -1;
  45. }
  46. /* Return status. */
  47. return (rc);
  48. }
  49. /**
  50. * statstape_open(machinenum, cachedir):
  51. * Open the archive set in preparation for calls to _printglobal, _printall,
  52. * and _print.
  53. */
  54. TAPE_S *
  55. statstape_open(uint64_t machinenum, const char * cachedir)
  56. {
  57. struct multitape_stats_internal * d;
  58. /* Allocate memory. */
  59. if ((d = malloc(sizeof(struct multitape_stats_internal))) == NULL)
  60. goto err0;
  61. d->machinenum = machinenum;
  62. /* Obtain storage layer cookie. */
  63. if ((d->SR = storage_read_init(machinenum)) == NULL)
  64. goto err1;
  65. /* Obtain chunk layer cookies. */
  66. if (cachedir != NULL) {
  67. if ((d->C = chunks_stats_init(cachedir)) == NULL)
  68. goto err2;
  69. } else {
  70. d->C = NULL;
  71. }
  72. /* Success! */
  73. return (d);
  74. err2:
  75. storage_read_free(d->SR);
  76. err1:
  77. free(d);
  78. err0:
  79. /* Failure! */
  80. return (NULL);
  81. }
  82. /**
  83. * statstape_printglobal(d, csv_filename):
  84. * Print global statistics relating to a set of archives. If ${csv_filename}
  85. * is not NULL, output will be written in CSV format to that filename.
  86. */
  87. int
  88. statstape_printglobal(TAPE_S * d, const char * csv_filename)
  89. {
  90. FILE * output = stdout;
  91. int csv = 0;
  92. /* Should we output to a CSV file? */
  93. if (csv_filename != NULL)
  94. csv = 1;
  95. /* Open CSV output file, if requested. */
  96. if (csv && (output = fopen(csv_filename, "w")) == NULL)
  97. goto err0;
  98. /* Ask the chunk storage layer to do this. */
  99. if (chunks_stats_printglobal(output, d->C, csv))
  100. goto err1;
  101. /* Close CSV output file, if requested. */
  102. if (csv && fclose(output)) {
  103. warnp("fclose");
  104. goto err0;
  105. }
  106. /* Success! */
  107. return (0);
  108. err1:
  109. if ((output != stdout) && fclose(output))
  110. warnp("fclose");
  111. err0:
  112. /* Failure! */
  113. return (-1);
  114. }
  115. /**
  116. * statstape_printall(d, csv_filename):
  117. * Print statistics relating to each of the archives in a set. If
  118. * ${csv_filename} is not NULL, output will be written in CSV format to that
  119. * filename.
  120. */
  121. int
  122. statstape_printall(TAPE_S * d, const char * csv_filename)
  123. {
  124. struct tapemetadata tmd;
  125. uint8_t * flist;
  126. size_t nfiles;
  127. size_t file;
  128. FILE * output = stdout;
  129. int csv = 0;
  130. /* Should we output to a CSV file? */
  131. if (csv_filename != NULL)
  132. csv = 1;
  133. /* Open CSV output file, if requested. */
  134. if (csv && (output = fopen(csv_filename, "a")) == NULL)
  135. goto err0;
  136. /* Get a list of the metadata files. */
  137. if (storage_directory_read(d->machinenum, 'm', 0, &flist, &nfiles))
  138. goto err1;
  139. /* Cache up to 100 bytes of blocks per chunk in the directory. */
  140. storage_read_set_cache_limit(d->SR, 100 * chunks_stats_getdirsz(d->C));
  141. /* Iterate through the files. */
  142. for (file = 0; file < nfiles; file++) {
  143. /* Zero archive statistics. */
  144. chunks_stats_zeroarchive(d->C);
  145. /* Read the tape metadata. */
  146. if (multitape_metadata_get_byhash(d->SR, d->C, &tmd,
  147. &flist[file * 32], 0))
  148. goto err2;
  149. /* Compute statistics. */
  150. if (multitape_chunkiter_tmd(d->SR, d->C, &tmd,
  151. callback_print, d->C, 0))
  152. goto err3;
  153. /* Print the statistics. */
  154. if (chunks_stats_printarchive(output, d->C, tmd.name, csv))
  155. goto err3;
  156. /* Free parsed metadata. */
  157. multitape_metadata_free(&tmd);
  158. }
  159. /* Free the list of files. */
  160. free(flist);
  161. /* Close CSV output file, if requested. */
  162. if (csv && fclose(output)) {
  163. warnp("fclose");
  164. goto err0;
  165. }
  166. /* Success! */
  167. return (0);
  168. err3:
  169. multitape_metadata_free(&tmd);
  170. err2:
  171. free(flist);
  172. err1:
  173. if ((output != stdout) && fclose(output))
  174. warnp("fclose");
  175. err0:
  176. /* Failure! */
  177. return (-1);
  178. }
  179. /* Print ${sep} if ${nulls} is zero; otherwise, print ${num} NULs. */
  180. static int
  181. print_sep(char sep, int nulls, int num)
  182. {
  183. int i;
  184. if (nulls == 0) {
  185. /* Print the normal separator. */
  186. if (fprintf(stdout, "%c", sep) < 0) {
  187. warnp("fprintf");
  188. goto err0;
  189. }
  190. } else {
  191. /* Print the specified number of NULs. */
  192. for (i = 0; i < num; i++) {
  193. if (fprintf(stdout, "%c", '\0') < 0) {
  194. warnp("fprintf");
  195. goto err0;
  196. }
  197. }
  198. }
  199. /* Success! */
  200. return (0);
  201. err0:
  202. /* Failure! */
  203. return (-1);
  204. }
  205. /**
  206. * statstape_printlist_item(d, tapehash, verbose, print_nulls, print_hash):
  207. * Print the name of the archive with ${tapehash}. If ${verbose} > 0, print
  208. * the creation times; if ${verbose} > 1, print the argument vector of the
  209. * program invocation which created the archive. If ${print_nulls} > 0, print
  210. * null character(s) between archives names and fields instead of newlines,
  211. * tabs, and spaces. If ${print_hash} > 0 and ${verbose} is 0, print the hash
  212. * instead of the archive name. If ${print_hash} > 0 and ${verbose} > 0,
  213. * print hash in addition to the normal behaviour.
  214. */
  215. int
  216. statstape_printlist_item(TAPE_S * d, const uint8_t tapehash[32], int verbose,
  217. int print_nulls, int print_hash)
  218. {
  219. struct tapemetadata tmd;
  220. char hexstr[65];
  221. struct tm * ltime;
  222. char datebuf[DATEBUFLEN];
  223. int arg;
  224. /* Print archive hash. */
  225. if (print_hash) {
  226. hexify(tapehash, hexstr, 32);
  227. fprintf(stdout, "%s", hexstr);
  228. if (verbose == 0) {
  229. /* We're finished; print archive separator and quit. */
  230. if (print_sep('\n', print_nulls, 1))
  231. goto err1;
  232. goto done;
  233. } else {
  234. /* We have more fields; print field separator. */
  235. if (print_sep('\t', print_nulls, 2))
  236. goto err1;
  237. }
  238. }
  239. /* Read the tape metadata. */
  240. if (multitape_metadata_get_byhash(d->SR, NULL, &tmd, tapehash, 0))
  241. goto err0;
  242. /* Print name. */
  243. if (fprintf(stdout, "%s", tmd.name) < 0) {
  244. warnp("fprintf");
  245. goto err1;
  246. }
  247. /* Print creation time. */
  248. if (verbose > 0 && tmd.ctime != -1) {
  249. if ((ltime = localtime(&tmd.ctime)) == NULL) {
  250. warnp("localtime");
  251. goto err1;
  252. }
  253. if (strftime(datebuf, DATEBUFLEN, "%F %T", ltime) == 0) {
  254. warnp("Cannot format date");
  255. goto err1;
  256. }
  257. /* Print field separator. */
  258. if (print_sep('\t', print_nulls, 2))
  259. goto err1;
  260. /* Print date. */
  261. if (fprintf(stdout, "%s", datebuf) < 0) {
  262. warnp("fprintf");
  263. goto err1;
  264. }
  265. }
  266. /* Print command line. */
  267. if (verbose > 1) {
  268. /* Print field separator. */
  269. if (print_sep('\t', print_nulls, 2))
  270. goto err1;
  271. for (arg = 0; arg < tmd.argc; arg++) {
  272. /* Print arg separator. */
  273. if ((arg > 0) && print_sep(' ', print_nulls, 3))
  274. goto err1;
  275. /* Print arg. */
  276. if (fprintf(stdout, "%s", tmd.argv[arg]) < 0) {
  277. warnp("fprintf");
  278. goto err1;
  279. }
  280. }
  281. }
  282. /* Print archive separator. */
  283. if (print_sep('\n', print_nulls, 1))
  284. goto err1;
  285. /* Free parsed metadata. */
  286. multitape_metadata_free(&tmd);
  287. done:
  288. /* Success! */
  289. return (0);
  290. err1:
  291. multitape_metadata_free(&tmd);
  292. err0:
  293. /* Failure! */
  294. return (-1);
  295. }
  296. /**
  297. * statstape_printlist(d, verbose, print_nulls, print_hashes):
  298. * Print the names of each of the archives in a set. If ${verbose} > 0, print
  299. * the creation times; if ${verbose} > 1, print the argument vector of the
  300. * program invocation which created the archive. If ${print_nulls} > 0, print
  301. * null character(s) between archives names and fields instead of newlines,
  302. * tabs, and spaces. If ${print_hashes} > 0 and ${verbose} is 0, print hashes
  303. * instead of archive names. If ${print_hashes} > 0 and ${verbose} > 0, print
  304. * hashes in addition to the normal behaviour.
  305. */
  306. int
  307. statstape_printlist(TAPE_S * d, int verbose, int print_nulls, int print_hashes)
  308. {
  309. uint8_t * flist;
  310. size_t nfiles;
  311. size_t file;
  312. /* Get a list of the metadata files. */
  313. if (storage_directory_read(d->machinenum, 'm', 0, &flist, &nfiles))
  314. goto err0;
  315. /* Iterate through the files. */
  316. for (file = 0; file < nfiles; file++) {
  317. if (statstape_printlist_item(d, &flist[file * 32], verbose,
  318. print_nulls, print_hashes))
  319. goto err1;
  320. }
  321. /* Free the list of files. */
  322. free(flist);
  323. /* Success! */
  324. return (0);
  325. err1:
  326. free(flist);
  327. err0:
  328. /* Failure! */
  329. return (-1);
  330. }
  331. /**
  332. * statstape_print(d, tapename, csv_filename):
  333. * Print statistics relating to a specific archive in a set. Return 0 on
  334. * success, 1 if the tape does not exist, or -1 on other errors. If
  335. * ${csv_filename} is not NULL, output will be written in CSV format to that
  336. * filename.
  337. */
  338. int
  339. statstape_print(TAPE_S * d, const char * tapename, const char * csv_filename)
  340. {
  341. struct tapemetadata tmd;
  342. int rc = -1; /* Presume error was not !found. */
  343. FILE * output = stdout;
  344. int csv = 0;
  345. /* Should we output to a CSV file? */
  346. if (csv_filename != NULL)
  347. csv = 1;
  348. /* Cache up to 100 bytes of blocks per chunk in the directory. */
  349. storage_read_set_cache_limit(d->SR, 100 * chunks_stats_getdirsz(d->C));
  350. /* Zero archive statistics. */
  351. chunks_stats_zeroarchive(d->C);
  352. /* Read the tape metadata. */
  353. switch (multitape_metadata_get_byname(d->SR, d->C, &tmd, tapename, 0)) {
  354. case 0:
  355. break;
  356. case 1:
  357. rc = 1;
  358. /* FALLTHROUGH */
  359. default:
  360. goto err0;
  361. }
  362. if (multitape_chunkiter_tmd(d->SR, d->C, &tmd,
  363. callback_print, d->C, 0))
  364. goto err2;
  365. /* Free parsed metadata. */
  366. multitape_metadata_free(&tmd);
  367. /* Open CSV output file, if requested. */
  368. if (csv && (output = fopen(csv_filename, "a")) == NULL)
  369. goto err0;
  370. /* Print the statistics. */
  371. if (chunks_stats_printarchive(output, d->C, tapename, csv))
  372. goto err1;
  373. /* Close CSV output file. */
  374. if (csv && fclose(output)) {
  375. warnp("fclose");
  376. goto err0;
  377. }
  378. /* Success! */
  379. return (0);
  380. err2:
  381. multitape_metadata_free(&tmd);
  382. err1:
  383. if ((output != stdout) && fclose(output))
  384. warnp("fclose");
  385. err0:
  386. /* Failure! */
  387. return (rc);
  388. }
  389. /**
  390. * statstape_close(d):
  391. * Close the given archive set.
  392. */
  393. int
  394. statstape_close(TAPE_S * d)
  395. {
  396. /* Free chunk layer cookies. */
  397. chunks_stats_free(d->C);
  398. /* Free storage layer cookie. */
  399. storage_read_free(d->SR);
  400. /* Free multitape cookie. */
  401. free(d);
  402. /* Success! */
  403. return (0);
  404. }