| 11478 | #endif /* CBM_CLI_ENABLE_TEST_API */ |
| 11479 | |
| 11480 | int cbm_cmd_update(int argc, char **argv) { |
| 11481 | parse_auto_answer(argc, argv); |
| 11482 | |
| 11483 | bool dry_run = false; |
| 11484 | bool force = false; |
| 11485 | for (int i = 0; i < argc; i++) { |
| 11486 | if (strcmp(argv[i], "--dry-run") == 0) { |
| 11487 | dry_run = true; |
| 11488 | } else if (strcmp(argv[i], "--force") == 0) { |
| 11489 | force = true; |
| 11490 | } else if (strcmp(argv[i], "-y") != 0 && strcmp(argv[i], "--yes") != 0 && |
| 11491 | strcmp(argv[i], "-n") != 0 && strcmp(argv[i], "--no") != 0) { |
| 11492 | (void)fprintf(stderr, "error: unknown update option: %s\n", argv[i]); |
| 11493 | return CLI_TRUE; |
| 11494 | } |
| 11495 | } |
| 11496 | |
| 11497 | /* Updates run from the install script, not from this process — on every |
| 11498 | * platform. |
| 11499 | * |
| 11500 | * Windows forced the split first: a running .exe cannot replace itself, so |
| 11501 | * an in-process updater needed a second resident binary to swap the first |
| 11502 | * one out, and that launcher stub was exactly the shape Defender's ML |
| 11503 | * scores as a dropper. |
| 11504 | * |
| 11505 | * The rest followed for the same reason rather than a different one. An |
| 11506 | * in-process updater is, structurally, a downloader: it fetches a remote |
| 11507 | * archive, extracts it, marks the result executable and runs it. That is |
| 11508 | * the behaviour Microsoft's Wacatac family describes almost verbatim, and |
| 11509 | * carrying it in the product binary put download/extract/chmod/exec in |
| 11510 | * every shipped artifact for a command most users run a handful of times. |
| 11511 | * |
| 11512 | * The install script already does all of it, is idempotent -- so re-running |
| 11513 | * it IS the update -- and runs while cbm is NOT running. Print the exact |
| 11514 | * command instead of feigning self-update. */ |
| 11515 | #ifndef CBM_CLI_ENABLE_TEST_API |
| 11516 | /* A release build has nothing to do but hand off. The flags are still |
| 11517 | * parsed and validated above, so `update --dry-run` and friends keep |
| 11518 | * rejecting typos instead of silently accepting them. */ |
| 11519 | (void)dry_run; |
| 11520 | (void)force; |
| 11521 | #endif |
| 11522 | #ifdef CBM_CLI_ENABLE_TEST_API |
| 11523 | if (g_cli_activation_test_ops_set) { |
| 11524 | (void)fprintf(stderr, "*** cbm test seam: portable update flow engaged; the " |
| 11525 | "script-update handoff is bypassed (test builds only) ***\n"); |
| 11526 | } else |
| 11527 | #endif |
| 11528 | { |
| 11529 | char self_dir[CLI_BUF_1K] = {0}; |
| 11530 | bool have_dir = false; |
| 11531 | /* cbm_detect_self_path resolves wide on Windows (non-ASCII install |
| 11532 | * paths survive) and normalizes to forward separators on every |
| 11533 | * platform, so one branch serves both. */ |
| 11534 | char *last_sep = cbm_detect_self_path(self_dir, sizeof(self_dir), cbm_get_home_dir()) |
| 11535 | ? strrchr(self_dir, '/') |
| 11536 | : NULL; |
| 11537 | if (last_sep) { |
no test coverage detected