#include <signal.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <syslog.h>
#include <unistd.h>
#include "config.h"
#include "git.h"
#include "linked_list.h"
#include "macros.h"
#include "network.h"
#define UNIX_PATH_MAX 108
pid_t fork_proc() {
pid_t pid = fork();
if (pid) {
exit((pid < 0) ? EXIT_FAILURE : EXIT_SUCCESS);
}
return pid;
}
void child_handler(int sig_num) {
signal(SIGCHLD, child_handler);
}
void hangup_handler(int sig_num) {
signal(SIGHUP, hangup_handler);
}
void init_daemon(int change_dir, char *path) {
pid_t pid = fork_proc();
if (setsid() < 0) {
exit(EXIT_FAILURE);
}
signal(SIGCHLD, child_handler);
signal(SIGHUP, hangup_handler);
pid = fork_proc();
umask(0);
if (change_dir) {
chdir((!path) ? "/" : path);
}
for (int i = sysconf(_SC_OPEN_MAX); i >= 0; close(i--));
}
int init_config(char *config_file, config **cfg) {
log(LOG_INFO, "Reading config file %s.", config_file);
*cfg = parse_config(config_file);
if (*cfg != NULL) {
log(LOG_INFO, "Successfully read %s.", config_file);
} else {
log(LOG_ERR, "Error reading %s.", config_file);
}
return (*cfg != NULL);
}
int *init_socket(config *cfg) {
linked_list *sock_list = NULL;
int *sock_arr = NULL;
const int sock_type = (strcasecmp(cfg->sock_type, "unix") == 0) ? AF_UNIX : AF_INET;
if (sock_type == AF_UNIX) {
size_t path_length = strlen(cfg->sock);
if (path_length >= UNIX_PATH_MAX) {
log(LOG_ERR, "The socket path is too long, and exceeded %i characters.", UNIX_PATH_MAX);
return NULL;
} else {
int fd;
struct sockaddr_un sa_un;
sa_un.sun_family = sock_type;
memcpy(sa_un.sun_path, cfg->sock, path_length+1);
fd = create_socket((struct sockaddr *)&sa_un, sizeof(struct sockaddr_un));
if (fd < 0) {
return NULL;
} else {
int *sock = calloc(1, sizeof(int));
*sock = fd;
sock_list = add_node(&sock_list, sock);
}
}
} else {
struct addrinfo hints, *ainfo_root, *ainfo;
memset(&hints, 0, sizeof(hints));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_SEQPACKET;
hints.ai_protocol = IPPROTO_SCTP;
hints.ai_flags = AI_PASSIVE;
const int gai_ret = getaddrinfo(cfg->sock, cfg->port, &hints, &ainfo_root);
if (gai_ret) {
log_reason(LOG_ERR, "getaddrinfo() failed for hostname %s.", cfg->sock, gai_strerror(gai_ret));
return NULL;
}
for (struct addrinfo *ai = ainfo_root; ai != NULL; ai = ai->ai_next) {
const int fd = create_socket(ai->ai_addr, ai->ai_addrlen);
if (fd >= 0) {
int *sock = calloc(1, sizeof(int));
*sock = fd;
sock_list = add_node(&sock_list, sock);
log(LOG_DEBUG, "ip addr: \"%s\"", addr_to_str(ai->ai_addr, ai->ai_addrlen));
}
}
freeaddrinfo(ainfo_root);
}
int sock_count = linked_list_size(sock_list);
sock_arr = calloc(sock_count+1, sizeof(int));
sock_arr[sock_count--] = -1;
for (linked_list *node = sock_list; node != NULL; node = node->prev, --sock_count) {
int *sock = (int *)node->data;
sock_arr[sock_count] = *sock;
node->data = NULL;
free(sock);
}
log(LOG_NOTICE, "Successfully created %i sockets for hostname \"%s\".", linked_list_size(sock_list), (cfg->sock != NULL) ? cfg->sock : "*");
cleanup_linked_list(sock_list);
return sock_arr;
}
int main_loop(config *cfg, int *listen_sockets, git_repo **repos) {
int done = 0;
struct stat st;
if (stat(cfg->pr_root, &st) < 0) {
mkdir(cfg->pr_root, 0755);
}
for (; !done;) {
break;
}
return 0;
}
void cleanup(config *cfg, int *listen_sockets, git_repo **repos) {
if (listen_sockets != NULL) {
for (int i = 0; listen_sockets[i] >= 0; ++i) {
struct sockaddr *sa = get_sock_addr(listen_sockets[i]);
close(listen_sockets[i]);
if (sa->sa_family == AF_UNIX) {
struct sockaddr_un *sa_un = (struct sockaddr_un *)sa;
unlink(sa_un->sun_path);
}
free(sa);
}
free(listen_sockets);
}
if (cfg) {
cleanup_config(cfg);
}
if (repos) {
cleanup_git(repos);
}
}
int main(int argc, char **argv) {
config *cfg = NULL;
git_repo **repos = NULL;
char *config_file = "test.conf";
int config_read = 0;
int *listen_sockets = NULL;
int exit_status = EXIT_FAILURE;
init_daemon(1, "/");
openlog("pullreqd", LOG_PID, LOG_DAEMON);
log(LOG_NOTICE, "pullreqd started.");
config_read = init_config(config_file, &cfg);
if (config_read) {
listen_sockets = init_socket(cfg);
}
if (config_read && listen_sockets != NULL) {
repos = init_git(cfg);
if (repos != NULL) {
exit_status = main_loop(cfg, listen_sockets, repos);
}
}
cleanup(cfg, listen_sockets, repos);
log(LOG_NOTICE, "pullreqd stopped.");
closelog();
return exit_status;
}