223 lines
5.4 KiB
C
223 lines
5.4 KiB
C
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdint.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
#include <signal.h>
|
|
#include <sys/epoll.h>
|
|
#include <sys/signalfd.h>
|
|
|
|
#include "event_loop.h"
|
|
#include "server.h"
|
|
#include "utils.h"
|
|
|
|
/*
|
|
* Data
|
|
*/
|
|
static bool _to_work = true;
|
|
static int _sig_fd = -1;
|
|
static int _server_fd = -1;
|
|
static bool _is_server = false;
|
|
|
|
/*
|
|
* Private API
|
|
*/
|
|
// Parse command line arguments and check if mandatory ones are present.
|
|
// Parameters:
|
|
// - argc
|
|
// - argv
|
|
// Returns:
|
|
// - true on success
|
|
// - false on failure
|
|
bool parse_cli_args(int argc, char** argv) {
|
|
if (argc < 2) {
|
|
return false;
|
|
}
|
|
int key_length, value_length;
|
|
int i = 1;
|
|
char first_sym;
|
|
while (i < argc) {
|
|
key_length = strlen(argv[i] + 1);
|
|
first_sym = argv[i][0];
|
|
if (key_length != 1 || (first_sym != '+' && first_sym != '-')) {
|
|
fprintf(stderr, "[!] Argument '%s' is discarded\n", argv[i]);
|
|
return false;
|
|
}
|
|
// flag
|
|
if (first_sym == '+') {
|
|
cli_arg_set(argv[i] + 1, argv[i] + 1);
|
|
i++;
|
|
continue;
|
|
}
|
|
// argument
|
|
if (i + 1 >= argc) {
|
|
fprintf(stderr, "[!] Argument '%s' misses a value\n", argv[i]);
|
|
return false;
|
|
}
|
|
value_length = strlen(argv[i + 1]);
|
|
if (value_length < 1) {
|
|
fprintf(stderr, "[!] Argument '%s' has empty value\n", argv[i]);
|
|
return false;
|
|
}
|
|
cli_arg_set(argv[i] + 1, argv[i + 1]);
|
|
i += 2;
|
|
}
|
|
bool required_args_present = true;
|
|
required_args_present &= cli_arg_get("M") ? true : false;
|
|
required_args_present &= cli_arg_get("A") ? true : false;
|
|
required_args_present &= cli_arg_get("P") ? true : false;
|
|
required_args_present &= cli_arg_get("E") ? true : false;
|
|
if (!required_args_present) {
|
|
fprintf(stderr, "[!] Some required arguments are missing\n");
|
|
return false;
|
|
}
|
|
const char *mode = cli_arg_get("M");
|
|
if (strcmp(mode, "server") && strcmp(mode, "client")) {
|
|
fprintf(stderr, "[!] Unknown mode '%s'\n", mode);
|
|
return false;
|
|
}
|
|
_is_server = !strcmp(mode, "server");
|
|
flag_verbose = cli_arg_get("V") ? 1 : 0;
|
|
if (flag_verbose) {
|
|
fprintf(stdout, "[I] Being verbose\n");
|
|
fprintf(stdout, "[I] - Mode ------ %s\n", cli_arg_get("M"));
|
|
fprintf(stdout, "[I] - Address --- %s:%d\n", cli_arg_get("A"), atoi(cli_arg_get("P")));
|
|
fprintf(stdout, "[I] - Extension - %s\n", cli_arg_get("E"));
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Initialize signals.
|
|
// Returns:
|
|
// - true on success
|
|
// Remarks:
|
|
// - loop_init() must be called before this one
|
|
static bool signals_init() {
|
|
sigset_t mask;
|
|
sigemptyset(&mask);
|
|
sigaddset(&mask, SIGTERM);
|
|
sigaddset(&mask, SIGINT);
|
|
sigaddset(&mask, SIGQUIT);
|
|
if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) {
|
|
fprintf(stderr, "[!] sigprocmask failed: %d\n", errno);
|
|
return false;
|
|
}
|
|
_sig_fd = signalfd(
|
|
-1,
|
|
&mask,
|
|
SFD_NONBLOCK
|
|
);
|
|
if (_sig_fd == -1) {
|
|
fprintf(stderr, "[!] signalfd failed: %d\n", errno);
|
|
return false;
|
|
}
|
|
struct epoll_event ev;
|
|
ev.data.fd = _sig_fd;
|
|
ev.events = EPOLLIN;
|
|
bool loop_res = loop_ctl(
|
|
EPOLL_CTL_ADD,
|
|
_sig_fd,
|
|
&ev
|
|
);
|
|
if (!loop_res) {
|
|
close(_sig_fd);
|
|
fprintf(stderr, "[!] loop_ctl failed\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Finalize signals.
|
|
static void signals_deinit() {
|
|
close(_sig_fd);
|
|
}
|
|
|
|
// Initialize the server.
|
|
// Returns:
|
|
// - true on success
|
|
static bool server_init() {
|
|
_server_fd = server_open(cli_arg_get("A"), atoi(cli_arg_get("P")));
|
|
if (_server_fd == -1) {
|
|
return false;
|
|
}
|
|
struct epoll_event ev;
|
|
ev.data.fd = _server_fd;
|
|
ev.events = EPOLLIN;
|
|
bool loop_res = loop_ctl(
|
|
EPOLL_CTL_ADD,
|
|
_server_fd,
|
|
&ev
|
|
);
|
|
if (!loop_res) {
|
|
server_close();
|
|
fprintf(stderr, "[!] loop_ctl failed\n");
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// Finalize the server.
|
|
static void server_deinit() {
|
|
server_close();
|
|
}
|
|
|
|
/*
|
|
* Callbacks and main
|
|
*/
|
|
// This callback is called by loop_wait when event happens.
|
|
static void on_fd_event(int fd, uint32_t events) {
|
|
// Termination
|
|
if (fd == _sig_fd) {
|
|
_to_work = false;
|
|
}
|
|
// Server event
|
|
else if (fd == _server_fd) {
|
|
if (!server_event(_server_fd)) {
|
|
fprintf(stderr, "[!] server_event failed\n");
|
|
_to_work = false;
|
|
}
|
|
}
|
|
else {
|
|
fprintf(stderr, "[!] Event has happened on an unknown file descriptor\n");
|
|
}
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
// Initialization
|
|
if (!parse_cli_args(argc, argv)) {
|
|
print_usage_text();
|
|
return 1;
|
|
}
|
|
if (!loop_init(on_fd_event)) {
|
|
return 1;
|
|
}
|
|
if (!signals_init()) {
|
|
loop_deinit();
|
|
return 1;
|
|
}
|
|
if (_is_server) {
|
|
if (!server_init()) {
|
|
signals_deinit();
|
|
loop_deinit();
|
|
return 1;
|
|
}
|
|
}
|
|
// Loop
|
|
while (_to_work) {
|
|
if (!loop_wait()) {
|
|
fprintf(stderr, "[!] loop_wait returns false, stopping...\n");
|
|
break;
|
|
}
|
|
fprintf(stderr, "--- loop ---\n");
|
|
}
|
|
// Termination
|
|
if (_is_server) {
|
|
server_close();
|
|
}
|
|
signals_deinit();
|
|
loop_deinit();
|
|
return 0;
|
|
}
|