Browse Source

Merge pull request #124 from kanoi/master

API commits - IP 0/0 means all, add "Log Interval" to config, restrict access to modify commands
nfactor-troky
Con Kolivas 13 years ago
parent
commit
2ca8d38e0e
  1. 66
      README
  2. 175
      api.c
  3. 2
      cgminer.c
  4. 2
      miner.h

66
README

@ -119,10 +119,13 @@ Usage instructions: Run "cgminer --help" to see options: @@ -119,10 +119,13 @@ Usage instructions: Run "cgminer --help" to see options:
Usage: . [-atDdGCgIKklmpPQqrRsTouvwOchnV]
Options for both config file and command line:
--api-allow Allow API access (if enabled) only to the given list of IP[/Prefix] address[/subnets]
--api-allow Allow API access (if enabled) only to the given list of [W:]IP[/Prefix] address[/subnets]
This overrides --api-network and you must specify 127.0.0.1 if it is required
W: in front of the IP address gives that address privileged access to all api commands
--api-description Description placed in the API status header (default: cgminer version)
--api-listen Listen for API requests (default: disabled)
By default any command that does not just display data returns access denied
See --api-allow to overcome this
--api-network Allow API (if enabled) to listen on/for any address (default: only 127.0.0.1)
--api-port Port number of miner API (default: 4028)
--auto-fan Automatically adjust all GPU fan speeds to maintain a target temperature
@ -537,11 +540,22 @@ running cgminer and reply with a string and then close the socket each time @@ -537,11 +540,22 @@ running cgminer and reply with a string and then close the socket each time
If you add the "--api-network" option, it will accept API requests from any
network attached computer.
You can only access the comands that reply with data in this mode.
By default, you cannot access any privileged command that affects the miner -
you will receive an access denied status message see --api-access below.
You can specify IP addresses/prefixes that are only allowed to access the API
with the "--api-access" option e.g. --api-access 192.168.0.1,10.0.0/24
with the "--api-access" option e.g. --api-access W:192.168.0.1,10.0.0/24
will allow 192.168.0.1 or any address matching 10.0.0.*, but nothing else
IP addresses are automatically padded with extra '.0's as needed
Without a /prefix is the same as specifying /32
0/0 means all IP addresses.
The 'W:' on the front gives that address/subnet privileged access to commands
that modify cgminer.
Without it those commands return an access denied status.
Privileged access is checked in the order the IP addresses were supplied to
"--api-access"
The first match determines the privilege level.
Using the "--api-access" option overides the "--api-network" option if they
are both specified
With "--api-access", 127.0.0.1 is not by default given access unless specified
@ -584,7 +598,9 @@ The STATUS section is: @@ -584,7 +598,9 @@ The STATUS section is:
This defaults to the cgminer version but is the value of --api-description
if it was specified at runtime.
The list of requests and replies are:
For API version 1.2:
The list of requests - a (*) means it requires privileged access - and replies are:
Request Reply Section Details
------- ------------- -------
@ -597,7 +613,8 @@ The list of requests and replies are: @@ -597,7 +613,8 @@ The list of requests and replies are:
Pool Count=N, <- the number of Pools
ADL=X, <- Y or N if ADL is compiled in the code
ADL in use=X, <- Y or N if any GPU has ADL
Strategy=Name| <- the current pool strategy
Strategy=Name, <- the current pool strategy
Log Interval=N| <- log interval (--log N)
summary SUMMARY The status summary of the miner
e.g. Elapsed=NNN,Found Blocks=N,Getworks=N,...|
@ -625,42 +642,58 @@ The list of requests and replies are: @@ -625,42 +642,58 @@ The list of requests and replies are:
cpucount CPUS Count=N| <- the number of CPUs
Always returns 0 if CPU mining is disabled
switchpool|N none There is no reply section just the STATUS section
switchpool|N (*)
none There is no reply section just the STATUS section
stating the results of switching pool N to the
highest priority (the pool is also enabled)
The Msg includes the pool URL
gpuenable|N none There is no reply section just the STATUS section
gpuenable|N (*)
none There is no reply section just the STATUS section
stating the results of the enable request
gpudisable|N none There is no reply section just the STATUS section
gpudisable|N (*)
none There is no reply section just the STATUS section
stating the results of the disable request
gpurestart|N none There is no reply section just the STATUS section
gpurestart|N (*)
none There is no reply section just the STATUS section
stating the results of the restart request
gpuintensity|N,I none There is no reply section just the STATUS section
gpuintensity|N,I (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N intensity to I
gpumem|N,V none There is no reply section just the STATUS section
gpumem|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N memoryclock to V MHz
gpuengine|N,V none There is no reply section just the STATUS section
gpuengine|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N clock to V MHz
gpufan|N,V none There is no reply section just the STATUS section
gpufan|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N fan speed to V%
gpuvddc|N,V none There is no reply section just the STATUS section
gpuvddc|N,V (*)
none There is no reply section just the STATUS section
stating the results of setting GPU N vddc to V
save|filename none There is no reply section just the STATUS section
save|filename (*)
none There is no reply section just the STATUS section
stating success or failure saving the cgminer config
to filename
quit none There is no status section but just a single "BYE|"
quit (*) none There is no status section but just a single "BYE|"
reply before cgminer quits
privileged (*)
none There is no reply section just the STATUS section
stating an error if you do not have privileged access
to the API and success if you do have privilege
The command doesn't change anything in cgminer
When you enable, disable or restart a GPU, you will also get Thread messages in
the cgminer status window
@ -694,8 +727,7 @@ api-example.c - a 'C' program to access the API (with source code) @@ -694,8 +727,7 @@ api-example.c - a 'C' program to access the API (with source code)
api-example summary 127.0.0.1 4028
miner.php - an example web page to access the API
This includes buttons to enable, disable and restart the GPUs and also to
quit cgminer
This includes buttons and inputs to attempt access to the privileged commands
You must modify the 2 lines near the top to change where it looks for cgminer
$miner = '127.0.0.1'; # hostname or IP address
$port = 4028;

175
api.c

@ -11,6 +11,7 @@ @@ -11,6 +11,7 @@
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
@ -151,7 +152,7 @@ static const char *COMMA = ","; @@ -151,7 +152,7 @@ static const char *COMMA = ",";
static const char SEPARATOR = '|';
static const char GPUSEP = ',';
static const char *APIVERSION = "1.1";
static const char *APIVERSION = "1.2";
static const char *DEAD = "Dead";
static const char *SICK = "Sick";
static const char *NOSTART = "NoStart";
@ -256,6 +257,8 @@ static const char *JSON_PARAMETER = "parameter"; @@ -256,6 +257,8 @@ static const char *JSON_PARAMETER = "parameter";
#define MSG_MISFN 42
#define MSG_BADFN 43
#define MSG_SAVED 44
#define MSG_ACCDENY 45
#define MSG_ACCOK 46
enum code_severity {
SEVERITY_ERR,
@ -341,15 +344,19 @@ struct CODES { @@ -341,15 +344,19 @@ struct CODES {
{ SEVERITY_ERR, MSG_MISFN, PARAM_NONE, "Missing save filename parameter" },
{ SEVERITY_ERR, MSG_BADFN, PARAM_STR, "Can't open or create save file '%s'" },
{ SEVERITY_ERR, MSG_SAVED, PARAM_STR, "Configuration saved to file '%s'" },
{ SEVERITY_ERR, MSG_ACCDENY, PARAM_STR, "Access denied to '%s' command" },
{ SEVERITY_SUCC, MSG_ACCOK, PARAM_NONE, "Privileged access OK" },
{ SEVERITY_FAIL, 0, 0, NULL }
};
static int my_thr_id = 0;
static int bye = 0;
static bool ping = true;
struct IP4ACCESS {
in_addr_t ip;
in_addr_t mask;
bool writemode;
};
static struct IP4ACCESS *ipaccess = NULL;
@ -496,9 +503,9 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, @@ -496,9 +503,9 @@ static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
strcpy(io_buffer, message(MSG_MINECON, 0, NULL, isjson));
if (isjson)
sprintf(buf, "," JSON_MINECON "{\"GPU Count\":%d,\"CPU Count\":%d,\"Pool Count\":%d,\"ADL\":\"%s\",\"ADL in use\":\"%s\",\"Strategy\":\"%s\"}" JSON_CLOSE, nDevs, cpucount, total_pools, adl, adlinuse, strategies[pool_strategy].s);
sprintf(buf, "," JSON_MINECON "{\"GPU Count\":%d,\"CPU Count\":%d,\"Pool Count\":%d,\"ADL\":\"%s\",\"ADL in use\":\"%s\",\"Strategy\":\"%s\",\"Log Interval\":\"%d\"}" JSON_CLOSE, nDevs, cpucount, total_pools, adl, adlinuse, strategies[pool_strategy].s, opt_log_interval);
else
sprintf(buf, _MINECON ",GPU Count=%d,CPU Count=%d,Pool Count=%d,ADL=%s,ADL in use=%s,Strategy=%s%c", nDevs, cpucount, total_pools, adl, adlinuse, strategies[pool_strategy].s, SEPARATOR);
sprintf(buf, _MINECON ",GPU Count=%d,CPU Count=%d,Pool Count=%d,ADL=%s,ADL in use=%s,Strategy=%s,Log Interval=%d%c", nDevs, cpucount, total_pools, adl, adlinuse, strategies[pool_strategy].s, opt_log_interval, SEPARATOR);
strcat(io_buffer, buf);
}
@ -1129,9 +1136,17 @@ void doquit(SOCKETTYPE c, __maybe_unused char *param, bool isjson) @@ -1129,9 +1136,17 @@ void doquit(SOCKETTYPE c, __maybe_unused char *param, bool isjson)
send_result(c, isjson);
*io_buffer = '\0';
bye = 1;
PTH(&thr_info[my_thr_id]) = 0L;
kill_work();
}
void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
{
strcpy(io_buffer, message(MSG_ACCOK, 0, NULL, isjson));
}
void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
{
FILE *fcfg;
@ -1156,30 +1171,32 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1156,30 +1171,32 @@ void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
struct CMDS {
char *name;
void (*func)(SOCKETTYPE, char *, bool);
bool requires_writemode;
} cmds[] = {
{ "version", apiversion },
{ "config", minerconfig },
{ "devs", devstatus },
{ "pools", poolstatus },
{ "summary", summary },
{ "gpuenable", gpuenable },
{ "gpudisable", gpudisable },
{ "gpurestart", gpurestart },
{ "gpu", gpudev },
{ "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 },
#ifdef WANT_CPUMINE
{ "cpu", cpudev },
{ "cpu", cpudev, false },
#endif
{ "gpucount", gpucount },
{ "cpucount", cpucount },
{ "switchpool", switchpool },
{ "gpuintensity", gpuintensity },
{ "gpumem", gpumem},
{ "gpuengine", gpuengine},
{ "gpufan", gpufan},
{ "gpuvddc", gpuvddc},
{ "save", dosave },
{ "quit", doquit },
{ NULL, NULL }
{ "gpucount", gpucount, false },
{ "cpucount", cpucount, false },
{ "switchpool", switchpool, 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 },
{ NULL, NULL, false }
};
static void send_result(SOCKETTYPE c, bool isjson)
@ -1192,16 +1209,16 @@ static void send_result(SOCKETTYPE c, bool isjson) @@ -1192,16 +1209,16 @@ static void send_result(SOCKETTYPE c, bool isjson)
len = strlen(io_buffer);
applog(LOG_DEBUG, "DBG: send reply: (%d) '%.10s%s'", len+1, io_buffer, len > 10 ? "..." : "");
applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", len+1, io_buffer, len > 10 ? "..." : "");
// ignore failure - it's closed immediately anyway
n = send(c, io_buffer, len+1, 0);
if (opt_debug) {
if (SOCKETFAIL(n))
applog(LOG_DEBUG, "DBG: send failed: %s", SOCKERRMSG);
applog(LOG_DEBUG, "API: send failed: %s", SOCKERRMSG);
else
applog(LOG_DEBUG, "DBG: sent %d", n);
applog(LOG_DEBUG, "API: sent %d", n);
}
}
@ -1233,14 +1250,18 @@ static void tidyup() @@ -1233,14 +1250,18 @@ static void tidyup()
}
/*
* Interpret IP[/Prefix][,IP2[/Prefix2][,...]] --api-allow option
*
* Interpret [R|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;
bool writemode;
buf = malloc(strlen(opt_api_allow) + 1);
if (unlikely(!buf))
@ -1274,38 +1295,53 @@ static void setup_ipaccess() @@ -1274,38 +1295,53 @@ static void setup_ipaccess()
if (comma)
*(comma++) = '\0';
slash = strchr(ptr, '/');
if (!slash)
ipaccess[ips].mask = 0xffffffff;
writemode = false;
if (isalpha(*ptr) && *(ptr+1) == ':') {
if (tolower(*ptr) == 'w')
writemode = true;
ptr += 2;
}
ipaccess[ips].writemode = writemode;
if (strcmp(ptr, ALLIP4) == 0)
ipaccess[ips].ip = ipaccess[ips].mask = 0;
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 << (8 * (mask >> 3)));
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 << (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';
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
octet = atoi(ptr);
if (octet < 0 || octet > 0xff)
goto popipo; // skip invalid
ipaccess[ips].ip |= (octet << (i * 8));
ipaccess[ips].ip |= (octet << (i * 8));
ptr = dot;
}
ptr = dot;
}
ipaccess[ips].ip &= ipaccess[ips].mask;
ipaccess[ips].ip &= ipaccess[ips].mask;
}
ips++;
popipo:
@ -1315,7 +1351,7 @@ popipo: @@ -1315,7 +1351,7 @@ popipo:
free(buf);
}
void api(void)
void api(int api_thr_id)
{
char buf[BUFSIZ];
char param_buf[BUFSIZ];
@ -1332,6 +1368,7 @@ void api(void) @@ -1332,6 +1368,7 @@ void api(void)
char *cmd;
char *param;
bool addrok;
bool writemode;
json_error_t json_err;
json_t *json_config;
json_t *json_val;
@ -1339,6 +1376,8 @@ void api(void) @@ -1339,6 +1376,8 @@ void api(void)
bool did;
int i;
my_thr_id = api_thr_id;
/* This should be done first to ensure curl has already called WSAStartup() in windows */
sleep(opt_log_interval);
@ -1423,27 +1462,27 @@ void api(void) @@ -1423,27 +1462,27 @@ void api(void)
goto die;
}
connectaddr = inet_ntoa(cli.sin_addr);
addrok = false;
writemode = false;
if (opt_api_allow) {
for (i = 0; i < ips; i++) {
if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) {
addrok = true;
writemode = ipaccess[i].writemode;
break;
}
}
} else {
if (opt_api_network)
addrok = true;
else {
connectaddr = inet_ntoa(cli.sin_addr);
else
addrok = (strcmp(connectaddr, localaddr) == 0);
}
}
if (opt_debug) {
connectaddr = inet_ntoa(cli.sin_addr);
applog(LOG_DEBUG, "DBG: connection from %s - %s", connectaddr, addrok ? "Accepted" : "Ignored");
}
if (opt_debug)
applog(LOG_DEBUG, "API: connection from %s - %s", connectaddr, addrok ? "Accepted" : "Ignored");
if (addrok) {
n = recv(c, &buf[0], BUFSIZ-1, 0);
@ -1454,9 +1493,9 @@ void api(void) @@ -1454,9 +1493,9 @@ void api(void)
if (opt_debug) {
if (SOCKETFAIL(n))
applog(LOG_DEBUG, "DBG: recv failed: %s", SOCKERRMSG);
applog(LOG_DEBUG, "API: recv failed: %s", SOCKERRMSG);
else
applog(LOG_DEBUG, "DBG: recv command: (%d) '%s'", n, buf);
applog(LOG_DEBUG, "API: recv command: (%d) '%s'", n, buf);
}
if (!SOCKETFAIL(n)) {
@ -1522,7 +1561,13 @@ void api(void) @@ -1522,7 +1561,13 @@ void api(void)
if (!did)
for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(cmd, cmds[i].name) == 0) {
(cmds[i].func)(c, param, isjson);
if (cmds[i].requires_writemode && !writemode) {
strcpy(io_buffer, message(MSG_ACCDENY, 0, cmds[i].name, isjson));
applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name);
}
else
(cmds[i].func)(c, param, isjson);
send_result(c, isjson);
did = true;
break;

2
cgminer.c

@ -2802,7 +2802,7 @@ static void *api_thread(void *userdata) @@ -2802,7 +2802,7 @@ static void *api_thread(void *userdata)
pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
api();
api(api_thr_id);
PTH(mythr) = 0L;

2
miner.h

@ -449,7 +449,7 @@ extern int set_engineclock(int gpu, int iEngineClock); @@ -449,7 +449,7 @@ extern int set_engineclock(int gpu, int iEngineClock);
extern int set_memoryclock(int gpu, int iMemoryClock);
#endif
extern void api(void);
extern void api(int thr_id);
#define MAX_GPUDEVICES 16
#define MAX_DEVICES 32

Loading…
Cancel
Save