Sick Gaming
The End of the Road: systemd’s “Socket” Units - Printable Version

+- Sick Gaming (https://www.sickgaming.net)
+-- Forum: Computers (https://www.sickgaming.net/forum-86.html)
+--- Forum: Linux, FreeBSD, and Unix types (https://www.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

<div style="margin: 5px 5% 10px 5%;"><img src="http://www.sickgaming.net/blog/wp-content/uploads/2018/10/the-end-of-the-road-systemds-socket-units.jpg" width="1915" height="1118" title="" alt="" /></div><div><div><img src="http://www.sickgaming.net/blog/wp-content/uploads/2018/10/the-end-of-the-road-systemds-socket-units.jpg" class="ff-og-image-inserted" /></div>
<p>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.</p>
<p>For the fly-by programmer, sockets can be somewhat hard to get right, but by using a systemd’s <i>socket</i> units, you can make systemd do the heavy lifting.</p>
<p>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.</p>
<p>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.</p>
<p>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 <a href="https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories">systemd-based surveillance system</a> we have been developing over the past several installments and learn how to send the images it captures to your laptop.</p>
<p>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.</p>
<h3>Sending Texts</h3>
<p>Socket units are stupidly simple, or rather, they usually are. Although <a href="https://www.freedesktop.org/software/systemd/man/systemd.socket.html">there are dozens of socket-specific directives you can use to fine tune your units</a>, you will rarely use more than two, In this case, you do exactly that and use only a listening directive and the <code>Accept</code> directive:</p>
<pre>
# echo.socket
[Unit] Description = Echo server [Socket] ListenStream = 4444 Accept = yes [Install] WantedBy = sockets.target
</pre>
<p>That is what a basic <i>socket</i> file looks like. It has a <code>[Socket]</code> section where you specify what it has to listen for. Apart from streams, it cand listen for datagrams, sequential packages, and so on. On the other side of the <code>=</code> is where to listen from. You could specify a full IP address, file system socket or something else. A single number, like in this case, means a port. The socket unit above will be listening on the local machine to port 4444.</p>
<p>The other socket-specific directive is <code>Accept</code>. <code>Accept</code> by default is set to <code>false</code>, as this is used mostly for <i>AF_UNIX</i> sockets. Not to get into too much detail, but <i>AF_UNIX</i> sockets are sockets where the processes sharing the information reside on the same machine.</p>
<p>As you want to send information from one machine to another, you will be using an <i>AF_INET</i>, and for that the best thing to do is have <code>Accept</code> set to <code>true</code> or <code>yes</code>.</p>
<p>The service itself is also pretty basic:</p>
<pre>
# [email protected]
[Unit] Description=Echo server service [Service] ExecStart=/path/to/socketthing.py StandardInput=socket </pre>
<p>In most cases, the service will have the same name the socket unit, except with an <i>@</i> and the <i>service</i> suffix. As your socket unit was <i>echo.socket</i>, your service will be <i>[email protected]</i>.</p>
<p>The service <code>Type</code> is <code>simple</code>, which is already the default, so there is no need to include it. <code>ExecStart</code> points to the <i>socketthing.py</i> script you will see in a minute, and the <code>StandardInput</code> for said script comes from the socket set up by <i>echo.socket</i>.</p>
<p>The <i>socketthing.py</i> script is barely three lines long:</p>
<pre>
#!/usr/bin/python
import sys
sys.stdout.write(sys.stdin.readline().strip().upper() + '\r\n')
</pre>
<p>What this does is read a line of text in from <i>STDIN</i>, 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>). Finally it sends it back across the socket to the terminal of the sending computer (<code>sys.stdout.write([...])</code>). This means a user will connect to your receiving machine’s socket, type in a string, and will see it echoed back in CAPITAL LETTERS.</p>
<p>Start the socket unit with:</p>
<pre>
sudo systemctl start echo.socket
</pre>
<p>And <i>echo.socket</i> will automatically call <i>[email protected]</i> (which in turn runs <i>socketthing.py</i>) each time someone tries to push a string to the server through port 4444.</p>
<p>To do that, on the sending computer, you can use a program like <a href="https://www.linux.com/news/socat-general-bidirectional-pipe-handler"><i>socat</i></a>:</p>
<pre>
$ <b>socat - TCP:<i>server_IP_address</i>:4444</b>
<b>hello computer</b>
HELLO COMPUTER
$
</pre>
<p>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…</p>
<h3>Transferring Files</h3>
<p>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…</p>
<pre>
# filetrans.socket
[Unit] Description=File transfer server [Socket] ListenStream=4444 Accept=yes [Install] WantedBy=sockets.target
</pre>
<p>… As is the service unit:</p>
<pre>
# [email protected]
[Unit] Description=File transfer server service [Service] ExecStart=/path/to/socketfilething.py StandardInput=socket </pre>
<p>All you need to do is change the name and description of the services and have the “new” <i>[email protected]</i> point to a script that will handle the reception of the file.</p>
<p>In this case, the script, <i>socketfilething.py</i>, will handle PDFs coming from the sending computer:</p>
<pre>
#!/usr/bin/python
import sys output_file = open ("/path/to/store/test.pdf", "wb")
output_file.write(sys.stdin.buffer.read())
output_file.close()
</pre>
<p>You use <code>sys.stdin.buffer.read()</code> to read in a stream of binary data from STDIN, and, as you have opened <i>test.pdf</i> in <i>write binary</i> mode (<code>"wb"</code>), you can just write the stream passed down from the socket directly into the file.</p>
<p>To try this our, from the sending end of things, you can send the PDF file over the wire again using <i>socat</i>:</p>
<pre>
cat some.pdf | socat - TCP:192.168.1.111:4444
</pre>
<p>On the receiving end, a copy of <i>some.pdf</i> called <i>test.pdf</i> will pop up in the directory of your choice.</p>
<p>You can probably see where we are going with this and how we can use it in our <a href="https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories">systemd-powered surveillance system</a>.</p>
<h3>Surveillance Sockets</h3>
<p>Again, on the receiving side, there is virtually no difference to either the socket unit:</p>
<pre>
# surveillance.socket #
[Unit] Description=Surveillance server [Socket] ListenStream=4444 Accept=yes [Install] WantedBy=sockets.target
</pre>
<p>… Or the service unit:</p>
<pre>
# [email protected] #
[Unit] Description=Surveillance server service [Service] ExecStart=/path/to/surveillancething.py StandardInput=socket </pre>
<p>Save for a change of name, description and the have it point to another script you can call <i>surveillancething.py</i>:</p>
<pre>
#!/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()
</pre>
<p>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"</code> line.</p>
<p>On the surveying side, you only need to change the <a href="https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories"><i>picmonitor.sh</i></a> file so that it sends the new image over the socket:</p>
<pre>
#!/bin/bash
fn=`date|tr [:punct:][:space:] _`.jpg cp /home/[<i>user name</i>]/monitor/monitor.jpg /home/[<i>user name</i>]/monitor/$fn cat /home/[<i>user name</i>]/monitor/$fn | socat - TCP:192.168.1.111:4444
</pre>
<p>Start <i>surveillance.socket</i> on the server and <a href="https://www.linux.com/blog/intro-to-linux/2018/8/systemd-timers-two-use-cases-0"><i>picchanged.timer </i></a> on the surveying machine, and you will start to receive images from your spying webcam.</p>
<h3>Conclusion</h3>
<p>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.</p>
<p>In case you missed anything, here’s an index to all the other systemd topics we have covered:</p>
<ol>
<li>Basic Services: <a href="https://www.linux.com/blog/learn/intro-to-linux/2018/5/writing-systemd-services-fun-and-profit">Writing Systemd Services for Fun and Profit</a></li>
<li>More Advanced Services: <a href="https://www.linux.com/blog/learn/2018/5/systemd-services-beyond-starting-and-stopping">Beyond Starting and Stopping</a></li>
<li>Device aware services: <a href="https://www.linux.com/blog/intro-to-linux/2018/6/systemd-services-reacting-change">Reacting to Change</a></li>
<li>Paths: <a href="https://www.linux.com/blog/learn/intro-to-linux/2018/6/systemd-services-monitoring-files-and-directories">Monitoring Files and Directories</a></li>
<li>Timers 1: <a href="https://www.linux.com/blog/learn/intro-to-linux/2018/7/setting-timer-systemd-linux">Setting Up a Timer</a></li>
<li>Timers 2: <a href="https://www.linux.com/blog/intro-to-linux/2018/8/systemd-timers-two-use-cases-0">Timers: Three Use Cases</a></li>
</ol>
</div>