/ 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(',', &param, &buf);
2751  
2752  	if (!(*param)) // missing user
2753  		goto exitsama;
2754  
2755  	*user = buf;
2756  
2757  	// copy user
2758  	copyadvanceafter(',', &param, &buf);
2759  
2760  	if (!*param) // missing pass
2761  		goto exitsama;
2762  
2763  	*pass = buf;
2764  
2765  	// copy pass
2766  	copyadvanceafter(',', &param, &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", &current_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  }