Control your backup NAS from the desktop
Programming Snapshot – Remote Backup
![© Lead Image © alphaspirit, 123RF.com © Lead Image © alphaspirit, 123RF.com](/var/linux_magazin/storage/images/issues/2023/272/magic-cargo/123rf-alphaspirit_running_man_laptop.png/823160-1-eng-US/123RF-alphaspirit_running_man_laptop.png_medium.png)
© Lead Image © alphaspirit, 123RF.com
To be able to power up and shut down his NAS and check the current status without getting out of his chair, Mike Schilli programs a graphical interface that sends a Magic Packet in this month's column.
As a backup solution, I use a Synology NAS with some hefty hard drives. But because of the strict regulations driven by paranoia and noise-protection goals in the hallowed halls of Perlmeister Studios, the device only runs when actually needed (i.e., when a backup is running). To switch it on when needed, I prefer to push a mouse around a GUI and click occasionally instead of getting out of my chair.
The Syno desktop application presented here (Figure 1) powers up the NAS via the local network at the push of a button and graphically displays the milestones during the boot process with progress bars. As soon as the system has finished booting and is ready for access, it notifies the user.
After the work is done, a mouse click on the GUI's Down button is all it takes, and the NAS receives the command to shut down via the network. While the shutdown is running, the GUI checks whether the NAS is still operational or if it is no longer responding to pings and has finally gone to sleep (Figure 2).
Just Switch It On
How does this magic work? Even when switched off, the NAS actively waits for a Wake-on-LAN (WoL) signal on the local network. Despite its control lights being off, the network card is running in a low power mode. If a matching broadcast packet arrives via the LAN, it tells the device's power supply or motherboard to switch on. The Magic Packet sent in this case contains the target system's MAC address, so that a controlling transmitter can notify different network nodes independently.
Figure 3 shows a practical example of the Magic Packet's format [1]. The first 6 bytes of the packet header each contain a fixed value, 0xFF
. This is followed by the payload, which consists of 16 repetitions of the 6-byte MAC address of the target device (00:11:32:6c:ab:cd
in this example). Each network card has its own setting, which specifies the device's manufacturer, model, and individual identifier.
Listing 1 cobbles together the Magic Packet in Go. Line 7 sets the NAS's MAC address as a string, while line 8 specifies that its length really is 6 bytes. In rare cases, there could be devices with longer MAC addresses, but their Magic Packets are more difficult to build. For the purpose of illustration, I'll keep things short and sweet.
Listing 1
wol.go
The MagicPacket
structure starting in line 9 abstracts the packet's two different areas: the header and payload. The sendMagicPacket()
function starting in line 13 then wraps the packet and, when done, sends it to the broadcast address 255.255.255.255
on the LAN. This means that all devices on the local network get to see the packet and can react accordingly. If a device listening via WoL receives a packet in which the packaged MAC address matches its own, it initiates the boot process.
The ParseMac()
function from the standard net Go library treasure trove translates the MAC string from line 7 into a binary hardware address, which the library's network functions need in order to send off the packet. The packet header with 6 0xFF
bytes is assembled by the for
loop starting in line 19. The subsequent double loop starting in line 22 then writes the MAC address in binary format 16 times in succession to the packet's payload area.
To convert the Go structure of the MagicPacket
type to a binary stream of bytes for packet recipients listening on the network, the standard binary.Write()
function in line 28 trawls the structure of the packet
variable. To do this, it writes the bytes of the structure in network format (big-endian, most significant byte first) to the buf
buffer. Line 36 then uses Write()
to send the buffer content via the UDP socket opened in line 31 by net.Dial()
to the LAN's broadcast address.
Be careful: binary.Write()
can only serialize a structure without error if all of its fields are of a fixed length. The function does not support Go's dynamically expandable array slices. You will see some really ugly runtime errors if you proceed without heeding this warning.
Panic
The states the application can be in at runtime are those of a simple finite machine. After starting the program, the NAS is usually asleep (state DOWN). The user issues the wake-up command by pressing the Up button. Following the bootstrap, the application keeps checking if the NAS can be pinged yet. If it shows no response, it is probably still asleep (i.e., resting in the DOWN state). But as soon as the ping
command reports success, the NAS is ready for operation and the finite machine jumps to the UP state.
Figure 4 shows the state machine diagram, while Listing 2 contains the implementation using the Go fsm library from GitHub. The NewFSM
function creates a new finite state machine starting in line 8 that processes two events: wake
(line 11), which transitions from the DOWN state to the UP state, and sleep
(line 12), which switches the machine from UP to DOWN. Listing 2 defines the conditions for these transitions in the enter_UP
and enter_DOWN
callbacks; the machine jumps to each of these before actually starting a transition.
Listing 2
fsm.go
Each of these two callbacks erects a hurdle that the program flow needs to clear before entering the new state. An infinite for
loop keeps running isPingable()
to check whether the desired state has already been reached, in which the NAS is either running or asleep, depending on the callback. If not, the callbacks wait a second and then try again. This is repeated until the desired state is reached.
Then the callbacks send a message with the new state of the machine on the stateReporter
channel provided by the caller earlier. The run()
function's final act is to return a reference for the ready-to-go state machine to the caller. With this handy tool at its disposal, the caller will be sending new commands to the machine using methods such as Event()
to initiate the associated state transitions.
Interestingly, the third-party library used for the fsm library from GitHub uses strings instead of typed variables for its states. This is not recommended Go style, because it means that a simple typo in a state in the code is enough to trigger a frantic search for runtime errors. This way, the type checker in the Go compiler has no way to detect the bug at compile time. The library clearly still has room for improvement, but you can't look a gift horse in the mouth.
On Screen!
Listing 3 has all the code to draw the small GUI shown in the screenshots. It is based on the Fyne framework, which the code pulls in from GitHub at compile time.
Listing 3
syno.go
The app features an application window with a label widget that displays the NAS status (UP or DOWN). In addition, there is an icon (check mark if the NAS is operational; X if not) and three buttons for user control. A progress bar also appears during the transition phases. After starting the program, initially only the Up button is active, while the Down button is grayed out (Figure 5). If you press a button to initiate an action, the app grays all buttons to prevent further impatient clicking triggering confusing actions.
The application causes some parts of the GUI to change dynamically with the program flow by making certain widgets disappear in some situations (the Hide()
function is used for this) and reappear later on using Show()
. The NAS status icon – either a check mark or an X – actually consists of two separate widgets okIcon
and downIcon
, but the app only displays one of them at any given time and keeps the other one tucked away invisibly.
The infinite progress bar is also always present in the application window. However, it is only visible and moving if an action is currently running (e.g., in the callback of the Up button starting in line 28). After line 33 calls sendMagicPacket()
to send the packet that starts the NAS via the network, line 34 calls progress.Show()
to display the progress bar. If the state of the machine changes, line 55 uses Hide()
to make the progress bar visually disappear from the app again.
Buy this article as PDF
(incl. VAT)
Buy Linux Magazine
Subscribe to our Linux Newsletters
Find Linux and Open Source Jobs
Subscribe to our ADMIN Newsletters
Support Our Work
Linux Magazine content is made possible with support from readers like you. Please consider contributing when you’ve found an article to be beneficial.
![Learn More](https://www.linux-magazine.com/var/linux_magazin/storage/images/media/linux-magazine-eng-us/images/misc/learn-more/834592-1-eng-US/Learn-More_medium.png)
News
-
Canonical Offers 12-Year LTS for Open Source Docker Images
Canonical is expanding its LTS offering to reach beyond the DEB packages with a new distro-less Docker image.
-
Plasma Desktop 6.1 Released with Several Enhancements
If you're a fan of Plasma Desktop, you should be excited about this new point release.
-
SUSE Offers CentOS 7 Support with Liberty Linux Lite
SUSE's Liberty Linux support offering now includes CentOS 7, which means businesses won't be forced to migrate those servers for some time.
-
Ubuntu's App Center Finally Supports Local Installs Again
If you regularly download .deb files and would prefer a GUI method of installing, Ubuntu has your back.
-
AlmaLinux Now Supports Raspberry Pi 5
If you're looking to create with the Raspberry Pi 5 and want to use AlmaLinux as your OS, you're in luck because it's now possible.
-
Kubuntu Focus Releases New Iterations of Ir14 and Ir16 Laptops
If you're a fan of the Kubuntu Focus laptops or have been waiting for the right time to purchase one, that time might be now.
-
NixOS 24.05 Is Ready for Prime Time
The latest release of NixOS (Uakari) has arrived and offers its usual reproducible, declarative, and reliable goodness.
-
Linux Lite 7.0 Officially Released
Based on Ubuntu 24.04 and kernel 6.8, Linux Lite version 7 now offers more options than ever.
-
KaOS Linux 2024.05 Adds Bcachfs Support and More
With updates all around, KaOS Linux now includes support for the bcachefs file system.
-
TUXEDO Computers Unveils New Iteration of the Stellaris Laptop Line
The Stellaris Slim 15 is the 6th generation and includes either an AMD or Intel CPU