Skip to main content
  1. Our Guides/
  2. CoreDNS @ Wolfspyre Labs/
  3. 🏗️ Setting Up CoreDNS/
  4. Pi4 Soup to nuts/

⑦ Extra Credit

🐾

PaperTTY: An eInk console #

PaperTTY 1 is a nifty python tool to display a console TTY on an eInk display. Here we use it to give us an easily viewable status indicator.

I wanted some mechanism of easily assessing the health of the DNS hosts…
I didn’t want to spend too much time on it, but I wanted it to be robust enough that it would ‘just work’ consistently.

I (admittedly briefly) explored a few different tools, and landed on a combination of PaperTTY and 🔗DNSTop

There are definitely other ways to achieve this goal. While I’ll likely play with others later..
…This works well enough for the moment.

dnstop


Install Pre-Reqs for PaperTTY #

Apt Packages #

Installing the needful
apt-get install libjpeg-dev libjpeg-dev libtiff6 python3-pip  
python3-pip-whl python3-setuptools-whl python3-venv  
python3-rgpio python3-rpi.gpio rgpio-tools librgpio
python3-gpiozero python3-rpi.gpio rpi.gpio-common

Poetry #

Poetry2 is a packaging and dependency management toolchain for Python.

They made installing Poetry really easy in ubuntu 24:

Install Poetry
apt-get install python3-poetry

Quickly test and validate that Poetry is functional #

Poetry Version evaluation
root@ubuntu:~# poetry --version ; cat /etc/lsb-release
Poetry (version 1.8.2)
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=24.04
DISTRIB_CODENAME=noble
DISTRIB_DESCRIPTION="Ubuntu 24.04.1 LTS"
Danger! The Internet is scary!
I do not advocate running random shit on the internet piped into an interpreter sight unseen.
I STRONGLY encourage you to look at the script (found here)
before running the install one-liner below:

Install Poetry #

Install Poetry
curl -sSL https://install.python-poetry.org | python3 -
Symlink poetry to user-scoped bin directories
_B='/root/.local/share/pypoetry/venv/bin/poetry'
for DIR in '/root/bin' '/root/.local/bin'; do
  TARGET="${DIR}/poetry"; \
  makeDirIfNeedful "${DIR}"; \
  if [[ -e ${TARGET} ]]; then \
    _L=`ls -la ${TARGET}|awk '{print $11}'`
    echo " TARGET: ${TARGET} exits. ${_L}";
  else 
    warn "TARGET: ${TARGET} does NOT exist. Linking";
    ln -s ${_B} ${TARGET}
    ls -la ${TARGET}
  fi
done

Quickly test and validate that Poetry is functional #

root@coredns-03:~# poetry --version
Poetry (version 1.2.1)

PaperTTY Installation #

Clone the Repo #

root@coredns-03:~# git clone https://github.com/joukos/PaperTTY.git
Cloning into 'PaperTTY'...
remote: Enumerating objects: 801, done.
remote: Counting objects: 100% (29/29), done.
remote: Compressing objects: 100% (21/21), done.
remote: Total 801 (delta 13), reused 20 (delta 8), pack-reused 772
Receiving objects: 100% (801/801), 7.00 MiB | 13.36 MiB/s, done.
Resolving deltas: 100% (497/497), done.
root@coredns-03:~#

Build PaperTTY #

root@coredns-03:~# cd ~/PaperTTY && poetry install

Actually running the command should look something like this:

root@coredns-03:~# cd ~/PaperTTY && poetry install
Creating virtualenv papertty-uSU0pReX-py3.10 in /root/.cache/pypoetry/virtualenvs
Updating dependencies
Resolving dependencies... (163.6s)

Writing lock file

Package operations: 1 install, 9 updates, 0 removals

  • Updating attrs (19.3.0 -> 22.1.0)
  • Updating idna (2.9 -> 3.4)
  • Updating setuptools (65.3.0 -> 50.3.2)
  • Updating six (1.15.0 -> 1.16.0)
  • Updating hyperlink (19.0.0 -> 21.0.0)
  • Updating incremental (17.5.0 -> 21.3.0)
  • Updating pyhamcrest (2.0.2 -> 2.0.3)
  • Updating zope-interface (5.1.0 -> 5.4.0)
  • Installing rpi-gpio (0.7.1)
  • Updating spidev (3.4 -> 3.5)

Installing the current project: papertty (0.1.8b)

Pretty neat and simple right?
Hopefully this got you a working setup, lets make sure real quick:

Quick test of PaperTTY #

So… a quick test of PaperTTY will take you down one of a few paths….

poetry run papertty --driver EPD2in13v2 scrub && \
echo "Evey loves steak" | poetry run papertty --driver EPD2in13v2 stdin

“Worky ish” #

So… Cool! It works…

eveysteak

BUT… it’s not terribly useful just yet without a magnifying glass…

SO lets continue, and install fonts, shall we?

Not so worky? #

Several things are likely to go wrong here…

Unless you lucked out, and things worked, head on over to the eInk Weirdness page and lets get you sorted out…

… or try to at least….

Then come back here and continue!


Installing Font-related packages #

PaperTTY includes the TomThumb3 Font. This is awesome, but pretty small. So, there’s a handful of fonts that can be installed and will work with PaperTTY…
In addition to the fonts available through apt, You can simply deploy TrueType fonts into /usr/share/fonts/truetype/ and reference them when launching PaperTTY.

Quick shout-out to OpenDyslexic4… Which is a neat font with increased readability. It wasn’t suitable to my needs here, but is useful in other contexts.

Install the fonts #

apt-get install -y xfonts-utils xfonts-encodings x11-common \
ttf-mscorefonts-installer libfontenc1 fonts-liberation cabextract 

The process of installing these packages will require you to accept a user agreement ( remember, it IS microsoft we’re talking about here… )

cd /tmp
_FONTDIR="/usr/share/fonts/truetype"
_R=ubuntu-font-family
_V=0.83
_ZipDir="${_R}-${_V}"
_F="0cef8205-${_ZipDir}.zip"
wget "https://assets.ubuntu.com/v1/${_F}"||exit 1
unzip ${_F} "${_ZipDir}/*" -d "${_FONTDIR}/"

Once you’ve installed the fonts; you can make things a hair more legible:

poetry run papertty --driver EPD2in13v2 scrub && echo "Evey loves steak" | \
poetry run papertty --driver EPD2in13v2 stdin --size=10 \
--font="/usr/share/fonts/truetype/msttcorefonts/Andale_Mono.ttf" 
eveysteak

Much more legible, IMHO… #

To be fair, from a legibility perspective, both are kinda small; but this thing IS only
2" wide and 1" tall.

fonts11

PaperTTY Start script #

The purpose of this script is to make the (re)configuration of the service independent from the invocation of PaperTTY.
(In other words, you won’t have to jiggle the systemd daemon-reload handle anytime you make changes to the PaperTTY start script.)

Install the following script someplace in your $PATH.
I personally chose /root/bin/start-PaperTTY.sh.
Note
If you put this script in a different location, be sure to change your systemd init script for papertty accordingly.
/root/bin/start-PaperTTY.sh:

#!/usr/bin/env bash
set -e
DATESTRING=$(date +%m%d%y)
_STARTTIME=$(/bin/date +%s)
runningTime () {
  _NOW=$(/bin/date +%s)
  echo -e "$(( ${_NOW}-${_STARTTIME}  ))"
}

convertsecs() {
 #take an int which is a number of seconds (IE:
 # now=$(date %s) ; sleep 30; newer_now=$(date %s); age=(${newer_now)-${now}) ; info "This took: $(convertsecs $age)"
 # ^^^^^---- untested example
 ((h=${1}/3600))
 ((m=(${1}%3600)/60))
 ((s=${1}%60))
 printf "%02d:%02d:%02d\n" ${h} ${m} ${s}
}

error () {
  MSG=$1
  _time=$(convertsecs $(runningTime))
  echo -e "\033[31m[${_time}] ERROR\033[0m: $MSG" >>/dev/stderr
}

warn () {
  MSG=$1
  _time=$(convertsecs $(runningTime))
  echo -e "\033[1;33m[${_time}] WARN\033[0m: $MSG" >>/dev/stderr
}

info () {
  MSG=$1
  _time=$(convertsecs $(runningTime))
  echo -e "\033[32m[${_time}] INFO\033[0m: $MSG"
}
VENVROOT='/root/.cache/pypoetry/virtualenvs'
PTTYROOT='/root/PaperTTY'
POETRYBIN='/root/bin/poetry'
if [[ `ls ${VENVROOT}|grep -c papertty` -ne 1 ]]; then
  error "I don't seem to be able to identify the papertty virtualenv. "
  sleep 1
  error "I'm looking for exactly one match on 'papertty' here: ${VENVROOT}:"
  error "Contents listed below....  `ls -lA ${VENVROOT}`"
  sleep 1
  error "I'm Not sure what to do. So... Executing maneuver FORCIBLY INSERT THUMBS UP ASS"
  sleep 3
  error "Ow. I'm In pain."
  false;
else
  _V=`ls ${VENVROOT}/papertty*/bin/activate`
  if [[ -e "${_V}" ]]; then
    info "WolfspyreLabs PaperTTY Wrapper -=/root/bin/StartPaperTTY=-"
    info "Virtualenv initializer: ${_V} "
    . ${_V}
    info "switching to PaperTTY Dir: ${PTTYROOT}"
    cd ${PTTYROOT}
    info "What a mess!!  Scrubbing the Eink Display"
    ${POETRYBIN} run papertty --driver EPD2in13v2 scrub
    info "Fuck...  what a day"
    info "Starting PaperTTY Terminal on /dev/vcsa3"
    info "  partial refresh disabled. Cursor disabled."
    info "  sleep 0.9. with an extra scrub first.... ..Because we care."
    info "     Thank you for choosing WolfspyreLabs "
    sleep 1
    info "Fuck your day.  --hugs n Kisses"
    ${POETRYBIN} run papertty --nopartial --driver EPD2in13v2 terminal --vcsa=/dev/vcsa3  --font="/usr/share/fonts/truetype/msttcorefonts/Andale_Mono.ttf" --size=9 --cols 51 --rows 13 --sleep 0.9 --scrub --cursor=none
  else
    error "${_V} is not real! BOO"
    false
  fi
fi

PaperTTY systemd service file #

/lib/systemd/system/papertty.service:

[Unit]
Description=PaperTTY
DefaultDependencies=no
After=getty.target

[Service]
Type=simple
KillSignal=SIGINT
TimeoutStopSec=8
Restart=on-failure

### Change the paths below to match yours
WorkingDirectory=/root/PaperTTY
ExecStart=/root/bin/StartPaperTTY
###

[Install]
WantedBy=sysinit.target

Install the systemd script #

cat <<EOF>/lib/systemd/system/papertty.service
[Unit]
Description=PaperTTY
DefaultDependencies=no
After=getty.target

[Service]
Type=simple
KillSignal=SIGINT
TimeoutStopSec=8
Restart=on-failure

### Change the paths below to match yours
WorkingDirectory=/root/PaperTTY
ExecStart=/root/bin/StartPaperTTY
###

[Install]
WantedBy=sysinit.target
EOF

rc.local script #

As pointed out on the PaperTTY Readme5 Occasionally agetty goes a little apeshit and needs to be kicked after papertty is started on a tty… I added this as a belt-and-suspenders prophylactic to keep the hosts stable.

it may not be necessary. YMMV.

‘/etc/rc.local`:

/#!/bin/bash
#/etc/rc.local

/etc/init.d/procps restart

exit 0

Last but not least… enable and start the PaperTTY Service #

Its the simplest of things… …it’s so easily overlooked…

Warning
I may or may not have forgotten to do this and sat scratching my head for a bit wondering why stuff wasn’t working….

systemctl enable papertty.service
systemctl start papertty.service

And with that, You’ve done it!

  • PaperTTY is now running
  • It’s bound to the virtual terminal you specified in the PaperTTY Start script.
  • AND it aught be started next time you reboot too!

Strong work!

Now lets set up getty to run DNSTop on the virtual terminal PaperTTY is displaying.

🐾

DNSTOP #

So, there’s not really a whole lot to this, once the kinks are identified and worked out, it’s pretty clean, really.

Packages #

apt-get install dnstop tio ttylog rungetty tio ttylog ttysnoop 

The two most important packages here are rungetty6 and dnstop7.

Rungetty #

If you’ve not poked much into the way a console is managed in linux, this might be a little weird.

TLDR: getty ((or something similar)) listens on known locations for user activity and does the needful for that context.. Usually this involves starting a ’login’ process and letting things go from there.


Rungetty start script #

We need to tell the system we want another virtual terminal instantiated.

and we want that terminal instantiated in a fashion which permits us to launch an arbitrary command instead of init / login

The simplest way of doing that is by creating a new singleton service which is responsible for making sure that a console manager process is started just for our chosen virtual terminal, and giving it unique startup parameters which tell it that we want it to run ((in our case)) ‘dnstop’ instead of login

Much like we did with papertty, the way to do this is by creating an init script / systemd startup file. Lets deploy one and continue on, shall we?

Rungetty init script #

/etc/systemd/system/getty-tty3.service

[Unit]
Description=Getty on vcsa3
Documentation=man:agetty(8) man:systemd-getty-generator(8)
Documentation=http://0pointer.de/blog/projects/serial-console.html
After=systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target
After=rc-local.service

# If additional gettys are spawned during boot then we should make
# sure that this is synchronized before getty.target, even though
# getty.target didn't actually pull it in.
Before=getty.target
IgnoreOnIsolate=yes

Conflicts=rescue.service
Before=rescue.service

# On systems without virtual consoles, don't start any getty. Note
# that serial gettys are covered by serial-getty@.service, not this
# unit.
ConditionPathExists=/dev/tty3

[Service]
ExecStart=/sbin/rungetty --autologin root tty3 -u root -g root -- /usr/bin/dnstop -l3 -4QRr5 eth0
Type=idle
Restart=always
RestartSec=0
UtmpIdentifier=tty3
TTYPath=/dev/tty3
TTYReset=yes
TTYVHangup=yes
TTYVTDisallocate=yes
KillMode=process
IgnoreSIGPIPE=no
SendSIGHUP=yes
UnsetEnvironment=LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREM
ENT LC_IDENTIFICATION

[Install]
WantedBy=getty.target

Once you’ve put this in place, much like you did with the PaperTTY script, you’ll need to enable it, and start it.

Enable the rungetty process #
systemctl enable getty-tty3.service
Start the rungetty process #
systemctl start getty-tty3.service

This will start a dnstop process as root, listening for dns traffic on eth0 and emitting some metrics.

I’ll leave adjusting dnstop’s parameters as an exercise for the reader.

And that’s about it for dnstop.

WTFUtil #


#