Skip to main content
  1. Posts/

Preparing ThinStation

·1296 words·7 mins· loading · loading ·
Sysadmin Linux
Table of Contents

Introduction
#

Building a thin client tailored to specific hardware comes down to the following steps:

  • Download the full ThinStation repository
  • Build a “fat” (full) image
  • Boot the thin client using the fat image
  • Gather a list of necessary kernel modules and packages for that client
  • Adjust the build configs, leaving only what’s essential (including what we just gathered)
  • Build a “thin” (lightweight) image

Setting the Stage
#

Let me note right away that there’s an alternative method: downloading a prebuilt .iso image. However, I find that less flexible, so I’ll describe the “proper” approach.

Cloning the Repository
#

Basic Git knowledge is recommended for working with ThinStation, as you’ll need a way to save your changes — and it’s easy to get lost in the file hierarchy once the system is unpacked. Cloning is done with:

1git clone --depth 1 git://github.com/Thinstation/thinstation.git -b 5.5-Stable

The 5.5-Stable is the current branch. Unfortunately, the repository has grown — it used to be just over 2 GB in 2014, and now it’s over 8 GB. That’s why the --depth 1 option is recommended in most guides.

It’s also worth noting that the maintainers recommend cloning as root — I found this out when trying to submit [file ownership fixes][pullrequest-filesmode]. Whether this is “correct” is up for debate, but if you run into permission issues, it might help.

Preparing chroot
#

In the root directory of the cloned repository is a bash script named setup-chroot. You need to run it as root because it mounts system directories. The most useful flag at this stage is -a — it avoids interactive prompts and installs everything automatically.

1cd thinstation
2sudo ./setup-chroot -a

Everything else will be done from inside this chroot. Initial setup may take up to an hour (especially on slow disks), but afterward, re-entry is nearly instant.

Fat Image
#

This step is a warm-up: we build a full image to boot the hardware and identify required modules and packages. If you manage a mix of hardware or plan future purchases, keep this image handy. You’ll only need to rebuild it after major changes (e.g., a new kernel version).

It’s possible your hardware already has support. Check ts/build/machine for your platform. If you find it — skip this step.

Build Configuration
#

Start by editing ts/build/build.conf (copy it from build.conf.example if needed). Uncomment the following lines:

 1...
 2package lshw           # For hardware info
 3...
 4package xorg7-v4l
 5package xorg7-vesa
 6package xorg7-vmware
 7package xorg7-ati
 8package xorg7-nouveau
 9package xorg7-openchrome
10package xorg7-intel
11package xorg7-sis
12package extensions
13package extensions-x
14param initrdcmd "gzip"
15param allres true
16param allfirmware true

Why switch compression to gzip? Because the hwlister.sh script uses file access times in /lib/firmware to detect used firmware. But squashfs mounts with relatime, so access times don’t change. gzip solves this easily. I [reported this issue][issue-squashfs], but haven’t heard back yet.

Building the Image
#

Enter the chroot, then run build:

1soar@localhost $ sudo ./setup-chroot
2[root@TS_chroot]/# cd build
3[root@TS_chroot]/build# ./build --allmodules --license ACCEPT --autodl

When done, the boot-images directory will contain bootable images — ISO, PXE, syslinux. Use whichever suits your needs.

Gathering Data
#

After successfully booting the hardware, drop to a shell and run:

1hwlister.sh

This creates:

  • /firmware.list
  • /module.list
  • /package.list (e.g., xorg7-*)
  • /vbe_modes.list (if using uvesafb)

Some files may be absent if no matching data was found.

Also, save the output of lshw — it may help later when the hardware is no longer at hand.

This script will attempt to upload files to your configured TFTP server — but if, like me, you’ve disabled TFTP write access, just fetch them manually and place them under ts/build/machine/MACHINENAME.

Thin Image
#

This is where you balance functionality and size. Smaller size means faster PXE boot, faster system start, and lower RAM usage. I needed an image for a single use case: an RDP terminal client. Here’s how.

For example, we have a wired office with thin clients that:

  • Get IP via DHCP
  • Boot via PXE
  • Launch a single app — an RDP client

Build Configuration — build.conf
#

  • Comment out all machine lines except the one you’re using
  • Keep only essential filesystems — vfat, ntfs; comment out isofs, udf, ext*
  • Comment all package xorg7-* lines if you have a custom machine profile
  • Comment all package locale-* except ru_RU and (optionally) en_US
  • Enable package sshd if you need remote access
  • Enable package ccidreader for smart cards / USB tokens
  • If you plan to skip window managers/desktops and show just one app (e.g., FreeRDP), enable package automount; disable package udisks
  • To keep it lightweight: enable package openbox; disable gtk-*, icons-*, fonts-*

Don’t forget to switch back to param initrdcmd "squashfs" and remove:

1package alltimezone
2param allres true
3param allfirmware true

Runtime Configuration — thinstation.conf.buildtime
#

This file acts like a bash script that sets environment variables for all boot-time scripts.

For an RDP session, an example config might include:

 1# As the user won't have any local UIs, set volumes to max
 2AUDIO_LEVEL=100
 3MIC_LEVEL=100
 4
 5# Somewhere to send logs, as we don't have a local storage
 6SYSLOG_SERVER=syslog.example.com
 7
 8# Locale
 9LOCALE=ru_RU.UTF8
10TIME_ZONE=Europe/Moscow
11
12# As we don't have a button to eject USB drives, we need this
13USB_STORAGE_SYNC=ON
14DISK_STORAGE_SYNC=ON
15# This will be a directory mounted to the remote desktop
16USB_MOUNT_DIR=/mnt/usb
17# This is my set of parameters suitable for FreeRDP, Windows, FAT32/NTFS and some unusual charsets
18USB_MOUNT_OPTIONS="rw,nosuid,nodev,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,showexec,utf8,flush,errors=remount-ro"
19DISK_MOUNT_OPTIONS="rw,nosuid,nodev,relatime,fmask=0022,dmask=0022,codepage=437,iocharset=ascii,shortname=mixed,showexec,utf8,flush,errors=remount-ro"
20
21# We will also need DHCP
22NET_USE_DHCP=ON
23
24# Desktop
25SESSION_0_TITLE=Desktop
26SESSION_0_TYPE=openbox
27SESSION_0_AUTOSTART=ON
28
29# ... and remote desktop
30SESSION_1_TITLE=RemoteDesktop
31SESSION_1_TYPE=freerdp
32SESSION_1_AUTOSTART=ON
33SESSION_1_FREERDP_SERVER=rdp.example.com
34SESSION_1_FREERDP_OPTIONS="+decorations +fonts +aero ..."

Building the Thin Image
#

With config done, build the lightweight image:

1soar@localhost $ sudo ./setup-chroot
2[root@TS_chroot]/# cd build
3[root@TS_chroot]/build# ./build --license ACCEPT --autodl

Depending on your build.conf, you’ll get bootable images for PXE, CD-ROM, disk, or USB. My setup yielded ~90 MB image size and ~1-minute PXE boot time (from power-on to desktop). From a local disk — even faster.

Other Possibilities
#

Everything described above assumes one universal image. But you may want several client variants (e.g., different server addresses). ThinStation supports loading extra configs and modules at boot — this is well documented, so I won’t cover it here.

Useful Notes
#

Cleaning the Kitchen
#

When switching package versions or compiling binaries, you’ll eventually need to clean up:

  1. Exit chroot
  2. Ensure your changes are committed to Git
  3. Unmount system FS: umount -R thinstation/*
  4. Run: sudo ./setup-chroot -a
  5. Remove leftovers: git clean -dx (deletes all untracked files)

Adding Your Own Packages
#

ThinStation (and CRUX Linux) distinguishes between:

  • package – installable configuration unit (may just be a config file or list of dependencies)
  • port – similar to .deb or .rpm, but only holds a file tree; build/install logic is in side scripts

If you just need to add a few config files — create a package. For binaries — make a port.

Creating a Custom Port
#

Add your directory in ts/etc/prt-get.conf:

1prtdir /ts/ports/yourproject

Create a Pkgfile:

 1name=mdetect-TS
 2version=0.5.2.3
 3release=1
 4source=(http://ftp.de.debian.org/debian/pool/main/m/$name/$name-$version.tar.bz2)
 5
 6build() {
 7	cd $name-$version
 8
 9	./configure --prefix=/usr \
10			--exec-prefix=/ \
11			--sysconfdir=/etc \
12			--mandir=/usr/man \
13			--disable-extras
14
15	make
16	make DESTDIR=$PKG install
17}

Build the port:

1cd ts/ports/yourproject/portname/
2pkgmk -kw

To fix footprint or checksums:

1pkgmk -kw
2pkgmk -uf
3pkgmk -um

Then install into your ThinStation build environment:

1prt-get install portname

Creating a Package
#

Minimal example:

1ts/build/packages/mypackage/
2├── dependencies
3├── etc
4│   └── somefile-placed-in-etc

Add the package in build.conf. If it depends on your port, include:

1# install
2export PACKAGE=mypackage
3export PORTS=$PACKAGE
4repackage -e
5
6# remove
7export PACKAGE=mypackage
8repackage -c

Updates
#

Sometimes the system can become inconsistent (e.g., if .footprint is outdated or install was interrupted). My go-to recovery steps:

1prt-get update mypackage
2prt-get update -uf mypackage
3prt-get update -um mypackage
4./build --removeall
5prt-get remove mypackage
6update mypackage
7prt-get install mypackage
8./build --license ACCEPT --autodl
@soar
Author
@soar
Senior SRE/DevOps engineer

Related

Introduction into storcli
·198 words·1 min· loading · loading
Sysadmin Hardware Linux
Hello, FirewallD
·888 words·5 mins· loading · loading
Sysadmin Firewall Imho Linux
ipset-persistent — sysv init for ipset
·230 words·2 mins· loading · loading
Sysadmin Linux Firewall
iptables-persistent but for ipset