deployd/server/main.c
Arija A. f298495d59
Add extension-path helper, overhaul metadata parsing and integrate stages API
- dirs.c / dirs.h
  - add sh_dirs_get_ext_path() to build extension paths
  - define SH_DIRS_MAX_EXT_PATH for buffer sizing
- metadata.h / metadata.c
  - revamp sh_Metadata_read() to return bool, drop error‑string outparam
  - add SH_METADATA_NEW() and SH_METADATA_FREE()
  - split parsing into helpers (name, extensions, user, groups, target, copy)
  - tighten validation, trimming, uniqueness checks, and error messages
  - rename sh_Metadata_is_full() to sh_Metadata_is_full_script()
- stages.h / stages.c
  - extend sh_find_stages() to take sh_PtrRange* for sysadmin block range
  - refine brace/paren balance, in‑function tracking, EOF error checks
  - improve backtick‑in‑subsidiary parsing error handling
- main.c
  - add sh_load_script() to fread entire script into memory with error checks
  - add sh_read_meta() wrapper to call metadata and stages APIs, free buffer, return proper exit codes
  - include deploy.h and progress printf() calls
- server/main.c
  - insert “TODO: Cron jobs” placeholder

Signed-off-by: Arija A. <ari@ari.lt>
2025-07-27 13:08:39 +03:00

249 lines
7.7 KiB
C

#include "include/conf.h"
#include "include/def.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/stat.h>
#include <sqlite3.h>
#include "include/main.h"
#include "include/admin.h"
#include "include/server.h"
#include "include/log.h"
#include "include/auth.h"
#include "include/proto.h"
#include "include/blake2s.h"
#define DP_HOST "127.0.0.1:6532"
#define DP_THREADS 4
static char **dp_saved_argv = NULL;
static dp_Logger dp_logger = {0};
static void dp_print_usage(const char *prog, dp_LogLevel level) {
dp_logf(&dp_logger, level, "main",
"Usage: %s <server|admin> [-h host:port]... [-t threads] [-s "
"secret_key.pem] [-p public_key.pem] [-l logfile.log] [-L "
"logfile.log (overwrite existing)] [-d (debug)]",
prog);
}
static void dp_handle_reload(int signo) {
(void)signo;
dp_log(&dp_logger, DP_LOG_INFO, "reload", "Reloading the program...");
dp_log_flush(&dp_logger);
execv(dp_saved_argv[0], dp_saved_argv);
}
static sqlite3 *dp_init_db(void) {
int ret = 0;
ret = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
if (ret != SQLITE_OK) {
dp_lohf(&dp_logger, DP_LOG_FATAL,
"Failed to configure SQLite serialized mode: %d", ret);
return NULL;
}
ret = sqlite3_initialize();
if (ret != SQLITE_OK) {
dp_lohf(&dp_logger, DP_LOG_FATAL, "Failed to initialize SQLite: %d",
ret);
return NULL;
}
static const char *db_path = DP_DIR "/main.db";
sqlite3 *database = NULL;
ret = sqlite3_open(db_path, &database);
if (ret != SQLITE_OK) {
dp_lohf(&dp_logger, DP_LOG_FATAL, "Can't open DB in main: %s",
sqlite3_errmsg(database));
return NULL;
}
if (chmod(db_path, 0600) != 0) {
dp_fah(&dp_logger, "Failed to set DB file permissions to 0600");
sqlite3_close(database);
return NULL;
}
/* TODO: Cron jobs */
/* clang-format off */
static const char *dp_schema[] = {
"PRAGMA journal_mode=WAL;",
"CREATE TABLE IF NOT EXISTS secrets (\n"
" id INTEGER PRIMARY KEY AUTOINCREMENT,\n"
" domain VARCHAR(" DP_STR(DP_DOMAIN_LEN) ") NOT NULL CHECK (LENGTH(domain) > 0 AND LENGTH(domain) <= " DP_STR(DP_DOMAIN_LEN) "),\n"
" secret VARCHAR(" DP_STR(DP_AUTH_SECRET_SIZE) ") NOT NULL CHECK (LENGTH(secret) = " DP_STR(DP_AUTH_SECRET_SIZE) "),\n"
" key_hash BLOB(" DP_STR(DP_BLAKE2S_OUTPUT_SIZE_MAX) ") NOT NULL CHECK (LENGTH(key_hash) = " DP_STR(DP_BLAKE2S_OUTPUT_SIZE_MAX) "),\n"
" key_salt BLOB(" DP_STR(DP_BLAKE2S_SALT_SIZE) ") NOT NULL CHECK (LENGTH(key_salt) = " DP_STR(DP_BLAKE2S_SALT_SIZE) "),\n"
" timestamp INTEGER NOT NULL CHECK (timestamp > 0),\n"
" expires INTEGER NOT NULL CHECK (expires = 0 OR expires > timestamp),\n"
" description TEXT NOT NULL\n"
");",
};
/* clang-format on */
for (size_t idx = 0; idx < sizeof(dp_schema) / sizeof(dp_schema[0]);
++idx) {
ret = sqlite3_exec(database, dp_schema[idx], NULL, NULL, NULL);
if (ret != SQLITE_OK) {
dp_lohf(&dp_logger, DP_LOG_FATAL,
"Failed to run SQlite command: %s\n", dp_schema[idx]);
sqlite3_close(database);
return NULL;
}
}
return database;
}
int main(int argc, char **argv) {
dp_log_set_level(&dp_logger, DP_LOG_INFO);
if (argc < 2) {
dp_print_usage(argv[0], DP_LOG_INFO);
return 1;
}
if (mkdir(DP_DIR, 0711) < 0 && errno != EEXIST) {
dp_lohf(&dp_logger, DP_LOG_ERROR, "Failed to create %s: %s", DP_DIR,
strerror(errno));
return 1;
}
if (mkdir(DP_SCRIPTS_DIR, 0755) < 0 && errno != EEXIST) {
dp_lohf(&dp_logger, DP_LOG_ERROR, "Failed to create %s: %s",
DP_SCRIPTS_DIR, strerror(errno));
return 1;
}
const char *subcmd = argv[1];
const bool is_server = strcmp(subcmd, "server") == 0;
const bool is_admin = strcmp(subcmd, "admin") == 0;
if (!is_server && !is_admin) {
dp_print_usage(argv[0], DP_LOG_ERROR);
return 1;
}
if (is_admin && argc != 2) {
dp_loh(&dp_logger, DP_LOG_ERROR, "`admin` takes no arguments");
dp_print_usage(argv[0], DP_LOG_ERROR);
return 1;
}
const char *pubkey = NULL;
const char *seckey = NULL;
int threads = DP_THREADS;
const char *hosts[DP_MAX_LISTENS] = {0};
uint8_t hosts_size = 0;
for (int idx = 2; idx < argc; ++idx) {
if (strcmp(argv[idx], "-h") == 0 && idx + 1 < argc) {
if (hosts_size < DP_MAX_LISTENS) {
hosts[hosts_size++] = argv[++idx];
} else {
dp_lohf(&dp_logger, DP_LOG_FATAL,
"Maximum number of hosts (%d) reached", DP_MAX_LISTENS);
return 1;
}
} else if (strcmp(argv[idx], "-t") == 0 && idx + 1 < argc) {
if (!is_server) {
dp_loh(&dp_logger, DP_LOG_ERROR,
"-t flag applies only to server mode.");
dp_print_usage(argv[0], DP_LOG_ERROR);
return 1;
}
threads = atoi(argv[++idx]);
} else if (strcmp(argv[idx], "-p") == 0 && idx + 1 < argc) {
if (!is_server) {
dp_loh(&dp_logger, DP_LOG_ERROR,
"-p flag applies only to server mode.");
dp_print_usage(argv[0], DP_LOG_ERROR);
return 1;
}
pubkey = argv[++idx];
} else if (strcmp(argv[idx], "-s") == 0 && idx + 1 < argc) {
if (!is_server) {
dp_loh(&dp_logger, DP_LOG_ERROR,
"-s flag applies only to server mode.");
dp_print_usage(argv[0], DP_LOG_ERROR);
return 1;
}
seckey = argv[++idx];
} else if (strcmp(argv[idx], "-l") == 0 && idx + 1 < argc) {
if (!dp_log_set_file(&dp_logger, argv[++idx], "wx")) {
(void)fputs("Failed to create a log file", stderr);
return 1;
}
} else if (strcmp(argv[idx], "-L") == 0 && idx + 1 < argc) {
if (!dp_log_set_file(&dp_logger, argv[++idx], "w")) {
(void)fputs("Failed to create a log file", stderr);
return 1;
}
} else if (strcmp(argv[idx], "-d") == 0) {
dp_log_set_level(&dp_logger, DP_LOG_DEBUG);
} else {
dp_lohf(&dp_logger, DP_LOG_ERROR, "Unknown option: %s\n",
argv[idx]);
dp_print_usage(argv[0], DP_LOG_ERROR);
return 1;
}
}
if (threads < 1 || threads >= (((uint16_t)1 << (uint16_t)16) - 1)) {
dp_lohf(&dp_logger, DP_LOG_FATAL, "Invalid thread count: %d", threads);
return 1;
}
if (!seckey || !pubkey) {
dp_loh(&dp_logger, DP_LOG_FATAL,
"No SSL private (n)or public key supplied, Use the [-p "
"public_key.pem] and [-s secret_key.pem] flags!");
return 1;
}
if (hosts_size == 0) {
hosts[0] = DP_HOST;
++hosts_size;
}
dp_saved_argv = argv;
(void)signal(SIGHUP, dp_handle_reload);
sqlite3 *database = dp_init_db();
if (!database) {
dp_fah(&dp_logger, "Error: Failed to initialise database");
return 1;
}
if (is_server) {
if (!dp_deployd_server(hosts, hosts_size, (uint16_t)threads, "cert.pem",
"key.pem", database, &dp_logger)) {
return 1;
}
} else if (is_admin) {
if (!dp_admin_repl(database)) {
return 1;
}
}
return 0;
}