Browse Source

API allow multiple commands/replies in one request

port-ckolivas
Kano 11 years ago committed by Noel Maersk
parent
commit
ae837a762f
  1. 227
      api.c
  2. 38
      doc/API

227
api.c

@ -29,12 +29,6 @@ @@ -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 = '|'; @@ -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 = '{'; @@ -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"; @@ -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 { @@ -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 @@ -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 { @@ -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 @@ -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) @@ -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) @@ -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) @@ -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;
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;
}
}
send_result(io_data, c, isjson);
did = true;
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

@ -109,6 +109,36 @@ The STATUS section is: @@ -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 @@ -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…
Cancel
Save