Browse Source
you can use PHP api-example.php as a json wrapper... to be tested on windows... Signed-off-by: Tanguy Pruvot <tanguy.pruvot@gmail.com>master
Tanguy Pruvot
10 years ago
7 changed files with 666 additions and 11 deletions
@ -0,0 +1,114 @@
@@ -0,0 +1,114 @@
|
||||
<?php |
||||
/** |
||||
* Sample Request API to ccminer |
||||
*/ |
||||
defined('API_PORT') || define('API_PORT', 4068); |
||||
|
||||
function getsock($port) |
||||
{ |
||||
$socket = null; |
||||
$socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP); |
||||
if ($socket === false || $socket === null) { |
||||
$error = socket_strerror(socket_last_error()); |
||||
$msg = "socket create($port) failed"; |
||||
echo "ERR: $msg '$error'\n"; |
||||
return NULL; |
||||
} |
||||
|
||||
$res = socket_connect($socket, '127.0.0.1', $port); |
||||
if ($res === false) { |
||||
$error = socket_strerror(socket_last_error()); |
||||
$msg = "socket connect($port) failed"; |
||||
echo "ERR: $msg '$error'\n"; |
||||
socket_close($socket); |
||||
return NULL; |
||||
} |
||||
return $socket; |
||||
} |
||||
|
||||
function readsockline($socket) |
||||
{ |
||||
$line = ''; |
||||
while (true) { |
||||
$byte = socket_read($socket, 1); |
||||
if ($byte === false || $byte === '') |
||||
break; |
||||
if ($byte === "\0") |
||||
break; |
||||
$line .= $byte; |
||||
} |
||||
return $line; |
||||
} |
||||
|
||||
|
||||
function request($cmd) |
||||
{ |
||||
$socket = getsock(API_PORT); |
||||
if ($socket == null) |
||||
return NULL; |
||||
|
||||
socket_write($socket, $cmd, strlen($cmd)); |
||||
$line = readsockline($socket); |
||||
socket_close($socket); |
||||
|
||||
if (strlen($line) == 0) { |
||||
echo "WARN: '$cmd' returned nothing\n"; |
||||
return $line; |
||||
} |
||||
|
||||
echo "$cmd returned '$line'\n"; |
||||
|
||||
$data = array(); |
||||
|
||||
$objs = explode('|', $line); |
||||
foreach ($objs as $obj) |
||||
{ |
||||
if (strlen($obj) > 0) |
||||
{ |
||||
$items = explode(';', $obj); |
||||
$item = $items[0]; |
||||
$id = explode('=', $items[0], 2); |
||||
if (count($id) == 1) |
||||
$name = $id[0]; |
||||
else |
||||
$name = $id[0].$id[1]; |
||||
|
||||
if (strlen($name) == 0) |
||||
$name = 'null'; |
||||
|
||||
if (isset($data[$name])) { |
||||
$num = 1; |
||||
while (isset($data[$name.$num])) |
||||
$num++; |
||||
$name .= $num; |
||||
} |
||||
|
||||
$counter = 0; |
||||
foreach ($items as $item) |
||||
{ |
||||
$id = explode('=', $item, 2); |
||||
if (count($id) == 2) |
||||
$data[$name][$id[0]] = $id[1]; |
||||
else |
||||
$data[$name][$counter] = $id[0]; |
||||
|
||||
$counter++; |
||||
} |
||||
|
||||
} |
||||
} |
||||
if ($cmd == 'summary') |
||||
return array_pop($data); |
||||
else |
||||
return $data; |
||||
} |
||||
|
||||
ob_start(); |
||||
$summary = request('summary'); |
||||
$stats = request('stats'); |
||||
ob_end_clean(); |
||||
//echo ob_get_clean()."\n"; |
||||
|
||||
header("Content-Type: application/json"); |
||||
echo json_encode(compact('summary', 'stats'))."\n"; |
||||
?> |
@ -0,0 +1,479 @@
@@ -0,0 +1,479 @@
|
||||
/*
|
||||
* Copyright 2014 ccminer team |
||||
* |
||||
* Implementation by tpruvot (based on cgminer) |
||||
* |
||||
* 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 |
||||
* Software Foundation; either version 2 of the License, or (at your option) |
||||
* any later version. See COPYING for more details. |
||||
*/ |
||||
#define APIVERSION "1.0" |
||||
|
||||
#include <stdio.h> |
||||
#include <ctype.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <stdbool.h> |
||||
#include <stdint.h> |
||||
#include <unistd.h> |
||||
#include <sys/time.h> |
||||
#include <time.h> |
||||
#include <math.h> |
||||
#include <stdarg.h> |
||||
#include <assert.h> |
||||
|
||||
#include <sys/stat.h> |
||||
#include <sys/types.h> |
||||
|
||||
#include "compat.h" |
||||
#include "miner.h" |
||||
|
||||
#ifndef WIN32 |
||||
# include <errno.h> |
||||
# include <sys/socket.h> |
||||
# include <netinet/in.h> |
||||
# include <arpa/inet.h> |
||||
# include <netdb.h> |
||||
# define SOCKETTYPE long |
||||
# define SOCKETFAIL(a) ((a) < 0) |
||||
# define INVSOCK -1 /* INVALID_SOCKET */ |
||||
# define INVINETADDR -1 /* INADDR_NONE */ |
||||
# define CLOSESOCKET close |
||||
# define SOCKETINIT {} |
||||
# define SOCKERRMSG strerror(errno) |
||||
#else |
||||
# include <winsock2.h> |
||||
# define SOCKETTYPE SOCKET |
||||
# define SOCKETFAIL(a) ((a) == SOCKET_ERROR) |
||||
# define INVSOCK INVALID_SOCKET |
||||
# define INVINETADDR INADDR_NONE |
||||
# define CLOSESOCKET closesocket |
||||
#endif |
||||
|
||||
#define GROUP(g) (toupper(g)) |
||||
#define PRIVGROUP GROUP('W') |
||||
#define NOPRIVGROUP GROUP('R') |
||||
#define ISPRIVGROUP(g) (GROUP(g) == PRIVGROUP) |
||||
#define GROUPOFFSET(g) (GROUP(g) - GROUP('A')) |
||||
#define VALIDGROUP(g) (GROUP(g) >= GROUP('A') && GROUP(g) <= GROUP('Z')) |
||||
#define COMMANDS(g) (apigroups[GROUPOFFSET(g)].commands) |
||||
#define DEFINEDGROUP(g) (ISPRIVGROUP(g) || COMMANDS(g) != NULL) |
||||
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 IP4ACCESS { |
||||
in_addr_t ip; |
||||
in_addr_t mask; |
||||
char group; |
||||
}; |
||||
|
||||
static int ips = 1; |
||||
static struct IP4ACCESS *ipaccess = NULL; |
||||
|
||||
// Big enough for largest API request
|
||||
// though a PC with 100s of CPUs may exceed the size ...
|
||||
// Current code assumes it can socket send this size also
|
||||
#define MYBUFSIZ 16384 |
||||
|
||||
// Socket is on 127.0.0.1
|
||||
#define QUEUE 10 |
||||
|
||||
#define LOCAL_ADDR_V4 "127.0.0.1" |
||||
static const char *localaddr = LOCAL_ADDR_V4; |
||||
static const char *UNAVAILABLE = " - API will not be available"; |
||||
static char *buffer = NULL; |
||||
|
||||
static int bye = 0; |
||||
|
||||
extern int opt_intensity; |
||||
extern int opt_n_threads; |
||||
extern int opt_api_listen; |
||||
extern uint64_t global_hashrate; |
||||
extern uint32_t accepted_count; |
||||
extern uint32_t rejected_count; |
||||
|
||||
char *opt_api_allow = LOCAL_ADDR_V4; |
||||
int opt_api_network = 1; |
||||
#define gpu_threads opt_n_threads |
||||
|
||||
extern void get_currentalgo(char* buf, int sz); |
||||
|
||||
/***************************************************************/ |
||||
|
||||
static void gpustatus(int thr_id) |
||||
{ |
||||
char buf[BUFSIZ]; |
||||
float gt; |
||||
int gf, gp; |
||||
|
||||
|
||||
if (thr_id >= 0 && thr_id < gpu_threads) { |
||||
struct cgpu_info *cgpu = &thr_info[thr_id].gpu; |
||||
|
||||
int total_secs = 1; // todo
|
||||
cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; |
||||
|
||||
#ifdef HAVE_HWMONITORING |
||||
// todo
|
||||
if (gpu->has_monitoring) { |
||||
gt = gpu_temp(gpu); |
||||
gf = gpu_fanspeed(gpu); |
||||
gp = gpu_fanpercent(gpu); |
||||
} |
||||
else |
||||
#endif |
||||
gt = gf = gp = 0; |
||||
|
||||
// todo: can be 0 if set by algo (auto)
|
||||
if (opt_intensity == 0 && opt_work_size) { |
||||
int i = 0; |
||||
uint32_t ws = opt_work_size; |
||||
while (ws > 1 && i++ < 32) |
||||
ws = ws >> 1; |
||||
cgpu->intensity = i; |
||||
} else { |
||||
cgpu->intensity = opt_intensity; |
||||
} |
||||
|
||||
// todo: per gpu
|
||||
cgpu->accepted = accepted_count; |
||||
cgpu->rejected = rejected_count; |
||||
|
||||
cgpu->khashes = stats_get_speed(thr_id) / 1000.0; |
||||
|
||||
sprintf(buf, "GPU=%d;TEMP=%.1f;FAN=%d;FANP=%d;KHS=%.2f;" |
||||
"ACC=%d;REJ=%d;HWF=%d;U=%.2f;I=%d|", |
||||
thr_id, gt, gf, gp, cgpu->khashes, |
||||
cgpu->accepted, cgpu->rejected, cgpu->hw_errors, |
||||
cgpu->utility, cgpu->intensity); |
||||
|
||||
strcat(buffer, buf); |
||||
} |
||||
} |
||||
|
||||
/*****************************************************************************/ |
||||
|
||||
static char *getsummary(char *params) |
||||
{ |
||||
char algo[64] = ""; |
||||
get_currentalgo(algo, sizeof(algo)); |
||||
*buffer = '\0'; |
||||
sprintf(buffer, "NAME=%s;VER=%s;API=%s;" |
||||
"ALGO=%s;KHS=%.2f|", |
||||
PACKAGE_NAME, PACKAGE_VERSION, APIVERSION, |
||||
algo, (double)global_hashrate / 1000.0); |
||||
return buffer; |
||||
} |
||||
|
||||
static char *getstats(char *params) |
||||
{ |
||||
*buffer = '\0'; |
||||
for (int i = 0; i < gpu_threads; i++) |
||||
gpustatus(i); |
||||
return buffer; |
||||
} |
||||
|
||||
struct CMDS { |
||||
char *name; |
||||
char *(*func)(char *); |
||||
} cmds[] = { |
||||
{ "summary", getsummary }, |
||||
{ "stats", getstats }, |
||||
}; |
||||
|
||||
#define CMDMAX 2 |
||||
|
||||
static void send_result(int c, char *result) |
||||
{ |
||||
int n; |
||||
|
||||
if (result == NULL) |
||||
result = ""; |
||||
|
||||
// ignore failure - it's closed immediately anyway
|
||||
n = write(c, result, strlen(result)+1); |
||||
} |
||||
|
||||
/*
|
||||
* Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option |
||||
* special case of 0/0 allows /0 (means all IP addresses) |
||||
*/ |
||||
#define ALLIP4 "0/0" |
||||
/*
|
||||
* N.B. IP4 addresses are by Definition 32bit big endian on all platforms |
||||
*/ |
||||
static void setup_ipaccess() |
||||
{ |
||||
char *buf, *ptr, *comma, *slash, *dot; |
||||
int ipcount, mask, octet, i; |
||||
char group; |
||||
|
||||
buf = malloc(strlen(opt_api_allow) + 1); |
||||
if (unlikely(!buf)) |
||||
proper_exit(1);//, "Failed to malloc ipaccess buf");
|
||||
|
||||
strcpy(buf, opt_api_allow); |
||||
|
||||
ipcount = 1; |
||||
ptr = buf; |
||||
while (*ptr) if (*(ptr++) == ',') |
||||
ipcount++; |
||||
|
||||
// possibly more than needed, but never less
|
||||
ipaccess = calloc(ipcount, sizeof(struct IP4ACCESS)); |
||||
if (unlikely(!ipaccess)) |
||||
proper_exit(1);//, "Failed to calloc ipaccess");
|
||||
|
||||
ips = 0; |
||||
ptr = buf; |
||||
while (ptr && *ptr) { |
||||
while (*ptr == ' ' || *ptr == '\t') |
||||
ptr++; |
||||
|
||||
if (*ptr == ',') { |
||||
ptr++; |
||||
continue; |
||||
} |
||||
|
||||
comma = strchr(ptr, ','); |
||||
if (comma) |
||||
*(comma++) = '\0'; |
||||
|
||||
group = NOPRIVGROUP; |
||||
|
||||
if (isalpha(*ptr) && *(ptr+1) == ':') { |
||||
if (DEFINEDGROUP(*ptr)) |
||||
group = GROUP(*ptr); |
||||
ptr += 2; |
||||
} |
||||
|
||||
ipaccess[ips].group = group; |
||||
|
||||
if (strcmp(ptr, ALLIP4) == 0) |
||||
ipaccess[ips].ip = ipaccess[ips].mask = 0; |
||||
else |
||||
{ |
||||
slash = strchr(ptr, '/'); |
||||
if (!slash) |
||||
ipaccess[ips].mask = 0xffffffff; |
||||
else { |
||||
*(slash++) = '\0'; |
||||
mask = atoi(slash); |
||||
if (mask < 1 || mask > 32) |
||||
goto popipo; // skip invalid/zero
|
||||
|
||||
ipaccess[ips].mask = 0; |
||||
while (mask-- >= 0) { |
||||
octet = 1 << (mask % 8); |
||||
ipaccess[ips].mask |= (octet << (24 - (8 * (mask >> 3)))); |
||||
} |
||||
} |
||||
|
||||
ipaccess[ips].ip = 0; // missing default to '.0'
|
||||
for (i = 0; ptr && (i < 4); i++) { |
||||
dot = strchr(ptr, '.'); |
||||
if (dot) |
||||
*(dot++) = '\0'; |
||||
|
||||
octet = atoi(ptr); |
||||
if (octet < 0 || octet > 0xff) |
||||
goto popipo; // skip invalid
|
||||
|
||||
ipaccess[ips].ip |= (octet << (24 - (i * 8))); |
||||
|
||||
ptr = dot; |
||||
} |
||||
|
||||
ipaccess[ips].ip &= ipaccess[ips].mask; |
||||
} |
||||
|
||||
ips++; |
||||
popipo: |
||||
ptr = comma; |
||||
} |
||||
|
||||
free(buf); |
||||
} |
||||
|
||||
static bool check_connect(struct sockaddr_in *cli, char **connectaddr, char *group) |
||||
{ |
||||
bool addrok = false; |
||||
|
||||
*connectaddr = inet_ntoa(cli->sin_addr); |
||||
|
||||
*group = NOPRIVGROUP; |
||||
if (opt_api_allow) { |
||||
int client_ip = htonl(cli->sin_addr.s_addr); |
||||
for (int i = 0; i < ips; i++) { |
||||
if ((client_ip & ipaccess[i].mask) == ipaccess[i].ip) { |
||||
addrok = true; |
||||
*group = ipaccess[i].group; |
||||
break; |
||||
} |
||||
} |
||||
} else if (opt_api_network) |
||||
addrok = true; |
||||
else |
||||
addrok = (strcmp(*connectaddr, localaddr) == 0); |
||||
|
||||
return addrok; |
||||
} |
||||
|
||||
static void api() |
||||
{ |
||||
const char *addr = localaddr; |
||||
short int port = opt_api_listen; // 4068
|
||||
char buf[BUFSIZ]; |
||||
int c, n, bound; |
||||
char *connectaddr; |
||||
char *binderror; |
||||
char group; |
||||
time_t bindstart; |
||||
struct sockaddr_in serv; |
||||
struct sockaddr_in cli; |
||||
socklen_t clisiz; |
||||
bool addrok = false; |
||||
long long counter; |
||||
char *result; |
||||
char *params; |
||||
int i; |
||||
|
||||
SOCKETTYPE *apisock; |
||||
if (!opt_api_listen && opt_debug) { |
||||
applog(LOG_DEBUG, "API disabled"); |
||||
return; |
||||
} |
||||
|
||||
if (opt_api_allow) { |
||||
setup_ipaccess(); |
||||
if (ips == 0) { |
||||
applog(LOG_WARNING, "API not running (no valid IPs specified)%s", UNAVAILABLE); |
||||
} |
||||
} |
||||
|
||||
apisock = calloc(1, sizeof(*apisock)); |
||||
*apisock = INVSOCK; |
||||
|
||||
sleep(1); |
||||
|
||||
*apisock = socket(AF_INET, SOCK_STREAM, 0); |
||||
if (*apisock == INVSOCK) { |
||||
applog(LOG_ERR, "API initialisation failed (%s)%s", strerror(errno), UNAVAILABLE); |
||||
return; |
||||
} |
||||
|
||||
memset(&serv, 0, sizeof(serv)); |
||||
serv.sin_family = AF_INET; |
||||
serv.sin_addr.s_addr = inet_addr(localaddr); |
||||
if (serv.sin_addr.s_addr == (in_addr_t)INVINETADDR) { |
||||
applog(LOG_ERR, "API initialisation 2 failed (%s)%s", strerror(errno), UNAVAILABLE); |
||||
return; |
||||
} |
||||
|
||||
serv.sin_port = htons(port); |
||||
|
||||
#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 1 minute ... in case the old one hasn't completely gone yet
|
||||
bound = 0; |
||||
bindstart = time(NULL); |
||||
while (bound == 0) { |
||||
if (bind(*apisock, (struct sockaddr *)(&serv), sizeof(serv)) < 0) { |
||||
binderror = strerror(errno); |
||||
if ((time(NULL) - bindstart) > 61) |
||||
break; |
||||
else { |
||||
applog(LOG_ERR, "API bind to port %d failed - trying again in 15sec", port); |
||||
sleep(15); |
||||
} |
||||
} |
||||
else |
||||
bound = 1; |
||||
} |
||||
|
||||
|
||||
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, "API initialisation 3 failed (%s)%s", strerror(errno), UNAVAILABLE); |
||||
CLOSESOCKET(*apisock); |
||||
free(apisock); |
||||
return; |
||||
} |
||||
|
||||
buffer = malloc(MYBUFSIZ+1); |
||||
|
||||
counter = 0; |
||||
while (bye == 0) { |
||||
counter++; |
||||
|
||||
clisiz = sizeof(cli); |
||||
if (SOCKETFAIL(c = accept(*apisock, (struct sockaddr *)(&cli), &clisiz))) { |
||||
applog(LOG_ERR, "API failed (%s)%s", strerror(errno), UNAVAILABLE); |
||||
CLOSESOCKET(*apisock); |
||||
free(apisock); |
||||
free(buffer); |
||||
return; |
||||
} |
||||
|
||||
addrok = check_connect(&cli, &connectaddr, &group); |
||||
if (opt_protocol) |
||||
applog(LOG_DEBUG, "API: connection from %s - %s", |
||||
connectaddr, addrok ? "Accepted" : "Ignored"); |
||||
|
||||
if (addrok) { |
||||
n = read(c, &buf[0], BUFSIZ-1); |
||||
if (n < 0) |
||||
close(c); |
||||
else { |
||||
buf[n] = '\0'; |
||||
params = strchr(buf, '|'); |
||||
if (params != NULL) |
||||
*(params++) = '\0'; |
||||
|
||||
for (i = 0; i < CMDMAX; i++) { |
||||
if (strcmp(buf, cmds[i].name) == 0) { |
||||
result = (cmds[i].func)(params); |
||||
send_result(c, result); |
||||
close(c); |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
CLOSESOCKET(*apisock); |
||||
free(apisock); |
||||
free(buffer); |
||||
} |
||||
|
||||
void *api_thread(void *userdata) |
||||
{ |
||||
struct thr_info *mythr = (struct thr_info*)userdata; |
||||
|
||||
api(); |
||||
|
||||
tq_freeze(mythr->q); |
||||
|
||||
return NULL; |
||||
} |
Loading…
Reference in new issue