diff --git a/.github/workflows/build-cmake.yml b/.github/workflows/build-cmake.yml index b5d5569..25c9a91 100644 --- a/.github/workflows/build-cmake.yml +++ b/.github/workflows/build-cmake.yml @@ -5,18 +5,16 @@ on: branches: ["main"] paths: - ".github/workflows/build-cmake.yml" - - "ipc/**/*.c" - - "ipc/**/*.h" - - "test/*.c" + - "**/*.c" + - "**/*.h" - "**/CMakeLists.txt" - "cmake/*.cmake" push: branches: ["main"] paths: - ".github/workflows/build-cmake.yml" - - "ipc/**/*.c" - - "ipc/**/*.h" - - "test/*.c" + - "**/*.c" + - "**/*.h" - "**/CMakeLists.txt" - "cmake/*.cmake" @@ -38,6 +36,9 @@ jobs: - name: Build run: | cmake --build build + - name: Test + run: | + ctest --verbose --output-on-failure --test-dir ${{ github.workspace }}/build/test windows-build-cmake: name: Windows @@ -57,3 +58,6 @@ jobs: - name: Build run: | cmake --build build + - name: Test + run: | + ctest --verbose --output-on-failure --test-dir ${{ github.workspace }}/build/test diff --git a/CMakeLists.txt b/CMakeLists.txt index 610d778..0fe11b3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,8 +4,10 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") project(libtestrig LANGUAGES C) +include(CTest) include(target_copy_dll) include(target_output_to_bin) +include(target_compile_warn_all) set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(LIB_DIR "${CMAKE_CURRENT_SOURCE_DIR}/lib") @@ -15,6 +17,8 @@ set(BIN_DIR "${CMAKE_SOURCE_DIR}/bin") set(IPC_SOURCES "${IPC_DIR}/ipc.c" + "${IPC_DIR}/pipe.c" + "${IPC_DIR}/sock.c" "${IPC_DIR}/message.c" "${IPC_DIR}/os.c") @@ -27,13 +31,6 @@ set(EPOS_SOURCES set(LIBTESTRIG_SOURCES ${IPC_SOURCES} ${EPOS_SOURCES}) -if (MSVC) - add_compile_options("/W4") -else() - add_compile_options("-Wall") -endif() - - if (WIN32) add_library(EposCmd SHARED IMPORTED) set_target_properties(EposCmd PROPERTIES @@ -50,14 +47,26 @@ else() set(EposCmd-FOUND TRUE) endif() +if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + message("-- Creating coverage reports in Debug mode.") + add_compile_options(-coverage) + add_link_options(-coverage) +endif() + add_library(libtestrig SHARED ${LIBTESTRIG_SOURCES}) target_compile_definitions(libtestrig PRIVATE COMPILING_TESTRIG_DLL) target_include_directories(libtestrig PUBLIC ${LIB_DIR}) target_link_libraries(libtestrig PUBLIC EposCmd) +target_compile_warn_all(libtestrig) target_output_to_bin(libtestrig) +set_target_properties(libtestrig + PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) if (WIN32) target_link_libraries(libtestrig PUBLIC Ws2_32 Kernel32) endif() +add_library(vscl::testrig::lib ALIAS libtestrig) + add_subdirectory("${CMAKE_SOURCE_DIR}/src") +add_subdirectory("${CMAKE_SOURCE_DIR}/test") diff --git a/CMakePresets.json b/CMakePresets.json index f8acb73..91b68b2 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,6 +15,7 @@ "CMAKE_COLOR_DIAGNOSTICS": "ON" } }, + { "name": "windows-only", "hidden": true, diff --git a/cmake/target_compile_warn_all.cmake b/cmake/target_compile_warn_all.cmake new file mode 100644 index 0000000..99a7d4c --- /dev/null +++ b/cmake/target_compile_warn_all.cmake @@ -0,0 +1,7 @@ +function(target_compile_warn_all IN_TARGET_NAME) + if (MSVC) + target_compile_options(${IN_TARGET_NAME} PRIVATE "/W4") + else() + target_compile_options(${IN_TARGET_NAME} PRIVATE "-Wall") + endif() +endfunction(target_compile_warn_all IN_TARGET_NAME) \ No newline at end of file diff --git a/lib/epos2/connect.c b/lib/epos2/connect.c index 9859e62..d91103b 100644 --- a/lib/epos2/connect.c +++ b/lib/epos2/connect.c @@ -7,7 +7,7 @@ #include "identify.h" static void vscl_failed_open_device(uint32_t error_code) { - vscl_print_error(error_code); + vscl_rig_perror(error_code); printf("Failed to open device with with following characteristics:\n"); } @@ -53,7 +53,7 @@ uint32_t vscl_initialize_devices(struct controller controllers_out[], void* hand error_code = vscl_clean_enable_device(&controllers_out[0], handles_out[0]); if (error_code != 0) { printf("While attempting to open gateway:\n\t"); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } @@ -77,7 +77,7 @@ uint32_t vscl_initialize_devices(struct controller controllers_out[], void* hand error_code = vscl_clean_enable_device(&controllers_out[i], handles_out[i]); if (error_code != 0) { printf("While attempting to open gateway:\n\t"); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } } @@ -100,7 +100,7 @@ uint32_t vscl_clean_enable_device(struct controller* controller, void* device_ha ret = VCS_ClearFault(device_handle, controller->node_id, &error_code); if (ret == 0) { printf("While clearing fault from %s at %s:\n\t", controller->name, controller->port); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } @@ -125,7 +125,7 @@ uint32_t vscl_close_device(struct controller* controller, void* device_handle) { if (ret == 0) { printf("While attempting to close device %s at node %ihh\n\t", controller->name, controller->node_id); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } @@ -156,7 +156,7 @@ uint32_t vscl_close_devices(struct controller controllers[], void* device_handle if (ret == 0) { printf("While closing subdevice %s at node %ihh:\n\t", controllers[i].name, controllers[i].node_id); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } @@ -169,7 +169,7 @@ uint32_t vscl_close_devices(struct controller controllers[], void* device_handle if (ret == 0) { printf("While closing gateway device %s at node %ihh:\n\t", controllers[0].name, controllers[0].node_id); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } diff --git a/lib/epos2/epos2.c b/lib/epos2/epos2.c index e16b80e..f286311 100644 --- a/lib/epos2/epos2.c +++ b/lib/epos2/epos2.c @@ -11,7 +11,7 @@ int vscl_decode_error(const uint32_t error_code, char* error_msg, vscl_byte_t ma return ret; } -int vscl_print_error(const uint32_t error_code) { +int vscl_rig_perror(const uint32_t error_code) { char msg[64] = { 0 }; int ret = vscl_decode_error(error_code, msg, 64); printf("ERROR 0x%X: %s\n", error_code, msg); @@ -24,7 +24,7 @@ uint32_t vscl_reset_device(void *device_handle, struct controller* controller_in uint32_t error_code = 0; int ret = VCS_ResetDevice(device_handle, controller_in->node_id, &error_code); if (ret == 0) { - vscl_print_error(error_code); + vscl_rig_perror(error_code); printf("Device failed to be reset: %s\n", controller_in->name); } @@ -47,7 +47,7 @@ uint32_t vscl_setup_can_gateway(struct controller controllers[3], void* handles[ error_code = vscl_initialize_devices(controllers, handles, 3); if (error_code != 0) { printf("When initializing multiple devices: "); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } @@ -60,7 +60,7 @@ uint32_t vscl_cleanup_testrig(struct controller controllers[3], void* handles[3] error_code = vscl_close_devices(controllers, handles, 3); if (error_code != 0) { printf("When cleaning up multiple devices: "); - vscl_print_error(error_code); + vscl_rig_perror(error_code); return error_code; } diff --git a/lib/epos2/epos2.h b/lib/epos2/epos2.h index 633d3cd..af56701 100644 --- a/lib/epos2/epos2.h +++ b/lib/epos2/epos2.h @@ -22,7 +22,7 @@ int TESTRIG_API vscl_decode_error(const uint32_t error_code, char* error_msg, vs /* * @brief Decode and print an error code's corresponding message. */ -int TESTRIG_API vscl_print_error(const uint32_t error_code); +int TESTRIG_API vscl_rig_perror(const uint32_t error_code); /* * @brief Reset a device. diff --git a/lib/epos2/identify.c b/lib/epos2/identify.c index fc6e54c..8b0935a 100644 --- a/lib/epos2/identify.c +++ b/lib/epos2/identify.c @@ -7,23 +7,23 @@ static void vscl_print_naming_error(vscl_byte_t error_code) { printf("While identifying device names:\n\t"); - vscl_print_error(error_code); + vscl_rig_perror(error_code); } static void vscl_print_protocol_ident_error(vscl_byte_t error_code, const char* name) { printf("While identifying device protocol stacks for device %s:\n\t", name); - vscl_print_error(error_code); + vscl_rig_perror(error_code); } static void vscl_print_interface_ident_error(vscl_byte_t error_code, const char* name, const char* protocol) { printf("While identifying device interface for device %s on protocol %s:\n\t", name, protocol); - vscl_print_error(error_code); + vscl_rig_perror(error_code); } static void vscl_print_port_ident_error(vscl_byte_t error_code, const char* name, const char* protocol, const char* iface) { printf("While identifying device interface for device %s on protocol %s and interface %s:\n\t", name, protocol, iface); - vscl_print_error(error_code); + vscl_rig_perror(error_code); } uint32_t vscl_ident_names(void) { diff --git a/lib/epos2/manage.c b/lib/epos2/manage.c index b86d5af..510f399 100644 --- a/lib/epos2/manage.c +++ b/lib/epos2/manage.c @@ -16,7 +16,7 @@ uint32_t vscl_abort(const struct controller* controller_in, void* device_handle) int ret = VCS_SetQuickStopState(device_handle, controller_in->node_id, &error_code); if (ret == 0) { - vscl_print_error(error_code); + vscl_rig_perror(error_code); printf("DANGER: Failed to abort rig operations!\n"); } else { diff --git a/lib/ipc/ipc.c b/lib/ipc/ipc.c index 3a88628..3bc3949 100644 --- a/lib/ipc/ipc.c +++ b/lib/ipc/ipc.c @@ -1,159 +1 @@ -#include -#include -#include -#include - #include "ipc.h" -#include "os.h" - -#ifdef _WIN32 -// TODO: Evaluate if this is sufficient (it is honestly kind of smelly) -typedef int socklen_t; -#endif - -int vscl_sock_genpath(char* sockpath) { - int retstat; - int baselen; - //int dstart; - - retstat = vscl_get_sock_destination(sockpath); - baselen = strlen(sockpath); - - if (retstat != 0) { return retstat; } - - srand(time(NULL)); - for (int i = baselen; i < 32 + baselen - 1; i++) { - int start = (i % 2 == 0) ? 'a' : 'A'; - sockpath[i] = rand() % (25 + 1) + start; - } - - strncat(sockpath, ".rigsock", 9); - return 0; -} - -int vscl_sock_setup(struct sockaddr_un* sockaddr_mut) { - int fd; - int path_set; - char sockpath[108] = { 0 }; - char blank[108] = { 0 }; - -#ifdef _WIN32 - WSADATA wsa_data; - int wsa_result; - - wsa_result = WSAStartup(MAKEWORD(2, 2), &wsa_data); - if (wsa_result != 0) { perror("failed WSAStartup"); return -1; } -#endif // _WIN32 - - fd = socket(AF_UNIX, SOCK_STREAM, 0); - if (fd == INVALID_SOCKET) { perror("failed to create socket"); return -1; } - - path_set = (strncmp( - sockaddr_mut->sun_path, blank, - strnlen(sockaddr_mut->sun_path, 108)) == 0) ? -1 : 1; - - if (path_set == -1) { - vscl_sock_genpath(sockpath); - long long int sockpathlen = strnlen(sockpath, 108); - strncpy(sockaddr_mut->sun_path, sockpath, sockpathlen + 1); - } - - sockaddr_mut->sun_family = AF_UNIX; - return fd; -} - -int vscl_sock_bind(const int fd, const struct sockaddr_un* sockaddr) { - int bindstat; - socklen_t socklen; - - socklen = sizeof(*sockaddr); - bindstat = bind(fd, (struct sockaddr*)sockaddr, socklen); - if (bindstat == -1) { perror("failed to bind socket"); } - - return bindstat; -} - -int vscl_sock_listen(const int fd, int max_backlog) { - int listenstat; - - listenstat = listen(fd, max_backlog); - if (listenstat == -1) { perror("failed to set socket to listen"); } - - return listenstat; -} - -int vscl_sock_connect(const int fd, const struct sockaddr_un* sockaddr) { - int connstat; - socklen_t socklen; - - socklen = sizeof(*sockaddr); - connstat = connect(fd, (struct sockaddr*)sockaddr, socklen); - if (connstat == -1) { perror("failed to connect to socket"); } - - return connstat; -} - -int vscl_sock_close(const int fd, struct sockaddr_un* sockaddr) { - int closestat; - -#ifdef _WIN32 - closestat = closesocket(fd); -#else - closestat = close(fd); -#endif - remove(sockaddr->sun_path); - - return closestat; -} - -int vscl_sock_send(const int fd, struct rig_message* msg) { - int nbytes; - vscl_byte_t buf[12] = { 0 }; - - for (int i = 0; i < 4; i++) { - buf[i] = msg->head[i]; - } - - for (int i = 0; i < 8; i++) { - buf[i + 4] = msg->data[i]; - } - -#ifdef _WIN32 - nbytes = send(fd, buf, 12, MSG_DONTROUTE); -#else - nbytes = write(fd, buf, 12); -#endif - if (nbytes == -1) { perror("Clientside socket send error"); } - - return nbytes; -} - -int vscl_ident_header_part(vscl_byte_t in[4], int idx) { - if (in[idx] == HEAD_STAY[idx]) { - return HEADER_IS_STAY; - } - else if (in[idx] == HEAD_DC[idx]) { - return HEADER_IS_DC; - } - else if (in[idx] == HEAD_SYNC[idx]) { - return HEADER_IS_SYNC; - } - else { - return -1; - } -} - -int vscl_ident_full_header(vscl_byte_t in[4]) { - int identity = -1; - - for (int i = 0; i < 4; i++) { - if (i == 0) { - identity = vscl_ident_header_part(in, 0); - } - else { - identity &= vscl_ident_header_part(in, i); - } - } - - return identity; -} diff --git a/lib/ipc/ipc.h b/lib/ipc/ipc.h index 1277399..9167569 100644 --- a/lib/ipc/ipc.h +++ b/lib/ipc/ipc.h @@ -1,82 +1,14 @@ #pragma once -// Headers #ifdef __cplusplus extern "C" { #endif -#ifdef _WIN32 -#include -#include -#else -#include -#include -#include -#include -#endif // _WIN32 -#include -#include - -#include "ipc/os.h" #include "ipc/message.h" -#include "libtestrig_api.h" - -/* - * @brief Generate a path to a socket file. - * - * This is in a temporary path with the .rigsock extension. - */ -TESTRIG_API int vscl_sock_genpath(char* sockpath); - -/* - * @brief Create a Unix socket to a random file. - * - * Returns a nonzero int representing the file descriptor, otherwise -1. - */ -TESTRIG_API int vscl_sock_setup(struct sockaddr_un* sockaddr_mut); - -/* - * @brief Bind the Unix socket, using the path specified in the passed sockaddr_un struct. - * - * Returns 0 on success, -1 on failure. - */ -TESTRIG_API int vscl_sock_bind(const int fd, const struct sockaddr_un* sockaddr); - -/* - * @brief Set the Unix socket to listen and permit connection attempts. - * - * Returns 0 on success, -1 on failure. - */ -TESTRIG_API int vscl_sock_listen(const int fd, int max_backlog); - -/* - * @brief Connect the Unix Socket, using the path specified in the passed sockaddr_un struct. - * - * Returns 0 on success, -1 on failure. - */ -TESTRIG_API int vscl_sock_connect(const int fd, const struct sockaddr_un* sockaddr); - -/* - * @brief Close the socket and clean up. - */ -TESTRIG_API int vscl_sock_close(const int fd, struct sockaddr_un* sockaddr); - -/* - * @brief Send a message over the socket without waiting for a response. - * TODO: make version that waits - */ -TESTRIG_API int vscl_sock_send(const int fd, struct rig_message* msg); - -/* - * @brief Identify the header byte. - */ -TESTRIG_API int vscl_ident_header_part(vscl_byte_t in[4], int idx); - -/* - * @brief Identify the four bytes in the header. - */ -TESTRIG_API int vscl_ident_full_header(vscl_byte_t in[4]); +#include "ipc/os.h" +#include "ipc/sock.h" +#include "ipc/pipe.h" #ifdef __cplusplus } // extern "C" -#endif \ No newline at end of file +#endif diff --git a/lib/ipc/message.c b/lib/ipc/message.c index 976e26e..af395c5 100644 --- a/lib/ipc/message.c +++ b/lib/ipc/message.c @@ -14,3 +14,33 @@ int vscl_set_message(struct rig_message* msg, const vscl_byte_t* head, const vsc memcpy(msg->data, data, 8); return 0; } + +int vscl_ident_header_part(vscl_byte_t in[4], int idx) { + if (in[idx] == HEAD_STAY[idx]) { + return HEADER_IS_STAY; + } + else if (in[idx] == HEAD_DC[idx]) { + return HEADER_IS_DC; + } + else if (in[idx] == HEAD_SYNC[idx]) { + return HEADER_IS_SYNC; + } + else { + return -1; + } +} + +int vscl_ident_full_header(vscl_byte_t in[4]) { + int identity = -1; + + for (int i = 0; i < 4; i++) { + if (i == 0) { + identity = vscl_ident_header_part(in, 0); + } + else { + identity &= vscl_ident_header_part(in, i); + } + } + + return identity; +} diff --git a/lib/ipc/message.h b/lib/ipc/message.h index 14d6d25..ecbf6e4 100644 --- a/lib/ipc/message.h +++ b/lib/ipc/message.h @@ -48,6 +48,16 @@ TESTRIG_API extern const vscl_byte_t MESSAGE_BLANK[8]; // Convenience function that encapsulates copying to the header and data fields. TESTRIG_API int vscl_set_message(struct rig_message* msg, const vscl_byte_t* head, const vscl_byte_t* data); +/* + * @brief Identify the header byte. + */ +TESTRIG_API int vscl_ident_header_part(vscl_byte_t in[4], int idx); + +/* + * @brief Identify the four bytes in the header. + */ +TESTRIG_API int vscl_ident_full_header(vscl_byte_t in[4]); + /* * The type of header. */ diff --git a/lib/ipc/os.c b/lib/ipc/os.c index f25e9f9..8f85906 100644 --- a/lib/ipc/os.c +++ b/lib/ipc/os.c @@ -22,7 +22,7 @@ int vscl_get_sock_destination(char *dest) { errno_t retstat; retstat = getenv_s(&retvalue, usrtemp, 76, "TEMP"); - if (retvalue == 0 || retstat != 0) { perror("Failed to get TEMP"); return retstat; } + if (retvalue == 0 || retstat != 0) { vscl_os_perror("Failed to get TEMP"); return retstat; } strncpy(dest, usrtemp, retvalue); strncat(dest, "\\", 2); @@ -36,8 +36,8 @@ int vscl_get_sock_destination(char *dest) { int vscl_make_new_proc(const char* prog, const char* args) { #if _WIN32 - PROCESS_INFORMATION pi; - STARTUPINFO si; + PROCESS_INFORMATION pi = { 0 }; + STARTUPINFO si = { 0 }; char cmd[1024] = { 0 }; sprintf(cmd, "%s %s", prog, args); @@ -55,7 +55,7 @@ int vscl_make_new_proc(const char* prog, const char* args) { &pi ); - if (mkdetach == FALSE) { vscl_winprint_error("failed to detach"); return -1; } + if (mkdetach == FALSE) { vscl_os_perror("failed to detach"); return -1; } CloseHandle(&si); CloseHandle(&pi); @@ -67,12 +67,12 @@ int vscl_make_new_proc(const char* prog, const char* args) { switch (pid) { case -1: - perror("failed to fork off"); + vscl_os_perror("failed to fork off"); return -1; break; case 0: if (setsid() == -1) { - perror("failed to detach"); + vscl_os_perror("failed to detach"); return -1; } else { @@ -84,7 +84,7 @@ int vscl_make_new_proc(const char* prog, const char* args) { strncpy(argv[0], prog, strlen(prog) + 1); argv[1] = (char*)calloc(64, 1); - char* arg1 = strstr(args, " "); + const char* arg1 = strstr(args, " "); if (arg1 == NULL) { strncpy(argv[1], args, strlen(args) + 1); } @@ -95,8 +95,8 @@ int vscl_make_new_proc(const char* prog, const char* args) { strncpy(interstr, args, interlen); strncpy(argv[1], interstr, strlen(interstr) + 1); - char* argn = strstr(arg1, " "); - char* argn1 = arg1; + char* argn = (char*)strstr(arg1, " "); + char* argn1 = (char*)arg1; while (argn != NULL) { num_args++; if (num_args > sizeof_argv) { @@ -110,7 +110,7 @@ int vscl_make_new_proc(const char* prog, const char* args) { size_t argnlen = vscl_strinterlen(argn, argn1); argnlen = (argnlen == 0) ? 64 : argnlen; - strncpy(argnstr, argn1, argnlen); + strncpy(argnstr, argn1 + 1, argnlen); strncpy(argv[n], argnstr, strlen(argnstr) + 1); argn1 = argn; @@ -118,6 +118,13 @@ int vscl_make_new_proc(const char* prog, const char* args) { } } + num_args++; + if (num_args > sizeof_argv) { + sizeof_argv *= 2; + argv = (char**)realloc(argv, sizeof_argv * sizeof(char*)); + } + + argv[num_args - 1] = NULL; execv(prog, argv); for (size_t i = 0; i < num_args; i++) { @@ -135,8 +142,16 @@ int vscl_make_new_proc(const char* prog, const char* args) { #endif } +void vscl_sleep(uint32_t s) { +#ifdef _WIN32 + Sleep(s * 1000); +#else + sleep(s); +#endif +} + +void vscl_os_perror(const char* preamble) { #ifdef _WIN32 -void vscl_winprint_error(const TCHAR* msg) { DWORD errcode = GetLastError(); TCHAR errmsg[256] = { 0 }; @@ -151,6 +166,8 @@ void vscl_winprint_error(const TCHAR* msg) { if (wides == 0) { printf("error while processing error\n"); } - wprintf(L"%s: %s", msg, errmsg); -} + printf("%s: %s", preamble, errmsg); +#else + perror(preamble); #endif +} diff --git a/lib/ipc/os.h b/lib/ipc/os.h index f0f294a..3460930 100644 --- a/lib/ipc/os.h +++ b/lib/ipc/os.h @@ -1,25 +1,20 @@ #pragma once -#ifdef _WIN32 -#include -#endif - #ifdef __cplusplus extern "C" { #endif // __cplusplus -#include "libtestrig_api.h" - -#ifndef _WIN32 -#define INVALID_SOCKET -1 +#ifdef _WIN32 +#include "win_headers.h" #endif +#include "libtestrig_api.h" +#include + TESTRIG_API int vscl_get_sock_destination(char* dest); TESTRIG_API int vscl_make_new_proc(const char* prog, const char* args); - -#ifdef _WIN32 -TESTRIG_API void vscl_winprint_error(const TCHAR* msg); -#endif +TESTRIG_API void vscl_sleep(uint32_t s); +TESTRIG_API void vscl_os_perror(const char* msg); #ifdef __cplusplus } // extern "C" diff --git a/lib/ipc/pipe.c b/lib/ipc/pipe.c new file mode 100644 index 0000000..2384371 --- /dev/null +++ b/lib/ipc/pipe.c @@ -0,0 +1,48 @@ +#include +#include "ipc/pipe.h" +#include "ipc/os.h" + +vscl_pipe_t vscl_create_pipes() { + + vscl_pipe_t piper = { 0 }; + +#if _WIN32 + SECURITY_ATTRIBUTES sattrs = { 0 }; + sattrs.nLength = sizeof(SECURITY_ATTRIBUTES); + sattrs.bInheritHandle = TRUE; + sattrs.lpSecurityDescriptor = NULL; + + if (!CreatePipe(&piper.child_read, &piper.child_write, &sattrs, 0)) + vscl_os_perror("While creating child read pipe"); + + if (!SetHandleInformation(piper.child_read, HANDLE_FLAG_INHERIT, 0)) + vscl_os_perror("While preventing child from taking parent pipe attributes"); +#else + int pipefd[2] = { 0 }; + int pipestat = pipe(pipefd); + if (pipestat == -1) { vscl_os_perror("While creating pipes"); } + + piper.read_fd = pipefd[0]; + piper.write_fd = pipefd[1]; + +#endif + return piper; +} + +int vscl_read_pipe(vscl_pipe_t* pipe, vscl_byte_t buf[], size_t nbytes) { +#if _WIN32 + return 0; +#else + size_t read_actual = read(pipe->read_fd, buf, nbytes); + return (read_actual == nbytes) ? 0 : -1; +#endif +} + +int vscl_write_pipe(vscl_pipe_t* pipe, vscl_byte_t buf[], size_t nbytes) { +#if _WIN32 + return 0; +#else + size_t write_actual = write(pipe->write_fd, buf, nbytes); + return (write_actual == nbytes) ? 0 : -1; +#endif +} diff --git a/lib/ipc/pipe.h b/lib/ipc/pipe.h new file mode 100644 index 0000000..7e288a5 --- /dev/null +++ b/lib/ipc/pipe.h @@ -0,0 +1,31 @@ +#pragma once + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef _WIN32 +#include "ipc/win_headers.h" +#else +#include +#endif + +#include "libtestrig_api.h" + +typedef struct { +#ifdef _WIN32 + HANDLE child_read; + HANDLE child_write; +#else + int read_fd; + int write_fd; +#endif +} vscl_pipe_t; + +TESTRIG_API vscl_pipe_t vscl_create_pipes(void); +TESTRIG_API int vscl_read_pipe(vscl_pipe_t* pipe, vscl_byte_t buf[], size_t nbytes); +TESTRIG_API int vscl_write_pipe(vscl_pipe_t* pipe, vscl_byte_t buf[], size_t nbytes); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/lib/ipc/sock.c b/lib/ipc/sock.c new file mode 100644 index 0000000..96942fd --- /dev/null +++ b/lib/ipc/sock.c @@ -0,0 +1,124 @@ +#include +#include +#include +#include + +#include "sock.h" +#include "os.h" + +int vscl_sock_genpath(char* sockpath) { + int retstat; + int baselen; + //int dstart; + + retstat = vscl_get_sock_destination(sockpath); + baselen = strlen(sockpath); + + if (retstat != 0) { return retstat; } + + srand(time(NULL)); + for (int i = baselen; i < 32 + baselen - 1; i++) { + int start = (i % 2 == 0) ? 'a' : 'A'; + sockpath[i] = rand() % (25 + 1) + start; + } + + strncat(sockpath, ".rigsock", 9); + return 0; +} + +int vscl_sock_setup(struct sockaddr_un* sockaddr_mut) { + int fd; + int path_set; + char sockpath[108] = { 0 }; + char blank[108] = { 0 }; + +#ifdef _WIN32 + WSADATA wsa_data; + int wsa_result; + + wsa_result = WSAStartup(MAKEWORD(2, 2), &wsa_data); + if (wsa_result != 0) { vscl_os_perror("Failed WSAStartup"); return -1; } +#endif // _WIN32 + + fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (fd == INVALID_SOCKET) { vscl_os_perror("Failed to create socket"); return -1; } + + path_set = (strncmp( + sockaddr_mut->sun_path, blank, + strnlen(sockaddr_mut->sun_path, 108)) == 0) ? -1 : 1; + + if (path_set == -1) { + vscl_sock_genpath(sockpath); + long long int sockpathlen = strnlen(sockpath, 108); + strncpy(sockaddr_mut->sun_path, sockpath, sockpathlen + 1); + } + + sockaddr_mut->sun_family = AF_UNIX; + return fd; +} + +int vscl_sock_bind(const int fd, const struct sockaddr_un* sockaddr) { + int bindstat; + socklen_t socklen; + + socklen = sizeof(*sockaddr); + bindstat = bind(fd, (struct sockaddr*)sockaddr, socklen); + if (bindstat == -1) { vscl_os_perror("Failed to bind socket"); } + + return bindstat; +} + +int vscl_sock_listen(const int fd, int max_backlog) { + int listenstat; + + listenstat = listen(fd, max_backlog); + if (listenstat == -1) { vscl_os_perror("Failed to set socket to listen"); } + + return listenstat; +} + +int vscl_sock_connect(const int fd, const struct sockaddr_un* sockaddr) { + int connstat; + socklen_t socklen; + + socklen = sizeof(*sockaddr); + connstat = connect(fd, (struct sockaddr*)sockaddr, socklen); + if (connstat == -1) { vscl_os_perror("Failed to connect to socket"); } + + return connstat; +} + +int vscl_sock_close(const int fd, struct sockaddr_un* sockaddr) { + int closestat; + +#ifdef _WIN32 + closestat = closesocket(fd); +#else + closestat = close(fd); +#endif + remove(sockaddr->sun_path); + + return closestat; +} + +int vscl_sock_send(const int fd, struct rig_message* msg) { + int nbytes; + vscl_byte_t buf[12] = { 0 }; + + for (int i = 0; i < 4; i++) { + buf[i] = msg->head[i]; + } + + for (int i = 0; i < 8; i++) { + buf[i + 4] = msg->data[i]; + } + +#ifdef _WIN32 + nbytes = send(fd, buf, 12, MSG_DONTROUTE); +#else + nbytes = write(fd, buf, 12); +#endif + if (nbytes == -1) { vscl_os_perror("Clientside socket send error"); } + + return nbytes; +} diff --git a/lib/ipc/sock.h b/lib/ipc/sock.h new file mode 100644 index 0000000..809e474 --- /dev/null +++ b/lib/ipc/sock.h @@ -0,0 +1,72 @@ +#pragma once + +// Headers +#ifdef __cplusplus +extern "C" { +#endif +#ifndef _WIN32 +#include +#include +#include +#include + +#define INVALID_SOCKET -1 +#else +#include "ipc/win_headers.h" +#endif // _WIN32 + +#include +#include + +#include "ipc/message.h" +#include "libtestrig_api.h" + +/* + * @brief Generate a path to a socket file. + * + * This is in a temporary path with the .rigsock extension. + */ +TESTRIG_API int vscl_sock_genpath(char* sockpath); + +/* + * @brief Create a Unix socket to a random file. + * + * Returns a nonzero int representing the file descriptor, otherwise -1. + */ +TESTRIG_API int vscl_sock_setup(struct sockaddr_un* sockaddr_mut); + +/* + * @brief Bind the Unix socket, using the path specified in the passed sockaddr_un struct. + * + * Returns 0 on success, -1 on failure. + */ +TESTRIG_API int vscl_sock_bind(const int fd, const struct sockaddr_un* sockaddr); + +/* + * @brief Set the Unix socket to listen and permit connection attempts. + * + * Returns 0 on success, -1 on failure. + */ +TESTRIG_API int vscl_sock_listen(const int fd, int max_backlog); + +/* + * @brief Connect the Unix Socket, using the path specified in the passed sockaddr_un struct. + * + * Returns 0 on success, -1 on failure. + */ +TESTRIG_API int vscl_sock_connect(const int fd, const struct sockaddr_un* sockaddr); + +/* + * @brief Close the socket and clean up. + */ +TESTRIG_API int vscl_sock_close(const int fd, struct sockaddr_un* sockaddr); + +/* + * @brief Send a message over the socket without waiting for a response. + * TODO: make version that waits + */ +TESTRIG_API int vscl_sock_send(const int fd, struct rig_message* msg); + +#ifdef __cplusplus +} // extern "C" +#endif diff --git a/lib/ipc/win_headers.h b/lib/ipc/win_headers.h new file mode 100644 index 0000000..1580cab --- /dev/null +++ b/lib/ipc/win_headers.h @@ -0,0 +1,6 @@ +#ifdef _WIN32 +#include +#include +#include +#include +#endif diff --git a/lib/libtestrig_api.h b/lib/libtestrig_api.h index b6445aa..812e0e2 100644 --- a/lib/libtestrig_api.h +++ b/lib/libtestrig_api.h @@ -12,4 +12,9 @@ #endif // _WIN32 #endif // TESTRIG_API -typedef char vscl_byte_t; \ No newline at end of file +typedef char vscl_byte_t; + +enum VSCL_PLATFORM { + VSCL_PLATFORM_WINDOWS, + VSCL_PLATFORM_LINUX, +}; \ No newline at end of file diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 14dd2f0..d7d6286 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,12 @@ set(TESTRIG_SOURCES "${CMAKE_CURRENT_LIST_DIR}/daemon.c") add_executable(testrig ${TESTRIG_SOURCES}) -target_link_libraries(testrig PRIVATE libtestrig) +target_link_libraries(testrig PRIVATE vscl::testrig::lib) target_include_directories(testrig PRIVATE ${LIB_DIR}) -target_output_to_bin(testrig) \ No newline at end of file +target_compile_warn_all(testrig) +target_output_to_bin(testrig) +set_target_properties(testrig + PROPERTIES VERIFY_INTERFACE_HEADER_SETS ON) + +add_executable(vscl::testrig::cli ALIAS testrig) + diff --git a/src/actions.c b/src/actions.c index c554e74..cc7f540 100644 --- a/src/actions.c +++ b/src/actions.c @@ -43,25 +43,22 @@ int delegate_to_daemon(enum CLI_ACTION act) { int found = seek_daemon(&sockaddr); if (!found) { - printf("testrigd not running; creating new testrig process at "); - int pid = vscl_make_new_proc(PROG_NAME, "--detach --daemon"); + printf("testrigd not running; Creating new testrig process at "); + int pid = vscl_make_new_proc(PROG_NAME, "--detach daemon"); printf("%i\n", pid); } int conn = vscl_sock_connect(sock, &sockaddr); int try = 1; while (conn == -1 && try < NUM_MAX_RETRIES) { -#ifdef _WIN32 - Sleep(1000); -#else - sleep(1); -#endif + vscl_sleep(1); + try++; conn = vscl_sock_connect(sock, &sockaddr); } if (try >= NUM_MAX_RETRIES) { - fprintf(stderr, "failed to connect after %i retries", NUM_MAX_RETRIES); + fprintf(stderr, "Failed to connect after %i retries", NUM_MAX_RETRIES); return -1; } @@ -124,7 +121,7 @@ int testrig_ident(other_args* others) { return 0; } else { - fprintf(stderr, "error: the arguments passed to ident could not be parsed\n"); + fprintf(stderr, "Error: The arguments passed to ident could not be parsed\n"); return 1; } } @@ -150,7 +147,7 @@ int testrig_open([[maybe_unused]] other_args* others) { return 0; } -int testrig_request([[maybe_unused]] other_args* others) { +int testrig_peek([[maybe_unused]] other_args* others) { // need to impl a daemon autolaunches struct sockaddr_un sockaddr = { 0 }; @@ -159,7 +156,7 @@ int testrig_request([[maybe_unused]] other_args* others) { if (setup == -1) { return 1; } int not_sought = seek_daemon(&daemon_sockaddr); - if (not_sought) { printf("not found"); vscl_sock_close(setup, &sockaddr); return 1; } + if (not_sought) { printf("Not found\n"); vscl_sock_close(setup, &sockaddr); return 1; } int conn = vscl_sock_connect(setup, &daemon_sockaddr); if (conn == -1) { return 1; } diff --git a/src/actions.h b/src/actions.h index 4043c16..c9930ba 100644 --- a/src/actions.h +++ b/src/actions.h @@ -17,7 +17,7 @@ int delegate_to_daemon(enum CLI_ACTION act); int testrig_ident(other_args* others); int testrig_stat(other_args* others); int testrig_open(other_args* others); -int testrig_request(other_args* others); +int testrig_peek(other_args* others); int testrig_close(other_args* others); void free_other_args(other_args* others); diff --git a/src/args.c b/src/args.c index 1ce42cd..7ca7fe5 100644 --- a/src/args.c +++ b/src/args.c @@ -14,10 +14,10 @@ static void die_invalid_arg(const char* arg) { static char* mode_map[] = { "command", "detach" }; char* action_map[] = { "help", "ident", "status", "daemon", - "open", "request", "close" }; + "open", "peek", "close" }; static int(*fun_map[])(other_args*) = { &help_me, &testrig_ident, &testrig_stat, &testrig_daemon, - &testrig_open, &testrig_request, &testrig_close }; + &testrig_open, &testrig_peek, &testrig_close }; int is_flag(const char* arg) { return (strstr(arg, "--") == NULL) ? 0 : 1; } int is_opt(const char* arg) { @@ -34,8 +34,7 @@ int parse_flag(const char* flag, struct parsed_args* parsed) { const char* name = strstr(flag, "--") + 2; int ret = 0; - if (!strncmp("daemon", name, 7)) { parsed->action = ACTION_DAEMON; ret = 1; } - else if (!strncmp("detach", name, 7)) { parsed->mode = CLI_MODE_DETACHED; ret = 1; } + if (!strncmp("detach", name, 7)) { parsed->mode = CLI_MODE_DETACHED; ret = 1; } else if (!strncmp("verbose", name, 8)) { parsed->verbosity = 1; ret = 1; } else if (!strncmp("help", name, 5)) { parsed->action = ACTION_HELP; ret = 1; } @@ -45,13 +44,16 @@ int parse_flag(const char* flag, struct parsed_args* parsed) { int parse_act(const char* act, struct parsed_args* parsed) { int ret = 0; - if (!strncmp("daemon", act, 7)) { parsed->action = ACTION_DAEMON; ret = 1; } - else if (!strncmp("ident", act, 6)) { parsed->action = ACTION_IDENT; ret = 1; } - else if (!strncmp("status", act, 7)) { parsed->action = ACTION_STAT; ret = 1; } - else if (!strncmp("open", act, 5)) { parsed->action = ACTION_OPEN; ret = 1; } - else if (!strncmp("request", act, 7)) { parsed->action = ACTION_REQUEST; ret = 1; } - else if (!strncmp("close", act, 6)) { parsed->action = ACTION_CLOSE; ret = 1; } - else if (!strncmp("help", act, 5)) { parsed->action = ACTION_HELP; ret = 1; } + const char* lead_stripped = strstr(act, " "); + const char* actual = (lead_stripped == NULL) ? act : lead_stripped + 1; + + if (!strncmp("daemon", actual, 7)) { parsed->action = ACTION_DAEMON; ret = 1; } + else if (!strncmp("ident", actual, 6)) { parsed->action = ACTION_IDENT; ret = 1; } + else if (!strncmp("status", actual, 7)) { parsed->action = ACTION_STAT; ret = 1; } + else if (!strncmp("open", actual, 5)) { parsed->action = ACTION_OPEN; ret = 1; } + else if (!strncmp("peek", actual, 7)) { parsed->action = ACTION_REQUEST; ret = 1; } + else if (!strncmp("close", actual, 6)) { parsed->action = ACTION_CLOSE; ret = 1; } + else if (!strncmp("help", actual, 5)) { parsed->action = ACTION_HELP; ret = 1; } return ret; } diff --git a/src/daemon.c b/src/daemon.c index bc27d4e..f5037d3 100644 --- a/src/daemon.c +++ b/src/daemon.c @@ -9,9 +9,7 @@ #include "ipc/ipc.h" #ifdef _WIN32 -#include -#include // holy cow there is some include order stuff with windows.h -typedef int socklen_t; +#include "ipc/win_headers.h" #else #include #include @@ -28,7 +26,7 @@ static SOCKET daemon_sock; BOOL WINAPI interrupt_catcher(DWORD ctrl_type) { if (ctrl_type == CTRL_C_EVENT) { - DAEMON_CURRENT_STATUS = TESTRIG_DAEMON_STOPPED; + DAEMON_CURRENT_STATUS = TESTRIG_DAEMON_CLEANING; printf("Stopping!\n"); vscl_sock_close(daemon_sock, daemon_sockaddr); return TRUE; @@ -39,14 +37,14 @@ BOOL WINAPI interrupt_catcher(DWORD ctrl_type) { #else static void interrupt_catcher(int sig, siginfo_t* info, [[ maybe_unused ]] void* ucontext) { if (sig != SIGINT || info->si_signo != SIGINT) { return; } - DAEMON_CURRENT_STATUS = TESTRIG_DAEMON_STOPPED; + DAEMON_CURRENT_STATUS = TESTRIG_DAEMON_CLEANING; } #endif // _WIN32: Clean Ctrl+C handlers static int deploy_interrupt_cleanup([[maybe_unused]] int sock, [[maybe_unused]] struct sockaddr_un* sockaddr) { #ifdef _WIN32 BOOL setted = SetConsoleCtrlHandler(interrupt_catcher, TRUE); - if (!setted) { vscl_winprint_error("daemon ctrl handler"); return 0; } + if (!setted) { vscl_os_perror("Daemon ctrl handler"); return 0; } daemon_sockaddr = sockaddr; daemon_sock = sock; @@ -57,7 +55,7 @@ static int deploy_interrupt_cleanup([[maybe_unused]] int sock, [[maybe_unused]] act.sa_sigaction = &interrupt_catcher; int sigint_bound = sigaction(SIGINT, &act, NULL); - if (sigint_bound == -1) { perror("daemon signal capture"); return 0; } + if (sigint_bound == -1) { vscl_os_perror("Daemon signal capture"); return 0; } return 1; #endif // _WIN32: Setup signal handler } // interrupt intercept maker @@ -87,9 +85,9 @@ int seek_daemon(struct sockaddr_un* sockaddr) { return 1; } // int seek_daemon(struct sockaddir_un* sockaddr) -// }}} fold +// }}} Daemon Process and Socket Identification -int testrig_daemon([[maybe_unused]] other_args* others) { +int testrig_daemon(other_args* others) { int retstat = 0; struct sockaddr_un sockaddr = { .sun_family = AF_UNIX }; @@ -98,7 +96,26 @@ int testrig_daemon([[maybe_unused]] other_args* others) { socklen_t socksize = sizeof(sockaddr); int sought = seek_daemon(&sockaddr); - if (sought) { fprintf(stderr, "error: daemon already running\n"); return -1; } + + if (others != NULL && others->data != NULL + && !strncmp(others->data[0], "down", 6)) { + + vscl_byte_t body[8] = "DOWN"; + struct rig_message msg = { 0 }; + + vscl_set_message(&msg, HEAD_SYNC, body); + int conn = vscl_sock_connect(sock, &sockaddr); + if (conn == -1) { return -1; } + + int sent = vscl_sock_send(sock, &msg); + if (sent == -1) { return -1; } + + return 0; + } + else if (sought) { + fprintf(stderr, "Error: Daemon already running\n"); + return -1; + } if (!deploy_interrupt_cleanup(sock, &sockaddr)) { vscl_sock_close(sock, &sockaddr); @@ -117,11 +134,16 @@ int testrig_daemon([[maybe_unused]] other_args* others) { return -1; } - printf("waiting for connection...\n"); + printf("Waiting for connection...\n"); DAEMON_CURRENT_STATUS = TESTRIG_DAEMON_LISTENING; while (DAEMON_CURRENT_STATUS == TESTRIG_DAEMON_LISTENING) { int accepted = accept(sock, (struct sockaddr*)&sockaddr, &socksize); - if (accepted == -1) { perror("daemon accept failure"); continue; } + if (accepted == -1) { + if (DAEMON_CURRENT_STATUS != TESTRIG_DAEMON_CLEANING) + vscl_os_perror("Daemon accept failure"); + + continue; + } vscl_byte_t buf[12] = { 0 }; @@ -130,17 +152,25 @@ int testrig_daemon([[maybe_unused]] other_args* others) { #else int recvd = read(accepted, buf, 12); #endif - if (recvd != 12) { perror("did not read full msg"); continue; } + if (recvd != 12) { + if (DAEMON_CURRENT_STATUS == TESTRIG_DAEMON_LISTENING + || DAEMON_CURRENT_STATUS == TESTRIG_DAEMON_CONNECTED) { + vscl_os_perror("Did not read full msg"); + } + + continue; + } vscl_byte_t body[8] = { 0 }; for (uint8_t i = 4; i < 12; i++) { body[i - 4] = buf[i]; } - if (!strncmp("STATUS", body, 7)) { testrig_stat(NULL); } - else if (!strncmp("OPEN", body, 5)) { testrig_open(NULL); } - else if (!strncmp("REQUEST", body, 7)) { testrig_request(NULL); } - else if (!strncmp("CLOSE", body, 6)) { testrig_close(NULL); } + if (!strncmp("STATUS", body, 7)) { testrig_stat(NULL); } + else if (!strncmp("OPEN", body, 5)) { testrig_open(NULL); } + else if (!strncmp("REQUEST", body, 7)) { testrig_peek(NULL); } + else if (!strncmp("CLOSE", body, 6)) { testrig_close(NULL); } + else if (!strncmp("DOWN", body, 5)) { DAEMON_CURRENT_STATUS = TESTRIG_DAEMON_CLEANING; } } vscl_sock_close(sock, &sockaddr); diff --git a/src/help.c b/src/help.c index 439c561..f8233ee 100644 --- a/src/help.c +++ b/src/help.c @@ -14,9 +14,9 @@ int help_me(other_args* which) { else if (!strncmp("stat", which_one, 5)) { printhelp_stat(); return 0; } else if (!strncmp("daemon", which_one, 7)) { printhelp_daemon(); return 0; } else if (!strncmp("open", which_one, 5)) { printhelp_open(); return 0; } - else if (!strncmp("request", which_one, 7)) { printhelp_request(); return 0; } + else if (!strncmp("peek", which_one, 7)) { printhelp_peek(); return 0; } else if (!strncmp("close", which_one, 6)) { printhelp_close(); return 0; } - else { printf("invalid target for \"help\"\n"); return 1; } + else { printf("invalid target for \"help\": \"%s\"\n", which_one); return 1; } } void print_usage(void) { @@ -24,15 +24,15 @@ void print_usage(void) { print_hline(5); printf("Options:\n" "--detach Detach the process from the shell.\n" - "--daemon Operate in daemon mode.\n" "--verbose Operate verbosely\n"); print_hline(5); printf("Actions:\n" "help Print out detailed help for a specific action.\n" + "daemon Operate on the daemon.\n" "ident Identify connected controllers.\n" "stat Determine the status of connected controllers.\n" "open Open connection to a device.\n" - "request Request a CAN frame and print it out to stdout.\n" + "peek Request a CAN frame and print it out to stdout.\n" "close Close connection to device and release it.\n"); printf("\n"); } @@ -59,7 +59,7 @@ void printhelp_stat(void) { } void printhelp_daemon(void) { - printf("testrig daemon mode\n"); + printf("testrig daemon\n"); print_hline(5); printf("The process serves as a mediator between the controllers\n" "and other processes through an opened Unix socket,\n" @@ -75,8 +75,8 @@ void printhelp_open(void) { "\n"); } -void printhelp_request(void) { - printf("testrig request\n"); +void printhelp_peek(void) { + printf("testrig peek\n"); print_hline(5); printf("Request a CAN frame and send it to stdout." "\n"); diff --git a/src/help.h b/src/help.h index 11eccaf..c794d97 100644 --- a/src/help.h +++ b/src/help.h @@ -11,5 +11,5 @@ void printhelp_ident(void); void printhelp_stat(void); void printhelp_daemon(void); void printhelp_open(void); -void printhelp_request(void); +void printhelp_peek(void); void printhelp_close(void); diff --git a/src/main.c b/src/main.c index b4f74ed..8e768c9 100644 --- a/src/main.c +++ b/src/main.c @@ -30,7 +30,7 @@ int main(int argc, char** argv) { } if (parsed.fun == NULL) { - printf("unimplemented action\n"); + printf("Unimplemented action\n"); } else if (ret == 0) { ret = parsed.fun(&others); diff --git a/stupid.txt b/stupid.txt deleted file mode 100644 index 2b68643..0000000 --- a/stupid.txt +++ /dev/null @@ -1,4 +0,0 @@ -testrigd not running; creating new testrig process at 47324 -Waiting for connection... -Accepted -Stopping... diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..6a0f0b8 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,18 @@ +enable_testing() + +function(make_unit_test OUT_TGT_NAME IN_SOURCES) + if (${ARGC} GREATER 2) + set(_other_args ${ARGV2}) + else() + set(_other_args) + endif() + + add_executable(${OUT_TGT_NAME} ${IN_SOURCES}) + target_link_libraries(${OUT_TGT_NAME} PRIVATE libtestrig) + target_copy_dll(${OUT_TGT_NAME}) + target_compile_warn_all(${OUT_TGT_NAME}) + add_test(NAME "${OUT_TGT_NAME}_test" COMMAND $ ${_other_args}) +endfunction(make_unit_test OUT_TGT_NAME IN_SOURCES) + +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/ipc") +add_subdirectory("${CMAKE_CURRENT_LIST_DIR}/cli") \ No newline at end of file diff --git a/test/cli/CMakeLists.txt b/test/cli/CMakeLists.txt new file mode 100644 index 0000000..4d8b940 --- /dev/null +++ b/test/cli/CMakeLists.txt @@ -0,0 +1 @@ +make_unit_test(daemon_interrupting "${CMAKE_CURRENT_LIST_DIR}/daemon_interrupting.c" $) \ No newline at end of file diff --git a/test/cli/daemon_interrupting.c b/test/cli/daemon_interrupting.c new file mode 100644 index 0000000..326b8d2 --- /dev/null +++ b/test/cli/daemon_interrupting.c @@ -0,0 +1,38 @@ +#include +#include + +#include "ipc/os.h" + +int main(int argc, char** argv) { + if (argc < 2) { fprintf(stderr, "This test requires arguments.\n"); return -1; } + + printf("Starting the daemon...\n"); + int made = vscl_make_new_proc(argv[1], "daemon"); + + if (made != 0 && made != -1) { + printf("Yielding for init...\n"); + vscl_sleep(5); + + printf("Killing the daemon...\n"); + int made2 = vscl_make_new_proc(argv[1], "daemon down"); + vscl_sleep(2); + + char sockpath[108] = { 0 }; + int making_path = vscl_get_sock_destination(sockpath); + if (making_path) { fprintf(stderr, + "Failed to get sock destination for your platform. Did an upstream test fail?\n"); return -1; } + + strncat(sockpath, "testrigd.sock", 15); + + FILE* potential_sock = fopen(sockpath, "r"); + int not_there = potential_sock == NULL; + + return (made != -1 && made2 != -1 && not_there) ? 0 : -1; + } + else if (made == -1) { + vscl_os_perror("Could not make the process.\n"); + return -1; + } + + return 0; +} diff --git a/test/ipc/CMakeLists.txt b/test/ipc/CMakeLists.txt new file mode 100644 index 0000000..0dd74dc --- /dev/null +++ b/test/ipc/CMakeLists.txt @@ -0,0 +1,3 @@ +make_unit_test(sock_naming "${CMAKE_CURRENT_LIST_DIR}/sock_naming.c") +make_unit_test(sock_communicating "${CMAKE_CURRENT_LIST_DIR}/sock_communicating.c" "parent") +make_unit_test(sock_quickopening "${CMAKE_CURRENT_LIST_DIR}/sock_quickopening.c") \ No newline at end of file diff --git a/test/ipc/sock_communicating.c b/test/ipc/sock_communicating.c new file mode 100644 index 0000000..f5cc441 --- /dev/null +++ b/test/ipc/sock_communicating.c @@ -0,0 +1,112 @@ +#include "ipc/sock.h" +#include "ipc/os.h" +#include + +static const char* TESTER_SOCK_NAME = "TEST_SOCK.rigsock"; // NOLINT + +int main(int argc, char** argv) { + if (argc < 2) { fprintf(stderr, "You must pass arguments to this test.\n"); return -1; } + + printf("This was invoked with the following command line:\n"); + printf("\t%s %s\n", argv[0], argv[1]); + fclose(stdin); + + char sockpath[108] = { 0 }; + int retstat = vscl_get_sock_destination(sockpath); + if (retstat != 0) { + fprintf(stderr, + "While preparing the sockets, socket destination grabbing failed.\n"); + return retstat; + } + + strncat(sockpath, TESTER_SOCK_NAME, 19); + struct sockaddr_un sockaddr = { .sun_family = AF_UNIX, }; + strncpy(sockaddr.sun_path, sockpath, 108); + + if (strncmp(argv[1], "parent", 7) == 0) { + int forkstat = vscl_make_new_proc(argv[0], "child"); + if (forkstat == -1) { fprintf(stderr, "Failure to launch\n"); return forkstat; } + else { printf("New process was spawned with PID %i\n", forkstat); } + + int parented = vscl_sock_setup(&sockaddr); + if (parented == INVALID_SOCKET) { + fprintf(stderr, "Parent sock maker fail\n"); + return -1; + } + + int parentstat = vscl_sock_bind(parented, &sockaddr); + if (parentstat == -1) { + fprintf(stderr, "Sock binder fail\n"); + int closestat = vscl_sock_close(parented, &sockaddr); + return (!closestat) ? closestat : parentstat; + } + + parentstat = vscl_sock_listen(parented, 1); + if (parentstat == -1) { + fprintf(stderr, "Sock listener fail at sockpath %s\n", sockaddr.sun_path); + int closestat = vscl_sock_close(parented, &sockaddr); + return (!closestat) ? closestat : parentstat; + } + + int exitplz = 0; + socklen_t socklen = (socklen_t)sizeof(struct sockaddr); + while (!exitplz) { + int acceptor = accept(parented, (struct sockaddr*)&sockaddr, &socklen); + if (acceptor == -1) { fprintf(stderr, "Accept epic fail\n"); continue; } + + vscl_byte_t buf[12] = { 0 }; + +#ifdef _WIN32 + int recvd = recv(acceptor, buf, 12, MSG_PEEK); +#else + int recvd = read(acceptor, buf, 12); +#endif + if (recvd != 12) { + fprintf(stderr, "Failure to read full msg: %i out of 12\n", recvd); + vscl_sock_close(parented, &sockaddr); + return (recvd == 0) ? -1 : recvd; + } + + printf("Success: The message I got was %s\n", buf); + assert(strncmp(buf, "HOWDY WORLD", 12) == 0); + + exitplz = 1; + } + + return vscl_sock_close(parented, &sockaddr); + } + else if (strncmp(argv[1], "child", 6) == 0) { + vscl_sleep(5); // arbitrary + + struct sockaddr_un childsock = { 0 }; + int childed = vscl_sock_setup(&childsock); + if (childed == INVALID_SOCKET) { fprintf(stderr, "Child sock maker fail\n"); return -1; } + else { printf("Child socket created at %s\n", childsock.sun_path); } + + int connection = vscl_sock_connect(childed, &sockaddr); + if (connection == -1) { + fprintf(stderr, "Child connection fail\n"); + vscl_sock_close(childed, &childsock); + int closestat = vscl_sock_close(childed, &sockaddr); + return (!closestat) ? closestat : connection; + } + + struct rig_message msg = { {'H', 'O', 'W', 'D'}, {'Y', ' ', 'W', 'O', 'R', 'L', 'D', 0} }; + int sent = vscl_sock_send(childed, &msg); + if (sent != 12) { + fprintf(stderr, "Failure to send all bytes: %i out of 12\n", sent); + vscl_sock_close(childed, &childsock); + int closestat = vscl_sock_close(childed, &sockaddr); + return (!closestat) ? closestat : -1; + } + else { + printf("Sent all bytes.\n"); + } + + return vscl_sock_close(childed, &childsock); + } + else { + fprintf(stderr, "Invalid token: %s\n", argv[1]); + return -1; + } +} diff --git a/test/ipc/sock_naming.c b/test/ipc/sock_naming.c new file mode 100644 index 0000000..c712a1b --- /dev/null +++ b/test/ipc/sock_naming.c @@ -0,0 +1,34 @@ +#include +#include +#include + +#include "ipc/sock.h" +#include "ipc/os.h" + +int main(void) { + printf("Begin socket naming tests.\n"); + int retstat = 0; + + char sockdest[108] = { 0 }; + retstat = vscl_get_sock_destination(sockdest); + if (retstat != 0) { fprintf(stderr, "Failed to find socket path: sockpath became %s\n", sockdest); return retstat; } + + printf("Sockets will go to %s\n", sockdest); + memset(sockdest, 0, 1); + retstat = vscl_sock_genpath(sockdest); + if (retstat != 0) { fprintf(stderr, "Could not generate a proper path for the socket.\n"); return retstat; } + + char new_sockdest[108] = { 0 }; + retstat = vscl_sock_genpath(new_sockdest); + if (retstat != 0) { fprintf(stderr, "Could not generate a proper path for the fresh socket.\n"); return retstat; } + + printf("The socket path was randomly: %s\n", sockdest); + printf("The fresh socket path was randomly: %s\n", new_sockdest); + + // Ensure no early truncation + assert(strstr(sockdest, ".rigsock") != NULL); + assert(strstr(new_sockdest, ".rigsock") != NULL); + printf("Success: The socket path was not truncated.\n"); + + return 0; +} diff --git a/test/ipc/sock_quickopening.c b/test/ipc/sock_quickopening.c new file mode 100644 index 0000000..8af7cdf --- /dev/null +++ b/test/ipc/sock_quickopening.c @@ -0,0 +1,17 @@ +#include "ipc/sock.h" +#include "ipc/os.h" +#include + + +int main(int argc, char** argv) { + struct sockaddr_un sockaddr = { 0 }; + int sock = vscl_sock_setup(&sockaddr); + if (sock == INVALID_SOCKET) { vscl_os_perror("Socket quickopen fail: setup"); return -1; } + else { printf("Sock created for quickopen at %s\n", sockaddr.sun_path); } + + int bound = vscl_sock_bind(sock, &sockaddr); + if (bound == -1) { vscl_os_perror("Socket quickopen fail: bind"); return -1; } + + printf("Goodbye!\n"); + return vscl_sock_close(sock, &sockaddr); +} \ No newline at end of file