API
API
API
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <sys/types.h>
#include "compat.h"
#include "miner.h"
#include "util.h"
#include "klist.h"
struct WSAERRORS {
int id;
char *code;
} WSAErrors[] = {
{ 0, "No error" },
{ WSAEINTR, "Interrupted system call" },
{ WSAEBADF, "Bad file number" },
{ WSAEACCES, "Permission denied" },
{ WSAEFAULT, "Bad address" },
{ WSAEINVAL, "Invalid argument" },
{ WSAEMFILE, "Too many open sockets" },
{ WSAEWOULDBLOCK, "Operation would block" },
{ WSAEINPROGRESS, "Operation now in progress" },
{ WSAEALREADY, "Operation already in progress" },
{ WSAENOTSOCK, "Socket operation on non-socket" },
{ WSAEDESTADDRREQ, "Destination address required" },
{ WSAEMSGSIZE, "Message too long" },
{ WSAEPROTOTYPE, "Protocol wrong type for socket" },
{ WSAENOPROTOOPT, "Bad protocol option" },
{ WSAEPROTONOSUPPORT, "Protocol not supported" },
{ WSAESOCKTNOSUPPORT, "Socket type not supported" },
{ WSAEOPNOTSUPP, "Operation not supported on socket" },
{ WSAEPFNOSUPPORT, "Protocol family not supported" },
{ WSAEAFNOSUPPORT, "Address family not supported" },
{ WSAEADDRINUSE, "Address already in use" },
{ WSAEADDRNOTAVAIL, "Can't assign requested address" },
{ WSAENETDOWN, "Network is down" },
{ WSAENETUNREACH, "Network is unreachable" },
{ WSAENETRESET, "Net connection reset" },
{ WSAECONNABORTED, "Software caused connection abort" },
{ WSAECONNRESET, "Connection reset by peer" },
{ WSAENOBUFS, "No buffer space available" },
{ WSAEISCONN, "Socket is already connected" },
{ WSAENOTCONN, "Socket is not connected" },
{ WSAESHUTDOWN, "Can't send after socket shutdown" },
{ WSAETOOMANYREFS, "Too many references, can't splice" },
{ WSAETIMEDOUT, "Connection timed out" },
{ WSAECONNREFUSED, "Connection refused" },
{ WSAELOOP, "Too many levels of symbolic links" },
{ WSAENAMETOOLONG, "File name too long" },
{ WSAEHOSTDOWN, "Host is down" },
{ WSAEHOSTUNREACH, "No route to host" },
{ WSAENOTEMPTY, "Directory not empty" },
{ WSAEPROCLIM, "Too many processes" },
{ WSAEUSERS, "Too many users" },
{ WSAEDQUOT, "Disc quota exceeded" },
{ WSAESTALE, "Stale NFS file handle" },
{ WSAEREMOTE, "Too many levels of remote in path" },
{ WSASYSNOTREADY, "Network system is unavailable" },
{ WSAVERNOTSUPPORTED, "Winsock version out of range" },
{ WSANOTINITIALISED, "WSAStartup not yet called" },
{ WSAEDISCON, "Graceful shutdown in progress" },
{ WSAHOST_NOT_FOUND, "Host not found" },
{ WSANO_DATA, "No host data of that type was found" },
{ -1, "Unknown error code" }
};
char *WSAErrorMsg(void) {
int i;
int id = WSAGetLastError();
/* Assume none of them are actually -1 */
for (i = 0; WSAErrors[i].id != -1; i++)
if (WSAErrors[i].id == id)
break;
return &(WSAbuf[0]);
}
#endif
"";
static const char *OSINFO =
#if defined(__linux)
"Linux";
#else
#if defined(__APPLE__)
"Apple";
#else
#if defined (WIN32)
"Windows";
#else
#if defined(unix)
"Unix";
#else
"Unknown";
#endif
#endif
#endif
#endif
#ifdef HAVE_AN_FPGA
#define _PGA "PGA"
#endif
#ifdef HAVE_AN_ASIC
#define _ASC "ASC"
#endif
#ifdef HAVE_AN_FPGA
#define JSON_PGA JSON1 _PGA JSON2
#endif
#ifdef HAVE_AN_ASIC
#define JSON_ASC JSON1 _ASC JSON2
#endif
#define MSG_POOL 7
#define MSG_NOPOOL 8
#define MSG_DEVS 9
#define MSG_NODEVS 10
#define MSG_SUMM 11
#define MSG_INVCMD 14
#define MSG_MISID 15
#define MSG_VERSION 22
#define MSG_INVJSON 23
#define MSG_MISCMD 24
#define MSG_MISPID 25
#define MSG_INVPID 26
#define MSG_SWITCHP 27
#define MSG_MISVAL 28
#define MSG_NOADL 29
#define MSG_INVINT 31
#define MSG_MINECONFIG 33
#define MSG_MISFN 42
#define MSG_BADFN 43
#define MSG_SAVED 44
#define MSG_ACCDENY 45
#define MSG_ACCOK 46
#define MSG_ENAPOOL 47
#define MSG_DISPOOL 48
#define MSG_ALRENAP 49
#define MSG_ALRDISP 50
#define MSG_DISLASTP 51
#define MSG_MISPDP 52
#define MSG_INVPDP 53
#define MSG_TOOMANYP 54
#define MSG_ADDPOOL 55
#ifdef HAVE_AN_FPGA
#define MSG_PGANON 56
#define MSG_PGADEV 57
#define MSG_INVPGA 58
#endif
#define MSG_NUMPGA 59
#define MSG_NOTIFY 60
#ifdef HAVE_AN_FPGA
#define MSG_PGALRENA 61
#define MSG_PGALRDIS 62
#define MSG_PGAENA 63
#define MSG_PGADIS 64
#define MSG_PGAUNW 65
#endif
#define MSG_REMLASTP 66
#define MSG_ACTPOOL 67
#define MSG_REMPOOL 68
#define MSG_DEVDETAILS 69
#define MSG_MINESTATS 70
#define MSG_MISCHK 71
#define MSG_CHECK 72
#define MSG_POOLPRIO 73
#define MSG_DUPPID 74
#define MSG_MISBOOL 75
#define MSG_INVBOOL 76
#define MSG_FOO 77
#define MSG_MINECOIN 78
#define MSG_DEBUGSET 79
#define MSG_PGAIDENT 80
#define MSG_PGANOID 81
#define MSG_SETCONFIG 82
#define MSG_UNKCON 83
#define MSG_INVNUM 84
#define MSG_CONPAR 85
#define MSG_CONVAL 86
#define MSG_USBSTA 87
#define MSG_NOUSTA 88
#ifdef HAVE_AN_FPGA
#define MSG_MISPGAOPT 89
#define MSG_PGANOSET 90
#define MSG_PGAHELP 91
#define MSG_PGASETOK 92
#define MSG_PGASETERR 93
#endif
#define MSG_ZERMIS 94
#define MSG_ZERINV 95
#define MSG_ZERSUM 96
#define MSG_ZERNOSUM 97
#define MSG_PGAUSBNODEV 98
#define MSG_INVHPLG 99
#define MSG_HOTPLUG 100
#define MSG_DISHPLG 101
#define MSG_NOHPLG 102
#define MSG_MISHPLG 103
#ifdef HAVE_AN_ASIC
#define MSG_MISASCOPT 116
#define MSG_ASCNOSET 117
#define MSG_ASCHELP 118
#define MSG_ASCSETOK 119
#define MSG_ASCSETERR 120
#endif
enum code_severity {
SEVERITY_ERR,
SEVERITY_WARN,
SEVERITY_INFO,
SEVERITY_SUCC,
SEVERITY_FAIL
};
enum code_parameters {
PARAM_PGA,
PARAM_ASC,
PARAM_PID,
PARAM_PGAMAX,
PARAM_ASCMAX,
PARAM_PMAX,
PARAM_POOLMAX,
PARAM_CMD,
PARAM_POOL,
PARAM_STR,
PARAM_BOTH,
PARAM_BOOL,
PARAM_SET,
PARAM_INT,
PARAM_NONE
};
struct CODES {
const enum code_severity severity;
const int code;
const enum code_parameters params;
const char *description;
} codes[] = {
{ SEVERITY_SUCC, MSG_POOL, PARAM_PMAX, "%d Pool(s)" },
{ SEVERITY_ERR, MSG_NOPOOL, PARAM_NONE, "No pools" },
struct IPACCESS {
struct in6_addr ip;
struct in6_addr mask;
char group;
};
struct APIGROUPS {
// This becomes a string like: "|cmd1|cmd2|cmd3|" so it's quick to search
char *commands;
} apigroups['Z' - 'A' + 1]; // only A=0 to Z=25 (R: noprivs, W: allprivs)
struct io_data {
size_t siz;
char *ptr;
char *cur;
bool sock;
bool close;
};
struct io_list {
struct io_data *io_data;
struct io_list *prev;
struct io_list *next;
};
io_data = cgmalloc(sizeof(*io_data));
io_data->ptr = cgmalloc(initial);
io_data->siz = initial;
io_data->sock = socket_buf;
io_reinit(io_data);
io_list = cgmalloc(sizeof(*io_list));
io_list->io_data = io_data;
if (io_head) {
io_list->next = io_head;
io_list->prev = io_head->prev;
io_list->next->prev = io_list;
io_list->prev->next = io_list;
} else {
io_list->prev = io_list;
io_list->next = io_list;
io_head = io_list;
}
return io_data;
}
len = strlen(buf);
dif = io_data->cur - io_data->ptr;
// send will always have enough space to add the JSON
tot = len + 1 + dif + sizeof(JSON_CLOSE) + sizeof(JSON_END);
return true;
}
if (io_head) {
io_list = io_head;
do {
io_next = io_list->next;
free(io_list->io_data->ptr);
free(io_list->io_data);
free(io_list);
io_list = io_next;
} while (io_list != io_head);
io_head = NULL;
}
}
if (count == 0)
return str;
ptr = buf;
while (*str)
switch (*str) {
case ',':
case '|':
case '=':
if (!isjson)
*(ptr++) = '\\';
*(ptr++) = *(str++);
break;
case '"':
if (isjson)
*(ptr++) = '\\';
*(ptr++) = *(str++);
break;
case '\\':
*(ptr++) = '\\';
*(ptr++) = *(str++);
break;
default:
*(ptr++) = *(str++);
break;
}
*ptr = '\0';
return buf;
}
if (root) {
if (extra) {
// extra tail
tmp = extra->prev;
return root;
}
api_data->name = strdup(name);
api_data->type = type;
if (root == NULL) {
root = api_data;
root->prev = root;
root->next = root;
} else {
api_data->prev = root->prev;
root->prev = api_data;
api_data->next = root;
api_data->prev->next = api_data;
}
api_data->data_was_malloc = copy_data;
if (!copy_data)
api_data->data = data;
else
switch(type) {
case API_ESCAPE:
case API_STRING:
case API_CONST:
api_data->data = cgmalloc(strlen((char *)data) + 1);
strcpy((char*)(api_data->data), (char *)data);
break;
case API_UINT8:
/* Most OSs won't really alloc less than 4 */
api_data->data = cgmalloc(4);
*(uint8_t *)api_data->data = *(uint8_t *)data;
break;
case API_INT16:
/* Most OSs won't really alloc less than 4 */
api_data->data = cgmalloc(4);
*(int16_t *)api_data->data = *(int16_t *)data;
break;
case API_UINT16:
/* Most OSs won't really alloc less than 4 */
api_data->data = cgmalloc(4);
*(uint16_t *)api_data->data = *(uint16_t *)data;
break;
case API_INT:
api_data->data = cgmalloc(sizeof(int));
*((int *)(api_data->data)) = *((int *)data);
break;
case API_UINT:
api_data->data = cgmalloc(sizeof(unsigned int));
*((unsigned int *)(api_data->data)) = *((unsigned int
*)data);
break;
case API_UINT32:
api_data->data = cgmalloc(sizeof(uint32_t));
*((uint32_t *)(api_data->data)) = *((uint32_t *)data);
break;
case API_HEX32:
api_data->data = cgmalloc(sizeof(uint32_t));
*((uint32_t *)(api_data->data)) = *((uint32_t *)data);
break;
case API_UINT64:
api_data->data = cgmalloc(sizeof(uint64_t));
*((uint64_t *)(api_data->data)) = *((uint64_t *)data);
break;
case API_INT64:
api_data->data = cgmalloc(sizeof(int64_t));
*((int64_t *)(api_data->data)) = *((int64_t *)data);
break;
case API_DOUBLE:
case API_ELAPSED:
case API_MHS:
case API_MHTOTAL:
case API_UTILITY:
case API_FREQ:
case API_HS:
case API_DIFF:
case API_PERCENT:
api_data->data = cgmalloc(sizeof(double));
*((double *)(api_data->data)) = *((double *)data);
break;
case API_BOOL:
api_data->data = cgmalloc(sizeof(bool));
*((bool *)(api_data->data)) = *((bool *)data);
break;
case API_TIMEVAL:
api_data->data = cgmalloc(sizeof(struct timeval));
memcpy(api_data->data, data, sizeof(struct timeval));
break;
case API_TIME:
api_data->data = cgmalloc(sizeof(time_t));
*(time_t *)(api_data->data) = *((time_t *)data);
break;
case API_VOLTS:
case API_TEMP:
case API_AVG:
api_data->data = cgmalloc(sizeof(float));
*((float *)(api_data->data)) = *((float *)data);
break;
default:
applog(LOG_ERR, "API: unknown1 data type %d ignored",
type);
api_data->type = API_STRING;
api_data->data_was_malloc = false;
api_data->data = (void *)UNKNOWN;
break;
}
return root;
}
struct api_data *api_add_escape(struct api_data *root, char *name, char *data, bool
copy_data)
{
return api_add_data_full(root, name, API_ESCAPE, (void *)data, copy_data);
}
struct api_data *api_add_string(struct api_data *root, char *name, char *data, bool
copy_data)
{
return api_add_data_full(root, name, API_STRING, (void *)data, copy_data);
}
struct api_data *api_add_const(struct api_data *root, char *name, const char *data,
bool copy_data)
{
return api_add_data_full(root, name, API_CONST, (void *)data, copy_data);
}
struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool
copy_data)
{
return api_add_data_full(root, name, API_INT, (void *)data, copy_data);
}
struct api_data *api_add_bool(struct api_data *root, char *name, bool *data, bool
copy_data)
{
return api_add_data_full(root, name, API_BOOL, (void *)data, copy_data);
}
struct api_data *api_add_time(struct api_data *root, char *name, time_t *data, bool
copy_data)
{
return api_add_data_full(root, name, API_TIME, (void *)data, copy_data);
}
struct api_data *api_add_mhs(struct api_data *root, char *name, double *data, bool
copy_data)
{
return api_add_data_full(root, name, API_MHS, (void *)data, copy_data);
}
struct api_data *api_add_temp(struct api_data *root, char *name, float *data, bool
copy_data)
{
return api_add_data_full(root, name, API_TEMP, (void *)data, copy_data);
}
struct api_data *api_add_freq(struct api_data *root, char *name, double *data, bool
copy_data)
{
return api_add_data_full(root, name, API_FREQ, (void *)data, copy_data);
}
struct api_data *api_add_volts(struct api_data *root, char *name, float *data, bool
copy_data)
{
return api_add_data_full(root, name, API_VOLTS, (void *)data, copy_data);
}
struct api_data *api_add_hs(struct api_data *root, char *name, double *data, bool
copy_data)
{
return api_add_data_full(root, name, API_HS, (void *)data, copy_data);
}
struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool
copy_data)
{
return api_add_data_full(root, name, API_DIFF, (void *)data, copy_data);
}
struct api_data *api_add_avg(struct api_data *root, char *name, float *data, bool
copy_data)
{
return api_add_data_full(root, name, API_AVG, (void *)data, copy_data);
}
buf = DATASB(item)->buf;
siz = (size_t)strlen(str);
old_siz = DATASB(item)->siz;
new_siz = old_siz + siz + 1; // include '\0'
if (DATASB(item)->tot < new_siz) {
ext = (siz + 1) + SBEXTEND - ((siz + 1) % SBEXTEND);
DATASB(item)->buf = buf = cgrealloc(DATASB(item)->buf, DATASB(item)-
>tot + ext);
DATASB(item)->tot += ext;
}
memcpy(buf + old_siz, str, siz + 1);
DATASB(item)->siz += siz;
}
K_WLOCK(strbufs);
item = k_unlink_head(strbufs);
K_WUNLOCK(strbufs);
DATASB(item)->siz = 0;
if (precom)
add_item_buf(item, COMMA);
if (isjson)
add_item_buf(item, JSON0);
while (root) {
if (!first)
add_item_buf(item, COMMA);
else
first = false;
if (isjson)
add_item_buf(item, JSON1);
add_item_buf(item, root->name);
if (isjson)
add_item_buf(item, JSON1);
if (isjson)
add_item_buf(item, ":");
else
add_item_buf(item, "=");
first = false;
done = false;
switch(root->type) {
case API_STRING:
case API_CONST:
if (isjson)
add_item_buf(item, JSON1);
add_item_buf(item, (char *)(root->data));
if (isjson)
add_item_buf(item, JSON1);
done = true;
break;
case API_ESCAPE:
original = (char *)(root->data);
escape = escape_string((char *)(root->data), isjson);
if (isjson)
add_item_buf(item, JSON1);
add_item_buf(item, escape);
if (isjson)
add_item_buf(item, JSON1);
if (escape != original)
free(escape);
done = true;
break;
case API_UINT8:
snprintf(buf, sizeof(buf), "%u", *(uint8_t *)root->data);
break;
case API_INT16:
snprintf(buf, sizeof(buf), "%d", *(int16_t *)root->data);
break;
case API_UINT16:
snprintf(buf, sizeof(buf), "%u", *(uint16_t *)root->data);
break;
case API_INT:
snprintf(buf, sizeof(buf), "%d", *((int *)(root->data)));
break;
case API_UINT:
snprintf(buf, sizeof(buf), "%u", *((unsigned int *)(root-
>data)));
break;
case API_UINT32:
snprintf(buf, sizeof(buf), "%"PRIu32, *((uint32_t *)(root-
>data)));
break;
case API_HEX32:
if (isjson)
add_item_buf(item, JSON1);
snprintf(buf, sizeof(buf), "0x%08x", *((uint32_t *)(root-
>data)));
add_item_buf(item, buf);
if (isjson)
add_item_buf(item, JSON1);
done = true;
break;
case API_UINT64:
snprintf(buf, sizeof(buf), "%"PRIu64, *((uint64_t *)(root-
>data)));
break;
case API_INT64:
snprintf(buf, sizeof(buf), "%"PRId64, *((int64_t *)(root-
>data)));
break;
case API_TIME:
snprintf(buf, sizeof(buf), "%lu", *((unsigned long *)(root-
>data)));
break;
case API_DOUBLE:
snprintf(buf, sizeof(buf), "%f", *((double *)(root-
>data)));
break;
case API_ELAPSED:
snprintf(buf, sizeof(buf), "%.0f", *((double *)(root-
>data)));
break;
case API_UTILITY:
case API_FREQ:
case API_MHS:
snprintf(buf, sizeof(buf), "%.2f", *((double *)(root-
>data)));
break;
case API_VOLTS:
case API_AVG:
snprintf(buf, sizeof(buf), "%.3f", *((float *)(root-
>data)));
break;
case API_MHTOTAL:
snprintf(buf, sizeof(buf), "%.4f", *((double *)(root-
>data)));
break;
case API_HS:
snprintf(buf, sizeof(buf), "%.15f", *((double *)(root-
>data)));
break;
case API_DIFF:
snprintf(buf, sizeof(buf), "%.8f", *((double *)(root-
>data)));
break;
case API_BOOL:
snprintf(buf, sizeof(buf), "%s", *((bool *)(root->data)) ?
TRUESTR : FALSESTR);
break;
case API_TIMEVAL:
snprintf(buf, sizeof(buf), "%ld.%06ld",
(long)((struct timeval *)(root->data))->tv_sec,
(long)((struct timeval *)(root->data))->tv_usec);
break;
case API_TEMP:
snprintf(buf, sizeof(buf), "%.2f", *((float *)(root-
>data)));
break;
case API_PERCENT:
snprintf(buf, sizeof(buf), "%.4f", *((double *)(root-
>data)) * 100.0);
break;
default:
applog(LOG_ERR, "API: unknown2 data type %d ignored", root-
>type);
if (isjson)
add_item_buf(item, JSON1);
add_item_buf(item, UNKNOWN);
if (isjson)
add_item_buf(item, JSON1);
done = true;
break;
}
if (!done)
add_item_buf(item, buf);
free(root->name);
if (root->data_was_malloc)
free(root->data);
if (root->next == root) {
free(root);
root = NULL;
} else {
tmp = root;
root = tmp->next;
root->prev = tmp->prev;
root->prev->next = root;
free(tmp);
}
}
if (isjson)
add_item_buf(item, JSON5);
else
add_item_buf(item, SEPSTR);
io_add(io_data, DATASB(item)->buf);
K_WLOCK(strbufs);
k_add_head(strbufs, item);
K_WUNLOCK(strbufs);
return root;
}
#ifdef HAVE_AN_ASIC
static int numascs(void)
{
int count = 0;
int i;
rd_lock(&devices_lock);
for (i = 0; i < total_devices; i++) {
ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV)
}
rd_unlock(&devices_lock);
return count;
}
rd_lock(&devices_lock);
for (i = 0; i < total_devices; i++) {
ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV)
if (count == (ascid + 1))
goto foundit;
}
rd_unlock(&devices_lock);
return -1;
foundit:
rd_unlock(&devices_lock);
return i;
}
#endif
#ifdef HAVE_AN_FPGA
static int numpgas(void)
{
int count = 0;
int i;
rd_lock(&devices_lock);
for (i = 0; i < total_devices; i++) {
FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV)
}
rd_unlock(&devices_lock);
return count;
}
rd_lock(&devices_lock);
for (i = 0; i < total_devices; i++) {
FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV)
if (count == (pgaid + 1))
goto foundit;
}
rd_unlock(&devices_lock);
return -1;
foundit:
rd_unlock(&devices_lock);
return i;
}
#endif
if (isjson)
io_add(io_data, JSON_START JSON_STATUS);
switch(codes[i].params) {
case PARAM_PGA:
case PARAM_ASC:
case PARAM_PID:
case PARAM_INT:
snprintf(buf, LIMSIZ, codes[i].description, paramid);
break;
case PARAM_POOL:
snprintf(buf, LIMSIZ, codes[i].description, paramid,
pools[paramid]->rpc_url);
break;
#ifdef HAVE_AN_FPGA
case PARAM_PGAMAX:
pga = numpgas();
snprintf(buf, LIMSIZ, codes[i].description, paramid,
pga - 1);
break;
#endif
#ifdef HAVE_AN_ASIC
case PARAM_ASCMAX:
asc = numascs();
snprintf(buf, LIMSIZ, codes[i].description, paramid,
asc - 1);
break;
#endif
case PARAM_PMAX:
snprintf(buf, LIMSIZ, codes[i].description,
total_pools);
break;
case PARAM_POOLMAX:
snprintf(buf, LIMSIZ, codes[i].description, paramid,
total_pools - 1);
break;
case PARAM_DMAX:
#ifdef HAVE_AN_ASIC
asc = numascs();
#endif
#ifdef HAVE_AN_FPGA
pga = numpgas();
#endif
#if LOCK_TRACKING
cgtime(&now);
dt = now.tv_sec;
tm = localtime(&dt);
LOCKMSG("%d-%02d-%02d %02d:%02d:%02d",
tm->tm_year + 1900,
tm->tm_mon + 1,
tm->tm_mday,
tm->tm_hour,
tm->tm_min,
tm->tm_sec);
}
static LOCKLIST *newlock(void *lock, enum cglock_typ typ, const char *file, const
char *func, const int linenum)
{
LOCKLIST *list;
return list;
}
static LOCKINFO *findlock(void *lock, enum cglock_typ typ, const char *file, const
char *func, const int linenum)
{
LOCKLIST *look;
look = lockhead;
while (look) {
if (look->info->lock == lock)
break;
look = look->next;
}
if (!look)
look = newlock(lock, typ, file, func, linenum);
return look->info;
}
static void addgettry(LOCKINFO *info, uint64_t id, const char *file, const char
*func, const int linenum, bool get)
{
LOCKSTAT *stat;
LOCKLINE *line;
if (get)
info->gets++;
else
info->tries++;
stat->lock_id = id;
stat->file = file;
stat->func = func;
stat->linenum = linenum;
cgtime(&stat->tv);
line->stat = stat;
if (get) {
line->next = info->lockgets;
if (info->lockgets)
info->lockgets->prev = line;
info->lockgets = line;
} else {
line->next = info->locktries;
if (info->locktries)
info->locktries->prev = line;
info->locktries = line;
}
}
static void markgotdid(LOCKINFO *info, uint64_t id, const char *file, const char
*func, const int linenum, bool got, int ret)
{
LOCKLINE *line;
if (got)
info->gots++;
else {
if (ret == 0)
info->dids++;
else
info->didnts++;
}
if (got || ret == 0) {
info->lastgot.lock_id = id;
info->lastgot.file = file;
info->lastgot.func = func;
info->lastgot.linenum = linenum;
cgtime(&info->lastgot.tv);
}
if (got)
line = info->lockgets;
else
line = info->locktries;
while (line) {
if (line->stat->lock_id == id)
break;
line = line->next;
}
if (!line) {
lockmsgnow();
LOCKMSGFFL("ERROR attempt to mark a lock as '%s' that wasn't '%s' id=
%"PRIu64,
got ? "got" : "did/didnt", got ? "get" : "try", id);
}
// Unlink it
if (line->prev)
line->prev->next = line->next;
if (line->next)
line->next->prev = line->prev;
if (got) {
if (info->lockgets == line)
info->lockgets = line->next;
} else {
if (info->locktries == line)
info->locktries = line->next;
}
free(line->stat);
free(line);
}
uint64_t api_getlock(void *lock, const char *file, const char *func, const int
linenum)
{
LOCKINFO *info;
uint64_t id;
locklock();
lockunlock();
return id;
}
void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const
int linenum)
{
LOCKINFO *info;
locklock();
lockunlock();
}
uint64_t api_trylock(void *lock, const char *file, const char *func, const int
linenum)
{
LOCKINFO *info;
uint64_t id;
locklock();
lockunlock();
return id;
}
void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char
*func, const int linenum)
{
LOCKINFO *info;
locklock();
lockunlock();
}
void api_gunlock(void *lock, const char *file, const char *func, const int linenum)
{
LOCKINFO *info;
locklock();
lockunlock();
}
void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char
*func, const int linenum)
{
locklock();
lockunlock();
}
dt = stat->tv.tv_sec;
tm = localtime(&dt);
dsp_det(status, &(info->lastgot));
} else
LOCKMSGMORE("... unused ...");
if (info->lockgets) {
LOCKMSGMORE("BLOCKED gets (%"PRIu64")", info->gets - info->gots);
line = info->lockgets;
while (line) {
dsp_det("", line->stat);
line = line->next;
}
} else
LOCKMSGMORE("no blocked gets");
if (info->locktries) {
LOCKMSGMORE("BLOCKED tries (%"PRIu64")", info->tries - info->dids -
info->didnts);
line = info->lockgets;
while (line) {
dsp_det("", line->stat);
line = line->next;
}
} else
LOCKMSGMORE("no blocked tries");
}
void show_locks()
{
LOCKLIST *list;
locklock();
lockmsgnow();
list = lockhead;
if (!list)
LOCKMSG("no locks?!?\n");
else {
while (list) {
dsp_lock(list->info);
list = list->next;
}
}
LOCKMSGFLUSH();
lockunlock();
}
#endif
#ifdef HAVE_AN_ASIC
asccount = numascs();
#endif
#ifdef HAVE_AN_FPGA
pgacount = numpgas();
#endif
#ifdef HAVE_AN_ASIC
static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom)
{
struct api_data *root = NULL;
char *enabled;
char *status;
int numasc = numascs();
dev_runtime = cgpu_runtime(cgpu);
if (cgpu->deven != DEV_DISABLED)
enabled = (char *)YES;
else
enabled = (char *)NO;
#ifdef HAVE_AN_FPGA
static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom)
{
struct api_data *root = NULL;
char *enabled;
char *status;
int numpga = numpgas();
if (cgpu->dev_start_tv.tv_sec == 0)
dev_runtime = total_secs;
else {
cgtime(&now);
dev_runtime = tdiff(&now, &(cgpu->dev_start_tv));
}
#ifdef USE_MODMINER
if (cgpu->drv->drv_id == DRIVER_modminer)
frequency = cgpu->clock;
#endif
if (cgpu->deven != DEV_DISABLED)
enabled = (char *)YES;
else
enabled = (char *)NO;
#ifdef HAVE_AN_ASIC
numasc = numascs();
#endif
#ifdef HAVE_AN_FPGA
numpga = numpgas();
#endif
if (numpga == 0 && numasc == 0) {
message(io_data, MSG_NODEVS, 0, NULL, isjson);
return;
}
#ifdef HAVE_AN_ASIC
if (numasc > 0) {
for (i = 0; i < numasc; i++) {
ascstatus(io_data, i, isjson, isjson && devcount > 0);
devcount++;
}
}
#endif
#ifdef HAVE_AN_FPGA
if (numpga > 0) {
for (i = 0; i < numpga; i++) {
pgastatus(io_data, i, isjson, isjson && devcount > 0);
devcount++;
}
}
#endif
#ifdef HAVE_AN_ASIC
numasc = numascs();
#endif
#ifdef HAVE_AN_FPGA
numpga = numpgas();
#endif
#ifdef HAVE_AN_ASIC
if (numasc > 0) {
for (i = 0; i < numasc; i++) {
#ifdef USE_USBUTILS
int dev = ascdevice(i);
if (dev < 0) // Should never happen
continue;
devcount++;
}
}
#endif
#ifdef HAVE_AN_FPGA
if (numpga > 0) {
for (i = 0; i < numpga; i++) {
#ifdef USE_USBUTILS
int dev = pgadevice(i);
if (dev < 0) // Should never happen
continue;
#ifdef HAVE_AN_FPGA
static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
*param, bool isjson, __maybe_unused char group)
{
bool io_open = false;
int numpga = numpgas();
int id;
if (numpga == 0) {
message(io_data, MSG_PGANON, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numpga) {
message(io_data, MSG_INVPGA, id, NULL, isjson);
return;
}
if (isjson)
io_open = io_add(io_data, COMSTR JSON_PGA);
if (numpga == 0) {
message(io_data, MSG_PGANON, 0, NULL, isjson);
return;
}
if (param == NULL || *param == '\0') {
message(io_data, MSG_MISID, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numpga) {
message(io_data, MSG_INVPGA, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
if (cgpu->deven != DEV_DISABLED) {
message(io_data, MSG_PGALRENA, id, NULL, isjson);
return;
}
#if 0 /* A DISABLED device wont change status FIXME: should disabling make it WELL?
*/
if (cgpu->status != LIFE_WELL) {
message(io_data, MSG_PGAUNW, id, NULL, isjson);
return;
}
#endif
#ifdef USE_USBUTILS
if (cgpu->usbinfo.nodev) {
message(io_data, MSG_PGAUSBNODEV, id, NULL, isjson);
return;
}
#endif
if (numpga == 0) {
message(io_data, MSG_PGANON, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numpga) {
message(io_data, MSG_INVPGA, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
if (cgpu->deven == DEV_DISABLED) {
message(io_data, MSG_PGALRDIS, id, NULL, isjson);
return;
}
cgpu->deven = DEV_DISABLED;
if (numpga == 0) {
message(io_data, MSG_PGANON, 0, NULL, isjson);
return;
}
cgpu = get_devices(dev);
drv = cgpu->drv;
if (!drv->identify_device)
message(io_data, MSG_PGANOID, id, NULL, isjson);
else {
drv->identify_device(cgpu);
message(io_data, MSG_PGAIDENT, id, NULL, isjson);
}
}
#endif
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
if (isjson)
io_open = io_add(io_data, COMSTR JSON_POOLS);
if (pool->removed)
continue;
switch (pool->enabled) {
case POOL_DISABLED:
status = (char *)DISABLED;
break;
case POOL_REJECTING:
status = (char *)REJECTING;
break;
case POOL_ENABLED:
if (pool->idle)
status = (char *)DEAD;
else
status = (char *)ALIVE;
break;
default:
status = (char *)UNKNOWN;
break;
}
if (pool->hdr_path)
lp = (char *)YES;
else
lp = (char *)NO;
mutex_unlock(&hash_lock);
#ifdef HAVE_AN_FPGA
count = numpgas();
#endif
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
id = atoi(param);
cg_rlock(&control_lock);
if (id < 0 || id >= total_pools) {
cg_runlock(&control_lock);
message(io_data, MSG_INVPID, id, NULL, isjson);
return;
}
pool = pools[id];
pool->enabled = POOL_ENABLED;
cg_runlock(&control_lock);
switch_pools(pool);
*(dst_b++) = *(src_p++);
}
if (*src_p)
src_p++;
*(dst_b++) = '\0';
}
static bool pooldetails(char *param, char **url, char **user, char **pass)
{
char *ptr, *buf;
// copy url
copyadvanceafter(',', ¶m, &buf);
*user = buf;
// copy user
copyadvanceafter(',', ¶m, &buf);
*pass = buf;
// copy pass
copyadvanceafter(',', ¶m, &buf);
return true;
exitsama:
free(ptr);
return false;
}
pool = add_pool();
detect_stratum(pool, url);
add_pool_details(pool, true, url, user, pass);
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= total_pools) {
message(io_data, MSG_INVPID, id, NULL, isjson);
return;
}
pool = pools[id];
if (pool->enabled == POOL_ENABLED) {
message(io_data, MSG_ALRENAP, id, NULL, isjson);
return;
}
pool->enabled = POOL_ENABLED;
if (pool->prio < current_pool()->prio)
switch_pools(pool);
// TODO: all cgminer code needs a mutex added everywhere for change
// access to total_pools and also parts of the pools[] array,
// just copying total_pools here wont solve that
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
bool pools_changed[total_pools];
int new_prio[total_pools];
for (i = 0; i < total_pools; ++i)
pools_changed[i] = false;
next = param;
while (next && *next) {
ptr = next;
next = strchr(ptr, ',');
if (next)
*(next++) = '\0';
i = atoi(ptr);
if (i < 0 || i >= total_pools) {
message(io_data, MSG_INVPID, i, NULL, isjson);
return;
}
if (pools_changed[i]) {
message(io_data, MSG_DUPPID, i, NULL, isjson);
return;
}
pools_changed[i] = true;
new_prio[i] = prio++;
}
// In priority order, cycle through the unchanged pools and append them
for (pr = 0; pr < total_pools; pr++)
for (i = 0; i < total_pools; i++) {
if (!pools_changed[i] && pools[i]->prio == pr) {
pools[i]->prio = prio++;
pools_changed[i] = true;
break;
}
}
if (current_pool()->prio)
switch_pools(NULL);
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
if (param == NULL || *param == '\0') {
message(io_data, MSG_MISPID, 0, NULL, isjson);
return;
}
*(comma++) = '\0';
id = atoi(param);
if (id < 0 || id >= total_pools) {
message(io_data, MSG_INVPID, id, NULL, isjson);
return;
}
pool = pools[id];
quota = atoi(comma);
if (quota < 0) {
message(io_data, MSG_INVNEG, quota, pool->rpc_url, isjson);
return;
}
pool->quota = quota;
adjust_quota_gcd();
message(io_data, MSG_SETQUOTA, quota, pool->rpc_url, isjson);
}
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= total_pools) {
message(io_data, MSG_INVPID, id, NULL, isjson);
return;
}
pool = pools[id];
if (pool->enabled == POOL_DISABLED) {
message(io_data, MSG_ALRDISP, id, NULL, isjson);
return;
}
if (enabled_pools <= 1) {
message(io_data, MSG_DISLASTP, id, NULL, isjson);
return;
}
pool->enabled = POOL_DISABLED;
if (pool == current_pool())
switch_pools(NULL);
if (total_pools == 0) {
message(io_data, MSG_NOPOOL, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= total_pools) {
message(io_data, MSG_INVPID, id, NULL, isjson);
return;
}
if (total_pools <= 1) {
message(io_data, MSG_REMLASTP, id, NULL, isjson);
return;
}
pool = pools[id];
if (pool == current_pool())
switch_pools(NULL);
if (pool == current_pool()) {
message(io_data, MSG_ACTPOOL, id, NULL, isjson);
return;
}
pool->enabled = POOL_DISABLED;
rpc_url = escape_string(pool->rpc_url, isjson);
if (rpc_url != pool->rpc_url)
dofree = true;
remove_pool(pool);
bye = true;
do_a_quit = true;
}
bye = true;
do_a_restart = true;
}
void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, bool
isjson, __maybe_unused char group)
{
struct api_data *root = NULL;
char *reason;
if (cgpu->device_last_not_well == 0)
reason = REASON_NONE;
else
switch(cgpu->device_not_well_reason) {
case REASON_THREAD_FAIL_INIT:
reason = REASON_THREAD_FAIL_INIT_STR;
break;
case REASON_THREAD_ZERO_HASH:
reason = REASON_THREAD_ZERO_HASH_STR;
break;
case REASON_THREAD_FAIL_QUEUE:
reason = REASON_THREAD_FAIL_QUEUE_STR;
break;
case REASON_DEV_SICK_IDLE_60:
reason = REASON_DEV_SICK_IDLE_60_STR;
break;
case REASON_DEV_DEAD_IDLE_600:
reason = REASON_DEV_DEAD_IDLE_600_STR;
break;
case REASON_DEV_NOSTART:
reason = REASON_DEV_NOSTART_STR;
break;
case REASON_DEV_OVER_HEAT:
reason = REASON_DEV_OVER_HEAT_STR;
break;
case REASON_DEV_THERMAL_CUTOFF:
reason = REASON_DEV_THERMAL_CUTOFF_STR;
break;
case REASON_DEV_COMMS_ERROR:
reason = REASON_DEV_COMMS_ERROR_STR;
break;
default:
reason = REASON_UNKNOWN_STR;
break;
}
// ALL counters (and only counters) must start the name with a '*'
// Simplifies future external support for identifying new counters
root = api_add_int(root, "NOTIFY", &device, false);
root = api_add_string(root, "Name", cgpu->drv->name, false);
root = api_add_int(root, "ID", &(cgpu->device_id), false);
root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false);
root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well),
false);
root = api_add_string(root, "Reason Not Well", reason, false);
root = api_add_int(root, "*Thread Fail Init", &(cgpu-
>thread_fail_init_count), false);
root = api_add_int(root, "*Thread Zero Hash", &(cgpu-
>thread_zero_hash_count), false);
root = api_add_int(root, "*Thread Fail Queue", &(cgpu-
>thread_fail_queue_count), false);
root = api_add_int(root, "*Dev Sick Idle 60s", &(cgpu-
>dev_sick_idle_60_count), false);
root = api_add_int(root, "*Dev Dead Idle 600s", &(cgpu-
>dev_dead_idle_600_count), false);
root = api_add_int(root, "*Dev Nostart", &(cgpu->dev_nostart_count), false);
root = api_add_int(root, "*Dev Over Heat", &(cgpu->dev_over_heat_count),
false);
root = api_add_int(root, "*Dev Thermal Cutoff", &(cgpu-
>dev_thermal_cutoff_count), false);
root = api_add_int(root, "*Dev Comms Error", &(cgpu->dev_comms_error_count),
false);
root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count),
false);
if (total_devices == 0) {
message(io_data, MSG_NODEVS, 0, NULL, isjson);
return;
}
if (isjson)
io_open = io_add(io_data, COMSTR JSON_NOTIFY);
if (total_devices == 0) {
message(io_data, MSG_NODEVS, 0, NULL, isjson);
return;
}
if (isjson)
io_open = io_add(io_data, COMSTR JSON_DEVDETAILS);
write_config(fcfg);
fclose(fcfg);
static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_stats
*stats, struct cgminer_pool_stats *pool_stats, struct api_data *extra, struct
cgpu_info *cgpu, bool isjson)
{
struct api_data *root = NULL;
if (pool_stats) {
root = api_add_uint32(root, "Pool Calls", &(pool_stats->getwork_calls),
false);
root = api_add_uint32(root, "Pool Attempts", &(pool_stats-
>getwork_attempts), false);
root = api_add_timeval(root, "Pool Wait", &(pool_stats->getwork_wait),
false);
root = api_add_timeval(root, "Pool Max", &(pool_stats-
>getwork_wait_max), false);
root = api_add_timeval(root, "Pool Min", &(pool_stats-
>getwork_wait_min), false);
root = api_add_double(root, "Pool Av", &(pool_stats-
>getwork_wait_rolling), false);
root = api_add_bool(root, "Work Had Roll Time", &(pool_stats-
>hadrolltime), false);
root = api_add_bool(root, "Work Can Roll", &(pool_stats->canroll),
false);
root = api_add_bool(root, "Work Had Expire", &(pool_stats->hadexpire),
false);
root = api_add_uint32(root, "Work Roll Time", &(pool_stats->rolltime),
false);
root = api_add_diff(root, "Work Diff", &(pool_stats->last_diff),
false);
root = api_add_diff(root, "Min Diff", &(pool_stats->min_diff), false);
root = api_add_diff(root, "Max Diff", &(pool_stats->max_diff), false);
root = api_add_uint32(root, "Min Diff Count", &(pool_stats-
>min_diff_count), false);
root = api_add_uint32(root, "Max Diff Count", &(pool_stats-
>max_diff_count), false);
root = api_add_uint64(root, "Times Sent", &(pool_stats->times_sent),
false);
root = api_add_uint64(root, "Bytes Sent", &(pool_stats->bytes_sent),
false);
root = api_add_uint64(root, "Times Recv", &(pool_stats-
>times_received), false);
root = api_add_uint64(root, "Bytes Recv", &(pool_stats-
>bytes_received), false);
root = api_add_uint64(root, "Net Bytes Sent", &(pool_stats-
>net_bytes_sent), false);
root = api_add_uint64(root, "Net Bytes Recv", &(pool_stats-
>net_bytes_received), false);
}
if (extra)
root = api_add_extra(root, extra);
if (cgpu) {
#ifdef USE_USBUTILS
char details[256];
if (cgpu->usbinfo.pipe_count)
snprintf(details, sizeof(details),
"%"PRIu64" %"PRIu64"/%"PRIu64"/%"PRIu64" %lu",
cgpu->usbinfo.pipe_count,
cgpu->usbinfo.clear_err_count,
cgpu->usbinfo.retry_err_count,
cgpu->usbinfo.clear_fail_count,
(unsigned long)(cgpu->usbinfo.last_pipe));
else
strcpy(details, "0");
snprintf(details, sizeof(details),
"r%"PRIu64" %.6f w%"PRIu64" %.6f",
cgpu->usbinfo.read_delay_count,
cgpu->usbinfo.total_read_delay,
cgpu->usbinfo.write_delay_count,
cgpu->usbinfo.total_write_delay);
if (cgpu->usbinfo.usb_tmo[0].count == 0 &&
cgpu->usbinfo.usb_tmo[1].count == 0 &&
cgpu->usbinfo.usb_tmo[2].count == 0) {
snprintf(details, sizeof(details),
"%"PRIu64" 0", cgpu->usbinfo.tmo_count);
} else {
snprintf(details, sizeof(details),
"%"PRIu64" %d=%d/%d/%d/%"PRIu64"/%"PRIu64
" %d=%d/%d/%d/%"PRIu64"/%"PRIu64
" %d=%d/%d/%d/%"PRIu64"/%"PRIu64" ",
cgpu->usbinfo.tmo_count,
USB_TMO_0, cgpu->usbinfo.usb_tmo[0].count,
cgpu->usbinfo.usb_tmo[0].min_tmo,
cgpu->usbinfo.usb_tmo[0].max_tmo,
cgpu->usbinfo.usb_tmo[0].total_over,
cgpu->usbinfo.usb_tmo[0].total_tmo,
USB_TMO_1, cgpu->usbinfo.usb_tmo[1].count,
cgpu->usbinfo.usb_tmo[1].min_tmo,
cgpu->usbinfo.usb_tmo[1].max_tmo,
cgpu->usbinfo.usb_tmo[1].total_over,
cgpu->usbinfo.usb_tmo[1].total_tmo,
USB_TMO_2, cgpu->usbinfo.usb_tmo[2].count,
cgpu->usbinfo.usb_tmo[2].min_tmo,
cgpu->usbinfo.usb_tmo[2].max_tmo,
cgpu->usbinfo.usb_tmo[2].total_over,
cgpu->usbinfo.usb_tmo[2].total_tmo);
}
return ++i;
}
if (isjson)
io_open = io_add(io_data, COMSTR JSON_MINESTATS);
i = 0;
for (j = 0; j < total_devices; j++) {
cgpu = get_devices(j);
if (isjson)
io_open = io_add(io_data, COMSTR JSON_MINESTATS);
i = 0;
for (j = 0; j < total_devices; j++) {
cgpu = get_devices(j);
i = 0;
for (j = 0; j < total_devices; j++) {
cgpu = get_devices(j);
if (!cgpu)
continue;
#ifdef USE_USBUTILS
if (cgpu->blacklisted)
continue;
if (cgpu->usbinfo.nodev) {
if (howoldsec <= 0)
continue;
if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec)
continue;
}
#endif
if (cgpu->drv) {
if (cgpu->drv->get_api_stats)
extra = cgpu->drv->get_api_stats(cgpu);
else
extra = NULL;
cg_rlock(&ch_lock);
root = api_add_timeval(root, "Current Block Time", &block_timeval, true);
root = api_add_string(root, "Current Block Hash", current_hash, true);
cg_runlock(&ch_lock);
if (param == NULL)
param = (char *)BLANK;
else
*param = tolower(*param);
switch(*param) {
case 's':
opt_realquiet = true;
break;
case 'q':
opt_quiet ^= true;
break;
case 'v':
opt_log_output ^= true;
if (opt_log_output)
opt_quiet = false;
break;
case 'd':
opt_debug ^= true;
opt_log_output = opt_debug;
if (opt_debug)
opt_quiet = false;
break;
case 'r':
opt_protocol ^= true;
if (opt_protocol)
opt_quiet = false;
break;
case 'p':
want_per_device_stats ^= true;
opt_log_output = want_per_device_stats;
break;
case 'n':
opt_log_output = false;
opt_debug = false;
opt_quiet = false;
opt_protocol = false;
want_per_device_stats = false;
opt_worktime = false;
break;
case 'w':
opt_worktime ^= true;
break;
#ifdef _MEMORY_DEBUG
case 'y':
cgmemspeedup();
break;
case 'z':
cgmemrpt();
break;
#endif
default:
// anything else just reports the settings
break;
}
#ifdef USE_USBUTILS
bool io_open = false;
int count = 0;
root = api_usb_stats(&count);
#endif
if (!root) {
message(io_data, MSG_NOUSTA, 0, NULL, isjson);
return;
}
#ifdef USE_USBUTILS
message(io_data, MSG_USBSTA, 0, NULL, isjson);
if (isjson)
io_open = io_add(io_data, COMSTR JSON_USBSTATS);
while (42) {
root = api_usb_stats(&count);
if (!root)
break;
#ifdef HAVE_AN_FPGA
static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c,
__maybe_unused char *param, bool isjson, __maybe_unused char group)
{
struct cgpu_info *cgpu;
struct device_drv *drv;
char buf[TMPBUFSIZ];
int numpga = numpgas();
if (numpga == 0) {
message(io_data, MSG_PGANON, 0, NULL, isjson);
return;
}
int id = atoi(param);
if (id < 0 || id >= numpga) {
message(io_data, MSG_INVPGA, id, NULL, isjson);
return;
}
int dev = pgadevice(id);
if (dev < 0) { // Should never happen
message(io_data, MSG_INVPGA, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
drv = cgpu->drv;
if (!drv->set_device)
message(io_data, MSG_PGANOSET, id, NULL, isjson);
else {
char *ret = drv->set_device(cgpu, opt, set, buf);
if (ret) {
if (strcasecmp(opt, "help") == 0)
message(io_data, MSG_PGAHELP, id, ret, isjson);
else
message(io_data, MSG_PGASETERR, id, ret, isjson);
} else
message(io_data, MSG_PGASETOK, id, NULL, isjson);
}
}
#endif
*sum = tolower(*sum);
if (*sum != 't' && *sum != 'f') {
message(io_data, MSG_INVBOOL, 0, NULL, isjson);
return;
}
if (all)
zero_stats();
if (bs)
zero_bestshare();
if (dosum)
message(io_data, MSG_ZERSUM, 0, all ? "All" : "BestShare", isjson);
else
message(io_data, MSG_ZERNOSUM, 0, all ? "All" : "BestShare", isjson);
}
value = atoi(param);
if (value < 0 || value > 9999) {
message(io_data, MSG_INVHPLG, 0, param, isjson);
return;
}
hotplug_time = value;
if (value)
message(io_data, MSG_HOTPLUG, value, NULL, isjson);
else
message(io_data, MSG_DISHPLG, 0, NULL, isjson);
#else
message(io_data, MSG_NOHPLG, 0, NULL, isjson);
return;
#endif
}
#ifdef HAVE_AN_ASIC
static void ascdev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char
*param, bool isjson, __maybe_unused char group)
{
bool io_open = false;
int numasc = numascs();
int id;
if (numasc == 0) {
message(io_data, MSG_ASCNON, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numasc) {
message(io_data, MSG_INVASC, id, NULL, isjson);
return;
}
if (isjson)
io_open = io_add(io_data, COMSTR JSON_ASC);
if (numasc == 0) {
message(io_data, MSG_ASCNON, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numasc) {
message(io_data, MSG_INVASC, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
if (cgpu->deven != DEV_DISABLED) {
message(io_data, MSG_ASCLRENA, id, NULL, isjson);
return;
}
#if 0 /* A DISABLED device wont change status FIXME: should disabling make it WELL?
*/
if (cgpu->status != LIFE_WELL) {
message(io_data, MSG_ASCUNW, id, NULL, isjson);
return;
}
#endif
#ifdef USE_USBUTILS
if (cgpu->usbinfo.nodev) {
message(io_data, MSG_ASCUSBNODEV, id, NULL, isjson);
return;
}
#endif
if (numasc == 0) {
message(io_data, MSG_ASCNON, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numasc) {
message(io_data, MSG_INVASC, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
if (cgpu->deven == DEV_DISABLED) {
message(io_data, MSG_ASCLRDIS, id, NULL, isjson);
return;
}
cgpu->deven = DEV_DISABLED;
if (numasc == 0) {
message(io_data, MSG_ASCNON, 0, NULL, isjson);
return;
}
id = atoi(param);
if (id < 0 || id >= numasc) {
message(io_data, MSG_INVASC, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
drv = cgpu->drv;
if (!drv->identify_device)
message(io_data, MSG_ASCNOID, id, NULL, isjson);
else {
drv->identify_device(cgpu);
message(io_data, MSG_ASCIDENT, id, NULL, isjson);
}
}
#endif
#ifdef HAVE_AN_ASIC
count = numascs();
#endif
#ifdef HAVE_AN_ASIC
static void ascset(struct io_data *io_data, __maybe_unused SOCKETTYPE c,
__maybe_unused char *param, bool isjson, __maybe_unused char group)
{
struct cgpu_info *cgpu;
struct device_drv *drv;
char buf[TMPBUFSIZ];
int numasc = numascs();
if (numasc == 0) {
message(io_data, MSG_ASCNON, 0, NULL, isjson);
return;
}
int id = atoi(param);
if (id < 0 || id >= numasc) {
message(io_data, MSG_INVASC, id, NULL, isjson);
return;
}
cgpu = get_devices(dev);
drv = cgpu->drv;
if (!drv->set_device)
message(io_data, MSG_ASCNOSET, id, NULL, isjson);
else {
char *ret = drv->set_device(cgpu, opt, set, buf);
if (ret) {
if (strcasecmp(opt, "help") == 0)
message(io_data, MSG_ASCHELP, id, ret, isjson);
else
message(io_data, MSG_ASCSETERR, id, ret, isjson);
} else
message(io_data, MSG_ASCSETOK, id, NULL, isjson);
}
}
#endif
mutex_unlock(&hash_lock);
temp = 0;
last_device_valid_work = 0;
for (i = 0; i < total_devices; i++) {
cgpu = get_devices(i);
if (last_device_valid_work == 0 ||
last_device_valid_work < cgpu->last_device_valid_work)
last_device_valid_work = cgpu->last_device_valid_work;
if (temp < cgpu->temp)
temp = cgpu->temp;
}
last_share_time = 0;
last_share_diff = 0;
for (i = 0; i < total_pools; i++) {
pool = pools[i];
if (pool->removed)
continue;
struct CMDS {
char *name;
void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char);
bool iswritemode;
bool joinable;
} cmds[] = {
{ "version", apiversion, false, true },
{ "config", minerconfig, false, true },
{ "devs", devstatus, false, true },
{ "edevs", edevstatus, false, true },
{ "pools", poolstatus, false, true },
{ "summary", summary, false, true },
#ifdef HAVE_AN_FPGA
{ "pga", pgadev, false, false },
{ "pgaenable", pgaenable, true, false },
{ "pgadisable", pgadisable, true, false },
{ "pgaidentify", pgaidentify, true, false },
#endif
{ "pgacount", pgacount, false, true },
{ "switchpool", switchpool, true, false },
{ "addpool", addpool, true, false },
{ "poolpriority", poolpriority, true, false },
{ "poolquota", poolquota, true, false },
{ "enablepool", enablepool, true, false },
{ "disablepool", disablepool, true, false },
{ "removepool", removepool, true, false },
{ "save", dosave, true, false },
{ "quit", doquit, true, false },
{ "privileged", privileged, true, false },
{ "notify", notify, false, true },
{ "devdetails", devdetails, false, true },
{ "restart", dorestart, true, false },
{ "stats", minerstats, false, true },
{ "dbgstats", minerdebug, false, true },
{ "estats", minerestats, false, true },
{ "check", checkcommand, false, false },
{ "failover-only", failoveronly, true, false },
{ "coin", minecoin, false, true },
{ "debug", debugstate, true, false },
{ "setconfig", setconfig, true, false },
{ "usbstats", usbstats, false, true },
#ifdef HAVE_AN_FPGA
{ "pgaset", pgaset, true, false },
#endif
{ "zero", dozero, true, false },
{ "hotplug", dohotplug, true, false },
#ifdef HAVE_AN_ASIC
{ "asc", ascdev, false, false },
{ "ascenable", ascenable, true, false },
{ "ascdisable", ascdisable, true, false },
{ "ascidentify", ascidentify, true, false },
{ "ascset", ascset, true, false },
#endif
{ "asccount", asccount, false, true },
{ "lcd", lcddata, false, true },
{ "lockstats", lockstats, true, true },
{ NULL, NULL, false, false }
};
found = false;
access = false;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(cmds[i].name, param) == 0) {
found = true;
break;
}
}
static void head_join(struct io_data *io_data, char *cmdptr, bool isjson, bool
*firstjoin)
{
char *ptr;
if (*firstjoin) {
if (isjson)
io_add(io_data, JSON0);
*firstjoin = false;
} else {
if (isjson)
io_add(io_data, JSON_BETWEEN_JOIN);
}
if (isjson) {
io_add(io_data, JSON1);
io_add(io_data, ptr);
io_add(io_data, JSON2);
} else {
io_add(io_data, JOIN_CMD);
io_add(io_data, ptr);
io_add(io_data, BETWEEN_JOIN);
}
if (ptr != cmdptr)
free(ptr);
}
if (isjson) {
io_add(io_data, JSON_END);
io_add(io_data, JSON3);
}
}
strcpy(buf, io_data->ptr);
if (io_data->close)
strcat(buf, JSON_CLOSE);
if (isjson)
strcat(buf, JSON_END);
len = strlen(buf);
tosend = len+1;
applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", tosend, buf, len > 10 ?
"..." : BLANK);
count = sendc = 0;
while (count < 5 && tosend > 0) {
// allow 50ms per attempt
struct timeval timeout = {0, 50000};
fd_set wd;
FD_ZERO(&wd);
FD_SET(c, &wd);
if ((res = select(c + 1, NULL, &wd, NULL, &timeout)) < 1) {
applog(LOG_WARNING, "API: send select failed (%d)", res);
return;
}
if (SOCKETFAIL(n)) {
count++;
if (sock_blocks())
continue;
return;
} else {
if (sendc <= 1) {
if (n == tosend)
applog(LOG_DEBUG, "API: sent all of %d first go",
tosend);
else
applog(LOG_DEBUG, "API: sent %d of %d first go", n,
tosend);
} else {
if (n == tosend)
applog(LOG_DEBUG, "API: sent all of remaining %d
(sendc=%d)", tosend, sendc);
else
applog(LOG_DEBUG, "API: sent %d of remaining %d
(sendc=%d)", n, tosend, sendc);
}
tosend -= n;
buf += n;
if (n == 0)
count++;
}
}
}
bye = true;
if (*apisock != INVSOCK) {
shutdown(*apisock, SHUT_RDWR);
CLOSESOCKET(*apisock);
*apisock = INVSOCK;
}
if (ipaccess != NULL) {
free(ipaccess);
ipaccess = NULL;
}
io_free();
mutex_unlock(&quit_restart_lock);
}
/*
* Interpret --api-groups G:cmd1:cmd2:cmd3,P:cmd4,*,...
*/
static void setup_groups()
{
char *api_groups = opt_api_groups ? opt_api_groups : (char *)BLANK;
char *buf, *ptr, *next, *colon;
char group;
char commands[TMPBUFSIZ];
char cmdbuf[100];
char *cmd;
bool addstar, did;
int i;
next = buf;
// for each group defined
while (next && *next) {
ptr = next;
next = strchr(ptr, ',');
if (next)
*(next++) = '\0';
group = GROUP(*ptr);
if (!VALIDGROUP(group))
quit(1, "API invalid group name '%c'", *ptr);
if (group == PRIVGROUP)
quit(1, "API group name can't be '%c'", PRIVGROUP);
if (group == NOPRIVGROUP)
quit(1, "API group name can't be '%c'", NOPRIVGROUP);
if (apigroups[GROUPOFFSET(group)].commands != NULL)
quit(1, "API duplicate group name '%c'", *ptr);
ptr += 2;
if (strcmp(ptr, "*") == 0)
addstar = true;
else {
did = false;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcasecmp(ptr, cmds[i].name) == 0) {
did = true;
break;
}
}
if (did) {
// skip duplicates
sprintf(cmdbuf, "|%s|", cmds[i].name);
if (strstr(commands, cmdbuf) == NULL) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = SEPARATOR;
*cmd = '\0';
}
} else {
quit(1, "API unknown command '%s' in group '%c'",
ptr, group);
}
}
ptr = colon;
}
ptr = apigroups[GROUPOFFSET(group)].commands =
cgmalloc(strlen(commands) + 1);
strcpy(ptr, commands);
}
ptr = apigroups[GROUPOFFSET(NOPRIVGROUP)].commands =
cgmalloc(strlen(commands) + 1);
strcpy(ptr, commands);
free(buf);
return;
}
/*
* Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option
* ipv6 address should be enclosed with a pair of square brackets and the prefix
left outside
* special case of 0/0 allows /0 (means all IP addresses)
*/
#define ALLIP "0/0"
/*
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms
*/
static void setup_ipaccess()
{
char *buf, *ptr, *comma, *slash, *end, *dot;
int ipcount, mask, i, shift;
char tmp[64], original[64];
bool ipv6 = false;
char group;
strcpy(buf, opt_api_allow);
ipcount = 1;
ptr = buf;
while (*ptr)
if (*(ptr++) == ',')
ipcount++;
ips = 0;
ptr = buf;
while (ptr && *ptr) {
while (*ptr == ' ' || *ptr == '\t')
ptr++;
if (*ptr == ',') {
ptr++;
continue;
}
ptr += 2;
}
ipaccess[ips].group = group;
if (strcmp(ptr, ALLIP) == 0) {
for (i = 0; i < 16; i++) {
ipaccess[ips].ip.s6_addr[i] = 0;
ipaccess[ips].mask.s6_addr[i] = 0;
}
}
else {
end = strchr(ptr, '/');
if (!end) {
for (i = 0; i < 16; i++)
ipaccess[ips].mask.s6_addr[i] = 0xff;
end = ptr + strlen(ptr);
}
slash = end--;
if (*ptr == '[' && *end == ']') {
*(ptr++) = '\0';
*(end--) = '\0';
ipv6 = true;
}
else
ipv6 = false;
if (*slash) {
*(slash++) = '\0';
mask = atoi(slash);
if (mask < 1 || (mask += ipv6 ? 0 : 96) > 128) {
applog(LOG_ERR, "API: ignored address with "
"invalid mask (%d) '%s'",
mask, original);
goto popipo; // skip invalid/zero
}
i = 0;
shift = 7;
while (mask-- > 0) {
ipaccess[ips].mask.s6_addr[i] |= 1 << shift;
if (shift-- == 0) {
i++;
shift = 7;
}
}
}
ips++;
popipo:
ptr = comma;
}
free(buf);
}
if (opt_debug)
applog(LOG_DEBUG, "API: killing cgminer");
kill_work();
return NULL;
}
static void *restart_thread(__maybe_unused void *userdata)
{
// allow thread creator to finish whatever it's doing
mutex_lock(&quit_restart_lock);
mutex_unlock(&quit_restart_lock);
if (opt_debug)
applog(LOG_DEBUG, "API: restarting cgminer");
app_restart();
return NULL;
}
*connectaddr = cgmalloc(INET6_ADDRSTRLEN);
getnameinfo((struct sockaddr *)cli, sizeof(*cli),
*connectaddr, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST);
*group = NOPRIVGROUP;
if (opt_api_allow) {
for (i = 0; i < ips; i++) {
match = true;
for (j = 0; j < 16; j++) {
if ((client_ip.s6_addr[j] & ipaccess[i].mask.s6_addr[j])
!= ipaccess[i].ip.s6_addr[j]) {
match = false;
break;
}
}
if (match) {
addrok = true;
*group = ipaccess[i].group;
break;
}
}
} else {
if (opt_api_network)
addrok = true;
else
addrok = (strcmp(*connectaddr, localaddr) == 0)
|| IN6_IS_ADDR_LOOPBACK(&client_ip);
}
return addrok;
}
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;
}
// 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, host->ai_addr, host->ai_addrlen))) {
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_mcast_port, binderror, MUNAVAILABLE);
goto die;
}
switch (host->ai_family) {
case AF_INET: {
struct ip_mreq grp;
memset(&grp, 0, sizeof(grp));
grp.imr_multiaddr.s_addr = ((struct sockaddr_in *)(host-
>ai_addr))->sin_addr.s_addr;
grp.imr_interface.s_addr = INADDR_ANY;
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;
}
break;
}
case AF_INET6: {
struct ipv6_mreq grp;
memcpy(&grp.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)(host-
>ai_addr))->sin6_addr),
sizeof(struct in6_addr));
grp.ipv6mr_interface= 0;
if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IPV6,
IPV6_ADD_MEMBERSHIP,
(void *)(&grp), sizeof(grp)))) {
applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG,
MUNAVAILABLE);
goto die;
}
break;
}
default:
break;
}
freeaddrinfo(res);
count++;
came_from_siz = sizeof(came_from);
if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf) - 1,
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';
snprintf(replybuf, sizeof(replybuf),
"cgm-" API_MCAST_CODE "-%d-%s",
opt_api_port, opt_api_mcast_des);
CLOSESOCKET(reply_sock);
}
} else
applog(LOG_DEBUG, "API mcast request was no good");
}
die:
CLOSESOCKET(mcast_sock);
}
pthread_detach(pthread_self());
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
RenameThread("APIMcast");
mcast();
PTH(mythr) = 0L;
return NULL;
}
void mcast_init()
{
struct thr_info *thr;
apisock = cgmalloc(sizeof(*apisock));
*apisock = INVSOCK;
json_config = NULL;
isjoin = false;
if (!opt_api_listen) {
applog(LOG_DEBUG, "API not running%s", UNAVAILABLE);
free(apisock);
return;
}
io_data = sock_io_new();
mutex_init(&quit_restart_lock);
setup_groups();
if (opt_api_allow) {
setup_ipaccess();
if (ips == 0) {
applog(LOG_WARNING, "API not running (no valid IPs specified)%s",
UNAVAILABLE);
free(apisock);
return;
}
}
/* This should be done before curl in needed
* to ensure curl has already called WSAStartup() in windows */
cgsleep_ms(opt_log_interval*1000);
#ifndef WIN32
// On linux with SO_REUSEADDR, bind will get the port if the previous
// socket is closed (even if it is still in TIME_WAIT) but fail if
// another program has it open - which is what we want
int optval = 1;
// If it doesn't work, we don't really care - just show a debug message
if (SOCKETFAIL(setsockopt(*apisock, SOL_SOCKET, SO_REUSEADDR, (void *)
(&optval), sizeof(optval))))
applog(LOG_DEBUG, "API setsockopt SO_REUSEADDR failed (ignored): %s",
SOCKERRMSG);
#else
// On windows a 2nd program can bind to a port>1024 already in use unless
// SO_EXCLUSIVEADDRUSE is used - however then the bind to a closed port
// in TIME_WAIT will fail until the timeout - so we leave the options alone
#endif
// 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(*apisock, host->ai_addr, host->ai_addrlen))) {
binderror = SOCKERRMSG;
if ((time(NULL) - bindstart) > 61)
break;
else {
applog(LOG_WARNING, "API bind to port %d failed - trying
again in 30sec", port);
cgsleep_ms(30000);
}
} else
bound = 1;
}
freeaddrinfo(res);
if (bound == 0) {
applog(LOG_ERR, "API bind to port %d failed (%s)%s", port, binderror,
UNAVAILABLE);
free(apisock);
return;
}
if (SOCKETFAIL(listen(*apisock, QUEUE))) {
applog(LOG_ERR, "API3 initialisation failed (%s)%s", SOCKERRMSG,
UNAVAILABLE);
CLOSESOCKET(*apisock);
free(apisock);
return;
}
if (opt_api_allow)
applog(LOG_WARNING, "API running in IP access mode on port %d (%d)",
port, (int)*apisock);
else {
if (opt_api_network)
applog(LOG_WARNING, "API running in UNRESTRICTED read access mode
on port %d (%d)", port, (int)*apisock);
else
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))) {
applog(LOG_ERR, "API failed (%s)%s (%d)", SOCKERRMSG,
UNAVAILABLE, (int)*apisock);
goto die;
}
if (addrok) {
/* Accept only half the TMPBUFSIZ to account for space
* potentially used by escaping chars. */
n = recv(c, &buf[0], TMPBUFSIZ / 2 - 1, 0);
if (SOCKETFAIL(n))
buf[0] = '\0';
else
buf[n] = '\0';
if (opt_debug) {
if (SOCKETFAIL(n))
applog(LOG_DEBUG, "API: recv failed: %s",
SOCKERRMSG);
else
applog(LOG_DEBUG, "API: recv command: (%d) '%s'", n,
buf);
}
if (!SOCKETFAIL(n)) {
// the time of the request in now
when = time(NULL);
io_reinit(io_data);
did = false;
if (*buf != ISJSON) {
isjson = false;
cmd = buf;
}
else {
isjson = true;
param = NULL;
if (!json_is_object(json_config)) {
message(io_data, MSG_INVJSON, 0, NULL, isjson);
send_result(io_data, c, isjson);
did = true;
} else {
json_val = json_object_get(json_config,
JSON_COMMAND);
if (json_val == NULL) {
message(io_data, MSG_MISCMD, 0, NULL,
isjson);
send_result(io_data, c, isjson);
did = true;
} else {
if (!json_is_string(json_val)) {
message(io_data, MSG_INVCMD, 0,
NULL, isjson);
send_result(io_data, c, isjson);
did = true;
} else {
cmd = (char
*)json_string_value(json_val);
json_val =
json_object_get(json_config, JSON_PARAMETER);
if (json_is_string(json_val))
param = (char
*)json_string_value(json_val);
else if (json_is_integer(json_val))
{
sprintf(param_buf, "%d",
(int)json_integer_value(json_val));
param = param_buf;
} else if (json_is_real(json_val))
{
sprintf(param_buf, "%f",
(double)json_real_value(json_val));
param = param_buf;
}
}
}
}
}
if (!did) {
char *cmdptr, *cmdsbuf = NULL;
if (strchr(cmd, CMDJOIN)) {
firstjoin = isjoin = true;
// cmd + leading+tailing '|' + '\0'
cmdsbuf = cgmalloc(strlen(cmd) + 3);
strcpy(cmdsbuf, "|");
param = NULL;
} else
firstjoin = isjoin = false;
cmdptr = cmd;
do {
did = false;
if (isjoin) {
cmd = strchr(cmdptr, CMDJOIN);
if (cmd)
*(cmd++) = '\0';
if (!*cmdptr)
goto inochi;
}
did = true;
if (!isjoin)
send_result(io_data, c,
isjson);
else
tail_join(io_data, isjson);
break;
}
}
if (!did) {
if (isjoin)
head_join(io_data, cmdptr, isjson,
&firstjoin);
message(io_data, MSG_INVCMD, 0, NULL,
isjson);
if (isjoin)
tail_join(io_data, isjson);
else
send_result(io_data, c, isjson);
}
inochi:
if (isjoin)
cmdptr = cmd;
} while (isjoin && cmdptr);
}
if (isjoin)
send_result(io_data, c, isjson);
free(apisock);
if (opt_debug)
applog(LOG_DEBUG, "API: terminating due to: %s",
do_a_quit ? "QUIT" : (do_a_restart ? "RESTART" : (bye ?
"BYE" : "UNKNOWN!")));
mutex_lock(&quit_restart_lock);
if (do_a_restart) {
if (thr_info_create(&bye_thr, NULL, restart_thread, &bye_thr)) {
mutex_unlock(&quit_restart_lock);
quit(1, "API failed to initiate a restart - aborting");
}
pthread_detach(bye_thr.pth);
} else if (do_a_quit) {
if (thr_info_create(&bye_thr, NULL, quit_thread, &bye_thr)) {
mutex_unlock(&quit_restart_lock);
quit(1, "API failed to initiate a clean quit - aborting");
}
pthread_detach(bye_thr.pth);
}
mutex_unlock(&quit_restart_lock);
}