Commit 7fa661afd9db288ff45112a1d70e3ddbfc9ed034

kanoi 2013-09-02T05:56:09

Merge pull request #489 from kanoi/master miner.php allow formula generation of new fields

diff --git a/API-README b/API-README
index 2d7110c..2829b95 100644
--- a/API-README
+++ b/API-README
@@ -1248,6 +1248,16 @@ N.B. the accuracy of the timing used to wait for the replies is
 ---------
 
 Default:
+ $allowgen = false;
+
+Set $allowgen to true to allow customsummarypages to use 'gen' 
+false means ignore any 'gen' options
+This is disabled by default due to the possible security risk
+of using it, see the end of this document for an explanation
+
+---------
+
+Default:
  $rigipsecurity = true;
 
 Set $rigipsecurity to false to show the IP/Port of the rig
@@ -1508,6 +1518,7 @@ The example given:
 With cgminer 2.10.2 and later, miner.php includes an extension to
 the custom pages that allows you to apply SQL style commands to
 the data: where, group, and having
+cgminer 3.4.2 also includes another option 'gen'
 
 As an example, miner.php includes a more complex custom page called 'Pools'
 this includes the extension:
@@ -1523,6 +1534,7 @@ $poolsext = array(
                         'STATS.Bytes Sent' => 'sum',
                         'STATS.Times Recv' => 'sum',
                         'STATS.Bytes Recv' => 'sum'),
+        'gen' => array('AvShr', 'POOL.Difficulty Accepted/max(POOL.Accepted,1)),
         'having' => array(array('STATS.Bytes Recv', '>', 0)))
 );
 
@@ -1574,3 +1586,21 @@ The first 4 are as expected - the numerical sum, average, minimum or maximum
  of course any valid 'DEVS.Xyz' would give the same 'count' value
 'any' is effectively random: the field value in the 1st row of the grouped data
 An unrecognised 'function' uses 'any'
+
+A 'gen' allows you to generate new fields from any php valid function of any
+of the other fields
+ e.g. 'gen' => array('AvShr', 'POOL.Difficulty Accepted/max(POOL.Accepted,1)),
+will generate a new field called GEN.AvShr that is the function shown, which
+in this case is the average difficulty of each share submitted
+
+THERE IS A SECURITY RISK WITH HOW GEN WORKS
+It simply replaces all the variables with their values and then requests PHP
+the execute the formula - thus if a field value returned from a cgminer API
+request contained PHP code, it could be executed by your web server
+Of course cgminer doesn't do this, but if you do not control the cgminer that
+returns the data in the API calls, someone could modify cgminer to return a
+PHP string in a field you use in 'gen'
+Thus use 'gen' at your own risk
+If someone feels the urge to write a mathematical interpreter in PHP to get
+around this risk, feel free to write one and submit it to the API author for
+consideration
diff --git a/miner.php b/miner.php
index 89f3cba..fdb9e52 100644
--- a/miner.php
+++ b/miner.php
@@ -3,7 +3,7 @@ session_start();
 #
 global $doctype, $title, $miner, $port, $readonly, $notify, $rigs;
 global $mcast, $mcastexpect, $mcastaddr, $mcastport, $mcastcode;
-global $mcastlistport, $mcasttimeout;
+global $mcastlistport, $mcasttimeout, $allowgen;
 global $rigipsecurity, $rigtotals, $forcerigtotals;
 global $socksndtimeoutsec, $sockrcvtimeoutsec;
 global $checklastshare, $poolinputs, $hidefields;
@@ -70,6 +70,10 @@ $mcastlistport = 4027;
 # to wait for replies to the Multicast message
 $mcasttimeout = 1.5;
 #
+# Set $allowgen to true to allow customsummarypages to use 'gen' 
+# false means ignore any 'gen' options
+$allowgen = false;
+#
 # Set $rigipsecurity to false to show the IP/Port of the rig
 # in the socket error messages and also show the full socket message
 $rigipsecurity = true;
@@ -145,7 +149,7 @@ $poolspage = array(
 			'POOL.Has GBT=GBT', 'STATS.Times Sent=TSent',
 			'STATS.Bytes Sent=BSent', 'STATS.Net Bytes Sent=NSent',
 			'STATS.Times Recv=TRecv', 'STATS.Bytes Recv=BRecv',
-			'STATS.Net Bytes Recv=NRecv'));
+			'STATS.Net Bytes Recv=NRecv', 'GEN.AvShr=AvShr'));
 #
 $poolssum = array(
  'SUMMARY' => array('MHS av', 'Found Blocks', 'Accepted',
@@ -162,7 +166,9 @@ $poolsext = array(
 	'calc' => array('POOL.Difficulty Accepted' => 'sum', 'POOL.Difficulty Rejected' => 'sum',
 			'STATS.Times Sent' => 'sum', 'STATS.Bytes Sent' => 'sum',
 			'STATS.Net Bytes Sent' => 'sum', 'STATS.Times Recv' => 'sum',
-			'STATS.Bytes Recv' => 'sum', 'STATS.Net Bytes Recv' => 'sum'),
+			'STATS.Bytes Recv' => 'sum', 'STATS.Net Bytes Recv' => 'sum',
+			'POOL.Accepted' => 'sum'),
+	'gen' => array('AvShr' => 'round(POOL.Difficulty Accepted/max(POOL.Accepted,1)*100)/100'),
 	'having' => array(array('STATS.Bytes Recv', '>', 0)))
 );
 
@@ -2349,8 +2355,55 @@ function processcompare($which, $ext, $section, $res)
  return $res;
 }
 #
-function processext($ext, $section, $res)
+function ss($a, $b)
+{
+ $la = strlen($a);
+ $lb = strlen($b);
+ if ($la != $lb)
+	return $la - $lb;
+ return strcmp($a, $b);
+}
+#
+function genfld($row, $calc)
+{
+ uksort($row, "ss");
+
+ foreach ($row as $name => $value)
+	if (strstr($calc, $name) !== FALSE)
+		$calc = str_replace($name, $value, $calc);
+
+ eval("\$val = $calc;");
+
+ return $val;
+}
+#
+function dogen($ext, $section, &$res, &$fields)
+{
+ $gen = $ext[$section]['gen'];
+
+ foreach ($gen as $fld => $calc)
+	$fields[] = "GEN.$fld";
+
+ foreach ($res as $rig => $result)
+	foreach ($result as $sec => $row)
+	{
+		$secname = preg_replace('/\d/', '', $sec);
+		if (secmatch($section, $secname))
+			foreach ($gen as $fld => $calc)
+			{
+				$name = "GEN.$fld";
+
+				$val = genfld($row, $calc);
+
+				$res[$rig][$sec][$name] = $val;
+			}
+	}
+}
+#
+function processext($ext, $section, $res, &$fields)
 {
+ global $allowgen;
+
  $res = processcompare('where', $ext, $section, $res);
 
  if (isset($ext[$section]['group']))
@@ -2418,6 +2471,10 @@ function processext($ext, $section, $res)
 	}
  }
 
+ // Generated fields (functions of other fields)
+ if ($allowgen === true && isset($ext[$section]['gen']))
+	dogen($ext, $section, $res, $fields);
+
  return processcompare('having', $ext, $section, $res);
 }
 #
@@ -2514,7 +2571,8 @@ function processcustompage($pagename, $sections, $sum, $ext, $namemap)
 
 		if (isset($results[$sectionmap[$section]]))
 		{
-			$rigresults = processext($ext, $section, $results[$sectionmap[$section]]);
+			$rigresults = processext($ext, $section, $results[$sectionmap[$section]], $fields);
+
 			$showfields = array();
 			$showhead = array();
 			foreach ($fields as $field)