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/0Function 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 0NOTE1: Some keyboard shortcuts may actually be registered by the window manager rather than the application.