Author Archives: Dan

Digital Signage with a Raspberry Pi and Google Slides

I have been looking at an easier way to Digital Signage and I just got my first Raspberry Pi.  I figured the low and High def inputs would allow me to strap a rPi on the back of a TV and provide rich content.

I am looking for a opensource, free/very low cost solution that is easy to manage and simple for the people updating it.  I foresee about 10 devices in my future.

I did a lot of reading and found that there are plenty of projects out there already that enable digital signage for the rPi.  I found this site and started down the list.

I tried a few applications and liked Screenly the best.  It is simple, performs well, and overall just works.  See their online demo for their interface. They provide their own rPi image or allow installation onto raspbian if you want to further customize it.  SSH is available on the Screenly image out of the box.  Screenly allows web pages, images, and videos (MP4) to be streamed to the rPi which gives me plenty of flexibility to mix and match what I like.  They have 2 versions; a centrally managed model or a free, per device management model.

My immediate thoughts were to convert our ancient overused powerpoint into something more rich, but to get users to buy into this solution I would first convert them over to Google Slides which would provide an easy to use, collaborative, updating presentation to all devices without actually touching any of the devices.

I created a simple Google Slide presentation with four or five slides and random comments on it.  I followed these instructions to make it automatically full-screen and play right in the browser.  I took that link and threw it right into Screenly-OSE and viola!

Caveats…

  • Google sets a single time for all slides…  Therefore you cannot make one slide longer than the others (as far as I can tell).  Transitions can be set differently though.
    • You could leverage second presentation and set the delay longer, or use the Screenly interface to get more specific in necessary.
  • One issue I ran into was that if you have Google loop the presentation rather than Screenly, the content never updates.  Obviously this defeats the purpose of using Google Slides in the first place.  Let Google finish the presentation and let Screenly reload the presentation and it will be fairly straightforward.
    • Related to the above post, then timing becomes an issue.  Some simple math should work to fix that though.
      • 5 seconds a slide (as per when publishing within Google) plus 3 seconds for transition time (as set in presentation) times 5 (number of slides) should come out to be about 40 seconds.  Tweak as needed.

Things to investigate:

It appears the database being used is simply for the ‘Playlist’.  I would imagine that the application would refresh the playlist frequently.  That being said, would placing the /home folder in a shared NFS location make management easier?  This could also lessen the wear and tear of the SDcard.  If all clients pointed back to this NFS share, would this update all of the clients or would this require a reboot/restart on the clients to apply any updates?  I do not know how the software is triggered or written… So more experiments to come when I get more Pi’s!

Teamviewer with UAC and Administrative Rights

Ran into an issue with a user where I needed to remotely access their computer.  They needed software installed ASAP and of course did not have admin access to the computer.  I had them go to www.teamviewer.com and install the light client which does not require administrative access.  

When going to install the application UAC came up but I could not see, click, or type when UAC was active.  With some quick googleing I was able to find that you can run the client with administrative credentials upon connecting to the client.   PROPS Teamviewer.

See the steps below.

1.

Fire up Teamviewer

 
2.

Ask for the ID number for the user, connect to that ID.

This will be a 9 digit number

 
3.

Local admin already?

If the user is not local admin already (if they are you will be able to interact with UAC), you will be prompted with this screen

 
804c7c24e3a1b7e2e4847b0a91dffee4a5db438c781e5308d18e7c4f45710207_1_big
4.

Click on ‘Click for additional information’

You will be prompted with the next window

 
97f3e68d0457d8c69d46f92ac4678355ce5193e874433fbaf6ef8e842fdb076c_2_big
5.

Authenticate as the local admin account

This basically allows you to restart the clients Teamviewer session with the local administrator account

A46209bd88214ff72860b0e697fa629dcb2a3b90fc1bf8053810e90d20a42ef8_3_big
6.

Connect & interact with UAC on a standard account!

Once you enter correct credentials, the status in the bottom left will tell you that it is restarting, and then you will join the end user’s session, as always

 
9be1c32a98eb178c326cc6756b624230e8ac4afa9ec9f90217bb41d656bc06c1_4_big

Tested and working

Sample debug output code for BASH/SHELL

When trying to debug your own scripts it can be difficult to see exactly what you want.  Pages of white text scrolling across the screen.  Here is a quick flexible function that can make life a whole lot easier by setting different debug values and instead of using echo, use the function name.

    #################################################
    # Variable Declarations
    #
    #What output to show on screen
    #debug levels:
    # 0 is no debug or logging.
    # 1 is basic output only (gree text)
    # 2 is basic output plus basic debug  (adds yellow text)
    # 3 is show everything (adds red text)
    debuglevel=2
     
    #log to file
    log=0
     
    #where the logfile is located
    logfile="/dev/null"
     
    #################################################
    #  functions
    output(){
            if [ $debuglevel -gt "0" ] && [ $1 = "1" ];
            then
                    /bin/echo -e "`/bin/date +"%m-%d-%Y %r"`:\e[00;32m $2\e[00m"
            fi
            if [ $debuglevel -gt "1" ] && [ $1 = "2" ];
            then
                    /bin/echo -e "`/bin/date +"%m-%d-%Y %r"`:\e[01;33m $2\e[00m"
            fi
            if [ $debuglevel -gt "2" ] && [ $1 = "3" ];
            then
                    /bin/echo -e "`/bin/date +"%m-%d-%Y %r"`:\e[00;31m $2\e[00m"
            fi
            if [ $log -eq "1" ];
            then
                    /bin/echo `/bin/date +"%m-%d-%Y %r"` :  $@ >> $logfile
            fi
    }

    #################################################
    # Code
    #
    output "1" "this is normal output"
    output "2" "this is basic debug output"
    output "3" "this is everything output"

Copy this into a basic script and run it, adjust the debuglevel variable at the top to see different output.  I know this will help me in the future and hope it will help others as well.

User Tracking and Logon Agents

We believe that tracking when users log into a computer is very good information.  We can easily look up where students log in, track computer usage, aid with helpdesk tasks, etc.  Our primary focus was to show that computer labs and carts are being used, and justify the 1:1 initiative we set forth.

This is was not a difficult task to setup but could be useful to others, so here it is!

First we need a good way to get data from the clients.  With Linux, we could do it a multitude of ways, with windows, it can be a bit limited.  We chose to do our data submission with a simple URL get.  Simple, easy, fast, reliable.  With some reading, it wasn’t too difficult to setup with Windows.

First… Server recording
We need a simple script to take variables and insert them into mysql with the needed information. Really all we need is the computer name and username.  We can use the server time and get the client’s IP from the server so that we know it is consistent.  Here is the server script:

<?php
// Written by Kirk Schnable for Marengo Community High School
// July 20th, 2011

/****************** SAMPLE SYNTAX ********************/
/* http://server/login.php?u=USERNAME&h=COMPUTERNAME */
/*****************************************************/

//# MySQL Connection
$link = mysql_connect("MYSQLSERVER", "DB", "PASSWORD") or die(mysql_error());

//# Gather Information
$user = mysql_real_escape_string($_GET['u']);
$hostname = mysql_real_escape_string($_GET['h']);
$ip = mysql_real_escape_string($_SERVER['REMOTE_ADDR']);
$date = date("Y-m-d H:i:s");

//# Query Database
$result = mysql_query("INSERT INTO `iplookup`.`login_records` (`LogonTime` ,`IP`, `Hostname` ,`User`)VALUES ('$date', '$ip', '$hostname', '$user');");
if(!$result){ /* do something on failure of mysql query */ }

?> 

MySQL Database Structure
Using the server side script we can see who the IP was of the user submitting the record, time stamp that event, see who the user is, and what the hostname of the computer is.
loa

Linux Logon Agent
The Linux logon agent could be written many different ways using several programs; curl, wget, lynx, etc.  Simply put it needs to pull two variables and curl that URL…  This needs to be put in the users (and/or skel’s) startup so that it runs at logon   This could be done in a single command like:

curl -s "http://SERVER/logon-agent/login.php?u=$USER&h=$HOSTNAME"

Windows Logon Agent
Unfortunately windows does not have awesome tools like wget, curl, lynx, etc so it is a bit more in-depth to get it working.  However it is easier to deploy using group policy on the server, you can deploy this script pretty easily. Put this script in the User Logon scripts:

' DECLARE ENVIRONMENT VARIABLES
dim URL

' GET SYSTEM VARIABLES
Set wshShell = CreateObject( "WScript.Shell" )
username = wshShell.ExpandEnvironmentStrings( "%USERNAME%" )
computer = wshShell.ExpandEnvironmentStrings( "%COMPUTERNAME%" )

' SET URL TO CONTACT
URL = "http://SERVER/logon-agent/login.php?u=" & username & "&h=" & computer

' ECHO VARIABLES FOR TESTING
'WScript.Echo URL
'WScript.Echo username
'WScript.Echo computer

on error resume next  
Set objXML = CreateObject("MSXML2.ServerXMLHTTP")  

if err then  
	msgbox("Error: " & err.description)  
	wscript.quit 1  
end if  

' Call the remote machine the request  
objXML.open "GET", URL, False  

objXML.send()  

' return the response  
'msgbox objXML.responSetext  

' clean up  
Set objXML = Nothing

Searching For Data!
This is a simple search web tool we created to locate user/computer/IP history.

<head>
<title>Realtime User Login Lookup</title>
<link rel="stylesheet" type="text/css" href="style.css" />
</head>
<div style="text-align: center;">
<span id="header">Computer Login Database</span>
<form method="post" action="">
<span>Lookup IP: </span><input id="ip" type="text" name="ip" value="<?php echo($_GET['ip']); ?>" />
<br/>
<span>Lookup Host: </span><input id="hostname" type="text" name="hostname" value="<?php echo($_GET['host']); ?>" />
<br/>
<span>Lookup User: </span><input id="user" type="text" name="user" value="<?php echo($_GET['user']); ?>" />
<br/>
<span>Records: </span><input id="records" type="text" name="records" value="10000" />
<br/>
<input type="submit" value="Go" />
</form>
</div>

<?php if($_POST){
$link = mysql_connect("SERVER", "USER", "PASSWORD") or die(mysql_error());
$user = mysql_real_escape_string($_POST['user']);
$hostname = mysql_real_escape_string($_POST['hostname']);
$ip = mysql_real_escape_string($_POST['ip']);
$records = mysql_real_escape_string(intval($_POST['records']));
$standarddate = 'l, F jS, Y \a\t g:ia';

if($ip){ // Lookup IP
	echo("<hr/>");
	echo('<div style="text-align: center;">');
	$rdate = mysql_query("SELECT DISTINCT CAST(`LogonTime` AS DATE) AS dateonly FROM `iplookup`.`login_records` WHERE `IP`='$ip'") or die(mysql_error());
	echo("<p><span>This computer has been logged into on " . mysql_num_rows($rdate) . " different days.</span></p>");
	$r = mysql_query("SELECT * FROM `iplookup`.`login_records` WHERE `IP`='$ip' ORDER BY `LogonTime` DESC LIMIT $records") or die(mysql_error());
	if(mysql_num_rows($r) == 0){ echo("<p><span>No Results for IP <b>$ip</b></span></p>"); }
	elseif(mysql_num_rows($r) == $records){ echo("<p><span>Showing the $records most recent logins on <b>$ip</b></span></p>"); }
	else{ echo("<p><span>Showing all " . mysql_num_rows($r) . " previously recorded logins on <b>$ip</b></span></p>"); }
	while($row = mysql_fetch_assoc($r)){
		$date = $row['LogonTime']; $date = strtotime($date); $date = date($standarddate, $date); $username = $row['User']; $host = $row['Hostname'];
		echo("<span><p><b>$username</b> @$host on $date</span></p>");
	}
	echo('</div>');
}

if($user){ // Lookup Username
	echo("<hr/>");
	echo('<div style="text-align: center;">');
	$rdate = mysql_query("SELECT DISTINCT CAST(`LogonTime` AS DATE) AS dateonly FROM `iplookup`.`login_records` WHERE `User`='$user'") or die(mysql_error());
	echo("<p><span>This user has logged in on " . mysql_num_rows($rdate) . " different days.</span></p>");
	$r = mysql_query("SELECT * FROM `iplookup`.`login_records` WHERE `User`='$user' ORDER BY `LogonTime` DESC LIMIT $records") or die(mysql_error());
	if(mysql_num_rows($r) == 0){ echo("<p><span>No Results for User <b>$user</b></span></p>"); }
	elseif(mysql_num_rows($r) == $records){ echo("<p><span>Showing the $records most recent IPs of <b>$user</b></span></p>"); }
	else{ echo("<p><span>Showing all " . mysql_num_rows($r) . " previously recorded IPs of <b>$user</b></span></p>"); }
	while($row = mysql_fetch_assoc($r)){
		$date = $row['LogonTime']; $date = strtotime($date); $date = date($standarddate, $date); $ip = $row['IP']; $host = $row['Hostname'];
		echo("<span><p><b>$ip</b> ($host) on $date</span></p>");
	}
	echo('</div>');
}

if($hostname){ // Lookup Hostname
	echo("<hr/>");
	echo('<div style="text-align: center;">');
	$rdate = mysql_query("SELECT DISTINCT CAST(`LogonTime` AS DATE) AS dateonly FROM `iplookup`.`login_records` WHERE `Hostname` LIKE '%$hostname%'") or die(mysql_error());
	echo("<p><span>This computer has been logged into on " . mysql_num_rows($rdate) . " different days.</span></p>");
	$r = mysql_query("SELECT * FROM `iplookup`.`login_records` WHERE `Hostname` LIKE '%$hostname%' ORDER BY `LogonTime` DESC LIMIT $records") or die(mysql_error());
	if(mysql_num_rows($r) == 0){ echo("<p><span>No Results for Hostname <b>$hostname</b></span></p>"); }
	elseif(mysql_num_rows($r) == $records){ echo("<p><span>Showing the $records most recent logins on <b>$hostname</b></span></p>"); }
	else{ echo("<p><span>Showing all " . mysql_num_rows($r) . " previously recorded logins on <b>$hostname</b></span></p>"); }
	while($row = mysql_fetch_assoc($r)){
		$date = $row['LogonTime']; $date = strtotime($date); $date = date($standarddate, $date); $ip = $row['IP']; $username = $row['User'];
		echo("<span><p><b>$username</b> ($ip) on $date</span></p>");
	}
	echo('</div>');
}

if(!$hostname && !$user && !$ip){ // Show "all" records.
        echo("<hr/>");
        echo('<div style="text-align: center;">');
        $r = mysql_query("SELECT * FROM `iplookup`.`login_records` ORDER BY `LogonTime` DESC LIMIT $records") or die(mysql_error());
        if(mysql_num_rows($r) == 0){ echo("<p><span>No Results.</span></p>"); }
        elseif(mysql_num_rows($r) == $records){ echo("<p><span>Showing the $records most recent logins.</span></p>"); }
        else{ echo("<p><span>Showing all " . mysql_num_rows($r) . " previously recorded logins.</span></p>"); }
        while($row = mysql_fetch_assoc($r)){
                $date = $row['LogonTime']; $date = strtotime($date); $date = date($standarddate, $date); $ip = $row['IP']; $username = $row['User']; $hostnames = $row['Hostname'];
                echo("<span><p><b>$username</b> ($ip) ($hostnames) on $date</span></p>");
        }
        echo('</div>');
}

}
?>

Its complimentary style.css:

BODY{ background-color: maroon; }
SPAN{ color: white; }
SPAN#header{ font-weight: bold; }

INPUT#ip{ width: 175px; }
INPUT#hostname{ width: 150px; }
INPUT#user{ width: 150px; }
INPUT#records{ width: 50px; }

Custom Reports…
Once you have this information you should be able to run reports however you see fit.  We have created a highly custom report for our use which is specific to our school that shows computer usage.  That code is not ready to be posted at this time however here is a glimpse of what could be done.  Please note that ltcart1,2204, and 2506 have been physically relocated to other rooms but because database entries exists, they show up as 0.
report

With this report we can see that with 30 calendar days, there have been 22 instructional days.  This shows that these rooms are using computers almost every day!

 

Web filter testing…

We have been having filtering issues at work.  The only pattern I can see is that upon heavy Internet use (40+Mb) the filtering services have a hard time keeping up.  The problem is that Internet usage is impossible to predict so calling support and trying to reproduce the problem is nearly impossible.  Assuming I can get through at the time it is happening, by the time I update the support personnel on the other end of the phone, it usually starts working again.

Instead of fighting this problem further I came up with a quick script to see how often it really isn’t working, and record my results.

A script that loops though and hits a blocked page every ‘X’ seconds and then parses the HTML results to see if the redirect or block page was served.  This should show my reliability of our web filter during the day without detrimenting any network performance.  I whipped together a quick and dirty script with some functions from my other scripts.  It should do the trick.

#!/bin/bash
#######################################################
# Script created to test web filter's reliability
# It polls a webpage that is supposed to be blocked.
#
#                               Dan Kane
#######################################################

# Web filter Server IP or unique text found in the block page html
WebServer="10.9.1"

# just zeroing the counters
blockedcounter=0

# just zeroing the counters
allowedcounter=0

# Log file location
logfile="/var/log/webfilter.log"

# Blocked url
blockurl="facebook.com"

# used for my output module to show debug information
debug="1"

# used for my output module to log everything
log="1"

# sleep through the loop or go as fast as we can?
sleep="1"

#######################################################
#FUNCTIONS
#

#used for debug and logging
output(){
        if [ $debug -eq "1" ];
        then
                /bin/echo `/bin/date +"%m-%d-%Y %r"`:  $@
        fi
        if [ $log -eq "1" ];
        then
                /bin/echo `/bin/date +"%m-%d-%Y %r"` :  $@ >> $logfile
        fi
}

onexit(){
        echo "didnt work $allowedcounter   :::   worked $blockedcounter"; exit
        exit
}

#######################################################
# CODE
#

# catch traps and show results
trap "onexit" SIGINT > /dev/null

# Enter our 1 second loop
while :
do
        website=$(wget -qO- $blockurl)
        found=$(echo $website | grep $WebServer | wc -l)
        if [ $found -lt 1 ];
        then
                allowedcounter=$(( $allowedcounter + 1 ))
                output "Page NOT blocked  ---  didnt work $allowedcounter   :::   worked $blockedcounter"
        else
                blockedcounter=$(( $blockedcounter + 1 ))
                output "Page blocked  ---   didnt work $allowedcounter   :::   worked $blockedcounter"
        fi
        if [ $sleep -eq "1" ];
        then
                sleep 1
        fi
done

We will start recording the results and see what else we can find out.  If the amount of data we are inserting is too much, maybe insert results into MySQL is in order, but we will see what we find first.