diff --git a/api-example.php b/api-example.php new file mode 100644 index 00000000..12486c0e --- /dev/null +++ b/api-example.php @@ -0,0 +1,117 @@ + 0) + { + $items = explode(',', $obj); + $item = $items[0]; + $id = explode('=', $items[0], 2); + if (count($id) == 1) + $name = $id[0]; + else + $name = $id[0].$id[1]; + + if (strlen($name) == 0) + $name = 'null'; + + if (isset($data[$name])) + { + $num = 1; + while (isset($data[$name.$num])) + $num++; + $name .= $num; + } + + $counter = 0; + foreach ($items as $item) + { + $id = explode('=', $item, 2); + if (count($id) == 2) + $data[$name][$id[0]] = $id[1]; + else + $data[$name][$counter] = $id[0]; + + $counter++; + } + } + } + + return $data; + } + + return null; +} +# +$ver = request('apiversion'); +echo print_r($ver, true)."\n"; +# +$dev = request('dev'); +echo print_r($dev, true)."\n"; +# +$pool = request('pool'); +echo print_r($pool, true)."\n"; +# +?> diff --git a/api.c b/api.c new file mode 100644 index 00000000..1f7dd810 --- /dev/null +++ b/api.c @@ -0,0 +1,296 @@ +/* + * Copyright 2011 Kano + * Copyright 2011 Con Kolivas + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. See COPYING for more details. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "compat.h" +#include "miner.h" + +#if defined(unix) + #include +// #include +// #include +#endif + +// Big enough for largest API request +// though a PC with 100s of CPUs may exceed the size ... +// Current code assumes it can socket send this size also +#define MYBUFSIZ 16384 + +static char *buffer = NULL; + +static const char *UNAVAILABLE = " - API will not be available"; + +static const char *BLANK = ""; + +static const char *VERSION = "0.1"; +static const char *DEAD = "DEAD"; +static const char *SICK = "SICK"; +static const char *DISABLED = "DISABLED"; +static const char * = ""; +zzz -> other pool status + +static int bye = 0; + +char *apiversion(char *params) +{ + return VERSION; +} + +void gpustatus(int thr_id) +{ + char buf[BUFSIZ]; + char status_buf[BUFSIZ]; + char *status; + float gt; + int gf, gp; + + if (thr_id >= 0 && thr_id < gpu_threads) { + int gpu = dev_from_id(thr_id); + struct cgpu_info *cgpu = &gpus[gpu]; + + cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + +#ifdef HAVE_ADL + if (cgpu->has_adl) { + gt = gpu_temp(gpu); + gf = gpu_fanspeed(gpu); + gp = gpu_fanpercent(gpu); + } + else +#endif + gt = gf = gp = 0; + + if (cgpu->status == LIFE_DEAD) + status = DEAD; + else if (cgpu->status == LIFE_SICK) + status = SICK; + else if (!gpu_devices[gpu]) + status = DISABLED; + else { + sprintf(status_buf, "%.1f", cgpu->rolling); + status = status_buf; + } + + sprintf(buf, "G%d=%.2f,%d,%d,%s,%.2f,%d,%d,%d,%.2f,%d|", + gpu, gt, gf, gp, status, + cgpu->total_mhashes / total_secs, + cgpu->accepted, cgpu->rejected, cgpu->hw_errors, + cgpu->utility, gpus[gpu].intensity); + + strcat(buffer, buf); + } +} + +void cpustatus(int thr_id) +{ + char buf[BUFSIZ]; + + if (thr_id >= gpu_threads) { + int cpu = dev_from_id(thr_id); + struct cgpu_info *cgpu = &cpus[cpu]; + + cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60; + + sprintf(buf, "C%d=%.2f,%.2f,%d,%d,%d,%.2f,%d|", + cpu, cgpu->rolling, + cgpu->total_mhashes / total_secs, + cgpu->accepted, cgpu->rejected, cgpu->hw_errors, + cgpu->utility, gpus[gpu].intensity); + + strcat(buffer, buf); + } +} + +char *devstatus(char *params) +{ + int i; + + *buffer = '\0'; + + for (i = 0; i < gpu_threads; i++) + gpustatus(i); + + for (i = gpu_threads; i < mining_threads; i++) + cpustatus(i); + + return buffer; +} + +char *poolstatus(char *params) +{ + char buf[BUFSIZ]; + char *status; + int i; + + *buffer = '\0'; + + for (i = 0; i < total_pools; i++) { + struct pool *pool = pools[i]; + + if (!pool->enabled) + status = DISABLED; + else + status = OK; + + sprintf(buf, "P%d=%s,%s,%d,%d,%d,%d,%d,%d,%d|", + i, pool->rpc_url, status, + pool->getwork_requested, + pool->accepted, pool->rejected, + pool->discarded_work, + pool->stale_shares, + pool->getfail_occasions, + pool->remotefail_occasions); + + strcat(buffer, buf); + } + + return buffer; +} + +struct CMDS { + const char *cmd; + char (*func)(); +} cmds[] = { + { "apiversion", apiversion }, + { "dev", devstatus }, + { "pool", poolstatus } +}; + +#define MAXCMD sizeof(cmds)/sizeof(struct CMDS) + +void send_result(int c, char *result) +{ + int n; + + if (result == NULL) + result = BLANK; + + // ignore failure - it's closed immediately anyway + n = write(c, result, strlen(result)+1); +} + +static void api() +{ + const char *addr; + int c, sock, n, bound; + char *binderror; + time_t bindstart; + short int port = 4028; + struct sockaddr_in serv; + struct sockaddr_in cli; + socklen_t clisiz; + long long counter; + char *result; + char *params; + + addr = "127.0.0.1"; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + applog(LOG_ERR, "API1 initialisation failed (%s)%s", strerror(errno), UNAVAILABLE); + return NULL; + } + + memset(&serv, 0, sizeof(serv)); + + serv.sin_family = AF_INET; + if (inet_pton(AF_INET, addr, &(serv.sin_addr)) == 0) { + applog(LOG_ERR, "API2 initialisation failed (%s)%s", strerror(errno), UNAVAILABLE); + return NULL; + } + serv.sin_port = htons(port); + + // try for 1 minute ... in case the old one hasn't completely gone yet + bound = 0; + bindstart = time(NULL); + while (bound == 0) { + if (bind(sock, (struct sockaddr *)(&serv), sizeof(serv)) < 0) { + binderror = strerror(errno); + if ((time(NULL) - bindstart) > 61) + break; + else { + applog(LOG_ERR, "API bind to port %d failed - trying again in 10sec", port); + sleep(10); + } + } + else + bound = 1; + } + + if (bound == 0) { + applog(LOG_ERR, "API bind to port %d failed (%s)%s", port, binderror, UNAVAILABLE); + return NULL; + } + + if (listen(sock, QUEUE) < 0) { + applog(LOG_ERR, "API3 initialisation failed (%s)%s", strerror(errno), UNAVAILABLE); + close(sock); + return NULL; + } + + buffer = malloc(MYBUFSIZ+1); + + counter = 0; + while (bye == 0) { + counter++; + + clisiz = sizeof(cli); + if ((c = accept(sock, (struct sockaddr *)(&cli), &clisiz)) < 0) { + applog(LOG_ERR, "API failed (%s)%s", strerror(errno), UNAVAILABLE); + close(sock); + free(buffer); + return NULL; + } + + inet_ntop(AF_INET, &(cli.sin_addr), &(tmpaddr[0]), sizeof(tmpaddr)-1); + if (strcmp(tmpaddr, addr) != 0) + close(c); + else { + n = read(c, &buf[0], BUFS); + if (n < 0) + close(c); + else { + params = strchr(buf, '|'); + if (params != NULL) + *(params++) = '\0'; + + for (i = 0; i < CMDMAX; i++) { + if (strcmp(buf, cmds[i].name) == 0) { + result = cmds[i].func(params); + send_result(c, result); + close(c); + break; + } + } + } + } + } + + close(sock); + free(buffer); + + return NULL; +}