mirror of
https://github.com/GOSTSec/sgminer
synced 2025-03-13 06:01:03 +00:00
API allow multiple commands/replies in one request
This commit is contained in:
parent
b5abf68938
commit
ae837a762f
229
api.c
229
api.c
@ -29,12 +29,6 @@
|
||||
#include "miner.h"
|
||||
#include "util.h"
|
||||
|
||||
// Big enough for largest API request
|
||||
// data is truncated at the end of the last record that fits
|
||||
// but still closed correctly for JSON
|
||||
// Current code assumes it can socket send this size + JSON_CLOSE + JSON_END
|
||||
#define SOCKBUFSIZ 65432
|
||||
|
||||
// BUFSIZ varies on Windows and Linux
|
||||
#define TMPBUFSIZ 8192
|
||||
|
||||
@ -126,7 +120,11 @@ static const char SEPARATOR = '|';
|
||||
#define SEPSTR "|"
|
||||
static const char GPUSEP = ',';
|
||||
|
||||
static const char *APIVERSION = "3.0";
|
||||
#define CMDJOIN '+'
|
||||
#define JOIN_CMD "CMD="
|
||||
#define BETWEEN_JOIN SEPSTR
|
||||
|
||||
static const char *APIVERSION = "3.1";
|
||||
static const char *DEAD = "Dead";
|
||||
static const char *SICK = "Sick";
|
||||
static const char *NOSTART = "NoStart";
|
||||
@ -215,9 +213,10 @@ static const char ISJSON = '{';
|
||||
#define JSON_MINECOIN JSON1 _MINECOIN JSON2
|
||||
#define JSON_DEBUGSET JSON1 _DEBUGSET JSON2
|
||||
#define JSON_SETCONFIG JSON1 _SETCONFIG JSON2
|
||||
#define JSON_USBSTATS JSON1 _USBSTATS JSON2
|
||||
|
||||
#define JSON_END JSON4 JSON5
|
||||
#define JSON_END_TRUNCATED JSON4_TRUNCATED JSON5
|
||||
#define JSON_BETWEEN_JOIN ","
|
||||
|
||||
static const char *JSON_COMMAND = "command";
|
||||
static const char *JSON_PARAMETER = "parameter";
|
||||
@ -296,7 +295,7 @@ static const char *JSON_PARAMETER = "parameter";
|
||||
#define MSG_INVNUM 84
|
||||
#define MSG_CONPAR 85
|
||||
#define MSG_CONVAL 86
|
||||
#define MSG_USBSTA 87
|
||||
|
||||
#define MSG_NOUSTA 88
|
||||
|
||||
#define MSG_ZERMIS 94
|
||||
@ -418,7 +417,6 @@ struct CODES {
|
||||
{ SEVERITY_SUCC, MSG_SETQUOTA,PARAM_SET, "Set pool '%s' to quota %d'" },
|
||||
{ SEVERITY_ERR, MSG_CONPAR, PARAM_NONE, "Missing config parameters 'name,N'" },
|
||||
{ SEVERITY_ERR, MSG_CONVAL, PARAM_STR, "Missing config value N for '%s,N'" },
|
||||
{ SEVERITY_SUCC, MSG_USBSTA, PARAM_NONE, "USB Statistics" },
|
||||
{ SEVERITY_INFO, MSG_NOUSTA, PARAM_NONE, "No USB Statistics" },
|
||||
{ SEVERITY_ERR, MSG_ZERMIS, PARAM_NONE, "Missing zero parameters" },
|
||||
{ SEVERITY_ERR, MSG_ZERINV, PARAM_STR, "Invalid zero parameter '%s'" },
|
||||
@ -1061,10 +1059,8 @@ static void message(struct io_data *io_data, int messageid, int paramid, char *p
|
||||
|
||||
int i;
|
||||
|
||||
io_reinit(io_data);
|
||||
|
||||
if (isjson)
|
||||
io_put(io_data, JSON_START JSON_STATUS);
|
||||
io_add(io_data, JSON_START JSON_STATUS);
|
||||
|
||||
for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) {
|
||||
if (codes[i].code == messageid) {
|
||||
@ -3045,44 +3041,45 @@ struct CMDS {
|
||||
char *name;
|
||||
void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char);
|
||||
bool iswritemode;
|
||||
bool joinable;
|
||||
} cmds[] = {
|
||||
{ "version", apiversion, false },
|
||||
{ "config", minerconfig, false },
|
||||
{ "devs", devstatus, false },
|
||||
{ "pools", poolstatus, false },
|
||||
{ "summary", summary, false },
|
||||
{ "gpuenable", gpuenable, true },
|
||||
{ "gpudisable", gpudisable, true },
|
||||
{ "gpurestart", gpurestart, true },
|
||||
{ "gpu", gpudev, false },
|
||||
{ "gpucount", gpucount, false },
|
||||
{ "switchpool", switchpool, true },
|
||||
{ "addpool", addpool, true },
|
||||
{ "poolpriority", poolpriority, true },
|
||||
{ "poolquota", poolquota, true },
|
||||
{ "enablepool", enablepool, true },
|
||||
{ "disablepool", disablepool, true },
|
||||
{ "removepool", removepool, true },
|
||||
{ "gpuintensity", gpuintensity, true },
|
||||
{ "gpumem", gpumem, true },
|
||||
{ "gpuengine", gpuengine, true },
|
||||
{ "gpufan", gpufan, true },
|
||||
{ "gpuvddc", gpuvddc, true },
|
||||
{ "save", dosave, true },
|
||||
{ "quit", doquit, true },
|
||||
{ "privileged", privileged, true },
|
||||
{ "notify", notify, false },
|
||||
{ "devdetails", devdetails, false },
|
||||
{ "restart", dorestart, true },
|
||||
{ "stats", minerstats, false },
|
||||
{ "check", checkcommand, false },
|
||||
{ "failover-only", failoveronly, true },
|
||||
{ "coin", minecoin, false },
|
||||
{ "debug", debugstate, true },
|
||||
{ "setconfig", setconfig, true },
|
||||
{ "zero", dozero, true },
|
||||
{ "lockstats", lockstats, true },
|
||||
{ NULL, NULL, false }
|
||||
{ "version", apiversion, false, true },
|
||||
{ "config", minerconfig, false, true },
|
||||
{ "devs", devstatus, false, true },
|
||||
{ "pools", poolstatus, false, true },
|
||||
{ "summary", summary, false, true },
|
||||
{ "gpuenable", gpuenable, true, false },
|
||||
{ "gpudisable", gpudisable, true, false },
|
||||
{ "gpurestart", gpurestart, true, false },
|
||||
{ "gpu", gpudev, false, false },
|
||||
{ "gpucount", gpucount, 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 },
|
||||
{ "gpuintensity", gpuintensity, true, false },
|
||||
{ "gpumem", gpumem, true, false },
|
||||
{ "gpuengine", gpuengine, true, false },
|
||||
{ "gpufan", gpufan, true, false },
|
||||
{ "gpuvddc", gpuvddc, 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 },
|
||||
{ "check", checkcommand, false, false },
|
||||
{ "failover-only", failoveronly, true, false },
|
||||
{ "coin", minecoin, false, true },
|
||||
{ "debug", debugstate, true, false },
|
||||
{ "setconfig", setconfig, true, false },
|
||||
{ "zero", dozero, true, false },
|
||||
{ "lockstats", lockstats, true, true },
|
||||
{ NULL, NULL, false, false }
|
||||
};
|
||||
|
||||
static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group)
|
||||
@ -3125,6 +3122,49 @@ static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, c
|
||||
io_close(io_data);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// External supplied string
|
||||
ptr = escape_string(cmdptr, isjson);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void tail_join(struct io_data *io_data, bool isjson)
|
||||
{
|
||||
if (io_data->close) {
|
||||
io_add(io_data, JSON_CLOSE);
|
||||
io_data->close = false;
|
||||
}
|
||||
|
||||
if (isjson) {
|
||||
io_add(io_data, JSON_END);
|
||||
io_add(io_data, JSON3);
|
||||
}
|
||||
}
|
||||
|
||||
static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson)
|
||||
{
|
||||
int count, sendc, res, tosend, len, n;
|
||||
@ -3689,7 +3729,7 @@ void api(int api_thr_id)
|
||||
struct sockaddr_in cli;
|
||||
socklen_t clisiz;
|
||||
char cmdbuf[100];
|
||||
char *cmd = NULL;
|
||||
char *cmd = NULL, *cmdptr, *cmdsbuf;
|
||||
char *param;
|
||||
bool addrok;
|
||||
char group;
|
||||
@ -3697,7 +3737,7 @@ void api(int api_thr_id)
|
||||
json_t *json_config = NULL;
|
||||
json_t *json_val;
|
||||
bool isjson;
|
||||
bool did;
|
||||
bool did, isjoin, firstjoin;
|
||||
int i;
|
||||
|
||||
SOCKETTYPE *apisock;
|
||||
@ -3898,27 +3938,80 @@ void api(int api_thr_id)
|
||||
}
|
||||
|
||||
if (!did) {
|
||||
for (i = 0; cmds[i].name != NULL; i++) {
|
||||
if (strcmp(cmd, cmds[i].name) == 0) {
|
||||
sprintf(cmdbuf, "|%s|", cmd);
|
||||
if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
|
||||
(cmds[i].func)(io_data, c, param, isjson, group);
|
||||
else {
|
||||
message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
|
||||
applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
|
||||
}
|
||||
if (strchr(cmd, CMDJOIN)) {
|
||||
firstjoin = isjoin = true;
|
||||
// cmd + leading '|' + '\0'
|
||||
cmdsbuf = malloc(strlen(cmd) + 2);
|
||||
if (!cmdsbuf)
|
||||
quithere(1, "OOM cmdsbuf");
|
||||
strcpy(cmdsbuf, "|");
|
||||
param = NULL;
|
||||
} else
|
||||
firstjoin = isjoin = false;
|
||||
|
||||
send_result(io_data, c, isjson);
|
||||
did = true;
|
||||
break;
|
||||
cmdptr = cmd;
|
||||
do {
|
||||
did = false;
|
||||
if (isjoin) {
|
||||
cmd = strchr(cmdptr, CMDJOIN);
|
||||
if (cmd)
|
||||
*(cmd++) = '\0';
|
||||
if (!*cmdptr)
|
||||
goto inochi;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = 0; cmds[i].name != NULL; i++) {
|
||||
if (strcmp(cmdptr, cmds[i].name) == 0) {
|
||||
sprintf(cmdbuf, "|%s|", cmdptr);
|
||||
if (isjoin) {
|
||||
if (strstr(cmdsbuf, cmdbuf)) {
|
||||
did = true;
|
||||
break;
|
||||
}
|
||||
strcat(cmdsbuf, cmdptr);
|
||||
strcat(cmdsbuf, "|");
|
||||
head_join(io_data, cmdptr, isjson, &firstjoin);
|
||||
if (!cmds[i].joinable) {
|
||||
message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
|
||||
did = true;
|
||||
tail_join(io_data, isjson);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
|
||||
(cmds[i].func)(io_data, c, param, isjson, group);
|
||||
else {
|
||||
message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson);
|
||||
applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
|
||||
}
|
||||
|
||||
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 (!did) {
|
||||
message(io_data, MSG_INVCMD, 0, NULL, isjson);
|
||||
if (isjoin)
|
||||
send_result(io_data, c, isjson);
|
||||
}
|
||||
|
||||
if (isjson && json_is_object(json_config))
|
||||
json_decref(json_config);
|
||||
}
|
||||
|
38
doc/API
38
doc/API
@ -109,6 +109,36 @@ The STATUS section is:
|
||||
This defaults to the sgminer version but is the value of --api-description
|
||||
if it was specified at runtime.
|
||||
|
||||
With API V3.1 you can also request multiple report replies in a single command
|
||||
request
|
||||
e.g. to request both summary and devs, the command would be summary+devs
|
||||
|
||||
This is only available for report commands that don't need parameters,
|
||||
and is not available for commands that change anything
|
||||
Any parameters supplied will be ignored
|
||||
|
||||
The extra formatting of the result is to have a section for each command
|
||||
e.g. summary|STATUS=....|devs|STATUS=...
|
||||
With JSON, each result is within a section of the command name
|
||||
e.g. {"summary":{"STATUS":[{"STATUS":"S"...}],"SUMMARY":[...],"id":1},
|
||||
"devs":{"STATUS":[{"STATUS:"S"...}],"DEVS":[...],"id":1},"id":1}
|
||||
|
||||
As before, if you supply bad JSON you'll just get a single 'E' STATUS section
|
||||
in the old format, since it doesn't switch to using the new format until it
|
||||
correctly processes the JSON and can match a '+' in the command
|
||||
|
||||
If you request a command multiple times, e.g. devs+devs
|
||||
you'll just get it once
|
||||
If this results in only one command, it will still use the new layout
|
||||
with just the one command
|
||||
|
||||
If you request a command that can't be used due to requiring parameters,
|
||||
a command that isn't a report, or an invalid command, you'll get an 'E' STATUS
|
||||
for that one but it will still attempt to process all other commands supplied
|
||||
|
||||
Blank/missing commands are ignore e.g. +devs++
|
||||
will just show 'devs' using the new layout
|
||||
|
||||
For API version 1.10 and later:
|
||||
|
||||
The list of requests - a (*) means it requires privileged access - and replies:
|
||||
@ -497,7 +527,13 @@ miner.php - an example web page to access the API
|
||||
Feature Changelog for external applications using the API:
|
||||
|
||||
|
||||
API V3.0 (cgminer v3.9.1)
|
||||
API V3.1 (cgminer v3.12.1)
|
||||
|
||||
Multiple report request command with '+' e.g. summary+devs
|
||||
|
||||
---------
|
||||
|
||||
API V3.0 (cgminer v3.11.0)
|
||||
|
||||
Allow unlimited size replies
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user