/* Exercise the actual daemon selection/action path. Native backend NOT linked. */ #define main guard_program_main #include "../src/main.c" #undef main static double fake_now = 100; static sample_t fake_sample; static process_t fixtures[8]; static unsigned nfixtures, real_requests, validation_requests, checks; static int fail_validation, fail_sample, partial; #define CHECK(x) do { if (!(x)) { fprintf(stderr, "FAIL runtime line %d: %s\n", __LINE__, #x); exit(1); } checks++; } while (0) double monotime(void) { return fake_now; } int secure_file(const char *p, int flags, unsigned mode) { (void)p; (void)flags; (void)mode; errno = EPERM; return -1; } int secure_directory(const char *p) { (void)p; return -1; } int platform_init(void) { return 0; } void platform_fini(void) { } int platform_sample(sample_t *s) { *s = fake_sample; s->at = fake_now; return fail_sample ? -1 : 0; } int platform_walk(int (*fn)(const process_t *, void *), void *arg, unsigned budget) { unsigned j; (void)budget; for (j = 0; j < nfixtures; j++) if (fn(&fixtures[j], arg)) return -1; return partial ? -1 : 0; } int platform_signal(const config_t *c, const process_t *p, enum action action, int real, uint64_t ceiling) { CHECK(action == TERM || action == KILL); CHECK(!protected_process(c, p)); if (real) { CHECK(c->armed && fake_sample.free < ceiling); real_requests++; } else validation_requests++; if (fail_validation) { errno = ESTALE; return -1; } return 0; /* This test backend cannot send any signal. */ } static void reset(void) { defaults(&cfg); cfg.log_mib = 100; cfg.nzones = 2; strcpy(cfg.zones[0].uuid, "11111111-1111-4111-8111-111111111111"); strcpy(cfg.zones[0].alias, "web-a"); cfg.zones[0].policy = ALLOW; strcpy(cfg.zones[1].uuid, "22222222-2222-4222-8222-222222222222"); strcpy(cfg.zones[1].alias, "web-b"); cfg.zones[1].policy = ALLOW; memset(processes, 0, sizeof (processes)); memset(zone_stats, 0, sizeof (zone_stats)); memset(metrics, 0, sizeof (metrics)); memset(&controller, 0, sizeof (controller)); memset(fixtures, 0, sizeof (fixtures)); engine_init(&engine); engine.state = CRITICAL; engine.danger = 1; stopping = 0; started = 0; log_ok = 1; log_bytes = 0; scan_generation = 0; fake_now = 100; partial = fail_sample = fail_validation = 0; real_requests = validation_requests = 0; memset(&fake_sample, 0, sizeof (fake_sample)); fake_sample.at = fake_now; fake_sample.free = 60000; fake_sample.lots = 130875; fake_sample.des = 65437; fake_sample.min = 49077; fixtures[0].pid = 123; fixtures[0].ppid = 100; fixtures[0].zid = 9; fixtures[0].initpid = 99; fixtures[0].nlwp = 1; fixtures[0].start_sec = 1000; fixtures[0].zone_generation = 20; fixtures[0].rss_kib = 100000; strcpy(fixtures[0].uuid, cfg.zones[0].uuid); strcpy(fixtures[0].name, "php-fpm"); nfixtures = 1; scan_processes(0); } int main(void) { FILE *log = tmpfile(); char contents[65536]; size_t n; const process_t *chosen; unsigned second; CHECK(log != NULL); logfd = fileno(log); reset(); act(&fake_sample); CHECK(real_requests == 0 && validation_requests == 1 && controller.has_pending); fake_now += 5; scan_processes(0); act(&fake_sample); CHECK(real_requests == 0 && !controller.has_pending); rewind(log); n = fread(contents, 1, sizeof (contents) - 1, log); contents[n] = 0; CHECK(strstr(contents, "WOULD_ACTION=SIGTERM") != NULL); CHECK(strstr(contents, "WOULD_ACTION=SIGKILL") != NULL); CHECK(strstr(contents, "ACTION_INTENT=") == NULL); CHECK(fseek(log, 0, SEEK_END) == 0); puts("PASS actual daemon dry-run path records WOULD_ACTION=SIGTERM/SIGKILL; zero real requests"); reset(); fake_sample.arc_ok = 1; fake_sample.swap_virtual_ok = fake_sample.swap_physical_ok = fake_sample.pageout_ok = 1; for (second = 0; second <= 60; second++) { fake_sample.at = 100 + second; fake_sample.arc = UINT64_C(6000000000) + 1000 * second; fake_sample.swap_alloc = 7000000 + 3 * second; fake_sample.swap_reserved = 480000 + 2 * second; fake_sample.swap_available = 6200000 - second; fake_sample.swap_physical_used_bytes = UINT64_C(4143648768) + 4096 * second; fake_sample.anon_pageout_pages = 1000 + 2 * second; record_metrics(&fake_sample); } log_sample(&fake_sample); CHECK(fflush(log) == 0 && fseek(log, 0, SEEK_SET) == 0); n = fread(contents, 1, sizeof (contents) - 1, log); contents[n] = 0; CHECK(strstr(contents, "swap_committed_ok=1 swap_committed_pages=7480300") != NULL); CHECK(strstr(contents, "swap_physical_used_ok=1 swap_physical_used_bytes=4143894528") != NULL); CHECK(strstr(contents, "anon_pageout_ok=1 anon_pageout_pages=1120") != NULL); CHECK(strstr(contents, "swap_alloc_pages=7000180 swap_reserved_pages=480120 swap_available_pages=6199940") != NULL); CHECK(strstr(contents, "metric=swap_committed_pages d5=+25@5.00s d15=+75@15.00s d60=+300@60.00s") != NULL); CHECK(strstr(contents, "metric=swap_physical_used_bytes d5=+20480@5.00s d15=+61440@15.00s d60=+245760@60.00s") != NULL); CHECK(strstr(contents, "metric=anon_pageout_pages d5=+10@5.00s d15=+30@15.00s d60=+120@60.00s") != NULL); CHECK(strstr(contents, "swap_used_pages") == NULL); CHECK(fseek(log, 0, SEEK_END) == 0); puts("PASS continuous sample contract and 5/15/60 deltas for commitment, physical swap and pageout"); reset(); cfg.armed = 1; act(&fake_sample); CHECK(real_requests == 1); fake_now += 5; scan_processes(0); act(&fake_sample); CHECK(real_requests == 2); reset(); cfg.armed = 1; started = 80; act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; log_ok = 0; act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; partial = 1; scan_processes(0); act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; fail_sample = 1; act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; fail_validation = 1; act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; fake_sample.free = 150000; act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; fake_now += 3; act(&fake_sample); CHECK(real_requests == 0); reset(); cfg.armed = 1; stopping = 1; act(&fake_sample); CHECK(real_requests == 0); reset(); fake_sample.free = 20000; act(&fake_sample); CHECK(!controller.has_pending && controller.nvictims == 1); puts("PASS armed-path mock requests, warmup, stale cache, logging/sampling failures, stop and fresh floor"); reset(); fixtures[1] = fixtures[0]; fixtures[1].pid = 124; fixtures[1].rss_kib = 900000; fixtures[1].zid = 10; strcpy(fixtures[1].uuid, cfg.zones[1].uuid); nfixtures = 2; fake_now = 101; scan_processes(0); chosen = candidate(); CHECK(chosen && chosen->pid == 124); /* Small zone grows fast enough to outweigh a larger steady zone. */ fixtures[0].rss_kib = 600000; fake_now = 161; scan_processes(0); chosen = candidate(); CHECK(chosen && chosen->pid == 123); cfg.zones[0].policy = PROTECT; fake_now = 162; scan_processes(0); chosen = candidate(); CHECK(chosen && chosen->pid == 124); fixtures[1].zid = 0; fake_now = 163; scan_processes(0); CHECK(candidate() == NULL); puts("PASS actual daemon ranking by zone/PID RSS and growth; protected zones and global zone excluded"); fclose(log); printf("PASS %u runtime checks; all platform calls mocked, no signals sent\n", checks); return 0; }