/* * server/main.c */ #include #include #include #include #include #include #include #include #include #include #include #include "dnbd2.h" #include "tree.h" #include "file.h" #include "config.h" #include "query.h" char *cfile = NULL; /* path to config file */ void *tree = NULL; /* tree containing datasets */ int server_fd; /* file descriptor for UPD communication */ struct sockaddr_in server_addr; /* * Flags and signal handlers. */ int sig_exit, sig_config; void exit_handler(int sig); void config_handler(int sig); /* * Function to configure server. We call it with LOAD on * initialization and with RELOAD when SIGHUP is caught. */ enum config_action { LOAD, RELOAD }; int configure(enum config_action action); void print_usage(void); int daemon_init(void); int main(int argc, char **argv) { int ret; ssize_t n; socklen_t len; dnbd2_data_reply_t reply; dnbd2_data_request_t request; struct sockaddr_in client_addr; struct sigaction exit_act, config_act; if (argc != 2) { print_usage(); return -1; } cfile = argv[1]; openlog(argv[0], LOG_PID, LOG_LOCAL2); syslog(LOG_NOTICE, "Starting DNBD2 Server."); /* Load datasets and bind to socket. */ if (configure(LOAD) == -1) goto out_no_start; /* Daemonize. */ ret = daemon_init(); if (ret == -1) { syslog(LOG_ERR, "Could not fork and background."); goto out_no_start; } /* Setup signal handlers. */ sigaction(SIGTERM, NULL, &exit_act); exit_act.sa_flags &= ~SA_RESTART; exit_act.sa_handler = exit_handler; sigaction(SIGHUP, NULL, &config_act); config_act.sa_flags &= ~SA_RESTART; config_act.sa_handler = config_handler; sigaction(SIGTERM, &exit_act, NULL); sigaction(SIGINT, &exit_act, NULL); sigaction(SIGHUP, &config_act, NULL); while (1) { if (sig_exit) { syslog(LOG_NOTICE, "Stopping Server."); exit(0); } if (sig_config) { syslog(LOG_NOTICE, "Reloading configuration."); if (configure(RELOAD) == -1) syslog(LOG_ERR, "Not using new configuration."); sig_config = 0; } /* Receive request. */ len = sizeof(client_addr); n = recvfrom(server_fd, &request, sizeof(request), 0, &client_addr, &len); if (n == -1 || n != sizeof(request)) continue; /* Process request. */ ret = handle_query(&request, &reply, &tree); if (ret == -1) continue; /* Send reply. */ sendto(server_fd, &reply, sizeof(reply), 0, &client_addr, len); } return 0; out_no_start: syslog(LOG_ERR, "Server not started."); fprintf(stderr, "Server not started - " "consult your syslog for errors.\n"); return -1; } void config_handler(int sig) { sig_config = 1; } void exit_handler(int sig) { sig_exit = 1; } int cmp_addr(struct sockaddr_in addr1, struct sockaddr_in addr2) { int diff = memcmp(&addr1.sin_addr.s_addr, &addr2.sin_addr.s_addr, sizeof(in_addr_t)); if (diff) return diff; return memcmp(&addr1.sin_port, &addr2.sin_port, sizeof(in_port_t)); } int configure(enum config_action action) { void *tmp_tree = NULL; int datasets, ret, tmp_fd = server_fd; struct sockaddr_in tmp_addr; datasets = parse_config_file(cfile, &tmp_addr, &tmp_tree); if (datasets <= 0) return -1; /* Create a socket and bind to it. */ if (action == LOAD || cmp_addr(tmp_addr, server_addr)) { tmp_fd = socket(PF_INET, SOCK_DGRAM, 0); if (tmp_fd == -1) { syslog(LOG_ERR, "Could not create socket."); return -1; } ret = bind(tmp_fd, (struct sockaddr *) &tmp_addr, sizeof(tmp_addr)); if (ret == -1) { close(tmp_fd); syslog(LOG_ERR, "Could not assign name to socket."); return -1; } } /* Make new socket available and close the old one if necesary. */ if (action == RELOAD && cmp_addr(tmp_addr, server_addr)) { if (close(server_fd) == -1 ) { syslog(LOG_ERR, "Could not close socket."); } } server_fd = tmp_fd; server_addr = tmp_addr; tree_destroy(tree); tree = tmp_tree; return 0; } void print_usage(void) { printf("usage: dnbd2-dserver config-file\n"); } int daemon_init(void) { pid_t pid = fork(); if (pid == -1) return pid; if (pid != 0) { /* We are the parent. */ exit(0); } /* We are the child. */ setsid(); chdir("/"); umask(0); return 0; }