From 7099a86ca74fa637f26af38674f80fb8efd5f6fa Mon Sep 17 00:00:00 2001 From: Trumeet Date: Tue, 26 Jul 2022 17:36:20 -0700 Subject: refactor(libacron/acronc/helloworld): move to separate directories The corresponding CMakeLists.txt files are still rough. --- client/acronc/.gitignore | 82 ++++++ client/acronc/CMakeLists.txt | 68 +++++ client/acronc/README.md | 11 + client/acronc/async_dns.c | 65 +++++ client/acronc/client.c | 114 +++++++++ client/acronc/client.h | 12 + client/acronc/common.h | 8 + client/acronc/config.c | 100 ++++++++ client/acronc/config.h | 12 + client/acronc/handler.h | 55 ++++ client/acronc/handler_signal.c | 40 +++ client/acronc/handler_socket.c | 255 ++++++++++++++++++ client/acronc/handler_stdin.c | 85 ++++++ client/acronc/helpers.c | 58 +++++ client/acronc/helpers.h | 24 ++ client/acronc/log.c | 61 +++++ client/acronc/log.h | 52 ++++ client/acronc/main.c | 105 ++++++++ client/helloworld/.gitignore | 82 ++++++ client/helloworld/CMakeLists.txt | 50 ++++ client/helloworld/main.c | 370 +++++++++++++++++++++++++++ client/helloworld/net.c | 181 +++++++++++++ client/helloworld/net.h | 45 ++++ client/libacron/CMakeLists.txt | 27 +- client/libacron/README.md | 3 - client/libacron/apps/acronc/README.md | 11 - client/libacron/apps/acronc/async_dns.c | 65 ----- client/libacron/apps/acronc/client.c | 114 --------- client/libacron/apps/acronc/client.h | 12 - client/libacron/apps/acronc/common.h | 8 - client/libacron/apps/acronc/config.c | 100 -------- client/libacron/apps/acronc/config.h | 12 - client/libacron/apps/acronc/handler.h | 55 ---- client/libacron/apps/acronc/handler_signal.c | 40 --- client/libacron/apps/acronc/handler_socket.c | 255 ------------------ client/libacron/apps/acronc/handler_stdin.c | 85 ------ client/libacron/apps/acronc/helpers.c | 58 ----- client/libacron/apps/acronc/helpers.h | 24 -- client/libacron/apps/acronc/log.c | 61 ----- client/libacron/apps/acronc/log.h | 52 ---- client/libacron/apps/acronc/main.c | 105 -------- client/libacron/apps/helloworld/main.c | 370 --------------------------- client/libacron/apps/helloworld/net.c | 181 ------------- client/libacron/apps/helloworld/net.h | 45 ---- 44 files changed, 1936 insertions(+), 1682 deletions(-) create mode 100644 client/acronc/.gitignore create mode 100644 client/acronc/CMakeLists.txt create mode 100644 client/acronc/README.md create mode 100644 client/acronc/async_dns.c create mode 100644 client/acronc/client.c create mode 100644 client/acronc/client.h create mode 100644 client/acronc/common.h create mode 100644 client/acronc/config.c create mode 100644 client/acronc/config.h create mode 100644 client/acronc/handler.h create mode 100644 client/acronc/handler_signal.c create mode 100644 client/acronc/handler_socket.c create mode 100644 client/acronc/handler_stdin.c create mode 100644 client/acronc/helpers.c create mode 100644 client/acronc/helpers.h create mode 100644 client/acronc/log.c create mode 100644 client/acronc/log.h create mode 100644 client/acronc/main.c create mode 100644 client/helloworld/.gitignore create mode 100644 client/helloworld/CMakeLists.txt create mode 100644 client/helloworld/main.c create mode 100644 client/helloworld/net.c create mode 100644 client/helloworld/net.h delete mode 100644 client/libacron/apps/acronc/README.md delete mode 100644 client/libacron/apps/acronc/async_dns.c delete mode 100644 client/libacron/apps/acronc/client.c delete mode 100644 client/libacron/apps/acronc/client.h delete mode 100644 client/libacron/apps/acronc/common.h delete mode 100644 client/libacron/apps/acronc/config.c delete mode 100644 client/libacron/apps/acronc/config.h delete mode 100644 client/libacron/apps/acronc/handler.h delete mode 100644 client/libacron/apps/acronc/handler_signal.c delete mode 100644 client/libacron/apps/acronc/handler_socket.c delete mode 100644 client/libacron/apps/acronc/handler_stdin.c delete mode 100644 client/libacron/apps/acronc/helpers.c delete mode 100644 client/libacron/apps/acronc/helpers.h delete mode 100644 client/libacron/apps/acronc/log.c delete mode 100644 client/libacron/apps/acronc/log.h delete mode 100644 client/libacron/apps/acronc/main.c delete mode 100644 client/libacron/apps/helloworld/main.c delete mode 100644 client/libacron/apps/helloworld/net.c delete mode 100644 client/libacron/apps/helloworld/net.h diff --git a/client/acronc/.gitignore b/client/acronc/.gitignore new file mode 100644 index 0000000..6741ba2 --- /dev/null +++ b/client/acronc/.gitignore @@ -0,0 +1,82 @@ +cmake-build-debug/ +cmake-build-release/ +json-c/ + +# https://raw.githubusercontent.com/github/gitignore/main/Global/JetBrains.gitignore +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser diff --git a/client/acronc/CMakeLists.txt b/client/acronc/CMakeLists.txt new file mode 100644 index 0000000..ec2b7fb --- /dev/null +++ b/client/acronc/CMakeLists.txt @@ -0,0 +1,68 @@ +cmake_minimum_required(VERSION 3.22) +project(libac C) + +set(CMAKE_C_STANDARD 11) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +add_definitions(-D_POSIX_C_SOURCE=200809L) +IF(CMAKE_BUILD_TYPE MATCHES Debug) + add_definitions(-DDEBUG) +ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) + +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_C_FLAGS_DEBUG + "${CMAKE_C_FLAGS_DEBUG} -g3 -O0 -fsanitize=address") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG + "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address") + set(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -fvisibility=hidden") +elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + # TODO: MSVC ASAN + set(CMAKE_C_FLAGS_DEBUG + "${CMAKE_C_FLAGS_DEBUG} /DEBUG /Z7 /Od") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG + "${CMAKE_EXE_LINKER_FLAGS_DEBUG}") +endif() + +add_subdirectory(../libacron ${CMAKE_BINARY_DIR}/libac) + +# helloworld +#add_executable(helloworld +# helloworld/main.c +# helloworld/net.c +# helloworld/net.h +# ) +#target_link_libraries(helloworld ${APPS_DEPS}) +#target_include_directories(helloworld PUBLIC "${PROJECT_BINARY_DIR}" include/) + +# acronc +add_executable(acronc + main.c + common.h + log.h + log.c + handler.h + handler_signal.c + config.c + config.h + client.c + client.h + handler_stdin.c + helpers.c + helpers.h + handler_socket.c + async_dns.c + ) +target_link_libraries(acronc ${APPS_DEPS} uv ac) +target_include_directories(acronc PUBLIC "${PROJECT_BINARY_DIR}" include/) + +install(TARGETS acronc + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/libac +) diff --git a/client/acronc/README.md b/client/acronc/README.md new file mode 100644 index 0000000..87c9db0 --- /dev/null +++ b/client/acronc/README.md @@ -0,0 +1,11 @@ +# acronc(1) + +Acron client using libac and libuv, written in C. + +Current status: In development. + +Windows is currently not supported yet. + +## License + +GPL-2.0-only \ No newline at end of file diff --git a/client/acronc/async_dns.c b/client/acronc/async_dns.c new file mode 100644 index 0000000..2e221cf --- /dev/null +++ b/client/acronc/async_dns.c @@ -0,0 +1,65 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "handler.h" +#include "log.h" + +#include + +static uv_getaddrinfo_t resolv; + +static struct addrinfo *ai = NULL; +static struct addrinfo *ai_current = NULL; +static void (*cb)(int status, + const struct addrinfo *, + void (*on_connect_result)(bool)) = NULL; + +static void on_conn(bool res) { + if (res) { + uv_freeaddrinfo(ai); + ai = NULL; + ai_current = NULL; + cb = NULL; + return; + } + ai_current = ai_current->ai_next; + if (!ai_current) { + /* No more result available. */ + cb(1, NULL, NULL); + uv_freeaddrinfo(ai); + ai = NULL; + ai_current = NULL; + cb = NULL; + return; + } + cb(0, ai_current, &on_conn); +} + +static void on_resolv(uv_getaddrinfo_t *req, int status, struct addrinfo *res) { + LOGDV("on_resolv(req = %p): status = %d, res = %p", + req, + status, + res); + if (status) { + LOGEV("Cannot resolve host: %s", uv_strerror(status)); + cb(status, NULL, NULL); + return; + } + ai = res; + ai_current = res; + cb(0, ai_current, &on_conn); +} + +int a_dns(const char *host, + uint16_t port, + void (*c)(int status, + const struct addrinfo *, + void (*on_connect_result)(bool))) { + int r; + char service[6]; + snprintf(service, 6, "%u", port); + cb = c; + if ((r = uv_getaddrinfo(loop, &resolv, on_resolv, host, service, NULL))) return r; + return 0; +} diff --git a/client/acronc/client.c b/client/acronc/client.c new file mode 100644 index 0000000..5553d4c --- /dev/null +++ b/client/acronc/client.c @@ -0,0 +1,114 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "client.h" +#include "helpers.h" + +#include +#include + +static void handle_event(const ac_event_t *event) { + switch (event->type) { + case AC_EVENT_LAGGING: { + ac_event_lagging_t *o = (ac_event_lagging_t *) event; + printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", + o->ms, + o->ticks); + break; + } + case AC_EVENT_ENTITY_DEATH: { + ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; + printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", + o->entity.name, + world_name(o->entity.world), + o->entity.pos.x, + o->entity.pos.y, + o->entity.pos.z, + o->message); + break; + } + case AC_EVENT_PLAYER_MESSAGE: { + ac_event_player_message_t *o = (ac_event_player_message_t *) event; + printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->text); + break; + } + case AC_EVENT_PLAYER_DISCONNECT: { + ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; + printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->reason); + break; + } + case AC_EVENT_PLAYER_JOIN: { + ac_event_player_join_t *o = (ac_event_player_join_t *) event; + printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z); + break; + } + default: { + printf("Received an unrecognized event of type '%u'.\n", + event->type); + } + } +} + +static void handle_response(const ac_response_t *response) { + switch (response->type) { + case AC_RESPONSE_OK: { + ac_response_ok_t *o = (ac_response_ok_t *) response; + printf("Request %d OK.\n", + o->id); + break; + } + case AC_RESPONSE_ERROR: { + ac_response_error_t *o = (ac_response_error_t *) response; + printf("Request %d failed: %s (%d).\n", + o->id, + o->message, + o->code); + break; + } + case AC_RESPONSE_CMD_OUT: { + ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; + printf("Request %d output by %s: %s.\n", + o->id, + o->sender, + o->out); + break; + } + case AC_RESPONSE_CMD_RESULT: { + ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; + printf("Request %d is done: %s (%d).\n", + o->id, + o->success ? "Success" : "Failed", + o->result); + break; + } + default: { + printf("Received an unrecognized response of type '%u'.\n", + response->type); + } + } +} + +int handle_object(ac_obj_t *obj) { + if (AC_IS_RESPONSE(obj->type)) handle_response((ac_response_t *) obj); + else handle_event((ac_event_t *) obj); + ac_object_free(obj); + return 0; +} \ No newline at end of file diff --git a/client/acronc/client.h b/client/acronc/client.h new file mode 100644 index 0000000..438d7fd --- /dev/null +++ b/client/acronc/client.h @@ -0,0 +1,12 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#ifndef ACRONC_CLIENT_H +#define ACRONC_CLIENT_H + +#include + +int handle_object(ac_obj_t *obj); + +#endif /* ACRONC_CLIENT_H */ diff --git a/client/acronc/common.h b/client/acronc/common.h new file mode 100644 index 0000000..0ca3b92 --- /dev/null +++ b/client/acronc/common.h @@ -0,0 +1,8 @@ +/* + * Created by yuuta on 7/23/22. + */ + +#ifndef ACRONC_COMMON_H +#define ACRONC_COMMON_H + +#endif /* ACRONC_COMMON_H */ diff --git a/client/acronc/config.c b/client/acronc/config.c new file mode 100644 index 0000000..046d931 --- /dev/null +++ b/client/acronc/config.c @@ -0,0 +1,100 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "config.h" + +#include +#include +#include +#include +#include + +int config_parse(const int argc, const char **argv, ac_connection_parameters_t *params) { + if (argc <= 1) { + fprintf(stderr, "Usage: %s [-t token] [-p port] @\n" + "Please refer to acronc(1) for more details.\n", + argv[0] ? "acronc" : argv[0]); + return 64; + } + bool svr_set = false; + for (int i = 1; i < argc; i++) { + const char *arg = argv[i]; + if (arg[0] != '-') { + if (svr_set) { + fprintf(stderr, "Unexpected value: %s\n", arg); + return 64; + } + const char *id = strtok((char *) arg, "@"); + if (!id) { + fprintf(stderr, "Malformed argument: should be ID@server.\n"); + return 64; + } + const char *server = strtok(NULL, ""); + if (!server) { + fprintf(stderr, "Malformed argument: should be ID@server.\n"); + return 64; + } + params->host = (char *) server; + params->id = (char *) id; + svr_set = true; + continue; + } + char a = arg[1]; + char **value_ptr; + char *port_str = NULL; + switch (a) { + case 't': { + value_ptr = ¶ms->token; + goto read_value; + } + case 'p': { + value_ptr = &port_str; + goto read_value; + } + default: { + fprintf(stderr, "Unexpected switch -%c.\n", a); + return 64; + } + } + read_value: + { + if (i == (argc - 1)) { + fprintf(stderr, "-%c requires an argument.\n", a); + return 64; + } + *value_ptr = (char *) argv[++i]; + } + if (port_str) { + errno = 0; + char *endptr; + unsigned long val = strtol(port_str, &endptr, 0); + if (errno) { + const int e = errno; + fprintf(stderr, "Cannot parse port: %s", strerror(e)); + return e; + } + if (endptr == port_str) { + fprintf(stderr, "Illegal port.\n"); + return 64; + } + if (val > UINT16_MAX) { + fprintf(stderr, "Illegal port.\n"); + return 64; + } + params->port = (uint16_t) val; + port_str = NULL; + } + } + if (!params->host || !params->id) { + fprintf(stderr, "Usage: acronc ID@server[:port]\n"); + return 64; + } + char tok[1024]; + if (!params->token) { + printf("Token: "); + scanf("%1023[^\n]", tok); + params->token = tok; + } + return 0; +} \ No newline at end of file diff --git a/client/acronc/config.h b/client/acronc/config.h new file mode 100644 index 0000000..ee51f34 --- /dev/null +++ b/client/acronc/config.h @@ -0,0 +1,12 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#ifndef ACRONC_CONFIG_H +#define ACRONC_CONFIG_H + +#include + +int config_parse(int argc, const char **argv, ac_connection_parameters_t *params); + +#endif /* ACRONC_CONFIG_H */ diff --git a/client/acronc/handler.h b/client/acronc/handler.h new file mode 100644 index 0000000..1a2ccfe --- /dev/null +++ b/client/acronc/handler.h @@ -0,0 +1,55 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#ifndef ACRONC_HANDLER_H +#define ACRONC_HANDLER_H + +#include +#include + +enum exit_reason { + /* Exit due to a signal. Socket is working. */ + EXIT_SIGNAL, + /* Exit due to an EOF from stdin. Socket is working. */ + EXIT_STDIN_EOF, + /* Exit due to an EOF from socket. Socket is closed. */ + EXIT_SOCKET_EOF, + /* Exit due to a socket failure. Socket is closed. */ + EXIT_FAILURE_SOCKET, + /* Exit due to a failure. Socket is working. */ + EXIT_FAILURE +}; + +struct uv_obj { + void *obj; + bool running; +}; + +extern uv_loop_t *loop; + +extern void (*on_exit)(enum exit_reason reason); + +int h_signal(void); + +int h_stdin(int (*on_input)(ac_request_t *req), + void (*on_close)(void)); + +int h_socket(ac_connection_parameters_t *p, + const struct addrinfo *ai, + void (*on_connect_result)(bool), + int (*on_ready)(void), + int (*on_received)(ac_obj_t *obj), + void (*on_closed)(void)); + +int sock_request(ac_request_t *req); + +int sock_ext(bool trigger_callback); + +int a_dns(const char *host, + uint16_t port, + void (*on_resolv)(int status, + const struct addrinfo *, + void (*on_connect_result)(bool))); + +#endif /* ACRONC_HANDLER_H */ diff --git a/client/acronc/handler_signal.c b/client/acronc/handler_signal.c new file mode 100644 index 0000000..1d0ba7e --- /dev/null +++ b/client/acronc/handler_signal.c @@ -0,0 +1,40 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "handler.h" +#include "log.h" + +static uv_signal_t sigint; +static struct uv_obj obj_sigint = { + .obj = &sigint, + .running = false +}; +static uv_signal_t sigterm; +static struct uv_obj obj_sigterm = { + .obj = &sigterm, + .running = false +}; + +static void on_signal(uv_signal_t *handle, int signum) { + LOGDV("on_signal(handle = %p): %d", + handle, + signum); + uv_signal_stop(&sigint); + obj_sigint.running = false; + uv_signal_stop(&sigterm); + obj_sigterm.running = false; + on_exit(EXIT_SIGNAL); +} + +int h_signal(void) { + int r; + if ((r = uv_signal_init(loop, &sigint))) return r; + if ((r = uv_signal_start(&sigint, on_signal, SIGINT))) return r; + + obj_sigint.running = true; + if ((r = uv_signal_init(loop, &sigterm))) return r; + if ((r = uv_signal_start(&sigterm, on_signal, SIGTERM))) return r; + obj_sigterm.running = true; + return 0; +} diff --git a/client/acronc/handler_socket.c b/client/acronc/handler_socket.c new file mode 100644 index 0000000..ff8eefb --- /dev/null +++ b/client/acronc/handler_socket.c @@ -0,0 +1,255 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "handler.h" +#include "log.h" +#include "helpers.h" + +#include +#include +#include + +static void (*cb_conn)(bool); + +static int (*cb_ready)(void); + +static int (*cb_recv)(ac_obj_t *obj); + +static void (*cb_close)(void); + +static ac_connection_parameters_t *params; + +static uv_tcp_t sock; +static uv_write_t write; + +static uv_connect_t conn; + +static bool ready = false; +static void *ac_conn = NULL; + +#define RUNNING !uv_is_closing((uv_handle_t *) &sock) + +static void on_close(uv_handle_t *handle) { + LOGDV("on_close(handle = %p)", + handle); + if (handle->data) + cb_close(); +} + +static void ex2(bool force, bool cb) { + LOGDV("Exiting socket handlers: Force WebSocket close: %s; Socket still running: %s.", + force ? "true" : "false", + RUNNING ? "true" : "false"); + if (ac_conn) { + ac_disconnect(ac_conn, + RUNNING ? force : true /* If sock is not running, always force */); + ac_conn = NULL; + } + if (RUNNING) { + sock.data = cb ? &ex2 /* Any non-NULL value */ : NULL; + uv_close((uv_handle_t *) &sock, on_close); + } else { + if (cb) { + cb_close(); + } + } +} + +static void ex(bool force) { + ex2(force, true); +} + +int sock_ext(bool trigger_callback) { + ex2(false, trigger_callback); + return 0; +} + +static void on_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) { + LOGDV("on_alloc(handle = %p): size = %lu", + handle, + size); + void *b = malloc(size); + if (!b) { + LOGEV("Cannot allocate memory of %u bytes: %s.", + size, + strerror(errno)); + /* Socket is still working now. */ + ex(false); + *buf = uv_buf_init(NULL, 0); /* Just in case? */ + return; + } + *buf = uv_buf_init(b, size); +} + +static void on_read(uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { + LOGDV("on_read(stream = %p): nread = %ld, buf = %p, buf->base = %p, buf->len = %lu", + tcp, + nread, + buf, buf->base, buf->len); + if (!nread) { + /* EAGAIN */ + if (buf->base) free(buf->base); + return; + } + if (nread == UV_EOF) { + LOGW("Received EOF from server."); + free(buf->base); + /* The socket * should * be closed already? */ + ex(true); + return; + } + if (nread < 0) { + LOGEV("Encountered a failure while reading from the socket: ", uv_strerror(nread)); + if (buf->base) free(buf->base); + /* Docs: The callee is responsible for stopping/closing the stream when an error happens */ + ex(true); /* Force close libac connection here. */ + return; + } + int r; + ac_obj_t *obj = NULL; + if ((r = ac_receive(ac_conn, buf->base, nread, &obj))) { + LOGEV("Cannot parse the response (%d).", r); + /* libac error. Socket is working. */ + ex(false); + return; + } + + if (!ready) { + enum ac_connection_state state; + if ((r = ac_get_state(ac_conn, &state))) { + LOGEV("Cannot get state (%d).", r); + /* libac error. Socket is working. */ + ex(false); + return; + } + if (state == AC_STATE_READY) { + ready = true; + if ((cb_ready())) { + /* acronc error. Socket is working. */ + ex(false); + return; + } + } + } + if (obj) { + LOGDV("Got object: %p", obj); + /* uv_async_send is unreliable, and missed messages will cause memory leak. */ + if (cb_recv(obj)) { + /* acronc error. Socket is working. */ + ex(false); + } + } + + free(buf->base); +} + +static void on_write(uv_write_t *req, int status) { + LOGDV("on_write(req = %p): %d", + req, + status); + if (status) { + LOGEV("Cannot write to socket: %s", uv_strerror(status)); + /* Socket may be closed? Anyway writing again will definitely be problematic, but closing may not. */ + ex(true); + } +} + +static int on_send(const void *s, + const void *buf, + const size_t len) { + LOGDV("on_send(s = %p): len = %u", + s, + len); + const uv_stream_t *stream = s; + uv_buf_t buffer[] = { + {.base = (char *) buf, .len = len} + }; + int r; + if ((r = uv_write(&write, (uv_stream_t *) stream, buffer, 1, on_write))) { + LOGEV("Cannot write to socket: %s", uv_strerror(r)); + /* Socket may be closed? Anyway writing again will definitely be problematic, but closing may not. */ + ex(true); + } + return 0; +} + +static void on_retry_closed(uv_handle_t *handle) { + cb_conn(false); +} + +static void on_connect(uv_connect_t *req, int status) { + LOGDV("on_connect(req = %p): status = %d", + req, + status); + if (status) { + LOGEV("Cannot connect to the server: %s", uv_strerror(status)); + /* Close it for the next retry. */ + if (RUNNING) { + LOGD("Closing socket before retry."); + uv_close((uv_handle_t *) &sock, on_retry_closed); + } else { + cb_conn(false); + } + return; + } + LOGI("Connected."); + uv_stream_t *stream = req->handle; + int r; + params->sock = stream; + if ((r = ac_connect(*params, &ac_conn))) { + LOGEV("Cannot initialize connection: %d.", r); + cb_conn(false); + return; + } + if ((r = uv_read_start(stream, on_alloc, on_read))) { + LOGEV("Cannot read socket: %s", uv_strerror(r)); + cb_conn(false); + return; + } + cb_conn(true); +} + +int h_socket(ac_connection_parameters_t *p, + const struct addrinfo *ai, + void (*on_connect_result)(bool), + int (*on_ready)(void), + int (*on_received)(ac_obj_t *obj), + void (*on_closed)(void)) { + cb_conn = on_connect_result; + cb_ready = on_ready; + cb_recv = on_received; + cb_close = on_closed; + params = p; + params->on_send = on_send; + + struct sockaddr *sa = ai->ai_addr; + LOGIV("Connecting to %s...", ntop(sa)); + int r; + if ((r = uv_tcp_init(loop, &sock))) { + LOGEV("Cannot initialize the socket: %s", uv_strerror(r)); + return r; + } + if ((r = uv_tcp_connect(&conn, &sock, sa, on_connect))) { + LOGEV("Cannot initialize the connection: %s", uv_strerror(r)); + return r; + } + return 0; +} + +int sock_request(ac_request_t *req) { + if (!ac_conn) { + /* Just in case. */ + LOGE("sock_request() called on a closed socket."); + /* Ignore it. */ + return 0; + } + int r = ac_request(ac_conn, req); + if (r) { + LOGEV("Cannot send request: %d.", r); + /* libac error. Socket is working. */ + ex(false); + } + return r; +} + diff --git a/client/acronc/handler_stdin.c b/client/acronc/handler_stdin.c new file mode 100644 index 0000000..3ac307d --- /dev/null +++ b/client/acronc/handler_stdin.c @@ -0,0 +1,85 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "handler.h" +#include "log.h" + +#include +#include + +static unsigned int id = 0; + +static uv_tty_t tty; + +static int (*cb_recv)(ac_request_t *req); + +static void (*cb_close)(void); + +static void on_close(uv_handle_t *handle) { + cb_close(); +} + +static void on_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) { + LOGDV("on_alloc(handle = %p): size = %lu", + handle, + size); + void *b = malloc(size); + if (!b) { + LOGEV("Cannot allocate memory of %u bytes: %s.", + size, + strerror(errno)); + /* Stream is still working now. */ + uv_close(handle, on_close); + *buf = uv_buf_init(NULL, 0); /* Just in case? */ + return; + } + *buf = uv_buf_init(b, size); +} + +static void on_stdin(uv_stream_t *t, ssize_t nread, const uv_buf_t *buf) { + LOGDV("on_stdin(stream = %p): nread = %ld, buf = %p, buf->base = %p, buf->len = %lu", + t, + nread, + buf, buf->base, buf->len); + if (!nread) { + if (buf->base) free(buf->base); + return; + } + if (nread == UV_EOF) { + LOGI("Exit."); + free(buf->base); + + cb_close(); + return; + } + if (nread < 0) { + LOGEV("Encountered a failure while reading stdin: ", uv_strerror(nread)); + if (buf->base) free(buf->base); + uv_close((uv_handle_t *) t, on_close); + return; + } + buf->base[nread - 1] = '\0'; /* Remove junk and tailing \n */ + if ((++ id) > INT_MAX) { + id = 0; + } + ac_request_cmd_t req = { + .type = AC_REQUEST_CMD, + .id = (int) id, + .cmd = buf->base + }; + if (cb_recv((ac_request_t *) &req)) { + uv_close((uv_handle_t *) t, on_close); + } + free(buf->base); +} + +int h_stdin(int (*on_input)(ac_request_t *req), + void (*on_close)(void)) { + cb_recv = on_input; + cb_close = on_close; + int r; + if ((r = uv_tty_init(loop, &tty, 0, 0))) return r; + if ((r = uv_read_start((uv_stream_t *) &tty, on_alloc, on_stdin))) return r; + return 0; +} \ No newline at end of file diff --git a/client/acronc/helpers.c b/client/acronc/helpers.c new file mode 100644 index 0000000..9e141d4 --- /dev/null +++ b/client/acronc/helpers.c @@ -0,0 +1,58 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#include "helpers.h" + +#include +#include +#include + +#ifdef WIN32 +#include +#include +#include + +#define errno_sock WSAGetLastError() + +#else + +#include +#include + +#define errno_sock errno + +#endif + +const char *ntop(const struct sockaddr *sa) { + static char buf[INET6_ADDRSTRLEN]; + const char *retval; + switch (sa->sa_family) { + case AF_INET: { + retval = inet_ntop(AF_INET, &((struct sockaddr_in *) sa)->sin_addr, buf, sizeof(buf)); + break; + } + case AF_INET6: { + retval = inet_ntop(AF_INET6, &((struct sockaddr_in6 *) sa)->sin6_addr, buf, sizeof(buf)); + break; + } + default: { + return "Unknown address family"; + } + } + if (retval) return retval; + return strerror(errno_sock); +} + +const char *world_name(const enum ac_world world) { + switch (world) { + case overworld: + return "overworld"; + case nether: + return "nether"; + case end: + return "end"; + default: + return "unknown world"; + } +} diff --git a/client/acronc/helpers.h b/client/acronc/helpers.h new file mode 100644 index 0000000..e983439 --- /dev/null +++ b/client/acronc/helpers.h @@ -0,0 +1,24 @@ +/* + * Created by yuuta on 7/24/22. + */ + +#ifndef ACRONC_HELPERS_H +#define ACRONC_HELPERS_H + +#include + +#ifdef WIN32 +#include +#include + +#else + +#include + +#endif + +const char *ntop(const struct sockaddr *sa); + +const char *world_name(enum ac_world world); + +#endif /* ACRONC_HELPERS_H */ diff --git a/client/acronc/log.c b/client/acronc/log.c new file mode 100644 index 0000000..08f48b6 --- /dev/null +++ b/client/acronc/log.c @@ -0,0 +1,61 @@ +/* + * Created by yuuta on 1/1/22. + */ + +#define _GNU_SOURCE + +#include "log.h" +#include "config.h" + +#include +#include +#include + +#ifdef __linux__ +#include +#include +#include +#endif + +void g_log(enum log_level level, + const char *file, + int line, + const char *format, + ...) { + FILE *stream = stderr; + switch (level) { + case log_fetal: + fprintf(stream, "F"); + break; + case log_error: + fprintf(stream, "E"); + break; + case log_warn: + fprintf(stream, "W"); + break; + case log_info: + fprintf(stream, "I"); + break; + case log_debug: +#ifdef DEBUG + fprintf(stream, "D"); + break; +#else + return; +#endif + default: + fprintf(stderr, "Unknown log level: %d.\n", level); + assert(0); + } + int tid = -1; +#ifdef __linux__ + tid = (int) syscall(__NR_gettid); +#endif + fprintf(stream, "[%d %s:%d]: ", + tid, file, line); + va_list list; + va_start(list, format); + vfprintf(stream, format, list); + va_end(list); + fprintf(stream, "\n"); +} diff --git a/client/acronc/log.h b/client/acronc/log.h new file mode 100644 index 0000000..7bc9532 --- /dev/null +++ b/client/acronc/log.h @@ -0,0 +1,52 @@ +/* + * Created by yuuta on 1/1/22. + */ + +#ifndef LOG_H +#define LOG_H + +enum log_level { + log_fetal = 1, + log_error = 2, + log_warn = 3, + log_info = 4, + log_debug = 5 +}; + +void g_log(enum log_level level, + const char *file, + int line, + const char *format, + ...); + +#define LOGF(X) g_log(log_fetal, __FUNCTION__, __LINE__, X) + +#define LOGFV(X, ...) g_log(log_fetal, __FUNCTION__, __LINE__, X, __VA_ARGS__) + +#define LOGE(X) g_log(log_error, __FUNCTION__, __LINE__, X) + +#define LOGEV(X, ...) g_log(log_error, __FUNCTION__, __LINE__, X, __VA_ARGS__) + +#define LOGW(X) g_log(log_warn, __FUNCTION__, __LINE__, X) + +#define LOGWV(X, ...) g_log(log_warn, __FUNCTION__, __LINE__, X, __VA_ARGS__) + +#define LOGI(X) g_log(log_info, __FUNCTION__, __LINE__, X) + +#define LOGIV(X, ...) g_log(log_info, __FUNCTION__, __LINE__, X, __VA_ARGS__) + +#ifdef DEBUG + +#define LOGD(X) g_log(log_debug, __FUNCTION__, __LINE__, X) + +#define LOGDV(X, ...) g_log(log_debug, __FUNCTION__, __LINE__, X, __VA_ARGS__) + +#else + +#define LOGD(X) + +#define LOGDV(X, ...) + +#endif + +#endif /* LOG_H */ diff --git a/client/acronc/main.c b/client/acronc/main.c new file mode 100644 index 0000000..6e18ed2 --- /dev/null +++ b/client/acronc/main.c @@ -0,0 +1,105 @@ +/* + * Created by yuuta on 7/23/22. + */ + +#include "log.h" +#include "handler.h" +#include "config.h" +#include "helpers.h" +#include "client.h" + +#include +#include +#include +#include +#include +#include + +static uv_loop_t lop; +uv_loop_t *loop = &lop; + +static void on_close(uv_handle_t *handle); + +static ac_connection_parameters_t params = { + .sock = NULL, + .version = 0, + .port = 25575, + .host = NULL, + .id = NULL, + .token = NULL, + .on_send = NULL, +}; + +static void cleanup(void) { + LOGD("cleanup()"); + /* sock must be closed before cleanup(). */ + uv_loop_close(loop); + ac_free(); + uv_tty_reset_mode(); +} + +static void on_stdin_closed(void) { + sock_ext(true); +} + +static void on_sock_closed(void) { + uv_stop(loop); +} + +static int on_input(ac_request_t *req) { + /* The socket will close itself upon errors. So do the input stream. */ + return sock_request(req); +} + +static int on_sock_ready(void) { + return h_stdin(on_input, on_stdin_closed); +} + +static void on_resolv(int status, const struct addrinfo *ai, void (*on_connected)(bool)) { + if (status) { + uv_stop(loop); + return; + } + + if (h_socket(¶ms, + ai, + on_connected, + on_sock_ready, + handle_object, + on_sock_closed)) { + /* Mark as true to clean up resources and prevent next retry. */ + on_connected(true); + uv_stop(loop); + } +} + +int main(int argc, const char **argv) { + int r; + + if ((r = config_parse(argc, argv, ¶ms))) return r; + atexit(cleanup); + libac_config_t config = { +#ifdef DEBUG + .out = NULL, +#else + .out = NULL, +#endif + .tok = NULL + }; + if ((r = ac_init(&config))) errx(r, "Cannot initialize Acron library."); + + if ((r = uv_loop_init(loop))) goto uviniterr; + if ((r = h_signal())) goto uviniterr; + if ((r = a_dns(params.host, params.port, on_resolv))) goto uviniterr; + + if ((r = uv_run(loop, UV_RUN_DEFAULT))) { + if (r == 1) { + /* Seems to return 1 if uv_stop is called. */ + return 0; + } + errx(-r, "Cannot run: %s", uv_strerror(r)); + } + return 0; + uviniterr: + errx(-r, "Cannot initialize: %s", uv_strerror(r)); +} \ No newline at end of file diff --git a/client/helloworld/.gitignore b/client/helloworld/.gitignore new file mode 100644 index 0000000..6741ba2 --- /dev/null +++ b/client/helloworld/.gitignore @@ -0,0 +1,82 @@ +cmake-build-debug/ +cmake-build-release/ +json-c/ + +# https://raw.githubusercontent.com/github/gitignore/main/Global/JetBrains.gitignore +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser diff --git a/client/helloworld/CMakeLists.txt b/client/helloworld/CMakeLists.txt new file mode 100644 index 0000000..f36e020 --- /dev/null +++ b/client/helloworld/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.22) +project(libac C) + +set(CMAKE_C_STANDARD 11) + +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +add_definitions(-D_POSIX_C_SOURCE=200809L) +IF(CMAKE_BUILD_TYPE MATCHES Debug) + add_definitions(-DDEBUG) +ENDIF(CMAKE_BUILD_TYPE MATCHES Debug) + +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" OR "${CMAKE_C_COMPILER_ID}" STREQUAL "Clang") + set(CMAKE_C_FLAGS_DEBUG + "${CMAKE_C_FLAGS_DEBUG} -g3 -O0 -fsanitize=address") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG + "${CMAKE_EXE_LINKER_FLAGS_DEBUG} -fsanitize=address") + set(CMAKE_C_FLAGS + "${CMAKE_C_FLAGS} -fvisibility=hidden") +elseif ("${CMAKE_C_COMPILER_ID}" STREQUAL "MSVC") + # TODO: MSVC ASAN + set(CMAKE_C_FLAGS_DEBUG + "${CMAKE_C_FLAGS_DEBUG} /DEBUG /Z7 /Od") + set(CMAKE_EXE_LINKER_FLAGS_DEBUG + "${CMAKE_EXE_LINKER_FLAGS_DEBUG}") +endif() + +add_subdirectory(../libacron ${CMAKE_BINARY_DIR}/libac) + +# helloworld +if(WIN32) + set(APPS_DEPS ws2_32) +endif() +set(APPS_DEPS ${APPS_DEPS} ac) +add_executable(helloworld + main.c + net.c + net.h + ) +target_link_libraries(helloworld ${APPS_DEPS}) + +install(TARGETS helloworld + EXPORT ${PROJECT_NAME}-targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} + INCLUDES DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_INCLUDEDIR}/libac +) diff --git a/client/helloworld/main.c b/client/helloworld/main.c new file mode 100644 index 0000000..c112fcb --- /dev/null +++ b/client/helloworld/main.c @@ -0,0 +1,370 @@ +/* + * Created by yuuta on 7/20/22. + */ + +#include "libac.h" +#include "net.h" + +#include + +#ifndef WIN32 + +#include +#include +#include + +#endif + +#ifdef __STDC_NO_THREADS__ +# ifdef WIN32 +#define THREAD_WIN32 +#include +static HANDLE mtx_conn; + +int lock(void) { \ +DWORD _dwlck = WaitForSingleObject(mtx_conn, 0); \ +if (_dwlck == WAIT_FAILED) { _dwlck = GetLastError(); } \ +if (_dwlck) { fprintf(stderr, "Cannot lock the mutex: %d.\n", _dwlck); return _dwlck; } \ +return 0; \ +} + +int unlock(void) { \ +if (!ReleaseMutex(mtx_conn)) { \ +int r = GetLastError(); \ +fprintf(stderr, "Cannot release the mutex: %d.\n", r); \ +return r; \ +} \ +return 0; \ +} + +# else +#error "Either C11 threading or Win32 API is required for concurrency." +# endif +#else + +#define THREAD_C11 + +#include + +static mtx_t mtx_conn; + +int lock(void) { + \ +if (mtx_lock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot lock the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +int unlock(void) { + \ +if (mtx_unlock(&mtx_conn) != thrd_success) { + \ +fprintf(stderr, "Cannot release the mutex.\n"); \ +return 1; \ + + } \ +return 0; \ + +} + +#endif + +static const char *world_name(const enum ac_world world) { + switch (world) { + case overworld: + return "overworld"; + case nether: + return "nether"; + case end: + return "end"; + default: + return "unknown world"; + } +} + +static void handle_event(const ac_event_t *event) { + switch (event->type) { + case AC_EVENT_LAGGING: { + ac_event_lagging_t *o = (ac_event_lagging_t *) event; + printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", + o->ms, + o->ticks); + break; + } + case AC_EVENT_ENTITY_DEATH: { + ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; + printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", + o->entity.name, + world_name(o->entity.world), + o->entity.pos.x, + o->entity.pos.y, + o->entity.pos.z, + o->message); + break; + } + case AC_EVENT_PLAYER_MESSAGE: { + ac_event_player_message_t *o = (ac_event_player_message_t *) event; + printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->text); + break; + } + case AC_EVENT_PLAYER_DISCONNECT: { + ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; + printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z, + o->reason); + break; + } + case AC_EVENT_PLAYER_JOIN: { + ac_event_player_join_t *o = (ac_event_player_join_t *) event; + printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", + o->player.name, + world_name(o->player.world), + o->player.pos.x, + o->player.pos.y, + o->player.pos.z); + break; + } + default: { + printf("Received an unrecognized event of type '%u'.\n", + event->type); + } + } +} + +static void handle_response(const ac_response_t *response) { + switch (response->type) { + case AC_RESPONSE_OK: { + ac_response_ok_t *o = (ac_response_ok_t *) response; + printf("Request %d OK.\n", + o->id); + break; + } + case AC_RESPONSE_ERROR: { + ac_response_error_t *o = (ac_response_error_t *) response; + printf("Request %d failed: %s (%d).\n", + o->id, + o->message, + o->code); + break; + } + case AC_RESPONSE_CMD_OUT: { + ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; + printf("Request %d output by %s: %s.\n", + o->id, + o->sender, + o->out); + break; + } + case AC_RESPONSE_CMD_RESULT: { + ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; + printf("Request %d is done: %s (%d).\n", + o->id, + o->success ? "Success" : "Failed", + o->result); + break; + } + default: { + printf("Received an unrecognized response of type '%u'.\n", + response->type); + } + } +} + +#ifndef WIN32 + +static void intr(int signum) {} + +#endif + +static int on_send(const void *sock, + const void *buffer, + const size_t len) { + int r; + printf("Writing %lu bytes.\n", len); + if ((r = net_send(sock, buffer, len))) { + fprintf(stderr, "Cannot write %lu bytes to server: %s (%d).\n", + len, + net_strerror(r), + r); + } + return r; +} + +static int say(void *connection) { + int r; + ac_config_t conf = { + .name = "helloworld" + }; + ac_request_cmd_t req = { + .type = AC_REQUEST_CMD, + .id = 100, + .config = &conf, + .cmd = "say Hi" + }; + if ((r = ac_request(connection, (ac_request_t *) &req))) { + return r; + } + return 0; +} + +int main(int argc, char **argv) { + int r; + if ((r = net_init())) { + return r; + } +#if defined(THREAD_WIN32) + if (!(mtx_conn = CreateMutex(NULL, TRUE, NULL))) { + r = GetLastError(); + fprintf(stderr, "Cannot create mutex: %d.\n", r); + return r; + } +#elif defined(THREAD_C11) + if (mtx_init(&mtx_conn, mtx_plain) != thrd_success) { + fprintf(stderr, "Cannot create mutex.\n"); + return 1; + } +#endif + +#ifdef WIN32 + fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); +#else + struct sigaction sa; + sa.sa_handler = intr; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGINT, &sa, NULL) || + sigaction(SIGTERM, &sa, NULL)) { + const int e = errno; + fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", + strerror(e), + e); + return e; + } +#endif + libac_config_t config = { +#ifdef DEBUG + .out = stderr, +#else + .out = NULL, +#endif + .tok = NULL + }; + if ((r = ac_init(&config))) return r; + void *connection; + SOCKET sock; + ac_connection_parameters_t parameters = { + .host = "localhost", + .port = 25575, + .id = "1", + .token = "123", + .version = 0, + .sock = NULL, + .on_send = on_send + }; + if ((r = net_connect(parameters.host, parameters.port, &sock))) { + fprintf(stderr, "Cannot connect to server: %s (%d).\n", + net_strerror(r), + r); + ac_free(); + net_free(); + return r; + } + parameters.sock = &sock; + if ((r = ac_connect(parameters, &connection))) { + net_close(&sock); + ac_free(); + net_free(); + return r; + } + ac_obj_t *obj; + int nr; + uint8_t buffer[1000U]; + size_t bytes; + enum ac_connection_state state; + printf("Waiting until the connection is established.\n"); + bool ready = false; + while (1) { + if ((nr = net_read(&sock, buffer, sizeof(buffer), &bytes, 10))) { + if (nr == NET_TIMEOUT) { + printf("Receive timeout.\n"); + continue; + } else { + goto end; + } + } + if ((r = lock())) { goto end; } + if ((r = ac_receive(connection, buffer, bytes, &obj))) { + if ((r = unlock())) { goto end; } + goto end; + } + if (!ready) { + /* Wait until ready. */ + if ((r = ac_get_state(connection, &state))) { + if ((r = unlock())) { goto end; } + goto end; + } + switch (state) { + case AC_STATE_INIT: + continue; + case AC_STATE_READY: + printf("Connection is established.\n"); + ready = true; + say(connection); + if ((r = unlock())) { goto end; } + continue; + default: + fprintf(stderr, "Unexpected state.\n"); + if ((r = unlock())) { goto end; } + goto end; + } + } + if ((r = unlock())) { goto end; } + + if (!obj) { + continue; + } + if (AC_IS_EVENT(obj->type)) { + handle_event((ac_event_t *) obj); + } else if (AC_IS_RESPONSE(obj->type)) { + handle_response((ac_response_t *) obj); + } + ac_object_free(obj); + } + end: + if (nr) { + if (nr == NET_CLOSED) { + fprintf(stderr, "Connection is closed while reading from the server.\n"); + } else { + fprintf(stderr, "Cannot receive from server: %s (%d).\n", + net_strerror(nr), + nr); + } + } + if ((r = lock())) { goto end; } + ac_disconnect(connection, false); + if ((r = unlock())) { goto end; } + net_close(&sock); + ac_free(); + net_free(); +#if defined(THREAD_WIN32) + CloseHandle(mtx_conn); +#elif defined(THREAD_C11) + mtx_destroy(&mtx_conn); +#endif + return r; +} \ No newline at end of file diff --git a/client/helloworld/net.c b/client/helloworld/net.c new file mode 100644 index 0000000..83192e0 --- /dev/null +++ b/client/helloworld/net.c @@ -0,0 +1,181 @@ +/* + * Created by yuuta on 7/22/22. + * + * A simple cross-platform (Unix and Windows) implementation of a socket client. + */ + +#include "net.h" + +#include +#include +#include +#include + +#ifdef WIN32 +#include /* For DWORD */ +#include +#include + +#define errno_sock WSAGetLastError() + +#else + +#include +#include +#include +#include + +#define closesocket(socket) close(socket) +#define errno_sock errno + +#endif + +int net_connect(const char *host, + const uint16_t port, + SOCKET *sock) { + int r; + struct addrinfo *res; + char service[6]; + + snprintf(service, 6, "%u", port); + if ((r = getaddrinfo(host, service, NULL, &res))) { + return r; + } + + SOCKET fd; + if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { + const int e = errno_sock; + freeaddrinfo(res); + return e; + } + if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { + const int e = errno_sock; + closesocket(fd); + freeaddrinfo(res); + return e; + } + freeaddrinfo(res); + *sock = fd; + return 0; +} + +int net_read(const SOCKET *sock, + void *buffer, + const size_t length, + size_t *read, + const unsigned int timeout) { + +#ifdef WIN32 + DWORD to = timeout * 1000; + if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { +#else + struct timeval tv; + tv.tv_sec = timeout; + tv.tv_usec = 0; + if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { +#endif + return errno_sock; + } + + int bytes; + if ((bytes = (int) recv(*sock, buffer, length, 0)) <= 0) { + if (bytes < 0) { + const int e = errno_sock; +#ifdef WIN32 + if (e == WSAETIMEDOUT) { +#else + if (e == EAGAIN) { +#endif + return NET_TIMEOUT; + } + return e; + } + return NET_CLOSED; + } + *read = bytes; + return 0; +} + +int net_send(const SOCKET *sock, + const void *buffer, + size_t len) { + size_t pos; + const uint8_t *ptr = buffer; + int retval; + + for (pos = 0U; pos < len; pos += retval) { + size_t n = len - pos; + retval = (int) send(*sock, &ptr[pos], (int) n, 0); + if (retval <= 0) { + return errno_sock; + } + } + return 0; +} + +int net_init(void) { +#ifdef WIN32 + int r; + WORD wVersionRequested; + WSADATA wsaData; + wVersionRequested = MAKEWORD(2, 2); + if ((r = WSAStartup(wVersionRequested, &wsaData))) { + fprintf(stderr, "WSAStartup failed with error: %d\n", r); + return r; + } + if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { + fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); + WSACleanup(); + return 1; + } +#endif + return 0; +} + +void net_close(const SOCKET *sock) { + closesocket(*sock); +} + +#ifdef WIN32 +static __declspec( thread ) LPTSTR err_buf = NULL; +#else +static _Thread_local char err_buf[1024]; +#endif + +void net_free(void) { +#ifdef WIN32 + WSACleanup(); + if (err_buf) { + LocalFree(err_buf); + err_buf = NULL; + } +#endif +} + +char *net_strerror(int errnum) { +#ifdef WIN32 + if (err_buf) { + LocalFree(err_buf); + err_buf = NULL; + } + FormatMessage( + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + errnum, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + (LPTSTR) &err_buf, + 0, + NULL); + if (!err_buf) { + return "Unknown error"; + } +#else + int r; + if ((r = strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) { + snprintf(err_buf, 1024, "%d (%d)", errnum, r); + } +#endif + return err_buf; +} \ No newline at end of file diff --git a/client/helloworld/net.h b/client/helloworld/net.h new file mode 100644 index 0000000..1a49709 --- /dev/null +++ b/client/helloworld/net.h @@ -0,0 +1,45 @@ +/* + * Created by yuuta on 7/23/22. + */ + +#ifndef ACRONC_NET_H +#define ACRONC_NET_H + +#include +#include + +#ifdef WIN32 +#include + +#else + +#define SOCKET int + +#endif + +#define NET_CLOSED -2 +#define NET_TIMEOUT -1 + +int net_connect(const char *host, + uint16_t port, + SOCKET *sock); + +int net_read(const SOCKET *sock, + void *buffer, + size_t length, + size_t *read, + unsigned int timeout); + +int net_send(const SOCKET *sock, + const void *buffer, + size_t len); + +void net_close(const SOCKET *sock); + +int net_init(void); + +void net_free(void); + +char *net_strerror(int errnum); + +#endif /* ACRONC_NET_H */ diff --git a/client/libacron/CMakeLists.txt b/client/libacron/CMakeLists.txt index 57ce521..57da4af 100644 --- a/client/libacron/CMakeLists.txt +++ b/client/libacron/CMakeLists.txt @@ -89,32 +89,7 @@ target_link_libraries(ac ${LIBAC_DEPS}) target_include_directories(ac-static PUBLIC ${LIBAC_INCLUDES}) target_link_libraries(ac-static ${LIBAC_DEPS}) -# Apps -if(WIN32) - set(APPS_DEPS ws2_32) -endif() -set(APPS_DEPS ${APPS_DEPS} ac) - -# apps/helloworld -add_executable(helloworld - apps/helloworld/main.c - apps/helloworld/net.c - apps/helloworld/net.h - ) -target_link_libraries(helloworld ${APPS_DEPS}) -target_include_directories(helloworld PUBLIC "${PROJECT_BINARY_DIR}" include/) - -# apps/acronc -add_executable(acronc - apps/acronc/main.c - apps/acronc/common.h - apps/acronc/log.h - apps/acronc/log.c - apps/acronc/handler.h apps/acronc/handler_signal.c apps/acronc/config.c apps/acronc/config.h apps/acronc/client.c apps/acronc/client.h apps/acronc/handler_stdin.c apps/acronc/helpers.c apps/acronc/helpers.h apps/acronc/handler_socket.c apps/acronc/async_dns.c) -target_link_libraries(acronc ${APPS_DEPS} uv) -target_include_directories(acronc PUBLIC "${PROJECT_BINARY_DIR}" include/) - -install(TARGETS ac ac-static helloworld acronc +install(TARGETS ac ac-static EXPORT ${PROJECT_NAME}-targets RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} diff --git a/client/libacron/README.md b/client/libacron/README.md index 418e07b..4514e21 100644 --- a/client/libacron/README.md +++ b/client/libacron/README.md @@ -3,9 +3,6 @@ A client library written in C, based on [json-c](https://github.com/json-c/json-c) and [wic](https://github.com/cjhdev/wic). -This document is for client developers. For users who want a remote console to their Minecraft -server, they should consult [acronc(1)](apps/acronc), a ready-to-use Acron cli. - ## Building Requirements: diff --git a/client/libacron/apps/acronc/README.md b/client/libacron/apps/acronc/README.md deleted file mode 100644 index 87c9db0..0000000 --- a/client/libacron/apps/acronc/README.md +++ /dev/null @@ -1,11 +0,0 @@ -# acronc(1) - -Acron client using libac and libuv, written in C. - -Current status: In development. - -Windows is currently not supported yet. - -## License - -GPL-2.0-only \ No newline at end of file diff --git a/client/libacron/apps/acronc/async_dns.c b/client/libacron/apps/acronc/async_dns.c deleted file mode 100644 index 2e221cf..0000000 --- a/client/libacron/apps/acronc/async_dns.c +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "handler.h" -#include "log.h" - -#include - -static uv_getaddrinfo_t resolv; - -static struct addrinfo *ai = NULL; -static struct addrinfo *ai_current = NULL; -static void (*cb)(int status, - const struct addrinfo *, - void (*on_connect_result)(bool)) = NULL; - -static void on_conn(bool res) { - if (res) { - uv_freeaddrinfo(ai); - ai = NULL; - ai_current = NULL; - cb = NULL; - return; - } - ai_current = ai_current->ai_next; - if (!ai_current) { - /* No more result available. */ - cb(1, NULL, NULL); - uv_freeaddrinfo(ai); - ai = NULL; - ai_current = NULL; - cb = NULL; - return; - } - cb(0, ai_current, &on_conn); -} - -static void on_resolv(uv_getaddrinfo_t *req, int status, struct addrinfo *res) { - LOGDV("on_resolv(req = %p): status = %d, res = %p", - req, - status, - res); - if (status) { - LOGEV("Cannot resolve host: %s", uv_strerror(status)); - cb(status, NULL, NULL); - return; - } - ai = res; - ai_current = res; - cb(0, ai_current, &on_conn); -} - -int a_dns(const char *host, - uint16_t port, - void (*c)(int status, - const struct addrinfo *, - void (*on_connect_result)(bool))) { - int r; - char service[6]; - snprintf(service, 6, "%u", port); - cb = c; - if ((r = uv_getaddrinfo(loop, &resolv, on_resolv, host, service, NULL))) return r; - return 0; -} diff --git a/client/libacron/apps/acronc/client.c b/client/libacron/apps/acronc/client.c deleted file mode 100644 index 5553d4c..0000000 --- a/client/libacron/apps/acronc/client.c +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "client.h" -#include "helpers.h" - -#include -#include - -static void handle_event(const ac_event_t *event) { - switch (event->type) { - case AC_EVENT_LAGGING: { - ac_event_lagging_t *o = (ac_event_lagging_t *) event; - printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", - o->ms, - o->ticks); - break; - } - case AC_EVENT_ENTITY_DEATH: { - ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; - printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", - o->entity.name, - world_name(o->entity.world), - o->entity.pos.x, - o->entity.pos.y, - o->entity.pos.z, - o->message); - break; - } - case AC_EVENT_PLAYER_MESSAGE: { - ac_event_player_message_t *o = (ac_event_player_message_t *) event; - printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z, - o->text); - break; - } - case AC_EVENT_PLAYER_DISCONNECT: { - ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; - printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z, - o->reason); - break; - } - case AC_EVENT_PLAYER_JOIN: { - ac_event_player_join_t *o = (ac_event_player_join_t *) event; - printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z); - break; - } - default: { - printf("Received an unrecognized event of type '%u'.\n", - event->type); - } - } -} - -static void handle_response(const ac_response_t *response) { - switch (response->type) { - case AC_RESPONSE_OK: { - ac_response_ok_t *o = (ac_response_ok_t *) response; - printf("Request %d OK.\n", - o->id); - break; - } - case AC_RESPONSE_ERROR: { - ac_response_error_t *o = (ac_response_error_t *) response; - printf("Request %d failed: %s (%d).\n", - o->id, - o->message, - o->code); - break; - } - case AC_RESPONSE_CMD_OUT: { - ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; - printf("Request %d output by %s: %s.\n", - o->id, - o->sender, - o->out); - break; - } - case AC_RESPONSE_CMD_RESULT: { - ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; - printf("Request %d is done: %s (%d).\n", - o->id, - o->success ? "Success" : "Failed", - o->result); - break; - } - default: { - printf("Received an unrecognized response of type '%u'.\n", - response->type); - } - } -} - -int handle_object(ac_obj_t *obj) { - if (AC_IS_RESPONSE(obj->type)) handle_response((ac_response_t *) obj); - else handle_event((ac_event_t *) obj); - ac_object_free(obj); - return 0; -} \ No newline at end of file diff --git a/client/libacron/apps/acronc/client.h b/client/libacron/apps/acronc/client.h deleted file mode 100644 index 438d7fd..0000000 --- a/client/libacron/apps/acronc/client.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#ifndef ACRONC_CLIENT_H -#define ACRONC_CLIENT_H - -#include - -int handle_object(ac_obj_t *obj); - -#endif /* ACRONC_CLIENT_H */ diff --git a/client/libacron/apps/acronc/common.h b/client/libacron/apps/acronc/common.h deleted file mode 100644 index 0ca3b92..0000000 --- a/client/libacron/apps/acronc/common.h +++ /dev/null @@ -1,8 +0,0 @@ -/* - * Created by yuuta on 7/23/22. - */ - -#ifndef ACRONC_COMMON_H -#define ACRONC_COMMON_H - -#endif /* ACRONC_COMMON_H */ diff --git a/client/libacron/apps/acronc/config.c b/client/libacron/apps/acronc/config.c deleted file mode 100644 index 046d931..0000000 --- a/client/libacron/apps/acronc/config.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -int config_parse(const int argc, const char **argv, ac_connection_parameters_t *params) { - if (argc <= 1) { - fprintf(stderr, "Usage: %s [-t token] [-p port] @\n" - "Please refer to acronc(1) for more details.\n", - argv[0] ? "acronc" : argv[0]); - return 64; - } - bool svr_set = false; - for (int i = 1; i < argc; i++) { - const char *arg = argv[i]; - if (arg[0] != '-') { - if (svr_set) { - fprintf(stderr, "Unexpected value: %s\n", arg); - return 64; - } - const char *id = strtok((char *) arg, "@"); - if (!id) { - fprintf(stderr, "Malformed argument: should be ID@server.\n"); - return 64; - } - const char *server = strtok(NULL, ""); - if (!server) { - fprintf(stderr, "Malformed argument: should be ID@server.\n"); - return 64; - } - params->host = (char *) server; - params->id = (char *) id; - svr_set = true; - continue; - } - char a = arg[1]; - char **value_ptr; - char *port_str = NULL; - switch (a) { - case 't': { - value_ptr = ¶ms->token; - goto read_value; - } - case 'p': { - value_ptr = &port_str; - goto read_value; - } - default: { - fprintf(stderr, "Unexpected switch -%c.\n", a); - return 64; - } - } - read_value: - { - if (i == (argc - 1)) { - fprintf(stderr, "-%c requires an argument.\n", a); - return 64; - } - *value_ptr = (char *) argv[++i]; - } - if (port_str) { - errno = 0; - char *endptr; - unsigned long val = strtol(port_str, &endptr, 0); - if (errno) { - const int e = errno; - fprintf(stderr, "Cannot parse port: %s", strerror(e)); - return e; - } - if (endptr == port_str) { - fprintf(stderr, "Illegal port.\n"); - return 64; - } - if (val > UINT16_MAX) { - fprintf(stderr, "Illegal port.\n"); - return 64; - } - params->port = (uint16_t) val; - port_str = NULL; - } - } - if (!params->host || !params->id) { - fprintf(stderr, "Usage: acronc ID@server[:port]\n"); - return 64; - } - char tok[1024]; - if (!params->token) { - printf("Token: "); - scanf("%1023[^\n]", tok); - params->token = tok; - } - return 0; -} \ No newline at end of file diff --git a/client/libacron/apps/acronc/config.h b/client/libacron/apps/acronc/config.h deleted file mode 100644 index ee51f34..0000000 --- a/client/libacron/apps/acronc/config.h +++ /dev/null @@ -1,12 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#ifndef ACRONC_CONFIG_H -#define ACRONC_CONFIG_H - -#include - -int config_parse(int argc, const char **argv, ac_connection_parameters_t *params); - -#endif /* ACRONC_CONFIG_H */ diff --git a/client/libacron/apps/acronc/handler.h b/client/libacron/apps/acronc/handler.h deleted file mode 100644 index 1a2ccfe..0000000 --- a/client/libacron/apps/acronc/handler.h +++ /dev/null @@ -1,55 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#ifndef ACRONC_HANDLER_H -#define ACRONC_HANDLER_H - -#include -#include - -enum exit_reason { - /* Exit due to a signal. Socket is working. */ - EXIT_SIGNAL, - /* Exit due to an EOF from stdin. Socket is working. */ - EXIT_STDIN_EOF, - /* Exit due to an EOF from socket. Socket is closed. */ - EXIT_SOCKET_EOF, - /* Exit due to a socket failure. Socket is closed. */ - EXIT_FAILURE_SOCKET, - /* Exit due to a failure. Socket is working. */ - EXIT_FAILURE -}; - -struct uv_obj { - void *obj; - bool running; -}; - -extern uv_loop_t *loop; - -extern void (*on_exit)(enum exit_reason reason); - -int h_signal(void); - -int h_stdin(int (*on_input)(ac_request_t *req), - void (*on_close)(void)); - -int h_socket(ac_connection_parameters_t *p, - const struct addrinfo *ai, - void (*on_connect_result)(bool), - int (*on_ready)(void), - int (*on_received)(ac_obj_t *obj), - void (*on_closed)(void)); - -int sock_request(ac_request_t *req); - -int sock_ext(bool trigger_callback); - -int a_dns(const char *host, - uint16_t port, - void (*on_resolv)(int status, - const struct addrinfo *, - void (*on_connect_result)(bool))); - -#endif /* ACRONC_HANDLER_H */ diff --git a/client/libacron/apps/acronc/handler_signal.c b/client/libacron/apps/acronc/handler_signal.c deleted file mode 100644 index 1d0ba7e..0000000 --- a/client/libacron/apps/acronc/handler_signal.c +++ /dev/null @@ -1,40 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "handler.h" -#include "log.h" - -static uv_signal_t sigint; -static struct uv_obj obj_sigint = { - .obj = &sigint, - .running = false -}; -static uv_signal_t sigterm; -static struct uv_obj obj_sigterm = { - .obj = &sigterm, - .running = false -}; - -static void on_signal(uv_signal_t *handle, int signum) { - LOGDV("on_signal(handle = %p): %d", - handle, - signum); - uv_signal_stop(&sigint); - obj_sigint.running = false; - uv_signal_stop(&sigterm); - obj_sigterm.running = false; - on_exit(EXIT_SIGNAL); -} - -int h_signal(void) { - int r; - if ((r = uv_signal_init(loop, &sigint))) return r; - if ((r = uv_signal_start(&sigint, on_signal, SIGINT))) return r; - - obj_sigint.running = true; - if ((r = uv_signal_init(loop, &sigterm))) return r; - if ((r = uv_signal_start(&sigterm, on_signal, SIGTERM))) return r; - obj_sigterm.running = true; - return 0; -} diff --git a/client/libacron/apps/acronc/handler_socket.c b/client/libacron/apps/acronc/handler_socket.c deleted file mode 100644 index ff8eefb..0000000 --- a/client/libacron/apps/acronc/handler_socket.c +++ /dev/null @@ -1,255 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "handler.h" -#include "log.h" -#include "helpers.h" - -#include -#include -#include - -static void (*cb_conn)(bool); - -static int (*cb_ready)(void); - -static int (*cb_recv)(ac_obj_t *obj); - -static void (*cb_close)(void); - -static ac_connection_parameters_t *params; - -static uv_tcp_t sock; -static uv_write_t write; - -static uv_connect_t conn; - -static bool ready = false; -static void *ac_conn = NULL; - -#define RUNNING !uv_is_closing((uv_handle_t *) &sock) - -static void on_close(uv_handle_t *handle) { - LOGDV("on_close(handle = %p)", - handle); - if (handle->data) - cb_close(); -} - -static void ex2(bool force, bool cb) { - LOGDV("Exiting socket handlers: Force WebSocket close: %s; Socket still running: %s.", - force ? "true" : "false", - RUNNING ? "true" : "false"); - if (ac_conn) { - ac_disconnect(ac_conn, - RUNNING ? force : true /* If sock is not running, always force */); - ac_conn = NULL; - } - if (RUNNING) { - sock.data = cb ? &ex2 /* Any non-NULL value */ : NULL; - uv_close((uv_handle_t *) &sock, on_close); - } else { - if (cb) { - cb_close(); - } - } -} - -static void ex(bool force) { - ex2(force, true); -} - -int sock_ext(bool trigger_callback) { - ex2(false, trigger_callback); - return 0; -} - -static void on_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) { - LOGDV("on_alloc(handle = %p): size = %lu", - handle, - size); - void *b = malloc(size); - if (!b) { - LOGEV("Cannot allocate memory of %u bytes: %s.", - size, - strerror(errno)); - /* Socket is still working now. */ - ex(false); - *buf = uv_buf_init(NULL, 0); /* Just in case? */ - return; - } - *buf = uv_buf_init(b, size); -} - -static void on_read(uv_stream_t *tcp, ssize_t nread, const uv_buf_t *buf) { - LOGDV("on_read(stream = %p): nread = %ld, buf = %p, buf->base = %p, buf->len = %lu", - tcp, - nread, - buf, buf->base, buf->len); - if (!nread) { - /* EAGAIN */ - if (buf->base) free(buf->base); - return; - } - if (nread == UV_EOF) { - LOGW("Received EOF from server."); - free(buf->base); - /* The socket * should * be closed already? */ - ex(true); - return; - } - if (nread < 0) { - LOGEV("Encountered a failure while reading from the socket: ", uv_strerror(nread)); - if (buf->base) free(buf->base); - /* Docs: The callee is responsible for stopping/closing the stream when an error happens */ - ex(true); /* Force close libac connection here. */ - return; - } - int r; - ac_obj_t *obj = NULL; - if ((r = ac_receive(ac_conn, buf->base, nread, &obj))) { - LOGEV("Cannot parse the response (%d).", r); - /* libac error. Socket is working. */ - ex(false); - return; - } - - if (!ready) { - enum ac_connection_state state; - if ((r = ac_get_state(ac_conn, &state))) { - LOGEV("Cannot get state (%d).", r); - /* libac error. Socket is working. */ - ex(false); - return; - } - if (state == AC_STATE_READY) { - ready = true; - if ((cb_ready())) { - /* acronc error. Socket is working. */ - ex(false); - return; - } - } - } - if (obj) { - LOGDV("Got object: %p", obj); - /* uv_async_send is unreliable, and missed messages will cause memory leak. */ - if (cb_recv(obj)) { - /* acronc error. Socket is working. */ - ex(false); - } - } - - free(buf->base); -} - -static void on_write(uv_write_t *req, int status) { - LOGDV("on_write(req = %p): %d", - req, - status); - if (status) { - LOGEV("Cannot write to socket: %s", uv_strerror(status)); - /* Socket may be closed? Anyway writing again will definitely be problematic, but closing may not. */ - ex(true); - } -} - -static int on_send(const void *s, - const void *buf, - const size_t len) { - LOGDV("on_send(s = %p): len = %u", - s, - len); - const uv_stream_t *stream = s; - uv_buf_t buffer[] = { - {.base = (char *) buf, .len = len} - }; - int r; - if ((r = uv_write(&write, (uv_stream_t *) stream, buffer, 1, on_write))) { - LOGEV("Cannot write to socket: %s", uv_strerror(r)); - /* Socket may be closed? Anyway writing again will definitely be problematic, but closing may not. */ - ex(true); - } - return 0; -} - -static void on_retry_closed(uv_handle_t *handle) { - cb_conn(false); -} - -static void on_connect(uv_connect_t *req, int status) { - LOGDV("on_connect(req = %p): status = %d", - req, - status); - if (status) { - LOGEV("Cannot connect to the server: %s", uv_strerror(status)); - /* Close it for the next retry. */ - if (RUNNING) { - LOGD("Closing socket before retry."); - uv_close((uv_handle_t *) &sock, on_retry_closed); - } else { - cb_conn(false); - } - return; - } - LOGI("Connected."); - uv_stream_t *stream = req->handle; - int r; - params->sock = stream; - if ((r = ac_connect(*params, &ac_conn))) { - LOGEV("Cannot initialize connection: %d.", r); - cb_conn(false); - return; - } - if ((r = uv_read_start(stream, on_alloc, on_read))) { - LOGEV("Cannot read socket: %s", uv_strerror(r)); - cb_conn(false); - return; - } - cb_conn(true); -} - -int h_socket(ac_connection_parameters_t *p, - const struct addrinfo *ai, - void (*on_connect_result)(bool), - int (*on_ready)(void), - int (*on_received)(ac_obj_t *obj), - void (*on_closed)(void)) { - cb_conn = on_connect_result; - cb_ready = on_ready; - cb_recv = on_received; - cb_close = on_closed; - params = p; - params->on_send = on_send; - - struct sockaddr *sa = ai->ai_addr; - LOGIV("Connecting to %s...", ntop(sa)); - int r; - if ((r = uv_tcp_init(loop, &sock))) { - LOGEV("Cannot initialize the socket: %s", uv_strerror(r)); - return r; - } - if ((r = uv_tcp_connect(&conn, &sock, sa, on_connect))) { - LOGEV("Cannot initialize the connection: %s", uv_strerror(r)); - return r; - } - return 0; -} - -int sock_request(ac_request_t *req) { - if (!ac_conn) { - /* Just in case. */ - LOGE("sock_request() called on a closed socket."); - /* Ignore it. */ - return 0; - } - int r = ac_request(ac_conn, req); - if (r) { - LOGEV("Cannot send request: %d.", r); - /* libac error. Socket is working. */ - ex(false); - } - return r; -} - diff --git a/client/libacron/apps/acronc/handler_stdin.c b/client/libacron/apps/acronc/handler_stdin.c deleted file mode 100644 index 3ac307d..0000000 --- a/client/libacron/apps/acronc/handler_stdin.c +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "handler.h" -#include "log.h" - -#include -#include - -static unsigned int id = 0; - -static uv_tty_t tty; - -static int (*cb_recv)(ac_request_t *req); - -static void (*cb_close)(void); - -static void on_close(uv_handle_t *handle) { - cb_close(); -} - -static void on_alloc(uv_handle_t *handle, size_t size, uv_buf_t *buf) { - LOGDV("on_alloc(handle = %p): size = %lu", - handle, - size); - void *b = malloc(size); - if (!b) { - LOGEV("Cannot allocate memory of %u bytes: %s.", - size, - strerror(errno)); - /* Stream is still working now. */ - uv_close(handle, on_close); - *buf = uv_buf_init(NULL, 0); /* Just in case? */ - return; - } - *buf = uv_buf_init(b, size); -} - -static void on_stdin(uv_stream_t *t, ssize_t nread, const uv_buf_t *buf) { - LOGDV("on_stdin(stream = %p): nread = %ld, buf = %p, buf->base = %p, buf->len = %lu", - t, - nread, - buf, buf->base, buf->len); - if (!nread) { - if (buf->base) free(buf->base); - return; - } - if (nread == UV_EOF) { - LOGI("Exit."); - free(buf->base); - - cb_close(); - return; - } - if (nread < 0) { - LOGEV("Encountered a failure while reading stdin: ", uv_strerror(nread)); - if (buf->base) free(buf->base); - uv_close((uv_handle_t *) t, on_close); - return; - } - buf->base[nread - 1] = '\0'; /* Remove junk and tailing \n */ - if ((++ id) > INT_MAX) { - id = 0; - } - ac_request_cmd_t req = { - .type = AC_REQUEST_CMD, - .id = (int) id, - .cmd = buf->base - }; - if (cb_recv((ac_request_t *) &req)) { - uv_close((uv_handle_t *) t, on_close); - } - free(buf->base); -} - -int h_stdin(int (*on_input)(ac_request_t *req), - void (*on_close)(void)) { - cb_recv = on_input; - cb_close = on_close; - int r; - if ((r = uv_tty_init(loop, &tty, 0, 0))) return r; - if ((r = uv_read_start((uv_stream_t *) &tty, on_alloc, on_stdin))) return r; - return 0; -} \ No newline at end of file diff --git a/client/libacron/apps/acronc/helpers.c b/client/libacron/apps/acronc/helpers.c deleted file mode 100644 index 9e141d4..0000000 --- a/client/libacron/apps/acronc/helpers.c +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#include "helpers.h" - -#include -#include -#include - -#ifdef WIN32 -#include -#include -#include - -#define errno_sock WSAGetLastError() - -#else - -#include -#include - -#define errno_sock errno - -#endif - -const char *ntop(const struct sockaddr *sa) { - static char buf[INET6_ADDRSTRLEN]; - const char *retval; - switch (sa->sa_family) { - case AF_INET: { - retval = inet_ntop(AF_INET, &((struct sockaddr_in *) sa)->sin_addr, buf, sizeof(buf)); - break; - } - case AF_INET6: { - retval = inet_ntop(AF_INET6, &((struct sockaddr_in6 *) sa)->sin6_addr, buf, sizeof(buf)); - break; - } - default: { - return "Unknown address family"; - } - } - if (retval) return retval; - return strerror(errno_sock); -} - -const char *world_name(const enum ac_world world) { - switch (world) { - case overworld: - return "overworld"; - case nether: - return "nether"; - case end: - return "end"; - default: - return "unknown world"; - } -} diff --git a/client/libacron/apps/acronc/helpers.h b/client/libacron/apps/acronc/helpers.h deleted file mode 100644 index e983439..0000000 --- a/client/libacron/apps/acronc/helpers.h +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Created by yuuta on 7/24/22. - */ - -#ifndef ACRONC_HELPERS_H -#define ACRONC_HELPERS_H - -#include - -#ifdef WIN32 -#include -#include - -#else - -#include - -#endif - -const char *ntop(const struct sockaddr *sa); - -const char *world_name(enum ac_world world); - -#endif /* ACRONC_HELPERS_H */ diff --git a/client/libacron/apps/acronc/log.c b/client/libacron/apps/acronc/log.c deleted file mode 100644 index 08f48b6..0000000 --- a/client/libacron/apps/acronc/log.c +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Created by yuuta on 1/1/22. - */ - -#define _GNU_SOURCE - -#include "log.h" -#include "config.h" - -#include -#include -#include - -#ifdef __linux__ -#include -#include -#include -#endif - -void g_log(enum log_level level, - const char *file, - int line, - const char *format, - ...) { - FILE *stream = stderr; - switch (level) { - case log_fetal: - fprintf(stream, "F"); - break; - case log_error: - fprintf(stream, "E"); - break; - case log_warn: - fprintf(stream, "W"); - break; - case log_info: - fprintf(stream, "I"); - break; - case log_debug: -#ifdef DEBUG - fprintf(stream, "D"); - break; -#else - return; -#endif - default: - fprintf(stderr, "Unknown log level: %d.\n", level); - assert(0); - } - int tid = -1; -#ifdef __linux__ - tid = (int) syscall(__NR_gettid); -#endif - fprintf(stream, "[%d %s:%d]: ", - tid, file, line); - va_list list; - va_start(list, format); - vfprintf(stream, format, list); - va_end(list); - fprintf(stream, "\n"); -} diff --git a/client/libacron/apps/acronc/log.h b/client/libacron/apps/acronc/log.h deleted file mode 100644 index 7bc9532..0000000 --- a/client/libacron/apps/acronc/log.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Created by yuuta on 1/1/22. - */ - -#ifndef LOG_H -#define LOG_H - -enum log_level { - log_fetal = 1, - log_error = 2, - log_warn = 3, - log_info = 4, - log_debug = 5 -}; - -void g_log(enum log_level level, - const char *file, - int line, - const char *format, - ...); - -#define LOGF(X) g_log(log_fetal, __FUNCTION__, __LINE__, X) - -#define LOGFV(X, ...) g_log(log_fetal, __FUNCTION__, __LINE__, X, __VA_ARGS__) - -#define LOGE(X) g_log(log_error, __FUNCTION__, __LINE__, X) - -#define LOGEV(X, ...) g_log(log_error, __FUNCTION__, __LINE__, X, __VA_ARGS__) - -#define LOGW(X) g_log(log_warn, __FUNCTION__, __LINE__, X) - -#define LOGWV(X, ...) g_log(log_warn, __FUNCTION__, __LINE__, X, __VA_ARGS__) - -#define LOGI(X) g_log(log_info, __FUNCTION__, __LINE__, X) - -#define LOGIV(X, ...) g_log(log_info, __FUNCTION__, __LINE__, X, __VA_ARGS__) - -#ifdef DEBUG - -#define LOGD(X) g_log(log_debug, __FUNCTION__, __LINE__, X) - -#define LOGDV(X, ...) g_log(log_debug, __FUNCTION__, __LINE__, X, __VA_ARGS__) - -#else - -#define LOGD(X) - -#define LOGDV(X, ...) - -#endif - -#endif /* LOG_H */ diff --git a/client/libacron/apps/acronc/main.c b/client/libacron/apps/acronc/main.c deleted file mode 100644 index 6e18ed2..0000000 --- a/client/libacron/apps/acronc/main.c +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Created by yuuta on 7/23/22. - */ - -#include "log.h" -#include "handler.h" -#include "config.h" -#include "helpers.h" -#include "client.h" - -#include -#include -#include -#include -#include -#include - -static uv_loop_t lop; -uv_loop_t *loop = &lop; - -static void on_close(uv_handle_t *handle); - -static ac_connection_parameters_t params = { - .sock = NULL, - .version = 0, - .port = 25575, - .host = NULL, - .id = NULL, - .token = NULL, - .on_send = NULL, -}; - -static void cleanup(void) { - LOGD("cleanup()"); - /* sock must be closed before cleanup(). */ - uv_loop_close(loop); - ac_free(); - uv_tty_reset_mode(); -} - -static void on_stdin_closed(void) { - sock_ext(true); -} - -static void on_sock_closed(void) { - uv_stop(loop); -} - -static int on_input(ac_request_t *req) { - /* The socket will close itself upon errors. So do the input stream. */ - return sock_request(req); -} - -static int on_sock_ready(void) { - return h_stdin(on_input, on_stdin_closed); -} - -static void on_resolv(int status, const struct addrinfo *ai, void (*on_connected)(bool)) { - if (status) { - uv_stop(loop); - return; - } - - if (h_socket(¶ms, - ai, - on_connected, - on_sock_ready, - handle_object, - on_sock_closed)) { - /* Mark as true to clean up resources and prevent next retry. */ - on_connected(true); - uv_stop(loop); - } -} - -int main(int argc, const char **argv) { - int r; - - if ((r = config_parse(argc, argv, ¶ms))) return r; - atexit(cleanup); - libac_config_t config = { -#ifdef DEBUG - .out = NULL, -#else - .out = NULL, -#endif - .tok = NULL - }; - if ((r = ac_init(&config))) errx(r, "Cannot initialize Acron library."); - - if ((r = uv_loop_init(loop))) goto uviniterr; - if ((r = h_signal())) goto uviniterr; - if ((r = a_dns(params.host, params.port, on_resolv))) goto uviniterr; - - if ((r = uv_run(loop, UV_RUN_DEFAULT))) { - if (r == 1) { - /* Seems to return 1 if uv_stop is called. */ - return 0; - } - errx(-r, "Cannot run: %s", uv_strerror(r)); - } - return 0; - uviniterr: - errx(-r, "Cannot initialize: %s", uv_strerror(r)); -} \ No newline at end of file diff --git a/client/libacron/apps/helloworld/main.c b/client/libacron/apps/helloworld/main.c deleted file mode 100644 index c112fcb..0000000 --- a/client/libacron/apps/helloworld/main.c +++ /dev/null @@ -1,370 +0,0 @@ -/* - * Created by yuuta on 7/20/22. - */ - -#include "libac.h" -#include "net.h" - -#include - -#ifndef WIN32 - -#include -#include -#include - -#endif - -#ifdef __STDC_NO_THREADS__ -# ifdef WIN32 -#define THREAD_WIN32 -#include -static HANDLE mtx_conn; - -int lock(void) { \ -DWORD _dwlck = WaitForSingleObject(mtx_conn, 0); \ -if (_dwlck == WAIT_FAILED) { _dwlck = GetLastError(); } \ -if (_dwlck) { fprintf(stderr, "Cannot lock the mutex: %d.\n", _dwlck); return _dwlck; } \ -return 0; \ -} - -int unlock(void) { \ -if (!ReleaseMutex(mtx_conn)) { \ -int r = GetLastError(); \ -fprintf(stderr, "Cannot release the mutex: %d.\n", r); \ -return r; \ -} \ -return 0; \ -} - -# else -#error "Either C11 threading or Win32 API is required for concurrency." -# endif -#else - -#define THREAD_C11 - -#include - -static mtx_t mtx_conn; - -int lock(void) { - \ -if (mtx_lock(&mtx_conn) != thrd_success) { - \ -fprintf(stderr, "Cannot lock the mutex.\n"); \ -return 1; \ - - } \ -return 0; \ - -} - -int unlock(void) { - \ -if (mtx_unlock(&mtx_conn) != thrd_success) { - \ -fprintf(stderr, "Cannot release the mutex.\n"); \ -return 1; \ - - } \ -return 0; \ - -} - -#endif - -static const char *world_name(const enum ac_world world) { - switch (world) { - case overworld: - return "overworld"; - case nether: - return "nether"; - case end: - return "end"; - default: - return "unknown world"; - } -} - -static void handle_event(const ac_event_t *event) { - switch (event->type) { - case AC_EVENT_LAGGING: { - ac_event_lagging_t *o = (ac_event_lagging_t *) event; - printf("Server lagging: running %ld milliseconds (%ld ticks) behind.\n", - o->ms, - o->ticks); - break; - } - case AC_EVENT_ENTITY_DEATH: { - ac_event_entity_death_t *o = (ac_event_entity_death_t *) event; - printf("Entity '%s' died at %s(%.2f, %.2f, %.2f): %s.\n", - o->entity.name, - world_name(o->entity.world), - o->entity.pos.x, - o->entity.pos.y, - o->entity.pos.z, - o->message); - break; - } - case AC_EVENT_PLAYER_MESSAGE: { - ac_event_player_message_t *o = (ac_event_player_message_t *) event; - printf("Player '%s' said at %s(%.2f, %.2f, %.2f): %s.\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z, - o->text); - break; - } - case AC_EVENT_PLAYER_DISCONNECT: { - ac_event_player_disconnect_t *o = (ac_event_player_disconnect_t *) event; - printf("Player '%s' disconnected at %s(%.2f, %.2f, %.2f): %s.\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z, - o->reason); - break; - } - case AC_EVENT_PLAYER_JOIN: { - ac_event_player_join_t *o = (ac_event_player_join_t *) event; - printf("Player '%s' joined at %s(%.2f, %.2f, %.2f).\n", - o->player.name, - world_name(o->player.world), - o->player.pos.x, - o->player.pos.y, - o->player.pos.z); - break; - } - default: { - printf("Received an unrecognized event of type '%u'.\n", - event->type); - } - } -} - -static void handle_response(const ac_response_t *response) { - switch (response->type) { - case AC_RESPONSE_OK: { - ac_response_ok_t *o = (ac_response_ok_t *) response; - printf("Request %d OK.\n", - o->id); - break; - } - case AC_RESPONSE_ERROR: { - ac_response_error_t *o = (ac_response_error_t *) response; - printf("Request %d failed: %s (%d).\n", - o->id, - o->message, - o->code); - break; - } - case AC_RESPONSE_CMD_OUT: { - ac_response_cmd_out_t *o = (ac_response_cmd_out_t *) response; - printf("Request %d output by %s: %s.\n", - o->id, - o->sender, - o->out); - break; - } - case AC_RESPONSE_CMD_RESULT: { - ac_response_cmd_result_t *o = (ac_response_cmd_result_t *) response; - printf("Request %d is done: %s (%d).\n", - o->id, - o->success ? "Success" : "Failed", - o->result); - break; - } - default: { - printf("Received an unrecognized response of type '%u'.\n", - response->type); - } - } -} - -#ifndef WIN32 - -static void intr(int signum) {} - -#endif - -static int on_send(const void *sock, - const void *buffer, - const size_t len) { - int r; - printf("Writing %lu bytes.\n", len); - if ((r = net_send(sock, buffer, len))) { - fprintf(stderr, "Cannot write %lu bytes to server: %s (%d).\n", - len, - net_strerror(r), - r); - } - return r; -} - -static int say(void *connection) { - int r; - ac_config_t conf = { - .name = "helloworld" - }; - ac_request_cmd_t req = { - .type = AC_REQUEST_CMD, - .id = 100, - .config = &conf, - .cmd = "say Hi" - }; - if ((r = ac_request(connection, (ac_request_t *) &req))) { - return r; - } - return 0; -} - -int main(int argc, char **argv) { - int r; - if ((r = net_init())) { - return r; - } -#if defined(THREAD_WIN32) - if (!(mtx_conn = CreateMutex(NULL, TRUE, NULL))) { - r = GetLastError(); - fprintf(stderr, "Cannot create mutex: %d.\n", r); - return r; - } -#elif defined(THREAD_C11) - if (mtx_init(&mtx_conn, mtx_plain) != thrd_success) { - fprintf(stderr, "Cannot create mutex.\n"); - return 1; - } -#endif - -#ifdef WIN32 - fprintf(stderr, "Warning: ^C handler on Windows is not yet available.\n"); -#else - struct sigaction sa; - sa.sa_handler = intr; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGINT, &sa, NULL) || - sigaction(SIGTERM, &sa, NULL)) { - const int e = errno; - fprintf(stderr, "Cannot set SIGINT or SIGTERM handlers: %s (%d).\n", - strerror(e), - e); - return e; - } -#endif - libac_config_t config = { -#ifdef DEBUG - .out = stderr, -#else - .out = NULL, -#endif - .tok = NULL - }; - if ((r = ac_init(&config))) return r; - void *connection; - SOCKET sock; - ac_connection_parameters_t parameters = { - .host = "localhost", - .port = 25575, - .id = "1", - .token = "123", - .version = 0, - .sock = NULL, - .on_send = on_send - }; - if ((r = net_connect(parameters.host, parameters.port, &sock))) { - fprintf(stderr, "Cannot connect to server: %s (%d).\n", - net_strerror(r), - r); - ac_free(); - net_free(); - return r; - } - parameters.sock = &sock; - if ((r = ac_connect(parameters, &connection))) { - net_close(&sock); - ac_free(); - net_free(); - return r; - } - ac_obj_t *obj; - int nr; - uint8_t buffer[1000U]; - size_t bytes; - enum ac_connection_state state; - printf("Waiting until the connection is established.\n"); - bool ready = false; - while (1) { - if ((nr = net_read(&sock, buffer, sizeof(buffer), &bytes, 10))) { - if (nr == NET_TIMEOUT) { - printf("Receive timeout.\n"); - continue; - } else { - goto end; - } - } - if ((r = lock())) { goto end; } - if ((r = ac_receive(connection, buffer, bytes, &obj))) { - if ((r = unlock())) { goto end; } - goto end; - } - if (!ready) { - /* Wait until ready. */ - if ((r = ac_get_state(connection, &state))) { - if ((r = unlock())) { goto end; } - goto end; - } - switch (state) { - case AC_STATE_INIT: - continue; - case AC_STATE_READY: - printf("Connection is established.\n"); - ready = true; - say(connection); - if ((r = unlock())) { goto end; } - continue; - default: - fprintf(stderr, "Unexpected state.\n"); - if ((r = unlock())) { goto end; } - goto end; - } - } - if ((r = unlock())) { goto end; } - - if (!obj) { - continue; - } - if (AC_IS_EVENT(obj->type)) { - handle_event((ac_event_t *) obj); - } else if (AC_IS_RESPONSE(obj->type)) { - handle_response((ac_response_t *) obj); - } - ac_object_free(obj); - } - end: - if (nr) { - if (nr == NET_CLOSED) { - fprintf(stderr, "Connection is closed while reading from the server.\n"); - } else { - fprintf(stderr, "Cannot receive from server: %s (%d).\n", - net_strerror(nr), - nr); - } - } - if ((r = lock())) { goto end; } - ac_disconnect(connection, false); - if ((r = unlock())) { goto end; } - net_close(&sock); - ac_free(); - net_free(); -#if defined(THREAD_WIN32) - CloseHandle(mtx_conn); -#elif defined(THREAD_C11) - mtx_destroy(&mtx_conn); -#endif - return r; -} \ No newline at end of file diff --git a/client/libacron/apps/helloworld/net.c b/client/libacron/apps/helloworld/net.c deleted file mode 100644 index 83192e0..0000000 --- a/client/libacron/apps/helloworld/net.c +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Created by yuuta on 7/22/22. - * - * A simple cross-platform (Unix and Windows) implementation of a socket client. - */ - -#include "net.h" - -#include -#include -#include -#include - -#ifdef WIN32 -#include /* For DWORD */ -#include -#include - -#define errno_sock WSAGetLastError() - -#else - -#include -#include -#include -#include - -#define closesocket(socket) close(socket) -#define errno_sock errno - -#endif - -int net_connect(const char *host, - const uint16_t port, - SOCKET *sock) { - int r; - struct addrinfo *res; - char service[6]; - - snprintf(service, 6, "%u", port); - if ((r = getaddrinfo(host, service, NULL, &res))) { - return r; - } - - SOCKET fd; - if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) <= 0) { - const int e = errno_sock; - freeaddrinfo(res); - return e; - } - if (connect(fd, res->ai_addr, res->ai_addrlen) < 0) { - const int e = errno_sock; - closesocket(fd); - freeaddrinfo(res); - return e; - } - freeaddrinfo(res); - *sock = fd; - return 0; -} - -int net_read(const SOCKET *sock, - void *buffer, - const size_t length, - size_t *read, - const unsigned int timeout) { - -#ifdef WIN32 - DWORD to = timeout * 1000; - if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&to, sizeof to)) { -#else - struct timeval tv; - tv.tv_sec = timeout; - tv.tv_usec = 0; - if (setsockopt(*sock, SOL_SOCKET, SO_RCVTIMEO, (const char *) &tv, sizeof tv)) { -#endif - return errno_sock; - } - - int bytes; - if ((bytes = (int) recv(*sock, buffer, length, 0)) <= 0) { - if (bytes < 0) { - const int e = errno_sock; -#ifdef WIN32 - if (e == WSAETIMEDOUT) { -#else - if (e == EAGAIN) { -#endif - return NET_TIMEOUT; - } - return e; - } - return NET_CLOSED; - } - *read = bytes; - return 0; -} - -int net_send(const SOCKET *sock, - const void *buffer, - size_t len) { - size_t pos; - const uint8_t *ptr = buffer; - int retval; - - for (pos = 0U; pos < len; pos += retval) { - size_t n = len - pos; - retval = (int) send(*sock, &ptr[pos], (int) n, 0); - if (retval <= 0) { - return errno_sock; - } - } - return 0; -} - -int net_init(void) { -#ifdef WIN32 - int r; - WORD wVersionRequested; - WSADATA wsaData; - wVersionRequested = MAKEWORD(2, 2); - if ((r = WSAStartup(wVersionRequested, &wsaData))) { - fprintf(stderr, "WSAStartup failed with error: %d\n", r); - return r; - } - if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE(wsaData.wVersion) != 2) { - fprintf(stderr, "Could not find a usable version of Winsock.dll\n"); - WSACleanup(); - return 1; - } -#endif - return 0; -} - -void net_close(const SOCKET *sock) { - closesocket(*sock); -} - -#ifdef WIN32 -static __declspec( thread ) LPTSTR err_buf = NULL; -#else -static _Thread_local char err_buf[1024]; -#endif - -void net_free(void) { -#ifdef WIN32 - WSACleanup(); - if (err_buf) { - LocalFree(err_buf); - err_buf = NULL; - } -#endif -} - -char *net_strerror(int errnum) { -#ifdef WIN32 - if (err_buf) { - LocalFree(err_buf); - err_buf = NULL; - } - FormatMessage( - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - errnum, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), - (LPTSTR) &err_buf, - 0, - NULL); - if (!err_buf) { - return "Unknown error"; - } -#else - int r; - if ((r = strerror_r(errnum, err_buf, sizeof(err_buf) / sizeof(char)))) { - snprintf(err_buf, 1024, "%d (%d)", errnum, r); - } -#endif - return err_buf; -} \ No newline at end of file diff --git a/client/libacron/apps/helloworld/net.h b/client/libacron/apps/helloworld/net.h deleted file mode 100644 index 1a49709..0000000 --- a/client/libacron/apps/helloworld/net.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Created by yuuta on 7/23/22. - */ - -#ifndef ACRONC_NET_H -#define ACRONC_NET_H - -#include -#include - -#ifdef WIN32 -#include - -#else - -#define SOCKET int - -#endif - -#define NET_CLOSED -2 -#define NET_TIMEOUT -1 - -int net_connect(const char *host, - uint16_t port, - SOCKET *sock); - -int net_read(const SOCKET *sock, - void *buffer, - size_t length, - size_t *read, - unsigned int timeout); - -int net_send(const SOCKET *sock, - const void *buffer, - size_t len); - -void net_close(const SOCKET *sock); - -int net_init(void); - -void net_free(void); - -char *net_strerror(int errnum); - -#endif /* ACRONC_NET_H */ -- cgit v1.2.3