Building a stateless Linux kiosk browser

Linux is a great system for some tasks.  One of these tasks is a simple kiosk browser.  That is, a computer that does nothing but pull up a web page and display the contents and, in my case, attach to a touch-screen to allow the user to interact with the web page. The downside is that these things are hard to keep in a working state.  The power goes out and the system shuts off unexpectedly and damages the filesystem which must now be repaired. Somebody somehow manages to change a setting and you have no idea what they changed. There are other issues that come up but they are all related to the fact that whatever you change in the OS is saved through to the next boot.

That is where stateless comes into play. With a stateless system in place the hard drive is never mounted in a read-write mode, it always stays read-only which means that the power can shut off, you can change whatever you want, it doesn’t matter. The system will always come back up the same way. It took some work to find all the information on setting up a Fedora box to be stateless, with the information scattered over various parts of the Internet. So here will be some documentation on installing Linux on a 4GB flash drive, get it up and running to browse web pages and then turn it into a stateless machine.

For reference, I am installing Fedora 16 32-bit on a DN2800MT ($109) motherboard in a Morex T-1620 ($50) fanless case. This gives me a computer that is slightly smaller footprint than the aluminum Mac Mini (by about 1/2 inch) and about 1/4 inch taller than the same Mac Mini. It has a 2GB memory chip ($10) and a 4GB Thumbdrive I had laying around ($5, be sure you get a quality thumb drive. If you end up with a USB1.1 or even a USB2 that is slow, you will be sorry).  That is it for the computer.  Right now it is hooked up to a Monoprice touch-screen monitor because it was available for testing, for our final product we are looking at other monitor options (such as a Lilliput USB-powered touch-monitor). For reference, this machine as it is running now takes up 12watts.  It peaks at 13w. With the 10″ USB-powered touch monitor (4.8watts) this puts me at about 17 watts which is too much for our standard PoE switches, but withing the 24watts available on PoE+ switches… hint hint hint.  By the way, Fedora 16 is what I had available at the time, I could have used 17 but happened to have 16 already downloaded.  The m/b is actually 64-bit but I think there must be a firmware glitch or something because they do not officially support ANY 64-bit OS.  Linux works fine in 64-bit up until it tries to switch the video mode and then the video shuts off (the system still works, but that doesn’t help me as a display kiosk).

Install and configure the system

  • Begin installing Fedora and set your root password.
  • On the “disk setup” screen select Use All Space, turn off Use LVM and turn on Review and Modify Partitioning Layout. (this will erase existing partitions, so make sure you only have your blank thumb drive plugged in)
  • It created 4 partitions for me, but I didn’t care for the sizes it came up with so we are going to customize it (note by default it puts swap first but I move it to be last):
    • sda1 – 1MB – (blank) – BIOS Boot
    • sda2 – 500MB – /boot – ext4
    • sda3 – 2500MB – / – ext4
    • sda4 – 800MB – (blank) – swap
    • Free – 33MB (I intentionally left a little free in case other thumb drives are slightly smaller than this one)
  • Confirm the boot loader location as /dev/sda.
  • Set the installation type to Minimal and turn on the Fedora 16 – i386 – Updates repository (this will make sure your system is fully up to date after it is installed), and set to Customize Now.
  • On the customize screen turn on Xfce under the Desktop Environmentsgroup. Then go into the Optional packages and make sure the following (and only the following) are selected:
    • NetworkManager-gnome
    • Terminal
    • gdm
    • gtk-xfce-engine
    • leafpad
    • polkit-gnome
    • xfce4-icon-theme
    • xfce4-mixer
    • xfce4-power-manager
    • xfce4-session-engines
    • xfwm4-theme-nodoka
  • Next go into the Base System group and turn on the X Window System item and select (only) the following optional packages:
    • desktop-backgrounds-basic
    • firstboot
    • gdm
    • plymouth-system-theme
    • xorg-x11-utils
  • Continue with the installation and then reboot. You will probably get a warning about a missing dependency for mtdev, just ignore it. We will fix it after the reboot.
  • You should be greeted by a “Welcome” GUI screen. Move forward through the process (Agree to license, set date/time (you will not yet be able to use NTP) and create a user. I used the following information for the new user:
    • Full Name – User
    • Username – user
    • Password – password (we are going to setup auto login anyway…)
  • Switch to a text terminal (CTRL-ALT-F3) and login as root so we can do some more setup. Run the following commands once you are logged in.
    • systemctl disable sendmail.service
    • yum install firefox nano ntp xorg-x11-apps xinput_calibrator
    • systemctl enable ntpd.service
    • The following commands setup DHCP to also retrieve a variable called “site-url”, which we will use to auto-populate the Firefox homepage.
      • cd /etc/dhcp
      • nano dhclient-em0.conf    (“em0” (zero) is the name of the network interface, check with ifconfig to see if yours is different).
      • There should already be a line beginning with “send vendor-…” at the top. Add the following information below this line:
        • option site-url code 224 = text;
        • request subnet-mask, broadcast-address, routers, domain-name, domain-name-servers, host-name, site-url;
      • Use CTRL-X to save and quit.
  • Switch back to the GUI terminal (CTRL-ALT-F2) and login as “user”.
    • You should get a dialog that asks what kind of panel you want, select Use default config.
    • Go to the Applications Menu -> Settings -> Settings Manager
      • Select Appearance.
      • Select the Icons tab.
      • Click HighContrast and then click GNOME. This will toggle the icon settings so all the icons start working.
      • Click the Overview button to go back.
    • Select Power Manager
      • Under General, set the power button action to Shutdown.
      • Under On AC, select the Monitor tab and set the display sleep and switch off display to Never.
      • Click Overview button to go back.
  • Switch back to the Text terminal (CTRL-ALT-F3).
    • cd /etc/gdm
    • nano custom.conf
    • Find the [daemon] section and add these two lines in that section:
      • AutomaticLoginEnable = true
      • AutomaticLogin = user     (this is the username we created above, which I told you to make ‘user’).
    • CTRL-X to save and quit.
    • logout
  • Switch back to the GUI terminal one more time (CTRL-ALT-F2).
    • Press the power button to ensure that the computer shuts down properly. It should do so immediately without asking you anything.
  • Wait for the computer to power down and then turn it back on and ensure that it auto-logs in to the GUI.
    • Run Firefox once to initialize the preferences folder.
    • Set the default homepage to whatever you want (this is basically the fallback homepage if the proper per-computer homepage cannot be found).
    • Open the Tools menu and select Add-ons.
    • Search for R-Kiosk and install it from the list, as of writing it is version 0.9.0 date 9/22/2011.
    • Close Firefox and re-launch, ensure that you are now running in fullscreen mode.
    • Use Alt-F4 to close Firefox.
      • To start Firefox in safe-mode so you can get the menu’s back, use Alt-F2 to open the Run Program dialog and enter:
      • firefox –safe-mode
  • Switch to the Text terminal (CTRL-ALT-F3).
    • Login as root so we can run a few more config commands.
    • cd /usr/local/bin
    • nano xinit.sh
      • Insert the following lines in this new file, then save and exit:
        • #!/bin/sh
        • #
      • This file will be executed at login, so you can put any GUI login initialization in here. For example, the touch-screen I was testing with had the X-axis inverted, so I needed to use the xinput command to fix that at each login.
    • chmod a+x xinit.sh
    • ln -sf /lib/systemd/system/multi-user.target /etc/systemd/system/default.target  (this switches us to Text only boot)
    • cd /etc/rc.d
    • nano rc.local
      • Paste the following lines into this new file and then save and exit:
#!/bin/sh
#

#
# Run everything in the background so normal startup continues.
#
(
    #
    # Give the nework a chance to settle.
    #
    while true; do
        ADDR=`/sbin/ifconfig | grep "inet addr" | grep -v "127.0.0.1"`
        if [ -n "$ADDR" ]; then break; fi
        sleep 10
    done

    #
    # Get the URL from the DHCP client and put it in the firefox prefs.
    #
    URL=`cat /var/lib/dhclient/*.lease | grep site-url | tail -n1 | awk '{print $3}' | sed 's/[\";]//g'`
    PROFILE=`ls -d1 /home/user/.mozilla/firefox/*.default`
    if [ -n "$URL" ]; then
        echo "user_pref(\"browser.startup.homepage\", \"$URL\");" >$PROFILE/user.js
    else
        echo "No URL found" >>/tmp/rc.local.txt
    fi

    #
    # Kill the getty on TTY2 and start up the GUI on TTY2.
    #
    systemctl stop getty\@tty2.service
    ( while true; do /etc/X11/prefdm -nodaemon; done )
) &
    • chmod a+x rc.local
    • This script we just created will wait for the DHCP to pick up an IP address. Then it will pull out the site-url from the DHCP response and, if it existed, put the URL into the homepage of the user’s Firefox profile. Then it will switch to GUI mode. Basically once we update Firefox we then switch to GUI so that Firefox doesn’t load before the network is ready. This is why we set our default boot mode to Text above.
    • cd /etc/rwtab.d
    • nano user
    • Enter a single line the following and then save and quit (this makes the user’s home folder stateless once we enable stateless mode):
      • files /home/user
    • logout
  • Once more, switch back to the GUI to set the run-at-login commands (ALT-CTRL-F2)
    • Go to the Application Menu -> Settings -> Session and Startup
      • Select the Application Autostart tab.
      • Click Add and enter the following information:
        • Name – Firefox
        • Description – Firefox Kiosk
        • Command – firefox (all lower case)
      • Click Add again and enter the following information:
        • Name – xinit.sh
        • Description – Init Scripts
        • Command – /usr/local/bin/xinit.sh
      • Click Close
      • Reboot (you can use the Power button if you want since that is setup).
  • Wait for the computer to boot up.  It should auto-login into the GUI and open Firefox to the “fallback” page you entered previously.
  • At this point you can do any other final configuration you wish before we switch to stateless. For example, if you need to use the xinput command to configure a touch-screen device or something like that you can do so. Another thing you can do is setup a script to run every night at midnight to turn off the screen (e.g. xset dpms force off) and another to run at 6am to turn the screen back on (xset dpms force on).
  • When you are ready to enable stateless mode, move on to the next section.

Enable Stateless Mode

There are only a few things you need to do to switch to stateless mode. Since you have to do some parts in single user mode we will just do everything that way. Boot the machine and at the GRUB menu hit the ‘e‘ key to edit the boot commands. Go down to the long linux line and add single to the end of that line. Then you can hit F10 to continue booting. When it is finished booting you should be already logged in at a command line.

  • mount -o remount,rw /     (Remount the filesystem read-write)
  • mount -o remount,rw /boot
  • cd /etc
  • nano grub2.cfg
  • Find the (first) menuentry and append readonlyroot to the end of the linux line. Save and quit.
  • nano fstab
  • There are 3 UUID= lines in this file. The first two are for / and /boot. Put a # at the beginning of those 2 lines to comment them out. Save and quit. This prevents the system from remounting the filesystem(s) in read-write mode.
  • cd /home/user/.mozilla/firefox
  • cd *.default
  • rm -f user.js
  • reboot

If you ever need to boot back into “full” mode you will have to essentially undo the above. Basically edit the grub boot command again and replace readonlyroot with single, then run the undo the edits to grub2.cfg and fstab. Then you can reboot and be back in “normal” mode.

Once the computer reboots you should now have a fully functional stateless system. This means you can pull the plug as many times as you want and never corrupt the file system. You can also at this point image off your 4GB thumb drive and duplicate it onto other thumb drives for use on various computers. For bonus points you can zero the swap partition and then compress the whole image to make it smaller for storage, but really it is only 4GB…

Configure DHCP server to send site-url’s

Now we have all these computers booting up, but they are all displaying the fall-back URL instead of their own individual URLs. This is pretty simple. I will document how to do this with the ISC dhcpd server that comes with Linux, but Windows’ DHCP server should be able to do the same. If you are using a special device that provides DHCP and you can’t create custom DHCP options to send you can always make your “fall-back” URL point to a PHP or ASPX script and use server-side code to detect what system this is by IP and redirect to the final site. All we are doing is defining DHCP code 224 as a text string and associating a value on a per-MAC basis.

Anyway, on your Linux DHCP server:

  • cd /etc/dhcp
  • nano dhcpd.conf
  • Add a new line near the top, before your subnet declarations:
    • option site-url code 224 = text;
  • Go into your subnet declaration or specific host declaration and add the following:
    • option site-url “http://myserver.fqdn/index.html”;
  • Save the file and restart DHCP and then reboot your new kiosk browser appliance and it should now go to this defined URL.

Leave a Reply

Your email address will not be published. Required fields are marked *