diff --git a/.gitignore b/.gitignore index 5b71f0b8..c4b3b6d2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,17 @@ CMakeCache.txt CMakeFiles Makefile +/Testing/ +/.idea/ +/cmake-build-debug/ +/cmake-build-release/ cmake_install.cmake - git_version.h web_resources.h +workspace.xml +.ninja_deps +.ninja_log datum_gateway - *.conf *.json !doc/example_datum_gateway_config.json diff --git a/CMakeLists.txt b/CMakeLists.txt index f136e5c9..13b6db05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.13) +cmake_minimum_required(VERSION 3.11) project(DATUM VERSION 0.4.0 LANGUAGES C) @@ -17,6 +17,22 @@ option(ENABLE_API "Build API support." ON) include(GNUInstallDirs) +if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + add_definitions(-D__APPLE__) + set(PLATFORM_SOURCES src/platform_macos.c) + +elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "OpenBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "NetBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "DragonFly") + add_definitions(-D__BSD__) + set(PLATFORM_SOURCES src/platform_bsd.c) + +elseif(UNIX) + add_definitions(-D__LINUX__) + set(PLATFORM_SOURCES src/platform_linux.c) +endif() + add_executable(datum_gateway src/datum_blocktemplates.c src/datum_coinbaser.c @@ -37,6 +53,7 @@ add_executable(datum_gateway src/thirdparty_base58.c src/thirdparty_segwit_addr.c ${CMAKE_CURRENT_BINARY_DIR}/web_resources.h + src/datum_cross_platform_io.h ) install(TARGETS datum_gateway DESTINATION bin) diff --git a/src/datum_api.c b/src/datum_api.c index 82620df5..37baf096 100644 --- a/src/datum_api.c +++ b/src/datum_api.c @@ -139,6 +139,12 @@ void datum_api_var_DATUM_MINER_TAG(char *buffer, size_t buffer_size, const T_DAT buffer[i+1] = '"'; buffer[i+2] = 0; } +void datum_api_var_PAGE_REFRESH_TIMED_OUT(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) { + if (datum_config.datum_page_refresh_seconds != 0) + { + snprintf(buffer, buffer_size, "%d", datum_config.datum_page_refresh_seconds); + } +} void datum_api_var_DATUM_POOL_DIFF(char *buffer, size_t buffer_size, const T_DATUM_API_DASH_VARS *vardata) { snprintf(buffer, buffer_size, "%llu", (unsigned long long)datum_config.override_vardiff_min); } @@ -256,7 +262,9 @@ DATUM_API_VarEntry var_entries[] = { {"STRATUM_JOB_WEIGHT", datum_api_var_STRATUM_JOB_WEIGHT}, {"STRATUM_JOB_SIGOPS", datum_api_var_STRATUM_JOB_SIGOPS}, {"STRATUM_JOB_TXNCOUNT", datum_api_var_STRATUM_JOB_TXNCOUNT}, - + + {"PAGE_REFRESH_TIMED_OUT", datum_api_var_PAGE_REFRESH_TIMED_OUT}, + {NULL, NULL} // Mark the end of the array }; @@ -955,7 +963,10 @@ size_t datum_api_fill_config_var(const char *var_start, const size_t var_name_le const size_t var_name_len_2 = var_end - var_start_2; const char * const underscore_pos = memchr(var_start_2, '_', var_name_len_2); int val; - if (var_name_len_2 == 3 && 0 == strncmp(var_start_2, "*ro", 3)) { + if (var_name_len_2 == 16 && 0 == strncmp(var_start_2, "time_out_in_secs", 16)) + { + val = datum_config.datum_page_refresh_seconds; + } else if (var_name_len_2 == 3 && 0 == strncmp(var_start_2, "*ro", 3)) { val = !(datum_config.api_modify_conf && datum_config.api_admin_password_len); if (!colon_pos) { var_start = "readonly:"; @@ -1147,7 +1158,17 @@ struct datum_api_config_set_status { // If anything fails (including validation), errors is appended and false is returned bool datum_api_config_set(const char * const key, const char * const val, struct datum_api_config_set_status * const status) { json_t * const errors = status->errors; - if (0 == strcmp(key, "mining_pool_address")) { + if (0 == strcmp(key, "time_out_in_secs")) { + const int val_int = datum_atoi_strict(val, strlen(val)); + if (val_int == datum_config.datum_page_refresh_seconds) return true; + if (val_int < 0) { + json_t * const j = json_pack("{s:i}", key, val_int); + json_array_append_new(errors, j); + return false; + } + datum_config.datum_page_refresh_seconds = val_int; + datum_api_json_modify_new("page_refresh", key, json_integer(val_int)); + } else if (0 == strcmp(key, "mining_pool_address")) { if (0 == strcmp(val, datum_config.mining_pool_address)) return true; unsigned char dummy[64]; if (!addr_2_output_script(val, &dummy[0], 64)) { diff --git a/src/datum_conf.c b/src/datum_conf.c index 622e0d91..c258fdac 100644 --- a/src/datum_conf.c +++ b/src/datum_conf.c @@ -188,6 +188,10 @@ const T_DATUM_CONFIG_ITEM datum_config_options[] = { .required = false, .ptr = &datum_config.datum_pooled_mining_only, .default_bool = true }, { .var_type = DATUM_CONF_INT, .category = "datum", .name = "protocol_global_timeout", .description = "If no valid messages are received from the DATUM server in this many seconds, give up and try to reconnect", .required = false, .ptr = &datum_config.datum_protocol_global_timeout, .default_int = 60 }, + + // status page refresh + { .var_type = DATUM_CONF_INT, .category = "page_refresh", .name = "time_out_in_secs", .description = "Refresh status page interval", + .required = false, .ptr = &datum_config.datum_page_refresh_seconds, .default_int = 5 }, }; #define NUM_CONFIG_ITEMS (sizeof(datum_config_options) / sizeof(datum_config_options[0])) diff --git a/src/datum_conf.h b/src/datum_conf.h index dbe33107..3adfe40f 100644 --- a/src/datum_conf.h +++ b/src/datum_conf.h @@ -149,7 +149,9 @@ typedef struct { bool clog_to_stderr; bool clog_rotate_daily; char clog_file[1024]; - + + int datum_page_refresh_seconds; + char datum_pool_host[1024]; int datum_pool_port; bool datum_pool_pass_workers; diff --git a/src/datum_cross_platform_io.h b/src/datum_cross_platform_io.h new file mode 100644 index 00000000..719f3cca --- /dev/null +++ b/src/datum_cross_platform_io.h @@ -0,0 +1,107 @@ +/* datum_cross_platform_io.h - Cross-platform I/O abstraction */ + +#ifndef DATUM_CROSS_PLATFORM_IO_H +#define DATUM_CROSS_PLATFORM_IO_H + +#if defined(__APPLE__) || defined(__BSD__) +#include +#include +#include +#include +#include +#include +#include +#include +#define IO_HANDLE int +#define IO_EVENT_READ EVFILT_READ +#define IO_EVENT_ERROR EVFILT_EXCEPT +#define IO_MAX_EVENTS 32 + +static inline int datum_io_create() { + return kqueue(); +} + +static inline int datum_io_add(IO_HANDLE kq, uintptr_t fd, struct kevent *evSet) { + evSet->ident = fd; + EV_SET(evSet, fd, EVFILT_READ, EV_ADD, 0, 0, NULL); + return kevent(kq, evSet, 1, NULL, 0, NULL); +} + +static inline int datum_io_delete(IO_HANDLE kq, uintptr_t fd, struct kevent *evSet) +{ + if (evSet) { + evSet->ident = fd; + } + EV_SET(evSet, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); + return kevent(kq, evSet, 1, NULL, 0, NULL); +} + +static inline int datum_io_modify(IO_HANDLE kq, uintptr_t fd, struct kevent *evSet) +{ + if (evSet) { + evSet->ident = fd; + } + EV_SET(evSet, fd, EVFILT_READ, EV_ADD | EV_ENABLE, 0, 0, NULL); + return kevent(kq, evSet, 1, NULL, 0, NULL); +} + +static inline int datum_io_wait(IO_HANDLE kq, struct kevent* events, int max_events, int timeout_ms) { + struct timespec ts = { + .tv_sec = timeout_ms / 1000, + .tv_nsec = (timeout_ms % 1000) * 1000000 + }; + return kevent(kq, NULL, 0, events, max_events, &ts); +} + +static inline int portable_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout) { + while (nanosleep(timeout, NULL) == -1 && errno == EINTR) continue; + return pthread_mutex_trylock(mutex); +} + +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#include +#define IO_HANDLE int +#define IO_EVENT_READ EPOLLIN +#define IO_EVENT_ERROR (EPOLLERR | EPOLLHUP) +#define IO_MAX_EVENTS 32 + +static inline int datum_io_create(int flags) { + return epoll_create1(flags); +} + +static inline int datum_io_add(IO_HANDLE epfd, uintptr_t fd, struct epoll_event *ev) { + if (ev) { + ev->events = EPOLLIN | EPOLLERR | EPOLLHUP; + ev->data.fd = fd; + } + return epoll_ctl(epfd, EPOLL_CTL_ADD, fd, ev); +} + +static inline int datum_io_delete(IO_HANDLE epfd, uintptr_t fd, struct epoll_event *ev) { + return epoll_ctl(epfd, EPOLL_CTL_DEL, fd, ev); +} + +static inline int datum_io_modify(IO_HANDLE epfd, uintptr_t fd, struct epoll_event *ev) { + return epoll_ctl(epfd, EPOLL_CTL_MOD, fd, ev); +} + +static inline int datum_io_wait(IO_HANDLE epfd, struct epoll_event* events, int max_events, int timeout_ms) { + return epoll_wait(epfd, events, max_events, timeout_ms); +} + +static inline int portable_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec *timeout) { + return pthread_mutex_timedlock(mutex, timeout); +} + +#else +#error Platform not supported +#endif + +#endif // DATUM_CROSS_PLATFORM_IO_H + diff --git a/src/datum_protocol.c b/src/datum_protocol.c index 76dbc425..bb02ffa8 100644 --- a/src/datum_protocol.c +++ b/src/datum_protocol.c @@ -34,17 +34,17 @@ */ // DATUM Client protocol implementation -// Encrypted and on the wire has 7.999 bits of entropy per byte in testing. completely uncompressable. +// Encrypted and on the wire has 7.999 bits of entropy per byte in testing. completely uncompressed. // TODO: Clean this up and break up various functions // TODO: Generalize encryption related operations vs repeated code // TODO: Implement versioning on the protocol for feature lists -// TODO: Add pool-side assistance with startup to ensure that the client's node is fully sync'd with the network +// TODO: Add pool-side assistance with startup to ensure that the client's node is fully synced with the network // TODO: Optionally allow pool to suggest node peers // TODO: Implement graceful negotiation of chain forks -// TODO: Implement preciousblock for pool blocks not found by the client +// TODO: Implement precious block for pool blocks not found by the client // TODO: Handle network failures that aren't immediately obvious more gracefully (like not receiving responses to server commands) -// TODO: Implement resuiming of work without allowing one client to cause duplicate work for another +// TODO: Implement resuming of work without allowing one client to cause duplicate work for another #include #include @@ -58,7 +58,13 @@ #include #include #include -#include +#include "datum_cross_platform_io.h" +#if defined(__APPLE__) || defined(__BSD__) +#include // macOS uses kqueue instead of epoll +#include +#else +#include // Linux-specific +#endif #include #include #include @@ -294,7 +300,7 @@ int datum_protocol_coinbaser_fetch_response(int len, unsigned char *data) { return 0; } - rc = pthread_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &ts); + rc = portable_mutex_timedlock(&datum_protocol_coinbaser_fetch_mutex, &ts); if (rc != 0) { DLOG_DEBUG("Could not get a lock on the coinbaser reception mutex after 5 seconds... bug?"); return 0; @@ -1467,7 +1473,12 @@ void *datum_protocol_client(void *args) { int sockfd = -1; int epollfd, nfds; int flag = 1; +#ifdef __linux__ struct epoll_event ev, events[MAX_DATUM_CLIENT_EVENTS]; +#elif defined(__APPLE__) || defined(__BSD__) + struct kevent ev, events[MAX_DATUM_CLIENT_EVENTS]; +#endif + struct timeval start, now; int ret,i,n; datum_protocol_client_active = 1; @@ -1580,23 +1591,26 @@ void *datum_protocol_client(void *args) { } // Set up epoll - if ((epollfd = epoll_create1(0)) == -1) { +#ifdef __linux__ + if ((epollfd = datum_io_create(0)) == -1) { +#elif defined(__APPLE__) || defined(__BSD__) + if ((epollfd = datum_io_create()) == -1) { +#endif + DLOG_FATAL("epoll_create1(...) error: %s",strerror(errno)); close(sockfd); datum_protocol_client_active = 0; return NULL; } - - ev.events = EPOLLIN | EPOLLERR | EPOLLHUP; - ev.data.fd = sockfd; - - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sockfd, &ev) == -1) { + + if (datum_io_add(epollfd, sockfd, &ev) == -1) { DLOG_FATAL("epoll_ctl(...) error: %s",strerror(errno)); close(sockfd); close(epollfd); datum_protocol_client_active = 0; return NULL; } + i = 0; datum_last_accepted_share_tsms = 0; datum_last_accepted_share_local_tsms = 0; @@ -1687,9 +1701,14 @@ void *datum_protocol_client(void *args) { } if (break_again) break; - - nfds = epoll_wait(epollfd, events, MAX_DATUM_CLIENT_EVENTS, 5); // Wait for 5ms - + +#ifdef __linux__ + int wait_ms = 5; +#elif defined(__APPLE__) || defined(__BSD__) + int wait_ms = 5000; +#endif + nfds = datum_io_wait(epollfd, events, MAX_DATUM_CLIENT_EVENTS, wait_ms); // Wait for 5ms + if (nfds == -1 && errno != EINTR) { DLOG_FATAL("epoll_wait(...) error: %s",strerror(errno)); break; @@ -1698,12 +1717,20 @@ void *datum_protocol_client(void *args) { if (nfds <= 0) { continue; // Timeout, nothing happened } - +#ifdef __linux__ if (events[0].events & (EPOLLERR | EPOLLHUP)) { +#elif defined(__APPLE__) || defined(__BSD__) + if (events[0].flags & EVFILT_EXCEPT) { +#endif + int err = 0; socklen_t errlen = sizeof(err); - - if (getsockopt(events[0].data.fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0) { +#ifdef __linux__ + int fd = events[0].data.fd; +#elif defined(__APPLE__) || defined(__BSD__) + int fd = events[0].ident; +#endif + if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &errlen) == 0) { if (err != 0) { DLOG_ERROR("Socket error: %s", strerror(err)); } else { @@ -1714,8 +1741,12 @@ void *datum_protocol_client(void *args) { } break; } - + +#ifdef __linux__ if (events[0].events & EPOLLIN) { +#elif defined(__APPLE__) || defined(__BSD__) + if (events[0].flags & EV_ADD) { +#endif // data to receive break_again = false; // Receive the header, followed by any data specified by the header @@ -1958,4 +1989,4 @@ int datum_encrypt_generate_keys(DATUM_ENC_KEYS *keys) { keys->is_remote = false; return 0; -} +} \ No newline at end of file diff --git a/src/datum_protocol.h b/src/datum_protocol.h index c56a40f3..c5bb2400 100644 --- a/src/datum_protocol.h +++ b/src/datum_protocol.h @@ -169,4 +169,17 @@ extern uint64_t datum_rejected_share_diff; #define DATUM_POW_SHARE_RESPONSE_ACCEPTED_TENTATIVELY 0x55 #define DATUM_POW_SHARE_RESPONSE_REJECTED 0x66 +// datum_platform_io.h +#ifdef __linux__ +#include +#define IO_EVENT_READ EPOLLIN +#define IO_EVENT_ERROR (EPOLLERR | EPOLLHUP) +#define IO_HANDLE int +#elif defined(__APPLE__) || defined(__BSD__) +#include +#define IO_EVENT_READ EVFILT_READ +#define IO_EVENT_ERROR EVFILT_EXCEPT +#define IO_HANDLE int +#endif + #endif diff --git a/src/datum_sockets.c b/src/datum_sockets.c index b70ca394..1b3f61cb 100644 --- a/src/datum_sockets.c +++ b/src/datum_sockets.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include #include @@ -60,6 +59,7 @@ #include "datum_protocol.h" #include "datum_utils.h" #include "datum_sockets.h" +#include "datum_cross_platform_io.h" int datum_active_threads = 0; int datum_active_clients = 0; @@ -105,8 +105,12 @@ void *datum_threadpool_thread(void *arg) { panic_from_thread(__LINE__); return 0; } - - my->epollfd = epoll_create1(EPOLL_CLOEXEC); +#ifdef __linux__ + my->epollfd = datum_io_create(EPOLL_CLOEXEC); +#elif defined(__APPLE__) || defined(__BSD__) + my->epollfd = datum_io_create(); +#endif + if (my->epollfd < 0) { DLOG_FATAL("could not epoll_create!"); panic_from_thread(__LINE__); @@ -144,9 +148,15 @@ void *datum_threadpool_thread(void *arg) { my->client_data[i].proxy_line_read = 0; // add to epoll for this thread - my->ev.events = EPOLLIN | EPOLLONESHOT | EPOLLERR; // | EPOLLRDHUP +#ifdef __linux__ + my->ev.events = EPOLLIN | EPOLLONESHOT | EPOLLERR; my->ev.data.u64 = i; // store client index... duh - if (epoll_ctl(my->epollfd, EPOLL_CTL_ADD, my->client_data[i].fd, &my->ev) < 0) { +#elif defined(__APPLE__) || defined(__BSD__) + my->ev.flags = EV_ADD | EV_ONESHOT | EV_ERROR; + my->ev.data = i; +#endif + if (datum_io_add(my->epollfd, my->client_data[i].fd, &my->ev) < 0) { + DLOG_ERROR("epoll_ctl add failed: %s", strerror(errno)); close(my->client_data[i].fd); // Close the file descriptor on error @@ -170,7 +180,7 @@ void *datum_threadpool_thread(void *arg) { DLOG_WARN("Executing command to empty thread (%d clients)",my->connected_clients); for (j = 0; j < my->app->max_clients_thread; j++) { if (my->client_data[j].fd != 0) { - epoll_ctl(my->epollfd, EPOLL_CTL_DEL, my->client_data[j].fd, NULL); + datum_io_delete(my->epollfd, my->client_data[j].fd, NULL); close(my->client_data[j].fd); // call closed client function, if any @@ -183,7 +193,7 @@ void *datum_threadpool_thread(void *arg) { for (j = 0; j < my->app->max_clients_thread; j++) { if ((my->client_data[j].fd != 0) && (my->client_data[j].kill_request)) { my->client_data[j].kill_request = false; - epoll_ctl(my->epollfd, EPOLL_CTL_DEL, my->client_data[j].fd, NULL); + datum_io_delete(my->epollfd, my->client_data[j].fd, NULL); close(my->client_data[j].fd); // call closed client function, if any @@ -217,7 +227,7 @@ void *datum_threadpool_thread(void *arg) { } } else { if (!(errno == EAGAIN || errno == EWOULDBLOCK)) { - epoll_ctl(my->epollfd, EPOLL_CTL_DEL, my->client_data[j].fd, NULL); + datum_io_delete(my->epollfd, my->client_data[j].fd, NULL); close(my->client_data[j].fd); // call closed client function, if any @@ -230,7 +240,7 @@ void *datum_threadpool_thread(void *arg) { } // check if we have any data to read from any existing clients - nfds = epoll_wait(my->epollfd, my->events, MAX_EVENTS, 7); + nfds = datum_io_wait(my->epollfd, my->events, MAX_EVENTS, 7); if (nfds < 0) { if (errno != EINTR) { DLOG_ERROR("epoll_wait returned %d", nfds); @@ -240,8 +250,12 @@ void *datum_threadpool_thread(void *arg) { } if (nfds) { for(i=0;ievents[i].data.u64; - +#elif defined(__APPLE__) || defined(__BSD__) + cidx = my->events[i].data; +#endif + if (cidx >= 0) { n = recv(my->client_data[cidx].fd, &my->client_data[cidx].buffer[my->client_data[cidx].in_buf], CLIENT_BUFFER - 1 - my->client_data[cidx].in_buf, MSG_DONTWAIT); if (n <= 0) { @@ -252,7 +266,7 @@ void *datum_threadpool_thread(void *arg) { } else { // an error occurred or the client closed the connection DLOG_DEBUG("Thread %03d epoll --- Closing fd %d (n=%d) errno=%d (%s) (req bytes: %d)", my->thread_id, my->client_data[cidx].fd, n, errno, strerror(errno), CLIENT_BUFFER - 1 - my->client_data[cidx].in_buf); - epoll_ctl(my->epollfd, EPOLL_CTL_DEL, my->client_data[cidx].fd, NULL); + datum_io_delete(my->epollfd, my->client_data[cidx].fd, NULL); close(my->client_data[cidx].fd); // call closed client function, if any @@ -299,7 +313,7 @@ void *datum_threadpool_thread(void *arg) { j = my->app->client_cmd_func(&my->client_data[cidx], start_line); if (j < 0) { //LOG_PRINTF("Thread %03d --- Closing fd %d (client_cmd_func returned %d)", my->thread_id, my->client_data[cidx].fd, j); - epoll_ctl(my->epollfd, EPOLL_CTL_DEL, my->client_data[cidx].fd, NULL); + datum_io_delete(my->epollfd, my->client_data[cidx].fd, NULL); close(my->client_data[cidx].fd); // call closed client function, if any @@ -328,8 +342,7 @@ void *datum_threadpool_thread(void *arg) { // buffer overrun. lose the data. will probably break things, so punt the client. this shouldn't happen with sane clients. my->client_data[cidx].in_buf = 0; my->client_data[cidx].buffer[0] = 0; - - epoll_ctl(my->epollfd, EPOLL_CTL_DEL, my->client_data[cidx].fd, NULL); + datum_io_delete(my->epollfd, my->client_data[cidx].fd, NULL); close(my->client_data[cidx].fd); // call closed client function, if any @@ -342,9 +355,14 @@ void *datum_threadpool_thread(void *arg) { if (my->client_data[cidx].fd > 0) { // re-add to epoll for this client +#ifdef __linux__ my->ev.events = EPOLLIN | EPOLLONESHOT; my->ev.data.u64 = cidx; // store client index... duh - if (epoll_ctl(my->epollfd, EPOLL_CTL_MOD, my->client_data[cidx].fd, &my->ev) < 0) { +#elif defined(__APPLE__) || defined(__BSD__) + my->ev.flags = EV_ADD | EV_ONESHOT; + my->ev.data = cidx; // store client index... duh +#endif + if (datum_io_modify(my->epollfd, my->client_data[cidx].fd, &my->ev) < 0) { // if this fails, there's probably some bad things happening. In any case, we can't continue serving this client so we should punt them. DLOG_ERROR("epoll_ctl mod for client %d", cidx); close(my->client_data[cidx].fd); // Close the file descriptor on error @@ -382,9 +400,14 @@ void clean_thread_data(T_DATUM_THREAD_DATA *d, T_DATUM_SOCKET_APP *app) { // clear polling events // TODO: dynamic allocation of buffers +#ifdef __linux__ memset(&d->ev, 0, sizeof(struct epoll_event)); memset(d->events, 0, sizeof(struct epoll_event) * MAX_CLIENTS_THREAD*2); - +#elif defined(__APPLE__) || defined(__BSD__) + memset(&d->ev, 0, sizeof(struct kevent)); + memset(d->events, 0, sizeof(struct kevent) * MAX_CLIENTS_THREAD*2); +#endif + // init the mutex ret = pthread_mutex_init(&d->thread_data_lock, NULL); if (ret) { @@ -674,8 +697,13 @@ void *datum_gateway_listener_thread(void *arg) { uint64_t reject_count = 0; T_DATUM_SOCKET_APP *app = (T_DATUM_SOCKET_APP *)arg; - + +#ifdef __linux__ struct epoll_event ev, events[MAX_EVENTS]; +#elif defined(__APPLE__) || defined(__BSD__) + struct kevent ev, events[MAX_EVENTS]; +#endif + int listen_socks[2], conn_sock, nfds, epollfd; if (!app) { @@ -710,8 +738,13 @@ void *datum_gateway_listener_thread(void *arg) { return NULL; } if (listen_socks_len < 2) listen_socks[1] = -1; - - epollfd = epoll_create1(0); + +#ifdef __linux__ + epollfd = datum_io_create(0); +#elif defined(__APPLE__) || defined(__BSD__) + epollfd = datum_io_create(); +#endif + if (epollfd < 0) { DLOG_FATAL("epoll_create1 failed: %s", strerror(errno)); panic_from_thread(__LINE__); @@ -720,9 +753,17 @@ void *datum_gateway_listener_thread(void *arg) { for (i = 0; i < 2; ++i) { if (listen_socks[i] == -1) continue; +#ifdef __linux__ + uintptr_t f_desc = listen_socks[i]; ev.events = EPOLLIN; - ev.data.fd = listen_socks[i]; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, ev.data.fd, &ev) < 0) { + ev.data.fd = f_desc; +#elif defined(__APPLE__) || defined(__BSD__) + uintptr_t f_desc = listen_socks[i]; + ev.ident = f_desc; + ev.filter = EVFILT_READ; +#endif + + if (datum_io_add(epollfd, f_desc, &ev) < 0) { DLOG_FATAL("epoll_ctl failed: %s", strerror(errno)); panic_from_thread(__LINE__); return NULL; @@ -732,7 +773,7 @@ void *datum_gateway_listener_thread(void *arg) { DLOG_INFO("DATUM Socket listener thread active for '%s'", app->name); for (;;) { - nfds = epoll_wait(epollfd, events, MAX_EVENTS, 100); + nfds = datum_io_wait(epollfd, events, MAX_EVENTS, 100); if (nfds) { if (datum_config.datum_pooled_mining_only && (!datum_protocol_is_active())) { curtime_tsms = current_time_millis(); // we only need this if we're rejecting connections @@ -745,8 +786,13 @@ void *datum_gateway_listener_thread(void *arg) { } } for (int n = 0; n < nfds; ++n) { - if (events[n].data.fd == listen_socks[0] || events[n].data.fd == listen_socks[1]) { - conn_sock = accept(events[n].data.fd, NULL, NULL); +#ifdef __linux__ + uintptr_t f_desc = events[n].data.fd; +#elif defined(__APPLE__) || defined(__BSD__) + uintptr_t f_desc = events[n].ident; +#endif + if (f_desc == listen_socks[0] || f_desc == listen_socks[1]) { + conn_sock = accept(f_desc, NULL, NULL); if (conn_sock < 0) { DLOG_ERROR("accept failed: %s", strerror(errno)); continue; diff --git a/src/datum_sockets.h b/src/datum_sockets.h index ab1a1015..649b17c4 100644 --- a/src/datum_sockets.h +++ b/src/datum_sockets.h @@ -40,7 +40,12 @@ #include "datum_blocktemplates.h" #endif -#include +#if defined(__APPLE__) || defined(__BSD__) +#include // macOS uses kqueue instead of epoll +#include +#else +#include // Linux-specific +#endif #include typedef struct T_DATUM_THREAD_DATA T_DATUM_THREAD_DATA; @@ -150,8 +155,12 @@ typedef struct T_DATUM_THREAD_DATA { int connected_clients; int next_open_client_index; - +#ifdef __linux__ struct epoll_event ev, events[MAX_CLIENTS_THREAD*2]; +#elif defined(__APPLE__) || defined(__BSD__) + struct kevent ev, events[MAX_CLIENTS_THREAD*2]; +#endif + int epollfd; // information for this socket application @@ -174,7 +183,7 @@ int assign_to_thread(T_DATUM_SOCKET_APP *app, int fd); void *datum_threadpool_thread(void *arg); static inline void datum_socket_thread_client_count_decrement(T_DATUM_THREAD_DATA *my, int cid_who_left, bool not_already_locked) { - // compiler will optimize the if's away in most cases, since this is inline + // compiler will optimize this away in most cases, since this is inline if (not_already_locked) pthread_mutex_lock(&my->thread_data_lock); // decrement connected client count for the thread diff --git a/src/datum_stratum.c b/src/datum_stratum.c index 3f7fec45..0e5481ce 100644 --- a/src/datum_stratum.c +++ b/src/datum_stratum.c @@ -122,7 +122,6 @@ void datum_stratum_v1_shutdown_all(void) { } DLOG_INFO("Sent disconnect request for all stratum clients to %u threads.", shutdown_threads); - return; } // Started as its own pthread during startup @@ -854,11 +853,9 @@ void stratum_update_vardiff(T_DATUM_CLIENT_DATA *c, bool no_quick) { // adjust diff upward a tick m->current_diff = m->current_diff << 1; reset_vardiff_stats(c); - return; } // nothing to do yet - return; } #define STAT_CYCLE_MS 60000 @@ -1662,55 +1659,48 @@ void datum_stratum_fingerprint_by_UA(T_DATUM_MINER_DATA *m) { // S21 Pro NOT confirmed to work this way (yet)... so keep the / if (strstr(m->useragent, "Antminer S21/") == m->useragent) { m->coinbase_selection = 5; // ANTMAIN2 - return; } // the ePIC control boards can handle almost any size coinbase // UA starts with: PowerPlay-BM/ - if (strstr(m->useragent, "PowerPlay-BM/") == m->useragent) { + else if (strstr(m->useragent, "PowerPlay-BM/") == m->useragent) { m->coinbase_selection = 4; // YUGE - return; } // "vinsh" reports as xminer // Tested to handle up to 16KB - if (strstr(m->useragent, "xminer-1.") == m->useragent) { + else if (strstr(m->useragent, "xminer-1.") == m->useragent) { m->coinbase_selection = 4; // YUGE - return; } // whatsminer works fine with about a 6.5 KB coinbase // UA starts with: whatsminer/v1 - if (strstr(m->useragent, "whatsminer/v1") == m->useragent) { + else if (strstr(m->useragent, "whatsminer/v1") == m->useragent) { m->coinbase_selection = 3; // RESPECTABLE - return; } // Braiins firmware // Appears to handle arbitrary coinbase sizes, however not extensively tested on all firmware versions // feed the S21-like coinbase for now, which is at least moderately sized // UA contains: bosminer-plus-tuner - if (strstr(m->useragent, "bosminer-plus-tuner") != NULL) { // match anywhere in string, not just beginning + else if (strstr(m->useragent, "bosminer-plus-tuner") != NULL) { // match anywhere in string, not just beginning m->coinbase_selection = 5; // ANTMAIN2 - return; } // Nicehash, sadly needs a smaller coinbase than even antminer s19s // they also need a high minimum difficulty - if (strstr(m->useragent, "NiceHash/") == m->useragent) { + else if (strstr(m->useragent, "NiceHash/") == m->useragent) { m->current_diff=524288; m->forced_high_min_diff=524288; m->coinbase_selection = 1; // TINY - return; } // The Bitaxe is tested to work with a large coinbase // However, it does slow work changes slightly when they're YUGE, so we'll go with - // the whatsminer tested size as a compromise. also should save some bandwidth, which + // the whatsminer tested size as a compromise. Also should save some bandwidth, which // is probably not a bad plan, given the low odds of a bitaxe finding a block. - if (strstr(m->useragent, "bitaxe") == m->useragent) { + else if (strstr(m->useragent, "bitaxe") == m->useragent) { m->coinbase_selection = 3; // RESPECTABLE - return; } } @@ -1893,7 +1883,6 @@ void stratum_job_merkle_root_calc(T_DATUM_STRATUM_JOB *s, unsigned char *coinbas } memcpy(merkle_root_output, next, 32); - return; } void stratum_calculate_merkle_branches(T_DATUM_STRATUM_JOB *s) { @@ -2128,8 +2117,6 @@ void update_stratum_job(T_DATUM_TEMPLATE_DATA *block_template, bool new_block, i pthread_rwlock_unlock(&stratum_global_job_ptr_lock); DLOG_DEBUG("Updated to job %d, ncb = %d, state = %d", s->global_index, s->need_coinbaser?1:0, s->job_state); - - return; } int assembleBlockAndSubmit(uint8_t *block_header, uint8_t *coinbase_txn, size_t coinbase_txn_size, T_DATUM_STRATUM_JOB *job, T_DATUM_STRATUM_THREADPOOL_DATA *sdata, const char *block_hash_hex, bool empty_work) { diff --git a/www/config.html b/www/config.html index 20ebc8c5..ab4c0fc6 100644 --- a/www/config.html +++ b/www/config.html @@ -108,6 +108,14 @@

Basic

+

Page Refresh

+
+
+ + +
+ +
diff --git a/www/home.html b/www/home.html index 2c73f0f0..946135db 100644 --- a/www/home.html +++ b/www/home.html @@ -6,6 +6,7 @@ +