/ api.c
api.c
1 /* 2 * Copyright 2011-2015 Andrew Smith 3 * Copyright 2011-2015,2018 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 3 of the License, or (at your option) 8 * any later version. See COPYING for more details. 9 */ 10 #define _MEMORY_DEBUG_MASTER 1 11 12 #include "config.h" 13 14 #include <stdio.h> 15 #include <ctype.h> 16 #include <stdlib.h> 17 #include <string.h> 18 #include <stdbool.h> 19 #include <stdint.h> 20 #include <unistd.h> 21 #include <limits.h> 22 #include <sys/types.h> 23 24 #include "compat.h" 25 #include "miner.h" 26 #include "util.h" 27 #include "klist.h" 28 29 #if defined(USE_BFLSC) || defined(USE_AVALON) || defined(USE_AVALON2) || defined(USE_AVALON4) || \ 30 defined(USE_HASHFAST) || defined(USE_BITFURY) || defined(USE_BITFURY16) || defined(USE_BLOCKERUPTER) || defined(USE_KLONDIKE) || \ 31 defined(USE_KNC) || defined(USE_BAB) || defined(USE_DRAGONMINT_T1) || defined(USE_DRILLBIT) || \ 32 defined(USE_MINION) || defined(USE_COINTERRA) || defined(USE_BITMINE_A1) || \ 33 defined(USE_ANT_S1) || defined(USE_ANT_S2) || defined(USE_ANT_S3) || defined(USE_SP10) || \ 34 defined(USE_SP30) || defined(USE_ICARUS) || defined(USE_HASHRATIO) || defined(USE_AVALON_MINER) || \ 35 defined(USE_AVALON7) || defined(USE_AVALON8) || defined(USE_BITMAIN_SOC) 36 #define HAVE_AN_ASIC 1 37 #endif 38 39 #if defined(USE_BITFORCE) || defined(USE_MODMINER) 40 #define HAVE_AN_FPGA 1 41 #endif 42 43 // BUFSIZ varies on Windows and Linux 44 #define TMPBUFSIZ 8192 45 46 // Number of requests to queue - normally would be small 47 // However lots of PGA's may mean more 48 #define QUEUE 100 49 50 #if defined WIN32 51 static char WSAbuf[1024]; 52 53 struct WSAERRORS { 54 int id; 55 char *code; 56 } WSAErrors[] = { 57 { 0, "No error" }, 58 { WSAEINTR, "Interrupted system call" }, 59 { WSAEBADF, "Bad file number" }, 60 { WSAEACCES, "Permission denied" }, 61 { WSAEFAULT, "Bad address" }, 62 { WSAEINVAL, "Invalid argument" }, 63 { WSAEMFILE, "Too many open sockets" }, 64 { WSAEWOULDBLOCK, "Operation would block" }, 65 { WSAEINPROGRESS, "Operation now in progress" }, 66 { WSAEALREADY, "Operation already in progress" }, 67 { WSAENOTSOCK, "Socket operation on non-socket" }, 68 { WSAEDESTADDRREQ, "Destination address required" }, 69 { WSAEMSGSIZE, "Message too long" }, 70 { WSAEPROTOTYPE, "Protocol wrong type for socket" }, 71 { WSAENOPROTOOPT, "Bad protocol option" }, 72 { WSAEPROTONOSUPPORT, "Protocol not supported" }, 73 { WSAESOCKTNOSUPPORT, "Socket type not supported" }, 74 { WSAEOPNOTSUPP, "Operation not supported on socket" }, 75 { WSAEPFNOSUPPORT, "Protocol family not supported" }, 76 { WSAEAFNOSUPPORT, "Address family not supported" }, 77 { WSAEADDRINUSE, "Address already in use" }, 78 { WSAEADDRNOTAVAIL, "Can't assign requested address" }, 79 { WSAENETDOWN, "Network is down" }, 80 { WSAENETUNREACH, "Network is unreachable" }, 81 { WSAENETRESET, "Net connection reset" }, 82 { WSAECONNABORTED, "Software caused connection abort" }, 83 { WSAECONNRESET, "Connection reset by peer" }, 84 { WSAENOBUFS, "No buffer space available" }, 85 { WSAEISCONN, "Socket is already connected" }, 86 { WSAENOTCONN, "Socket is not connected" }, 87 { WSAESHUTDOWN, "Can't send after socket shutdown" }, 88 { WSAETOOMANYREFS, "Too many references, can't splice" }, 89 { WSAETIMEDOUT, "Connection timed out" }, 90 { WSAECONNREFUSED, "Connection refused" }, 91 { WSAELOOP, "Too many levels of symbolic links" }, 92 { WSAENAMETOOLONG, "File name too long" }, 93 { WSAEHOSTDOWN, "Host is down" }, 94 { WSAEHOSTUNREACH, "No route to host" }, 95 { WSAENOTEMPTY, "Directory not empty" }, 96 { WSAEPROCLIM, "Too many processes" }, 97 { WSAEUSERS, "Too many users" }, 98 { WSAEDQUOT, "Disc quota exceeded" }, 99 { WSAESTALE, "Stale NFS file handle" }, 100 { WSAEREMOTE, "Too many levels of remote in path" }, 101 { WSASYSNOTREADY, "Network system is unavailable" }, 102 { WSAVERNOTSUPPORTED, "Winsock version out of range" }, 103 { WSANOTINITIALISED, "WSAStartup not yet called" }, 104 { WSAEDISCON, "Graceful shutdown in progress" }, 105 { WSAHOST_NOT_FOUND, "Host not found" }, 106 { WSANO_DATA, "No host data of that type was found" }, 107 { -1, "Unknown error code" } 108 }; 109 110 char *WSAErrorMsg(void) { 111 int i; 112 int id = WSAGetLastError(); 113 114 /* Assume none of them are actually -1 */ 115 for (i = 0; WSAErrors[i].id != -1; i++) 116 if (WSAErrors[i].id == id) 117 break; 118 119 sprintf(WSAbuf, "Socket Error: (%d) %s", id, WSAErrors[i].code); 120 121 return &(WSAbuf[0]); 122 } 123 #endif 124 125 #if defined(__APPLE__) || defined(__FreeBSD__) 126 #define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP 127 #define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP 128 #endif 129 130 static const char *UNAVAILABLE = " - API will not be available"; 131 static const char *MUNAVAILABLE = " - API multicast listener will not be available"; 132 133 static const char *BLANK = ""; 134 static const char *COMMA = ","; 135 #define COMSTR "," 136 static const char SEPARATOR = '|'; 137 #define SEPSTR "|" 138 #define CMDJOIN '+' 139 #define JOIN_CMD "CMD=" 140 #define BETWEEN_JOIN SEPSTR 141 142 static const char *APIVERSION = "3.7"; 143 static const char *DEAD = "Dead"; 144 #if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA) 145 static const char *SICK = "Sick"; 146 static const char *NOSTART = "NoStart"; 147 static const char *INIT = "Initialising"; 148 #endif 149 static const char *DISABLED = "Disabled"; 150 static const char *ALIVE = "Alive"; 151 static const char *REJECTING = "Rejecting"; 152 static const char *UNKNOWN = "Unknown"; 153 154 static __maybe_unused const char *NONE = "None"; 155 156 static const char *YES = "Y"; 157 static const char *NO = "N"; 158 static const char *NULLSTR = "(null)"; 159 160 static const char *TRUESTR = "true"; 161 static const char *FALSESTR = "false"; 162 163 static const char *SHA256STR = "sha256"; 164 165 static const char *DEVICECODE = "" 166 #ifdef USE_ANT_S1 167 "ANT " 168 #endif 169 #ifdef USE_ANT_S2 170 "AS2 " 171 #endif 172 #ifdef USE_ANT_S3 173 "AS3 " 174 #endif 175 #ifdef USE_AVALON 176 "AVA " 177 #endif 178 #ifdef USE_BAB 179 "BaB " 180 #endif 181 #ifdef USE_BFLSC 182 "BAS " 183 #endif 184 #ifdef USE_BITFORCE 185 "BFL " 186 #endif 187 #ifdef USE_BITFURY 188 "BFU " 189 #endif 190 #ifdef USE_BLOCKERUPTER 191 "BET " 192 #endif 193 #ifdef USE_DRILLBIT 194 "DRB " 195 #endif 196 #ifdef USE_DRAGONMINT_T1 197 "DT1 " 198 #endif 199 #ifdef USE_HASHFAST 200 "HFA " 201 #endif 202 #ifdef USE_HASHRATIO 203 "HRO " 204 #endif 205 #ifdef USE_BITMINE_A1 206 "BA1 " 207 #endif 208 #ifdef USE_ICARUS 209 "ICA " 210 #endif 211 #ifdef USE_KNC 212 "KnC " 213 #endif 214 #ifdef USE_MINION 215 "MBA " 216 #endif 217 #ifdef USE_MODMINER 218 "MMQ " 219 #endif 220 #ifdef USE_COINTERRA 221 "CTA " 222 #endif 223 #ifdef USE_SP10 224 "SPN " 225 #endif 226 #ifdef USE_SP30 227 "S30 " 228 #endif 229 230 231 ""; 232 233 static const char *OSINFO = 234 #if defined(__linux) 235 "Linux"; 236 #else 237 #if defined(__APPLE__) 238 "Apple"; 239 #else 240 #if defined (WIN32) 241 "Windows"; 242 #else 243 #if defined(unix) 244 "Unix"; 245 #else 246 "Unknown"; 247 #endif 248 #endif 249 #endif 250 #endif 251 252 #define _DEVS "DEVS" 253 #define _POOLS "POOLS" 254 #define _SUMMARY "SUMMARY" 255 #define _STATUS "STATUS" 256 #define _VERSION "VERSION" 257 #define _MINECONFIG "CONFIG" 258 259 #ifdef HAVE_AN_FPGA 260 #define _PGA "PGA" 261 #endif 262 263 #ifdef HAVE_AN_ASIC 264 #define _ASC "ASC" 265 #endif 266 267 #define _PGAS "PGAS" 268 #define _ASCS "ASCS" 269 #define _NOTIFY "NOTIFY" 270 #define _DEVDETAILS "DEVDETAILS" 271 #define _BYE "BYE" 272 #define _RESTART "RESTART" 273 #define _MINESTATS "STATS" 274 #define _MINEDEBUG "DBGSTATS" 275 #define _CHECK "CHECK" 276 #define _MINECOIN "COIN" 277 #define _DEBUGSET "DEBUG" 278 #define _SETCONFIG "SETCONFIG" 279 #define _USBSTATS "USBSTATS" 280 #define _LCD "LCD" 281 282 static const char ISJSON = '{'; 283 #define JSON0 "{" 284 #define JSON1 "\"" 285 #define JSON2 "\":[" 286 #define JSON3 "]" 287 #define JSON4 ",\"id\":1" 288 // If anyone cares, id=0 for truncated output 289 #define JSON4_TRUNCATED ",\"id\":0" 290 #define JSON5 "}" 291 #define JSON6 "\":" 292 293 #define JSON_START JSON0 294 #define JSON_DEVS JSON1 _DEVS JSON2 295 #define JSON_POOLS JSON1 _POOLS JSON2 296 #define JSON_SUMMARY JSON1 _SUMMARY JSON2 297 #define JSON_STATUS JSON1 _STATUS JSON2 298 #define JSON_VERSION JSON1 _VERSION JSON2 299 #define JSON_MINECONFIG JSON1 _MINECONFIG JSON2 300 #define JSON_ACTION JSON0 JSON1 _STATUS JSON6 301 302 #ifdef HAVE_AN_FPGA 303 #define JSON_PGA JSON1 _PGA JSON2 304 #endif 305 306 #ifdef HAVE_AN_ASIC 307 #define JSON_ASC JSON1 _ASC JSON2 308 #endif 309 310 #define JSON_PGAS JSON1 _PGAS JSON2 311 #define JSON_ASCS JSON1 _ASCS JSON2 312 #define JSON_NOTIFY JSON1 _NOTIFY JSON2 313 #define JSON_DEVDETAILS JSON1 _DEVDETAILS JSON2 314 #define JSON_BYE JSON1 _BYE JSON1 315 #define JSON_RESTART JSON1 _RESTART JSON1 316 #define JSON_CLOSE JSON3 317 #define JSON_MINESTATS JSON1 _MINESTATS JSON2 318 #define JSON_MINEDEBUG JSON1 _MINEDEBUG JSON2 319 #define JSON_CHECK JSON1 _CHECK JSON2 320 #define JSON_MINECOIN JSON1 _MINECOIN JSON2 321 #define JSON_DEBUGSET JSON1 _DEBUGSET JSON2 322 #define JSON_SETCONFIG JSON1 _SETCONFIG JSON2 323 #define JSON_USBSTATS JSON1 _USBSTATS JSON2 324 #define JSON_LCD JSON1 _LCD JSON2 325 #define JSON_END JSON4 JSON5 326 #define JSON_END_TRUNCATED JSON4_TRUNCATED JSON5 327 #define JSON_BETWEEN_JOIN "," 328 329 static const char *JSON_COMMAND = "command"; 330 static const char *JSON_PARAMETER = "parameter"; 331 332 #define MSG_POOL 7 333 #define MSG_NOPOOL 8 334 #define MSG_DEVS 9 335 #define MSG_NODEVS 10 336 #define MSG_SUMM 11 337 #define MSG_INVCMD 14 338 #define MSG_MISID 15 339 340 #define MSG_VERSION 22 341 #define MSG_INVJSON 23 342 #define MSG_MISCMD 24 343 #define MSG_MISPID 25 344 #define MSG_INVPID 26 345 #define MSG_SWITCHP 27 346 #define MSG_MISVAL 28 347 #define MSG_NOADL 29 348 #define MSG_INVINT 31 349 #define MSG_MINECONFIG 33 350 #define MSG_MISFN 42 351 #define MSG_BADFN 43 352 #define MSG_SAVED 44 353 #define MSG_ACCDENY 45 354 #define MSG_ACCOK 46 355 #define MSG_ENAPOOL 47 356 #define MSG_DISPOOL 48 357 #define MSG_ALRENAP 49 358 #define MSG_ALRDISP 50 359 #define MSG_DISLASTP 51 360 #define MSG_MISPDP 52 361 #define MSG_INVPDP 53 362 #define MSG_TOOMANYP 54 363 #define MSG_ADDPOOL 55 364 365 #ifdef HAVE_AN_FPGA 366 #define MSG_PGANON 56 367 #define MSG_PGADEV 57 368 #define MSG_INVPGA 58 369 #endif 370 371 #define MSG_NUMPGA 59 372 #define MSG_NOTIFY 60 373 374 #ifdef HAVE_AN_FPGA 375 #define MSG_PGALRENA 61 376 #define MSG_PGALRDIS 62 377 #define MSG_PGAENA 63 378 #define MSG_PGADIS 64 379 #define MSG_PGAUNW 65 380 #endif 381 382 #define MSG_REMLASTP 66 383 #define MSG_ACTPOOL 67 384 #define MSG_REMPOOL 68 385 #define MSG_DEVDETAILS 69 386 #define MSG_MINESTATS 70 387 #define MSG_MISCHK 71 388 #define MSG_CHECK 72 389 #define MSG_POOLPRIO 73 390 #define MSG_DUPPID 74 391 #define MSG_MISBOOL 75 392 #define MSG_INVBOOL 76 393 #define MSG_FOO 77 394 #define MSG_MINECOIN 78 395 #define MSG_DEBUGSET 79 396 #define MSG_PGAIDENT 80 397 #define MSG_PGANOID 81 398 #define MSG_SETCONFIG 82 399 #define MSG_UNKCON 83 400 #define MSG_INVNUM 84 401 #define MSG_CONPAR 85 402 #define MSG_CONVAL 86 403 #define MSG_USBSTA 87 404 #define MSG_NOUSTA 88 405 406 #ifdef HAVE_AN_FPGA 407 #define MSG_MISPGAOPT 89 408 #define MSG_PGANOSET 90 409 #define MSG_PGAHELP 91 410 #define MSG_PGASETOK 92 411 #define MSG_PGASETERR 93 412 #endif 413 414 #define MSG_ZERMIS 94 415 #define MSG_ZERINV 95 416 #define MSG_ZERSUM 96 417 #define MSG_ZERNOSUM 97 418 #define MSG_PGAUSBNODEV 98 419 #define MSG_INVHPLG 99 420 #define MSG_HOTPLUG 100 421 #define MSG_DISHPLG 101 422 #define MSG_NOHPLG 102 423 #define MSG_MISHPLG 103 424 425 #define MSG_NUMASC 104 426 #ifdef HAVE_AN_ASIC 427 #define MSG_ASCNON 105 428 #define MSG_ASCDEV 106 429 #define MSG_INVASC 107 430 #define MSG_ASCLRENA 108 431 #define MSG_ASCLRDIS 109 432 #define MSG_ASCENA 110 433 #define MSG_ASCDIS 111 434 #define MSG_ASCUNW 112 435 #define MSG_ASCIDENT 113 436 #define MSG_ASCNOID 114 437 #endif 438 #define MSG_ASCUSBNODEV 115 439 440 #ifdef HAVE_AN_ASIC 441 #define MSG_MISASCOPT 116 442 #define MSG_ASCNOSET 117 443 #define MSG_ASCHELP 118 444 #define MSG_ASCSETOK 119 445 #define MSG_ASCSETERR 120 446 #endif 447 448 #define MSG_INVNEG 121 449 #define MSG_SETQUOTA 122 450 #define MSG_LOCKOK 123 451 #define MSG_LOCKDIS 124 452 #define MSG_LCD 125 453 454 #define MSG_MINEDEBUG 126 455 456 #define MSG_DEPRECATED 127 457 458 enum code_severity { 459 SEVERITY_ERR, 460 SEVERITY_WARN, 461 SEVERITY_INFO, 462 SEVERITY_SUCC, 463 SEVERITY_FAIL 464 }; 465 466 enum code_parameters { 467 PARAM_PGA, 468 PARAM_ASC, 469 PARAM_PID, 470 PARAM_PGAMAX, 471 PARAM_ASCMAX, 472 PARAM_PMAX, 473 PARAM_POOLMAX, 474 475 // Single generic case: have the code resolve it - see below 476 PARAM_DMAX, 477 478 PARAM_CMD, 479 PARAM_POOL, 480 PARAM_STR, 481 PARAM_BOTH, 482 PARAM_BOOL, 483 PARAM_SET, 484 PARAM_INT, 485 PARAM_NONE 486 }; 487 488 struct CODES { 489 const enum code_severity severity; 490 const int code; 491 const enum code_parameters params; 492 const char *description; 493 } codes[] = { 494 { SEVERITY_SUCC, MSG_POOL, PARAM_PMAX, "%d Pool(s)" }, 495 { SEVERITY_ERR, MSG_NOPOOL, PARAM_NONE, "No pools" }, 496 497 { SEVERITY_SUCC, MSG_DEVS, PARAM_DMAX, 498 #ifdef HAVE_AN_ASIC 499 "%d ASC(s)" 500 #endif 501 #if defined(HAVE_AN_ASIC) && defined(HAVE_AN_FPGA) 502 " - " 503 #endif 504 #ifdef HAVE_AN_FPGA 505 "%d PGA(s)" 506 #endif 507 }, 508 509 { SEVERITY_ERR, MSG_NODEVS, PARAM_NONE, "No " 510 #ifdef HAVE_AN_ASIC 511 "ASCs" 512 #endif 513 #if defined(HAVE_AN_ASIC) && defined(HAVE_AN_FPGA) 514 "/" 515 #endif 516 #ifdef HAVE_AN_FPGA 517 "PGAs" 518 #endif 519 }, 520 521 { SEVERITY_SUCC, MSG_SUMM, PARAM_NONE, "Summary" }, 522 { SEVERITY_ERR, MSG_INVCMD, PARAM_NONE, "Invalid command" }, 523 { SEVERITY_ERR, MSG_MISID, PARAM_NONE, "Missing device id parameter" }, 524 #ifdef HAVE_AN_FPGA 525 { SEVERITY_ERR, MSG_PGANON, PARAM_NONE, "No PGAs" }, 526 { SEVERITY_SUCC, MSG_PGADEV, PARAM_PGA, "PGA%d" }, 527 { SEVERITY_ERR, MSG_INVPGA, PARAM_PGAMAX, "Invalid PGA id %d - range is 0 - %d" }, 528 { SEVERITY_INFO, MSG_PGALRENA,PARAM_PGA, "PGA %d already enabled" }, 529 { SEVERITY_INFO, MSG_PGALRDIS,PARAM_PGA, "PGA %d already disabled" }, 530 { SEVERITY_INFO, MSG_PGAENA, PARAM_PGA, "PGA %d sent enable message" }, 531 { SEVERITY_INFO, MSG_PGADIS, PARAM_PGA, "PGA %d set disable flag" }, 532 { SEVERITY_ERR, MSG_PGAUNW, PARAM_PGA, "PGA %d is not flagged WELL, cannot enable" }, 533 #endif 534 { SEVERITY_SUCC, MSG_NUMPGA, PARAM_NONE, "PGA count" }, 535 { SEVERITY_SUCC, MSG_NUMASC, PARAM_NONE, "ASC count" }, 536 { SEVERITY_SUCC, MSG_VERSION, PARAM_NONE, "CGMiner versions" }, 537 { SEVERITY_ERR, MSG_INVJSON, PARAM_NONE, "Invalid JSON" }, 538 { SEVERITY_ERR, MSG_MISCMD, PARAM_CMD, "Missing JSON '%s'" }, 539 { SEVERITY_ERR, MSG_MISPID, PARAM_NONE, "Missing pool id parameter" }, 540 { SEVERITY_ERR, MSG_INVPID, PARAM_POOLMAX, "Invalid pool id %d - range is 0 - %d" }, 541 { SEVERITY_SUCC, MSG_SWITCHP, PARAM_POOL, "Switching to pool %d:'%s'" }, 542 { SEVERITY_SUCC, MSG_MINECONFIG,PARAM_NONE, "CGMiner config" }, 543 { SEVERITY_ERR, MSG_MISFN, PARAM_NONE, "Missing save filename parameter" }, 544 { SEVERITY_ERR, MSG_BADFN, PARAM_STR, "Can't open or create save file '%s'" }, 545 { SEVERITY_SUCC, MSG_SAVED, PARAM_STR, "Configuration saved to file '%s'" }, 546 { SEVERITY_ERR, MSG_ACCDENY, PARAM_STR, "Access denied to '%s' command" }, 547 { SEVERITY_SUCC, MSG_ACCOK, PARAM_NONE, "Privileged access OK" }, 548 { SEVERITY_SUCC, MSG_ENAPOOL, PARAM_POOL, "Enabling pool %d:'%s'" }, 549 { SEVERITY_SUCC, MSG_POOLPRIO,PARAM_NONE, "Changed pool priorities" }, 550 { SEVERITY_ERR, MSG_DUPPID, PARAM_PID, "Duplicate pool specified %d" }, 551 { SEVERITY_SUCC, MSG_DISPOOL, PARAM_POOL, "Disabling pool %d:'%s'" }, 552 { SEVERITY_INFO, MSG_ALRENAP, PARAM_POOL, "Pool %d:'%s' already enabled" }, 553 { SEVERITY_INFO, MSG_ALRDISP, PARAM_POOL, "Pool %d:'%s' already disabled" }, 554 { SEVERITY_ERR, MSG_DISLASTP,PARAM_POOL, "Cannot disable last active pool %d:'%s'" }, 555 { SEVERITY_ERR, MSG_MISPDP, PARAM_NONE, "Missing addpool details" }, 556 { SEVERITY_ERR, MSG_INVPDP, PARAM_STR, "Invalid addpool details '%s'" }, 557 { SEVERITY_ERR, MSG_TOOMANYP,PARAM_NONE, "Reached maximum number of pools (%d)" }, 558 { SEVERITY_SUCC, MSG_ADDPOOL, PARAM_POOL, "Added pool %d: '%s'" }, 559 { SEVERITY_ERR, MSG_REMLASTP,PARAM_POOL, "Cannot remove last pool %d:'%s'" }, 560 { SEVERITY_ERR, MSG_ACTPOOL, PARAM_POOL, "Cannot remove active pool %d:'%s'" }, 561 { SEVERITY_SUCC, MSG_REMPOOL, PARAM_BOTH, "Removed pool %d:'%s'" }, 562 { SEVERITY_SUCC, MSG_NOTIFY, PARAM_NONE, "Notify" }, 563 { SEVERITY_SUCC, MSG_DEVDETAILS,PARAM_NONE, "Device Details" }, 564 { SEVERITY_SUCC, MSG_MINESTATS,PARAM_NONE, "CGMiner stats" }, 565 { SEVERITY_ERR, MSG_MISCHK, PARAM_NONE, "Missing check cmd" }, 566 { SEVERITY_SUCC, MSG_CHECK, PARAM_NONE, "Check command" }, 567 { SEVERITY_ERR, MSG_MISBOOL, PARAM_NONE, "Missing parameter: true/false" }, 568 { SEVERITY_ERR, MSG_INVBOOL, PARAM_NONE, "Invalid parameter should be true or false" }, 569 { SEVERITY_SUCC, MSG_FOO, PARAM_BOOL, "Failover-Only set to %s" }, 570 { SEVERITY_SUCC, MSG_MINECOIN,PARAM_NONE, "CGMiner coin" }, 571 { SEVERITY_SUCC, MSG_DEBUGSET,PARAM_NONE, "Debug settings" }, 572 #ifdef HAVE_AN_FPGA 573 { SEVERITY_SUCC, MSG_PGAIDENT,PARAM_PGA, "Identify command sent to PGA%d" }, 574 { SEVERITY_WARN, MSG_PGANOID, PARAM_PGA, "PGA%d does not support identify" }, 575 #endif 576 { SEVERITY_SUCC, MSG_SETCONFIG,PARAM_SET, "Set config '%s' to %d" }, 577 { SEVERITY_ERR, MSG_UNKCON, PARAM_STR, "Unknown config '%s'" }, 578 { SEVERITY_ERR, MSG_DEPRECATED, PARAM_STR, "Deprecated config option '%s'" }, 579 { SEVERITY_ERR, MSG_INVNUM, PARAM_BOTH, "Invalid number (%d) for '%s' range is 0-9999" }, 580 { SEVERITY_ERR, MSG_INVNEG, PARAM_BOTH, "Invalid negative number (%d) for '%s'" }, 581 { SEVERITY_SUCC, MSG_SETQUOTA,PARAM_SET, "Set pool '%s' to quota %d'" }, 582 { SEVERITY_ERR, MSG_CONPAR, PARAM_NONE, "Missing config parameters 'name,N'" }, 583 { SEVERITY_ERR, MSG_CONVAL, PARAM_STR, "Missing config value N for '%s,N'" }, 584 { SEVERITY_SUCC, MSG_USBSTA, PARAM_NONE, "USB Statistics" }, 585 { SEVERITY_INFO, MSG_NOUSTA, PARAM_NONE, "No USB Statistics" }, 586 #ifdef HAVE_AN_FPGA 587 { SEVERITY_ERR, MSG_MISPGAOPT, PARAM_NONE, "Missing option after PGA number" }, 588 { SEVERITY_WARN, MSG_PGANOSET, PARAM_PGA, "PGA %d does not support pgaset" }, 589 { SEVERITY_INFO, MSG_PGAHELP, PARAM_BOTH, "PGA %d set help: %s" }, 590 { SEVERITY_SUCC, MSG_PGASETOK, PARAM_BOTH, "PGA %d set OK" }, 591 { SEVERITY_ERR, MSG_PGASETERR, PARAM_BOTH, "PGA %d set failed: %s" }, 592 #endif 593 { SEVERITY_ERR, MSG_ZERMIS, PARAM_NONE, "Missing zero parameters" }, 594 { SEVERITY_ERR, MSG_ZERINV, PARAM_STR, "Invalid zero parameter '%s'" }, 595 { SEVERITY_SUCC, MSG_ZERSUM, PARAM_STR, "Zeroed %s stats with summary" }, 596 { SEVERITY_SUCC, MSG_ZERNOSUM, PARAM_STR, "Zeroed %s stats without summary" }, 597 #ifdef USE_USBUTILS 598 { SEVERITY_ERR, MSG_PGAUSBNODEV, PARAM_PGA, "PGA%d has no device" }, 599 { SEVERITY_ERR, MSG_ASCUSBNODEV, PARAM_PGA, "ASC%d has no device" }, 600 #endif 601 { SEVERITY_ERR, MSG_INVHPLG, PARAM_STR, "Invalid value for hotplug (%s) must be 0..9999" }, 602 { SEVERITY_SUCC, MSG_HOTPLUG, PARAM_INT, "Hotplug check set to %ds" }, 603 { SEVERITY_SUCC, MSG_DISHPLG, PARAM_NONE, "Hotplug disabled" }, 604 { SEVERITY_WARN, MSG_NOHPLG, PARAM_NONE, "Hotplug is not available" }, 605 { SEVERITY_ERR, MSG_MISHPLG, PARAM_NONE, "Missing hotplug parameter" }, 606 #ifdef HAVE_AN_ASIC 607 { SEVERITY_ERR, MSG_ASCNON, PARAM_NONE, "No ASCs" }, 608 { SEVERITY_SUCC, MSG_ASCDEV, PARAM_ASC, "ASC%d" }, 609 { SEVERITY_ERR, MSG_INVASC, PARAM_ASCMAX, "Invalid ASC id %d - range is 0 - %d" }, 610 { SEVERITY_INFO, MSG_ASCLRENA,PARAM_ASC, "ASC %d already enabled" }, 611 { SEVERITY_INFO, MSG_ASCLRDIS,PARAM_ASC, "ASC %d already disabled" }, 612 { SEVERITY_INFO, MSG_ASCENA, PARAM_ASC, "ASC %d sent enable message" }, 613 { SEVERITY_INFO, MSG_ASCDIS, PARAM_ASC, "ASC %d set disable flag" }, 614 { SEVERITY_ERR, MSG_ASCUNW, PARAM_ASC, "ASC %d is not flagged WELL, cannot enable" }, 615 { SEVERITY_SUCC, MSG_ASCIDENT,PARAM_ASC, "Identify command sent to ASC%d" }, 616 { SEVERITY_WARN, MSG_ASCNOID, PARAM_ASC, "ASC%d does not support identify" }, 617 { SEVERITY_ERR, MSG_MISASCOPT, PARAM_NONE, "Missing option after ASC number" }, 618 { SEVERITY_WARN, MSG_ASCNOSET, PARAM_ASC, "ASC %d does not support ascset" }, 619 { SEVERITY_INFO, MSG_ASCHELP, PARAM_BOTH, "ASC %d set help: %s" }, 620 { SEVERITY_SUCC, MSG_ASCSETOK, PARAM_BOTH, "ASC %d set OK" }, 621 { SEVERITY_ERR, MSG_ASCSETERR, PARAM_BOTH, "ASC %d set failed: %s" }, 622 #endif 623 { SEVERITY_SUCC, MSG_LCD, PARAM_NONE, "LCD" }, 624 { SEVERITY_SUCC, MSG_LOCKOK, PARAM_NONE, "Lock stats created" }, 625 { SEVERITY_WARN, MSG_LOCKDIS, PARAM_NONE, "Lock stats not enabled" }, 626 { SEVERITY_FAIL, 0, 0, NULL } 627 }; 628 629 static const char *localaddr = "127.0.0.1"; 630 631 static int my_thr_id = 0; 632 static bool bye; 633 634 // Used to control quit restart access to shutdown variables 635 static pthread_mutex_t quit_restart_lock; 636 637 static bool do_a_quit; 638 static bool do_a_restart; 639 640 static time_t when = 0; // when the request occurred 641 642 struct IPACCESS { 643 struct in6_addr ip; 644 struct in6_addr mask; 645 char group; 646 }; 647 648 #define GROUP(g) (toupper(g)) 649 #define PRIVGROUP GROUP('W') 650 #define NOPRIVGROUP GROUP('R') 651 #define ISPRIVGROUP(g) (GROUP(g) == PRIVGROUP) 652 #define GROUPOFFSET(g) (GROUP(g) - GROUP('A')) 653 #define VALIDGROUP(g) (GROUP(g) >= GROUP('A') && GROUP(g) <= GROUP('Z')) 654 #define COMMANDS(g) (apigroups[GROUPOFFSET(g)].commands) 655 #define DEFINEDGROUP(g) (ISPRIVGROUP(g) || COMMANDS(g) != NULL) 656 657 struct APIGROUPS { 658 // This becomes a string like: "|cmd1|cmd2|cmd3|" so it's quick to search 659 char *commands; 660 } apigroups['Z' - 'A' + 1]; // only A=0 to Z=25 (R: noprivs, W: allprivs) 661 662 static struct IPACCESS *ipaccess = NULL; 663 static int ips = 0; 664 665 struct io_data { 666 size_t siz; 667 char *ptr; 668 char *cur; 669 bool sock; 670 bool close; 671 }; 672 673 struct io_list { 674 struct io_data *io_data; 675 struct io_list *prev; 676 struct io_list *next; 677 }; 678 679 static struct io_list *io_head = NULL; 680 681 #define SOCKBUFALLOCSIZ 65536 682 683 #define io_new(init) _io_new(init, false) 684 #define sock_io_new() _io_new(SOCKBUFALLOCSIZ, true) 685 686 #define ALLOC_SBITEMS 2 687 #define LIMIT_SBITEMS 0 688 689 typedef struct sbitem { 690 char *buf; 691 size_t siz; 692 size_t tot; 693 } SBITEM; 694 695 // Size to grow tot if exceeded 696 #define SBEXTEND 4096 697 698 #define DATASB(_item) ((SBITEM *)(_item->data)) 699 700 static K_LIST *strbufs; 701 702 static void io_reinit(struct io_data *io_data) 703 { 704 io_data->cur = io_data->ptr; 705 *(io_data->ptr) = '\0'; 706 io_data->close = false; 707 } 708 709 static struct io_data *_io_new(size_t initial, bool socket_buf) 710 { 711 struct io_data *io_data; 712 struct io_list *io_list; 713 714 io_data = cgmalloc(sizeof(*io_data)); 715 io_data->ptr = cgmalloc(initial); 716 io_data->siz = initial; 717 io_data->sock = socket_buf; 718 io_reinit(io_data); 719 720 io_list = cgmalloc(sizeof(*io_list)); 721 722 io_list->io_data = io_data; 723 724 if (io_head) { 725 io_list->next = io_head; 726 io_list->prev = io_head->prev; 727 io_list->next->prev = io_list; 728 io_list->prev->next = io_list; 729 } else { 730 io_list->prev = io_list; 731 io_list->next = io_list; 732 io_head = io_list; 733 } 734 735 return io_data; 736 } 737 738 static bool io_add(struct io_data *io_data, char *buf) 739 { 740 size_t len, dif, tot; 741 742 len = strlen(buf); 743 dif = io_data->cur - io_data->ptr; 744 // send will always have enough space to add the JSON 745 tot = len + 1 + dif + sizeof(JSON_CLOSE) + sizeof(JSON_END); 746 747 if (tot > io_data->siz) { 748 size_t new = io_data->siz + (2 * SOCKBUFALLOCSIZ); 749 750 if (new < tot) 751 new = (2 + (size_t)((float)tot / (float)SOCKBUFALLOCSIZ)) * SOCKBUFALLOCSIZ; 752 753 io_data->ptr = cgrealloc(io_data->ptr, new); 754 io_data->cur = io_data->ptr + dif; 755 io_data->siz = new; 756 } 757 758 memcpy(io_data->cur, buf, len + 1); 759 io_data->cur += len; 760 761 return true; 762 } 763 764 static bool io_put(struct io_data *io_data, char *buf) 765 { 766 io_reinit(io_data); 767 return io_add(io_data, buf); 768 } 769 770 static void io_close(struct io_data *io_data) 771 { 772 io_data->close = true; 773 } 774 775 static void io_free() 776 { 777 struct io_list *io_list, *io_next; 778 779 if (io_head) { 780 io_list = io_head; 781 do { 782 io_next = io_list->next; 783 784 free(io_list->io_data->ptr); 785 free(io_list->io_data); 786 free(io_list); 787 788 io_list = io_next; 789 } while (io_list != io_head); 790 791 io_head = NULL; 792 } 793 } 794 795 // This is only called when expected to be needed (rarely) 796 // i.e. strings outside of the codes control (input from the user) 797 static char *escape_string(char *str, bool isjson) 798 { 799 char *buf, *ptr; 800 int count; 801 802 count = 0; 803 for (ptr = str; *ptr; ptr++) { 804 switch (*ptr) { 805 case ',': 806 case '|': 807 case '=': 808 if (!isjson) 809 count++; 810 break; 811 case '"': 812 if (isjson) 813 count++; 814 break; 815 case '\\': 816 count++; 817 break; 818 } 819 } 820 821 if (count == 0) 822 return str; 823 824 buf = cgmalloc(strlen(str) + count + 1); 825 826 ptr = buf; 827 while (*str) 828 switch (*str) { 829 case ',': 830 case '|': 831 case '=': 832 if (!isjson) 833 *(ptr++) = '\\'; 834 *(ptr++) = *(str++); 835 break; 836 case '"': 837 if (isjson) 838 *(ptr++) = '\\'; 839 *(ptr++) = *(str++); 840 break; 841 case '\\': 842 *(ptr++) = '\\'; 843 *(ptr++) = *(str++); 844 break; 845 default: 846 *(ptr++) = *(str++); 847 break; 848 } 849 850 *ptr = '\0'; 851 852 return buf; 853 } 854 855 static struct api_data *api_add_extra(struct api_data *root, struct api_data *extra) 856 { 857 struct api_data *tmp; 858 859 if (root) { 860 if (extra) { 861 // extra tail 862 tmp = extra->prev; 863 864 // extra prev = root tail 865 extra->prev = root->prev; 866 867 // root tail next = extra 868 root->prev->next = extra; 869 870 // extra tail next = root 871 tmp->next = root; 872 873 // root prev = extra tail 874 root->prev = tmp; 875 } 876 } else 877 root = extra; 878 879 return root; 880 } 881 882 static struct api_data *api_add_data_full(struct api_data *root, char *name, enum api_data_type type, void *data, bool copy_data) 883 { 884 struct api_data *api_data; 885 886 api_data = cgmalloc(sizeof(struct api_data)); 887 888 api_data->name = strdup(name); 889 api_data->type = type; 890 891 if (root == NULL) { 892 root = api_data; 893 root->prev = root; 894 root->next = root; 895 } else { 896 api_data->prev = root->prev; 897 root->prev = api_data; 898 api_data->next = root; 899 api_data->prev->next = api_data; 900 } 901 902 api_data->data_was_malloc = copy_data; 903 904 // Avoid crashing on bad data 905 if (data == NULL) { 906 api_data->type = type = API_CONST; 907 data = (void *)NULLSTR; 908 api_data->data_was_malloc = copy_data = false; 909 } 910 911 if (!copy_data) 912 api_data->data = data; 913 else 914 switch(type) { 915 case API_ESCAPE: 916 case API_STRING: 917 case API_CONST: 918 api_data->data = cgmalloc(strlen((char *)data) + 1); 919 strcpy((char*)(api_data->data), (char *)data); 920 break; 921 case API_UINT8: 922 /* Most OSs won't really alloc less than 4 */ 923 api_data->data = cgmalloc(4); 924 *(uint8_t *)api_data->data = *(uint8_t *)data; 925 break; 926 case API_INT16: 927 /* Most OSs won't really alloc less than 4 */ 928 api_data->data = cgmalloc(4); 929 *(int16_t *)api_data->data = *(int16_t *)data; 930 break; 931 case API_UINT16: 932 /* Most OSs won't really alloc less than 4 */ 933 api_data->data = cgmalloc(4); 934 *(uint16_t *)api_data->data = *(uint16_t *)data; 935 break; 936 case API_INT: 937 api_data->data = cgmalloc(sizeof(int)); 938 *((int *)(api_data->data)) = *((int *)data); 939 break; 940 case API_UINT: 941 api_data->data = cgmalloc(sizeof(unsigned int)); 942 *((unsigned int *)(api_data->data)) = *((unsigned int *)data); 943 break; 944 case API_UINT32: 945 api_data->data = cgmalloc(sizeof(uint32_t)); 946 *((uint32_t *)(api_data->data)) = *((uint32_t *)data); 947 break; 948 case API_HEX32: 949 api_data->data = cgmalloc(sizeof(uint32_t)); 950 *((uint32_t *)(api_data->data)) = *((uint32_t *)data); 951 break; 952 case API_UINT64: 953 api_data->data = cgmalloc(sizeof(uint64_t)); 954 *((uint64_t *)(api_data->data)) = *((uint64_t *)data); 955 break; 956 case API_INT64: 957 api_data->data = cgmalloc(sizeof(int64_t)); 958 *((int64_t *)(api_data->data)) = *((int64_t *)data); 959 break; 960 case API_DOUBLE: 961 case API_ELAPSED: 962 case API_MHS: 963 case API_MHTOTAL: 964 case API_UTILITY: 965 case API_FREQ: 966 case API_HS: 967 case API_DIFF: 968 case API_PERCENT: 969 api_data->data = cgmalloc(sizeof(double)); 970 *((double *)(api_data->data)) = *((double *)data); 971 break; 972 case API_BOOL: 973 api_data->data = cgmalloc(sizeof(bool)); 974 *((bool *)(api_data->data)) = *((bool *)data); 975 break; 976 case API_TIMEVAL: 977 api_data->data = cgmalloc(sizeof(struct timeval)); 978 memcpy(api_data->data, data, sizeof(struct timeval)); 979 break; 980 case API_TIME: 981 api_data->data = cgmalloc(sizeof(time_t)); 982 *(time_t *)(api_data->data) = *((time_t *)data); 983 break; 984 case API_VOLTS: 985 case API_TEMP: 986 case API_AVG: 987 api_data->data = cgmalloc(sizeof(float)); 988 *((float *)(api_data->data)) = *((float *)data); 989 break; 990 default: 991 applog(LOG_ERR, "API: unknown1 data type %d ignored", type); 992 api_data->type = API_STRING; 993 api_data->data_was_malloc = false; 994 api_data->data = (void *)UNKNOWN; 995 break; 996 } 997 998 return root; 999 } 1000 1001 struct api_data *api_add_escape(struct api_data *root, char *name, char *data, bool copy_data) 1002 { 1003 return api_add_data_full(root, name, API_ESCAPE, (void *)data, copy_data); 1004 } 1005 1006 struct api_data *api_add_string(struct api_data *root, char *name, char *data, bool copy_data) 1007 { 1008 return api_add_data_full(root, name, API_STRING, (void *)data, copy_data); 1009 } 1010 1011 struct api_data *api_add_const(struct api_data *root, char *name, const char *data, bool copy_data) 1012 { 1013 return api_add_data_full(root, name, API_CONST, (void *)data, copy_data); 1014 } 1015 1016 struct api_data *api_add_uint8(struct api_data *root, char *name, uint8_t *data, bool copy_data) 1017 { 1018 return api_add_data_full(root, name, API_UINT8, (void *)data, copy_data); 1019 } 1020 1021 struct api_data *api_add_int16(struct api_data *root, char *name, uint16_t *data, bool copy_data) 1022 { 1023 return api_add_data_full(root, name, API_INT16, (void *)data, copy_data); 1024 } 1025 1026 struct api_data *api_add_uint16(struct api_data *root, char *name, uint16_t *data, bool copy_data) 1027 { 1028 return api_add_data_full(root, name, API_UINT16, (void *)data, copy_data); 1029 } 1030 1031 struct api_data *api_add_int(struct api_data *root, char *name, int *data, bool copy_data) 1032 { 1033 return api_add_data_full(root, name, API_INT, (void *)data, copy_data); 1034 } 1035 1036 struct api_data *api_add_uint(struct api_data *root, char *name, unsigned int *data, bool copy_data) 1037 { 1038 return api_add_data_full(root, name, API_UINT, (void *)data, copy_data); 1039 } 1040 1041 struct api_data *api_add_uint32(struct api_data *root, char *name, uint32_t *data, bool copy_data) 1042 { 1043 return api_add_data_full(root, name, API_UINT32, (void *)data, copy_data); 1044 } 1045 1046 struct api_data *api_add_hex32(struct api_data *root, char *name, uint32_t *data, bool copy_data) 1047 { 1048 return api_add_data_full(root, name, API_HEX32, (void *)data, copy_data); 1049 } 1050 1051 struct api_data *api_add_uint64(struct api_data *root, char *name, uint64_t *data, bool copy_data) 1052 { 1053 return api_add_data_full(root, name, API_UINT64, (void *)data, copy_data); 1054 } 1055 1056 struct api_data *api_add_int64(struct api_data *root, char *name, int64_t *data, bool copy_data) 1057 { 1058 return api_add_data_full(root, name, API_INT64, (void *)data, copy_data); 1059 } 1060 1061 struct api_data *api_add_double(struct api_data *root, char *name, double *data, bool copy_data) 1062 { 1063 return api_add_data_full(root, name, API_DOUBLE, (void *)data, copy_data); 1064 } 1065 1066 struct api_data *api_add_elapsed(struct api_data *root, char *name, double *data, bool copy_data) 1067 { 1068 return api_add_data_full(root, name, API_ELAPSED, (void *)data, copy_data); 1069 } 1070 1071 struct api_data *api_add_bool(struct api_data *root, char *name, bool *data, bool copy_data) 1072 { 1073 return api_add_data_full(root, name, API_BOOL, (void *)data, copy_data); 1074 } 1075 1076 struct api_data *api_add_timeval(struct api_data *root, char *name, struct timeval *data, bool copy_data) 1077 { 1078 return api_add_data_full(root, name, API_TIMEVAL, (void *)data, copy_data); 1079 } 1080 1081 struct api_data *api_add_time(struct api_data *root, char *name, time_t *data, bool copy_data) 1082 { 1083 return api_add_data_full(root, name, API_TIME, (void *)data, copy_data); 1084 } 1085 1086 struct api_data *api_add_mhs(struct api_data *root, char *name, double *data, bool copy_data) 1087 { 1088 return api_add_data_full(root, name, API_MHS, (void *)data, copy_data); 1089 } 1090 1091 struct api_data *api_add_mhtotal(struct api_data *root, char *name, double *data, bool copy_data) 1092 { 1093 return api_add_data_full(root, name, API_MHTOTAL, (void *)data, copy_data); 1094 } 1095 1096 struct api_data *api_add_temp(struct api_data *root, char *name, float *data, bool copy_data) 1097 { 1098 return api_add_data_full(root, name, API_TEMP, (void *)data, copy_data); 1099 } 1100 1101 struct api_data *api_add_utility(struct api_data *root, char *name, double *data, bool copy_data) 1102 { 1103 return api_add_data_full(root, name, API_UTILITY, (void *)data, copy_data); 1104 } 1105 1106 struct api_data *api_add_freq(struct api_data *root, char *name, double *data, bool copy_data) 1107 { 1108 return api_add_data_full(root, name, API_FREQ, (void *)data, copy_data); 1109 } 1110 1111 struct api_data *api_add_volts(struct api_data *root, char *name, float *data, bool copy_data) 1112 { 1113 return api_add_data_full(root, name, API_VOLTS, (void *)data, copy_data); 1114 } 1115 1116 struct api_data *api_add_hs(struct api_data *root, char *name, double *data, bool copy_data) 1117 { 1118 return api_add_data_full(root, name, API_HS, (void *)data, copy_data); 1119 } 1120 1121 struct api_data *api_add_diff(struct api_data *root, char *name, double *data, bool copy_data) 1122 { 1123 return api_add_data_full(root, name, API_DIFF, (void *)data, copy_data); 1124 } 1125 1126 struct api_data *api_add_percent(struct api_data *root, char *name, double *data, bool copy_data) 1127 { 1128 return api_add_data_full(root, name, API_PERCENT, (void *)data, copy_data); 1129 } 1130 1131 struct api_data *api_add_avg(struct api_data *root, char *name, float *data, bool copy_data) 1132 { 1133 return api_add_data_full(root, name, API_AVG, (void *)data, copy_data); 1134 } 1135 1136 static void add_item_buf(K_ITEM *item, const char *str) 1137 { 1138 size_t old_siz, new_siz, siz, ext; 1139 char *buf; 1140 1141 buf = DATASB(item)->buf; 1142 siz = (size_t)strlen(str); 1143 1144 old_siz = DATASB(item)->siz; 1145 new_siz = old_siz + siz + 1; // include '\0' 1146 if (DATASB(item)->tot < new_siz) { 1147 ext = (siz + 1) + SBEXTEND - ((siz + 1) % SBEXTEND); 1148 DATASB(item)->buf = buf = cgrealloc(DATASB(item)->buf, DATASB(item)->tot + ext); 1149 DATASB(item)->tot += ext; 1150 } 1151 memcpy(buf + old_siz, str, siz + 1); 1152 DATASB(item)->siz += siz; 1153 } 1154 1155 static struct api_data *print_data(struct io_data *io_data, struct api_data *root, bool isjson, bool precom) 1156 { 1157 // N.B. strings don't use this buffer so 64 is enough (for now) 1158 char buf[64]; 1159 struct api_data *tmp; 1160 bool done, first = true; 1161 char *original, *escape; 1162 K_ITEM *item; 1163 1164 K_WLOCK(strbufs); 1165 item = k_unlink_head(strbufs); 1166 K_WUNLOCK(strbufs); 1167 1168 DATASB(item)->siz = 0; 1169 1170 if (precom) 1171 add_item_buf(item, COMMA); 1172 1173 if (isjson) 1174 add_item_buf(item, JSON0); 1175 1176 while (root) { 1177 if (!first) 1178 add_item_buf(item, COMMA); 1179 else 1180 first = false; 1181 1182 if (isjson) 1183 add_item_buf(item, JSON1); 1184 1185 add_item_buf(item, root->name); 1186 1187 if (isjson) 1188 add_item_buf(item, JSON1); 1189 1190 if (isjson) 1191 add_item_buf(item, ":"); 1192 else 1193 add_item_buf(item, "="); 1194 1195 first = false; 1196 1197 done = false; 1198 switch(root->type) { 1199 case API_STRING: 1200 case API_CONST: 1201 if (isjson) 1202 add_item_buf(item, JSON1); 1203 add_item_buf(item, (char *)(root->data)); 1204 if (isjson) 1205 add_item_buf(item, JSON1); 1206 done = true; 1207 break; 1208 case API_ESCAPE: 1209 original = (char *)(root->data); 1210 escape = escape_string((char *)(root->data), isjson); 1211 if (isjson) 1212 add_item_buf(item, JSON1); 1213 add_item_buf(item, escape); 1214 if (isjson) 1215 add_item_buf(item, JSON1); 1216 if (escape != original) 1217 free(escape); 1218 done = true; 1219 break; 1220 case API_UINT8: 1221 snprintf(buf, sizeof(buf), "%u", *(uint8_t *)root->data); 1222 break; 1223 case API_INT16: 1224 snprintf(buf, sizeof(buf), "%d", *(int16_t *)root->data); 1225 break; 1226 case API_UINT16: 1227 snprintf(buf, sizeof(buf), "%u", *(uint16_t *)root->data); 1228 break; 1229 case API_INT: 1230 snprintf(buf, sizeof(buf), "%d", *((int *)(root->data))); 1231 break; 1232 case API_UINT: 1233 snprintf(buf, sizeof(buf), "%u", *((unsigned int *)(root->data))); 1234 break; 1235 case API_UINT32: 1236 snprintf(buf, sizeof(buf), "%"PRIu32, *((uint32_t *)(root->data))); 1237 break; 1238 case API_HEX32: 1239 if (isjson) 1240 add_item_buf(item, JSON1); 1241 snprintf(buf, sizeof(buf), "0x%08x", *((uint32_t *)(root->data))); 1242 add_item_buf(item, buf); 1243 if (isjson) 1244 add_item_buf(item, JSON1); 1245 done = true; 1246 break; 1247 case API_UINT64: 1248 snprintf(buf, sizeof(buf), "%"PRIu64, *((uint64_t *)(root->data))); 1249 break; 1250 case API_INT64: 1251 snprintf(buf, sizeof(buf), "%"PRId64, *((int64_t *)(root->data))); 1252 break; 1253 case API_TIME: 1254 snprintf(buf, sizeof(buf), "%lu", *((unsigned long *)(root->data))); 1255 break; 1256 case API_DOUBLE: 1257 snprintf(buf, sizeof(buf), "%f", *((double *)(root->data))); 1258 break; 1259 case API_ELAPSED: 1260 snprintf(buf, sizeof(buf), "%.0f", *((double *)(root->data))); 1261 break; 1262 case API_UTILITY: 1263 case API_FREQ: 1264 case API_MHS: 1265 snprintf(buf, sizeof(buf), "%.2f", *((double *)(root->data))); 1266 break; 1267 case API_VOLTS: 1268 case API_AVG: 1269 snprintf(buf, sizeof(buf), "%.3f", *((float *)(root->data))); 1270 break; 1271 case API_MHTOTAL: 1272 snprintf(buf, sizeof(buf), "%.4f", *((double *)(root->data))); 1273 break; 1274 case API_HS: 1275 snprintf(buf, sizeof(buf), "%.15f", *((double *)(root->data))); 1276 break; 1277 case API_DIFF: 1278 snprintf(buf, sizeof(buf), "%.8f", *((double *)(root->data))); 1279 break; 1280 case API_BOOL: 1281 snprintf(buf, sizeof(buf), "%s", *((bool *)(root->data)) ? TRUESTR : FALSESTR); 1282 break; 1283 case API_TIMEVAL: 1284 snprintf(buf, sizeof(buf), "%ld.%06ld", 1285 (long)((struct timeval *)(root->data))->tv_sec, 1286 (long)((struct timeval *)(root->data))->tv_usec); 1287 break; 1288 case API_TEMP: 1289 snprintf(buf, sizeof(buf), "%.2f", *((float *)(root->data))); 1290 break; 1291 case API_PERCENT: 1292 snprintf(buf, sizeof(buf), "%.4f", *((double *)(root->data)) * 100.0); 1293 break; 1294 default: 1295 applog(LOG_ERR, "API: unknown2 data type %d ignored", root->type); 1296 if (isjson) 1297 add_item_buf(item, JSON1); 1298 add_item_buf(item, UNKNOWN); 1299 if (isjson) 1300 add_item_buf(item, JSON1); 1301 done = true; 1302 break; 1303 } 1304 1305 if (!done) 1306 add_item_buf(item, buf); 1307 1308 free(root->name); 1309 if (root->data_was_malloc) 1310 free(root->data); 1311 1312 if (root->next == root) { 1313 free(root); 1314 root = NULL; 1315 } else { 1316 tmp = root; 1317 root = tmp->next; 1318 root->prev = tmp->prev; 1319 root->prev->next = root; 1320 free(tmp); 1321 } 1322 } 1323 1324 if (isjson) 1325 add_item_buf(item, JSON5); 1326 else 1327 add_item_buf(item, SEPSTR); 1328 1329 io_add(io_data, DATASB(item)->buf); 1330 1331 K_WLOCK(strbufs); 1332 k_add_head(strbufs, item); 1333 K_WUNLOCK(strbufs); 1334 1335 return root; 1336 } 1337 1338 #define DRIVER_COUNT_DRV(X) if (devices[i]->drv->drv_id == DRIVER_##X) \ 1339 count++; 1340 1341 #ifdef HAVE_AN_ASIC 1342 static int numascs(void) 1343 { 1344 int count = 0; 1345 int i; 1346 1347 rd_lock(&devices_lock); 1348 for (i = 0; i < total_devices; i++) { 1349 ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV) 1350 } 1351 rd_unlock(&devices_lock); 1352 return count; 1353 } 1354 1355 static int ascdevice(int ascid) 1356 { 1357 int count = 0; 1358 int i; 1359 1360 rd_lock(&devices_lock); 1361 for (i = 0; i < total_devices; i++) { 1362 ASIC_PARSE_COMMANDS(DRIVER_COUNT_DRV) 1363 if (count == (ascid + 1)) 1364 goto foundit; 1365 } 1366 1367 rd_unlock(&devices_lock); 1368 return -1; 1369 1370 foundit: 1371 1372 rd_unlock(&devices_lock); 1373 return i; 1374 } 1375 #endif 1376 1377 #ifdef HAVE_AN_FPGA 1378 static int numpgas(void) 1379 { 1380 int count = 0; 1381 int i; 1382 1383 rd_lock(&devices_lock); 1384 for (i = 0; i < total_devices; i++) { 1385 FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV) 1386 } 1387 rd_unlock(&devices_lock); 1388 return count; 1389 } 1390 1391 static int pgadevice(int pgaid) 1392 { 1393 int count = 0; 1394 int i; 1395 1396 rd_lock(&devices_lock); 1397 for (i = 0; i < total_devices; i++) { 1398 FPGA_PARSE_COMMANDS(DRIVER_COUNT_DRV) 1399 if (count == (pgaid + 1)) 1400 goto foundit; 1401 } 1402 1403 rd_unlock(&devices_lock); 1404 return -1; 1405 1406 foundit: 1407 1408 rd_unlock(&devices_lock); 1409 return i; 1410 } 1411 #endif 1412 1413 #define LIMSIZ (TMPBUFSIZ - 1) 1414 1415 // All replies (except BYE and RESTART) start with a message 1416 // thus for JSON, message() inserts JSON_START at the front 1417 // and send_result() adds JSON_END at the end 1418 static void message(struct io_data *io_data, int messageid, int paramid, char *param2, bool isjson) 1419 { 1420 struct api_data *root = NULL; 1421 char buf[TMPBUFSIZ]; 1422 char severity[2]; 1423 #ifdef HAVE_AN_ASIC 1424 int asc; 1425 #endif 1426 #ifdef HAVE_AN_FPGA 1427 int pga; 1428 #endif 1429 int i; 1430 1431 if (isjson) 1432 io_add(io_data, JSON_START JSON_STATUS); 1433 1434 for (i = 0; codes[i].severity != SEVERITY_FAIL; i++) { 1435 if (codes[i].code == messageid) { 1436 switch (codes[i].severity) { 1437 case SEVERITY_WARN: 1438 severity[0] = 'W'; 1439 break; 1440 case SEVERITY_INFO: 1441 severity[0] = 'I'; 1442 break; 1443 case SEVERITY_SUCC: 1444 severity[0] = 'S'; 1445 break; 1446 case SEVERITY_ERR: 1447 default: 1448 severity[0] = 'E'; 1449 break; 1450 } 1451 severity[1] = '\0'; 1452 1453 switch(codes[i].params) { 1454 case PARAM_PGA: 1455 case PARAM_ASC: 1456 case PARAM_PID: 1457 case PARAM_INT: 1458 snprintf(buf, LIMSIZ, codes[i].description, paramid); 1459 break; 1460 case PARAM_POOL: 1461 snprintf(buf, LIMSIZ, codes[i].description, paramid, pools[paramid]->rpc_url); 1462 break; 1463 #ifdef HAVE_AN_FPGA 1464 case PARAM_PGAMAX: 1465 pga = numpgas(); 1466 snprintf(buf, LIMSIZ, codes[i].description, paramid, pga - 1); 1467 break; 1468 #endif 1469 #ifdef HAVE_AN_ASIC 1470 case PARAM_ASCMAX: 1471 asc = numascs(); 1472 snprintf(buf, LIMSIZ, codes[i].description, paramid, asc - 1); 1473 break; 1474 #endif 1475 case PARAM_PMAX: 1476 snprintf(buf, LIMSIZ, codes[i].description, total_pools); 1477 break; 1478 case PARAM_POOLMAX: 1479 snprintf(buf, LIMSIZ, codes[i].description, paramid, total_pools - 1); 1480 break; 1481 case PARAM_DMAX: 1482 #ifdef HAVE_AN_ASIC 1483 asc = numascs(); 1484 #endif 1485 #ifdef HAVE_AN_FPGA 1486 pga = numpgas(); 1487 #endif 1488 1489 snprintf(buf, LIMSIZ, codes[i].description 1490 #ifdef HAVE_AN_ASIC 1491 , asc 1492 #endif 1493 #ifdef HAVE_AN_FPGA 1494 , pga 1495 #endif 1496 ); 1497 break; 1498 case PARAM_CMD: 1499 snprintf(buf, LIMSIZ, codes[i].description, JSON_COMMAND); 1500 break; 1501 case PARAM_STR: 1502 snprintf(buf, LIMSIZ, codes[i].description, param2); 1503 break; 1504 case PARAM_BOTH: 1505 snprintf(buf, LIMSIZ, codes[i].description, paramid, param2); 1506 break; 1507 case PARAM_BOOL: 1508 snprintf(buf, LIMSIZ, codes[i].description, paramid ? TRUESTR : FALSESTR); 1509 break; 1510 case PARAM_SET: 1511 snprintf(buf, LIMSIZ, codes[i].description, param2, paramid); 1512 break; 1513 case PARAM_NONE: 1514 default: 1515 strcpy(buf, codes[i].description); 1516 } 1517 1518 root = api_add_string(root, _STATUS, severity, false); 1519 root = api_add_time(root, "When", &when, false); 1520 root = api_add_int(root, "Code", &messageid, false); 1521 root = api_add_escape(root, "Msg", buf, false); 1522 /* Do not give out description for random probes to 1523 * addresses with inappropriately open API ports. */ 1524 if (messageid != MSG_INVCMD) 1525 root = api_add_escape(root, "Description", opt_api_description, false); 1526 1527 root = print_data(io_data, root, isjson, false); 1528 if (isjson) 1529 io_add(io_data, JSON_CLOSE); 1530 return; 1531 } 1532 } 1533 1534 root = api_add_string(root, _STATUS, "F", false); 1535 root = api_add_time(root, "When", &when, false); 1536 int id = -1; 1537 root = api_add_int(root, "Code", &id, false); 1538 sprintf(buf, "%d", messageid); 1539 root = api_add_escape(root, "Msg", buf, false); 1540 root = api_add_escape(root, "Description", opt_api_description, false); 1541 1542 root = print_data(io_data, root, isjson, false); 1543 if (isjson) 1544 io_add(io_data, JSON_CLOSE); 1545 } 1546 1547 #if LOCK_TRACKING 1548 1549 #define LOCK_FMT_FFL " - called from %s %s():%d" 1550 1551 #define LOCKMSG(fmt, ...) fprintf(stderr, "APILOCK: " fmt "\n", ##__VA_ARGS__) 1552 #define LOCKMSGMORE(fmt, ...) fprintf(stderr, " " fmt "\n", ##__VA_ARGS__) 1553 #define LOCKMSGFFL(fmt, ...) fprintf(stderr, "APILOCK: " fmt LOCK_FMT_FFL "\n", ##__VA_ARGS__, file, func, linenum) 1554 #define LOCKMSGFLUSH() fflush(stderr) 1555 1556 typedef struct lockstat { 1557 uint64_t lock_id; 1558 const char *file; 1559 const char *func; 1560 int linenum; 1561 struct timeval tv; 1562 } LOCKSTAT; 1563 1564 typedef struct lockline { 1565 struct lockline *prev; 1566 struct lockstat *stat; 1567 struct lockline *next; 1568 } LOCKLINE; 1569 1570 typedef struct lockinfo { 1571 void *lock; 1572 enum cglock_typ typ; 1573 const char *file; 1574 const char *func; 1575 int linenum; 1576 uint64_t gets; 1577 uint64_t gots; 1578 uint64_t tries; 1579 uint64_t dids; 1580 uint64_t didnts; // should be tries - dids 1581 uint64_t unlocks; 1582 LOCKSTAT lastgot; 1583 LOCKLINE *lockgets; 1584 LOCKLINE *locktries; 1585 } LOCKINFO; 1586 1587 typedef struct locklist { 1588 LOCKINFO *info; 1589 struct locklist *next; 1590 } LOCKLIST; 1591 1592 static uint64_t lock_id = 1; 1593 1594 static LOCKLIST *lockhead; 1595 1596 static void lockmsgnow() 1597 { 1598 struct timeval now; 1599 struct tm *tm; 1600 time_t dt; 1601 1602 cgtime(&now); 1603 1604 dt = now.tv_sec; 1605 tm = localtime(&dt); 1606 1607 LOCKMSG("%d-%02d-%02d %02d:%02d:%02d", 1608 tm->tm_year + 1900, 1609 tm->tm_mon + 1, 1610 tm->tm_mday, 1611 tm->tm_hour, 1612 tm->tm_min, 1613 tm->tm_sec); 1614 } 1615 1616 static LOCKLIST *newlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum) 1617 { 1618 LOCKLIST *list; 1619 1620 list = cgcalloc(1, sizeof(*list)); 1621 list->info = cgcalloc(1, sizeof(*(list->info))); 1622 list->next = lockhead; 1623 lockhead = list; 1624 1625 list->info->lock = lock; 1626 list->info->typ = typ; 1627 list->info->file = file; 1628 list->info->func = func; 1629 list->info->linenum = linenum; 1630 1631 return list; 1632 } 1633 1634 static LOCKINFO *findlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum) 1635 { 1636 LOCKLIST *look; 1637 1638 look = lockhead; 1639 while (look) { 1640 if (look->info->lock == lock) 1641 break; 1642 look = look->next; 1643 } 1644 1645 if (!look) 1646 look = newlock(lock, typ, file, func, linenum); 1647 1648 return look->info; 1649 } 1650 1651 static void addgettry(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool get) 1652 { 1653 LOCKSTAT *stat; 1654 LOCKLINE *line; 1655 1656 stat = cgcalloc(1, sizeof(*stat)); 1657 line = cgcalloc(1, sizeof(*line)); 1658 1659 if (get) 1660 info->gets++; 1661 else 1662 info->tries++; 1663 1664 stat->lock_id = id; 1665 stat->file = file; 1666 stat->func = func; 1667 stat->linenum = linenum; 1668 cgtime(&stat->tv); 1669 1670 line->stat = stat; 1671 1672 if (get) { 1673 line->next = info->lockgets; 1674 if (info->lockgets) 1675 info->lockgets->prev = line; 1676 info->lockgets = line; 1677 } else { 1678 line->next = info->locktries; 1679 if (info->locktries) 1680 info->locktries->prev = line; 1681 info->locktries = line; 1682 } 1683 } 1684 1685 static void markgotdid(LOCKINFO *info, uint64_t id, const char *file, const char *func, const int linenum, bool got, int ret) 1686 { 1687 LOCKLINE *line; 1688 1689 if (got) 1690 info->gots++; 1691 else { 1692 if (ret == 0) 1693 info->dids++; 1694 else 1695 info->didnts++; 1696 } 1697 1698 if (got || ret == 0) { 1699 info->lastgot.lock_id = id; 1700 info->lastgot.file = file; 1701 info->lastgot.func = func; 1702 info->lastgot.linenum = linenum; 1703 cgtime(&info->lastgot.tv); 1704 } 1705 1706 if (got) 1707 line = info->lockgets; 1708 else 1709 line = info->locktries; 1710 while (line) { 1711 if (line->stat->lock_id == id) 1712 break; 1713 line = line->next; 1714 } 1715 1716 if (!line) { 1717 lockmsgnow(); 1718 LOCKMSGFFL("ERROR attempt to mark a lock as '%s' that wasn't '%s' id=%"PRIu64, 1719 got ? "got" : "did/didnt", got ? "get" : "try", id); 1720 } 1721 1722 // Unlink it 1723 if (line->prev) 1724 line->prev->next = line->next; 1725 if (line->next) 1726 line->next->prev = line->prev; 1727 1728 if (got) { 1729 if (info->lockgets == line) 1730 info->lockgets = line->next; 1731 } else { 1732 if (info->locktries == line) 1733 info->locktries = line->next; 1734 } 1735 1736 free(line->stat); 1737 free(line); 1738 } 1739 1740 // Yes this uses locks also ... ;/ 1741 static void locklock() 1742 { 1743 if (unlikely(pthread_mutex_lock(&lockstat_lock))) 1744 quithere(1, "WTF MUTEX ERROR ON LOCK! errno=%d", errno); 1745 } 1746 1747 static void lockunlock() 1748 { 1749 if (unlikely(pthread_mutex_unlock(&lockstat_lock))) 1750 quithere(1, "WTF MUTEX ERROR ON UNLOCK! errno=%d", errno); 1751 } 1752 1753 uint64_t api_getlock(void *lock, const char *file, const char *func, const int linenum) 1754 { 1755 LOCKINFO *info; 1756 uint64_t id; 1757 1758 locklock(); 1759 1760 info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); 1761 id = lock_id++; 1762 addgettry(info, id, file, func, linenum, true); 1763 1764 lockunlock(); 1765 1766 return id; 1767 } 1768 1769 void api_gotlock(uint64_t id, void *lock, const char *file, const char *func, const int linenum) 1770 { 1771 LOCKINFO *info; 1772 1773 locklock(); 1774 1775 info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); 1776 markgotdid(info, id, file, func, linenum, true, 0); 1777 1778 lockunlock(); 1779 } 1780 1781 uint64_t api_trylock(void *lock, const char *file, const char *func, const int linenum) 1782 { 1783 LOCKINFO *info; 1784 uint64_t id; 1785 1786 locklock(); 1787 1788 info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); 1789 id = lock_id++; 1790 addgettry(info, id, file, func, linenum, false); 1791 1792 lockunlock(); 1793 1794 return id; 1795 } 1796 1797 void api_didlock(uint64_t id, int ret, void *lock, const char *file, const char *func, const int linenum) 1798 { 1799 LOCKINFO *info; 1800 1801 locklock(); 1802 1803 info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); 1804 markgotdid(info, id, file, func, linenum, false, ret); 1805 1806 lockunlock(); 1807 } 1808 1809 void api_gunlock(void *lock, const char *file, const char *func, const int linenum) 1810 { 1811 LOCKINFO *info; 1812 1813 locklock(); 1814 1815 info = findlock(lock, CGLOCK_UNKNOWN, file, func, linenum); 1816 info->unlocks++; 1817 1818 lockunlock(); 1819 } 1820 1821 void api_initlock(void *lock, enum cglock_typ typ, const char *file, const char *func, const int linenum) 1822 { 1823 locklock(); 1824 1825 findlock(lock, typ, file, func, linenum); 1826 1827 lockunlock(); 1828 } 1829 1830 void dsp_det(char *msg, LOCKSTAT *stat) 1831 { 1832 struct tm *tm; 1833 time_t dt; 1834 1835 dt = stat->tv.tv_sec; 1836 tm = localtime(&dt); 1837 1838 LOCKMSGMORE("%s id=%"PRIu64" by %s %s():%d at %d-%02d-%02d %02d:%02d:%02d", 1839 msg, 1840 stat->lock_id, 1841 stat->file, 1842 stat->func, 1843 stat->linenum, 1844 tm->tm_year + 1900, 1845 tm->tm_mon + 1, 1846 tm->tm_mday, 1847 tm->tm_hour, 1848 tm->tm_min, 1849 tm->tm_sec); 1850 } 1851 1852 void dsp_lock(LOCKINFO *info) 1853 { 1854 LOCKLINE *line; 1855 char *status; 1856 1857 LOCKMSG("Lock %p created by %s %s():%d", 1858 info->lock, 1859 info->file, 1860 info->func, 1861 info->linenum); 1862 LOCKMSGMORE("gets:%"PRIu64" gots:%"PRIu64" tries:%"PRIu64 1863 " dids:%"PRIu64" didnts:%"PRIu64" unlocks:%"PRIu64, 1864 info->gets, 1865 info->gots, 1866 info->tries, 1867 info->dids, 1868 info->didnts, 1869 info->unlocks); 1870 1871 if (info->gots > 0 || info->dids > 0) { 1872 if (info->unlocks < info->gots + info->dids) 1873 status = "Last got/did still HELD"; 1874 else 1875 status = "Last got/did (idle)"; 1876 1877 dsp_det(status, &(info->lastgot)); 1878 } else 1879 LOCKMSGMORE("... unused ..."); 1880 1881 if (info->lockgets) { 1882 LOCKMSGMORE("BLOCKED gets (%"PRIu64")", info->gets - info->gots); 1883 line = info->lockgets; 1884 while (line) { 1885 dsp_det("", line->stat); 1886 line = line->next; 1887 } 1888 } else 1889 LOCKMSGMORE("no blocked gets"); 1890 1891 if (info->locktries) { 1892 LOCKMSGMORE("BLOCKED tries (%"PRIu64")", info->tries - info->dids - info->didnts); 1893 line = info->lockgets; 1894 while (line) { 1895 dsp_det("", line->stat); 1896 line = line->next; 1897 } 1898 } else 1899 LOCKMSGMORE("no blocked tries"); 1900 } 1901 1902 void show_locks() 1903 { 1904 LOCKLIST *list; 1905 1906 locklock(); 1907 1908 lockmsgnow(); 1909 1910 list = lockhead; 1911 if (!list) 1912 LOCKMSG("no locks?!?\n"); 1913 else { 1914 while (list) { 1915 dsp_lock(list->info); 1916 list = list->next; 1917 } 1918 } 1919 1920 LOCKMSGFLUSH(); 1921 1922 lockunlock(); 1923 } 1924 #endif 1925 1926 static void lockstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 1927 { 1928 #if LOCK_TRACKING 1929 show_locks(); 1930 message(io_data, MSG_LOCKOK, 0, NULL, isjson); 1931 #else 1932 message(io_data, MSG_LOCKDIS, 0, NULL, isjson); 1933 #endif 1934 } 1935 1936 static void apiversion(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 1937 { 1938 struct api_data *root = NULL; 1939 bool io_open; 1940 1941 message(io_data, MSG_VERSION, 0, NULL, isjson); 1942 io_open = io_add(io_data, isjson ? COMSTR JSON_VERSION : _VERSION COMSTR); 1943 1944 root = api_add_string(root, "CGMiner", VERSION, false); 1945 root = api_add_const(root, "API", APIVERSION, false); 1946 1947 root = print_data(io_data, root, isjson, false); 1948 if (isjson && io_open) 1949 io_close(io_data); 1950 } 1951 1952 static void minerconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 1953 { 1954 struct api_data *root = NULL; 1955 bool io_open; 1956 int asccount = 0; 1957 int pgacount = 0; 1958 1959 #ifdef HAVE_AN_ASIC 1960 asccount = numascs(); 1961 #endif 1962 1963 #ifdef HAVE_AN_FPGA 1964 pgacount = numpgas(); 1965 #endif 1966 1967 message(io_data, MSG_MINECONFIG, 0, NULL, isjson); 1968 io_open = io_add(io_data, isjson ? COMSTR JSON_MINECONFIG : _MINECONFIG COMSTR); 1969 1970 root = api_add_int(root, "ASC Count", &asccount, false); 1971 root = api_add_int(root, "PGA Count", &pgacount, false); 1972 root = api_add_int(root, "Pool Count", &total_pools, false); 1973 root = api_add_const(root, "Strategy", strategies[pool_strategy].s, false); 1974 root = api_add_int(root, "Log Interval", &opt_log_interval, false); 1975 root = api_add_const(root, "Device Code", DEVICECODE, false); 1976 root = api_add_const(root, "OS", OSINFO, false); 1977 #ifdef USE_USBUTILS 1978 if (hotplug_time == 0) 1979 root = api_add_const(root, "Hotplug", DISABLED, false); 1980 else 1981 root = api_add_int(root, "Hotplug", &hotplug_time, false); 1982 #else 1983 root = api_add_const(root, "Hotplug", NONE, false); 1984 #endif 1985 1986 root = print_data(io_data, root, isjson, false); 1987 if (isjson && io_open) 1988 io_close(io_data); 1989 } 1990 1991 #if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA) 1992 static const char *status2str(enum alive status) 1993 { 1994 switch (status) { 1995 case LIFE_WELL: 1996 return ALIVE; 1997 case LIFE_SICK: 1998 return SICK; 1999 case LIFE_DEAD: 2000 return DEAD; 2001 case LIFE_NOSTART: 2002 return NOSTART; 2003 case LIFE_INIT: 2004 return INIT; 2005 default: 2006 return UNKNOWN; 2007 } 2008 } 2009 #endif 2010 2011 #ifdef HAVE_AN_ASIC 2012 static void ascstatus(struct io_data *io_data, int asc, bool isjson, bool precom) 2013 { 2014 struct api_data *root = NULL; 2015 char *enabled; 2016 char *status; 2017 int numasc = numascs(); 2018 2019 if (numasc > 0 && asc >= 0 && asc < numasc) { 2020 int dev = ascdevice(asc); 2021 if (dev < 0) // Should never happen 2022 return; 2023 2024 struct cgpu_info *cgpu = get_devices(dev); 2025 float temp = cgpu->temp; 2026 double dev_runtime; 2027 2028 dev_runtime = cgpu_runtime(cgpu); 2029 2030 cgpu->utility = cgpu->accepted / dev_runtime * 60; 2031 2032 if (cgpu->deven != DEV_DISABLED) 2033 enabled = (char *)YES; 2034 else 2035 enabled = (char *)NO; 2036 2037 status = (char *)status2str(cgpu->status); 2038 2039 root = api_add_int(root, "ASC", &asc, false); 2040 root = api_add_string(root, "Name", cgpu->drv->name, false); 2041 root = api_add_int(root, "ID", &(cgpu->device_id), false); 2042 root = api_add_string(root, "Enabled", enabled, false); 2043 root = api_add_string(root, "Status", status, false); 2044 root = api_add_temp(root, "Temperature", &temp, false); 2045 double mhs = cgpu->total_mhashes / dev_runtime; 2046 root = api_add_mhs(root, "MHS av", &mhs, false); 2047 char mhsname[27]; 2048 sprintf(mhsname, "MHS %ds", opt_log_interval); 2049 root = api_add_mhs(root, mhsname, &(cgpu->rolling), false); 2050 root = api_add_mhs(root, "MHS 1m", &cgpu->rolling1, false); 2051 root = api_add_mhs(root, "MHS 5m", &cgpu->rolling5, false); 2052 root = api_add_mhs(root, "MHS 15m", &cgpu->rolling15, false); 2053 root = api_add_int(root, "Accepted", &(cgpu->accepted), false); 2054 root = api_add_int(root, "Rejected", &(cgpu->rejected), false); 2055 root = api_add_int(root, "Hardware Errors", &(cgpu->hw_errors), false); 2056 root = api_add_utility(root, "Utility", &(cgpu->utility), false); 2057 int last_share_pool = cgpu->last_share_pool_time > 0 ? 2058 cgpu->last_share_pool : -1; 2059 root = api_add_int(root, "Last Share Pool", &last_share_pool, false); 2060 root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); 2061 root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); 2062 root = api_add_int64(root, "Diff1 Work", &(cgpu->diff1), false); 2063 root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); 2064 root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); 2065 root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); 2066 #ifdef USE_USBUTILS 2067 root = api_add_bool(root, "No Device", &(cgpu->usbinfo.nodev), false); 2068 #endif 2069 root = api_add_time(root, "Last Valid Work", &(cgpu->last_device_valid_work), false); 2070 double hwp = (cgpu->hw_errors + cgpu->diff1) ? 2071 (double)(cgpu->hw_errors) / (double)(cgpu->hw_errors + cgpu->diff1) : 0; 2072 root = api_add_percent(root, "Device Hardware%", &hwp, false); 2073 double rejp = cgpu->diff1 ? 2074 (double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0; 2075 root = api_add_percent(root, "Device Rejected%", &rejp, false); 2076 root = api_add_elapsed(root, "Device Elapsed", &(dev_runtime), false); 2077 2078 root = print_data(io_data, root, isjson, precom); 2079 } 2080 } 2081 #endif 2082 2083 #ifdef HAVE_AN_FPGA 2084 static void pgastatus(struct io_data *io_data, int pga, bool isjson, bool precom) 2085 { 2086 struct api_data *root = NULL; 2087 char *enabled; 2088 char *status; 2089 int numpga = numpgas(); 2090 2091 if (numpga > 0 && pga >= 0 && pga < numpga) { 2092 int dev = pgadevice(pga); 2093 if (dev < 0) // Should never happen 2094 return; 2095 2096 struct cgpu_info *cgpu = get_devices(dev); 2097 double frequency = 0; 2098 float temp = cgpu->temp; 2099 struct timeval now; 2100 double dev_runtime; 2101 2102 if (cgpu->dev_start_tv.tv_sec == 0) 2103 dev_runtime = total_secs; 2104 else { 2105 cgtime(&now); 2106 dev_runtime = tdiff(&now, &(cgpu->dev_start_tv)); 2107 } 2108 2109 if (dev_runtime < 1.0) 2110 dev_runtime = 1.0; 2111 2112 #ifdef USE_MODMINER 2113 if (cgpu->drv->drv_id == DRIVER_modminer) 2114 frequency = cgpu->clock; 2115 #endif 2116 2117 cgpu->utility = cgpu->accepted / dev_runtime * 60; 2118 2119 if (cgpu->deven != DEV_DISABLED) 2120 enabled = (char *)YES; 2121 else 2122 enabled = (char *)NO; 2123 2124 status = (char *)status2str(cgpu->status); 2125 2126 root = api_add_int(root, "PGA", &pga, false); 2127 root = api_add_string(root, "Name", cgpu->drv->name, false); 2128 root = api_add_int(root, "ID", &(cgpu->device_id), false); 2129 root = api_add_string(root, "Enabled", enabled, false); 2130 root = api_add_string(root, "Status", status, false); 2131 root = api_add_temp(root, "Temperature", &temp, false); 2132 double mhs = cgpu->total_mhashes / dev_runtime; 2133 root = api_add_mhs(root, "MHS av", &mhs, false); 2134 char mhsname[27]; 2135 sprintf(mhsname, "MHS %ds", opt_log_interval); 2136 root = api_add_mhs(root, mhsname, &(cgpu->rolling), false); 2137 root = api_add_mhs(root, "MHS 1m", &cgpu->rolling1, false); 2138 root = api_add_mhs(root, "MHS 5m", &cgpu->rolling5, false); 2139 root = api_add_mhs(root, "MHS 15m", &cgpu->rolling15, false); 2140 root = api_add_int(root, "Accepted", &(cgpu->accepted), false); 2141 root = api_add_int(root, "Rejected", &(cgpu->rejected), false); 2142 root = api_add_int(root, "Hardware Errors", &(cgpu->hw_errors), false); 2143 root = api_add_utility(root, "Utility", &(cgpu->utility), false); 2144 int last_share_pool = cgpu->last_share_pool_time > 0 ? 2145 cgpu->last_share_pool : -1; 2146 root = api_add_int(root, "Last Share Pool", &last_share_pool, false); 2147 root = api_add_time(root, "Last Share Time", &(cgpu->last_share_pool_time), false); 2148 root = api_add_mhtotal(root, "Total MH", &(cgpu->total_mhashes), false); 2149 root = api_add_freq(root, "Frequency", &frequency, false); 2150 root = api_add_int64(root, "Diff1 Work", &(cgpu->diff1), false); 2151 root = api_add_diff(root, "Difficulty Accepted", &(cgpu->diff_accepted), false); 2152 root = api_add_diff(root, "Difficulty Rejected", &(cgpu->diff_rejected), false); 2153 root = api_add_diff(root, "Last Share Difficulty", &(cgpu->last_share_diff), false); 2154 #ifdef USE_USBUTILS 2155 root = api_add_bool(root, "No Device", &(cgpu->usbinfo.nodev), false); 2156 #endif 2157 root = api_add_time(root, "Last Valid Work", &(cgpu->last_device_valid_work), false); 2158 double hwp = (cgpu->hw_errors + cgpu->diff1) ? 2159 (double)(cgpu->hw_errors) / (double)(cgpu->hw_errors + cgpu->diff1) : 0; 2160 root = api_add_percent(root, "Device Hardware%", &hwp, false); 2161 double rejp = cgpu->diff1 ? 2162 (double)(cgpu->diff_rejected) / (double)(cgpu->diff1) : 0; 2163 root = api_add_percent(root, "Device Rejected%", &rejp, false); 2164 root = api_add_elapsed(root, "Device Elapsed", &(dev_runtime), false); 2165 2166 root = print_data(io_data, root, isjson, precom); 2167 } 2168 } 2169 #endif 2170 2171 static void devstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 2172 { 2173 bool io_open = false; 2174 int numasc = 0; 2175 int numpga = 0; 2176 #if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA) 2177 int devcount = 0; 2178 int i; 2179 #endif 2180 2181 #ifdef HAVE_AN_ASIC 2182 numasc = numascs(); 2183 #endif 2184 2185 #ifdef HAVE_AN_FPGA 2186 numpga = numpgas(); 2187 #endif 2188 2189 if (numpga == 0 && numasc == 0) { 2190 message(io_data, MSG_NODEVS, 0, NULL, isjson); 2191 return; 2192 } 2193 2194 2195 message(io_data, MSG_DEVS, 0, NULL, isjson); 2196 if (isjson) 2197 io_open = io_add(io_data, COMSTR JSON_DEVS); 2198 2199 #ifdef HAVE_AN_ASIC 2200 if (numasc > 0) { 2201 for (i = 0; i < numasc; i++) { 2202 ascstatus(io_data, i, isjson, isjson && devcount > 0); 2203 2204 devcount++; 2205 } 2206 } 2207 #endif 2208 2209 #ifdef HAVE_AN_FPGA 2210 if (numpga > 0) { 2211 for (i = 0; i < numpga; i++) { 2212 pgastatus(io_data, i, isjson, isjson && devcount > 0); 2213 2214 devcount++; 2215 } 2216 } 2217 #endif 2218 2219 if (isjson && io_open) 2220 io_close(io_data); 2221 } 2222 2223 static void edevstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 2224 { 2225 bool io_open = false; 2226 int numasc = 0; 2227 int numpga = 0; 2228 #if defined(HAVE_AN_ASIC) || defined(HAVE_AN_FPGA) 2229 int devcount = 0; 2230 int i; 2231 #endif 2232 #ifdef USE_USBUTILS 2233 time_t howoldsec = 0; 2234 #endif 2235 2236 #ifdef HAVE_AN_ASIC 2237 numasc = numascs(); 2238 #endif 2239 2240 #ifdef HAVE_AN_FPGA 2241 numpga = numpgas(); 2242 #endif 2243 2244 if (numpga == 0 && numasc == 0) { 2245 message(io_data, MSG_NODEVS, 0, NULL, isjson); 2246 return; 2247 } 2248 2249 #ifdef USE_USBUTILS 2250 if (param && *param) 2251 howoldsec = (time_t)atoi(param); 2252 #endif 2253 2254 message(io_data, MSG_DEVS, 0, NULL, isjson); 2255 if (isjson) 2256 io_open = io_add(io_data, COMSTR JSON_DEVS); 2257 2258 #ifdef HAVE_AN_ASIC 2259 if (numasc > 0) { 2260 for (i = 0; i < numasc; i++) { 2261 #ifdef USE_USBUTILS 2262 int dev = ascdevice(i); 2263 if (dev < 0) // Should never happen 2264 continue; 2265 2266 struct cgpu_info *cgpu = get_devices(dev); 2267 if (!cgpu) 2268 continue; 2269 if (cgpu->blacklisted) 2270 continue; 2271 if (cgpu->usbinfo.nodev) { 2272 if (howoldsec <= 0) 2273 continue; 2274 if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec) 2275 continue; 2276 } 2277 #endif 2278 2279 ascstatus(io_data, i, isjson, isjson && devcount > 0); 2280 2281 devcount++; 2282 } 2283 } 2284 #endif 2285 2286 #ifdef HAVE_AN_FPGA 2287 if (numpga > 0) { 2288 for (i = 0; i < numpga; i++) { 2289 #ifdef USE_USBUTILS 2290 int dev = pgadevice(i); 2291 if (dev < 0) // Should never happen 2292 continue; 2293 2294 struct cgpu_info *cgpu = get_devices(dev); 2295 if (!cgpu) 2296 continue; 2297 if (cgpu->blacklisted) 2298 continue; 2299 if (cgpu->usbinfo.nodev) { 2300 if (howoldsec <= 0) 2301 continue; 2302 if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec) 2303 continue; 2304 } 2305 #endif 2306 2307 pgastatus(io_data, i, isjson, isjson && devcount > 0); 2308 2309 devcount++; 2310 } 2311 } 2312 #endif 2313 2314 if (isjson && io_open) 2315 io_close(io_data); 2316 } 2317 2318 #ifdef HAVE_AN_FPGA 2319 static void pgadev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2320 { 2321 bool io_open = false; 2322 int numpga = numpgas(); 2323 int id; 2324 2325 if (numpga == 0) { 2326 message(io_data, MSG_PGANON, 0, NULL, isjson); 2327 return; 2328 } 2329 2330 if (param == NULL || *param == '\0') { 2331 message(io_data, MSG_MISID, 0, NULL, isjson); 2332 return; 2333 } 2334 2335 id = atoi(param); 2336 if (id < 0 || id >= numpga) { 2337 message(io_data, MSG_INVPGA, id, NULL, isjson); 2338 return; 2339 } 2340 2341 message(io_data, MSG_PGADEV, id, NULL, isjson); 2342 2343 if (isjson) 2344 io_open = io_add(io_data, COMSTR JSON_PGA); 2345 2346 pgastatus(io_data, id, isjson, false); 2347 2348 if (isjson && io_open) 2349 io_close(io_data); 2350 } 2351 2352 static void pgaenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2353 { 2354 struct cgpu_info *cgpu; 2355 int numpga = numpgas(); 2356 struct thr_info *thr; 2357 int pga; 2358 int id; 2359 int i; 2360 2361 if (numpga == 0) { 2362 message(io_data, MSG_PGANON, 0, NULL, isjson); 2363 return; 2364 } 2365 2366 if (param == NULL || *param == '\0') { 2367 message(io_data, MSG_MISID, 0, NULL, isjson); 2368 return; 2369 } 2370 2371 id = atoi(param); 2372 if (id < 0 || id >= numpga) { 2373 message(io_data, MSG_INVPGA, id, NULL, isjson); 2374 return; 2375 } 2376 2377 int dev = pgadevice(id); 2378 if (dev < 0) { // Should never happen 2379 message(io_data, MSG_INVPGA, id, NULL, isjson); 2380 return; 2381 } 2382 2383 cgpu = get_devices(dev); 2384 2385 applog(LOG_DEBUG, "API: request to pgaenable pgaid %d device %d %s%u", 2386 id, dev, cgpu->drv->name, cgpu->device_id); 2387 2388 if (cgpu->deven != DEV_DISABLED) { 2389 message(io_data, MSG_PGALRENA, id, NULL, isjson); 2390 return; 2391 } 2392 2393 #if 0 /* A DISABLED device wont change status FIXME: should disabling make it WELL? */ 2394 if (cgpu->status != LIFE_WELL) { 2395 message(io_data, MSG_PGAUNW, id, NULL, isjson); 2396 return; 2397 } 2398 #endif 2399 2400 #ifdef USE_USBUTILS 2401 if (cgpu->usbinfo.nodev) { 2402 message(io_data, MSG_PGAUSBNODEV, id, NULL, isjson); 2403 return; 2404 } 2405 #endif 2406 2407 for (i = 0; i < mining_threads; i++) { 2408 thr = get_thread(i); 2409 pga = thr->cgpu->cgminer_id; 2410 if (pga == dev) { 2411 cgpu->deven = DEV_ENABLED; 2412 applog(LOG_DEBUG, "API: Pushing sem post to thread %d", thr->id); 2413 cgsem_post(&thr->sem); 2414 } 2415 } 2416 2417 message(io_data, MSG_PGAENA, id, NULL, isjson); 2418 } 2419 2420 static void pgadisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2421 { 2422 struct cgpu_info *cgpu; 2423 int numpga = numpgas(); 2424 int id; 2425 2426 if (numpga == 0) { 2427 message(io_data, MSG_PGANON, 0, NULL, isjson); 2428 return; 2429 } 2430 2431 if (param == NULL || *param == '\0') { 2432 message(io_data, MSG_MISID, 0, NULL, isjson); 2433 return; 2434 } 2435 2436 id = atoi(param); 2437 if (id < 0 || id >= numpga) { 2438 message(io_data, MSG_INVPGA, id, NULL, isjson); 2439 return; 2440 } 2441 2442 int dev = pgadevice(id); 2443 if (dev < 0) { // Should never happen 2444 message(io_data, MSG_INVPGA, id, NULL, isjson); 2445 return; 2446 } 2447 2448 cgpu = get_devices(dev); 2449 2450 applog(LOG_DEBUG, "API: request to pgadisable pgaid %d device %d %s%u", 2451 id, dev, cgpu->drv->name, cgpu->device_id); 2452 2453 if (cgpu->deven == DEV_DISABLED) { 2454 message(io_data, MSG_PGALRDIS, id, NULL, isjson); 2455 return; 2456 } 2457 2458 cgpu->deven = DEV_DISABLED; 2459 2460 message(io_data, MSG_PGADIS, id, NULL, isjson); 2461 } 2462 2463 static void pgaidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2464 { 2465 struct cgpu_info *cgpu; 2466 struct device_drv *drv; 2467 int numpga = numpgas(); 2468 int id; 2469 2470 if (numpga == 0) { 2471 message(io_data, MSG_PGANON, 0, NULL, isjson); 2472 return; 2473 } 2474 2475 if (param == NULL || *param == '\0') { 2476 message(io_data, MSG_MISID, 0, NULL, isjson); 2477 return; 2478 } 2479 2480 id = atoi(param); 2481 if (id < 0 || id >= numpga) { 2482 message(io_data, MSG_INVPGA, id, NULL, isjson); 2483 return; 2484 } 2485 2486 int dev = pgadevice(id); 2487 if (dev < 0) { // Should never happen 2488 message(io_data, MSG_INVPGA, id, NULL, isjson); 2489 return; 2490 } 2491 2492 cgpu = get_devices(dev); 2493 drv = cgpu->drv; 2494 2495 if (!drv->identify_device) 2496 message(io_data, MSG_PGANOID, id, NULL, isjson); 2497 else { 2498 drv->identify_device(cgpu); 2499 message(io_data, MSG_PGAIDENT, id, NULL, isjson); 2500 } 2501 } 2502 #endif 2503 2504 static void poolstatus(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 2505 { 2506 struct api_data *root = NULL; 2507 bool io_open = false; 2508 char *status, *lp; 2509 int i; 2510 double sdiff0 = 0.0; 2511 2512 if (total_pools == 0) { 2513 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2514 return; 2515 } 2516 2517 message(io_data, MSG_POOL, 0, NULL, isjson); 2518 2519 if (isjson) 2520 io_open = io_add(io_data, COMSTR JSON_POOLS); 2521 2522 for (i = 0; i < total_pools; i++) { 2523 struct pool *pool = pools[i]; 2524 2525 if (pool->removed) 2526 continue; 2527 2528 switch (pool->enabled) { 2529 case POOL_DISABLED: 2530 status = (char *)DISABLED; 2531 break; 2532 case POOL_REJECTING: 2533 status = (char *)REJECTING; 2534 break; 2535 case POOL_ENABLED: 2536 if (pool->idle) 2537 status = (char *)DEAD; 2538 else 2539 status = (char *)ALIVE; 2540 break; 2541 default: 2542 status = (char *)UNKNOWN; 2543 break; 2544 } 2545 2546 if (pool->hdr_path) 2547 lp = (char *)YES; 2548 else 2549 lp = (char *)NO; 2550 2551 root = api_add_int(root, "POOL", &i, false); 2552 root = api_add_escape(root, "URL", pool->rpc_url, false); 2553 root = api_add_string(root, "Status", status, false); 2554 root = api_add_int(root, "Priority", &(pool->prio), false); 2555 root = api_add_int(root, "Quota", &pool->quota, false); 2556 root = api_add_string(root, "Long Poll", lp, false); 2557 root = api_add_uint(root, "Getworks", &(pool->getwork_requested), false); 2558 root = api_add_int64(root, "Accepted", &(pool->accepted), false); 2559 root = api_add_int64(root, "Rejected", &(pool->rejected), false); 2560 root = api_add_int(root, "Works", &pool->works, false); 2561 root = api_add_uint(root, "Discarded", &(pool->discarded_work), false); 2562 root = api_add_uint(root, "Stale", &(pool->stale_shares), false); 2563 root = api_add_uint(root, "Get Failures", &(pool->getfail_occasions), false); 2564 root = api_add_uint(root, "Remote Failures", &(pool->remotefail_occasions), false); 2565 root = api_add_escape(root, "User", pool->rpc_user, false); 2566 root = api_add_time(root, "Last Share Time", &(pool->last_share_time), false); 2567 root = api_add_int64(root, "Diff1 Shares", &(pool->diff1), false); 2568 if (pool->rpc_proxy) { 2569 root = api_add_const(root, "Proxy Type", proxytype(pool->rpc_proxytype), false); 2570 root = api_add_escape(root, "Proxy", pool->rpc_proxy, false); 2571 } else { 2572 root = api_add_const(root, "Proxy Type", BLANK, false); 2573 root = api_add_const(root, "Proxy", BLANK, false); 2574 } 2575 root = api_add_diff(root, "Difficulty Accepted", &(pool->diff_accepted), false); 2576 root = api_add_diff(root, "Difficulty Rejected", &(pool->diff_rejected), false); 2577 root = api_add_diff(root, "Difficulty Stale", &(pool->diff_stale), false); 2578 root = api_add_diff(root, "Last Share Difficulty", &(pool->last_share_diff), false); 2579 root = api_add_diff(root, "Work Difficulty", &(pool->cgminer_pool_stats.last_diff), false); 2580 root = api_add_bool(root, "Has Stratum", &(pool->has_stratum), false); 2581 root = api_add_bool(root, "Stratum Active", &(pool->stratum_active), false); 2582 if (pool->stratum_active) { 2583 root = api_add_escape(root, "Stratum URL", pool->stratum_url, false); 2584 root = api_add_diff(root, "Stratum Difficulty", &(pool->sdiff), false); 2585 } else { 2586 root = api_add_const(root, "Stratum URL", BLANK, false); 2587 root = api_add_diff(root, "Stratum Difficulty", &(sdiff0), false); 2588 } 2589 root = api_add_bool(root, "Has Vmask", &(pool->vmask), false); 2590 root = api_add_bool(root, "Has GBT", &(pool->has_gbt), false); 2591 root = api_add_uint64(root, "Best Share", &(pool->best_diff), true); 2592 double rejp = (pool->diff_accepted + pool->diff_rejected + pool->diff_stale) ? 2593 (double)(pool->diff_rejected) / (double)(pool->diff_accepted + pool->diff_rejected + pool->diff_stale) : 0; 2594 root = api_add_percent(root, "Pool Rejected%", &rejp, false); 2595 double stalep = (pool->diff_accepted + pool->diff_rejected + pool->diff_stale) ? 2596 (double)(pool->diff_stale) / (double)(pool->diff_accepted + pool->diff_rejected + pool->diff_stale) : 0; 2597 root = api_add_percent(root, "Pool Stale%", &stalep, false); 2598 root = api_add_uint64(root, "Bad Work", &(pool->bad_work), true); 2599 root = api_add_uint32(root, "Current Block Height", &(pool->current_height), true); 2600 uint32_t nversion = (uint32_t)strtoul(pool->bbversion, NULL, 16); 2601 root = api_add_uint32(root, "Current Block Version", &nversion, true); 2602 2603 root = print_data(io_data, root, isjson, isjson && (i > 0)); 2604 } 2605 2606 if (isjson && io_open) 2607 io_close(io_data); 2608 } 2609 2610 static void summary(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 2611 { 2612 struct api_data *root = NULL; 2613 bool io_open; 2614 double utility, mhs, work_utility; 2615 2616 message(io_data, MSG_SUMM, 0, NULL, isjson); 2617 io_open = io_add(io_data, isjson ? COMSTR JSON_SUMMARY : _SUMMARY COMSTR); 2618 2619 // stop hashmeter() changing some while copying 2620 mutex_lock(&hash_lock); 2621 2622 utility = total_accepted / ( total_secs ? total_secs : 1 ) * 60; 2623 mhs = total_mhashes_done / total_secs; 2624 work_utility = total_diff1 / ( total_secs ? total_secs : 1 ) * 60; 2625 2626 root = api_add_elapsed(root, "Elapsed", &(total_secs), true); 2627 root = api_add_mhs(root, "MHS av", &(mhs), false); 2628 char mhsname[27]; 2629 sprintf(mhsname, "MHS %ds", opt_log_interval); 2630 root = api_add_mhs(root, mhsname, &(total_rolling), false); 2631 root = api_add_mhs(root, "MHS 1m", &rolling1, false); 2632 root = api_add_mhs(root, "MHS 5m", &rolling5, false); 2633 root = api_add_mhs(root, "MHS 15m", &rolling15, false); 2634 root = api_add_uint(root, "Found Blocks", &(found_blocks), true); 2635 root = api_add_int64(root, "Getworks", &(total_getworks), true); 2636 root = api_add_int64(root, "Accepted", &(total_accepted), true); 2637 root = api_add_int64(root, "Rejected", &(total_rejected), true); 2638 root = api_add_int(root, "Hardware Errors", &(hw_errors), true); 2639 root = api_add_utility(root, "Utility", &(utility), false); 2640 root = api_add_int64(root, "Discarded", &(total_discarded), true); 2641 root = api_add_int64(root, "Stale", &(total_stale), true); 2642 root = api_add_uint(root, "Get Failures", &(total_go), true); 2643 root = api_add_uint(root, "Local Work", &(local_work), true); 2644 root = api_add_uint(root, "Remote Failures", &(total_ro), true); 2645 root = api_add_uint(root, "Network Blocks", &(new_blocks), true); 2646 root = api_add_mhtotal(root, "Total MH", &(total_mhashes_done), true); 2647 root = api_add_utility(root, "Work Utility", &(work_utility), false); 2648 root = api_add_diff(root, "Difficulty Accepted", &(total_diff_accepted), true); 2649 root = api_add_diff(root, "Difficulty Rejected", &(total_diff_rejected), true); 2650 root = api_add_diff(root, "Difficulty Stale", &(total_diff_stale), true); 2651 root = api_add_uint64(root, "Best Share", &(best_diff), true); 2652 double hwp = (hw_errors + total_diff1) ? 2653 (double)(hw_errors) / (double)(hw_errors + total_diff1) : 0; 2654 root = api_add_percent(root, "Device Hardware%", &hwp, false); 2655 double rejp = total_diff1 ? 2656 (double)(total_diff_rejected) / (double)(total_diff1) : 0; 2657 root = api_add_percent(root, "Device Rejected%", &rejp, false); 2658 double prejp = (total_diff_accepted + total_diff_rejected + total_diff_stale) ? 2659 (double)(total_diff_rejected) / (double)(total_diff_accepted + total_diff_rejected + total_diff_stale) : 0; 2660 root = api_add_percent(root, "Pool Rejected%", &prejp, false); 2661 double stalep = (total_diff_accepted + total_diff_rejected + total_diff_stale) ? 2662 (double)(total_diff_stale) / (double)(total_diff_accepted + total_diff_rejected + total_diff_stale) : 0; 2663 root = api_add_percent(root, "Pool Stale%", &stalep, false); 2664 root = api_add_time(root, "Last getwork", &last_getwork, false); 2665 2666 mutex_unlock(&hash_lock); 2667 2668 root = print_data(io_data, root, isjson, false); 2669 if (isjson && io_open) 2670 io_close(io_data); 2671 } 2672 2673 static void pgacount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 2674 { 2675 struct api_data *root = NULL; 2676 bool io_open; 2677 int count = 0; 2678 2679 #ifdef HAVE_AN_FPGA 2680 count = numpgas(); 2681 #endif 2682 2683 message(io_data, MSG_NUMPGA, 0, NULL, isjson); 2684 io_open = io_add(io_data, isjson ? COMSTR JSON_PGAS : _PGAS COMSTR); 2685 2686 root = api_add_int(root, "Count", &count, false); 2687 2688 root = print_data(io_data, root, isjson, false); 2689 if (isjson && io_open) 2690 io_close(io_data); 2691 } 2692 2693 static void switchpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2694 { 2695 struct pool *pool; 2696 int id; 2697 2698 if (total_pools == 0) { 2699 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2700 return; 2701 } 2702 2703 if (param == NULL || *param == '\0') { 2704 message(io_data, MSG_MISPID, 0, NULL, isjson); 2705 return; 2706 } 2707 2708 id = atoi(param); 2709 cg_rlock(&control_lock); 2710 if (id < 0 || id >= total_pools) { 2711 cg_runlock(&control_lock); 2712 message(io_data, MSG_INVPID, id, NULL, isjson); 2713 return; 2714 } 2715 2716 pool = pools[id]; 2717 pool->enabled = POOL_ENABLED; 2718 cg_runlock(&control_lock); 2719 switch_pools(pool); 2720 2721 message(io_data, MSG_SWITCHP, id, NULL, isjson); 2722 } 2723 2724 static void copyadvanceafter(char ch, char **param, char **buf) 2725 { 2726 #define src_p (*param) 2727 #define dst_b (*buf) 2728 2729 while (*src_p && *src_p != ch) { 2730 if (*src_p == '\\' && *(src_p+1) != '\0') 2731 src_p++; 2732 2733 *(dst_b++) = *(src_p++); 2734 } 2735 if (*src_p) 2736 src_p++; 2737 2738 *(dst_b++) = '\0'; 2739 } 2740 2741 static bool pooldetails(char *param, char **url, char **user, char **pass) 2742 { 2743 char *ptr, *buf; 2744 2745 ptr = buf = cgmalloc(strlen(param)+1); 2746 2747 *url = buf; 2748 2749 // copy url 2750 copyadvanceafter(',', ¶m, &buf); 2751 2752 if (!(*param)) // missing user 2753 goto exitsama; 2754 2755 *user = buf; 2756 2757 // copy user 2758 copyadvanceafter(',', ¶m, &buf); 2759 2760 if (!*param) // missing pass 2761 goto exitsama; 2762 2763 *pass = buf; 2764 2765 // copy pass 2766 copyadvanceafter(',', ¶m, &buf); 2767 2768 return true; 2769 2770 exitsama: 2771 free(ptr); 2772 return false; 2773 } 2774 2775 static void addpool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2776 { 2777 char *url, *user, *pass; 2778 struct pool *pool; 2779 char *ptr; 2780 2781 if (param == NULL || *param == '\0') { 2782 message(io_data, MSG_MISPDP, 0, NULL, isjson); 2783 return; 2784 } 2785 2786 if (!pooldetails(param, &url, &user, &pass)) { 2787 ptr = escape_string(param, isjson); 2788 message(io_data, MSG_INVPDP, 0, ptr, isjson); 2789 if (ptr != param) 2790 free(ptr); 2791 ptr = NULL; 2792 return; 2793 } 2794 2795 pool = add_pool(); 2796 detect_stratum(pool, url); 2797 add_pool_details(pool, true, url, user, pass); 2798 2799 ptr = escape_string(url, isjson); 2800 message(io_data, MSG_ADDPOOL, pool->pool_no, ptr, isjson); 2801 if (ptr != url) 2802 free(ptr); 2803 ptr = NULL; 2804 } 2805 2806 static void enablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2807 { 2808 struct pool *pool; 2809 int id; 2810 2811 if (total_pools == 0) { 2812 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2813 return; 2814 } 2815 2816 if (param == NULL || *param == '\0') { 2817 message(io_data, MSG_MISPID, 0, NULL, isjson); 2818 return; 2819 } 2820 2821 id = atoi(param); 2822 if (id < 0 || id >= total_pools) { 2823 message(io_data, MSG_INVPID, id, NULL, isjson); 2824 return; 2825 } 2826 2827 pool = pools[id]; 2828 if (pool->enabled == POOL_ENABLED) { 2829 message(io_data, MSG_ALRENAP, id, NULL, isjson); 2830 return; 2831 } 2832 2833 pool->enabled = POOL_ENABLED; 2834 if (pool->prio < current_pool()->prio) 2835 switch_pools(pool); 2836 2837 message(io_data, MSG_ENAPOOL, id, NULL, isjson); 2838 } 2839 2840 static void poolpriority(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2841 { 2842 char *ptr, *next; 2843 int i, pr, prio = 0; 2844 2845 // TODO: all cgminer code needs a mutex added everywhere for change 2846 // access to total_pools and also parts of the pools[] array, 2847 // just copying total_pools here wont solve that 2848 2849 if (total_pools == 0) { 2850 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2851 return; 2852 } 2853 2854 if (param == NULL || *param == '\0') { 2855 message(io_data, MSG_MISPID, 0, NULL, isjson); 2856 return; 2857 } 2858 2859 bool pools_changed[total_pools]; 2860 int new_prio[total_pools]; 2861 for (i = 0; i < total_pools; ++i) 2862 pools_changed[i] = false; 2863 2864 next = param; 2865 while (next && *next) { 2866 ptr = next; 2867 next = strchr(ptr, ','); 2868 if (next) 2869 *(next++) = '\0'; 2870 2871 i = atoi(ptr); 2872 if (i < 0 || i >= total_pools) { 2873 message(io_data, MSG_INVPID, i, NULL, isjson); 2874 return; 2875 } 2876 2877 if (pools_changed[i]) { 2878 message(io_data, MSG_DUPPID, i, NULL, isjson); 2879 return; 2880 } 2881 2882 pools_changed[i] = true; 2883 new_prio[i] = prio++; 2884 } 2885 2886 // Only change them if no errors 2887 for (i = 0; i < total_pools; i++) { 2888 if (pools_changed[i]) 2889 pools[i]->prio = new_prio[i]; 2890 } 2891 2892 // In priority order, cycle through the unchanged pools and append them 2893 for (pr = 0; pr < total_pools; pr++) 2894 for (i = 0; i < total_pools; i++) { 2895 if (!pools_changed[i] && pools[i]->prio == pr) { 2896 pools[i]->prio = prio++; 2897 pools_changed[i] = true; 2898 break; 2899 } 2900 } 2901 2902 if (current_pool()->prio) 2903 switch_pools(NULL); 2904 2905 message(io_data, MSG_POOLPRIO, 0, NULL, isjson); 2906 } 2907 2908 static void poolquota(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2909 { 2910 struct pool *pool; 2911 int quota, id; 2912 char *comma; 2913 2914 if (total_pools == 0) { 2915 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2916 return; 2917 } 2918 2919 if (param == NULL || *param == '\0') { 2920 message(io_data, MSG_MISPID, 0, NULL, isjson); 2921 return; 2922 } 2923 2924 comma = strchr(param, ','); 2925 if (!comma) { 2926 message(io_data, MSG_CONVAL, 0, param, isjson); 2927 return; 2928 } 2929 2930 *(comma++) = '\0'; 2931 2932 id = atoi(param); 2933 if (id < 0 || id >= total_pools) { 2934 message(io_data, MSG_INVPID, id, NULL, isjson); 2935 return; 2936 } 2937 pool = pools[id]; 2938 2939 quota = atoi(comma); 2940 if (quota < 0) { 2941 message(io_data, MSG_INVNEG, quota, pool->rpc_url, isjson); 2942 return; 2943 } 2944 2945 pool->quota = quota; 2946 adjust_quota_gcd(); 2947 message(io_data, MSG_SETQUOTA, quota, pool->rpc_url, isjson); 2948 } 2949 2950 static void disablepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2951 { 2952 struct pool *pool; 2953 int id; 2954 2955 if (total_pools == 0) { 2956 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2957 return; 2958 } 2959 2960 if (param == NULL || *param == '\0') { 2961 message(io_data, MSG_MISPID, 0, NULL, isjson); 2962 return; 2963 } 2964 2965 id = atoi(param); 2966 if (id < 0 || id >= total_pools) { 2967 message(io_data, MSG_INVPID, id, NULL, isjson); 2968 return; 2969 } 2970 2971 pool = pools[id]; 2972 if (pool->enabled == POOL_DISABLED) { 2973 message(io_data, MSG_ALRDISP, id, NULL, isjson); 2974 return; 2975 } 2976 2977 if (enabled_pools <= 1) { 2978 message(io_data, MSG_DISLASTP, id, NULL, isjson); 2979 return; 2980 } 2981 2982 pool->enabled = POOL_DISABLED; 2983 if (pool == current_pool()) 2984 switch_pools(NULL); 2985 2986 message(io_data, MSG_DISPOOL, id, NULL, isjson); 2987 } 2988 2989 static void removepool(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 2990 { 2991 struct pool *pool; 2992 char *rpc_url; 2993 bool dofree = false; 2994 int id; 2995 2996 if (total_pools == 0) { 2997 message(io_data, MSG_NOPOOL, 0, NULL, isjson); 2998 return; 2999 } 3000 3001 if (param == NULL || *param == '\0') { 3002 message(io_data, MSG_MISPID, 0, NULL, isjson); 3003 return; 3004 } 3005 3006 id = atoi(param); 3007 if (id < 0 || id >= total_pools) { 3008 message(io_data, MSG_INVPID, id, NULL, isjson); 3009 return; 3010 } 3011 3012 if (total_pools <= 1) { 3013 message(io_data, MSG_REMLASTP, id, NULL, isjson); 3014 return; 3015 } 3016 3017 pool = pools[id]; 3018 if (pool == current_pool()) 3019 switch_pools(NULL); 3020 3021 if (pool == current_pool()) { 3022 message(io_data, MSG_ACTPOOL, id, NULL, isjson); 3023 return; 3024 } 3025 3026 pool->enabled = POOL_DISABLED; 3027 rpc_url = escape_string(pool->rpc_url, isjson); 3028 if (rpc_url != pool->rpc_url) 3029 dofree = true; 3030 3031 remove_pool(pool); 3032 3033 message(io_data, MSG_REMPOOL, id, rpc_url, isjson); 3034 3035 if (dofree) 3036 free(rpc_url); 3037 rpc_url = NULL; 3038 } 3039 3040 void doquit(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3041 { 3042 if (isjson) 3043 io_put(io_data, JSON_ACTION JSON_BYE); 3044 else 3045 io_put(io_data, _BYE); 3046 3047 bye = true; 3048 do_a_quit = true; 3049 } 3050 3051 void dorestart(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3052 { 3053 if (isjson) 3054 io_put(io_data, JSON_ACTION JSON_RESTART); 3055 else 3056 io_put(io_data, _RESTART); 3057 3058 bye = true; 3059 do_a_restart = true; 3060 } 3061 3062 void privileged(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3063 { 3064 message(io_data, MSG_ACCOK, 0, NULL, isjson); 3065 } 3066 3067 void notifystatus(struct io_data *io_data, int device, struct cgpu_info *cgpu, bool isjson, __maybe_unused char group) 3068 { 3069 struct api_data *root = NULL; 3070 char *reason; 3071 3072 if (cgpu->device_last_not_well == 0) 3073 reason = REASON_NONE; 3074 else 3075 switch(cgpu->device_not_well_reason) { 3076 case REASON_THREAD_FAIL_INIT: 3077 reason = REASON_THREAD_FAIL_INIT_STR; 3078 break; 3079 case REASON_THREAD_ZERO_HASH: 3080 reason = REASON_THREAD_ZERO_HASH_STR; 3081 break; 3082 case REASON_THREAD_FAIL_QUEUE: 3083 reason = REASON_THREAD_FAIL_QUEUE_STR; 3084 break; 3085 case REASON_DEV_SICK_IDLE_60: 3086 reason = REASON_DEV_SICK_IDLE_60_STR; 3087 break; 3088 case REASON_DEV_DEAD_IDLE_600: 3089 reason = REASON_DEV_DEAD_IDLE_600_STR; 3090 break; 3091 case REASON_DEV_NOSTART: 3092 reason = REASON_DEV_NOSTART_STR; 3093 break; 3094 case REASON_DEV_OVER_HEAT: 3095 reason = REASON_DEV_OVER_HEAT_STR; 3096 break; 3097 case REASON_DEV_THERMAL_CUTOFF: 3098 reason = REASON_DEV_THERMAL_CUTOFF_STR; 3099 break; 3100 case REASON_DEV_COMMS_ERROR: 3101 reason = REASON_DEV_COMMS_ERROR_STR; 3102 break; 3103 default: 3104 reason = REASON_UNKNOWN_STR; 3105 break; 3106 } 3107 3108 // ALL counters (and only counters) must start the name with a '*' 3109 // Simplifies future external support for identifying new counters 3110 root = api_add_int(root, "NOTIFY", &device, false); 3111 root = api_add_string(root, "Name", cgpu->drv->name, false); 3112 root = api_add_int(root, "ID", &(cgpu->device_id), false); 3113 root = api_add_time(root, "Last Well", &(cgpu->device_last_well), false); 3114 root = api_add_time(root, "Last Not Well", &(cgpu->device_last_not_well), false); 3115 root = api_add_string(root, "Reason Not Well", reason, false); 3116 root = api_add_int(root, "*Thread Fail Init", &(cgpu->thread_fail_init_count), false); 3117 root = api_add_int(root, "*Thread Zero Hash", &(cgpu->thread_zero_hash_count), false); 3118 root = api_add_int(root, "*Thread Fail Queue", &(cgpu->thread_fail_queue_count), false); 3119 root = api_add_int(root, "*Dev Sick Idle 60s", &(cgpu->dev_sick_idle_60_count), false); 3120 root = api_add_int(root, "*Dev Dead Idle 600s", &(cgpu->dev_dead_idle_600_count), false); 3121 root = api_add_int(root, "*Dev Nostart", &(cgpu->dev_nostart_count), false); 3122 root = api_add_int(root, "*Dev Over Heat", &(cgpu->dev_over_heat_count), false); 3123 root = api_add_int(root, "*Dev Thermal Cutoff", &(cgpu->dev_thermal_cutoff_count), false); 3124 root = api_add_int(root, "*Dev Comms Error", &(cgpu->dev_comms_error_count), false); 3125 root = api_add_int(root, "*Dev Throttle", &(cgpu->dev_throttle_count), false); 3126 3127 root = print_data(io_data, root, isjson, isjson && (device > 0)); 3128 } 3129 3130 static void notify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, char group) 3131 { 3132 struct cgpu_info *cgpu; 3133 bool io_open = false; 3134 int i; 3135 3136 if (total_devices == 0) { 3137 message(io_data, MSG_NODEVS, 0, NULL, isjson); 3138 return; 3139 } 3140 3141 message(io_data, MSG_NOTIFY, 0, NULL, isjson); 3142 3143 if (isjson) 3144 io_open = io_add(io_data, COMSTR JSON_NOTIFY); 3145 3146 for (i = 0; i < total_devices; i++) { 3147 cgpu = get_devices(i); 3148 notifystatus(io_data, i, cgpu, isjson, group); 3149 } 3150 3151 if (isjson && io_open) 3152 io_close(io_data); 3153 } 3154 3155 static void devdetails(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3156 { 3157 struct api_data *root = NULL; 3158 bool io_open = false; 3159 struct cgpu_info *cgpu; 3160 int i; 3161 3162 if (total_devices == 0) { 3163 message(io_data, MSG_NODEVS, 0, NULL, isjson); 3164 return; 3165 } 3166 3167 message(io_data, MSG_DEVDETAILS, 0, NULL, isjson); 3168 3169 if (isjson) 3170 io_open = io_add(io_data, COMSTR JSON_DEVDETAILS); 3171 3172 for (i = 0; i < total_devices; i++) { 3173 cgpu = get_devices(i); 3174 3175 root = api_add_int(root, "DEVDETAILS", &i, false); 3176 root = api_add_string(root, "Name", cgpu->drv->name, false); 3177 root = api_add_int(root, "ID", &(cgpu->device_id), false); 3178 root = api_add_string(root, "Driver", cgpu->drv->dname, false); 3179 root = api_add_const(root, "Kernel", cgpu->kname ? : BLANK, false); 3180 root = api_add_const(root, "Model", cgpu->name ? : BLANK, false); 3181 root = api_add_const(root, "Device Path", cgpu->device_path ? : BLANK, false); 3182 3183 root = print_data(io_data, root, isjson, isjson && (i > 0)); 3184 } 3185 3186 if (isjson && io_open) 3187 io_close(io_data); 3188 } 3189 3190 void dosave(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3191 { 3192 char filename[PATH_MAX]; 3193 FILE *fcfg; 3194 char *ptr; 3195 3196 if (param == NULL || *param == '\0') { 3197 default_save_file(filename); 3198 param = filename; 3199 } 3200 3201 fcfg = fopen(param, "w"); 3202 if (!fcfg) { 3203 ptr = escape_string(param, isjson); 3204 message(io_data, MSG_BADFN, 0, ptr, isjson); 3205 if (ptr != param) 3206 free(ptr); 3207 ptr = NULL; 3208 return; 3209 } 3210 3211 write_config(fcfg); 3212 fclose(fcfg); 3213 3214 ptr = escape_string(param, isjson); 3215 message(io_data, MSG_SAVED, 0, ptr, isjson); 3216 if (ptr != param) 3217 free(ptr); 3218 ptr = NULL; 3219 } 3220 3221 static int itemstats(struct io_data *io_data, int i, char *id, struct cgminer_stats *stats, struct cgminer_pool_stats *pool_stats, struct api_data *extra, struct cgpu_info *cgpu, bool isjson) 3222 { 3223 struct api_data *root = NULL; 3224 3225 root = api_add_int(root, "STATS", &i, false); 3226 root = api_add_string(root, "ID", id, false); 3227 root = api_add_elapsed(root, "Elapsed", &(total_secs), false); 3228 root = api_add_uint32(root, "Calls", &(stats->getwork_calls), false); 3229 root = api_add_timeval(root, "Wait", &(stats->getwork_wait), false); 3230 root = api_add_timeval(root, "Max", &(stats->getwork_wait_max), false); 3231 root = api_add_timeval(root, "Min", &(stats->getwork_wait_min), false); 3232 3233 if (pool_stats) { 3234 root = api_add_uint32(root, "Pool Calls", &(pool_stats->getwork_calls), false); 3235 root = api_add_uint32(root, "Pool Attempts", &(pool_stats->getwork_attempts), false); 3236 root = api_add_timeval(root, "Pool Wait", &(pool_stats->getwork_wait), false); 3237 root = api_add_timeval(root, "Pool Max", &(pool_stats->getwork_wait_max), false); 3238 root = api_add_timeval(root, "Pool Min", &(pool_stats->getwork_wait_min), false); 3239 root = api_add_double(root, "Pool Av", &(pool_stats->getwork_wait_rolling), false); 3240 root = api_add_bool(root, "Work Had Roll Time", &(pool_stats->hadrolltime), false); 3241 root = api_add_bool(root, "Work Can Roll", &(pool_stats->canroll), false); 3242 root = api_add_bool(root, "Work Had Expire", &(pool_stats->hadexpire), false); 3243 root = api_add_uint32(root, "Work Roll Time", &(pool_stats->rolltime), false); 3244 root = api_add_diff(root, "Work Diff", &(pool_stats->last_diff), false); 3245 root = api_add_diff(root, "Min Diff", &(pool_stats->min_diff), false); 3246 root = api_add_diff(root, "Max Diff", &(pool_stats->max_diff), false); 3247 root = api_add_uint32(root, "Min Diff Count", &(pool_stats->min_diff_count), false); 3248 root = api_add_uint32(root, "Max Diff Count", &(pool_stats->max_diff_count), false); 3249 root = api_add_uint64(root, "Times Sent", &(pool_stats->times_sent), false); 3250 root = api_add_uint64(root, "Bytes Sent", &(pool_stats->bytes_sent), false); 3251 root = api_add_uint64(root, "Times Recv", &(pool_stats->times_received), false); 3252 root = api_add_uint64(root, "Bytes Recv", &(pool_stats->bytes_received), false); 3253 root = api_add_uint64(root, "Net Bytes Sent", &(pool_stats->net_bytes_sent), false); 3254 root = api_add_uint64(root, "Net Bytes Recv", &(pool_stats->net_bytes_received), false); 3255 } 3256 3257 if (extra) 3258 root = api_add_extra(root, extra); 3259 3260 if (cgpu) { 3261 #ifdef USE_USBUTILS 3262 char details[256]; 3263 3264 if (cgpu->usbinfo.pipe_count) 3265 snprintf(details, sizeof(details), 3266 "%"PRIu64" %"PRIu64"/%"PRIu64"/%"PRIu64" %lu", 3267 cgpu->usbinfo.pipe_count, 3268 cgpu->usbinfo.clear_err_count, 3269 cgpu->usbinfo.retry_err_count, 3270 cgpu->usbinfo.clear_fail_count, 3271 (unsigned long)(cgpu->usbinfo.last_pipe)); 3272 else 3273 strcpy(details, "0"); 3274 3275 root = api_add_string(root, "USB Pipe", details, true); 3276 3277 snprintf(details, sizeof(details), 3278 "r%"PRIu64" %.6f w%"PRIu64" %.6f", 3279 cgpu->usbinfo.read_delay_count, 3280 cgpu->usbinfo.total_read_delay, 3281 cgpu->usbinfo.write_delay_count, 3282 cgpu->usbinfo.total_write_delay); 3283 3284 root = api_add_string(root, "USB Delay", details, true); 3285 3286 if (cgpu->usbinfo.usb_tmo[0].count == 0 && 3287 cgpu->usbinfo.usb_tmo[1].count == 0 && 3288 cgpu->usbinfo.usb_tmo[2].count == 0) { 3289 snprintf(details, sizeof(details), 3290 "%"PRIu64" 0", cgpu->usbinfo.tmo_count); 3291 } else { 3292 snprintf(details, sizeof(details), 3293 "%"PRIu64" %d=%d/%d/%d/%"PRIu64"/%"PRIu64 3294 " %d=%d/%d/%d/%"PRIu64"/%"PRIu64 3295 " %d=%d/%d/%d/%"PRIu64"/%"PRIu64" ", 3296 cgpu->usbinfo.tmo_count, 3297 USB_TMO_0, cgpu->usbinfo.usb_tmo[0].count, 3298 cgpu->usbinfo.usb_tmo[0].min_tmo, 3299 cgpu->usbinfo.usb_tmo[0].max_tmo, 3300 cgpu->usbinfo.usb_tmo[0].total_over, 3301 cgpu->usbinfo.usb_tmo[0].total_tmo, 3302 USB_TMO_1, cgpu->usbinfo.usb_tmo[1].count, 3303 cgpu->usbinfo.usb_tmo[1].min_tmo, 3304 cgpu->usbinfo.usb_tmo[1].max_tmo, 3305 cgpu->usbinfo.usb_tmo[1].total_over, 3306 cgpu->usbinfo.usb_tmo[1].total_tmo, 3307 USB_TMO_2, cgpu->usbinfo.usb_tmo[2].count, 3308 cgpu->usbinfo.usb_tmo[2].min_tmo, 3309 cgpu->usbinfo.usb_tmo[2].max_tmo, 3310 cgpu->usbinfo.usb_tmo[2].total_over, 3311 cgpu->usbinfo.usb_tmo[2].total_tmo); 3312 } 3313 3314 root = api_add_string(root, "USB tmo", details, true); 3315 #endif 3316 } 3317 3318 root = print_data(io_data, root, isjson, isjson && (i > 0)); 3319 3320 return ++i; 3321 } 3322 3323 static void minerstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3324 { 3325 struct cgpu_info *cgpu; 3326 bool io_open = false; 3327 struct api_data *extra; 3328 char id[20]; 3329 int i, j; 3330 3331 message(io_data, MSG_MINESTATS, 0, NULL, isjson); 3332 3333 if (isjson) 3334 io_open = io_add(io_data, COMSTR JSON_MINESTATS); 3335 3336 i = 0; 3337 for (j = 0; j < total_devices; j++) { 3338 cgpu = get_devices(j); 3339 3340 if (cgpu && cgpu->drv) { 3341 if (cgpu->drv->get_api_stats) 3342 extra = cgpu->drv->get_api_stats(cgpu); 3343 else 3344 extra = NULL; 3345 3346 sprintf(id, "%s%d", cgpu->drv->name, cgpu->device_id); 3347 i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson); 3348 } 3349 } 3350 3351 for (j = 0; j < total_pools; j++) { 3352 struct pool *pool = pools[j]; 3353 3354 sprintf(id, "POOL%d", j); 3355 i = itemstats(io_data, i, id, &(pool->cgminer_stats), &(pool->cgminer_pool_stats), NULL, NULL, isjson); 3356 } 3357 3358 if (isjson && io_open) 3359 io_close(io_data); 3360 } 3361 3362 static void minerdebug(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3363 { 3364 struct cgpu_info *cgpu; 3365 bool io_open = false; 3366 struct api_data *extra; 3367 char id[20]; 3368 int i, j; 3369 3370 message(io_data, MSG_MINEDEBUG, 0, NULL, isjson); 3371 3372 if (isjson) 3373 io_open = io_add(io_data, COMSTR JSON_MINESTATS); 3374 3375 i = 0; 3376 for (j = 0; j < total_devices; j++) { 3377 cgpu = get_devices(j); 3378 3379 if (cgpu && cgpu->drv) { 3380 if (cgpu->drv->get_api_debug) 3381 extra = cgpu->drv->get_api_debug(cgpu); 3382 else 3383 extra = NULL; 3384 3385 sprintf(id, "%s%d", cgpu->drv->name, cgpu->device_id); 3386 i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson); 3387 } 3388 } 3389 3390 for (j = 0; j < total_pools; j++) { 3391 struct pool *pool = pools[j]; 3392 3393 sprintf(id, "POOL%d", j); 3394 i = itemstats(io_data, i, id, &(pool->cgminer_stats), &(pool->cgminer_pool_stats), NULL, NULL, isjson); 3395 } 3396 3397 if (isjson && io_open) 3398 io_close(io_data); 3399 } 3400 3401 static void minerestats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3402 { 3403 struct cgpu_info *cgpu; 3404 bool io_open = false; 3405 struct api_data *extra; 3406 char id[20]; 3407 int i, j; 3408 #ifdef USE_USBUTILS 3409 time_t howoldsec = 0; 3410 3411 if (param && *param) 3412 howoldsec = (time_t)atoi(param); 3413 #endif 3414 3415 message(io_data, MSG_MINESTATS, 0, NULL, isjson); 3416 if (isjson) 3417 io_open = io_add(io_data, COMSTR JSON_MINESTATS); 3418 3419 i = 0; 3420 for (j = 0; j < total_devices; j++) { 3421 cgpu = get_devices(j); 3422 if (!cgpu) 3423 continue; 3424 #ifdef USE_USBUTILS 3425 if (cgpu->blacklisted) 3426 continue; 3427 if (cgpu->usbinfo.nodev) { 3428 if (howoldsec <= 0) 3429 continue; 3430 if ((when - cgpu->usbinfo.last_nodev.tv_sec) >= howoldsec) 3431 continue; 3432 } 3433 #endif 3434 if (cgpu->drv) { 3435 if (cgpu->drv->get_api_stats) 3436 extra = cgpu->drv->get_api_stats(cgpu); 3437 else 3438 extra = NULL; 3439 3440 sprintf(id, "%s%d", cgpu->drv->name, cgpu->device_id); 3441 i = itemstats(io_data, i, id, &(cgpu->cgminer_stats), NULL, extra, cgpu, isjson); 3442 } 3443 } 3444 3445 if (isjson && io_open) 3446 io_close(io_data); 3447 } 3448 3449 static void failoveronly(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3450 { 3451 message(io_data, MSG_DEPRECATED, 0, param, isjson); 3452 } 3453 3454 static void minecoin(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3455 { 3456 struct api_data *root = NULL; 3457 bool io_open; 3458 3459 message(io_data, MSG_MINECOIN, 0, NULL, isjson); 3460 io_open = io_add(io_data, isjson ? COMSTR JSON_MINECOIN : _MINECOIN COMSTR); 3461 3462 root = api_add_const(root, "Hash Method", SHA256STR, false); 3463 3464 cg_rlock(&ch_lock); 3465 root = api_add_timeval(root, "Current Block Time", &block_timeval, true); 3466 root = api_add_string(root, "Current Block Hash", current_hash, true); 3467 cg_runlock(&ch_lock); 3468 3469 root = api_add_bool(root, "LP", &have_longpoll, false); 3470 root = api_add_diff(root, "Network Difficulty", ¤t_diff, true); 3471 3472 root = print_data(io_data, root, isjson, false); 3473 if (isjson && io_open) 3474 io_close(io_data); 3475 } 3476 3477 static void debugstate(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3478 { 3479 struct api_data *root = NULL; 3480 bool io_open; 3481 3482 if (param == NULL) 3483 param = (char *)BLANK; 3484 else 3485 *param = tolower(*param); 3486 3487 switch(*param) { 3488 case 's': 3489 opt_realquiet = true; 3490 break; 3491 case 'q': 3492 opt_quiet ^= true; 3493 break; 3494 case 'v': 3495 opt_log_output ^= true; 3496 if (opt_log_output) 3497 opt_quiet = false; 3498 break; 3499 case 'd': 3500 opt_debug ^= true; 3501 opt_log_output = opt_debug; 3502 if (opt_debug) 3503 opt_quiet = false; 3504 break; 3505 case 'r': 3506 opt_protocol ^= true; 3507 if (opt_protocol) 3508 opt_quiet = false; 3509 break; 3510 case 'p': 3511 want_per_device_stats ^= true; 3512 opt_log_output = want_per_device_stats; 3513 break; 3514 case 'n': 3515 opt_log_output = false; 3516 opt_debug = false; 3517 opt_quiet = false; 3518 opt_protocol = false; 3519 want_per_device_stats = false; 3520 opt_worktime = false; 3521 break; 3522 case 'w': 3523 opt_worktime ^= true; 3524 break; 3525 #ifdef _MEMORY_DEBUG 3526 case 'y': 3527 cgmemspeedup(); 3528 break; 3529 case 'z': 3530 cgmemrpt(); 3531 break; 3532 #endif 3533 default: 3534 // anything else just reports the settings 3535 break; 3536 } 3537 3538 message(io_data, MSG_DEBUGSET, 0, NULL, isjson); 3539 io_open = io_add(io_data, isjson ? COMSTR JSON_DEBUGSET : _DEBUGSET COMSTR); 3540 3541 root = api_add_bool(root, "Silent", &opt_realquiet, false); 3542 root = api_add_bool(root, "Quiet", &opt_quiet, false); 3543 root = api_add_bool(root, "Verbose", &opt_log_output, false); 3544 root = api_add_bool(root, "Debug", &opt_debug, false); 3545 root = api_add_bool(root, "RPCProto", &opt_protocol, false); 3546 root = api_add_bool(root, "PerDevice", &want_per_device_stats, false); 3547 root = api_add_bool(root, "WorkTime", &opt_worktime, false); 3548 3549 root = print_data(io_data, root, isjson, false); 3550 if (isjson && io_open) 3551 io_close(io_data); 3552 } 3553 3554 static void setconfig(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3555 { 3556 if (!strcasecmp(param, "queue") || ! strcasecmp(param, "scantime") || !strcasecmp(param, "expiry")) 3557 message(io_data, MSG_DEPRECATED, 0, param, isjson); 3558 3559 message(io_data, MSG_UNKCON, 0, param, isjson); 3560 } 3561 3562 static void usbstats(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3563 { 3564 struct api_data *root = NULL; 3565 3566 #ifdef USE_USBUTILS 3567 bool io_open = false; 3568 int count = 0; 3569 3570 root = api_usb_stats(&count); 3571 #endif 3572 3573 if (!root) { 3574 message(io_data, MSG_NOUSTA, 0, NULL, isjson); 3575 return; 3576 } 3577 3578 #ifdef USE_USBUTILS 3579 message(io_data, MSG_USBSTA, 0, NULL, isjson); 3580 3581 if (isjson) 3582 io_open = io_add(io_data, COMSTR JSON_USBSTATS); 3583 3584 root = print_data(io_data, root, isjson, false); 3585 3586 while (42) { 3587 root = api_usb_stats(&count); 3588 if (!root) 3589 break; 3590 3591 root = print_data(io_data, root, isjson, isjson); 3592 } 3593 3594 if (isjson && io_open) 3595 io_close(io_data); 3596 #endif 3597 } 3598 3599 #ifdef HAVE_AN_FPGA 3600 static void pgaset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3601 { 3602 struct cgpu_info *cgpu; 3603 struct device_drv *drv; 3604 char buf[TMPBUFSIZ]; 3605 int numpga = numpgas(); 3606 3607 if (numpga == 0) { 3608 message(io_data, MSG_PGANON, 0, NULL, isjson); 3609 return; 3610 } 3611 3612 if (param == NULL || *param == '\0') { 3613 message(io_data, MSG_MISID, 0, NULL, isjson); 3614 return; 3615 } 3616 3617 char *opt = strchr(param, ','); 3618 if (opt) 3619 *(opt++) = '\0'; 3620 if (!opt || !*opt) { 3621 message(io_data, MSG_MISPGAOPT, 0, NULL, isjson); 3622 return; 3623 } 3624 3625 int id = atoi(param); 3626 if (id < 0 || id >= numpga) { 3627 message(io_data, MSG_INVPGA, id, NULL, isjson); 3628 return; 3629 } 3630 3631 int dev = pgadevice(id); 3632 if (dev < 0) { // Should never happen 3633 message(io_data, MSG_INVPGA, id, NULL, isjson); 3634 return; 3635 } 3636 3637 cgpu = get_devices(dev); 3638 drv = cgpu->drv; 3639 3640 char *set = strchr(opt, ','); 3641 if (set) 3642 *(set++) = '\0'; 3643 3644 if (!drv->set_device) 3645 message(io_data, MSG_PGANOSET, id, NULL, isjson); 3646 else { 3647 char *ret = drv->set_device(cgpu, opt, set, buf); 3648 if (ret) { 3649 if (strcasecmp(opt, "help") == 0) 3650 message(io_data, MSG_PGAHELP, id, ret, isjson); 3651 else 3652 message(io_data, MSG_PGASETERR, id, ret, isjson); 3653 } else 3654 message(io_data, MSG_PGASETOK, id, NULL, isjson); 3655 } 3656 } 3657 #endif 3658 3659 static void dozero(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3660 { 3661 if (param == NULL || *param == '\0') { 3662 message(io_data, MSG_ZERMIS, 0, NULL, isjson); 3663 return; 3664 } 3665 3666 char *sum = strchr(param, ','); 3667 if (sum) 3668 *(sum++) = '\0'; 3669 if (!sum || !*sum) { 3670 message(io_data, MSG_MISBOOL, 0, NULL, isjson); 3671 return; 3672 } 3673 3674 bool all = false; 3675 bool bs = false; 3676 if (strcasecmp(param, "all") == 0) 3677 all = true; 3678 else if (strcasecmp(param, "bestshare") == 0) 3679 bs = true; 3680 3681 if (all == false && bs == false) { 3682 message(io_data, MSG_ZERINV, 0, param, isjson); 3683 return; 3684 } 3685 3686 *sum = tolower(*sum); 3687 if (*sum != 't' && *sum != 'f') { 3688 message(io_data, MSG_INVBOOL, 0, NULL, isjson); 3689 return; 3690 } 3691 3692 bool dosum = (*sum == 't'); 3693 if (dosum) 3694 print_summary(); 3695 3696 if (all) 3697 zero_stats(); 3698 if (bs) 3699 zero_bestshare(); 3700 3701 if (dosum) 3702 message(io_data, MSG_ZERSUM, 0, all ? "All" : "BestShare", isjson); 3703 else 3704 message(io_data, MSG_ZERNOSUM, 0, all ? "All" : "BestShare", isjson); 3705 } 3706 3707 static void dohotplug(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3708 { 3709 #ifdef USE_USBUTILS 3710 int value; 3711 3712 if (param == NULL || *param == '\0') { 3713 message(io_data, MSG_MISHPLG, 0, NULL, isjson); 3714 return; 3715 } 3716 3717 value = atoi(param); 3718 if (value < 0 || value > 9999) { 3719 message(io_data, MSG_INVHPLG, 0, param, isjson); 3720 return; 3721 } 3722 3723 hotplug_time = value; 3724 3725 if (value) 3726 message(io_data, MSG_HOTPLUG, value, NULL, isjson); 3727 else 3728 message(io_data, MSG_DISHPLG, 0, NULL, isjson); 3729 #else 3730 message(io_data, MSG_NOHPLG, 0, NULL, isjson); 3731 return; 3732 #endif 3733 } 3734 3735 #ifdef HAVE_AN_ASIC 3736 static void ascdev(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3737 { 3738 bool io_open = false; 3739 int numasc = numascs(); 3740 int id; 3741 3742 if (numasc == 0) { 3743 message(io_data, MSG_ASCNON, 0, NULL, isjson); 3744 return; 3745 } 3746 3747 if (param == NULL || *param == '\0') { 3748 message(io_data, MSG_MISID, 0, NULL, isjson); 3749 return; 3750 } 3751 3752 id = atoi(param); 3753 if (id < 0 || id >= numasc) { 3754 message(io_data, MSG_INVASC, id, NULL, isjson); 3755 return; 3756 } 3757 3758 message(io_data, MSG_ASCDEV, id, NULL, isjson); 3759 3760 if (isjson) 3761 io_open = io_add(io_data, COMSTR JSON_ASC); 3762 3763 ascstatus(io_data, id, isjson, false); 3764 3765 if (isjson && io_open) 3766 io_close(io_data); 3767 } 3768 3769 static void ascenable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3770 { 3771 struct cgpu_info *cgpu; 3772 int numasc = numascs(); 3773 struct thr_info *thr; 3774 int asc; 3775 int id; 3776 int i; 3777 3778 if (numasc == 0) { 3779 message(io_data, MSG_ASCNON, 0, NULL, isjson); 3780 return; 3781 } 3782 3783 if (param == NULL || *param == '\0') { 3784 message(io_data, MSG_MISID, 0, NULL, isjson); 3785 return; 3786 } 3787 3788 id = atoi(param); 3789 if (id < 0 || id >= numasc) { 3790 message(io_data, MSG_INVASC, id, NULL, isjson); 3791 return; 3792 } 3793 3794 int dev = ascdevice(id); 3795 if (dev < 0) { // Should never happen 3796 message(io_data, MSG_INVASC, id, NULL, isjson); 3797 return; 3798 } 3799 3800 cgpu = get_devices(dev); 3801 3802 applog(LOG_DEBUG, "API: request to ascenable ascid %d device %d %s%u", 3803 id, dev, cgpu->drv->name, cgpu->device_id); 3804 3805 if (cgpu->deven != DEV_DISABLED) { 3806 message(io_data, MSG_ASCLRENA, id, NULL, isjson); 3807 return; 3808 } 3809 3810 #if 0 /* A DISABLED device wont change status FIXME: should disabling make it WELL? */ 3811 if (cgpu->status != LIFE_WELL) { 3812 message(io_data, MSG_ASCUNW, id, NULL, isjson); 3813 return; 3814 } 3815 #endif 3816 3817 #ifdef USE_USBUTILS 3818 if (cgpu->usbinfo.nodev) { 3819 message(io_data, MSG_ASCUSBNODEV, id, NULL, isjson); 3820 return; 3821 } 3822 #endif 3823 3824 for (i = 0; i < mining_threads; i++) { 3825 thr = get_thread(i); 3826 asc = thr->cgpu->cgminer_id; 3827 if (asc == dev) { 3828 cgpu->deven = DEV_ENABLED; 3829 applog(LOG_DEBUG, "API: Pushing sem post to thread %d", thr->id); 3830 cgsem_post(&thr->sem); 3831 } 3832 } 3833 3834 message(io_data, MSG_ASCENA, id, NULL, isjson); 3835 } 3836 3837 static void ascdisable(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3838 { 3839 struct cgpu_info *cgpu; 3840 int numasc = numascs(); 3841 int id; 3842 3843 if (numasc == 0) { 3844 message(io_data, MSG_ASCNON, 0, NULL, isjson); 3845 return; 3846 } 3847 3848 if (param == NULL || *param == '\0') { 3849 message(io_data, MSG_MISID, 0, NULL, isjson); 3850 return; 3851 } 3852 3853 id = atoi(param); 3854 if (id < 0 || id >= numasc) { 3855 message(io_data, MSG_INVASC, id, NULL, isjson); 3856 return; 3857 } 3858 3859 int dev = ascdevice(id); 3860 if (dev < 0) { // Should never happen 3861 message(io_data, MSG_INVASC, id, NULL, isjson); 3862 return; 3863 } 3864 3865 cgpu = get_devices(dev); 3866 3867 applog(LOG_DEBUG, "API: request to ascdisable ascid %d device %d %s%u", 3868 id, dev, cgpu->drv->name, cgpu->device_id); 3869 3870 if (cgpu->deven == DEV_DISABLED) { 3871 message(io_data, MSG_ASCLRDIS, id, NULL, isjson); 3872 return; 3873 } 3874 3875 cgpu->deven = DEV_DISABLED; 3876 3877 message(io_data, MSG_ASCDIS, id, NULL, isjson); 3878 } 3879 3880 static void ascidentify(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, __maybe_unused char group) 3881 { 3882 struct cgpu_info *cgpu; 3883 struct device_drv *drv; 3884 int numasc = numascs(); 3885 int id; 3886 3887 if (numasc == 0) { 3888 message(io_data, MSG_ASCNON, 0, NULL, isjson); 3889 return; 3890 } 3891 3892 if (param == NULL || *param == '\0') { 3893 message(io_data, MSG_MISID, 0, NULL, isjson); 3894 return; 3895 } 3896 3897 id = atoi(param); 3898 if (id < 0 || id >= numasc) { 3899 message(io_data, MSG_INVASC, id, NULL, isjson); 3900 return; 3901 } 3902 3903 int dev = ascdevice(id); 3904 if (dev < 0) { // Should never happen 3905 message(io_data, MSG_INVASC, id, NULL, isjson); 3906 return; 3907 } 3908 3909 cgpu = get_devices(dev); 3910 drv = cgpu->drv; 3911 3912 if (!drv->identify_device) 3913 message(io_data, MSG_ASCNOID, id, NULL, isjson); 3914 else { 3915 drv->identify_device(cgpu); 3916 message(io_data, MSG_ASCIDENT, id, NULL, isjson); 3917 } 3918 } 3919 #endif 3920 3921 static void asccount(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3922 { 3923 struct api_data *root = NULL; 3924 bool io_open; 3925 int count = 0; 3926 3927 #ifdef HAVE_AN_ASIC 3928 count = numascs(); 3929 #endif 3930 3931 message(io_data, MSG_NUMASC, 0, NULL, isjson); 3932 io_open = io_add(io_data, isjson ? COMSTR JSON_ASCS : _ASCS COMSTR); 3933 3934 root = api_add_int(root, "Count", &count, false); 3935 3936 root = print_data(io_data, root, isjson, false); 3937 if (isjson && io_open) 3938 io_close(io_data); 3939 } 3940 3941 #ifdef HAVE_AN_ASIC 3942 static void ascset(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 3943 { 3944 struct cgpu_info *cgpu; 3945 struct device_drv *drv; 3946 char buf[TMPBUFSIZ]; 3947 int numasc = numascs(); 3948 3949 if (numasc == 0) { 3950 message(io_data, MSG_ASCNON, 0, NULL, isjson); 3951 return; 3952 } 3953 3954 if (param == NULL || *param == '\0') { 3955 message(io_data, MSG_MISID, 0, NULL, isjson); 3956 return; 3957 } 3958 3959 char *opt = strchr(param, ','); 3960 if (opt) 3961 *(opt++) = '\0'; 3962 if (!opt || !*opt) { 3963 message(io_data, MSG_MISASCOPT, 0, NULL, isjson); 3964 return; 3965 } 3966 3967 int id = atoi(param); 3968 if (id < 0 || id >= numasc) { 3969 message(io_data, MSG_INVASC, id, NULL, isjson); 3970 return; 3971 } 3972 3973 int dev = ascdevice(id); 3974 if (dev < 0) { // Should never happen 3975 message(io_data, MSG_INVASC, id, NULL, isjson); 3976 return; 3977 } 3978 3979 cgpu = get_devices(dev); 3980 drv = cgpu->drv; 3981 3982 char *set = strchr(opt, ','); 3983 if (set) 3984 *(set++) = '\0'; 3985 3986 if (!drv->set_device) 3987 message(io_data, MSG_ASCNOSET, id, NULL, isjson); 3988 else { 3989 char *ret = drv->set_device(cgpu, opt, set, buf); 3990 if (ret) { 3991 if (strcasecmp(opt, "help") == 0) 3992 message(io_data, MSG_ASCHELP, id, ret, isjson); 3993 else 3994 message(io_data, MSG_ASCSETERR, id, ret, isjson); 3995 } else 3996 message(io_data, MSG_ASCSETOK, id, NULL, isjson); 3997 } 3998 } 3999 #endif 4000 4001 static void lcddata(struct io_data *io_data, __maybe_unused SOCKETTYPE c, __maybe_unused char *param, bool isjson, __maybe_unused char group) 4002 { 4003 struct api_data *root = NULL; 4004 struct cgpu_info *cgpu; 4005 bool io_open; 4006 double ghs = 0.0, last_share_diff = 0.0; 4007 float temp = 0.0; 4008 time_t last_share_time = 0; 4009 time_t last_device_valid_work = 0; 4010 struct pool *pool = NULL; 4011 char *rpc_url = "none", *rpc_user = ""; 4012 int i; 4013 4014 message(io_data, MSG_LCD, 0, NULL, isjson); 4015 io_open = io_add(io_data, isjson ? COMSTR JSON_LCD : _LCD COMSTR); 4016 4017 // stop hashmeter() changing some while copying 4018 mutex_lock(&hash_lock); 4019 4020 root = api_add_elapsed(root, "Elapsed", &(total_secs), true); 4021 ghs = total_mhashes_done / total_secs / 1000.0; 4022 root = api_add_mhs(root, "GHS av", &ghs, true); 4023 ghs = rolling5 / 1000.0; 4024 root = api_add_mhs(root, "GHS 5m", &ghs, true); 4025 ghs = total_rolling / 1000.0; 4026 root = api_add_mhs(root, "GHS 5s", &ghs, true); 4027 4028 mutex_unlock(&hash_lock); 4029 4030 temp = 0; 4031 last_device_valid_work = 0; 4032 for (i = 0; i < total_devices; i++) { 4033 cgpu = get_devices(i); 4034 if (last_device_valid_work == 0 || 4035 last_device_valid_work < cgpu->last_device_valid_work) 4036 last_device_valid_work = cgpu->last_device_valid_work; 4037 if (temp < cgpu->temp) 4038 temp = cgpu->temp; 4039 } 4040 4041 last_share_time = 0; 4042 last_share_diff = 0; 4043 for (i = 0; i < total_pools; i++) { 4044 pool = pools[i]; 4045 4046 if (pool->removed) 4047 continue; 4048 4049 if (last_share_time == 0 || last_share_time < pool->last_share_time) { 4050 last_share_time = pool->last_share_time; 4051 last_share_diff = pool->last_share_diff; 4052 } 4053 } 4054 pool = current_pool(); 4055 if (pool) { 4056 rpc_url = pool->rpc_url; 4057 rpc_user = pool->rpc_user; 4058 } 4059 4060 root = api_add_temp(root, "Temperature", &temp, false); 4061 root = api_add_diff(root, "Last Share Difficulty", &last_share_diff, false); 4062 root = api_add_time(root, "Last Share Time", &last_share_time, false); 4063 root = api_add_uint64(root, "Best Share", &best_diff, true); 4064 root = api_add_time(root, "Last Valid Work", &last_device_valid_work, false); 4065 root = api_add_uint(root, "Found Blocks", &found_blocks, true); 4066 root = api_add_escape(root, "Current Pool", rpc_url, true); 4067 root = api_add_escape(root, "User", rpc_user, true); 4068 4069 root = print_data(io_data, root, isjson, false); 4070 if (isjson && io_open) 4071 io_close(io_data); 4072 } 4073 4074 static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group); 4075 4076 struct CMDS { 4077 char *name; 4078 void (*func)(struct io_data *, SOCKETTYPE, char *, bool, char); 4079 bool iswritemode; 4080 bool joinable; 4081 } cmds[] = { 4082 { "version", apiversion, false, true }, 4083 { "config", minerconfig, false, true }, 4084 { "devs", devstatus, false, true }, 4085 { "edevs", edevstatus, false, true }, 4086 { "pools", poolstatus, false, true }, 4087 { "summary", summary, false, true }, 4088 #ifdef HAVE_AN_FPGA 4089 { "pga", pgadev, false, false }, 4090 { "pgaenable", pgaenable, true, false }, 4091 { "pgadisable", pgadisable, true, false }, 4092 { "pgaidentify", pgaidentify, true, false }, 4093 #endif 4094 { "pgacount", pgacount, false, true }, 4095 { "switchpool", switchpool, true, false }, 4096 { "addpool", addpool, true, false }, 4097 { "poolpriority", poolpriority, true, false }, 4098 { "poolquota", poolquota, true, false }, 4099 { "enablepool", enablepool, true, false }, 4100 { "disablepool", disablepool, true, false }, 4101 { "removepool", removepool, true, false }, 4102 { "save", dosave, true, false }, 4103 { "quit", doquit, true, false }, 4104 { "privileged", privileged, true, false }, 4105 { "notify", notify, false, true }, 4106 { "devdetails", devdetails, false, true }, 4107 { "restart", dorestart, true, false }, 4108 { "stats", minerstats, false, true }, 4109 { "dbgstats", minerdebug, false, true }, 4110 { "estats", minerestats, false, true }, 4111 { "check", checkcommand, false, false }, 4112 { "failover-only", failoveronly, true, false }, 4113 { "coin", minecoin, false, true }, 4114 { "debug", debugstate, true, false }, 4115 { "setconfig", setconfig, true, false }, 4116 { "usbstats", usbstats, false, true }, 4117 #ifdef HAVE_AN_FPGA 4118 { "pgaset", pgaset, true, false }, 4119 #endif 4120 { "zero", dozero, true, false }, 4121 { "hotplug", dohotplug, true, false }, 4122 #ifdef HAVE_AN_ASIC 4123 { "asc", ascdev, false, false }, 4124 { "ascenable", ascenable, true, false }, 4125 { "ascdisable", ascdisable, true, false }, 4126 { "ascidentify", ascidentify, true, false }, 4127 { "ascset", ascset, true, false }, 4128 #endif 4129 { "asccount", asccount, false, true }, 4130 { "lcd", lcddata, false, true }, 4131 { "lockstats", lockstats, true, true }, 4132 { NULL, NULL, false, false } 4133 }; 4134 4135 static void checkcommand(struct io_data *io_data, __maybe_unused SOCKETTYPE c, char *param, bool isjson, char group) 4136 { 4137 struct api_data *root = NULL; 4138 bool io_open; 4139 char cmdbuf[100]; 4140 bool found, access; 4141 int i; 4142 4143 if (param == NULL || *param == '\0') { 4144 message(io_data, MSG_MISCHK, 0, NULL, isjson); 4145 return; 4146 } 4147 4148 found = false; 4149 access = false; 4150 for (i = 0; cmds[i].name != NULL; i++) { 4151 if (strcmp(cmds[i].name, param) == 0) { 4152 found = true; 4153 4154 sprintf(cmdbuf, "|%s|", param); 4155 if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) 4156 access = true; 4157 4158 break; 4159 } 4160 } 4161 4162 message(io_data, MSG_CHECK, 0, NULL, isjson); 4163 io_open = io_add(io_data, isjson ? COMSTR JSON_CHECK : _CHECK COMSTR); 4164 4165 root = api_add_const(root, "Exists", found ? YES : NO, false); 4166 root = api_add_const(root, "Access", access ? YES : NO, false); 4167 4168 root = print_data(io_data, root, isjson, false); 4169 if (isjson && io_open) 4170 io_close(io_data); 4171 } 4172 4173 static void head_join(struct io_data *io_data, char *cmdptr, bool isjson, bool *firstjoin) 4174 { 4175 char *ptr; 4176 4177 if (*firstjoin) { 4178 if (isjson) 4179 io_add(io_data, JSON0); 4180 *firstjoin = false; 4181 } else { 4182 if (isjson) 4183 io_add(io_data, JSON_BETWEEN_JOIN); 4184 } 4185 4186 // External supplied string 4187 ptr = escape_string(cmdptr, isjson); 4188 4189 if (isjson) { 4190 io_add(io_data, JSON1); 4191 io_add(io_data, ptr); 4192 io_add(io_data, JSON2); 4193 } else { 4194 io_add(io_data, JOIN_CMD); 4195 io_add(io_data, ptr); 4196 io_add(io_data, BETWEEN_JOIN); 4197 } 4198 4199 if (ptr != cmdptr) 4200 free(ptr); 4201 } 4202 4203 static void tail_join(struct io_data *io_data, bool isjson) 4204 { 4205 if (io_data->close) { 4206 io_add(io_data, JSON_CLOSE); 4207 io_data->close = false; 4208 } 4209 4210 if (isjson) { 4211 io_add(io_data, JSON_END); 4212 io_add(io_data, JSON3); 4213 } 4214 } 4215 4216 static void send_result(struct io_data *io_data, SOCKETTYPE c, bool isjson) 4217 { 4218 int count, sendc, res, tosend, len, n; 4219 char *buf = io_data->ptr; 4220 4221 strcpy(buf, io_data->ptr); 4222 4223 if (io_data->close) 4224 strcat(buf, JSON_CLOSE); 4225 4226 if (isjson) 4227 strcat(buf, JSON_END); 4228 4229 len = strlen(buf); 4230 tosend = len+1; 4231 4232 applog(LOG_DEBUG, "API: send reply: (%d) '%.10s%s'", tosend, buf, len > 10 ? "..." : BLANK); 4233 4234 count = sendc = 0; 4235 while (count < 5 && tosend > 0) { 4236 // allow 50ms per attempt 4237 struct timeval timeout = {0, 50000}; 4238 fd_set wd; 4239 4240 FD_ZERO(&wd); 4241 FD_SET(c, &wd); 4242 if ((res = select(c + 1, NULL, &wd, NULL, &timeout)) < 1) { 4243 applog(LOG_WARNING, "API: send select failed (%d)", res); 4244 return; 4245 } 4246 4247 n = send(c, buf, tosend, 0); 4248 sendc++; 4249 4250 if (SOCKETFAIL(n)) { 4251 count++; 4252 if (sock_blocks()) 4253 continue; 4254 4255 applog(LOG_WARNING, "API: send (%d:%d) failed: %s", len+1, (len+1 - tosend), SOCKERRMSG); 4256 4257 return; 4258 } else { 4259 if (sendc <= 1) { 4260 if (n == tosend) 4261 applog(LOG_DEBUG, "API: sent all of %d first go", tosend); 4262 else 4263 applog(LOG_DEBUG, "API: sent %d of %d first go", n, tosend); 4264 } else { 4265 if (n == tosend) 4266 applog(LOG_DEBUG, "API: sent all of remaining %d (sendc=%d)", tosend, sendc); 4267 else 4268 applog(LOG_DEBUG, "API: sent %d of remaining %d (sendc=%d)", n, tosend, sendc); 4269 } 4270 4271 tosend -= n; 4272 buf += n; 4273 4274 if (n == 0) 4275 count++; 4276 } 4277 } 4278 } 4279 4280 static void tidyup(__maybe_unused void *arg) 4281 { 4282 mutex_lock(&quit_restart_lock); 4283 4284 SOCKETTYPE *apisock = (SOCKETTYPE *)arg; 4285 4286 bye = true; 4287 4288 if (*apisock != INVSOCK) { 4289 shutdown(*apisock, SHUT_RDWR); 4290 CLOSESOCKET(*apisock); 4291 *apisock = INVSOCK; 4292 } 4293 4294 if (ipaccess != NULL) { 4295 free(ipaccess); 4296 ipaccess = NULL; 4297 } 4298 4299 io_free(); 4300 4301 mutex_unlock(&quit_restart_lock); 4302 } 4303 4304 /* 4305 * Interpret --api-groups G:cmd1:cmd2:cmd3,P:cmd4,*,... 4306 */ 4307 static void setup_groups() 4308 { 4309 char *api_groups = opt_api_groups ? opt_api_groups : (char *)BLANK; 4310 char *buf, *ptr, *next, *colon; 4311 char group; 4312 char commands[TMPBUFSIZ]; 4313 char cmdbuf[100]; 4314 char *cmd; 4315 bool addstar, did; 4316 int i; 4317 4318 buf = cgmalloc(strlen(api_groups) + 1); 4319 4320 strcpy(buf, api_groups); 4321 4322 next = buf; 4323 // for each group defined 4324 while (next && *next) { 4325 ptr = next; 4326 next = strchr(ptr, ','); 4327 if (next) 4328 *(next++) = '\0'; 4329 4330 // Validate the group 4331 if (*(ptr+1) != ':') { 4332 colon = strchr(ptr, ':'); 4333 if (colon) 4334 *colon = '\0'; 4335 quit(1, "API invalid group name '%s'", ptr); 4336 } 4337 4338 group = GROUP(*ptr); 4339 if (!VALIDGROUP(group)) 4340 quit(1, "API invalid group name '%c'", *ptr); 4341 4342 if (group == PRIVGROUP) 4343 quit(1, "API group name can't be '%c'", PRIVGROUP); 4344 4345 if (group == NOPRIVGROUP) 4346 quit(1, "API group name can't be '%c'", NOPRIVGROUP); 4347 4348 if (apigroups[GROUPOFFSET(group)].commands != NULL) 4349 quit(1, "API duplicate group name '%c'", *ptr); 4350 4351 ptr += 2; 4352 4353 // Validate the command list (and handle '*') 4354 cmd = &(commands[0]); 4355 *(cmd++) = SEPARATOR; 4356 *cmd = '\0'; 4357 addstar = false; 4358 while (ptr && *ptr) { 4359 colon = strchr(ptr, ':'); 4360 if (colon) 4361 *(colon++) = '\0'; 4362 4363 if (strcmp(ptr, "*") == 0) 4364 addstar = true; 4365 else { 4366 did = false; 4367 for (i = 0; cmds[i].name != NULL; i++) { 4368 if (strcasecmp(ptr, cmds[i].name) == 0) { 4369 did = true; 4370 break; 4371 } 4372 } 4373 if (did) { 4374 // skip duplicates 4375 sprintf(cmdbuf, "|%s|", cmds[i].name); 4376 if (strstr(commands, cmdbuf) == NULL) { 4377 strcpy(cmd, cmds[i].name); 4378 cmd += strlen(cmds[i].name); 4379 *(cmd++) = SEPARATOR; 4380 *cmd = '\0'; 4381 } 4382 } else { 4383 quit(1, "API unknown command '%s' in group '%c'", ptr, group); 4384 } 4385 } 4386 4387 ptr = colon; 4388 } 4389 4390 // * = allow all non-iswritemode commands 4391 if (addstar) { 4392 for (i = 0; cmds[i].name != NULL; i++) { 4393 if (cmds[i].iswritemode == false) { 4394 // skip duplicates 4395 sprintf(cmdbuf, "|%s|", cmds[i].name); 4396 if (strstr(commands, cmdbuf) == NULL) { 4397 strcpy(cmd, cmds[i].name); 4398 cmd += strlen(cmds[i].name); 4399 *(cmd++) = SEPARATOR; 4400 *cmd = '\0'; 4401 } 4402 } 4403 } 4404 } 4405 4406 ptr = apigroups[GROUPOFFSET(group)].commands = cgmalloc(strlen(commands) + 1); 4407 4408 strcpy(ptr, commands); 4409 } 4410 4411 // Now define R (NOPRIVGROUP) as all non-iswritemode commands 4412 cmd = &(commands[0]); 4413 *(cmd++) = SEPARATOR; 4414 *cmd = '\0'; 4415 for (i = 0; cmds[i].name != NULL; i++) { 4416 if (cmds[i].iswritemode == false) { 4417 strcpy(cmd, cmds[i].name); 4418 cmd += strlen(cmds[i].name); 4419 *(cmd++) = SEPARATOR; 4420 *cmd = '\0'; 4421 } 4422 } 4423 4424 ptr = apigroups[GROUPOFFSET(NOPRIVGROUP)].commands = cgmalloc(strlen(commands) + 1); 4425 4426 strcpy(ptr, commands); 4427 4428 // W (PRIVGROUP) is handled as a special case since it simply means all commands 4429 4430 free(buf); 4431 return; 4432 } 4433 4434 /* 4435 * Interpret [W:]IP[/Prefix][,[R|W:]IP2[/Prefix2][,...]] --api-allow option 4436 * ipv6 address should be enclosed with a pair of square brackets and the prefix left outside 4437 * special case of 0/0 allows /0 (means all IP addresses) 4438 */ 4439 #define ALLIP "0/0" 4440 /* 4441 * N.B. IP4 addresses are by Definition 32bit big endian on all platforms 4442 */ 4443 static void setup_ipaccess() 4444 { 4445 char *buf, *ptr, *comma, *slash, *end, *dot; 4446 int ipcount, mask, i, shift; 4447 char tmp[64], original[64]; 4448 bool ipv6 = false; 4449 char group; 4450 4451 buf = cgmalloc(strlen(opt_api_allow) + 1); 4452 4453 strcpy(buf, opt_api_allow); 4454 4455 ipcount = 1; 4456 ptr = buf; 4457 while (*ptr) 4458 if (*(ptr++) == ',') 4459 ipcount++; 4460 4461 // possibly more than needed, but never less 4462 ipaccess = cgcalloc(ipcount, sizeof(struct IPACCESS)); 4463 4464 ips = 0; 4465 ptr = buf; 4466 while (ptr && *ptr) { 4467 while (*ptr == ' ' || *ptr == '\t') 4468 ptr++; 4469 4470 if (*ptr == ',') { 4471 ptr++; 4472 continue; 4473 } 4474 4475 comma = strchr(ptr, ','); 4476 if (comma) 4477 *(comma++) = '\0'; 4478 4479 strncpy(original, ptr, sizeof(original)); 4480 original[sizeof(original)-1] = '\0'; 4481 group = NOPRIVGROUP; 4482 4483 if (isalpha(*ptr) && *(ptr+1) == ':') { 4484 if (DEFINEDGROUP(*ptr)) 4485 group = GROUP(*ptr); 4486 4487 ptr += 2; 4488 } 4489 4490 ipaccess[ips].group = group; 4491 4492 if (strcmp(ptr, ALLIP) == 0) { 4493 for (i = 0; i < 16; i++) { 4494 ipaccess[ips].ip.s6_addr[i] = 0; 4495 ipaccess[ips].mask.s6_addr[i] = 0; 4496 } 4497 } 4498 else { 4499 end = strchr(ptr, '/'); 4500 if (!end) { 4501 for (i = 0; i < 16; i++) 4502 ipaccess[ips].mask.s6_addr[i] = 0xff; 4503 end = ptr + strlen(ptr); 4504 } 4505 slash = end--; 4506 if (*ptr == '[' && *end == ']') { 4507 *(ptr++) = '\0'; 4508 *(end--) = '\0'; 4509 ipv6 = true; 4510 } 4511 else 4512 ipv6 = false; 4513 if (*slash) { 4514 *(slash++) = '\0'; 4515 mask = atoi(slash); 4516 if (mask < 1 || (mask += ipv6 ? 0 : 96) > 128) { 4517 applog(LOG_ERR, "API: ignored address with " 4518 "invalid mask (%d) '%s'", 4519 mask, original); 4520 goto popipo; // skip invalid/zero 4521 } 4522 4523 for (i = 0; i < 16; i++) 4524 ipaccess[ips].mask.s6_addr[i] = 0; 4525 4526 i = 0; 4527 shift = 7; 4528 while (mask-- > 0) { 4529 ipaccess[ips].mask.s6_addr[i] |= 1 << shift; 4530 if (shift-- == 0) { 4531 i++; 4532 shift = 7; 4533 } 4534 } 4535 } 4536 4537 for (i = 0; i < 16; i++) 4538 ipaccess[ips].ip.s6_addr[i] = 0; // missing default to '[::]' 4539 if (ipv6) { 4540 if (INET_PTON(AF_INET6, ptr, &(ipaccess[ips].ip)) != 1) { 4541 applog(LOG_ERR, "API: ignored invalid " 4542 "IPv6 address '%s'", 4543 original); 4544 goto popipo; 4545 } 4546 } 4547 else { 4548 /* v4 mapped v6 address, 4549 * such as "::ffff:255.255.255.255" 4550 * but pad on extra missing .0 as needed */ 4551 dot = strchr(ptr, '.'); 4552 if (!dot) { 4553 snprintf(tmp, sizeof(tmp), 4554 "::ffff:%s.0.0.0", 4555 ptr); 4556 } else { 4557 dot = strchr(dot+1, '.'); 4558 if (!dot) { 4559 snprintf(tmp, sizeof(tmp), 4560 "::ffff:%s.0.0", 4561 ptr); 4562 } else { 4563 dot = strchr(dot+1, '.'); 4564 if (!dot) { 4565 snprintf(tmp, sizeof(tmp), 4566 "::ffff:%s.0", 4567 ptr); 4568 } else { 4569 snprintf(tmp, sizeof(tmp), 4570 "::ffff:%s", 4571 ptr); 4572 } 4573 } 4574 } 4575 if (INET_PTON(AF_INET6, tmp, &(ipaccess[ips].ip)) != 1) { 4576 applog(LOG_ERR, "API: ignored invalid " 4577 "IPv4 address '%s' (as %s)", 4578 original, tmp); 4579 goto popipo; 4580 } 4581 } 4582 for (i = 0; i < 16; i++) 4583 ipaccess[ips].ip.s6_addr[i] &= ipaccess[ips].mask.s6_addr[i]; 4584 } 4585 4586 ips++; 4587 popipo: 4588 ptr = comma; 4589 } 4590 4591 free(buf); 4592 } 4593 4594 static void *quit_thread(__maybe_unused void *userdata) 4595 { 4596 // allow thread creator to finish whatever it's doing 4597 mutex_lock(&quit_restart_lock); 4598 mutex_unlock(&quit_restart_lock); 4599 4600 if (opt_debug) 4601 applog(LOG_DEBUG, "API: killing cgminer"); 4602 4603 kill_work(); 4604 4605 return NULL; 4606 } 4607 4608 static void *restart_thread(__maybe_unused void *userdata) 4609 { 4610 // allow thread creator to finish whatever it's doing 4611 mutex_lock(&quit_restart_lock); 4612 mutex_unlock(&quit_restart_lock); 4613 4614 if (opt_debug) 4615 applog(LOG_DEBUG, "API: restarting cgminer"); 4616 4617 app_restart(); 4618 4619 return NULL; 4620 } 4621 4622 static bool check_connect(struct sockaddr_storage *cli, char **connectaddr, char *group) 4623 { 4624 bool addrok = false; 4625 int i, j; 4626 bool match; 4627 char tmp[30]; 4628 struct in6_addr client_ip; 4629 4630 *connectaddr = cgmalloc(INET6_ADDRSTRLEN); 4631 getnameinfo((struct sockaddr *)cli, sizeof(*cli), 4632 *connectaddr, INET6_ADDRSTRLEN, NULL, 0, NI_NUMERICHOST); 4633 4634 // v4 mapped v6 address, such as "::ffff:255.255.255.255" 4635 if (cli->ss_family == AF_INET) { 4636 sprintf(tmp, "::ffff:%s", *connectaddr); 4637 INET_PTON(AF_INET6, tmp, &client_ip); 4638 } 4639 else 4640 INET_PTON(AF_INET6, *connectaddr, &client_ip); 4641 4642 *group = NOPRIVGROUP; 4643 if (opt_api_allow) { 4644 for (i = 0; i < ips; i++) { 4645 match = true; 4646 for (j = 0; j < 16; j++) { 4647 if ((client_ip.s6_addr[j] & ipaccess[i].mask.s6_addr[j]) 4648 != ipaccess[i].ip.s6_addr[j]) { 4649 match = false; 4650 break; 4651 } 4652 } 4653 if (match) { 4654 addrok = true; 4655 *group = ipaccess[i].group; 4656 break; 4657 } 4658 } 4659 } else { 4660 if (opt_api_network) 4661 addrok = true; 4662 else 4663 addrok = (strcmp(*connectaddr, localaddr) == 0) 4664 || IN6_IS_ADDR_LOOPBACK(&client_ip); 4665 } 4666 4667 return addrok; 4668 } 4669 4670 static void mcast() 4671 { 4672 struct sockaddr_storage came_from; 4673 time_t bindstart; 4674 char *binderror; 4675 SOCKETTYPE mcast_sock = INVSOCK; 4676 SOCKETTYPE reply_sock = INVSOCK; 4677 socklen_t came_from_siz; 4678 char *connectaddr; 4679 ssize_t rep; 4680 int bound; 4681 int count; 4682 int reply_port; 4683 bool addrok; 4684 char group; 4685 4686 char port_s[10], came_from_port[10]; 4687 struct addrinfo hints, *res, *host, *client; 4688 4689 char expect[] = "cgminer-"; // first 8 bytes constant 4690 char *expect_code; 4691 size_t expect_code_len; 4692 char buf[1024]; 4693 char replybuf[1024]; 4694 4695 sprintf(port_s, "%d", opt_api_mcast_port); 4696 memset(&hints, 0, sizeof(hints)); 4697 hints.ai_family = AF_UNSPEC; 4698 if (getaddrinfo(opt_api_mcast_addr, port_s, &hints, &res) != 0) 4699 quit(1, "Invalid API Multicast Address"); 4700 host = res; 4701 while (host != NULL) { 4702 mcast_sock = socket(res->ai_family, SOCK_DGRAM, 0); 4703 if (mcast_sock > 0) 4704 break; 4705 host = host->ai_next; 4706 } 4707 if (mcast_sock == INVSOCK) { 4708 freeaddrinfo(res); 4709 quit(1, "API mcast could not open socket"); 4710 } 4711 4712 int optval = 1; 4713 if (SOCKETFAIL(setsockopt(mcast_sock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval)))) { 4714 applog(LOG_ERR, "API mcast setsockopt SO_REUSEADDR failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); 4715 goto die; 4716 } 4717 4718 // try for more than 1 minute ... in case the old one hasn't completely gone yet 4719 bound = 0; 4720 bindstart = time(NULL); 4721 while (bound == 0) { 4722 if (SOCKETFAIL(bind(mcast_sock, host->ai_addr, host->ai_addrlen))) { 4723 binderror = SOCKERRMSG; 4724 if ((time(NULL) - bindstart) > 61) 4725 break; 4726 else 4727 cgsleep_ms(30000); 4728 } else 4729 bound = 1; 4730 } 4731 4732 if (bound == 0) { 4733 applog(LOG_ERR, "API mcast bind to port %d failed (%s)%s", opt_api_mcast_port, binderror, MUNAVAILABLE); 4734 goto die; 4735 } 4736 4737 switch (host->ai_family) { 4738 case AF_INET: { 4739 struct ip_mreq grp; 4740 memset(&grp, 0, sizeof(grp)); 4741 grp.imr_multiaddr.s_addr = ((struct sockaddr_in *)(host->ai_addr))->sin_addr.s_addr; 4742 grp.imr_interface.s_addr = INADDR_ANY; 4743 4744 if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, 4745 (void *)(&grp), sizeof(grp)))) { 4746 applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); 4747 goto die; 4748 } 4749 break; 4750 } 4751 case AF_INET6: { 4752 struct ipv6_mreq grp; 4753 memcpy(&grp.ipv6mr_multiaddr, &(((struct sockaddr_in6 *)(host->ai_addr))->sin6_addr), 4754 sizeof(struct in6_addr)); 4755 grp.ipv6mr_interface= 0; 4756 4757 if (SOCKETFAIL(setsockopt(mcast_sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, 4758 (void *)(&grp), sizeof(grp)))) { 4759 applog(LOG_ERR, "API mcast join failed (%s)%s", SOCKERRMSG, MUNAVAILABLE); 4760 goto die; 4761 } 4762 break; 4763 } 4764 default: 4765 break; 4766 } 4767 freeaddrinfo(res); 4768 4769 expect_code_len = sizeof(expect) + strlen(opt_api_mcast_code); 4770 expect_code = cgmalloc(expect_code_len + 1); 4771 snprintf(expect_code, expect_code_len+1, "%s%s-", expect, opt_api_mcast_code); 4772 4773 count = 0; 4774 while (80085) { 4775 cgsleep_ms(1000); 4776 4777 count++; 4778 came_from_siz = sizeof(came_from); 4779 if (SOCKETFAIL(rep = recvfrom(mcast_sock, buf, sizeof(buf) - 1, 4780 0, (struct sockaddr *)(&came_from), &came_from_siz))) { 4781 applog(LOG_DEBUG, "API mcast failed count=%d (%s) (%d)", 4782 count, SOCKERRMSG, (int)mcast_sock); 4783 continue; 4784 } 4785 4786 addrok = check_connect(&came_from, &connectaddr, &group); 4787 applog(LOG_DEBUG, "API mcast from %s - %s", 4788 connectaddr, addrok ? "Accepted" : "Ignored"); 4789 if (!addrok) 4790 continue; 4791 4792 buf[rep] = '\0'; 4793 if (rep > 0 && buf[rep-1] == '\n') 4794 buf[--rep] = '\0'; 4795 4796 getnameinfo((struct sockaddr *)(&came_from), came_from_siz, 4797 NULL, 0, came_from_port, sizeof(came_from_port), NI_NUMERICHOST); 4798 4799 applog(LOG_DEBUG, "API mcast request rep=%d (%s) from [%s]:%s", 4800 (int)rep, buf, connectaddr, came_from_port); 4801 4802 if ((size_t)rep > expect_code_len && memcmp(buf, expect_code, expect_code_len) == 0) { 4803 reply_port = atoi(&buf[expect_code_len]); 4804 if (reply_port < 1 || reply_port > 65535) { 4805 applog(LOG_DEBUG, "API mcast request ignored - invalid port (%s)", 4806 &buf[expect_code_len]); 4807 } else { 4808 applog(LOG_DEBUG, "API mcast request OK port %s=%d", 4809 &buf[expect_code_len], reply_port); 4810 4811 if (getaddrinfo(connectaddr, &buf[expect_code_len], &hints, &res) != 0) { 4812 applog(LOG_ERR, "Invalid client address %s", connectaddr); 4813 continue; 4814 } 4815 client = res; 4816 while (client) { 4817 reply_sock = socket(res->ai_family, SOCK_DGRAM, 0); 4818 if (mcast_sock > 0) 4819 break; 4820 client = client->ai_next; 4821 } 4822 if (reply_sock == INVSOCK) { 4823 freeaddrinfo(res); 4824 applog(LOG_ERR, "API mcast could not open socket to client %s", connectaddr); 4825 continue; 4826 } 4827 4828 snprintf(replybuf, sizeof(replybuf), 4829 "cgm-" API_MCAST_CODE "-%d-%s", 4830 opt_api_port, opt_api_mcast_des); 4831 4832 rep = sendto(reply_sock, replybuf, strlen(replybuf)+1, 4833 0, client->ai_addr, client->ai_addrlen); 4834 freeaddrinfo(res); 4835 if (SOCKETFAIL(rep)) { 4836 applog(LOG_DEBUG, "API mcast send reply failed (%s) (%d)", 4837 SOCKERRMSG, (int)reply_sock); 4838 } else { 4839 applog(LOG_DEBUG, "API mcast send reply (%s) succeeded (%d) (%d)", 4840 replybuf, (int)rep, (int)reply_sock); 4841 } 4842 4843 CLOSESOCKET(reply_sock); 4844 } 4845 } else 4846 applog(LOG_DEBUG, "API mcast request was no good"); 4847 } 4848 4849 die: 4850 4851 CLOSESOCKET(mcast_sock); 4852 } 4853 4854 static void *mcast_thread(void *userdata) 4855 { 4856 struct thr_info *mythr = userdata; 4857 4858 pthread_detach(pthread_self()); 4859 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL); 4860 4861 RenameThread("APIMcast"); 4862 4863 mcast(); 4864 4865 PTH(mythr) = 0L; 4866 4867 return NULL; 4868 } 4869 4870 void mcast_init() 4871 { 4872 struct thr_info *thr; 4873 4874 thr = cgcalloc(1, sizeof(*thr)); 4875 4876 if (thr_info_create(thr, NULL, mcast_thread, thr)) 4877 quit(1, "API mcast thread create failed"); 4878 } 4879 4880 #ifdef USE_BITMAIN_SOC 4881 void reCalculateAVG() 4882 { 4883 new_total_mhashes_done = total_mhashes_done; 4884 if(total_secs>0) 4885 new_total_secs = total_secs-1; 4886 else new_total_secs=total_secs; 4887 } 4888 #endif 4889 4890 void api(int api_thr_id) 4891 { 4892 struct io_data *io_data; 4893 struct thr_info bye_thr; 4894 char buf[TMPBUFSIZ]; 4895 char param_buf[TMPBUFSIZ]; 4896 SOCKETTYPE c; 4897 int n, bound; 4898 char *connectaddr; 4899 char *binderror; 4900 time_t bindstart; 4901 short int port = opt_api_port; 4902 char port_s[10]; 4903 struct sockaddr_storage cli; 4904 socklen_t clisiz; 4905 char cmdbuf[100]; 4906 char *cmd = NULL; 4907 char *param; 4908 bool addrok; 4909 char group; 4910 json_error_t json_err; 4911 json_t *json_config; 4912 json_t *json_val; 4913 bool isjson; 4914 bool did, isjoin, firstjoin; 4915 int i; 4916 struct addrinfo hints, *res, *host; 4917 SOCKETTYPE *apisock; 4918 4919 apisock = cgmalloc(sizeof(*apisock)); 4920 *apisock = INVSOCK; 4921 json_config = NULL; 4922 isjoin = false; 4923 4924 if (!opt_api_listen) { 4925 applog(LOG_DEBUG, "API not running%s", UNAVAILABLE); 4926 free(apisock); 4927 return; 4928 } 4929 4930 io_data = sock_io_new(); 4931 4932 mutex_init(&quit_restart_lock); 4933 4934 pthread_cleanup_push(tidyup, (void *)apisock); 4935 my_thr_id = api_thr_id; 4936 4937 setup_groups(); 4938 4939 if (opt_api_allow) { 4940 setup_ipaccess(); 4941 4942 if (ips == 0) { 4943 applog(LOG_WARNING, "API not running (no valid IPs specified)%s", UNAVAILABLE); 4944 free(apisock); 4945 return; 4946 } 4947 } 4948 4949 /* This should be done before curl in needed 4950 * to ensure curl has already called WSAStartup() in windows */ 4951 cgsleep_ms(opt_log_interval*1000); 4952 4953 sprintf(port_s, "%d", port); 4954 memset(&hints, 0, sizeof(hints)); 4955 hints.ai_flags = AI_PASSIVE; 4956 hints.ai_family = AF_UNSPEC; 4957 if (getaddrinfo(opt_api_host, port_s, &hints, &res) != 0) { 4958 applog(LOG_ERR, "API failed to resolve %s", opt_api_host); 4959 free(apisock); 4960 return; 4961 } 4962 host = res; 4963 while (host) { 4964 *apisock = socket(res->ai_family, SOCK_STREAM, 0); 4965 if (*apisock > 0) 4966 break; 4967 host = host->ai_next; 4968 } 4969 if (*apisock == INVSOCK) { 4970 applog(LOG_ERR, "API initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE); 4971 freeaddrinfo(res); 4972 free(apisock); 4973 return; 4974 } 4975 4976 #ifndef WIN32 4977 // On linux with SO_REUSEADDR, bind will get the port if the previous 4978 // socket is closed (even if it is still in TIME_WAIT) but fail if 4979 // another program has it open - which is what we want 4980 int optval = 1; 4981 // If it doesn't work, we don't really care - just show a debug message 4982 if (SOCKETFAIL(setsockopt(*apisock, SOL_SOCKET, SO_REUSEADDR, (void *)(&optval), sizeof(optval)))) 4983 applog(LOG_DEBUG, "API setsockopt SO_REUSEADDR failed (ignored): %s", SOCKERRMSG); 4984 #else 4985 // On windows a 2nd program can bind to a port>1024 already in use unless 4986 // SO_EXCLUSIVEADDRUSE is used - however then the bind to a closed port 4987 // in TIME_WAIT will fail until the timeout - so we leave the options alone 4988 #endif 4989 4990 // try for more than 1 minute ... in case the old one hasn't completely gone yet 4991 bound = 0; 4992 bindstart = time(NULL); 4993 while (bound == 0) { 4994 if (SOCKETFAIL(bind(*apisock, host->ai_addr, host->ai_addrlen))) { 4995 binderror = SOCKERRMSG; 4996 if ((time(NULL) - bindstart) > 61) 4997 break; 4998 else { 4999 applog(LOG_WARNING, "API bind to port %d failed - trying again in 30sec", port); 5000 cgsleep_ms(30000); 5001 } 5002 } else 5003 bound = 1; 5004 } 5005 freeaddrinfo(res); 5006 5007 if (bound == 0) { 5008 applog(LOG_ERR, "API bind to port %d failed (%s)%s", port, binderror, UNAVAILABLE); 5009 free(apisock); 5010 return; 5011 } 5012 5013 if (SOCKETFAIL(listen(*apisock, QUEUE))) { 5014 applog(LOG_ERR, "API3 initialisation failed (%s)%s", SOCKERRMSG, UNAVAILABLE); 5015 CLOSESOCKET(*apisock); 5016 free(apisock); 5017 return; 5018 } 5019 5020 if (opt_api_allow) 5021 applog(LOG_WARNING, "API running in IP access mode on port %d (%d)", port, (int)*apisock); 5022 else { 5023 if (opt_api_network) 5024 applog(LOG_WARNING, "API running in UNRESTRICTED read access mode on port %d (%d)", port, (int)*apisock); 5025 else 5026 applog(LOG_WARNING, "API running in local read access mode on port %d (%d)", port, (int)*apisock); 5027 } 5028 5029 if (opt_api_mcast) 5030 mcast_init(); 5031 5032 strbufs = k_new_list("StrBufs", sizeof(SBITEM), ALLOC_SBITEMS, LIMIT_SBITEMS, false); 5033 5034 while (!bye) { 5035 clisiz = sizeof(cli); 5036 if (SOCKETFAIL(c = accept(*apisock, (struct sockaddr *)(&cli), &clisiz))) { 5037 applog(LOG_ERR, "API failed (%s)%s (%d)", SOCKERRMSG, UNAVAILABLE, (int)*apisock); 5038 goto die; 5039 } 5040 5041 addrok = check_connect((struct sockaddr_storage *)&cli, &connectaddr, &group); 5042 applog(LOG_DEBUG, "API: connection from %s - %s", 5043 connectaddr, addrok ? "Accepted" : "Ignored"); 5044 5045 if (addrok) { 5046 /* Accept only half the TMPBUFSIZ to account for space 5047 * potentially used by escaping chars. */ 5048 n = recv(c, &buf[0], TMPBUFSIZ / 2 - 1, 0); 5049 if (SOCKETFAIL(n)) 5050 buf[0] = '\0'; 5051 else 5052 buf[n] = '\0'; 5053 5054 if (opt_debug) { 5055 if (SOCKETFAIL(n)) 5056 applog(LOG_DEBUG, "API: recv failed: %s", SOCKERRMSG); 5057 else 5058 applog(LOG_DEBUG, "API: recv command: (%d) '%s'", n, buf); 5059 } 5060 5061 if (!SOCKETFAIL(n)) { 5062 // the time of the request in now 5063 when = time(NULL); 5064 io_reinit(io_data); 5065 5066 did = false; 5067 5068 if (*buf != ISJSON) { 5069 isjson = false; 5070 5071 param = strchr(buf, SEPARATOR); 5072 if (param != NULL) 5073 *(param++) = '\0'; 5074 5075 cmd = buf; 5076 } 5077 else { 5078 isjson = true; 5079 5080 param = NULL; 5081 5082 json_config = json_loadb(buf, n, 0, &json_err); 5083 5084 if (!json_is_object(json_config)) { 5085 message(io_data, MSG_INVJSON, 0, NULL, isjson); 5086 send_result(io_data, c, isjson); 5087 did = true; 5088 } else { 5089 json_val = json_object_get(json_config, JSON_COMMAND); 5090 if (json_val == NULL) { 5091 message(io_data, MSG_MISCMD, 0, NULL, isjson); 5092 send_result(io_data, c, isjson); 5093 did = true; 5094 } else { 5095 if (!json_is_string(json_val)) { 5096 message(io_data, MSG_INVCMD, 0, NULL, isjson); 5097 send_result(io_data, c, isjson); 5098 did = true; 5099 } else { 5100 cmd = (char *)json_string_value(json_val); 5101 json_val = json_object_get(json_config, JSON_PARAMETER); 5102 if (json_is_string(json_val)) 5103 param = (char *)json_string_value(json_val); 5104 else if (json_is_integer(json_val)) { 5105 sprintf(param_buf, "%d", (int)json_integer_value(json_val)); 5106 param = param_buf; 5107 } else if (json_is_real(json_val)) { 5108 sprintf(param_buf, "%f", (double)json_real_value(json_val)); 5109 param = param_buf; 5110 } 5111 } 5112 } 5113 } 5114 } 5115 5116 if (!did) { 5117 char *cmdptr, *cmdsbuf = NULL; 5118 5119 if (strchr(cmd, CMDJOIN)) { 5120 firstjoin = isjoin = true; 5121 // cmd + leading+tailing '|' + '\0' 5122 cmdsbuf = cgmalloc(strlen(cmd) + 3); 5123 strcpy(cmdsbuf, "|"); 5124 param = NULL; 5125 } else 5126 firstjoin = isjoin = false; 5127 5128 cmdptr = cmd; 5129 do { 5130 did = false; 5131 if (isjoin) { 5132 cmd = strchr(cmdptr, CMDJOIN); 5133 if (cmd) 5134 *(cmd++) = '\0'; 5135 if (!*cmdptr) 5136 goto inochi; 5137 } 5138 5139 for (i = 0; cmds[i].name != NULL; i++) { 5140 if (strcmp(cmdptr, cmds[i].name) == 0) { 5141 sprintf(cmdbuf, "|%s|", cmdptr); 5142 if (isjoin) { 5143 if (strstr(cmdsbuf, cmdbuf)) { 5144 did = true; 5145 break; 5146 } 5147 strcat(cmdsbuf, cmdptr); 5148 strcat(cmdsbuf, "|"); 5149 head_join(io_data, cmdptr, isjson, &firstjoin); 5150 if (!cmds[i].joinable) { 5151 message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson); 5152 did = true; 5153 tail_join(io_data, isjson); 5154 break; 5155 } 5156 } 5157 if (ISPRIVGROUP(group) || strstr(COMMANDS(group), cmdbuf)) 5158 (cmds[i].func)(io_data, c, param, isjson, group); 5159 else { 5160 message(io_data, MSG_ACCDENY, 0, cmds[i].name, isjson); 5161 applog(LOG_DEBUG, "API: access denied to '%s' for '%s' command", connectaddr, cmds[i].name); 5162 } 5163 5164 did = true; 5165 if (!isjoin) 5166 send_result(io_data, c, isjson); 5167 else 5168 tail_join(io_data, isjson); 5169 break; 5170 } 5171 } 5172 5173 if (!did) { 5174 if (isjoin) 5175 head_join(io_data, cmdptr, isjson, &firstjoin); 5176 message(io_data, MSG_INVCMD, 0, NULL, isjson); 5177 if (isjoin) 5178 tail_join(io_data, isjson); 5179 else 5180 send_result(io_data, c, isjson); 5181 } 5182 inochi: 5183 if (isjoin) 5184 cmdptr = cmd; 5185 } while (isjoin && cmdptr); 5186 } 5187 5188 if (isjoin) 5189 send_result(io_data, c, isjson); 5190 5191 if (isjson && json_is_object(json_config)) 5192 json_decref(json_config); 5193 } 5194 } 5195 CLOSESOCKET(c); 5196 } 5197 die: 5198 /* Blank line fix for older compilers since pthread_cleanup_pop is a 5199 * macro that gets confused by a label existing immediately before it 5200 */ 5201 ; 5202 pthread_cleanup_pop(true); 5203 5204 free(apisock); 5205 5206 if (opt_debug) 5207 applog(LOG_DEBUG, "API: terminating due to: %s", 5208 do_a_quit ? "QUIT" : (do_a_restart ? "RESTART" : (bye ? "BYE" : "UNKNOWN!"))); 5209 5210 mutex_lock(&quit_restart_lock); 5211 5212 if (do_a_restart) { 5213 if (thr_info_create(&bye_thr, NULL, restart_thread, &bye_thr)) { 5214 mutex_unlock(&quit_restart_lock); 5215 quit(1, "API failed to initiate a restart - aborting"); 5216 } 5217 pthread_detach(bye_thr.pth); 5218 } else if (do_a_quit) { 5219 if (thr_info_create(&bye_thr, NULL, quit_thread, &bye_thr)) { 5220 mutex_unlock(&quit_restart_lock); 5221 quit(1, "API failed to initiate a clean quit - aborting"); 5222 } 5223 pthread_detach(bye_thr.pth); 5224 } 5225 5226 mutex_unlock(&quit_restart_lock); 5227 }