Browse Source

Merge pull request #243 from kanoi/master

define, implement and document API option --api-groups
nfactor-troky
Con Kolivas 13 years ago
parent
commit
4c5d41a8dd
  1. 42
      API-README
  2. 2
      README
  3. 330
      api.c
  4. 15
      cgminer.c
  5. 1
      miner.h

42
API-README

@ -19,8 +19,9 @@ IP addresses are automatically padded with extra '.0's as needed @@ -19,8 +19,9 @@ 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.
that modify cgminer (thus all API commands)
Without it those commands return an access denied status.
See --api-groups below to define other groups like W:
Privileged access is checked in the order the IP addresses were supplied to
"--api-allow"
The first match determines the privilege level.
@ -28,6 +29,26 @@ Using the "--api-allow" option overides the "--api-network" option if they @@ -28,6 +29,26 @@ Using the "--api-allow" option overides the "--api-network" option if they
are both specified
With "--api-allow", 127.0.0.1 is not by default given access unless specified
More groups (like the privileged group W:) can be defined using the
--api-groups command
Valid groups are only the letters A-Z (except R & W are predefined) and are
not case sensitive
The R: group is the same as not privileged access
The W: group is (as stated) privileged access (thus all API commands)
To give an IP address/subnet access to a group you use the group letter
in front of the IP address instead of W: e.g. P:192.168.0/32
An IP address/subnet can only be a member of one group
A sample API group would be:
--api-groups P:switchpool:enablepool:addpool:disablepool:removepool:*
This would create a group 'P' that can do all current pool commands and all
non-priviliged commands - the '*' means all non-priviledged commands
Without the '*' the group would only have access to the pool commands
Defining multiple groups example:
--api-groups Q:quit:restart:*,S:save
This would define 2 groups:
Q: that can 'quit' and 'restart' as well as all non-priviledged commands
S: that can only 'save' and no other commands
The RPC API request can be either simple text or JSON.
If the request is JSON (starts with '{'), it will reply with a JSON formatted
@ -86,8 +107,8 @@ The list of requests - a (*) means it requires privileged access - and replies a @@ -86,8 +107,8 @@ The list of requests - a (*) means it requires privileged access - and replies a
Request Reply Section Details
------- ------------- -------
version VERSION CGMiner=cgminer version
API=API version
version VERSION CGMiner=cgminer, version
API=API| version
config CONFIG Some miner configuration information:
GPU Count=N, <- the number of GPUs
@ -243,6 +264,9 @@ The list of requests - a (*) means it requires privileged access - and replies a @@ -243,6 +264,9 @@ The list of requests - a (*) means it requires privileged access - and replies a
Device drivers are also able to add stats to the
end of the details returned
check|cmd COMMAND Exists=Y/N, <- 'cmd' exists in this version
Access=Y/N| <- you have access to use 'cmd'
When you enable, disable or restart a GPU or PGA, you will also get Thread messages
in the cgminer status window
@ -285,7 +309,17 @@ miner.php - an example web page to access the API @@ -285,7 +309,17 @@ miner.php - an example web page to access the API
Feature Changelog for external applications using the API:
API V1.12
API V1.13
Added API commands:
'check'
Support was added to cgminer for API access groups with the --api-groups option
It's 100% backwards compatible with previous --api-access commands
----------
API V1.12 (cgminer v2.4.3)
Modified API commands:
'stats' - more pool stats added

2
README

@ -119,6 +119,8 @@ Options for both config file and command line: @@ -119,6 +119,8 @@ Options for both config file and command line:
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-groups API one letter groups G:cmd:cmd[,P:cmd:*...]
See API-README for usage
--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

330
api.c

@ -158,6 +158,7 @@ static char *msg_buffer = NULL; @@ -158,6 +158,7 @@ static char *msg_buffer = NULL;
static SOCKETTYPE sock = INVSOCK;
static const char *UNAVAILABLE = " - API will not be available";
static const char *GROUPDIS = " - groups will be disabled";
static const char *BLANK = "";
static const char *COMMA = ",";
@ -165,7 +166,7 @@ static const char SEPARATOR = '|'; @@ -165,7 +166,7 @@ static const char SEPARATOR = '|';
#define SEPSTR "|"
static const char GPUSEP = ',';
static const char *APIVERSION = "1.12";
static const char *APIVERSION = "1.13";
static const char *DEAD = "Dead";
static const char *SICK = "Sick";
static const char *NOSTART = "NoStart";
@ -243,6 +244,7 @@ static const char *OSINFO = @@ -243,6 +244,7 @@ static const char *OSINFO =
#define _BYE "BYE"
#define _RESTART "RESTART"
#define _MINESTATS "STATS"
#define _CHECK "CHECK"
static const char ISJSON = '{';
#define JSON0 "{"
@ -277,6 +279,7 @@ static const char ISJSON = '{'; @@ -277,6 +279,7 @@ static const char ISJSON = '{';
#define JSON_RESTART JSON1 _RESTART JSON1
#define JSON_CLOSE JSON3
#define JSON_MINESTATS JSON1 _MINESTATS JSON2
#define JSON_CHECK JSON1 _CHECK JSON2
#define JSON_END JSON4
static const char *JSON_COMMAND = "command";
@ -364,6 +367,8 @@ static const char *JSON_PARAMETER = "parameter"; @@ -364,6 +367,8 @@ static const char *JSON_PARAMETER = "parameter";
#define MSG_REMPOOL 68
#define MSG_DEVDETAILS 69
#define MSG_MINESTATS 70
#define MSG_MISCHK 71
#define MSG_CHECK 72
enum code_severity {
SEVERITY_ERR,
@ -507,6 +512,8 @@ struct CODES { @@ -507,6 +512,8 @@ struct CODES {
{ SEVERITY_SUCC, MSG_NOTIFY, PARAM_NONE, "Notify" },
{ SEVERITY_SUCC, MSG_DEVDETAILS,PARAM_NONE, "Device Details" },
{ SEVERITY_SUCC, MSG_MINESTATS,PARAM_NONE, "CGMiner stats" },
{ SEVERITY_ERR, MSG_MISCHK, PARAM_NONE, "Missing check cmd" },
{ SEVERITY_SUCC, MSG_CHECK, PARAM_NONE, "Check command" },
{ SEVERITY_FAIL, 0, 0, NULL }
};
@ -525,9 +532,25 @@ static time_t when = 0; // when the request occurred @@ -525,9 +532,25 @@ static time_t when = 0; // when the request occurred
struct IP4ACCESS {
in_addr_t ip;
in_addr_t mask;
bool writemode;
char group;
};
#define GROUP(g) (toupper(g))
#define PRIVGROUP GROUP('W')
#define NOPRIVGROUP GROUP('R')
#define ISPRIVGROUP(g) (GROUP(g) == PRIVGROUP)
#define GROUPOFFSET(g) (GROUP(g) - GROUP('A'))
#define VALIDGROUP(g) (GROUP(g) >= GROUP('A') && GROUP(g) <= GROUP('Z'))
#define COMMANDS(g) (apigroups[GROUPOFFSET(g)].commands)
#define DEFINEDGROUP(g) (ISPRIVGROUP(g) || COMMANDS(g) != NULL)
struct APIGROUPS {
// This becomes a string like: "|cmd1|cmd2|cmd3|" so it's quick to search
char *commands;
} apigroups['Z' - 'A' + 1]; // only A=0 to Z=25 (R: noprivs, W: allprivs)
static bool groups_enabled = false;
static struct IP4ACCESS *ipaccess = NULL;
static int ips = 0;
@ -796,7 +819,7 @@ static char *message(int messageid, int paramid, char *param2, bool isjson) @@ -796,7 +819,7 @@ static char *message(int messageid, int paramid, char *param2, bool isjson)
return msg_buffer;
}
static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
sprintf(io_buffer, isjson
? "%s," JSON_VERSION "{\"CGMiner\":\"%s\",\"API\":\"%s\"}" JSON_CLOSE
@ -805,7 +828,7 @@ static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, @@ -805,7 +828,7 @@ static void apiversion(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
VERSION, APIVERSION);
}
static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void minerconfig(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
int gpucount = 0;
@ -1003,7 +1026,7 @@ static void cpustatus(int cpu, bool isjson) @@ -1003,7 +1026,7 @@ static void cpustatus(int cpu, bool isjson)
}
#endif
static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
int devcount = 0;
int numgpu = 0;
@ -1069,7 +1092,7 @@ static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, b @@ -1069,7 +1092,7 @@ static void devstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, b
}
#ifdef HAVE_OPENCL
static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int id;
@ -1103,7 +1126,7 @@ static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1103,7 +1126,7 @@ static void gpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
}
#endif
#ifdef HAVE_AN_FPGA
static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int numpga = numpgas();
int id;
@ -1137,7 +1160,7 @@ static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1137,7 +1160,7 @@ static void pgadev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcat(io_buffer, JSON_CLOSE);
}
static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int numpga = numpgas();
struct thr_info *thr;
@ -1193,7 +1216,7 @@ static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1193,7 +1216,7 @@ static void pgaenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcpy(io_buffer, message(MSG_PGAENA, id, NULL, isjson));
}
static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int numpga = numpgas();
int id;
@ -1234,7 +1257,7 @@ static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1234,7 +1257,7 @@ static void pgadisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
#endif
#ifdef WANT_CPUMINE
static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int id;
@ -1268,7 +1291,7 @@ static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1268,7 +1291,7 @@ static void cpudev(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
}
#endif
static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
char *status, *lp;
@ -1345,7 +1368,7 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, @@ -1345,7 +1368,7 @@ static void poolstatus(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
strcat(io_buffer, JSON_CLOSE);
}
static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
double utility, mhs;
@ -1379,7 +1402,7 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo @@ -1379,7 +1402,7 @@ static void summary(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo
#endif
}
#ifdef HAVE_OPENCL
static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
struct thr_info *thr;
int gpu;
@ -1425,7 +1448,7 @@ static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1425,7 +1448,7 @@ static void gpuenable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcpy(io_buffer, message(MSG_GPUREN, id, NULL, isjson));
}
static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int id;
@ -1455,7 +1478,7 @@ static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1455,7 +1478,7 @@ static void gpudisable(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcpy(io_buffer, message(MSG_GPUDIS, id, NULL, isjson));
}
static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int id;
@ -1480,7 +1503,7 @@ static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1480,7 +1503,7 @@ static void gpurestart(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcpy(io_buffer, message(MSG_GPUREI, id, NULL, isjson));
}
#endif
static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
int numgpu = 0;
@ -1499,8 +1522,7 @@ static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo @@ -1499,8 +1522,7 @@ static void gpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
strcat(io_buffer, buf);
}
static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
int count = 0;
@ -1519,7 +1541,7 @@ static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo @@ -1519,7 +1541,7 @@ static void pgacount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
strcat(io_buffer, buf);
}
static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
int count = 0;
@ -1538,7 +1560,7 @@ static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo @@ -1538,7 +1560,7 @@ static void cpucount(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bo
strcat(io_buffer, buf);
}
static void switchpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void switchpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
struct pool *pool;
int id;
@ -1619,7 +1641,7 @@ exitsama: @@ -1619,7 +1641,7 @@ exitsama:
return false;
}
static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
char *url, *user, *pass;
char *ptr;
@ -1647,7 +1669,7 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1647,7 +1669,7 @@ static void addpool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
ptr = NULL;
}
static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
struct pool *pool;
int id;
@ -1681,7 +1703,7 @@ static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1681,7 +1703,7 @@ static void enablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcpy(io_buffer, message(MSG_ENAPOOL, id, NULL, isjson));
}
static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
struct pool *pool;
int id;
@ -1720,7 +1742,7 @@ static void disablepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1720,7 +1742,7 @@ 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)
static void removepool(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
struct pool *pool;
char *rpc_url;
@ -1806,7 +1828,7 @@ static bool splitgpuvalue(char *param, int *gpu, char **value, bool isjson) @@ -1806,7 +1828,7 @@ static bool splitgpuvalue(char *param, int *gpu, char **value, bool isjson)
return true;
}
static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
int id;
char *value;
@ -1835,7 +1857,7 @@ static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson) @@ -1835,7 +1857,7 @@ static void gpuintensity(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
strcpy(io_buffer, message(MSG_GPUINT, id, intensitystr, isjson));
}
static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
#ifdef HAVE_ADL
int id;
@ -1856,7 +1878,7 @@ static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool @@ -1856,7 +1878,7 @@ static void gpumem(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool
#endif
}
static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
#ifdef HAVE_ADL
int id;
@ -1877,7 +1899,7 @@ static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, b @@ -1877,7 +1899,7 @@ static void gpuengine(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, b
#endif
}
static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
#ifdef HAVE_ADL
int id;
@ -1898,7 +1920,7 @@ static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool @@ -1898,7 +1920,7 @@ static void gpufan(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool
#endif
}
static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
#ifdef HAVE_ADL
int id;
@ -1919,7 +1941,7 @@ static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo @@ -1919,7 +1941,7 @@ static void gpuvddc(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, boo
#endif
}
#endif
void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
if (isjson)
strcpy(io_buffer, JSON_START JSON_BYE);
@ -1930,7 +1952,7 @@ void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson @@ -1930,7 +1952,7 @@ void doquit(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson
do_a_quit = true;
}
void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
if (isjson)
strcpy(io_buffer, JSON_START JSON_RESTART);
@ -1941,12 +1963,12 @@ void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isj @@ -1941,12 +1963,12 @@ void dorestart(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isj
do_a_restart = true;
}
void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
void privileged(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
strcpy(io_buffer, message(MSG_ACCOK, 0, NULL, isjson));
}
void notifystatus(int device, struct cgpu_info *cgpu, bool isjson)
void notifystatus(int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
char *reason;
@ -2000,7 +2022,7 @@ void notifystatus(int device, struct cgpu_info *cgpu, bool isjson) @@ -2000,7 +2022,7 @@ void notifystatus(int device, struct cgpu_info *cgpu, bool isjson)
strcat(io_buffer, buf);
}
static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
int i;
@ -2017,13 +2039,13 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool @@ -2017,13 +2039,13 @@ static void notify(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool
}
for (i = 0; i < total_devices; i++)
notifystatus(i, devices[i], isjson);
notifystatus(i, devices[i], isjson, group);
if (isjson)
strcat(io_buffer, JSON_CLOSE);
}
static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char buf[TMPBUFSIZ];
struct cgpu_info *cgpu;
@ -2059,7 +2081,7 @@ static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, @@ -2059,7 +2081,7 @@ static void devdetails(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
strcat(io_buffer, JSON_CLOSE);
}
void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson)
void dosave(__maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group)
{
char filename[PATH_MAX];
FILE *fcfg;
@ -2135,7 +2157,8 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, struct cgmine @@ -2135,7 +2157,8 @@ static int itemstats(int i, char *id, struct cgminer_stats *stats, struct cgmine
return i;
}
static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson)
static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group)
{
char extra[TMPBUFSIZ];
char id[20];
@ -2174,10 +2197,12 @@ static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param, @@ -2174,10 +2197,12 @@ static void minerstats(__maybe_unused SOCKETTYPE c, __maybe_unused char *param,
strcat(io_buffer, JSON_CLOSE);
}
static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group);
struct CMDS {
char *name;
void (*func)(SOCKETTYPE, char *, bool);
bool requires_writemode;
void (*func)(SOCKETTYPE, char *, bool, char);
bool iswritemode;
} cmds[] = {
{ "version", apiversion, false },
{ "config", minerconfig, false },
@ -2220,9 +2245,47 @@ struct CMDS { @@ -2220,9 +2245,47 @@ struct CMDS {
{ "devdetails", devdetails, false },
{ "restart", dorestart, true },
{ "stats", minerstats, false },
{ "check", checkcommand, false },
{ NULL, NULL, false }
};
static void checkcommand(__maybe_unused SOCKETTYPE c, char *param, bool isjson, char group)
{
char buf[TMPBUFSIZ];
char cmdbuf[100];
bool found, access;
int i;
if (param == NULL || *param == '\0') {
strcpy(io_buffer, message(MSG_MISCHK, 0, NULL, isjson));
return;
}
found = false;
access = false;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(cmds[i].name, param) == 0) {
found = true;
sprintf(cmdbuf, "|%s|", param);
if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
access = true;
break;
}
}
strcpy(io_buffer, message(MSG_CHECK, 0, NULL, isjson));
sprintf(buf, isjson
? "," JSON_CHECK "{\"Exists\":\"%s\",\"Access\":\"%s\"}" JSON_CLOSE
: _CHECK ",Exists=%s,Access=%s" SEPSTR,
found ? YES : NO,
access ? YES : NO);
strcat(io_buffer, buf);
}
static void send_result(SOCKETTYPE c, bool isjson)
{
int n;
@ -2277,7 +2340,154 @@ static void tidyup(__maybe_unused void *arg) @@ -2277,7 +2340,154 @@ static void tidyup(__maybe_unused void *arg)
}
/*
* Interpret [R|W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option
* Interpret --api-groups G:cmd1:cmd2:cmd3,P:cmd4,*,...
*/
static void setup_groups()
{
char *buf, *ptr, *next, *colon;
char group;
char commands[TMPBUFSIZ];
char cmdbuf[100];
char *cmd;
bool addstar, did;
int i;
buf = malloc(strlen(opt_api_groups) + 1);
if (unlikely(!buf))
quit(1, "Failed to malloc ipgroups buf");
strcpy(buf, opt_api_groups);
next = buf;
// for each group defined
while (next && *next) {
ptr = next;
next = strchr(ptr, ',');
if (next)
*(next++) = '\0';
// Validate the group
if (*(ptr+1) != ':') {
colon = strchr(ptr, ':');
if (colon)
*colon = '\0';
applog(LOG_WARNING, "API invalid group name '%s'%s", ptr, GROUPDIS);
goto shin;
}
group = GROUP(*ptr);
if (!VALIDGROUP(group)) {
applog(LOG_WARNING, "API invalid group name '%c'%s", *ptr, GROUPDIS);
goto shin;
}
if (group == PRIVGROUP) {
applog(LOG_WARNING, "API group name can't be '%c'%s", PRIVGROUP, GROUPDIS);
goto shin;
}
if (group == NOPRIVGROUP) {
applog(LOG_WARNING, "API group name can't be '%c'%s", NOPRIVGROUP, GROUPDIS);
goto shin;
}
if (apigroups[GROUPOFFSET(group)].commands != NULL) {
applog(LOG_WARNING, "API duplicate group name '%c'%s", *ptr, GROUPDIS);
goto shin;
}
ptr += 2;
// Validate the command list (and handle '*')
cmd = &(commands[0]);
*(cmd++) = SEPARATOR;
*cmd = '\0';
addstar = false;
while (ptr && *ptr) {
colon = strchr(ptr, ':');
if (colon)
*(colon++) = '\0';
if (strcmp(ptr, "*") == 0)
addstar = true;
else {
did = false;
for (i = 0; cmds[i].name != NULL; i++) {
if (strcasecmp(ptr, cmds[i].name) == 0) {
did = true;
break;
}
}
if (did) {
// skip duplicates
sprintf(cmdbuf, "|%s|", cmds[i].name);
if (strstr(commands, cmdbuf) == NULL) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = SEPARATOR;
*cmd = '\0';
}
} else {
applog(LOG_WARNING, "API unknown command '%s' in group '%c'%s", ptr, group, GROUPDIS);
goto shin;
}
}
ptr = colon;
}
// * = allow all non-iswritemode commands
if (addstar) {
for (i = 0; cmds[i].name != NULL; i++) {
if (cmds[i].iswritemode == false) {
// skip duplicates
sprintf(cmdbuf, "|%s|", cmds[i].name);
if (strstr(commands, cmdbuf) == NULL) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = SEPARATOR;
*cmd = '\0';
}
}
}
}
ptr = apigroups[GROUPOFFSET(group)].commands = malloc(strlen(commands) + 1);
if (unlikely(!ptr))
quit(1, "Failed to malloc group commands buf");
strcpy(ptr, commands);
}
// Now define R (NOPRIVGROUP) as all non-iswritemode commands
cmd = &(commands[0]);
*(cmd++) = SEPARATOR;
*cmd = '\0';
for (i = 0; cmds[i].name != NULL; i++) {
if (cmds[i].iswritemode == false) {
strcpy(cmd, cmds[i].name);
cmd += strlen(cmds[i].name);
*(cmd++) = SEPARATOR;
*cmd = '\0';
}
}
ptr = apigroups[GROUPOFFSET(NOPRIVGROUP)].commands = malloc(strlen(commands) + 1);
if (unlikely(!ptr))
quit(1, "Failed to malloc noprivgroup commands buf");
strcpy(ptr, commands);
// W (PRIVGROUP) is handled as a special case since it simply means all commands
groups_enabled = true;
shin:
free(buf);
return;
}
/*
* Interpret [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"
@ -2288,7 +2498,7 @@ static void setup_ipaccess() @@ -2288,7 +2498,7 @@ static void setup_ipaccess()
{
char *buf, *ptr, *comma, *slash, *dot;
int ipcount, mask, octet, i;
bool writemode;
char group;
buf = malloc(strlen(opt_api_allow) + 1);
if (unlikely(!buf))
@ -2322,16 +2532,16 @@ static void setup_ipaccess() @@ -2322,16 +2532,16 @@ static void setup_ipaccess()
if (comma)
*(comma++) = '\0';
writemode = false;
group = NOPRIVGROUP;
if (isalpha(*ptr) && *(ptr+1) == ':') {
if (tolower(*ptr) == 'w')
writemode = true;
if (DEFINEDGROUP(*ptr))
group = GROUP(*ptr);
ptr += 2;
}
ipaccess[ips].writemode = writemode;
ipaccess[ips].group = group;
if (strcmp(ptr, ALLIP4) == 0)
ipaccess[ips].ip = ipaccess[ips].mask = 0;
@ -2421,10 +2631,11 @@ void api(int api_thr_id) @@ -2421,10 +2631,11 @@ void api(int api_thr_id)
struct sockaddr_in serv;
struct sockaddr_in cli;
socklen_t clisiz;
char cmdbuf[100];
char *cmd;
char *param;
bool addrok;
bool writemode;
char group;
json_error_t json_err;
json_t *json_config;
json_t *json_val;
@ -2445,6 +2656,9 @@ void api(int api_thr_id) @@ -2445,6 +2656,9 @@ void api(int api_thr_id)
return;
}
if (opt_api_groups)
setup_groups();
if (opt_api_allow) {
setup_ipaccess();
@ -2523,15 +2737,16 @@ void api(int api_thr_id) @@ -2523,15 +2737,16 @@ void api(int api_thr_id)
connectaddr = inet_ntoa(cli.sin_addr);
addrok = false;
writemode = false;
group = NOPRIVGROUP;
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;
if (groups_enabled)
for (i = 0; i < ips; i++) {
if ((cli.sin_addr.s_addr & ipaccess[i].mask) == ipaccess[i].ip) {
addrok = true;
group = ipaccess[i].group;
break;
}
}
}
} else {
if (opt_api_network)
addrok = true;
@ -2622,12 +2837,13 @@ void api(int api_thr_id) @@ -2622,12 +2837,13 @@ void api(int api_thr_id)
if (!did)
for (i = 0; cmds[i].name != NULL; i++) {
if (strcmp(cmd, cmds[i].name) == 0) {
if (cmds[i].requires_writemode && !writemode) {
sprintf(cmdbuf, "|%s|", cmd);
if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf))
(cmds[i].func)(c, param, isjson, group);
else {
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;

15
cgminer.c

@ -131,6 +131,7 @@ bool opt_autofan; @@ -131,6 +131,7 @@ bool opt_autofan;
bool opt_autoengine;
bool opt_noadl;
char *opt_api_allow = NULL;
char *opt_api_groups;
char *opt_api_description = PACKAGE_STRING;
int opt_api_port = 4028;
bool opt_api_listen;
@ -680,6 +681,13 @@ static char *set_api_allow(const char *arg) @@ -680,6 +681,13 @@ static char *set_api_allow(const char *arg)
return NULL;
}
static char *set_api_groups(const char *arg)
{
opt_set_charp(arg, &opt_api_groups);
return NULL;
}
static char *set_api_description(const char *arg)
{
opt_set_charp(arg, &opt_api_description);
@ -730,10 +738,13 @@ static struct opt_table opt_config_table[] = { @@ -730,10 +738,13 @@ static struct opt_table opt_config_table[] = {
#endif
OPT_WITH_ARG("--api-allow",
set_api_allow, NULL, NULL,
"Allow API access only to the given list of IP[/Prefix] addresses[/subnets]"),
"Allow API access only to the given list of [G:]IP[/Prefix] addresses[/subnets]"),
OPT_WITH_ARG("--api-description",
set_api_description, NULL, NULL,
"Description placed in the API status header, default: cgminer version"),
OPT_WITH_ARG("--api-groups",
set_api_groups, NULL, NULL,
"API one letter groups G:cmd:cmd[,P:cmd:*...] defining the cmds a groups can use"),
OPT_WITHOUT_ARG("--api-listen",
opt_set_bool, &opt_api_listen,
"Enable API, default: disabled"),
@ -2872,6 +2883,8 @@ void write_config(FILE *fcfg) @@ -2872,6 +2883,8 @@ void write_config(FILE *fcfg)
fprintf(fcfg, ",\n\"api-allow\" : \"%s\"", opt_api_allow);
if (strcmp(opt_api_description, PACKAGE_STRING) != 0)
fprintf(fcfg, ",\n\"api-description\" : \"%s\"", opt_api_description);
if (opt_api_groups)
fprintf(fcfg, ",\n\"api-groups\" : \"%s\"", opt_api_groups);
if (opt_icarus_timing)
fprintf(fcfg, ",\n\"icarus-timing\" : \"%s\"", opt_icarus_timing);
fputs("\n}", fcfg);

1
miner.h

@ -524,6 +524,7 @@ extern bool opt_autofan; @@ -524,6 +524,7 @@ extern bool opt_autofan;
extern bool opt_autoengine;
extern bool use_curses;
extern char *opt_api_allow;
extern char *opt_api_groups;
extern char *opt_api_description;
extern int opt_api_port;
extern bool opt_api_listen;

Loading…
Cancel
Save