|
11 | 11 | #include "dir.h"
|
12 | 12 | #include "packfile.h"
|
13 | 13 | #include "help.h"
|
| 14 | +#include "archive.h" |
14 | 15 |
|
15 | 16 | /*
|
16 | 17 | * Remove the deepest subdirectory in the provided path string. Path must not
|
@@ -260,6 +261,47 @@ static int unregister_dir(void)
|
260 | 261 | return res;
|
261 | 262 | }
|
262 | 263 |
|
| 264 | +static int add_directory_to_archiver(struct strvec *archiver_args, |
| 265 | + const char *path, int recurse) |
| 266 | +{ |
| 267 | + int at_root = !*path; |
| 268 | + DIR *dir = opendir(at_root ? "." : path); |
| 269 | + struct dirent *e; |
| 270 | + struct strbuf buf = STRBUF_INIT; |
| 271 | + size_t len; |
| 272 | + int res = 0; |
| 273 | + |
| 274 | + if (!dir) |
| 275 | + return error_errno(_("could not open directory '%s'"), path); |
| 276 | + |
| 277 | + if (!at_root) |
| 278 | + strbuf_addf(&buf, "%s/", path); |
| 279 | + len = buf.len; |
| 280 | + strvec_pushf(archiver_args, "--prefix=%s", buf.buf); |
| 281 | + |
| 282 | + while (!res && (e = readdir(dir))) { |
| 283 | + if (!strcmp(".", e->d_name) || !strcmp("..", e->d_name)) |
| 284 | + continue; |
| 285 | + |
| 286 | + strbuf_setlen(&buf, len); |
| 287 | + strbuf_addstr(&buf, e->d_name); |
| 288 | + |
| 289 | + if (e->d_type == DT_REG) |
| 290 | + strvec_pushf(archiver_args, "--add-file=%s", buf.buf); |
| 291 | + else if (e->d_type != DT_DIR) |
| 292 | + warning(_("skipping '%s', which is neither file nor " |
| 293 | + "directory"), buf.buf); |
| 294 | + else if (recurse && |
| 295 | + add_directory_to_archiver(archiver_args, |
| 296 | + buf.buf, recurse) < 0) |
| 297 | + res = -1; |
| 298 | + } |
| 299 | + |
| 300 | + closedir(dir); |
| 301 | + strbuf_release(&buf); |
| 302 | + return res; |
| 303 | +} |
| 304 | + |
263 | 305 | /* printf-style interface, expects `<key>=<value>` argument */
|
264 | 306 | static int set_config(const char *fmt, ...)
|
265 | 307 | {
|
@@ -500,6 +542,107 @@ static int cmd_clone(int argc, const char **argv)
|
500 | 542 | return res;
|
501 | 543 | }
|
502 | 544 |
|
| 545 | +static int cmd_diagnose(int argc, const char **argv) |
| 546 | +{ |
| 547 | + struct option options[] = { |
| 548 | + OPT_END(), |
| 549 | + }; |
| 550 | + const char * const usage[] = { |
| 551 | + N_("scalar diagnose [<enlistment>]"), |
| 552 | + NULL |
| 553 | + }; |
| 554 | + struct strbuf zip_path = STRBUF_INIT; |
| 555 | + struct strvec archiver_args = STRVEC_INIT; |
| 556 | + char **argv_copy = NULL; |
| 557 | + int stdout_fd = -1, archiver_fd = -1; |
| 558 | + time_t now = time(NULL); |
| 559 | + struct tm tm; |
| 560 | + struct strbuf path = STRBUF_INIT, buf = STRBUF_INIT; |
| 561 | + int res = 0; |
| 562 | + |
| 563 | + argc = parse_options(argc, argv, NULL, options, |
| 564 | + usage, 0); |
| 565 | + |
| 566 | + setup_enlistment_directory(argc, argv, usage, options, &zip_path); |
| 567 | + |
| 568 | + strbuf_addstr(&zip_path, "/.scalarDiagnostics/scalar_"); |
| 569 | + strbuf_addftime(&zip_path, |
| 570 | + "%Y%m%d_%H%M%S", localtime_r(&now, &tm), 0, 0); |
| 571 | + strbuf_addstr(&zip_path, ".zip"); |
| 572 | + switch (safe_create_leading_directories(zip_path.buf)) { |
| 573 | + case SCLD_EXISTS: |
| 574 | + case SCLD_OK: |
| 575 | + break; |
| 576 | + default: |
| 577 | + error_errno(_("could not create directory for '%s'"), |
| 578 | + zip_path.buf); |
| 579 | + goto diagnose_cleanup; |
| 580 | + } |
| 581 | + stdout_fd = dup(1); |
| 582 | + if (stdout_fd < 0) { |
| 583 | + res = error_errno(_("could not duplicate stdout")); |
| 584 | + goto diagnose_cleanup; |
| 585 | + } |
| 586 | + |
| 587 | + archiver_fd = xopen(zip_path.buf, O_CREAT | O_WRONLY | O_TRUNC, 0666); |
| 588 | + if (archiver_fd < 0 || dup2(archiver_fd, 1) < 0) { |
| 589 | + res = error_errno(_("could not redirect output")); |
| 590 | + goto diagnose_cleanup; |
| 591 | + } |
| 592 | + |
| 593 | + init_zip_archiver(); |
| 594 | + strvec_pushl(&archiver_args, "scalar-diagnose", "--format=zip", NULL); |
| 595 | + |
| 596 | + strbuf_reset(&buf); |
| 597 | + strbuf_addstr(&buf, "Collecting diagnostic info\n\n"); |
| 598 | + get_version_info(&buf, 1); |
| 599 | + |
| 600 | + strbuf_addf(&buf, "Enlistment root: %s\n", the_repository->worktree); |
| 601 | + write_or_die(stdout_fd, buf.buf, buf.len); |
| 602 | + strvec_pushf(&archiver_args, |
| 603 | + "--add-virtual-file=diagnostics.log:%.*s", |
| 604 | + (int)buf.len, buf.buf); |
| 605 | + |
| 606 | + if ((res = add_directory_to_archiver(&archiver_args, ".git", 0)) || |
| 607 | + (res = add_directory_to_archiver(&archiver_args, ".git/hooks", 0)) || |
| 608 | + (res = add_directory_to_archiver(&archiver_args, ".git/info", 0)) || |
| 609 | + (res = add_directory_to_archiver(&archiver_args, ".git/logs", 1)) || |
| 610 | + (res = add_directory_to_archiver(&archiver_args, ".git/objects/info", 0))) |
| 611 | + goto diagnose_cleanup; |
| 612 | + |
| 613 | + strvec_pushl(&archiver_args, "--prefix=", |
| 614 | + oid_to_hex(the_hash_algo->empty_tree), "--", NULL); |
| 615 | + |
| 616 | + /* `write_archive()` modifies the `argv` passed to it. Let it. */ |
| 617 | + argv_copy = xmemdupz(archiver_args.v, |
| 618 | + sizeof(char *) * archiver_args.nr); |
| 619 | + res = write_archive(archiver_args.nr, (const char **)argv_copy, NULL, |
| 620 | + the_repository, NULL, 0); |
| 621 | + if (res) { |
| 622 | + error(_("failed to write archive")); |
| 623 | + goto diagnose_cleanup; |
| 624 | + } |
| 625 | + |
| 626 | + if (!res) |
| 627 | + fprintf(stderr, "\n" |
| 628 | + "Diagnostics complete.\n" |
| 629 | + "All of the gathered info is captured in '%s'\n", |
| 630 | + zip_path.buf); |
| 631 | + |
| 632 | +diagnose_cleanup: |
| 633 | + if (archiver_fd >= 0) { |
| 634 | + close(1); |
| 635 | + dup2(stdout_fd, 1); |
| 636 | + } |
| 637 | + free(argv_copy); |
| 638 | + strvec_clear(&archiver_args); |
| 639 | + strbuf_release(&zip_path); |
| 640 | + strbuf_release(&path); |
| 641 | + strbuf_release(&buf); |
| 642 | + |
| 643 | + return res; |
| 644 | +} |
| 645 | + |
503 | 646 | static int cmd_list(int argc, const char **argv)
|
504 | 647 | {
|
505 | 648 | if (argc != 1)
|
@@ -801,6 +944,7 @@ static struct {
|
801 | 944 | { "reconfigure", cmd_reconfigure },
|
802 | 945 | { "delete", cmd_delete },
|
803 | 946 | { "version", cmd_version },
|
| 947 | + { "diagnose", cmd_diagnose }, |
804 | 948 | { NULL, NULL},
|
805 | 949 | };
|
806 | 950 |
|
|
0 commit comments