From eec7c1a96350dddbd08628c23810d992961d769d Mon Sep 17 00:00:00 2001 From: Kano Date: Fri, 20 Apr 2012 23:50:27 +1000 Subject: [PATCH 1/2] api.c escape required characters in return strings + pools returns the username --- README | 11 +++++- api.c | 117 +++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 116 insertions(+), 12 deletions(-) diff --git a/README b/README index ed710877..3232f874 100644 --- a/README +++ b/README @@ -596,6 +596,15 @@ An example request in both formats to set GPU 0 fan to 80%: The format of each reply (unless stated otherwise) is a STATUS section followed by an optional detail section +From API verion 1.7 onwards, reply strings in JSON and Text have the +necessary escaping as required to avoid ambiguity - they didn't before 1.7 +For JSON the 2 characters '"' and '\' are escaped with a '\' before them +For Text the 4 characters '|' ',' '=' and '\' are escaped the same way + +Only user entered information will contain characters that require being +escaped, such as Pool URL, User and Password or the Config save filename, +when they are returned in messages or as their values by the API + For API version 1.4 and later: The STATUS section is: @@ -622,7 +631,7 @@ The STATUS section is: This defaults to the cgminer version but is the value of --api-description if it was specified at runtime. -For API version 1.6: +For API version 1.7: The list of requests - a (*) means it requires privileged access - and replies are: diff --git a/api.c b/api.c index 953b1938..ac2197c2 100644 --- a/api.c +++ b/api.c @@ -157,7 +157,7 @@ static const char *COMMA = ","; static const char SEPARATOR = '|'; static const char GPUSEP = ','; -static const char *APIVERSION = "1.6"; +static const char *APIVERSION = "1.7"; static const char *DEAD = "Dead"; static const char *SICK = "Sick"; static const char *NOSTART = "NoStart"; @@ -483,6 +483,68 @@ extern struct device_api bitforce_api; extern struct device_api icarus_api; #endif +// This is only called when expected to be needed (rarely) +// i.e. strings outside of the codes control (input from the user) +static char *escape_string(char *str, bool isjson) +{ + char *buf, *ptr; + int count; + + count = 0; + for (ptr = str; *ptr; ptr++) { + switch (*ptr) { + case ',': + case '|': + case '=': + if (!isjson) + count++; + break; + case '"': + if (isjson) + count++; + break; + case '\\': + count++; + break; + } + } + + if (count == 0) + return str; + + buf = malloc(strlen(str) + count + 1); + if (unlikely(!buf)) + quit(1, "Failed to malloc escape buf"); + + 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 defined(USE_BITFORCE) || defined(USE_ICARUS) static int numpgas() { @@ -1102,6 +1164,8 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, { char buf[BUFSIZ]; char *status, *lp; + char *rpc_url; + char *rpc_user; int i; if (total_pools == 0) { @@ -1133,27 +1197,40 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, else lp = (char *)NO; + rpc_url = escape_string(pool->rpc_url, isjson); + rpc_user = escape_string(pool->rpc_user, isjson); + if (isjson) - sprintf(buf, "%s{\"POOL\":%d,\"URL\":\"%s\",\"Status\":\"%s\",\"Priority\":%d,\"Long Poll\":\"%s\",\"Getworks\":%d,\"Accepted\":%d,\"Rejected\":%d,\"Discarded\":%d,\"Stale\":%d,\"Get Failures\":%d,\"Remote Failures\":%d}", + sprintf(buf, "%s{\"POOL\":%d,\"URL\":\"%s\",\"Status\":\"%s\",\"Priority\":%d,\"Long Poll\":\"%s\",\"Getworks\":%d,\"Accepted\":%d,\"Rejected\":%d,\"Discarded\":%d,\"Stale\":%d,\"Get Failures\":%d,\"Remote Failures\":%d,\"User\":\"%s\"}", (i > 0) ? COMMA : "", - i, pool->rpc_url, status, pool->prio, lp, + i, rpc_url, status, pool->prio, lp, pool->getwork_requested, pool->accepted, pool->rejected, pool->discarded_work, pool->stale_shares, pool->getfail_occasions, - pool->remotefail_occasions); + pool->remotefail_occasions, + rpc_user); else - sprintf(buf, "POOL=%d,URL=%s,Status=%s,Priority=%d,Long Poll=%s,Getworks=%d,Accepted=%d,Rejected=%d,Discarded=%d,Stale=%d,Get Failures=%d,Remote Failures=%d%c", - i, pool->rpc_url, status, pool->prio, lp, + sprintf(buf, "POOL=%d,URL=%s,Status=%s,Priority=%d,Long Poll=%s,Getworks=%d,Accepted=%d,Rejected=%d,Discarded=%d,Stale=%d,Get Failures=%d,Remote Failures=%d,User=%s%c", + i, rpc_url, status, pool->prio, lp, pool->getwork_requested, pool->accepted, pool->rejected, pool->discarded_work, pool->stale_shares, pool->getfail_occasions, - pool->remotefail_occasions, SEPARATOR); + pool->remotefail_occasions, + rpc_user, SEPARATOR); strcat(io_buffer, buf); + + if (rpc_url != pool->rpc_url) + free(rpc_url); + rpc_url = NULL; + + if (rpc_user != pool->rpc_user) + free(rpc_user); + rpc_user = NULL; } if (isjson) @@ -1443,6 +1520,7 @@ exitsama: static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) { char *url, *user, *pass; + char *ptr; if (param == NULL || *param == '\0') { strcpy(io_buffer, message(MSG_MISPDP, 0, NULL, isjson)); @@ -1450,7 +1528,11 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) } if (!pooldetails(param, &url, &user, &pass)) { - strcpy(io_buffer, message(MSG_INVPDP, 0, param, isjson)); + ptr = escape_string(param, isjson); + strcpy(io_buffer, message(MSG_INVPDP, 0, ptr, isjson)); + if (ptr != param) + free(ptr); + ptr = NULL; return; } @@ -1459,7 +1541,11 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) return; } - strcpy(io_buffer, message(MSG_ADDPOOL, 0, url, isjson)); + ptr = escape_string(url, isjson); + strcpy(io_buffer, message(MSG_ADDPOOL, 0, ptr, isjson)); + if (ptr != url) + free(ptr); + ptr = NULL; } static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1792,6 +1878,7 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) { FILE *fcfg; + char *ptr; if (param == NULL || *param == '\0') { strcpy(io_buffer, message(MSG_MISFN, 0, NULL, isjson)); @@ -1800,14 +1887,22 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) fcfg = fopen(param, "w"); if (!fcfg) { - strcpy(io_buffer, message(MSG_BADFN, 0, param, isjson)); + ptr = escape_string(param, isjson); + strcpy(io_buffer, message(MSG_BADFN, 0, ptr, isjson)); + if (ptr != param) + free(ptr); + ptr = NULL; return; } write_config(fcfg); fclose(fcfg); - strcpy(io_buffer, message(MSG_SAVED, 0, param, isjson)); + ptr = escape_string(param, isjson); + strcpy(io_buffer, message(MSG_SAVED, 0, ptr, isjson)); + if (ptr != param) + free(ptr); + ptr = NULL; } struct CMDS { From 2e1d2017de3e7cfc80d42ee2d80ffe52f1c8d09d Mon Sep 17 00:00:00 2001 From: Kano Date: Sat, 21 Apr 2012 03:15:41 +1000 Subject: [PATCH 2/2] API add removepool like the screen interface --- README | 6 ++++++ api.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ cgminer.c | 4 ++-- miner.h | 1 + 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/README b/README index 3232f874..63448466 100644 --- a/README +++ b/README @@ -709,6 +709,12 @@ The list of requests - a (*) means it requires privileged access - and replies a stating the results of disabling pool N The Msg includes the pool URL + removepool|N (*) + none There is no reply section just the STATUS section + stating the results of removing pool N + The Msg includes the pool URL + N.B. all details for the pool will be lost + gpuenable|N (*) none There is no reply section just the STATUS section stating the results of the enable request diff --git a/api.c b/api.c index ac2197c2..dc373f70 100644 --- a/api.c +++ b/api.c @@ -337,6 +337,10 @@ static const char *JSON_PARAMETER = "parameter"; #define MSG_PGAUNW 65 #endif +#define MSG_REMLASTP 66 +#define MSG_ACTPOOL 67 +#define MSG_REMPOOL 68 + enum code_severity { SEVERITY_ERR, SEVERITY_WARN, @@ -456,6 +460,9 @@ struct CODES { { SEVERITY_ERR, MSG_INVPDP, PARAM_STR, "Invalid addpool details '%s'" }, { SEVERITY_ERR, MSG_TOOMANYP,PARAM_NONE, "Reached maximum number of pools (%d)" }, { SEVERITY_SUCC, MSG_ADDPOOL, PARAM_STR, "Added pool '%s'" }, + { SEVERITY_ERR, MSG_REMLASTP,PARAM_POOL, "Cannot remove last pool %d:'%s'" }, + { SEVERITY_ERR, MSG_ACTPOOL, PARAM_POOL, "Cannot remove active pool %d:'%s'" }, + { SEVERITY_SUCC, MSG_REMPOOL, PARAM_BOTH, "Removed pool %d:'%s'" }, { SEVERITY_SUCC, MSG_NOTIFY, PARAM_NONE, "Notify" }, { SEVERITY_FAIL, 0, 0, NULL } }; @@ -1621,6 +1628,57 @@ static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) strcpy(io_buffer, message(MSG_DISPOOL, id, NULL, isjson)); } +static void removepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) +{ + struct pool *pool; + char *rpc_url; + bool dofree = false; + int id; + + if (total_pools == 0) { + strcpy(io_buffer, message(MSG_NOPOOL, 0, NULL, isjson)); + return; + } + + if (param == NULL || *param == '\0') { + strcpy(io_buffer, message(MSG_MISPID, 0, NULL, isjson)); + return; + } + + id = atoi(param); + if (id < 0 || id >= total_pools) { + strcpy(io_buffer, message(MSG_INVPID, id, NULL, isjson)); + return; + } + + if (total_pools <= 1) { + strcpy(io_buffer, message(MSG_REMLASTP, id, NULL, isjson)); + return; + } + + pool = pools[id]; + if (pool == current_pool()) + switch_pools(NULL); + + if (pool == current_pool()) { + strcpy(io_buffer, message(MSG_ACTPOOL, id, NULL, isjson)); + return; + } + + pool->enabled = false; + rpc_url = escape_string(pool->rpc_url, isjson); + if (rpc_url != pool->rpc_url) + dofree = true; + + remove_pool(pool); + + strcpy(io_buffer, message(MSG_REMPOOL, id, rpc_url, isjson)); + + if (dofree) + free(rpc_url); + rpc_url = NULL; +} + static bool splitgpuvalue(char *param, int *gpu, char **value, bool isjson) { int id; @@ -1934,6 +1992,7 @@ struct CMDS { { "addpool", addpool, true }, { "enablepool", enablepool, true }, { "disablepool", disablepool, true }, + { "removepool", removepool, true }, { "gpuintensity", gpuintensity, true }, { "gpumem", gpumem, true }, { "gpuengine", gpuengine, true }, diff --git a/cgminer.c b/cgminer.c index 70f3e5e9..0023922a 100644 --- a/cgminer.c +++ b/cgminer.c @@ -2383,10 +2383,11 @@ static void display_pool_summary(struct pool *pool) unlock_curses(); } } +#endif /* We can't remove the memory used for this struct pool because there may * still be work referencing it. We just remove it from the pools list */ -static void remove_pool(struct pool *pool) +void remove_pool(struct pool *pool) { int i, last_pool = total_pools - 1; struct pool *other; @@ -2407,7 +2408,6 @@ static void remove_pool(struct pool *pool) pool->pool_no = total_pools; total_pools--; } -#endif void write_config(FILE *fcfg) { diff --git a/miner.h b/miner.h index 24a6ef36..aa87786d 100644 --- a/miner.h +++ b/miner.h @@ -645,6 +645,7 @@ extern int curses_int(const char *query); extern char *curses_input(const char *query); extern void kill_work(void); extern void switch_pools(struct pool *selected); +extern void remove_pool(struct pool *pool); extern void write_config(FILE *fcfg); extern void log_curses(int prio, const char *f, va_list ap); extern void clear_logwin(void);