<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Martin André</title>
    <description>The latest articles on DEV Community by Martin André (@martichou).</description>
    <link>https://dev.to/martichou</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F460046%2Ff49d031a-b21f-42f3-a0d2-6f9b1ff3779e.png</url>
      <title>DEV Community: Martin André</title>
      <link>https://dev.to/martichou</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/martichou"/>
    <language>en</language>
    <item>
      <title>Block most ads on any device (Wireguard + Pi-Hole)</title>
      <dc:creator>Martin André</dc:creator>
      <pubDate>Thu, 13 May 2021 11:56:37 +0000</pubDate>
      <link>https://dev.to/martichou/block-most-ads-on-any-device-19l0</link>
      <guid>https://dev.to/martichou/block-most-ads-on-any-device-19l0</guid>
      <description>&lt;p&gt;We all know how ads can be annoying and blocking them typically involve installing an ad-blocker on each device, browser, ... In this guide I'll show you how you can block ads on most device (iPhone, Mac, Android, Windows, ...).&lt;/p&gt;

&lt;h1&gt;
  
  
  Overview
&lt;/h1&gt;

&lt;p&gt;Pi-Hole is a general purpose network-wide ad-blocker that protect your network from ads &amp;amp; trackers. It's main advantage over browser's ad-blocker is that it block ads on any type of software.&lt;/p&gt;

&lt;h1&gt;
  
  
  Prerequisites
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;A VPS (near you or in the country you want to be)&lt;/li&gt;
&lt;li&gt;Know the basics of Linux&lt;/li&gt;
&lt;li&gt;Some 30 free minutes in your schedule&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is plenty of VPS provider, I've personally chosen &lt;a href="https://hetzner.cloud/?ref=3AeK7y8WMiUS"&gt;Hetzner&lt;/a&gt; but Linode is also really good ! (you can use my Hetzner link to get $20 cloud credits)&lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up Wireguard
&lt;/h1&gt;

&lt;p&gt;It takes no time to install Wireguard on Linux thanks to &lt;a href="https://github.com/angristan/wireguard-install"&gt;angristan&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-O&lt;/span&gt; https://raw.githubusercontent.com/angristan/wireguard-install/master/wireguard-install.sh
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x wireguard-install.sh
./wireguard-install.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go read the &lt;a href="https://github.com/angristan/wireguard-install"&gt;README&lt;/a&gt; to learn how to use the script. &lt;/p&gt;

&lt;h1&gt;
  
  
  Setting up Pi-Hole
&lt;/h1&gt;

&lt;p&gt;All you need is running this simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://install.pi-hole.net | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;During the setup you will be able to choose the interface for Pi-Hole to listen to, choose wg0.&lt;/em&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Optional: install Unbound
&lt;/h1&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;unbound
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might have to configure Unbound for it to be fasttttt.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim /etc/unbound/unbound.conf.d/pi-hole.conf

server:
    verbosity: 0
    interface: 127.0.0.1
    port: 5335
    &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-ip4&lt;/span&gt;: &lt;span class="nb"&gt;yes
    &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-udp&lt;/span&gt;: &lt;span class="nb"&gt;yes
    &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-tcp&lt;/span&gt;: &lt;span class="nb"&gt;yes
    &lt;/span&gt;&lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="nt"&gt;-ip6&lt;/span&gt;: &lt;span class="nb"&gt;yes
    &lt;/span&gt;prefer-ip6: no
    harden-glue: &lt;span class="nb"&gt;yes
    &lt;/span&gt;harden-dnssec-stripped: &lt;span class="nb"&gt;yes
    &lt;/span&gt;use-caps-for-id: no
    edns-buffer-size: 1472
    prefetch: &lt;span class="nb"&gt;yes
    &lt;/span&gt;prefetch-key: &lt;span class="nb"&gt;yes
    &lt;/span&gt;minimal-responses: &lt;span class="nb"&gt;yes
    &lt;/span&gt;cache-min-ttl: 300
    cache-max-ttl: 86400
    serve-expired: &lt;span class="nb"&gt;yes
    &lt;/span&gt;msg-cache-size: 50m
    rrset-cache-size: 100m
    num-threads: 1
    so-reuseport: &lt;span class="nb"&gt;yes
    &lt;/span&gt;so-rcvbuf: 4m
    so-sndbuf: 4m
    private-address: 192.168.0.0/16
    private-address: 169.254.0.0/16
    private-address: 172.16.0.0/12
    private-address: 10.0.0.0/8
    private-address: fd00::/8
    private-address: fe80::/10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can finally restart unbound.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service unbound restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Optional: Configure Pi-Hole for Unbound
&lt;/h1&gt;

&lt;p&gt;As you use Unbound, you will have to disable Pi-Hole DNS cache as well as redirecting to the right DNS server instead of using Cloudflare, ...&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim /etc/pihole/setupVars.conf

&lt;span class="nv"&gt;WEBPASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;BLOCKING_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;ADMIN_EMAIL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;WEBUIBOXEDLAYOUT&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;traditional
&lt;span class="nv"&gt;WEBTHEME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;default-dark
&lt;span class="nv"&gt;PIHOLE_INTERFACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;wg0
&lt;span class="nv"&gt;IPV4_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;IPV6_ADDRESS&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="nv"&gt;QUERY_LOGGING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;INSTALL_WEB_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;INSTALL_WEB_INTERFACE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;LIGHTTPD_ENABLED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;CACHE_SIZE&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0
&lt;span class="nv"&gt;DNSMASQ_LISTENING&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;single
&lt;span class="nv"&gt;PIHOLE_DNS_1&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;127.0.0.1#5335
&lt;span class="nv"&gt;DNS_FQDN_REQUIRED&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;DNS_BOGUS_PRIV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;DNSSEC&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false
&lt;/span&gt;&lt;span class="nv"&gt;REV_SERVER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally you can repair Pi-Hole using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pihole -r
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can now go on your Pi-Hole dashboard: &lt;a href="http://ip/admin"&gt;http://ip/admin&lt;/a&gt;.&lt;br&gt;
And check that your settings are correctly configured.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dashboard &amp;gt; Settings &amp;gt; System &amp;gt; FTL Information
&amp;gt;&amp;gt;&amp;gt; DNS cache size should be = 0.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dashboard &amp;gt; Settings &amp;gt; DNS &amp;gt; Upstream DNS Servers
&amp;gt;&amp;gt;&amp;gt; Custom 1 (IPv4) = 127.0.0.1#5335
&amp;gt;&amp;gt;&amp;gt; Everything else should be unchecked.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Dashboard &amp;gt; Settings &amp;gt; DNS &amp;gt; Interface listening behavior
&amp;gt;&amp;gt;&amp;gt; Listen only on interface wg0.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;And that's pretty much it!&lt;/p&gt;

&lt;p&gt;All you have to do now is to generate a config client for your Wireguard server, install it on any device and once the connection will be established, you can say bye bye to ads and hello to anonymity.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Set up Wireguard / Pi-Hole and OpenVPN together</title>
      <dc:creator>Martin André</dc:creator>
      <pubDate>Fri, 04 Dec 2020 10:45:47 +0000</pubDate>
      <link>https://dev.to/martichou/set-up-wireguard-pi-hole-and-openvpn-together-55ip</link>
      <guid>https://dev.to/martichou/set-up-wireguard-pi-hole-and-openvpn-together-55ip</guid>
      <description>&lt;p&gt;Wireguard is fairly new but already ready to replace OpenVPN. It provides a secure connection tunnel from a client to a server using public and private key authentication.&lt;/p&gt;

&lt;p&gt;In this tutorial I will assume that you already have some basic knowledge of networking and command line.&lt;/p&gt;

&lt;h3&gt;
  
  
  What will we do today?
&lt;/h3&gt;

&lt;p&gt;The other day I was struggling configuring my Wireguard instance to use Pi-Hole while also using the Cloudflare DNS and my company's DNS over a OpenVPN connection to reach the servers of the company.&lt;/p&gt;

&lt;p&gt;As a result I decided to write my guide, based on my experience. The little graph below resume what we'll end up with.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbidwwmdjk8mct4jbbs86.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fbidwwmdjk8mct4jbbs86.png" alt="Graph of the network"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Setting up Pi-Hole
&lt;/h3&gt;

&lt;p&gt;As the name of the project tends to pretend, Pi-Hole is not only reserved for Raspberry Pi. You can run it on a traditional server too and that's what we're going to do.&lt;/p&gt;

&lt;p&gt;All you need is running this simple command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sSL&lt;/span&gt; https://install.pi-hole.net | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you need more information for the install, check out &lt;a href="https://dev.to/thomasbnt/setup-pi-hole-3gkd#install"&gt;this guide&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Once it's installed, head towards the web admin page of the Pi-Hole and go on the &lt;code&gt;Settings&lt;/code&gt; page &amp;gt; DNS.&lt;/p&gt;

&lt;p&gt;Here you can select which Upstream DNS servers you want to use and setup your own DNS too.&lt;br&gt;
So in my case, my company DNS address is &lt;code&gt;10.51.1.1&lt;/code&gt;which result in this configuration: &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdgg1lrqrn9xwskjzvs96.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fdgg1lrqrn9xwskjzvs96.png" alt="Custom Upstream"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4k947895vvpmrdbrefqu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F4k947895vvpmrdbrefqu.png" alt="Interfaces"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvvn18v4brtk0u9ltxz39.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2Fvvn18v4brtk0u9ltxz39.png" alt="Advanced Settings"&gt;&lt;/a&gt;&lt;br&gt;
This config allows me to use &lt;code&gt;1.1.1.1&lt;/code&gt; for general requests and &lt;code&gt;10.51.1.1&lt;/code&gt; when it's linked to my company (by the domain name).&lt;/p&gt;

&lt;p&gt;You're done with Pi-Hole for the DNS, you might want to play with it a bit to block ads correctly.&lt;/p&gt;
&lt;h3&gt;
  
  
  Open-VPN
&lt;/h3&gt;

&lt;p&gt;In our network graph the Open-VPN connection is only used to speak with my company network. It's running Open-VPN due to our router running &lt;code&gt;pfSense&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;To set it up it's pretty easy, you just have to get your &lt;code&gt;config.ovpn&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Then install &lt;code&gt;openvpn&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;openvpn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define your credentials:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"username"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/openvpn/credentials
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"password"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; /etc/openvpn/credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And start the tunnel:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;openvpn &lt;span class="nt"&gt;--config&lt;/span&gt; /path/config.ovpn &lt;span class="nt"&gt;--daemon&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should see a new &lt;code&gt;tun0&lt;/code&gt; interface when you type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ip a
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wireguard server
&lt;/h3&gt;

&lt;p&gt;Let's install and configure out Wireguard instance now!&lt;/p&gt;

&lt;p&gt;The install process is just 3 commands long:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;linux-headers-&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;uname&lt;/span&gt; &lt;span class="nt"&gt;--kernel-release&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
add-apt-repository ppa:wireguard/wireguard
apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nb"&gt;install &lt;/span&gt;wireguard
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can configure Wireguard.&lt;br&gt;
Start by creating the needed folder and the private/public keys of the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/wireguard/keys
&lt;span class="nb"&gt;cd&lt;/span&gt; /etc/wireguard/keys
&lt;span class="nb"&gt;umask &lt;/span&gt;077
wg genkey | &lt;span class="nb"&gt;tee &lt;/span&gt;privatekey | wg pubkey &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; publickey
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We'll now create &lt;code&gt;/etc/wireguard/wg0.conf&lt;/code&gt; which is our Wireguard config:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;vim /etc/wireguard/wg0.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PrivateKey = private_key # from the step above
Address = 172.16.0.0/12,fd5b:5840:9e9f:a477::1/64 # you can change it, but IT STAY PRIVATE IPS
ListenPort = 8999
PostUp = sysctl -w net.ipv4.ip_forward=1; iptables -A FORWARD -i %i -j ACCEPT; iptables -t nat -A POSTROUTING -o ens2 -j MASQUERADE; iptables -t nat -A POSTROUTING -o tun0 -j MASQUERADE; ip6tables -A FORWARD -i %i -j ACCEPT; ip6tables -A FORWARD -o %i -j ACCEPT; ip6tables -t nat -A POSTROUTING -o he-ipv6 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -t nat -D POSTROUTING -o ens2 -j MASQUERADE; iptables -t nat -D POSTROUTING -o tun0 -j MASQUERADE; ip6tables -D FORWARD -i %i -j ACCEPT; ip6tables -D FORWARD -o %i -j ACCEPT; ip6tables -t nat -D POSTROUTING -o he-ipv6 -j MASQUERADE

[Peer]
PublicKey = public_key_client_one
AllowedIPs = 172.16.66.2,fd5b:5840:9e9f:a477::ca:571e/128 # update if you changed the Address from above

[Peer]
PublicKey = public_key_client_two
AllowedIPs = 172.16.66.3,fd5b:5840:9e9f:a477::746f:786f/128 # update if you changed the Address from above
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Once it's configured, make it start and launch at boots:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; systemctl &lt;span class="nb"&gt;enable &lt;/span&gt;wg-quick@wg0.service
 systemctl start wg-quick@wg0.service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Wireguard client
&lt;/h3&gt;

&lt;p&gt;As it's not the main goal of this tutorial and as it's not very complicated, I'll just give you an example of a client's config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[Interface]
PrivateKey = client_private_key
Address = 172.16.66.3/32,fd5b:5840:9e9f:a477::746f:786f/64
DNS = 10.18.1.57 # IMPORTANT (IP OF THE PI-HOLE)

[Peer]
PublicKey = server_public_key
AllowedIPs = 0.0.0.0/0,::/0 # ROUTE ALL TRAFIC
Endpoint = 123.123.123.123:8999 # IP OF THE SERVER:PORT
PersistentKeepalive = 15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tutorial</category>
      <category>linux</category>
      <category>vpn</category>
      <category>wireguard</category>
    </item>
    <item>
      <title>Rust vs Go - Load testing  webserv (&gt;400k req/s)</title>
      <dc:creator>Martin André</dc:creator>
      <pubDate>Mon, 16 Nov 2020 23:12:33 +0000</pubDate>
      <link>https://dev.to/martichou/rust-vs-go-load-testing-400k-req-s-53l</link>
      <guid>https://dev.to/martichou/rust-vs-go-load-testing-400k-req-s-53l</guid>
      <description>&lt;p&gt;&lt;strong&gt;TLDR:&lt;/strong&gt; Go can reach 270k req/s where Rust can hit 400k req/s.&lt;/p&gt;

&lt;h2&gt;
  
  
  Go server
&lt;/h2&gt;

&lt;p&gt;Let's go straight to buisness with a minimal server sample using &lt;a href="https://github.com/julienschmidt/httprouter"&gt;httprouter&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"fmt"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;

    &lt;span class="s"&gt;"github.com/julienschmidt/httprouter"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="n"&gt;httprouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Params&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;fmt&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fprint&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"Welcome!"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;httprouter&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;New&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8080"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;router&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Rust
&lt;/h2&gt;

&lt;p&gt;For the Rust server we'll use &lt;a href="https://github.com/actix/actix-web"&gt;Actix&lt;/a&gt; as our framework of choice, down below is the minimal sample.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;actix_web&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Responder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;App&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;HttpServer&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Responder&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"Welcome!"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ServiceConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="nf"&gt;.route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/health"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;web&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;health_check&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[actix_web::main]&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;serv&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HttpServer&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;App&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.wrap&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;middleware&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Compress&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="nf"&gt;.configure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;serv&lt;/span&gt;&lt;span class="nf"&gt;.bind&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"127.0.0.1:8080"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
        &lt;span class="nf"&gt;.run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="k"&gt;.await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Benchmark
&lt;/h2&gt;

&lt;p&gt;To put a big load on both our servers, we're going to use &lt;a href="https://github.com/wg/wrk"&gt;wrk&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;the benchmarks are performed on a i7-8750H (6c, 12threads)&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wrk &lt;span class="nt"&gt;-t12&lt;/span&gt; &lt;span class="nt"&gt;-c1000&lt;/span&gt; &lt;span class="nt"&gt;-d15s&lt;/span&gt; http://127.0.0.1:8080/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;h4&gt;
  
  
  Rust:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 15s test @ http://127.0.0.1:8080/
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     3.73ms    4.70ms  57.76ms   85.86%
    Req/Sec    33.66k     5.80k   69.35k    71.65%
  6039978 requests in 15.10s, 714.26MB read
Requests/sec: 400095.92
Transfer/sec:     47.31MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Go:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Running 15s test @ http://127.0.0.1:8080/
  12 threads and 1000 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency     5.03ms    6.11ms 102.78ms   86.66%
    Req/Sec    22.81k     4.77k   53.73k    71.19%
  4087276 requests in 15.10s, 487.24MB read
Requests/sec: 270691.36
Transfer/sec:     32.27MB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The results speak for themselves... 400.000 vs 270.000 for Rust and Go respectively.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;While Go might be easier to write and faster to compile compared to Rust, it's still slower compared to its competitors.&lt;/p&gt;

&lt;p&gt;If you're hesitating, let me give you this advice: &lt;em&gt;use rust if you want speed, else go with Go&lt;/em&gt;.&lt;/p&gt;

&lt;h6&gt;
  
  
  Cover image from &lt;a href="https://dzone.com/articles/a-detailed-comparison-between-top-programming-lang"&gt;dzone&lt;/a&gt;.
&lt;/h6&gt;

</description>
      <category>rust</category>
      <category>webdev</category>
      <category>go</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Get simple IO stats using Rust (throughput, ...)</title>
      <dc:creator>Martin André</dc:creator>
      <pubDate>Wed, 11 Nov 2020 17:06:29 +0000</pubDate>
      <link>https://dev.to/martichou/get-simple-io-stats-using-rust-throughput-47m4</link>
      <guid>https://dev.to/martichou/get-simple-io-stats-using-rust-throughput-47m4</guid>
      <description>&lt;p&gt;The other day I wanted to grab some information about my disk performance in real-time. I saw the really useful command &lt;code&gt;iostat&lt;/code&gt; and got curious. How does this tool can know how many megabytes per seconds my disks is using, etc.&lt;/p&gt;

&lt;p&gt;So I dug into the &lt;a href="https://github.com/sysstat/sysstat/blob/master/iostat.c"&gt;source code&lt;/a&gt; of this command and found my answer.&lt;/p&gt;

&lt;p&gt;Basically it just read &lt;code&gt;/proc/diskstats&lt;/code&gt; and that's all (if we don't care about formatting, sorting, etc).&lt;/p&gt;

&lt;p&gt;As I'm learning Rust (and doing a project involving stats) I thought it would be great to write a simple program to only display how many megabytes per seconds my disks are using. And so my journey began.&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting up the project
&lt;/h2&gt;

&lt;p&gt;We can create our project with &lt;strong&gt;cargo&lt;/strong&gt;. Cargo is a package manager for Rust and you can use it to simplify your life.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo new nameofbinary &lt;span class="nt"&gt;--bin&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;nameofbinary
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(if you've never used Rust before, take a look at &lt;a class="comment-mentioned-user" href="https://dev.to/aligoren"&gt;@aligoren&lt;/a&gt;
's fantastic guide &lt;a href="https://dev.to/aligoren/getting-started-with-rust-1c19"&gt;here&lt;/a&gt;)&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Let's code our program
&lt;/h2&gt;

&lt;p&gt;What will we need ? First take a look at the &lt;code&gt;/proc/diskstats&lt;/code&gt;;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; 259       0 nvme0n1 93188 31843 6970296 27235 1834264 427303 105983322 432380 0 411608 472996 0 0 0 0 33212 13380
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Wow&lt;/strong&gt;. What a mess. However we can easily understand it by a little bit of googling (&lt;a href="https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats"&gt;doc from kernel.org&lt;/a&gt;).&lt;br&gt;
By reading it we understand that each value is organized and separated by whitespace. Great !&lt;/p&gt;

&lt;p&gt;Now we can start coding (we'll do everything in the main.rs).&lt;br&gt;
We'll first create a &lt;code&gt;struct&lt;/code&gt; which will handle the number of megabytes one disk has read or written.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// This structure will hold our data for the disks&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;IoStats&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;mb_read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c"&gt;// f64 cause it won't be an integer, we need precision&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;mb_wrtn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here is the pseudo code of our program first;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hold_prev = previous stats reference
file = file descriptor to /proc/diskstats
loop:
    hold_curr = current stats reference
    io_stats = content of the file
    for line in io_stats:
        split_field = line.split (get each value in an array)
        compute_mb_s() = calculate the diff between hold_prev and hold_curr
    write_result
    hold_prev = hold_curr
    wait 1s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's dive into the real code now;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// Hashmap of previous drives stats to compute difference from&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IoStats&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c"&gt;// Open the file we'll use to get the stats&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;fd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;File&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="s"&gt;"/proc/diskstats"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;prev&lt;/code&gt; is a hashmap which will help us find the prev disk more easily by checking only by the disk name.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fd&lt;/code&gt; is our file descriptor (our way to read the diskstats file).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c"&gt;// Main program loop&lt;/span&gt;
&lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Create the curr Hashmap, allow us to compare with the prev one&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IoStats&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

    &lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nn"&gt;thread&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;curr&lt;/code&gt; is the same hashmap type as prev and will hold current value of disks stats.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prev = curr&lt;/code&gt; is where we give an utility to &lt;code&gt;prev&lt;/code&gt; (now prev contains all the value as curr)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;thread::sleep&lt;/code&gt; pause the loop for 1s (this avoid spamming the cpu/terminal)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HashMap&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;IoStats&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HashMap&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c"&gt;// Create the output string&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c"&gt;// Add the header string to the output&lt;/span&gt;
&lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;Device          mb_reads/s      mb_wrtn/s&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="c"&gt;// Create a new empty string&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;io_data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;String&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c"&gt;// Read the content of the file (diskstats) to the io_data string&lt;/span&gt;
&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="nf"&gt;.read_to_string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;io_data&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c"&gt;// Iterate over each line (each disk)&lt;/span&gt;
&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;io_data&lt;/span&gt;&lt;span class="nf"&gt;.lines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c"&gt;// Move the cursor to the start of the file&lt;/span&gt;
&lt;span class="n"&gt;fd&lt;/span&gt;&lt;span class="nf"&gt;.seek&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SeekFrom&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c"&gt;// Print the result&lt;/span&gt;
&lt;span class="nd"&gt;writeln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;stdout&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.lock&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;output&lt;/code&gt; is the string we'll print (single string to print everything at once)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output.push_str&lt;/code&gt; add the header to the output string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fd.read_to_string&lt;/code&gt; read the content of &lt;code&gt;fd&lt;/code&gt; into &lt;code&gt;io_data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;for&lt;/code&gt; iterate over each line from &lt;code&gt;io_data&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fd.seek&lt;/code&gt; reset the cursor position to the start of the file (allow use to read it again)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;writeln!&lt;/code&gt; write the output string in &lt;code&gt;stdout&lt;/code&gt; (terminal)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt; &lt;span class="n"&gt;in&lt;/span&gt; &lt;span class="n"&gt;io_data&lt;/span&gt;&lt;span class="nf"&gt;.lines&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c"&gt;// Split field (separated by whitespace) and collect them without specific type&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;line&lt;/span&gt;&lt;span class="nf"&gt;.split_whitespace&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.collect&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;_&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;IoStats&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;mb_read&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2048.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;mb_wrtn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="py"&gt;.parse&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;f64&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt; &lt;span class="mf"&gt;2048.0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="c"&gt;// If prev already contains the info we compute the diff to get mb/s&lt;/span&gt;
    &lt;span class="c"&gt;// Else we add to the print line the "fake" data.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="nf"&gt;.contains_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Get the object from the hashmap&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;prev&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="c"&gt;// Construct speed line and append it to curr hashmap&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mb_read_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="py"&gt;.mb_read&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pds&lt;/span&gt;&lt;span class="py"&gt;.mb_read&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;mb_wrtn_s&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="py"&gt;.mb_wrtn&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="n"&gt;pds&lt;/span&gt;&lt;span class="py"&gt;.mb_wrtn&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="c"&gt;// Add the line, formatted with color and spacing&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0;32m{:16}&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0;34m{:10.2}{:15.2}&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="n"&gt;mb_read_s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;mb_wrtn_s&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="c"&gt;// Insert the current disk data to the curr HashMap&lt;/span&gt;
        &lt;span class="c"&gt;// the curr will later be saved as prev&lt;/span&gt;
        &lt;span class="n"&gt;curr&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="nf"&gt;.to_owned&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;ds&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c"&gt;// Add the line with fake data and formatting&lt;/span&gt;
        &lt;span class="n"&gt;output&lt;/span&gt;&lt;span class="nf"&gt;.push_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0;32m{:16}&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0;34m{:10.2}{:15.2}&lt;/span&gt;&lt;span class="se"&gt;\x1b&lt;/span&gt;&lt;span class="s"&gt;[0m&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;fields&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mf"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mf"&gt;0.00&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;line.split_whitespace().collect::&amp;lt;Vec&amp;lt;_&amp;gt;&amp;gt;()&lt;/code&gt; simply return us each values, without whitespace, as &amp;amp;str&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ds&lt;/code&gt; create our IoStats struct for this disk&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ds -&amp;gt; mb_read/mb_wrtn&lt;/code&gt; are first converted to &lt;code&gt;f64&lt;/code&gt; and then divided by &lt;code&gt;2048.0&lt;/code&gt; to convert from sector (one sector is almost always 512kb) to mb.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;prev.contains_key&lt;/code&gt; check if the previous hashmap contains the disk (in case of first run or usb stick)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;pds&lt;/code&gt; is the IoStats of the previous run of the loop for that particular disk&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mb_read_s&lt;/code&gt; and &lt;code&gt;mb_wrtn_s&lt;/code&gt; are the mb/s value (/s as we loop every 1s)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;output.push_str&lt;/code&gt; add the current disk's info to the output string&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;curr.insert&lt;/code&gt; add the current disk's info to the curr hashmap&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Final words
&lt;/h2&gt;

&lt;p&gt;As we saw it's pretty easy to get stats from our disks in Rust. Everything is properly placed inside &lt;code&gt;/proc/diskstats&lt;/code&gt; for us.&lt;/p&gt;

&lt;p&gt;If you want to take a look at the full code &lt;a href="https://github.com/Martichou/iostats_demo"&gt;github&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Thanks for ready it, hope you learned something new ! ❤&lt;/p&gt;

</description>
      <category>linux</category>
      <category>rust</category>
      <category>tutorial</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
