![]() |
|
The End of the Road: systemd’s “Socket” Units - Printable Version +- Sick Gaming (https://sickgaming.net) +-- Forum: Computers (https://sickgaming.net/forum-86.html) +--- Forum: Linux, BSD & Unix (https://sickgaming.net/forum-88.html) +--- Thread: The End of the Road: systemd’s “Socket” Units (/thread-86689.html) |
The End of the Road: systemd’s “Socket” Units - xSicKxBot - 10-02-2018 The End of the Road: systemd’s “Socket” Units ![]() ![]() Sockets are used for two different processes to share data or for shuttling information from one machine to another and the network. They are extremely useful and the basis of things like FTP, real-time network chat systems, secure shells, and so on. For the fly-by programmer, sockets can be somewhat hard to get right, but by using a systemd’s socket units, you can make systemd do the heavy lifting. Besides making sockets simpler to set up, systemd dumps whatever comes in through the socket to STDIN. This means you don’t have to bother with complicated socket management in you script; just pick up the data from STDIN and use it from there. The other advantage is that systemd will make sure your socket is active only as long as necessary, waking it up when data is incoming, and closing it down again when it is done. This saves resources, as the server associated on the receiving side will be closed most of the time and will only be activated if a systemd socket unit detects activity on its port. To see how all this works, first you’ll see how easy it is to send some strings of text over a systemd activated socket. Later, we’ll look at how to send a whole binary file. Finally, we will pick up the systemd-based surveillance system we have been developing over the past several installments and learn how to send the images it captures to your laptop. DISCLAIMER: What you’ll see here are over-simplified examples created for teaching purposes only. Although they all work, there is no error-handling or security built into any of them. I don’t recommend you use them in a real-world scenario. Sending Texts Socket units are stupidly simple, or rather, they usually are. Although there are dozens of socket-specific directives you can use to fine tune your units, you will rarely use more than two, In this case, you do exactly that and use only a listening directive and the Code: AcceptCode: # echo.socket
[Unit] Description = Echo server [Socket] ListenStream = 4444 Accept = yes [Install] WantedBy = sockets.targetThat is what a basic socket file looks like. It has a Code: [Socket]Code: =The other socket-specific directive is Code: AcceptCode: AcceptCode: falseAs you want to send information from one machine to another, you will be using an AF_INET, and for that the best thing to do is have Code: AcceptCode: trueCode: yesThe service itself is also pretty basic: Code: # echo@.service
[Unit] Description=Echo server service [Service] ExecStart=/path/to/socketthing.py StandardInput=socketIn most cases, the service will have the same name the socket unit, except with an @ and the service suffix. As your socket unit was echo.socket, your service will be echo@.service. The service Code: TypeCode: simpleCode: ExecStartCode: StandardInputThe socketthing.py script is barely three lines long: Code: #!/usr/bin/python
import sys
sys.stdout.write(sys.stdin.readline().strip().upper() + '\r\n')What this does is read a line of text in from STDIN, which, as you saw, comes in via the socket. Then it strips all the spaces from the beginning and the end, and puts it into uppercase ( Code: sys.stdin.readline().strip().upper()Code: sys.stdout.write([...])Start the socket unit with: Code: sudo systemctl start echo.socketAnd echo.socket will automatically call echo@.service (which in turn runs socketthing.py) each time someone tries to push a string to the server through port 4444. To do that, on the sending computer, you can use a program like socat: Code: $ socat - TCP:server_IP_address:4444
hello computer
HELLO COMPUTER
$Although good for illustrating how to get started, this example is pretty pointless. Let’s do something a bit more useful and send over a whole file… Transferring Files For a systemd, there is no difference between sending a stream of text to a stream of binary data. In fact, to all practical effects the socket file is the same… Code: # filetrans.socket
[Unit] Description=File transfer server [Socket] ListenStream=4444 Accept=yes [Install] WantedBy=sockets.target… As is the service unit: Code: # filetrans@.service
[Unit] Description=File transfer server service [Service] ExecStart=/path/to/socketfilething.py StandardInput=socketAll you need to do is change the name and description of the services and have the “new” filetrans@.service point to a script that will handle the reception of the file. In this case, the script, socketfilething.py, will handle PDFs coming from the sending computer: Code: #!/usr/bin/python
import sys output_file = open ("/path/to/store/test.pdf", "wb")
output_file.write(sys.stdin.buffer.read())
output_file.close()You use Code: sys.stdin.buffer.read()Code: "wb"To try this our, from the sending end of things, you can send the PDF file over the wire again using socat: Code: cat some.pdf | socat - TCP:192.168.1.111:4444On the receiving end, a copy of some.pdf called test.pdf will pop up in the directory of your choice. You can probably see where we are going with this and how we can use it in our systemd-powered surveillance system. Surveillance Sockets Again, on the receiving side, there is virtually no difference to either the socket unit: Code: # surveillance.socket #
[Unit] Description=Surveillance server [Socket] ListenStream=4444 Accept=yes [Install] WantedBy=sockets.target… Or the service unit: Code: # surveillance@.service #
[Unit] Description=Surveillance server service [Service] ExecStart=/path/to/surveillancething.py StandardInput=socketSave for a change of name, description and the have it point to another script you can call surveillancething.py: Code: #!/usr/bin/python import sys from time import strftime fn = strftime("%Y_%m_%d_%H_%M_%S")+".jpg" output_file = open ("/path/to/store/" + fn, "wb")
output_file.write(sys.stdin.buffer.read()) output_file.close()This new script is very similar to the prior one you used to send a PDF. The only difference is that, as the surveying machine sends an image every time it detects changes, you want to give each image you receive a unique name, preferably with a time stamp, hence the Code: fn = strftime("%Y_%m_%d_%H_%M_%S")+".jpg"On the surveying side, you only need to change the picmonitor.sh file so that it sends the new image over the socket: Code: #!/bin/bash
fn=`date|tr [:punct:][:space:] _`.jpg cp /home/[user name]/monitor/monitor.jpg /home/[user name]/monitor/$fn cat /home/[user name]/monitor/$fn | socat - TCP:192.168.1.111:4444Start surveillance.socket on the server and picchanged.timer on the surveying machine, and you will start to receive images from your spying webcam. Conclusion And that’s it! Over the past few months, we have covered everything you need to know to get started writing systemd units. We have gone from the most basic service units, all the way through device event-activated services, timers, and more. In case you missed anything, here’s an index to all the other systemd topics we have covered:
|