From 9323263d694cde2e64a01ad691860dc791181ef4 Mon Sep 17 00:00:00 2001
From: Robert Ricci <ricci@cs.utah.edu>
Date: Wed, 15 Dec 2004 22:33:46 +0000
Subject: [PATCH] Blinky light support for motes on stargates.

Display a new 'Blinky Lights' button on the showexp page. In order to
do this, I have to get a list of which classes/types are in use in
the experiment.

This leads to moteleds.php3, which displays the blink lights using
Tim's cool Java applet.
---
 configure                |   2 +-
 utils/GNUmakefile.in     |   2 +-
 utils/spewleds.in        |  79 ++++++++++++++++++++++++++++++++++++++
 www/BlinkenLichten.class | Bin 2414 -> 2744 bytes
 www/BlinkenLichten.java  |  37 +++++++++++++-----
 www/ledpipe.php3         |  54 ++++++++++++++++++++------
 www/moteleds.php3        |  80 +++++++++++++++++++++++++++++++++++++++
 www/showexp.php3         |  21 ++++++++++
 www/showstuff.php3       |   2 +-
 9 files changed, 254 insertions(+), 23 deletions(-)
 create mode 100755 utils/spewleds.in
 create mode 100644 www/moteleds.php3

diff --git a/configure b/configure
index 4a9fedec0e..d4a4436e80 100755
--- a/configure
+++ b/configure
@@ -1559,7 +1559,7 @@ outfiles="$outfiles Makeconf GNUmakefile \
 	utils/cvsupd.pl utils/newnode utils/grantnodetype \
 	utils/nsgen/GNUmakefile utils/nsgen/webnsgen \
 	utils/link_config utils/import_commitlog utils/dhcpd_wrapper \
-	utils/opsreboot utils/deletenode utils/webdeletenode \
+	utils/opsreboot utils/deletenode utils/webdeletenode utils/spewleds \
 	www/GNUmakefile www/defs.php3 www/dbdefs.php3 \
 	www/swish.conf www/websearch \
 	vis/GNUmakefile vis/webvistopology vis/dbvistopology \
diff --git a/utils/GNUmakefile.in b/utils/GNUmakefile.in
index bd0e935396..054396dc88 100644
--- a/utils/GNUmakefile.in
+++ b/utils/GNUmakefile.in
@@ -18,7 +18,7 @@ BIN_SCRIPTS	= delay_config sshtb create_image node_admin link_config
 SBIN_SCRIPTS	= vlandiff vlansync withadminprivs export_tables cvsupd.pl \
                   eventping grantnodetype import_commitlog dhcpd_wrapper \
 		  opsreboot deletenode node_statewait
-LIBEXEC_SCRIPTS	= webcreateimage newnode webdeletenode
+LIBEXEC_SCRIPTS	= webcreateimage newnode webdeletenode spewleds
 
 #
 # Force dependencies on the scripts so that they will be rerun through
diff --git a/utils/spewleds.in b/utils/spewleds.in
new file mode 100755
index 0000000000..c08cf18e38
--- /dev/null
+++ b/utils/spewleds.in
@@ -0,0 +1,79 @@
+#!/usr/bin/perl -wT
+
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2004 University of Utah and the Flux Group.
+# All rights reserved.
+#
+
+#
+# Simple script to connect to the LED status port on a stargate and spew the LED status
+# to stdout
+#
+
+use English;
+use Getopt::Std;
+use IO::Socket;
+
+use lib '@prefix@/lib';
+use libdb;
+
+my $LED_STATUS_PORT = 1812;
+
+# Make output unbuffered
+$| = 1;
+
+#
+# Args
+#
+if (@ARGV != 1) {
+    die "Usage: spewleds mote\n";
+}
+
+my ($mote) = @ARGV;
+
+#
+# Untaint the argument. 
+#
+if ($mote =~ /^([-\w.]+)$/) {
+	$mote = $1;
+} else {
+    die("Tainted node name: $mote");
+}
+
+#
+# Make sure they have permissions to see this node
+#
+if (! TBNodeAccessCheck($UID, TB_NODEACCESS_READINFO, $mote)) {
+    print STDERR
+    "*** osload: Not enough permission to view that node!\n";
+    exit 1;
+}
+
+#
+# Make sure it's a stargate or garcia (XXX garcia should be temporary)
+#
+my ($type, $class) = TBNodeType($mote);
+if ($type ne "garcia" && $class ne "sg") {
+    die "Node $mote is not of the correct type ($type,$class)\n";
+}
+
+#
+# Connect to the LED status port
+#
+my $sock = new IO::Socket::INET (
+                                  PeerAddr => "$mote",
+                                  PeerPort => "$LED_STATUS_PORT",
+                                  Proto => 'tcp',
+                                 );
+die "Could not create socket: $!\n" unless $sock;
+
+#
+# Okie, just loop on this guy forever
+#
+while (my $string = <$sock>) {
+    print "$string"
+}
+
+close $sock;
+exit 0;
diff --git a/www/BlinkenLichten.class b/www/BlinkenLichten.class
index 3e6b3fb2c59e0fe162a19145fae62db8317dcb69..265cbe65d9a70cee43c4783c859d4475deaf7770 100644
GIT binary patch
delta 1502
zcmY+EYgbfd6vuyunK@@T9d#Tc9Sc**gfkL&i2@1`@`4R`Nl~Ogj0rDXlDuRVW0r|p
zMowmCrd{b?2o9`h`JxxS=qvOMdeKLy{tpAOTr+3y{p|hUd+pov{2iUhPyO-FFLww~
z!tF3^VcNr-3iD~0pB!Ft7;<>e;gUnz;eCh84j(vNarn^Ts>4SP*Bm}}xbE<Yi$kT$
ze5za`JmHeZlSZn}B}|RW9BN%j>s<1wbGe6l<B@vG<zChsaf3@P8x49>E;bFhxNJ7k
z(<WBJmDK1GrOCwFVsKlHb(?Y9Zpbqx>JAfcr#UtoX_v`px5=Z$WgdG>&{pI7tnq!$
zOVQ5@p2#6XdJXCGNYUf*I<I(~Hr6-T>v4u|kGJUZI7_F;Ap1Pd8Qpn0JT4gU+YETT
z!^<A;a?;}lNspTx@c7I``JBTZUl`n%2KSZGeZ7|b9^V-9EiZa}XQc0q^n)?~$nzc}
z9Q3$lqK+EpF$U5pt1vHGRGc{2ajc`X<M@%%`o8YI{;JWN)-}s^idyR$8qzg^KOMWX
zqyJE{KfN%xdw!^^Kbh>c2v70$zTVN^;1(<WTd2;l`;(ngq;u>GDGU2Vvb($QxJ7PO
zkcFjcmV?Dyg5;R$KwNc9an(J=m72IMoSG_nRHp)QOFZO{bKi&rixblzDAwuX3{)ta
zlq2Jye~bGwl!~p)u-ebCB;iLx8J0Eq(STTG(2oWU4^5KS5}zc}5(`aGKF-2uPKHG#
z6D$^sS|YJv_K@Mxabk@MSX61pY`L$B*$TGCkJ%ZP-e$!l^IK9%b3ul+BZ+the?<As
z)gqkM+GX=@V}TMZRWr(DS*>rama~FIRI-dJe5z%wkyNYSIyO>Af_ipI-^zMA*g%)G
zJ#69_n-zPtq9?RsP1>sx)c@g`hG-jtx|+}$v40IGhN<-9fi;m;H*f;O1cSFJ@#CSp
z6wCd%bC;q*&3nWjV?{g=QJL}U|K1rUY`Wn4mTEQ?hG=9f-c*GtDA)CZ9a1;zYt>z8
zBSyPQ+pDtnskl}-G|MH)HWjmzgY03u+<)ePsG0gQZAXr{?W>K&vtxIsw0(-(Bc$%e
zH2yDXLc*U<Dc{1zm18_+cUOx&Tpm>RC0CgfpIU~pVIo<nAcOv8@j##;U&DM*u;p3U
zLsDm9l4f89v#^RR?D79$l4fA3g3$hH<ZRYW8ZjJEhDX(=V_Jrj>d31s5iiuvm+8T%
z&?sv)%8lZic~b*Fqkf%Le+KClKdg6RK+nWE-3?3jnfuYGlj&5x8Pe}GL=4aH!0ZB|
kC7U-r9$2i^gCTvFB&B5=lJ&A4xGQ?@E-Kn<vgL5(KP$5h00000

delta 1173
zcmYL|%Xbt-6vlr&-7__53kZXO3=)I@CiY}PcnO3zgoikTh)4{H!6>45NbqP-6k}sR
z2@1Z(S5(C3$vOg^9OH56!o4e(uH5)1s9z<4oIZ6Qb#Hz3-FvIYYyQc_zyJI5ulN-C
zG0WjBk7YTN<>M^h8lE!@8ZH<v8s0LzZMb9@GF&#iV|dqa#c<W|p5cAN2O)-aA#-^k
zq?+|1^Vs05ja6i64VlHJkQz3J+{G4`yfx%*;%&~{9#X}QDnfQvkztn;Z4O~~C~Hqh
zj=c_HpTm04CGU6QA%|6RFlFZ+aMr_)!a;ZOBO!A*<Uk*_JWHn&Pdf3m6UQz6JY{2E
z=CtJ%7k-r<%WHI5&e36cop#HB+k1l}mY814o4jE8ke4hUanw?w+wzG!`za?ZpE;<{
zo%;n(SiW@PE1t1jbJo|+y6&Ri@VMoBj#++4(OVhymfiT<8}@>9W9QDwkpE}pRB+_R
zA3@pkQ>XhXOEPVh-pu8grRs&jV>T&$q5{%lBc0K{YLt9lUY)cjosPzsKPtncZ#oGY
z<ek5rsw@WzL(xy%lh7m;C9KrCw-n{l2}=(|IbSMn4We8iqcuIjypF;I)gAftIBUkJ
z&7~3+7RRZR%6Ss`V9H5YJx0FVlyF~bCZEyq=6ptIEm1y`u;doYCz#tImJ1SAjg|@t
zjiaJBTh$F}=u@=2Ss;ROQ9PR!YSCJ)x3QjuY@nWvL~LdyTiC=_wzG}Bk|lP~uKiAS
z(L<Y>vzuP_Xx#e*-Xi$@Tu{~}3c6<ac0V+5UmvUse#w?bsErE#n(9^8F|j{F5Zq#g
zT2qK_vRq{EzxLl>BV=8DjghBn9Qi;Uj<D0@oApHDkaiDiwX24s)Y8Qw*$vu@I3@-?
z;(wgA9Mq}r=$3S2te+gTCKTzexM60Hp#pG{w94|_E1W*ir5#7UNgV6TBDrFiS%t}o
z7DotWX~e~X?>F6Kt=6)H#WSdjCm%weF#1LRj9T2!eEk}AoE60rr*z5LJAEq4b0lh9
tD}JNesf0x{Gel#WbEgec*aJEc>z4D14JbOO-}It>+H(SXQLz*!{|91xx>*1K

diff --git a/www/BlinkenLichten.java b/www/BlinkenLichten.java
index 85237de751..0a40a5c4ca 100644
--- a/www/BlinkenLichten.java
+++ b/www/BlinkenLichten.java
@@ -34,7 +34,9 @@ public class BlinkenLichten
     /**
      * The current status of the light, just on/off for now.
      */
-    private boolean on;
+    private boolean red_on;
+    private boolean green_on;
+    private boolean yellow_on;
     
     public BlinkenLichten()
     {
@@ -79,15 +81,19 @@ public class BlinkenLichten
     
     public void run()
     {
-	byte buffer[] = new byte[1];
+	byte buffer[] = new byte[6];
 	
 	try
 	{
 	    /* Just read a character at a time from the other side. */
-	    while( this.is.read(buffer) > 0 )
+	    // Bad, bad, bad
+	    //while( this.is.read(buffer) > 0 )
+	    while( this.is.read(buffer,0,6) > 0 )
 	    {
 		/* 1 == on, 0 == off */
-		this.on = (buffer[0] == '1');
+		this.red_on = (buffer[0] == '1');
+		this.green_on = (buffer[2] == '1');
+		this.yellow_on = (buffer[4] == '1');
 		
 		repaint();
 	    }
@@ -108,16 +114,29 @@ public class BlinkenLichten
     public void paint(Graphics g)
     {
 	Dimension size = getSize();
+	int width = size.width / 3;
+	int height = size.height;
 
 	/*
-	 * Just paint the entire canvas provided to the applet, no need to get
-	 * fancy.
+	 * Paint each of the three LED values
 	 */
-	if( this.on )
+	if (this.red_on)
 	    g.setColor(Color.red);
 	else
-	    g.setColor(Color.black);
-	g.fillRect(0, 0, size.width, size.height);
+	    g.setColor(Color.red.darker().darker());
+	g.fillRect(0, 0, width, height);
+
+	if (this.green_on)
+	    g.setColor(Color.green);
+	else
+	    g.setColor(Color.green.darker().darker());
+	g.fillRect(width, 0, width, height);
+
+	if (this.yellow_on)
+	    g.setColor(Color.yellow);
+	else
+	    g.setColor(Color.yellow.darker().darker());
+	g.fillRect(width *2, 0, width, height);
     }
     
     public void destroy()
diff --git a/www/ledpipe.php3 b/www/ledpipe.php3
index 675d18a6f5..166efb7566 100644
--- a/www/ledpipe.php3
+++ b/www/ledpipe.php3
@@ -7,12 +7,7 @@
 include("defs.php3");
 
 #
-# Standard Testbed Header
-#
-#PAGEHEADER("Watch Experiment Log");
-
-#
-# Only known and logged in users can end experiments.
+# Only known and logged in users can watch LEDs
 #
 $uid = GETLOGIN();
 LOGGEDINORDIE($uid);
@@ -42,11 +37,48 @@ header("Cache-Control: no-cache, must-revalidate");
 header("Pragma: no-cache");
 flush();
 
-for ($lpc = 0; $lpc < 30; $lpc++) {
-	sleep(1);
-	$on_off = $lpc % 2;
-	echo "$on_off";
-	flush();
+#for ($lpc = 0; $lpc < 30; $lpc++) {
+    #	sleep(1);
+    #	$on_off = $lpc % 2;
+    #	echo "$on_off";
+    #	flush();
+    #}
+
+#
+# Silly, I can't get php to get the buffering behavior I want with a socket, so
+# we'll open a pipe to a perl process
+#
+$socket = popen("$TBSUEXEC_PATH $uid nobody spewleds $node","r");
+if (!$socket) {
+    USERERROR("Error opening $node - $errstr",1);
+}
+
+#
+# Clean up when the remote user disconnects
+#
+function SPEWCLEANUP()
+{
+    global $socket;
+
+    if (!$socket || !connection_aborted()) {
+	exit();
+    }
+    pclose($socket);
+    exit();
+}
+ignore_user_abort(1);
+register_shutdown_function("SPEWCLEANUP");
+
+#
+# Just loop forver reading from the socket
+#
+while(!feof($socket)) {
+
+    # Bad rob! No biscuit!
+    $onoff = fread($socket,6);
+    echo "$onoff";
+    flush();
 }
+fclose($socket);
 
 ?>
diff --git a/www/moteleds.php3 b/www/moteleds.php3
new file mode 100644
index 0000000000..c975ef5958
--- /dev/null
+++ b/www/moteleds.php3
@@ -0,0 +1,80 @@
+<?php
+#
+# EMULAB-COPYRIGHT
+# Copyright (c) 2004 University of Utah and the Flux Group.
+# All rights reserved.
+#
+
+include("defs.php3");
+include("showstuff.php3");
+
+#
+# Make sure they are logged in
+#
+$uid = GETLOGIN();
+LOGGEDINORDIE($uid);
+
+#
+# Verify page arguments.
+# 
+if (!isset($pid) ||
+    strcmp($pid, "") == 0) {
+    USERERROR("You must provide a Project ID.", 1);
+}
+
+if (!isset($eid) ||
+    strcmp($eid, "") == 0) {
+    USERERROR("You must provide an Experiment ID.", 1);
+}
+
+#
+# Standard Testbed Header now that we have the pid/eid okay.
+#
+PAGEHEADER("View mote LEDs ($pid/$eid)");
+
+#
+# Make sure they have permission to view this experiment
+#
+if (! TBExptAccessCheck($uid, $pid, $eid, $TB_EXPT_READINFO)) {
+    USERERROR("You do not have permission to view experiment $exp_eid!", 1);
+}
+
+#
+# Get a list of all nodes in this experiment of type 'garcia' or 'stargate'
+#
+$query_result =
+    DBQueryFatal("select r.node_id,t.type,t.class from reserved as r ".
+		     "left join nodes as n on ".
+		     "     r.node_id=n.node_id ".
+		     "left join node_types as t on ".
+		     "     n.type=t.type ".
+		     "where r.pid='$pid' and r.eid='$eid'");
+		     
+if (mysql_num_rows($query_result) == 0) {
+    echo "<h3>No nodes to display in this experiment!</h3>\n";
+} else {
+    echo "<center>
+          <h3>Blinky Lights</h3>
+          </center>
+          <table align=center cellpadding=2 border=1>
+	  <tr><th>Node</th><th>LEDs</th>\n";
+    while ($row = mysql_fetch_array($query_result)) {
+	if ($row['type'] != "garcia" && $row['class'] != "sg") {
+	    # Only the LEDs, mam
+	    continue;
+	}
+	echo "<tr><th>$row[node_id]</th><td>";
+	SHOWBLINKENLICHTEN($uid,
+	    $HTTP_COOKIE_VARS[$TBAUTHCOOKIE],
+	"ledpipe.php3?node=$row[node_id]");
+    }
+    echo "</table>\n";
+
+}
+
+#
+# Standard Testbed Footer
+# 
+PAGEFOOTER();
+
+?>
diff --git a/www/showexp.php3 b/www/showexp.php3
index b3ddad5636..a0cdd5dd0c 100644
--- a/www/showexp.php3
+++ b/www/showexp.php3
@@ -72,6 +72,20 @@ $isbatch    = $row["batchmode"];
 $wireless   = $row["wirelesslans"];
 $linktest_running = $row["linktest_pid"];
 
+#
+# Get a list of node types and classes in this experiment
+#
+$query_result =
+    DBQueryFatal("select distinct t.type,t.class from reserved as r " .
+		 "       left join nodes as n on r.node_id=n.node_id ".
+		 "       left join node_types as t on n.type=t.type ".
+		 "where r.eid='$eid' and r.pid='$pid'");
+while ($row = mysql_fetch_array($query_result)) {
+    $classes[$row['class']] = 1;
+    $types[$row['type']] = 1;
+}
+
+
 echo "<font size=+2>Experiment <b>".
      "<a href='showproject.php3?pid=$pid'>$pid</a>/".
      "<a href='showexp.php3?pid=$pid&eid=$eid'>$eid</a></b></font>\n";
@@ -194,6 +208,13 @@ if ($wireless) {
 WRITESUBMENUBUTTON("Show History",
 		   "showstats.php3?showby=expt&which=$expindex");
 
+# Blinky lights - but only if they have nodes of the correct type in their
+# experiment
+if ($types['garcia'] || $classes['sg']) {
+    WRITESUBMENUBUTTON("Show Blinky Lights",
+		   "moteleds.php3?pid=$exp_pid&eid=$exp_eid");
+}
+
 if (ISADMIN($uid)) {
     if ($expstate == $TB_EXPTSTATE_ACTIVE) {
 	SUBMENUSECTION("Beta-Test Options");
diff --git a/www/showstuff.php3 b/www/showstuff.php3
index 7bceaa0a70..ee6ba4b4a5 100644
--- a/www/showstuff.php3
+++ b/www/showstuff.php3
@@ -1004,7 +1004,7 @@ function SHOWEXPLIST($type,$id,$gid = "") {
 #                      $HTTP_COOKIE_VARS[$TBAUTHCOOKIE],
 #                      "ledpipe.php3?node=em1");
 #
-function SHOWBLINKENLICHTEN($uid, $auth, $pipeurl, $width = 10, $height = 10) {
+function SHOWBLINKENLICHTEN($uid, $auth, $pipeurl, $width = 30, $height = 10) {
 	echo "
           <applet code='BlinkenLichten.class' width='$width' height='$height'>
             <param name='pipeurl' value='$pipeurl'>
-- 
GitLab