From c8b0a5fe5a10d6a04954c89bdcf65a3df3a05ba5 Mon Sep 17 00:00:00 2001 From: "Nikita Tyukalov, ASUS, Linux" Date: Tue, 24 Mar 2026 20:44:22 +0300 Subject: [PATCH] Added argument parser and set all logs to STDERR --- event_loop.c | 6 ++--- main.c | 73 +++++++++++++++++++++++++++++++++++++++++++------- server.c | 10 +++---- utils.c | 75 ++++++++++++++++++++++++++++++++++++++++++++++++++++ utils.h | 23 ++++++++++++++++ 5 files changed, 170 insertions(+), 17 deletions(-) diff --git a/event_loop.c b/event_loop.c index 96c068b..6a70d20 100644 --- a/event_loop.c +++ b/event_loop.c @@ -23,7 +23,7 @@ static struct epoll_event _evs[MAX_EPOLL_EVENTS]; bool loop_init(loop_cb_t callback) { _efd = epoll_create1(0); if (_efd == -1) { - printf("[!] epoll_create1 failed: %d\n", errno); + fprintf(stderr, "[!] epoll_create1 failed: %d\n", errno); return false; } _cb = callback; @@ -36,7 +36,7 @@ void loop_deinit() { bool loop_ctl(int op, int fd, struct epoll_event *event) { if (epoll_ctl(_efd, op, fd, event) == -1) { - printf("[!] epoll_ctl failed: %d\n", errno); + fprintf(stderr, "[!] epoll_ctl failed: %d\n", errno); return false; } return true; @@ -50,7 +50,7 @@ bool loop_wait() { -1 ); if (result < 0) { - printf("[!] epoll_wait failed: %d\n", errno); + fprintf(stderr, "[!] epoll_wait failed: %d\n", errno); return false; } if (!result) { diff --git a/main.c b/main.c index 665cdee..7718029 100644 --- a/main.c +++ b/main.c @@ -1,6 +1,8 @@ #include #include #include +#include +#include #include #include @@ -9,6 +11,7 @@ #include "event_loop.h" #include "server.h" +#include "utils.h" /* * Data @@ -20,6 +23,54 @@ static int _server_fd = -1; /* * 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; + return required_args_present; +} + // Initialize signals. // Returns: // - true on success @@ -32,7 +83,7 @@ static bool signals_init() { sigaddset(&mask, SIGINT); sigaddset(&mask, SIGQUIT); if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) { - printf("[!] sigprocmask failed: %d\n", errno); + fprintf(stderr, "[!] sigprocmask failed: %d\n", errno); return false; } _sig_fd = signalfd( @@ -41,7 +92,7 @@ static bool signals_init() { SFD_NONBLOCK ); if (_sig_fd == -1) { - printf("[!] signalfd failed: %d\n", errno); + fprintf(stderr, "[!] signalfd failed: %d\n", errno); return false; } struct epoll_event ev; @@ -54,7 +105,7 @@ static bool signals_init() { ); if (!loop_res) { close(_sig_fd); - printf("[!] loop_ctl failed\n"); + fprintf(stderr, "[!] loop_ctl failed\n"); return false; } return true; @@ -69,7 +120,7 @@ static void signals_deinit() { // Returns: // - true on success static bool server_init() { - _server_fd = server_open("0.0.0.0", 20070); + _server_fd = server_open(cli_arg_get("A"), atoi(cli_arg_get("P"))); if (_server_fd == -1) { return false; } @@ -83,7 +134,7 @@ static bool server_init() { ); if (!loop_res) { server_close(); - printf("[!] loop_ctl failed\n"); + fprintf(stderr, "[!] loop_ctl failed\n"); return false; } return true; @@ -106,16 +157,20 @@ static void on_fd_event(int fd, uint32_t events) { // Server event else if (fd == _server_fd) { if (!server_event(_server_fd)) { - printf("[!] server_event failed\n"); + fprintf(stderr, "[!] server_event failed\n"); _to_work = false; } } else { - printf("[!] Event has happened on an unknown file descriptor\n"); + fprintf(stderr, "[!] Event has happened on an unknown file descriptor\n"); } } int main(int argc, char **argv) { + if (!parse_cli_args(argc, argv)) { + print_usage_text(); + return 1; + } if (!loop_init(on_fd_event)) { return 1; } @@ -131,10 +186,10 @@ int main(int argc, char **argv) { while (_to_work) { if (!loop_wait()) { - printf("[!] loop_wait returns false, stopping...\n"); + fprintf(stderr, "[!] loop_wait returns false, stopping...\n"); break; } - printf("--- loop ---\n"); + fprintf(stderr, "--- loop ---\n"); } server_close(); diff --git a/server.c b/server.c index 8a2031a..6fb65d6 100644 --- a/server.c +++ b/server.c @@ -34,7 +34,7 @@ bool _accept() { int server_open(const char* host, int port) { struct hostent *he = gethostbyname(host); if (!he) { - printf("[!] gethostbyname failed: %d (h_errno)\n", h_errno); + fprintf(stderr, "[!] gethostbyname failed: %d (h_errno)\n", h_errno); return -1; } _fd = socket( @@ -43,13 +43,13 @@ int server_open(const char* host, int port) { IPPROTO_TCP ); if (_fd == -1) { - printf("[!] socket failed: %d\n", errno); + fprintf(stderr, "[!] socket failed: %d\n", errno); return -1; } const int opt = 1; if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) { close(_fd); - printf("[!] setsockopt failed: %d\n", errno); + fprintf(stderr, "[!] setsockopt failed: %d\n", errno); return -1; } struct sockaddr_in addr; @@ -58,12 +58,12 @@ int server_open(const char* host, int port) { memcpy(&addr.sin_addr, he->h_addr, he->h_length); if (bind(_fd, (const void *)&addr, sizeof(addr)) == -1) { close(_fd); - printf("[!] bind failed: %d\n", errno); + fprintf(stderr, "[!] bind failed: %d\n", errno); return -1; } if (listen(_fd, LISTEN_BACKLOG) == -1) { close(_fd); - printf("[!] listen failed: %d\n", errno); + fprintf(stderr, "[!] listen failed: %d\n", errno); return -1; } return _fd; diff --git a/utils.c b/utils.c index e69de29..b359648 100644 --- a/utils.c +++ b/utils.c @@ -0,0 +1,75 @@ +#include "utils.h" + +#include +#include +#include + +/* + * Macros + */ +#define MAX_CLI_ARGS (20) + +/* + * Type definitions + */ +typedef struct { + const char *key; + char *value; +} cli_arg_t; + +/* + * Data + */ +static cli_arg_t _cli_args[MAX_CLI_ARGS]; + +/* + * Public API + */ +void print_usage_text() { + printf("ipoim -M -A
-P -C ...\n"); + printf(" --- MANDATORY ARGUMENTS ---\n"); + printf(" -M - operation mode ('provider' or 'last-mile')\n"); + printf(" -A
- IPv4 to listen on/connect to\n"); + printf(" -P - port to listen on/connect to\n"); + printf(" -E - extension to use, one of:\n"); + printf(" * pipe - use STDIN and STDOUT\n"); + printf(" --- OPTIONAL ARGUMENTS ---\n"); + printf(" +V - be verbose\n"); +} + +bool cli_arg_set(const char *key, char *value) { + int index_to_use = -1; + for (int i = 0; i < MAX_CLI_ARGS; ++i) { + // empty entry + if (_cli_args[i].key == NULL) { + // update index only if no place is found yet + if (index_to_use == -1) + index_to_use = i; + } + // entry with the same name + else if (!strcmp(_cli_args[i].key, key)) { + index_to_use = i; + break; + } + } + // no space for the argument + if (index_to_use == -1) { + return false; + } + _cli_args[index_to_use].key = key; + _cli_args[index_to_use].value = value; + return true; +} + +const char *cli_arg_get(const char *key) { + for (int i = 0; i < MAX_CLI_ARGS; ++i) { + if (_cli_args[i].key == NULL) { + continue; + } + if (strcmp(_cli_args[i].key, key)) { + continue; + } + return _cli_args[i].value; + } + return NULL; +} diff --git a/utils.h b/utils.h index 4fe2741..4bad6aa 100644 --- a/utils.h +++ b/utils.h @@ -1,6 +1,29 @@ #ifndef __UTILS_H #define __UTILS_H +#include +// Print usage text. +void print_usage_text(); + +// Set CLI argument. +// Parameters: +// - key - argument key (without leading dash) +// - value - argument value as string +// Returns: +// - true on success +// - false on failure +// Remarks: +// - neither key nor value are copied, they are saved as pointers +// - use this function only with data from argv +bool cli_arg_set(const char *key, char *value); + +// Get CLI argument. +// Parameters: +// - key - argument key (without leading dash) +// Returns: +// - pointer to the value (not owned by you) +// - NULL on failure +const char *cli_arg_get(const char *key); #endif