deployd/server/deploys.c
Arija A. d1d0aec439
Add dproto documentation
Signed-off-by: Arija A. <ari@ari.lt>
2025-07-24 23:55:42 +03:00

159 lines
4.9 KiB
C

#include "include/conf.h"
#include "include/proto.h"
#include "include/deploys.h"
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#define DP_DEPLOY_PATH_MAX_LEN 1024
#define DP_DEPLOY_PIPE_READ 0
#define DP_DEPLOY_PIPE_WRITE 1
bool dp_trigger_online_deploy(SSL *ssl,
const char *domain,
size_t domain_len,
dp_Logger *logger,
dp_DeployCommand command,
bool is_unsafe) {
if (!ssl || !domain || strlen(domain) == 0 || !logger) {
return false;
}
char script_path[DP_DEPLOY_PATH_MAX_LEN] = {0};
/* Construct script path: DP_SCRIPTS_DIR + "/" + domain + ".deployer" */
const size_t dir_len = strlen(DP_SCRIPTS_DIR);
const size_t ext_len = strlen(".deployer");
if (dir_len + 1 + domain_len + ext_len >= DP_DEPLOY_PATH_MAX_LEN) {
dp_log(logger, DP_LOG_ERROR, DP_DEPLOYS_LOG "/online",
"Script path too long");
return false;
}
memcpy(script_path, DP_SCRIPTS_DIR, dir_len);
script_path[dir_len] = '/';
memcpy(script_path + dir_len + 1, domain, domain_len);
memcpy(script_path + dir_len + 1 + domain_len, ".deployer", ext_len);
/* Check if script is executable */
if (access(script_path, X_OK) != 0) {
perror("[deploy] Script is not executable or missing");
return false;
}
dp_logf(logger, DP_LOG_INFO, DP_DEPLOYS_LOG "/online",
"Starting online deploy %.*s", (int)domain_len, domain);
/* Set up pipe for capturing child output */
int pipe_fds[2];
if (pipe(pipe_fds) != 0) {
perror("[deploy] Failed to create pipe");
return false;
}
pid_t child_pid = fork();
if (child_pid < 0) {
perror("[deploy] Fork failed");
close(pipe_fds[DP_DEPLOY_PIPE_READ]);
close(pipe_fds[DP_DEPLOY_PIPE_WRITE]);
return false;
}
if (child_pid == 0) {
/* --- CHILD PROCESS --- */
close(pipe_fds[DP_DEPLOY_PIPE_READ]);
/* Redirect stdout and stderr to write end of pipe */
dup2(pipe_fds[DP_DEPLOY_PIPE_WRITE], STDOUT_FILENO);
dup2(pipe_fds[DP_DEPLOY_PIPE_WRITE], STDERR_FILENO);
/* Detach from stdin */
int dev_null = open("/dev/null", O_RDONLY);
if (dev_null >= 0) {
dup2(dev_null, STDIN_FILENO);
close(dev_null);
}
char *argv[5] = {script_path, NULL, NULL, NULL};
char **arg = NULL;
if (is_unsafe) {
argv[1] = "--unsafe";
arg = &argv[2];
} else {
arg = &argv[1];
}
switch (command) {
case dp_DeployCommand_trigger: break;
case dp_DeployCommand_teardown: *arg = "teardown"; break;
case dp_DeployCommand_deploy: *arg = "deploy"; break;
case dp_DeployCommand_rollback: *arg = "rollback"; break;
case dp_DeployCommand_cleanup: *arg = "cleanup"; break;
case dp_DeployCommand_restart: *arg = "restart"; break;
case dp_DeployCommand_sysadmin: *arg = "sysadmin"; break;
case dp_DeployCommand_logs: *arg = "logs"; break;
}
/* Execute the deployment script */
execv(script_path, argv);
/* If execl fails */
dp_err(logger, DP_LOG_FATAL, DP_DEPLOYS_LOG "/online",
"execv() failed");
_exit(127);
}
/* --- PARENT PROCESS --- */
close(pipe_fds[DP_DEPLOY_PIPE_WRITE]);
char buffer[1024] = {0};
ssize_t read_len = 0;
while ((read_len = read(pipe_fds[DP_DEPLOY_PIPE_READ], buffer,
sizeof(buffer))) > 0) {
if (!dp_proto_log_chunker(ssl, buffer, (size_t)read_len, logger)) {
return false;
}
if (!dp_proto_ping(ssl)) {
return false;
}
uint8_t ping_reply = 0x00;
if (SSL_read(ssl, &ping_reply, 1) != 1) {
dp_proto_error(ssl, dp_PacketError_internal,
"Unable to read PING_REPLY", logger);
return false;
}
if ((dp_PacketType)ping_reply != dp_PacketType_ping_reply) {
dp_proto_error(ssl, dp_PacketError_proto_packet_invalid,
"Invalid response to PING", logger);
return false;
}
sleep(1);
}
close(pipe_fds[DP_DEPLOY_PIPE_READ]);
/* Wait for child process to finish */
int exit_status = 0;
waitpid(child_pid, &exit_status, 0);
if (!dp_proto_log_end(ssl, logger)) {
return false;
}
dp_logf(logger, DP_LOG_INFO, DP_DEPLOYS_LOG "/online",
"Finished online deploy %.*s", (int)domain_len, domain);
return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == 0;
}