#include #include #include #include #include #include #include #include #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() { /* Fork off the parent. */ pid_t pid = fork(); /* The process id is non zero, so it must be the parent process. */ if (pid) { /* Exit with EXIT_FAILURE if the parent's pid is * negative, otherwise exit with EXIT_SUCCESS. */ 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) { /* Fork the parent. */ pid_t pid = fork_proc(); /* Exit if the child process isn't the session leader. */ if (setsid() < 0) { exit(EXIT_FAILURE); } /* Catch, ignore, and/or handle signals. */ signal(SIGCHLD, child_handler); signal(SIGHUP, hangup_handler); /* Fork again. */ pid = fork_proc(); /* Set new file permissions. */ umask(0); /* If change_dir is true, change the working directory to path, or * the root directory if path isn't set. */ if (change_dir) { chdir((!path) ? "/" : path); } /* Close any open file descriptors. */ 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); /* Did the config file parser succeed? */ 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; /* Set socket type to unix socket, if socket-type was set to unix, otherwise set it to network socket. */ 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); /* Is the path too long? */ 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); /* Did getaddrinfo fail? */ 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; /* Does the PR root directory exist? */ if (stat(cfg->pr_root, &st) < 0) { /* Create the PR root directory. */ 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 the socket. */ close(listen_sockets[i]); /* Is this a unix domain socket? */ if (sa->sa_family == AF_UNIX) { struct sockaddr_un *sa_un = (struct sockaddr_un *)sa; /* Unlink the path. */ 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; /* Start it in daemon mode. */ init_daemon(1, "/"); /* Open the logfile. */ openlog("pullreqd", LOG_PID, LOG_DAEMON); log(LOG_NOTICE, "pullreqd started."); /* Read the config file. */ config_read = init_config(config_file, &cfg); /* Did we successfully read the config file? */ if (config_read) { /* Create the Listen socket(s). */ listen_sockets = init_socket(cfg); } /* Did we successfully read the config file, and create any listen sockets? */ 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; }