api.c 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. /*
  2. * Copyright 2011 Kano
  3. * Copyright 2011 Con Kolivas
  4. *
  5. * This program is free software; you can redistribute it and/or modify it
  6. * under the terms of the GNU General Public License as published by the Free
  7. * Software Foundation; either version 2 of the License, or (at your option)
  8. * any later version. See COPYING for more details.
  9. */
  10. #include "config.h"
  11. #include <stdio.h>
  12. #include <stdlib.h>
  13. #include <string.h>
  14. #include <stdbool.h>
  15. #include <stdint.h>
  16. #include <unistd.h>
  17. #include <sys/types.h>
  18. #include "compat.h"
  19. #include "miner.h"
  20. #if defined(unix)
  21. #include <errno.h>
  22. #endif
  23. #include <sys/socket.h>
  24. #include <netinet/in.h>
  25. #include <arpa/inet.h>
  26. // Big enough for largest API request
  27. // though a PC with 100s of CPUs may exceed the size ...
  28. // Current code assumes it can socket send this size also
  29. #define MYBUFSIZ 16384
  30. // Number of requests to queue - normally would be small
  31. #define QUEUE 10
  32. static char *io_buffer = NULL;
  33. static char *msg_buffer = NULL;
  34. static const char *UNAVAILABLE = " - API will not be available";
  35. static const char *BLANK = "";
  36. static const char SEPARATOR = '|';
  37. static const char *SEPARATORSTR = "|";
  38. #define MSG_INVGPU 1
  39. #define MSG_ALRENA 2
  40. #define MSG_ALRDIS 3
  41. #define MSG_GPUMRE 4
  42. #define MSG_GPUREN 5
  43. #define MSG_GPUNON 6
  44. #define MSG_POOL 7
  45. #define MSG_NOPOOL 8
  46. #define MSG_DEVS 9
  47. #define MSG_NODEVS 10
  48. #define MSG_SUMM 11
  49. #define MSG_GPUDIS 12
  50. #define MSG_GPUREI 13
  51. #define MSG_INVCMD 14
  52. #define MSG_MISID 15
  53. #define MSG_CPUNON 16
  54. #define MSG_GPUDEV 17
  55. #define MSG_CPUDEV 18
  56. #define MSG_INVCPU 19
  57. enum code_severity {
  58. SEVERITY_ERR,
  59. SEVERITY_WARN,
  60. SEVERITY_INFO,
  61. SEVERITY_SUCC,
  62. SEVERITY_FAIL
  63. };
  64. enum code_parameters {
  65. PARAM_GPU,
  66. PARAM_CPU,
  67. PARAM_GPUMAX,
  68. PARAM_CPUMAX,
  69. PARAM_PMAX,
  70. PARAM_GCMAX,
  71. PARAM_NONE
  72. };
  73. struct CODES {
  74. const enum code_severity severity;
  75. const int code;
  76. const enum code_parameters params;
  77. const char *description;
  78. } codes[] = {
  79. { SEVERITY_ERR, MSG_INVGPU, PARAM_GPUMAX, "Invalid GPU id %d - range is 0 - %d" },
  80. { SEVERITY_INFO, MSG_ALRENA, PARAM_GPU, "GPU %d already enabled" },
  81. { SEVERITY_INFO, MSG_ALRDIS, PARAM_GPU, "GPU %d already disabled" },
  82. { SEVERITY_WARN, MSG_GPUMRE, PARAM_GPU, "GPU %d must be restarted first" },
  83. { SEVERITY_INFO, MSG_GPUREN, PARAM_GPU, "GPU %d sent enable message" },
  84. { SEVERITY_ERR, MSG_GPUNON, PARAM_NONE, "No GPUs" },
  85. { SEVERITY_SUCC, MSG_POOL, PARAM_PMAX, "%d Pool(s)" },
  86. { SEVERITY_ERR, MSG_NOPOOL, PARAM_NONE, "No pools" },
  87. { SEVERITY_SUCC, MSG_DEVS, PARAM_GCMAX, "%d GPU(s) - %d CPU(s)" },
  88. { SEVERITY_ERR, MSG_NODEVS, PARAM_NONE, "No GPUs/CPUs" },
  89. { SEVERITY_SUCC, MSG_SUMM, PARAM_NONE, "Summary" },
  90. { SEVERITY_INFO, MSG_GPUDIS, PARAM_GPU, "GPU %d set disable flag" },
  91. { SEVERITY_INFO, MSG_GPUREI, PARAM_GPU, "GPU %d restart attempted" },
  92. { SEVERITY_ERR, MSG_INVCMD, PARAM_NONE, "Invalid command" },
  93. { SEVERITY_ERR, MSG_MISID, PARAM_NONE, "Missing device id parameter" },
  94. { SEVERITY_ERR, MSG_CPUNON, PARAM_NONE, "No CPUs" },
  95. { SEVERITY_SUCC, MSG_GPUDEV, PARAM_GPU, "GPU%d" },
  96. { SEVERITY_SUCC, MSG_CPUDEV, PARAM_CPU, "CPU%d" },
  97. { SEVERITY_ERR, MSG_INVCPU, PARAM_CPUMAX, "Invalid CPU id %d - range is 0 - %d" },
  98. { SEVERITY_FAIL }
  99. };
  100. static const char *APIVERSION = "0.2";
  101. static const char *DEAD = "DEAD";
  102. static const char *SICK = "SICK";
  103. static const char *NOSTART = "NOSTART";
  104. static const char *DISABLED = "DISABLED";
  105. static const char *ALIVE = "ALIVE";
  106. static const char *DYNAMIC = "D";
  107. static const char *YES = "Y";
  108. static const char *NO = "N";
  109. static int bye = 0;
  110. static bool ping = true;
  111. static char *message(int messageid, int gpuid)
  112. {
  113. char severity;
  114. char *ptr;
  115. int cpu;
  116. int i;
  117. for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) {
  118. if (codes[i].code == messageid) {
  119. switch (codes[i].severity) {
  120. case SEVERITY_WARN:
  121. severity = 'W';
  122. break;
  123. case SEVERITY_INFO:
  124. severity = 'I';
  125. break;
  126. case SEVERITY_SUCC:
  127. severity = 'S';
  128. break;
  129. case SEVERITY_ERR:
  130. default:
  131. severity = 'E';
  132. break;
  133. }
  134. sprintf(msg_buffer, "STATUS=%c,CODE=%d,MSG=", severity, messageid);
  135. ptr = msg_buffer + strlen(msg_buffer);
  136. switch(codes[i].params) {
  137. case PARAM_GPU:
  138. sprintf(ptr, codes[i].description, gpuid);
  139. break;
  140. case PARAM_CPU:
  141. sprintf(ptr, codes[i].description, gpuid);
  142. break;
  143. case PARAM_GPUMAX:
  144. sprintf(ptr, codes[i].description, gpuid, nDevs - 1);
  145. break;
  146. case PARAM_PMAX:
  147. sprintf(ptr, codes[i].description, total_pools);
  148. break;
  149. case PARAM_GCMAX:
  150. if (opt_n_threads > 0)
  151. cpu = num_processors;
  152. else
  153. cpu = 0;
  154. sprintf(ptr, codes[i].description, nDevs, cpu);
  155. break;
  156. case PARAM_NONE:
  157. default:
  158. strcpy(ptr, codes[i].description);
  159. }
  160. strcat(msg_buffer, SEPARATORSTR);
  161. return msg_buffer;
  162. }
  163. }
  164. sprintf(msg_buffer, "STATUS=F,CODE=-1,MSG=%d%c", messageid, SEPARATOR);
  165. return msg_buffer;
  166. }
  167. void apiversion(char *params)
  168. {
  169. strcpy(io_buffer, APIVERSION);
  170. }
  171. void gpustatus(int gpu)
  172. {
  173. char intensity[20];
  174. char buf[BUFSIZ];
  175. char *enabled;
  176. char *status;
  177. float gt;
  178. int gf, gp;
  179. int i;
  180. if (gpu >= 0 && gpu < nDevs) {
  181. struct cgpu_info *cgpu = &gpus[gpu];
  182. cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60;
  183. #ifdef HAVE_ADL
  184. if (cgpu->has_adl) {
  185. gt = gpu_temp(gpu);
  186. gf = gpu_fanspeed(gpu);
  187. gp = gpu_fanpercent(gpu);
  188. }
  189. else
  190. #endif
  191. gt = gf = gp = 0;
  192. if (gpu_devices[gpu])
  193. enabled = (char *)YES;
  194. else
  195. enabled = (char *)NO;
  196. if (cgpu->status == LIFE_DEAD)
  197. status = (char *)DEAD;
  198. else if (cgpu->status == LIFE_SICK)
  199. status = (char *)SICK;
  200. else if (cgpu->status == LIFE_NOSTART)
  201. status = (char *)NOSTART;
  202. else
  203. status = (char *)ALIVE;
  204. if (cgpu->dynamic)
  205. strcpy(intensity, DYNAMIC);
  206. else
  207. sprintf(intensity, "%d", gpus->intensity);
  208. sprintf(buf, "GPU=%d,GT=%.2f,FR=%d,FP=%d,EN=%s,STA=%s,MHS=%.2f,A=%d,R=%d,HW=%d,U=%.2f,I=%s%c",
  209. gpu, gt, gf, gp, enabled, status,
  210. cgpu->total_mhashes / total_secs,
  211. cgpu->accepted, cgpu->rejected, cgpu->hw_errors,
  212. cgpu->utility, intensity, SEPARATOR);
  213. strcat(io_buffer, buf);
  214. }
  215. }
  216. void cpustatus(int cpu)
  217. {
  218. char buf[BUFSIZ];
  219. if (opt_n_threads > 0 && cpu >= 0 && cpu < num_processors) {
  220. struct cgpu_info *cgpu = &cpus[cpu];
  221. cgpu->utility = cgpu->accepted / ( total_secs ? total_secs : 1 ) * 60;
  222. sprintf(buf, "CPU=%d,STA=%.2f,MHS=%.2f,A=%d,R=%d,U=%.2f%c",
  223. cpu, cgpu->rolling,
  224. cgpu->total_mhashes / total_secs,
  225. cgpu->accepted, cgpu->rejected,
  226. cgpu->utility, SEPARATOR);
  227. strcat(io_buffer, buf);
  228. }
  229. }
  230. void devstatus(char *params)
  231. {
  232. int i;
  233. if (nDevs == 0 && num_processors == 0) {
  234. strcpy(io_buffer, message(MSG_NODEVS, 0));
  235. return;
  236. }
  237. strcpy(io_buffer, message(MSG_DEVS, 0));
  238. for (i = 0; i < nDevs; i++)
  239. gpustatus(i);
  240. if (opt_n_threads > 0)
  241. for (i = 0; i < num_processors; i++)
  242. cpustatus(i);
  243. }
  244. void gpudev(char *params)
  245. {
  246. int id;
  247. if (nDevs == 0) {
  248. strcpy(io_buffer, message(MSG_GPUNON, 0));
  249. return;
  250. }
  251. if (*params == '\0') {
  252. strcpy(io_buffer, message(MSG_MISID, 0));
  253. return;
  254. }
  255. id = atoi(params);
  256. if (id < 0 || id >= nDevs) {
  257. strcpy(io_buffer, message(MSG_INVGPU, id));
  258. return;
  259. }
  260. strcpy(io_buffer, message(MSG_GPUDEV, id));
  261. gpustatus(id);
  262. }
  263. void cpudev(char *params)
  264. {
  265. int id;
  266. if (opt_n_threads == 0) {
  267. strcpy(io_buffer, message(MSG_CPUNON, 0));
  268. return;
  269. }
  270. if (*params == '\0') {
  271. strcpy(io_buffer, message(MSG_MISID, 0));
  272. return;
  273. }
  274. id = atoi(params);
  275. if (id < 0 || id >= num_processors) {
  276. strcpy(io_buffer, message(MSG_INVCPU, id));
  277. return;
  278. }
  279. strcpy(io_buffer, message(MSG_CPUDEV, id));
  280. cpustatus(id);
  281. }
  282. void poolstatus(char *params)
  283. {
  284. char buf[BUFSIZ];
  285. char *status, *lp;
  286. int i;
  287. if (total_pools == 0) {
  288. strcpy(io_buffer, message(MSG_NOPOOL, 0));
  289. return;
  290. }
  291. strcpy(io_buffer, message(MSG_POOL, 0));
  292. for (i = 0; i < total_pools; i++) {
  293. struct pool *pool = pools[i];
  294. if (!pool->enabled)
  295. status = (char *)DISABLED;
  296. else
  297. {
  298. if (pool->idle)
  299. status = (char *)DEAD;
  300. else
  301. status = (char *)ALIVE;
  302. }
  303. if (pool->hdr_path)
  304. lp = (char *)YES;
  305. else
  306. lp = (char *)NO;
  307. sprintf(buf, "POOL=%d,URL=%s,STA=%s,PRI=%d,LP=%s,Q=%d,A=%d,R=%d,DW=%d,ST=%d,GF=%d,RF=%d%c",
  308. i, pool->rpc_url, status, pool->prio, lp,
  309. pool->getwork_requested,
  310. pool->accepted, pool->rejected,
  311. pool->discarded_work,
  312. pool->stale_shares,
  313. pool->getfail_occasions,
  314. pool->remotefail_occasions, SEPARATOR);
  315. strcat(io_buffer, buf);
  316. }
  317. }
  318. void summary(char *params)
  319. {
  320. double utility, mhs;
  321. utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60;
  322. mhs = total_mhashes_done / total_secs;
  323. sprintf(io_buffer, "%sSUMMARY=all,EL=%.0lf,ALGO=%s,MHS=%.2lf,SOL=%d,Q=%d,A=%d,R=%d,HW=%d,U=%.2lf,DW=%d,ST=%d,GF=%d,LW=%d,RO=%d,BC=%d%c",
  324. message(MSG_SUMM, 0),
  325. total_secs, algo_names[opt_algo], mhs, found_blocks,
  326. total_getworks, total_accepted, total_rejected,
  327. hw_errors, utility, total_discarded, total_stale,
  328. total_go, local_work, total_ro, new_blocks, SEPARATOR);
  329. }
  330. void gpuenable(char *params)
  331. {
  332. struct thr_info *thr;
  333. int gpu;
  334. int id;
  335. int i;
  336. if (gpu_threads == 0) {
  337. strcpy(io_buffer, message(MSG_GPUNON, 0));
  338. return;
  339. }
  340. if (*params == '\0') {
  341. strcpy(io_buffer, message(MSG_MISID, 0));
  342. return;
  343. }
  344. id = atoi(params);
  345. if (id < 0 || id >= nDevs) {
  346. strcpy(io_buffer, message(MSG_INVGPU, id));
  347. return;
  348. }
  349. if (gpu_devices[id]) {
  350. strcpy(io_buffer, message(MSG_ALRENA, id));
  351. return;
  352. }
  353. for (i = 0; i < gpu_threads; i++) {
  354. gpu = thr_info[i].cgpu->cpu_gpu;
  355. if (gpu == id) {
  356. thr = &thr_info[i];
  357. if (thr->cgpu->status != LIFE_WELL) {
  358. strcpy(io_buffer, message(MSG_GPUMRE, id));
  359. return;
  360. }
  361. gpu_devices[id] = true;
  362. tq_push(thr->q, &ping);
  363. }
  364. }
  365. strcpy(io_buffer, message(MSG_GPUREN, id));
  366. }
  367. void gpudisable(char *params)
  368. {
  369. int id;
  370. if (nDevs == 0) {
  371. strcpy(io_buffer, message(MSG_GPUNON, 0));
  372. return;
  373. }
  374. if (*params == '\0') {
  375. strcpy(io_buffer, message(MSG_MISID, 0));
  376. return;
  377. }
  378. id = atoi(params);
  379. if (id < 0 || id >= nDevs) {
  380. strcpy(io_buffer, message(MSG_INVGPU, id));
  381. return;
  382. }
  383. if (!gpu_devices[id]) {
  384. strcpy(io_buffer, message(MSG_ALRDIS, id));
  385. return;
  386. }
  387. gpu_devices[id] = false;
  388. strcpy(io_buffer, message(MSG_GPUDIS, id));
  389. }
  390. void gpurestart(char *params)
  391. {
  392. int id;
  393. if (nDevs == 0) {
  394. strcpy(io_buffer, message(MSG_GPUNON, 0));
  395. return;
  396. }
  397. if (*params == '\0') {
  398. strcpy(io_buffer, message(MSG_MISID, 0));
  399. return;
  400. }
  401. id = atoi(params);
  402. if (id < 0 || id >= nDevs) {
  403. strcpy(io_buffer, message(MSG_INVGPU, id));
  404. return;
  405. }
  406. reinit_device(&gpus[id]);
  407. strcpy(io_buffer, message(MSG_GPUREI, id));
  408. }
  409. void doquit(char *params)
  410. {
  411. *io_buffer = '\0';
  412. bye = 1;
  413. kill_work();
  414. }
  415. struct CMDS {
  416. char *name;
  417. void (*func)(char *);
  418. } cmds[] = {
  419. { "apiversion", apiversion },
  420. { "devs", devstatus },
  421. { "pools", poolstatus },
  422. { "summary", summary },
  423. { "gpuenable", gpuenable },
  424. { "gpudisable", gpudisable },
  425. { "gpurestart", gpurestart },
  426. { "gpu", gpudev },
  427. { "cpu", cpudev },
  428. { "quit", doquit },
  429. { NULL }
  430. };
  431. void send_result(int c)
  432. {
  433. int n;
  434. // ignore failure - it's closed immediately anyway
  435. n = write(c, io_buffer, strlen(io_buffer)+1);
  436. }
  437. void api(void)
  438. {
  439. char buf[BUFSIZ];
  440. const char *localaddr = "127.0.0.1";
  441. int c, sock, n, bound;
  442. char connectaddr[32];
  443. char *binderror;
  444. time_t bindstart;
  445. short int port = opt_api_port;
  446. struct sockaddr_in serv;
  447. struct sockaddr_in cli;
  448. socklen_t clisiz;
  449. char *params;
  450. bool portok;
  451. bool did;
  452. int i;
  453. sock = socket(AF_INET, SOCK_STREAM, 0);
  454. if (sock < 0) {
  455. applog(LOG_ERR, "API1 initialisation failed (%s)%s", strerror(errno), UNAVAILABLE);
  456. return;
  457. }
  458. memset(&serv, 0, sizeof(serv));
  459. serv.sin_family = AF_INET;
  460. if (!opt_api_listen) {
  461. if (inet_pton(AF_INET, localaddr, &(serv.sin_addr)) == 0) {
  462. applog(LOG_ERR, "API2 initialisation failed (%s)%s", strerror(errno), UNAVAILABLE);
  463. return;
  464. }
  465. }
  466. serv.sin_port = htons(port);
  467. // try for 1 minute ... in case the old one hasn't completely gone yet
  468. bound = 0;
  469. bindstart = time(NULL);
  470. while (bound == 0) {
  471. if (bind(sock, (struct sockaddr *)(&serv), sizeof(serv)) < 0) {
  472. binderror = strerror(errno);
  473. if ((time(NULL) - bindstart) > 61)
  474. break;
  475. else {
  476. applog(LOG_WARNING, "API bind to port %d failed - trying again in 15sec", port);
  477. sleep(15);
  478. }
  479. }
  480. else
  481. bound = 1;
  482. }
  483. if (bound == 0) {
  484. applog(LOG_ERR, "API bind to port %d failed (%s)%s", port, binderror, UNAVAILABLE);
  485. return;
  486. }
  487. if (listen(sock, QUEUE) < 0) {
  488. applog(LOG_ERR, "API3 initialisation failed (%s)%s", strerror(errno), UNAVAILABLE);
  489. close(sock);
  490. return;
  491. }
  492. sleep(opt_log_interval);
  493. if (opt_api_listen)
  494. applog(LOG_WARNING, "API running in UNRESTRICTED access mode");
  495. else
  496. applog(LOG_WARNING, "API running in restricted access mode");
  497. io_buffer = malloc(MYBUFSIZ+1);
  498. msg_buffer = malloc(MYBUFSIZ+1);
  499. while (bye == 0) {
  500. clisiz = sizeof(cli);
  501. if ((c = accept(sock, (struct sockaddr *)(&cli), &clisiz)) < 0) {
  502. applog(LOG_ERR, "API failed (%s)%s", strerror(errno), UNAVAILABLE);
  503. goto die;
  504. }
  505. if (opt_api_listen)
  506. portok = true;
  507. else {
  508. inet_ntop(AF_INET, &(cli.sin_addr), &(connectaddr[0]), sizeof(connectaddr)-1);
  509. portok = (strcmp(connectaddr, localaddr) == 0);
  510. }
  511. if (portok) {
  512. n = read(c, &buf[0], BUFSIZ-1);
  513. if (n >= 0) {
  514. did = false;
  515. buf[n] = '\0';
  516. params = strchr(buf, SEPARATOR);
  517. if (params == NULL)
  518. params = (char *)BLANK;
  519. else
  520. *(params++) = '\0';
  521. for (i = 0; cmds[i].name != NULL; i++) {
  522. if (strcmp(buf, cmds[i].name) == 0) {
  523. (cmds[i].func)(params);
  524. send_result(c);
  525. did = true;
  526. break;
  527. }
  528. }
  529. if (!did) {
  530. strcpy(io_buffer, message(MSG_INVCMD, 0));
  531. send_result(c);
  532. }
  533. }
  534. }
  535. close(c);
  536. }
  537. die:
  538. free(msg_buffer);
  539. free(io_buffer);
  540. close(sock);
  541. }