diff --git a/api.c b/api.c index 4fcc66f9..b046a980 100644 --- a/api.c +++ b/api.c @@ -126,6 +126,7 @@ char *WSAErrorMsg(void) { #endif static const char *UNAVAILABLE = " - API will not be available"; +static const char *MUNAVAILABLE = " - API multicast listener will not be available"; static const char *BLANK = ""; static const char *COMMA = ","; @@ -134,7 +135,7 @@ static const char SEPARATOR = '|'; #define SEPSTR "|" static const char GPUSEP = ','; -static const char *APIVERSION = "1.28"; +static const char *APIVERSION = "1.29"; static const char *DEAD = "Dead"; #if defined(HAVE_OPENCL) || defined(HAVE_AN_FPGA) || defined(HAVE_AN_ASIC) static const char *SICK = "Sick"; @@ -4259,6 +4260,164 @@ static void *restart_thread(__maybe_unused void *userdata) return NULL; } +static void mcast() +{ + struct sockaddr_in listen; + struct ip_mreq grp; + struct sockaddr_in came_from; + time_t bindstart; + char *binderror; + SOCKETTYPE mcast_sock; + SOCKETTYPE reply_sock; + socklen_t came_from_siz; + ssize_t rep; + int bound; + int count; + int reply_port; + + char expect[] = "cgminer-"; // first 8 bytes constant + char *expect_code; + size_t expect_code_len; + char buf[1024]; + char reply[] = "cgm-" API_MCAST_CODE "-"; // constant + char replybuf[1024]; + + memset(&grp, 0, sizeof(grp)); + grp.imr_multiaddr.s_addr = inet_addr(opt_api_mcast_addr); + if (grp.imr_multiaddr.s_addr == INADDR_NONE) + quit(1, "Invalid Multicast Address"); + grp.imr_interface.s_addr = inet_addr("192.168.7.70"); + + mcast_sock = socket(AF_INET, SOCK_DGRAM, 0); + + int optval = 1; + if (SOCKETFAIL(setsockopt(mcast_sock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval)))) { + applog(LOG_ERR, "API mcast setsockopt SO_REUSEADDR failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); + goto die; + } + + memset(&listen, 0, sizeof(listen)); + listen.sin_family = AF_INET; + listen.sin_addr.s_addr = INADDR_ANY; + listen.sin_port = htons(opt_api_mcast_port); + + // try for more than 1 minute ... in case the old one hasn't completely gone yet + bound = 0; + bindstart = time(NULL); + while (bound == 0) { + if (SOCKETFAIL(bind(mcast_sock, (struct sockaddr *)(&listen), sizeof(listen)))) { + binderror = SOCKERRMSG; + if ((time(NULL) - bindstart) > 61) + break; + else + cgsleep_ms(30000); + } else + bound = 1; + } + + if (bound == 0) { + applog(LOG_ERR, "API mcast bind to port %d failed (%s)%s", opt_api_port, binderror, MUNAVAILABLE); + goto die; + } + + if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (void *)(&grp), sizeof(grp)))) { + applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); + goto die; + } + + expect_code_len = sizeof(expect) + strlen(opt_api_mcast_code); + expect_code = malloc(expect_code_len+1); + if (!expect_code) + quit(1, "Failed to malloc mcast expect_code"); + snprintf(expect_code, expect_code_len+1, "%s%s-", expect, opt_api_mcast_code); + + count = 0; + while (80085) { + cgsleep_ms(1000); + + count++; + came_from_siz = sizeof(came_from); + if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf), + 0, (struct sockaddr *)(&came_from), &came_from_siz))) { + applog(LOG_DEBUG, "API mcast failed count=%d (%s) (%d)", + count, SOCKERRMSG, (int)mcast_sock); + continue; + } + buf[rep] = '\0'; + if (rep > 0 && buf[rep-1] == '\n') + buf[--rep] = '\0'; + + applog(LOG_DEBUG, "API mcast request rep=%d (%s) from %s:%d", + (int)rep, buf, + inet_ntoa(came_from.sin_addr), + ntohs(came_from.sin_port)); + + if ((size_t)rep > expect_code_len && memcmp(buf, expect_code, expect_code_len) == 0) { + reply_port = atoi(&buf[expect_code_len]); + if (reply_port < 1 || reply_port > 65535) { + applog(LOG_DEBUG, "API mcast request ignored - invalid port (%s)", + &buf[expect_code_len]); + } else { + applog(LOG_DEBUG, "API mcast request OK port %s=%d", + &buf[expect_code_len], reply_port); + + came_from.sin_port = htons(reply_port); + reply_sock = socket(AF_INET, SOCK_DGRAM, 0); + + snprintf(replybuf, sizeof(replybuf), + "cgm-" API_MCAST_CODE "-%d", + opt_api_port); + + rep = sendto(reply_sock, replybuf, strlen(replybuf)+1, + 0, (struct sockaddr *)(&came_from), + sizeof(came_from)); + if (SOCKETFAIL(rep)) { + applog(LOG_DEBUG, "API mcast reply failed (%s) (%d)", + SOCKERRMSG, (int)reply_sock); + } else { + applog(LOG_DEBUG, "API mcast reply (%s) succeeded (%d) (%d)", + reply, (int)rep, (int)reply_sock); + } + + CLOSESOCKET(reply_sock); + } + } else + applog(LOG_DEBUG, "API mcast request was no good"); + } + +die: + + CLOSESOCKET(mcast_sock); +} + +static void *mcast_thread(void *userdata) +{ + struct thr_info *mythr = userdata; + + pthread_detach(pthread_self()); + pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); + + RenameThread("api_mcast"); + + mcast(); + + PTH(mythr) = 0L; + + return NULL; +} + +void mcast_init() +{ + struct thr_info *thr; + + thr = calloc(1, sizeof(*thr)); + if (!thr) + quit(1, "Failed to calloc mcast thr"); + + if (thr_info_create(thr, NULL, mcast_thread, thr)) + quit(1, "API mcast thread create failed"); +} + void api(int api_thr_id) { struct io_data *io_data; @@ -4389,6 +4548,9 @@ void api(int api_thr_id) applog(LOG_WARNING, "API running in local read access mode on port %d (%d)", port, (int)*apisock); } + if (opt_api_mcast) + mcast_init(); + while (!bye) { clisiz = sizeof(cli); if (SOCKETFAIL(c = accept(*apisock, (struct sockaddr *)(&cli), &clisiz))) { diff --git a/cgminer.c b/cgminer.c index d997534d..1552d2cd 100644 --- a/cgminer.c +++ b/cgminer.c @@ -145,6 +145,10 @@ char *opt_api_groups; char *opt_api_description = PACKAGE_STRING; int opt_api_port = 4028; bool opt_api_listen; +bool opt_api_mcast; +char *opt_api_mcast_addr = API_MCAST_ADDR; +char *opt_api_mcast_code = API_MCAST_CODE; +int opt_api_mcast_port = 4028; bool opt_api_network; bool opt_delaynet; bool opt_disable_pool; @@ -890,6 +894,20 @@ static char *set_api_description(const char *arg) return NULL; } +static char *set_api_mcast_addr(const char *arg) +{ + opt_set_charp(arg, &opt_api_mcast_addr); + + return NULL; +} + +static char *set_api_mcast_code(const char *arg) +{ + opt_set_charp(arg, &opt_api_mcast_code); + + return NULL; +} + #ifdef USE_ICARUS static char *set_icarus_options(const char *arg) { @@ -943,6 +961,18 @@ static struct opt_table opt_config_table[] = { OPT_WITHOUT_ARG("--api-listen", opt_set_bool, &opt_api_listen, "Enable API, default: disabled"), + OPT_WITHOUT_ARG("--api-mcast", + opt_set_bool, &opt_api_mcast, + "Enable API Multicast listener, default: disabled"), + OPT_WITH_ARG("--api-mcast-addr", + set_api_mcast_addr, NULL, NULL, + "API Multicast listen address"), + OPT_WITH_ARG("--api-mcast-code", + set_api_mcast_code, NULL, NULL, + "Code expected in the API Multicast"), + OPT_WITH_ARG("--api-mcast-port", + set_int_1_to_65535, opt_show_intval, &opt_api_mcast_port, + "Port number of miner API Multicast listener"), OPT_WITHOUT_ARG("--api-network", opt_set_bool, &opt_api_network, "Allow API (if enabled) to listen on/for any address, default: only 127.0.0.1"), @@ -4177,6 +4207,10 @@ void write_config(FILE *fcfg) fprintf(fcfg, ",\n\"remove-disabled\" : true"); if (opt_api_allow) fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", json_escape(opt_api_allow)); + if (strcmp(opt_api_mcast_addr, API_MCAST_ADDR) != 0) + fprintf(fcfg, ",\n\"api-mcast-addr\" : \"%s\"", json_escape(opt_api_mcast_addr)); + if (strcmp(opt_api_mcast_code, API_MCAST_CODE) != 0) + fprintf(fcfg, ",\n\"api-mcast-code\" : \"%s\"", json_escape(opt_api_mcast_code)); if (strcmp(opt_api_description, PACKAGE_STRING) != 0) fprintf(fcfg, ",\n\"api-description\" : \"%s\"", json_escape(opt_api_description)); if (opt_api_groups) diff --git a/miner.h b/miner.h index f4eb91a8..ab59cba0 100644 --- a/miner.h +++ b/miner.h @@ -867,6 +867,9 @@ static inline void cg_wunlock(cglock_t *lock) struct pool; +#define API_MCAST_CODE "FTW" +#define API_MCAST_ADDR "224.0.0.75" + extern bool opt_protocol; extern bool have_longpoll; extern char *opt_kernel_path; @@ -877,6 +880,10 @@ extern bool opt_autofan; extern bool opt_autoengine; extern bool use_curses; extern char *opt_api_allow; +extern bool opt_api_mcast; +extern char *opt_api_mcast_addr; +extern char *opt_api_mcast_code; +extern int opt_api_mcast_port; extern char *opt_api_groups; extern char *opt_api_description; extern int opt_api_port;