Commit c7cc2e42262f88798cc77aec56cb78f69531429d

Con Kolivas 2012-04-21T14:03:41

Merge branch 'master' into ztex

diff --git a/README b/README
index ed71087..6344846 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:
 
@@ -700,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 953b193..dc373f7 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";
@@ -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 }
 };
@@ -483,6 +490,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 +1171,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 +1204,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 +1527,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 +1535,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 +1548,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)
@@ -1535,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;
@@ -1792,6 +1936,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 +1945,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 {
@@ -1839,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 1577dec..dff9814 100644
--- a/cgminer.c
+++ b/cgminer.c
@@ -2388,10 +2388,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;
@@ -2412,7 +2413,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 69abfe7..1f68beb 100644
--- a/miner.h
+++ b/miner.h
@@ -656,6 +656,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);