cpuminer/util.c

656 lines
14 KiB
C
Raw Normal View History

2010-11-25 10:03:59 +01:00
/*
2012-02-12 23:47:20 +01:00
* Copyright 2010 Jeff Garzik, 2012 pooler
2010-11-25 10:03:59 +01:00
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
2011-03-15 04:17:34 +01:00
* Software Foundation; either version 2 of the License, or (at your option)
2010-11-25 10:03:59 +01:00
* any later version. See COPYING for more details.
*/
#define _GNU_SOURCE
#include "cpuminer-config.h"
#include <stdio.h>
#include <stdlib.h>
2011-03-18 07:53:13 +01:00
#include <ctype.h>
#include <stdarg.h>
2010-11-25 10:03:59 +01:00
#include <string.h>
2012-03-10 13:37:33 +01:00
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
2010-11-25 10:03:59 +01:00
#include <jansson.h>
#include <curl/curl.h>
#include <time.h>
#if defined(WIN32)
#include <winsock2.h>
#include <mstcpip.h>
#else
#include <sys/socket.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#endif
#include "compat.h"
2010-11-25 10:03:59 +01:00
#include "miner.h"
2011-03-15 04:17:34 +01:00
#include "elist.h"
2010-11-25 10:03:59 +01:00
struct data_buffer {
void *buf;
size_t len;
};
struct upload_buffer {
const void *buf;
size_t len;
size_t pos;
2010-11-25 10:03:59 +01:00
};
2011-03-18 07:53:13 +01:00
struct header_info {
char *lp_path;
char *reason;
2011-03-18 07:53:13 +01:00
};
2011-03-15 04:17:34 +01:00
struct tq_ent {
void *data;
struct list_head q_node;
};
struct thread_q {
struct list_head q;
bool frozen;
pthread_mutex_t mutex;
pthread_cond_t cond;
};
void applog(int prio, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
#ifdef HAVE_SYSLOG_H
if (use_syslog) {
va_list ap2;
char *buf;
int len;
va_copy(ap2, ap);
len = vsnprintf(NULL, 0, fmt, ap2) + 1;
va_end(ap2);
buf = alloca(len);
if (vsnprintf(buf, len, fmt, ap) >= 0)
syslog(prio, "%s", buf);
}
#else
if (0) {}
#endif
else {
char *f;
int len;
time_t now;
struct tm tm, *tm_p;
time(&now);
pthread_mutex_lock(&applog_lock);
tm_p = localtime(&now);
memcpy(&tm, tm_p, sizeof(tm));
pthread_mutex_unlock(&applog_lock);
len = 40 + strlen(fmt) + 2;
f = alloca(len);
sprintf(f, "[%d-%02d-%02d %02d:%02d:%02d] %s\n",
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
tm.tm_hour,
tm.tm_min,
tm.tm_sec,
fmt);
pthread_mutex_lock(&applog_lock);
vfprintf(stderr, f, ap); /* atomic write to stderr */
pthread_mutex_unlock(&applog_lock);
}
va_end(ap);
}
2010-11-25 10:03:59 +01:00
static void databuf_free(struct data_buffer *db)
{
if (!db)
return;
2011-03-15 04:17:34 +01:00
2010-11-25 10:03:59 +01:00
free(db->buf);
memset(db, 0, sizeof(*db));
}
static size_t all_data_cb(const void *ptr, size_t size, size_t nmemb,
void *user_data)
{
struct data_buffer *db = user_data;
size_t len = size * nmemb;
size_t oldlen, newlen;
void *newmem;
2012-02-03 23:47:04 +01:00
static const unsigned char zero = 0;
2010-11-25 10:03:59 +01:00
oldlen = db->len;
newlen = oldlen + len;
newmem = realloc(db->buf, newlen + 1);
if (!newmem)
return 0;
db->buf = newmem;
db->len = newlen;
memcpy(db->buf + oldlen, ptr, len);
memcpy(db->buf + newlen, &zero, 1); /* null terminate */
return len;
}
static size_t upload_data_cb(void *ptr, size_t size, size_t nmemb,
void *user_data)
{
struct upload_buffer *ub = user_data;
int len = size * nmemb;
if (len > ub->len - ub->pos)
len = ub->len - ub->pos;
2010-11-25 10:03:59 +01:00
if (len) {
memcpy(ptr, ub->buf + ub->pos, len);
ub->pos += len;
2010-11-25 10:03:59 +01:00
}
return len;
}
#if LIBCURL_VERSION_NUM >= 0x071200
static int seek_data_cb(void *user_data, curl_off_t offset, int origin)
{
struct upload_buffer *ub = user_data;
switch (origin) {
case SEEK_SET:
ub->pos = offset;
break;
case SEEK_CUR:
ub->pos += offset;
break;
case SEEK_END:
ub->pos = ub->len + offset;
break;
default:
return 1; /* CURL_SEEKFUNC_FAIL */
}
return 0; /* CURL_SEEKFUNC_OK */
}
#endif
2011-03-18 07:53:13 +01:00
static size_t resp_hdr_cb(void *ptr, size_t size, size_t nmemb, void *user_data)
{
struct header_info *hi = user_data;
size_t remlen, slen, ptrlen = size * nmemb;
char *rem, *val = NULL, *key = NULL;
void *tmp;
val = calloc(1, ptrlen);
key = calloc(1, ptrlen);
if (!key || !val)
goto out;
tmp = memchr(ptr, ':', ptrlen);
if (!tmp || (tmp == ptr)) /* skip empty keys / blanks */
goto out;
slen = tmp - ptr;
if ((slen + 1) == ptrlen) /* skip key w/ no value */
goto out;
memcpy(key, ptr, slen); /* store & nul term key */
key[slen] = 0;
rem = ptr + slen + 1; /* trim value's leading whitespace */
remlen = ptrlen - slen - 1;
while ((remlen > 0) && (isspace(*rem))) {
remlen--;
rem++;
}
memcpy(val, rem, remlen); /* store value, trim trailing ws */
val[remlen] = 0;
while ((*val) && (isspace(val[strlen(val) - 1]))) {
val[strlen(val) - 1] = 0;
}
if (!*val) /* skip blank value */
goto out;
if (!strcasecmp("X-Long-Polling", key)) {
hi->lp_path = val; /* steal memory reference */
val = NULL;
}
if (!strcasecmp("X-Reject-Reason", key)) {
hi->reason = val; /* steal memory reference */
val = NULL;
}
2011-03-18 07:53:13 +01:00
out:
free(key);
free(val);
return ptrlen;
}
#if LIBCURL_VERSION_NUM >= 0x070f06
static int json_rpc_call_lp_cb(void *userdata, curl_socket_t fd,
curlsocktype purpose)
{
int keepalive = 1;
int tcp_keepcnt = 3;
int tcp_keepidle = 50;
int tcp_keepintvl = 50;
#ifndef WIN32
if (unlikely(setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &keepalive,
sizeof(keepalive))))
return 1;
#ifdef __linux
if (unlikely(setsockopt(fd, SOL_TCP, TCP_KEEPCNT,
&tcp_keepcnt, sizeof(tcp_keepcnt))))
return 1;
if (unlikely(setsockopt(fd, SOL_TCP, TCP_KEEPIDLE,
&tcp_keepidle, sizeof(tcp_keepidle))))
return 1;
if (unlikely(setsockopt(fd, SOL_TCP, TCP_KEEPINTVL,
&tcp_keepintvl, sizeof(tcp_keepintvl))))
return 1;
#endif /* __linux */
#ifdef __APPLE_CC__
if (unlikely(setsockopt(fd, IPPROTO_TCP, TCP_KEEPALIVE,
&tcp_keepintvl, sizeof(tcp_keepintvl))))
return 1;
#endif /* __APPLE_CC__ */
#else /* WIN32 */
struct tcp_keepalive vals;
vals.onoff = 1;
vals.keepalivetime = tcp_keepidle * 1000;
vals.keepaliveinterval = tcp_keepintvl * 1000;
DWORD outputBytes;
if (unlikely(WSAIoctl(fd, SIO_KEEPALIVE_VALS, &vals, sizeof(vals),
NULL, 0, &outputBytes, NULL, NULL)))
return 1;
#endif /* WIN32 */
return 0;
}
#endif
json_t *json_rpc_call(CURL *curl, const char *url,
2011-03-18 07:53:13 +01:00
const char *userpass, const char *rpc_req,
2012-01-21 00:31:51 +01:00
bool longpoll_scan, bool longpoll, int *curl_err)
2010-11-25 10:03:59 +01:00
{
json_t *val, *err_val, *res_val;
2010-11-25 10:03:59 +01:00
int rc;
2012-03-10 23:29:11 +01:00
struct data_buffer all_data = {0};
2010-11-25 10:03:59 +01:00
struct upload_buffer upload_data;
2012-03-10 23:29:11 +01:00
json_error_t err;
2010-11-25 10:03:59 +01:00
struct curl_slist *headers = NULL;
char len_hdr[64];
char curl_err_str[CURL_ERROR_SIZE];
long timeout = longpoll ? opt_timeout : 30;
2012-03-10 23:29:11 +01:00
struct header_info hi = {0};
bool lp_scanning = longpoll_scan && !have_longpoll;
2010-11-25 10:03:59 +01:00
/* it is assumed that 'curl' is freshly [re]initialized at this pt */
2010-11-25 10:03:59 +01:00
if (opt_protocol)
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1);
curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_ENCODING, "");
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
2011-12-16 18:01:45 +01:00
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, 1);
2010-11-25 10:03:59 +01:00
curl_easy_setopt(curl, CURLOPT_TCP_NODELAY, 1);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, all_data_cb);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &all_data);
curl_easy_setopt(curl, CURLOPT_READFUNCTION, upload_data_cb);
curl_easy_setopt(curl, CURLOPT_READDATA, &upload_data);
#if LIBCURL_VERSION_NUM >= 0x071200
curl_easy_setopt(curl, CURLOPT_SEEKFUNCTION, &seek_data_cb);
curl_easy_setopt(curl, CURLOPT_SEEKDATA, &upload_data);
#endif
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, curl_err_str);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1);
2011-03-18 07:53:13 +01:00
curl_easy_setopt(curl, CURLOPT_TIMEOUT, timeout);
curl_easy_setopt(curl, CURLOPT_HEADERFUNCTION, resp_hdr_cb);
curl_easy_setopt(curl, CURLOPT_HEADERDATA, &hi);
2012-02-26 01:34:58 +01:00
if (opt_proxy) {
curl_easy_setopt(curl, CURLOPT_PROXY, opt_proxy);
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, opt_proxy_type);
}
2010-11-25 10:03:59 +01:00
if (userpass) {
curl_easy_setopt(curl, CURLOPT_USERPWD, userpass);
curl_easy_setopt(curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
}
#if LIBCURL_VERSION_NUM >= 0x070f06
if (longpoll)
curl_easy_setopt(curl, CURLOPT_SOCKOPTFUNCTION, json_rpc_call_lp_cb);
#endif
2010-11-25 10:03:59 +01:00
curl_easy_setopt(curl, CURLOPT_POST, 1);
if (opt_protocol)
applog(LOG_DEBUG, "JSON protocol request:\n%s\n", rpc_req);
2010-11-25 10:03:59 +01:00
upload_data.buf = rpc_req;
upload_data.len = strlen(rpc_req);
upload_data.pos = 0;
2010-11-25 10:03:59 +01:00
sprintf(len_hdr, "Content-Length: %lu",
(unsigned long) upload_data.len);
headers = curl_slist_append(headers,
"Content-Type: application/json");
2010-11-25 10:03:59 +01:00
headers = curl_slist_append(headers, len_hdr);
headers = curl_slist_append(headers, "User-Agent: " PACKAGE_STRING);
headers = curl_slist_append(headers,
"X-Mining-Extensions: midstate");
headers = curl_slist_append(headers, "Accept:"); /* disable Accept hdr*/
2010-11-25 10:03:59 +01:00
headers = curl_slist_append(headers, "Expect:"); /* disable Expect hdr*/
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
rc = curl_easy_perform(curl);
2012-01-21 00:31:51 +01:00
if (curl_err != NULL)
*curl_err = rc;
if (rc) {
2011-12-04 22:55:30 +01:00
if (!(longpoll && rc == CURLE_OPERATION_TIMEDOUT))
applog(LOG_ERR, "HTTP request failed: %s", curl_err_str);
2010-11-25 10:03:59 +01:00
goto err_out;
}
2010-11-25 10:03:59 +01:00
2011-03-18 07:53:13 +01:00
/* If X-Long-Polling was found, activate long polling */
if (lp_scanning && hi.lp_path) {
2011-03-18 07:53:13 +01:00
have_longpoll = true;
tq_push(thr_info[longpoll_thr_id].q, hi.lp_path);
} else
free(hi.lp_path);
hi.lp_path = NULL;
2012-02-03 23:47:04 +01:00
if (!all_data.buf) {
applog(LOG_ERR, "Empty data received in json_rpc_call.");
goto err_out;
}
2012-03-10 23:29:11 +01:00
#if JANSSON_VERSION_HEX >= 0x020000
val = json_loads(all_data.buf, 0, &err);
#else
val = json_loads(all_data.buf, &err);
#endif
2010-11-25 10:03:59 +01:00
if (!val) {
applog(LOG_ERR, "JSON decode failed(%d): %s", err.line, err.text);
2010-11-25 10:03:59 +01:00
goto err_out;
}
if (opt_protocol) {
char *s = json_dumps(val, JSON_INDENT(3));
applog(LOG_DEBUG, "JSON protocol response:\n%s", s);
2010-11-25 10:03:59 +01:00
free(s);
}
/* JSON-RPC valid response returns a non-null 'result',
2012-03-10 23:29:11 +01:00
* and a null 'error'. */
res_val = json_object_get(val, "result");
err_val = json_object_get(val, "error");
if (!res_val || json_is_null(res_val) ||
(err_val && !json_is_null(err_val))) {
char *s;
if (err_val)
s = json_dumps(err_val, JSON_INDENT(3));
else
s = strdup("(unknown reason)");
applog(LOG_ERR, "JSON-RPC call failed: %s", s);
free(s);
2011-03-15 04:17:34 +01:00
goto err_out;
}
if (hi.reason)
json_object_set_new(val, "reject-reason", json_string(hi.reason));
2010-11-25 10:03:59 +01:00
databuf_free(&all_data);
curl_slist_free_all(headers);
curl_easy_reset(curl);
2010-11-25 10:03:59 +01:00
return val;
err_out:
databuf_free(&all_data);
curl_slist_free_all(headers);
curl_easy_reset(curl);
2010-11-25 10:03:59 +01:00
return NULL;
}
2011-03-15 04:17:34 +01:00
char *bin2hex(const unsigned char *p, size_t len)
2010-11-25 10:03:59 +01:00
{
int i;
char *s = malloc((len * 2) + 1);
if (!s)
return NULL;
2011-03-15 04:17:34 +01:00
2010-11-25 10:03:59 +01:00
for (i = 0; i < len; i++)
sprintf(s + (i * 2), "%02x", (unsigned int) p[i]);
return s;
}
bool hex2bin(unsigned char *p, const char *hexstr, size_t len)
{
while (*hexstr && len) {
char hex_byte[3];
unsigned int v;
if (!hexstr[1]) {
applog(LOG_ERR, "hex2bin str truncated");
2010-11-25 10:03:59 +01:00
return false;
}
hex_byte[0] = hexstr[0];
hex_byte[1] = hexstr[1];
hex_byte[2] = 0;
if (sscanf(hex_byte, "%x", &v) != 1) {
applog(LOG_ERR, "hex2bin sscanf '%s' failed", hex_byte);
2010-11-25 10:03:59 +01:00
return false;
}
*p = (unsigned char) v;
p++;
hexstr += 2;
len--;
}
return (len == 0 && *hexstr == 0) ? true : false;
}
2010-11-27 05:12:24 +01:00
/* Subtract the `struct timeval' values X and Y,
storing the result in RESULT.
Return 1 if the difference is negative, otherwise 0. */
2012-03-10 23:29:11 +01:00
int timeval_subtract(struct timeval *result, struct timeval *x,
struct timeval *y)
2010-11-27 05:12:24 +01:00
{
2012-03-10 23:29:11 +01:00
/* Perform the carry for the later subtraction by updating Y. */
if (x->tv_usec < y->tv_usec) {
int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1;
y->tv_usec -= 1000000 * nsec;
y->tv_sec += nsec;
}
if (x->tv_usec - y->tv_usec > 1000000) {
int nsec = (x->tv_usec - y->tv_usec) / 1000000;
y->tv_usec += 1000000 * nsec;
y->tv_sec -= nsec;
}
/* Compute the time remaining to wait.
* `tv_usec' is certainly positive. */
result->tv_sec = x->tv_sec - y->tv_sec;
result->tv_usec = x->tv_usec - y->tv_usec;
/* Return 1 if result is negative. */
return x->tv_sec < y->tv_sec;
2010-11-27 05:12:24 +01:00
}
2012-03-10 13:37:33 +01:00
bool fulltest(const uint32_t *hash, const uint32_t *target)
{
int i;
bool rc = true;
2012-03-10 13:37:33 +01:00
for (i = 7; i >= 0; i--) {
if (hash[i] > target[i]) {
rc = false;
break;
}
2012-03-10 13:37:33 +01:00
if (hash[i] < target[i]) {
rc = true;
break;
}
}
if (opt_debug) {
2012-03-10 13:37:33 +01:00
uint32_t hash_be[32], target_be[32];
char *hash_str, *target_str;
for (i = 0; i < 8; i++) {
be32enc(hash_be + i, hash[7 - i]);
be32enc(target_be + i, target[7 - i]);
}
hash_str = bin2hex((unsigned char *)hash_be, 32);
target_str = bin2hex((unsigned char *)target_be, 32);
2012-03-10 13:37:33 +01:00
applog(LOG_DEBUG, "DEBUG: %s\nHash: %s\nTarget: %s",
rc ? "hash <= target"
: "hash > target (false positive)",
hash_str,
2012-03-10 13:37:33 +01:00
target_str);
free(hash_str);
free(target_str);
}
2012-03-10 13:37:33 +01:00
return rc;
}
2011-03-15 04:17:34 +01:00
struct thread_q *tq_new(void)
{
struct thread_q *tq;
tq = calloc(1, sizeof(*tq));
if (!tq)
return NULL;
INIT_LIST_HEAD(&tq->q);
pthread_mutex_init(&tq->mutex, NULL);
pthread_cond_init(&tq->cond, NULL);
return tq;
}
void tq_free(struct thread_q *tq)
{
struct tq_ent *ent, *iter;
if (!tq)
return;
list_for_each_entry_safe(ent, iter, &tq->q, q_node) {
list_del(&ent->q_node);
free(ent);
}
pthread_cond_destroy(&tq->cond);
pthread_mutex_destroy(&tq->mutex);
memset(tq, 0, sizeof(*tq)); /* poison */
free(tq);
}
static void tq_freezethaw(struct thread_q *tq, bool frozen)
{
pthread_mutex_lock(&tq->mutex);
tq->frozen = frozen;
pthread_cond_signal(&tq->cond);
pthread_mutex_unlock(&tq->mutex);
}
void tq_freeze(struct thread_q *tq)
{
tq_freezethaw(tq, true);
}
void tq_thaw(struct thread_q *tq)
{
tq_freezethaw(tq, false);
}
bool tq_push(struct thread_q *tq, void *data)
{
struct tq_ent *ent;
bool rc = true;
ent = calloc(1, sizeof(*ent));
if (!ent)
return false;
ent->data = data;
INIT_LIST_HEAD(&ent->q_node);
pthread_mutex_lock(&tq->mutex);
if (!tq->frozen) {
list_add_tail(&ent->q_node, &tq->q);
} else {
free(ent);
rc = false;
}
pthread_cond_signal(&tq->cond);
pthread_mutex_unlock(&tq->mutex);
return rc;
}
void *tq_pop(struct thread_q *tq, const struct timespec *abstime)
{
struct tq_ent *ent;
void *rval = NULL;
int rc;
pthread_mutex_lock(&tq->mutex);
if (!list_empty(&tq->q))
goto pop;
if (abstime)
rc = pthread_cond_timedwait(&tq->cond, &tq->mutex, abstime);
else
rc = pthread_cond_wait(&tq->cond, &tq->mutex);
if (rc)
goto out;
if (list_empty(&tq->q))
goto out;
pop:
ent = list_entry(tq->q.next, struct tq_ent, q_node);
rval = ent->data;
list_del(&ent->q_node);
free(ent);
out:
pthread_mutex_unlock(&tq->mutex);
return rval;
}