/* startup_validation.c — Startup pre-flight validation functions. * * Implements validate_vcpus(), validate_startup_config(), * validate_peer_count(), and validate_cert_files() for use in main.c * and the controller unit test suite. * * Requirements: 4.1 (peer count), 4.2 (cert pre-flight), * 4.3 (vcpus), 4.4 (vcpus key deprecation warn) */ #define _GNU_SOURCE #include #include #include #include /* ── Req 4.3: --vcpus validation ─────────────────────────────────────── */ /* * validate_vcpus: returns 0 if vcpus >= 1, -EINVAL otherwise. * Called from main.c after argp_parse() sets arguments.vcpus. */ int validate_vcpus(int vcpus) { if (vcpus <= 0) { return -EINVAL; } return 0; } /* ── Req 4.4: vcpus key in config file — warn only ───────────────────── */ /* * validate_startup_config: scan config_path for a "vcpus" key. * If found, write a deprecation warning into warn_buf (must contain "vcpus"). * Always returns 0 — the presence of a vcpus key is not a fatal error. * warn_buf is left empty (first byte = '\0') when no vcpus key is found. * * The scan is line-based: a line contains the vcpus key if it starts with * optional whitespace followed by the literal string "vcpus". */ int validate_startup_config(const char *config_path, char *warn_buf, size_t warn_len) { if (warn_buf && warn_len > 0) { warn_buf[0] = '\0'; } if (!config_path) { return 0; } FILE *fp = fopen(config_path, "r"); if (!fp) { return 0; /* can't open — not a fatal error at this stage */ } char line[1024]; while (fgets(line, sizeof(line), fp)) { /* skip leading whitespace */ const char *p = line; while (*p == ' ' || *p == '\t') { p++; } if (strncmp(p, "vcpus", 5) == 0) { /* Found a vcpus key — write deprecation warning */ if (warn_buf && warn_len > 0) { snprintf(warn_buf, warn_len, "config file contains deprecated 'vcpus' key; " "use --vcpus CLI parameter instead"); } break; } } fclose(fp); return 0; } /* ── Req 4.1: even-N peer count rejection ─────────────────────────────── */ /* * validate_peer_count: returns 0 for valid odd counts (1, 3, 5, ...), * -EINVAL for even counts > 1. * n_peers == 1 is single-node mode — always valid. * Even counts > 1 cannot form a safe majority partition. */ int validate_peer_count(int n_peers) { if (n_peers > 1 && (n_peers % 2) == 0) { return -EINVAL; } return 0; } /* ── Req 4.2: TLS cert pre-flight ─────────────────────────────────────── */ /* * validate_cert_files: check that cert_path and key_path are readable. * On the first missing/unreadable file, sets err_buf to a message * containing the full file path and returns -ENOENT. * Returns 0 if both files are accessible. */ int validate_cert_files(const char *cert_path, const char *key_path, char *err_buf, size_t err_len) { if (access(cert_path, R_OK) != 0) { if (err_buf && err_len > 0) { snprintf(err_buf, err_len, "TLS certificate not found or unreadable: %s", cert_path); } return -ENOENT; } if (access(key_path, R_OK) != 0) { if (err_buf && err_len > 0) { snprintf(err_buf, err_len, "TLS key not found or unreadable: %s", key_path); } return -ENOENT; } return 0; }