diff --git a/CMakeLists.txt b/CMakeLists.txt index aafe031..61d2a19 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,6 +25,7 @@ add_subdirectory(memory-map) add_subdirectory(mutex) add_subdirectory(queue) add_subdirectory(perf) +add_subdirectory(signal) add_subdirectory(socket) add_subdirectory(string) add_subdirectory(time) diff --git a/array/array_test.c b/array/array_test.c index 9a7e8f0..1f3d76a 100644 --- a/array/array_test.c +++ b/array/array_test.c @@ -90,6 +90,7 @@ void *__wrap_realloc(void *p, size_t n) void fail_test() { + int tmp; int *arr, total = 0; assert(sc_array_create(arr, SIZE_MAX) == false); @@ -160,6 +161,12 @@ void fail_test() assert(arr[i] == i); } + total = 0; + sc_array_foreach(arr, tmp) { + total += tmp; + } + assert(total == 10); + sc_array_destroy(arr); } #else diff --git a/math/sc_math.c b/math/sc_math.c index c932dbd..21e8b51 100644 --- a/math/sc_math.c +++ b/math/sc_math.c @@ -37,7 +37,7 @@ #elif (SIZE_MAX == 0xFFFFFFFFFFFFFFFF) #define SIZE_T_BITS 64 #else - #error TBD code SIZE_T_BITS + #error unknown size_t bits #endif bool sc_math_is_pow2(size_t num) diff --git a/signal/CMakeLists.txt b/signal/CMakeLists.txt new file mode 100644 index 0000000..9cd7fd7 --- /dev/null +++ b/signal/CMakeLists.txt @@ -0,0 +1,117 @@ +cmake_minimum_required(VERSION 3.5.1) +project(sc_signal C) + +set(CMAKE_C_STANDARD 99) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +add_executable(sc_signal signal_example.c sc_signal.h sc_signal.c) + +if (NOT CMAKE_C_COMPILER_ID MATCHES "MSVC") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -g -Wall -pedantic -Werror -D_GNU_SOURCE") +endif () + + +# --------------------------------------------------------------------------- # +# --------------------- Test Configuration Start ---------------------------- # +# --------------------------------------------------------------------------- # + +include(CTest) +include(CheckCCompilerFlag) + +enable_testing() + +add_executable(${PROJECT_NAME}_test signal_test.c sc_signal.c) + +target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_SIZE_MAX=1400000ul) + +check_c_source_compiles(" + #include + #include + + int main(int argc, char **argv) { + void *array[10]; + size_t size = backtrace(array, 10); + backtrace_symbols_fd(array, size, STDERR_FILENO); + return 0; +}" HAVE_BACKTRACE) + +FIND_LIBRARY(EXECINFO_LIBRARY NAMES execinfo) +IF (EXECINFO_LIBRARY) + SET(CMAKE_REQUIRED_LIBRARIES "${EXECINFO_LIBRARY}") +ENDIF(EXECINFO_LIBRARY) + +if (${HAVE_BACKTRACE}) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DHAVE_BACKTRACE") +endif () + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + + target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer) + target_compile_options(${PROJECT_NAME}_test PRIVATE -DSC_HAVE_WRAP) + target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-builtin) + #target_link_options(${PROJECT_NAME}_test PRIVATE -Wl,--wrap=realloc) + endif () +endif () + +if ("${CMAKE_C_COMPILER_ID}" STREQUAL "Clang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "AppleClang" OR + "${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + + target_compile_options(${PROJECT_NAME}_test PRIVATE -fno-omit-frame-pointer) + + if (SANITIZER) + target_compile_options(${PROJECT_NAME}_test PRIVATE -fsanitize=${SANITIZER}) + target_link_options(${PROJECT_NAME}_test PRIVATE -fsanitize=${SANITIZER}) + endif () +endif () + + +add_test(NAME ${PROJECT_NAME}_test COMMAND ${PROJECT_NAME}_test) + +SET(MEMORYCHECK_COMMAND_OPTIONS + "-q --log-fd=2 --trace-children=yes --track-origins=yes \ + --leak-check=full --show-leak-kinds=all \ + --error-exitcode=255") + +add_custom_target(valgrind_${PROJECT_NAME} ${CMAKE_COMMAND} + -E env CTEST_OUTPUT_ON_FAILURE=1 + ${CMAKE_CTEST_COMMAND} -C $ + --overwrite MemoryCheckCommandOptions=${MEMORYCHECK_COMMAND_OPTIONS} + --verbose -T memcheck WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +add_custom_target(check_${PROJECT_NAME} ${CMAKE_COMMAND} + -E env CTEST_OUTPUT_ON_FAILURE=1 + ${CMAKE_CTEST_COMMAND} -C $ --verbose + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +# ----------------------- - Code Coverage Start ----------------------------- # + +if (${CMAKE_BUILD_TYPE} MATCHES "Coverage") + if ("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU") + target_compile_options(${PROJECT_NAME}_test PRIVATE --coverage) + target_link_libraries(${PROJECT_NAME}_test gcov) + else () + message(FATAL_ERROR "Only GCC is supported for coverage") + endif () +endif () + +add_custom_target(coverage_${PROJECT_NAME}) +add_custom_command( + TARGET coverage_${PROJECT_NAME} + COMMAND lcov --capture --directory .. + --output-file coverage.info --rc lcov_branch_coverage=1 + COMMAND lcov --remove coverage.info '/usr/*' '*example*' '*test*' + --output-file coverage.info --rc lcov_branch_coverage=1 + COMMAND lcov --list coverage.info --rc lcov_branch_coverage=1 +) + +add_dependencies(coverage_${PROJECT_NAME} check_${PROJECT_NAME}) + +# -------------------------- Code Coverage End ------------------------------ # + + +# ----------------------- Test Configuration End ---------------------------- # + diff --git a/signal/sc_signal.c b/signal/sc_signal.c new file mode 100644 index 0000000..fad7bc3 --- /dev/null +++ b/signal/sc_signal.c @@ -0,0 +1,495 @@ +/* + * MIT License + * + * Copyright (c) 2020 Ozan Tezcan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "sc_signal.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +#define get_uint(va, size) \ + (size) == 3 ? va_arg(va, unsigned long long) : \ + (size) == 2 ? va_arg(va, unsigned long) : \ + (size) == 1 ? va_arg(va, unsigned int) : \ + 0 + +#define get_int(va, size) \ + (size) == 3 ? va_arg(va, long long) : \ + (size) == 2 ? va_arg(va, long) : \ + (size) == 1 ? va_arg(va, int) : \ + 0 + +int sc_signal_vsnprintf(char *buf, size_t size, const char *fmt, va_list va) +{ + size_t len; + size_t out_cap = size == 0 ? 0 : size - 1; + char *str; + char *out = buf; + char *pos = (char *) fmt; + char digits[32]; + + while (true) { + char *orig = pos; + + switch (*pos) { + case '\0': { + *out = '\0'; + return (int) (out - buf); + } + case '%': { + pos++; + switch (*pos) { + case 's': + str = (char *) va_arg(va, const char *); + str = (str == NULL) ? "(null)" : str; + len = strlen(str); + break; + case 'l': + pos += (*(pos + 1) == 'l') ? 2 : 1; + case 'd': + case 'u': + len = 0; + + if (*pos == 'u') { + uint64_t u64 = get_uint(va, pos - orig); + + do { + digits[31 - (len++)] = '0' + (u64 % 10); + } while (u64 /= 10UL); + + } else if (*pos == 'd') { + int64_t i64 = get_int(va, pos - orig); + uint64_t u64 = i64 < 0 ? -i64 : i64; + + do { + digits[31 - (len++)] = '0' + (u64 % 10); + } while (u64 /= 10UL); + + if (i64 < 0) { + digits[31 - (len++)] = '-'; + } + } else { + return -1; + } + + str = &digits[32 - len]; + break; + case 'p': { + char *arr = "0123456789abcdef"; + len = 0; + int s = (sizeof(void *) == sizeof(unsigned long long)) ? 3 : 2; + uint64_t u64 = get_uint(va, s); + + do { + digits[31 - (len++)] = arr[u64 % 16]; + } while (u64 /= 16UL); + + digits[31 - (len++)] = 'x'; + digits[31 - (len++)] = '0'; + str = &digits[32 - len]; + } break; + case '%': + str = "%"; + len = 1; + break; + default: + return -1; + } + pos++; + } break; + default: { + while (*pos != '\0' && *pos != '%') { + pos++; + } + str = orig; + len = pos - orig; + break; + } + } + + len = len < out_cap ? len : out_cap; + memcpy(out, str, len); + + out += len; + out_cap -= len; + } +} + +int sc_signal_snprintf(char *buf, size_t size, const char *fmt, ...) +{ + int ret; + va_list args; + + va_start(args, fmt); + ret = sc_signal_vsnprintf(buf, size, fmt, args); + va_end(args); + + return ret; +} + +#if defined(_WIN32) + + #define WIN32_LEAN_AND_MEAN + #include + #include + #include + #include + + #pragma warning(disable : 4996) + +static void sc_signal_log(int fd, char *buf, size_t len, char *fmt, ...) +{ + int written; + va_list args; + + va_start(args, fmt); + written = sc_signal_vsnprintf(buf, len, fmt, args); + va_end(args); + + write(fd, buf, written); +} + +BOOL WINAPI sc_console_handler(DWORD type) +{ + int rc; + char *err; + char buf[128]; + int fd = sc_signal_log_fd != -1 ? sc_signal_log_fd : _fileno(stdout); + + switch (type) { + case CTRL_C_EVENT: + err = "CTRL_C event"; + break; + case CTRL_BREAK_EVENT: + err = "CTRL_BREAK event"; + break; + default: + sc_signal_log(fd, buf, sizeof(buf), + "Unknown console event [%d], shutting down! \n", type); + _Exit(1); + } + + sc_signal_log(fd, buf, sizeof(buf), "Received : %s, (%d) \n", err, type); + + if (sc_signal_will_shutdown != 0) { + sc_signal_log(fd, buf, sizeof(buf), "Forcing shut down! \n"); + _Exit(1); + } + + sc_signal_will_shutdown = 1; + + if (sc_signal_shutdown_fd != INVALID_SOCKET) { + sc_signal_log(fd, buf, sizeof(buf), "Sending shutdown command. \n"); + rc = send(sc_signal_shutdown_fd, (void *) &(int){1}, 1, 0); + if (rc != 1) { + sc_signal_log(fd, buf, sizeof(buf), + "Failed to send shutdown command, " + "shutting down immediately! \n"); + _Exit(1); + } + } else { + sc_signal_log(fd, buf, sizeof(buf), + "No shutdown handler, shutting down! \n"); + _Exit(0); + } + + + return TRUE; +} + +LONG WINAPI sc_signal_on_fatal(PEXCEPTION_POINTERS info) +{ + char buf[128]; + int fd = sc_signal_log_fd != -1 ? sc_signal_log_fd : _fileno(stderr); + + sc_signal_log(fd, buf, sizeof(buf), "Fatal signal : %d, shutting down! \n", + info->ExceptionRecord->ExceptionCode); + + return 0; +} + +void sc_signal_std_on_fatal(int type) +{ + char buf[128]; + int fd = sc_signal_log_fd != -1 ? sc_signal_log_fd : _fileno(stderr); + const char *sig_str; + + switch (type) { + case SIGSEGV: + sig_str = "SIGSEGV"; + break; + case SIGABRT: + sig_str = "SIGABRT"; + break; + case SIGFPE: + sig_str = "SIGFPE"; + break; + case SIGILL: + sig_str = "SIGILL"; + break; + default: + sig_str = "Unknown signal"; + break; + } + + sc_signal_log(fd, buf, sizeof(buf), + "Fatal signal : [%s][%d], shutting down! \n", sig_str, type); + + _Exit(1); +} + +void sc_signal_std_on_shutdown(int type) +{ + sc_console_handler(CTRL_C_EVENT); +} +int sc_signal_init() +{ + BOOL b; + sc_signal_log_fd = -1; + sc_signal_shutdown_fd = -1; + + b = SetConsoleCtrlHandler(sc_console_handler, TRUE); + if (!b) { + return -1; + } + + SetUnhandledExceptionFilter(sc_signal_on_fatal); + signal(SIGABRT, sc_signal_std_on_fatal); + signal(SIGINT, sc_signal_std_on_shutdown); + + return 0; +} + +#else + +// clang-format off +#include + +#ifdef HAVE_BACKTRACE +#include + +static void *sc_instruction(ucontext_t *uc) +{ + void* insp = NULL; + + #if defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6) + #if defined(_STRUCT_X86_THREAD_STATE64) && !defined(__i386__) + insp = (void *) uc->uc_mcontext->__ss.__rip; + #elif defined(__i386__) + insp = (void *) uc->uc_mcontext->__ss.__eip; + #else + insp = (void *) arm_thread_state64_get_pc(uc->uc_mcontext->__ss); + #endif + #elif defined(__linux__) + #if defined(__i386__) || ((defined(__x86_64__)) && defined(__ILP32__)) + insp = (void *) uc->uc_mcontext.gregs[REG_EIP]; + #elif defined(__x86_64__) + insp = (void *) uc->uc_mcontext.gregs[REG_RIP]; + #elif defined(__ia64__) + insp = (void *) uc->uc_mcontext.sc_ip; + #elif defined(__arm__) + insp = (void *) uc->uc_mcontext.arm_pc; + #elif defined(__aarch64__) + insp = (void *) uc->uc_mcontext.pc; + #endif + #elif defined(__FreeBSD__) + #if defined(__i386__) + insp = (void *) uc->uc_mcontext.mc_eip; + #elif defined(__x86_64__) + insp = (void *) uc->uc_mcontext.mc_rip; + #endif + #elif defined(__OpenBSD__) + #if defined(__i386__) + insp = (void *) uc->sc_eip; + #elif defined(__x86_64__) + insp = (void *) uc->sc_rip; + #endif + #elif defined(__NetBSD__) + #if defined(__i386__) + insp = (void *) uc->uc_mcontext.__gregs[_REG_EIP]; + #elif defined(__x86_64__) + insp = (void *) uc->uc_mcontext.__gregs[_REG_RIP]; + #endif + #elif defined(__DragonFly__) + insp = (void *) uc->uc_mcontext.mc_rip; + #endif + + return insp; +} + +#endif + +// clang-format on + +static void sc_signal_log(int fd, char *buf, size_t len, char *fmt, ...) +{ + int written; + va_list args; + + va_start(args, fmt); + written = sc_signal_vsnprintf(buf, len, fmt, args); + va_end(args); + + write(fd, buf, written); +} + +static void sc_signal_on_shutdown(int sig) +{ + int rc; + int fd = sc_signal_log_fd != -1 ? sc_signal_log_fd : STDOUT_FILENO; + char buf[4096], *sig_str; + + switch (sig) { + case SIGINT: + sig_str = "SIGINT"; + break; + case SIGTERM: + sig_str = "SIGTERM"; + break; + default: + sig_str = "Shutdown signal"; + break; + } + + sc_signal_log(fd, buf, sizeof(buf), "Received : %s, (%d) \n", sig_str, sig); + + if (sc_signal_will_shutdown != 0) { + sc_signal_log(fd, buf, sizeof(buf), "Forcing shut down! \n"); + _Exit(1); + } + + sc_signal_will_shutdown = 1; + + if (sc_signal_shutdown_fd != -1) { + sc_signal_log(fd, buf, sizeof(buf), "Sending shutdown command. \n"); + rc = write(sc_signal_shutdown_fd, (void *) &(int){1}, 1); + if (rc != 1) { + sc_signal_log(fd, buf, sizeof(buf), + "Failed to send shutdown command, " + "shutting down immediately! \n"); + _Exit(1); + } + } else { + sc_signal_log(fd, buf, sizeof(buf), + "No shutdown handler, shutting down! \n"); + _Exit(0); + } +} + +static void sc_signal_on_fatal(int sig, siginfo_t *info, void *context) +{ + int fd = sc_signal_log_fd != -1 ? sc_signal_log_fd : STDERR_FILENO; + + char buf[4096], *sig_str; + struct sigaction act; + + switch (sig) { + case SIGSEGV: + sig_str = "SIGSEGV"; + break; + case SIGABRT: + sig_str = "SIGABRT"; + break; + case SIGBUS: + sig_str = "SIGBUS"; + break; + case SIGFPE: + sig_str = "SIGFPE"; + break; + case SIGILL: + sig_str = "SIGILL"; + break; + default: + sig_str = "Unknown signal"; + break; + } + + sc_signal_log(fd, buf, sizeof(buf), "\nSignal received : [%d][%s] \n", sig, + sig_str); + + sc_signal_log(fd, buf, sizeof(buf), + "\n----------------- CRASH REPORT ---------------- \n"); + +#ifdef HAVE_BACKTRACE + void *caller = sc_instruction((ucontext_t *) context); + int trace_size; + void *trace[100]; + + sc_signal_log(fd, buf, sizeof(buf), "\n Caller [%p] \n\n", caller); + + trace_size = backtrace(trace, 100); + backtrace_symbols_fd(trace, trace_size, fd); +#endif + sc_signal_log(fd, buf, sizeof(buf), + "\n--------------- CRASH REPORT END -------------- \n"); + + sc_signal_log(fd, buf, sizeof(buf), "\nSignal handler completed! \n"); + close(fd); + + sigemptyset(&act.sa_mask); + act.sa_flags = SA_NODEFER | SA_ONSTACK | SA_RESETHAND; + act.sa_handler = SIG_DFL; + sigaction(sig, &act, NULL); + + kill(getpid(), sig); +} + +int sc_signal_init() +{ + bool rc = true; + struct sigaction action; + + sc_signal_log_fd = -1; + sc_signal_shutdown_fd = -1; + + rc &= (signal(SIGHUP, SIG_IGN) != SIG_ERR); + rc &= (signal(SIGPIPE, SIG_IGN) != SIG_ERR); + rc &= (sigemptyset(&action.sa_mask) == 0); + + action.sa_flags = 0; + action.sa_handler = sc_signal_on_shutdown; + + rc &= (sigaction(SIGTERM, &action, NULL) == 0); + rc &= (sigaction(SIGINT, &action, NULL) == 0); + + rc &= (sigemptyset(&action.sa_mask) == 0); + action.sa_flags = SA_NODEFER | SA_RESETHAND | SA_SIGINFO; + action.sa_sigaction = sc_signal_on_fatal; + + rc &= (sigaction(SIGABRT, &action, NULL) == 0); + rc &= (sigaction(SIGSEGV, &action, NULL) == 0); + rc &= (sigaction(SIGBUS, &action, NULL) == 0); + rc &= (sigaction(SIGFPE, &action, NULL) == 0); + rc &= (sigaction(SIGILL, &action, NULL) == 0); + + return rc ? 0 : -1; +} + + +#endif diff --git a/signal/sc_signal.h b/signal/sc_signal.h new file mode 100644 index 0000000..ba64076 --- /dev/null +++ b/signal/sc_signal.h @@ -0,0 +1,45 @@ +/* + * MIT License + * + * Copyright (c) 2020 Ozan Tezcan + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef SC_SIGNAL_H +#define SC_SIGNAL_H + +#include +#include + +#if defined(_WIN32) + #include +volatile SOCKET sc_signal_shutdown_fd; +#else +volatile sig_atomic_t sc_signal_shutdown_fd; +#endif + +volatile sig_atomic_t sc_signal_log_fd; +volatile sig_atomic_t sc_signal_will_shutdown; + +int sc_signal_init(); +int sc_signal_vsnprintf(char *buf, size_t size, const char *fmt, va_list va); +int sc_signal_snprintf(char *buf, size_t size, const char *fmt, ...); + +#endif diff --git a/signal/signal_example.c b/signal/signal_example.c new file mode 100644 index 0000000..21edf3d --- /dev/null +++ b/signal/signal_example.c @@ -0,0 +1,7 @@ +#include "sc_signal.h" + +int main() +{ + + return 0; +} diff --git a/signal/signal_test.c b/signal/signal_test.c new file mode 100644 index 0000000..ef1ece6 --- /dev/null +++ b/signal/signal_test.c @@ -0,0 +1,280 @@ +#include "sc_signal.h" + +#include +#include +#include +#include +#include +#include +#include + +#if defined(_WIN32) || defined(_WIN64) + #include + #include + #include + #pragma comment(lib, "ws2_32.lib") + +typedef SOCKET sc_sock_int; + +#else + #include + #include +typedef int sc_sock_int; +#endif + +enum sc_sock_ev +{ + SC_SOCK_NONE = 0u, + SC_SOCK_READ = 1u, + SC_SOCK_WRITE = 2u, +}; + +enum sc_sock_family +{ + SC_SOCK_INET = AF_INET, + SC_SOCK_INET6 = AF_INET6, + SC_SOCK_UNIX = AF_UNIX +}; + +struct sc_sock_fd +{ + sc_sock_int fd; + enum sc_sock_ev op; + int type; + int index; +}; + +struct sc_sock_pipe +{ + struct sc_sock_fd fdt; + sc_sock_int fds[2]; +}; + +#if defined(_WIN32) || defined(_WIN64) + +int sc_sock_pipe_init(struct sc_sock_pipe *p, int type) +{ + SOCKET listener; + int rc; + struct sockaddr_in addr; + int addrlen = sizeof(addr); + int val = 1; + BOOL nodelay = 1; + + p->fdt.type = type; + p->fds[0] = INVALID_SOCKET; + p->fds[1] = INVALID_SOCKET; + + /* Create listening socket. */ + listener = socket(AF_INET, SOCK_STREAM, 0); + if (listener == SOCKET_ERROR) { + goto wsafail; + } + + rc = setsockopt(listener, SOL_SOCKET, SO_EXCLUSIVEADDRUSE, (char *) &val, + sizeof(val)); + if (rc == SOCKET_ERROR) { + goto wsafail; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + addr.sin_port = 0; + + rc = bind(listener, (const struct sockaddr *) &addr, sizeof(addr)); + if (rc == SOCKET_ERROR) { + goto wsafail; + } + + rc = getsockname(listener, (struct sockaddr *) &addr, &addrlen); + if (rc == SOCKET_ERROR) { + goto wsafail; + } + + rc = listen(listener, 1); + if (rc == SOCKET_ERROR) { + goto wsafail; + } + + p->fds[1] = socket(AF_INET, SOCK_STREAM, 0); + if (p->fds[1] == SOCKET_ERROR) { + goto wsafail; + } + + rc = setsockopt(p->fds[1], IPPROTO_TCP, TCP_NODELAY, (char *) &nodelay, + sizeof(nodelay)); + if (rc == SOCKET_ERROR) { + goto wsafail; + } + + rc = connect(p->fds[1], (struct sockaddr *) &addr, sizeof(addr)); + if (rc == SOCKET_ERROR) { + goto wsafail; + } + + p->fds[0] = accept(listener, (struct sockaddr *) &addr, &addrlen); + if (p->fds[0] == INVALID_SOCKET) { + goto wsafail; + } + + closesocket(listener); + + return 0; + +wsafail: + return -1; +} + +int sc_sock_pipe_term(struct sc_sock_pipe *p) +{ + int rc = 0, rv; + + rv = closesocket(p->fds[0]); + if (rv != 0) { + rc = -1; + } + + rv = closesocket(p->fds[1]); + if (rv != 0) { + rc = -1; + } + + return rc; +} + +int sc_sock_pipe_write(struct sc_sock_pipe *p, void *data, int len) +{ + int rc; + + rc = send(p->fds[1], data, len, 0); + + return rc; +} + +int sc_sock_pipe_read(struct sc_sock_pipe *p, void *data, int len) +{ + int rc; + + rc = recv(p->fds[0], (char *) data, len, 0); + + return rc; +} + +#else + +int sc_sock_pipe_init(struct sc_sock_pipe *p, int type) +{ + int rc; + + rc = pipe(p->fds); + if (rc != 0) { + return -1; + } + + p->fdt.type = type; + p->fdt.op = 0; + p->fdt.fd = p->fds[0]; + + return 0; +} + +int sc_sock_pipe_term(struct sc_sock_pipe *p) +{ + int rc = 0, rv; + + rv = close(p->fds[0]); + if (rv != 0) { + rc = -1; + } + + rv = close(p->fds[1]); + if (rv != 0) { + rc = -1; + } + + return rc; +} + +int sc_sock_pipe_write(struct sc_sock_pipe *p, void *data, int len) +{ + ssize_t n; + char *b = data; + +retry: + n = write(p->fds[1], b, len); + if (n == -1 && errno == EINTR) { + goto retry; + } + + if (n > 0 && n != len) { + len -= n; + b += n; + goto retry; + } + + return n; +} + +int sc_sock_pipe_read(struct sc_sock_pipe *p, void *data, int len) +{ + ssize_t n; + char *b = data; + +retry: + n = read(p->fds[0], b, len); + if (n == -1 && errno == EINTR) { + goto retry; + } + + if (n > 0 && n != len) { + len -= n; + b += n; + goto retry; + } + + return n; +} + +#endif +void test1() +{ + int rc; + char buf[32]; + struct sc_sock_pipe pipe; + + rc = sc_signal_init(); + assert(rc == 0); + + rc = sc_sock_pipe_init(&pipe, 0); + assert(rc == 0); + + sc_signal_shutdown_fd = pipe.fds[1]; + raise(SIGINT); + + rc = sc_sock_pipe_read(&pipe, buf, 1); + assert(rc == 1); + + rc = sc_sock_pipe_term(&pipe); + assert(rc == 0); +} + + +int main() +{ +#if defined(_WIN32) || defined(_WIN64) + WSADATA data; + + int rc = WSAStartup(MAKEWORD(2, 2), &data); + assert(rc == 0); + assert(LOBYTE(data.wVersion) == 2 && HIBYTE(data.wVersion) == 2); +#endif + + test1(); + +#if defined(_WIN32) || defined(_WIN64) + rc = WSACleanup(); + assert(rc == 0); +#endif + return 0; +} diff --git a/socket/sc_sock.c b/socket/sc_sock.c index 1fbbdab..ab76e79 100644 --- a/socket/sc_sock.c +++ b/socket/sc_sock.c @@ -466,7 +466,7 @@ retry: } if (!sock->blocking && err == SC_EAGAIN) { - return 0; + return SC_SOCK_WANT_WRITE; } sc_sock_errstr(sock, 0);