Web page slide show



Intro

These days we seem to be monitoring more and more monitors that have web interfaces.

Chances are that all these monitors cannot be consolidated into one big picture. I have one monitor for Unix system health (Nagios), one monitor for Windows system health (SCOM), one monitor for external network link health (Telco provider), one monitor for incoming help desk tickets, and on and on the list of things that I have to monitor goes.

So I looked into creating a web page slide show of all these monitors.


Windows: The VBScript approach

I have written a number of VBscripts that do some amazing automation on a MicroSoft system including automatically logging into a system. They are easy enough to write but are not my preferred approach. Lugging around a Windows server and license is just too much baggage for the task at hand.


Unix: The ultralight JavaScript approach

I borrowed this idea from someone else. It seems to work quite well however it has a limitation that requires you to log into the pages beforehand to circumvent the login dialogs. So I looked into that further.

Doing systems stuff with JavaScript is akin to stroking a cat's fur backwards - often it can be done, but it's really uncomfortable. In my quest to be able to send keystrokes to the browser so that it could automatically login, I homed in on using JavaScript's KeyBoardEvent handlers (createEvent, initKeyEvent, dispatchEvent). My attempts required a lot of Googling and an acceptance that most of the JavaScript examples I found actually didn't work. In the end it proved futile as the DOM simply does not allow you to send keystrokes to input text fields - regardless of what some of the time wasters on the net would say and not back it up with working examples.

Below is a really basic JavaScript example that just cycles through a list of web sites in a FireFox browser.

<html>
  <head>
  <meta http-equiv="content-type" content="text/html; charset=UTF-8">
  <title>Web Page Slide Show</title>
    <script language="Javascript">
var mainWindowTimeout  = 604800000;
var childWindow        = null;
var childWindowTimeout = 10000;
var childWindowTimer   = 0;
var childWindowIndex   = 0;

/* Webpage Timeline */
var secondsArray = Array("20","20","20","20");
var urlArray     = Array("http://nagios.mycompany.com.au/index.tml",
                         "http://scom.mycompany.com.au",
                         "http://mycompany.portal.telco.com.au?show=30",
                         "https://helpdesk.mycompany.com.au/reports?status=open&list=25&sort=date");

function start()
{ 
  childWindow = window.open("http://www.mycompany.com.au/", "childWindow", "width=1240,height=1028,toolbar=0,scrollbars=0,location=0,resizable=0,directories=0,status=0,menubar=0,fullscreen=0");
  childWindow.moveTo(10,10);
  childWindow.resizeTo(1240,1028);
  childWindowTimer = setTimeout('nextUrl()', childWindowTimeout);
  /* Refresh the main window after a long while. */
  if (mainWindowTimeout > 0) setTimeout('mainWindowRefresh()', mainWindowTimeout);
  childWindow.focus();
}

function mainWindowRefresh()
{ 
  clearTimeout(childWindowTimer);
  childWindow.close();
  document.location.reload(true);
}

function changeDisplay()
{
  childWindow.location = urlArray[childWindowIndex];
  childWindowTimer = setTimeout('nextUrl()', secondsArray[childWindowIndex] * 1000);
}

function nextUrl()
{
  childWindowIndex++;
  if (childWindowIndex > urlArray.length)
    childWindowIndex = 0;
  changeDisplay();
}

    </script>
  </head>
  <body onload="start();" bgcolor="#000000" text="#aaaaaa">
  </body>
</html>

This is as good as it gets. JavaScript (or rather the DOM) is just not going to allow me to send keystrokes to input fields inside the browser.


Unix: Other approaches

Thinking laterally, there are a few other ways of sending keystrokes to another process to drive it.

Ioctl
This is a heavyweight approach requiring a C program to call the ioctl() API. Unless you're really keen then forget it.

Write to stdin
Bash is pretty crafty with managing file descriptors, and Linux provides you with access to the the /proc namespace. Unfortunately the following didn't do anything with the PID of my FireFox window.

echo "http://www.mycompany.com" >> /proc/<PID-FireFox>/fd/0

Function Keys
Back in the days of dumb terminals, you could send an escape sequence to program a function key on the user's keyboard with some text, and then send another escape sequence to keypress that function key. To think that's how we used to autologin into Novell Netware.

Clipboard
Just like the function key method described above but use the clipboard buffer instead.


Unix: The xdotool approach

If you're comfortable running X11, then this is the ideal tool for controlling windows. xdotool is available from the EPEL repo.

xdotool is feature rich and allows you not to just send keystrokes, but mouse and button events as well.

Below is a Bash script that generates the same web page slide show as the JavaScript example above. The difference is that the Bash script is more extensible and easier to maintain.

#!/bin/sh
#
# BSD License for webshow.sh - create a web based slide show.
# Copyright (c) 2015, Arthur Gouros
# All rights reserved.
# 
# Redistribution and use in source and binary forms, with or without 
# modification, are permitted provided that the following conditions are met:
# 
# - Redistributions of source code must retain the above copyright notice, 
#   this list of conditions and the following disclaimer.
# - Redistributions in binary form must reproduce the above copyright notice, 
#   this list of conditions and the following disclaimer in the documentation 
#   and/or other materials provided with the distribution.
# - Neither the name of Arthur Gouros nor the names of its contributors 
#   may be used to endorse or promote products derived from this software 
#   without specific prior written permission.
# 
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 
# POSSIBILITY OF SUCH DAMAGE.
#
# Author: Arthur Gouros 16/12/2015

# Set X target
export DISPLAY=":0.0"

launch_ff()
{
  # Get the PID of any firefox session otherwise spawn a new one.
  FF_PID="`/usr/sbin/pidof -s firefox`"
  if test -z "${FF_PID}"
  then
    /usr/bin/firefox --new-window "http://www.mycompany.com.au" --foreground > /dev/null 2>&1 &
    FF_PID=$!
    sleep 10
  fi

  FF_XWID=`xdotool search --onlyvisible --pid ${FF_PID}`
  if test $? -ne 0
  then
    # An alternate way of finding the same thing.
    FF_XWID=`xdotool search --name "Mozilla Firefox"`
  fi

  # Activate the FireFox window
  xdotool windowactivate --sync ${FF_XWID}

  # Resize the window accordingly
  xdotool windowsize --sync ${FF_XWID} 1240 1028
}


check_ff_is_running()
{
  /bin/ps -p ${FF_PID} > /dev/null 2>&1
  if test $? -ne 0
  then
    launch_ff
  fi
  xdotool windowfocus --sync ${FF_XWID}
}


load_url()
{
  pause="${1}"
  url="${2}"
  shift 2

  # Ensure a target window is available
  check_ff_is_running

  # Send shortcut to stop loading the old page
  xdotool windowfocus --sync ${FF_XWID} key 'Escape'
  sleep 0.5

  # Send shortcut to focus on the title bar
  # Note: Control+L is a registered hotkey in the Gnome UI. This is the work around.
  xdotool windowfocus ${FF_XWID} key 'ctrl+l'

  # Send shortcuts to clear the title bar
  xdotool windowfocus --sync ${FF_XWID} key 'ctrl+a' BackSpace
  #xdotool key --window ${FF_XWID} 'ctrl+a' BackSpace

  # Load URL
  xdotool type --window ${FF_XWID} "${url}" 
  xdotool key --window ${FF_XWID} Return

  # Send extra keystrokes if any
  process_extra_keys $*

  # Pause
  sleep "${pause}"
}


process_extra_keys()
{
  if test $# -gt 0
  then
    # Check for iteration restrictions.
    if test "${1}" = "RUNONCE"
    then
      if test "${n}" = "1"
      then
        shift 1
      else
        return
      fi
    fi

    while test $# -gt 0
    do
      cmd="${1:0:1}"
      val="${1:2}"
    
      case "${cmd}" in

        w ) sleep "${val}"
            ;;

        t ) # Send string to target window
            xdotool type --window ${FF_XWID} "${val}"
            ;;

        T ) # Set focus on target window then send string
            xdotool windowfocus --sync ${FF_XWID} type "${val}"
            ;;

        k ) # Send keystroke to target window
            xdotool key --window ${FF_XWID} "${val}"
            ;;

        K ) # Set focus on target window then send keystroke
            xdotool windowfocus --sync ${FF_XWID} key "${val}"
            ;;

        m ) # Move mouse pointer on target window to co-ords
            xdotool mousemove --window ${FF_XWID} ${val}
            ;;

        M ) # Set foucu on target window then move mouse pointer to co-ords
            xdotool windowfocus --sync ${FF_XWID} mousemove ${val}
            ;;

        c ) # Send mouse click to target window (1=left, 2=middle, 3=right)
            xdotool click --window ${FF_XWID} "${val}"
            ;;

        C ) # Set focus on target window then send mouse click (1=left, 2=middle, 3=right)
            xdotool windowfocus --sync ${FF_XWID} click "${val}"
            ;;
      esac

      shift 1
    done
  fi
}

#----------------
# Main
#----------------
n=1
while (true)
do
  load_url 20 'http://nagios.mycompany.com.au/index.tml' 't:hello'
  load_url 20 'http://scom.mycompany.com.au' RUNONCE 'w:2' 't:arthur' 'k:Tab' 't:secret' 'k:Return' 'w:5'
  load_url 20 'http://mycompany.portal.telco.com.au?show=30'
  load_url 20 'https://helpdesk.mycompany.com.au/reports?status=open&list=25&sort=date'

  n=$((n += 1))
done

exit 0

NOTE1: Some keyboard shortcuts may actually be registered by the window manager rather than the application.