<?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: MrViK</title>
    <description>The latest articles on DEV Community by MrViK (@__mrvik__).</description>
    <link>https://dev.to/__mrvik__</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%2F164948%2F9a8202ed-8731-44ae-b55a-f5cf7074e0dc.jpg</url>
      <title>DEV Community: MrViK</title>
      <link>https://dev.to/__mrvik__</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/__mrvik__"/>
    <language>en</language>
    <item>
      <title>Bringing the power of Go templates to service descriptions</title>
      <dc:creator>MrViK</dc:creator>
      <pubDate>Mon, 11 Jan 2021 19:07:09 +0000</pubDate>
      <link>https://dev.to/__mrvik__/bringing-the-power-of-go-templates-to-service-descriptions-28ci</link>
      <guid>https://dev.to/__mrvik__/bringing-the-power-of-go-templates-to-service-descriptions-28ci</guid>
      <description>&lt;p&gt;Hey, on previous articles from this series I mentioned some missing features on the Go-PID1 and its service launcher.&lt;/p&gt;

&lt;p&gt;To recap:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;We have no templates - It's a big deal&lt;/li&gt;
&lt;li&gt;Support for reloading services&lt;/li&gt;
&lt;li&gt;Better handling of mounts&lt;/li&gt;
&lt;li&gt;Other things like setting up the keymap for console&lt;/li&gt;
&lt;li&gt;(Added now) Init is very fragile, a single fail on &lt;code&gt;service-launcher&lt;/code&gt; halts the system.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And I fixed all of them and the fixes have been merged into &lt;code&gt;master&lt;/code&gt; by this time.&lt;/p&gt;

&lt;p&gt;But we're here to talk about templates. Let's see how &lt;code&gt;systemd&lt;/code&gt; does it:&lt;/p&gt;

&lt;p&gt;From &lt;code&gt;systemd.unit(5)&lt;/code&gt; on section &lt;code&gt;SPECIFIERS&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Many settings resolve specifiers which may be used to write generic unit files referring to runtime or unit parameters that are replaced when the files are loaded. Specifiers must be known and resolvable for the setting to be valid. The following specifiers are understood:&lt;/p&gt;

&lt;p&gt;"%i" Instance name For instantiated units this is the string between the first "@" character and the type suffix. Empty for non-instantiated units.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;There are a lot more of specifiers on that list, but let's focus on this one (and it's unescaped variant &lt;code&gt;%I&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;If you have &lt;code&gt;systemd&lt;/code&gt; on the PID1 you can do &lt;code&gt;systemctl cat getty@tty1.service&lt;/code&gt; to see the code under those templates.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;systemd&lt;/code&gt; templates always come with an @ symbol and the text between the '@' and the end of the name (w/o .service or whatever) is the "argument".&lt;/p&gt;

&lt;p&gt;Here is the service description for &lt;code&gt;getty@tty1&lt;/code&gt;, note that is a template linked to &lt;code&gt;getty@.service&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight systemd"&gt;&lt;code&gt;&lt;span class="c"&gt;# /usr/lib/systemd/system/getty@.service&lt;/span&gt;
&lt;span class="c"&gt;#  SPDX-License-Identifier: LGPL-2.1-or-later&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  This file is part of systemd.&lt;/span&gt;
&lt;span class="c"&gt;#&lt;/span&gt;
&lt;span class="c"&gt;#  systemd is free software; you can redistribute it and/or modify it&lt;/span&gt;
&lt;span class="c"&gt;#  under the terms of the GNU Lesser General Public License as published by&lt;/span&gt;
&lt;span class="c"&gt;#  the Free Software Foundation; either version 2.1 of the License, or&lt;/span&gt;
&lt;span class="c"&gt;#  (at your option) any later version.&lt;/span&gt;

&lt;span class="k"&gt;[Unit]&lt;/span&gt;
&lt;span class="nt"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;Getty on %I
&lt;span class="nt"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;man:agetty(8) man:systemd-getty-generator(8)
&lt;span class="nt"&gt;Documentation&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;http://0pointer.de/blog/projects/serial-console.html
&lt;span class="nt"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;systemd-user-sessions.service plymouth-quit-wait.service getty-pre.target

&lt;span class="c"&gt;# If additional gettys are spawned during boot then we should make&lt;/span&gt;
&lt;span class="c"&gt;# sure that this is synchronized before getty.target, even though&lt;/span&gt;
&lt;span class="c"&gt;# getty.target didn't actually pull it in.&lt;/span&gt;
&lt;span class="nt"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;getty.target
&lt;span class="nt"&gt;IgnoreOnIsolate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;yes

&lt;span class="c"&gt;# IgnoreOnIsolate causes issues with sulogin, if someone isolates&lt;/span&gt;
&lt;span class="c"&gt;# rescue.target or starts rescue.service from multi-user.target or&lt;/span&gt;
&lt;span class="c"&gt;# graphical.target.&lt;/span&gt;
&lt;span class="nt"&gt;Conflicts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;rescue.service
&lt;span class="nt"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;rescue.service

&lt;span class="c"&gt;# On systems without virtual consoles, don't start any getty. Note&lt;/span&gt;
&lt;span class="c"&gt;# that serial gettys are covered by serial-getty@.service, not this&lt;/span&gt;
&lt;span class="c"&gt;# unit.&lt;/span&gt;
&lt;span class="nt"&gt;ConditionPathExists&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/dev/tty0

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="c"&gt;# the VT is cleared by TTYVTDisallocate&lt;/span&gt;
&lt;span class="c"&gt;# The '-o' option value tells agetty to replace 'login' arguments with an&lt;/span&gt;
&lt;span class="c"&gt;# option to preserve environment (-p), followed by '--' for safety, and then&lt;/span&gt;
&lt;span class="c"&gt;# the entered username.&lt;/span&gt;
&lt;span class="nt"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;-/sbin/agetty -o '-p -- \\u' --noclear %I $TERM
&lt;span class="nt"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;idle
&lt;span class="nt"&gt;Restart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;always
&lt;span class="nt"&gt;RestartSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;0
&lt;span class="nt"&gt;UtmpIdentifier&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;%I
&lt;span class="nt"&gt;TTYPath&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/dev/%I
&lt;span class="nt"&gt;TTYReset&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;yes
&lt;span class="nt"&gt;TTYVHangup&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;yes
&lt;span class="nt"&gt;TTYVTDisallocate&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;yes
&lt;span class="nt"&gt;IgnoreSIGPIPE&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;no
&lt;span class="nt"&gt;SendSIGHUP&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;yes

&lt;span class="c"&gt;# Unset locale for the console getty since the console has problems&lt;/span&gt;
&lt;span class="c"&gt;# displaying some internationalized messages.&lt;/span&gt;
&lt;span class="nt"&gt;UnsetEnvironment&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;LANG LANGUAGE LC_CTYPE LC_NUMERIC LC_TIME LC_COLLATE LC_MONETARY LC_MESSAGES LC_PAPER LC_NAME LC_ADDRESS LC_TELEPHONE LC_MEASUREMENT LC_IDENTIFICATION

&lt;span class="k"&gt;[Install]&lt;/span&gt;
&lt;span class="nt"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;getty.target
&lt;span class="nt"&gt;DefaultInstance&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;tty1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On this unit all occurrences of "%I" get replaced by the argument.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ways of implementing this
&lt;/h2&gt;

&lt;p&gt;I initially thought about using Regex replace. And it should work but we have &lt;code&gt;text/template&lt;/code&gt;, a powerful way of creating data driven templates.&lt;/p&gt;

&lt;p&gt;What data? Well, copying the good things from &lt;code&gt;systemd&lt;/code&gt;, when a service has an @ symbol on it's name, we pass the file to the templates interpreter with the filename and the argument.&lt;/p&gt;

&lt;h3&gt;
  
  
  Environment exposed to templates
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Data -&amp;gt; The &lt;code&gt;Argument&lt;/code&gt; variable based on what follows the '@' (before &lt;code&gt;.yml&lt;/code&gt; or &lt;code&gt;.yaml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Functions -&amp;gt; &lt;code&gt;os.Getenv&lt;/code&gt; is exposed as &lt;code&gt;Getenv&lt;/code&gt; so you can retrieve environment variables.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Currently there are no plans on adding more things, but the data struct and funcmap are easily extensible. Feel free to add functions adapted to your needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Trying templates
&lt;/h2&gt;

&lt;p&gt;So how do they look.&lt;/p&gt;

&lt;p&gt;Let's see the template for getty. We took a look to the &lt;code&gt;systemd&lt;/code&gt; approach (which includes a lot of things). How was the world before templates on Go-PID1?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agetty@tty1&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Getty on tty1&lt;/span&gt;
&lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setsid&lt;/span&gt;
&lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--wait&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sbin/agetty&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--noclear&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tty1&lt;/span&gt;  &lt;span class="c1"&gt;# Hardcoded values&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;linux&lt;/span&gt;
&lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Previously each file was only allowed to define a single unit. Now each file can define a slice of services so templates are more powerful yet.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="c1"&gt;# vim: filetype=yaml&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;with .Argument&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;  &lt;span class="c1"&gt;# If no argument, no services are defined here&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;agetty@{{.}}&lt;/span&gt;  &lt;span class="c1"&gt;# Dynamic name!&lt;/span&gt;
  &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Getty on {{.}}. We have templates. yay!&lt;/span&gt;
  &lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;setsid&lt;/span&gt;
  &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--wait&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/sbin/agetty&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- if eq . "tty1"&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--noclear&lt;/span&gt;  &lt;span class="c1"&gt;# Conditionals inside templates. Take this!&lt;/span&gt;
    &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;- end&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;.&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;  &lt;span class="c1"&gt;# This argument takes the value from .Argument&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;Getenv "TERM"&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;  &lt;span class="c1"&gt;# We call the Getenv function from template&lt;/span&gt;
  &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always&lt;/span&gt;
&lt;span class="pi"&gt;{{&lt;/span&gt;&lt;span class="nv"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;}}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And how do they look on the FS?&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--synar7Vs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rj7xwuq3kvbniu2sky9z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--synar7Vs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/rj7xwuq3kvbniu2sky9z.png" alt="ls of yml-templates"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And another new feature is the hot reload. Add, remove or update services and do &lt;code&gt;slc reload&lt;/code&gt; to apply changes.&lt;br&gt;
So if you link another service to a template and do a reload, the service will be read and started.&lt;/p&gt;

&lt;p&gt;So cool, isn't it?&lt;/p&gt;

</description>
      <category>go</category>
      <category>linux</category>
      <category>systemsprogramming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Following slippery processes</title>
      <dc:creator>MrViK</dc:creator>
      <pubDate>Thu, 07 Jan 2021 13:17:32 +0000</pubDate>
      <link>https://dev.to/__mrvik__/following-slippery-processes-6</link>
      <guid>https://dev.to/__mrvik__/following-slippery-processes-6</guid>
      <description>&lt;p&gt;Hey, if you followed the previous parts about writing a PID 1 (and a service launcher) with Go you may expect this.&lt;br&gt;
The approach of the created &lt;code&gt;service-launcher&lt;/code&gt; is to follow a &lt;strong&gt;single&lt;/strong&gt; process, so if it forks and main process exits, the service fails/succeeds with code from main process instead of following the forked one.&lt;/p&gt;

&lt;p&gt;To follow those processes (that can spawn several processes) we should tweak the &lt;code&gt;service-launcher&lt;/code&gt; to follow a variable number of them.&lt;/p&gt;

&lt;p&gt;Instead of doing this and thus, making a monolith around &lt;code&gt;service-launcher&lt;/code&gt; I have decided to create a helper to launch and follow processes that fork.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Important idea: What do we call "main" process?&lt;br&gt;
We call main process to the process spawned by &lt;code&gt;service-launcher&lt;/code&gt;. That process will be monitored and will decide if a service is running or exited (failed/succeeded).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1&gt;
  
  
  Default behavior
&lt;/h1&gt;

&lt;p&gt;From &lt;code&gt;fork(2)&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;fork()&lt;/code&gt; creates a new process by duplicating the calling process. The new process is referred to as the child process. The calling process is referred to as the parent process.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;So if we call a &lt;code&gt;fork&lt;/code&gt;-ing process and do pstree, there will be 2 identical processes, one as parent and the other as child.&lt;/p&gt;

&lt;p&gt;Take the following C code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="cp"&gt;# include &amp;lt;unistd.h&amp;gt;
# include &amp;lt;sys/types.h&amp;gt;
# include &amp;lt;sys/wait.h&amp;gt;
&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&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;pid_t&lt;/span&gt; &lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;fork&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pid&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="c1"&gt;// Forked process. Sleep and exit&lt;/span&gt;
            &lt;span class="n"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;wait&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;NULL&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// Wait for its child&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="mi"&gt;0&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;If we run &lt;code&gt;htop&lt;/code&gt; or &lt;code&gt;pstree&lt;/code&gt; while running that code we get the following tree:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--mYiDWVuB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pd55dpccmx8e7kmh8nf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--mYiDWVuB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/pd55dpccmx8e7kmh8nf6.png" alt="Process tree, 2 fork executables"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, that's right, but, what if the parent process exits before child? To achieve this, remove the &lt;code&gt;wait&lt;/code&gt; call from C code. In sake of brevity I won't show it, but that "forked" process bubbles trying to find the next CHILD_SUBREAPER. The PID 1 acts as child reaper implicitly, so if none of the processes on tree has that role, PID 1 will become the parent of our orphaned process. We lost it.&lt;/p&gt;

&lt;p&gt;This is the default behavior, and the process that launched &lt;code&gt;./fork&lt;/code&gt; cannot follow the &lt;code&gt;fork(2)&lt;/code&gt;-ed process.&lt;/p&gt;
&lt;h1&gt;
  
  
  Having control over them
&lt;/h1&gt;

&lt;p&gt;So we want a program able to follow &lt;strong&gt;all&lt;/strong&gt; its descendent processes.&lt;/p&gt;

&lt;p&gt;Thanks Linux for &lt;code&gt;cgroups(7)&lt;/code&gt;. We'll be using the version 2 (unified hierarchy).&lt;/p&gt;
&lt;h2&gt;
  
  
  How do &lt;code&gt;cgroups2&lt;/code&gt; work
&lt;/h2&gt;

&lt;p&gt;First, you need &lt;code&gt;cgroup2&lt;/code&gt; mounted on &lt;code&gt;/sys/fs/cgroup&lt;/code&gt;. &lt;code&gt;service-launcher&lt;/code&gt; does mount this filesystem by default on init.&lt;/p&gt;

&lt;p&gt;Now, simply cd to &lt;code&gt;/sys/fs/cgroup&lt;/code&gt; and &lt;code&gt;mkdir&lt;/code&gt;. &lt;code&gt;cd&lt;/code&gt; into that dir and surprise! All the enabled controllers are present just after you did &lt;code&gt;mkdir&lt;/code&gt;. Add your current shell PID to the cgroup &lt;code&gt;echo $$ &amp;gt;&amp;gt; cgroup.procs&lt;/code&gt;. You can also &lt;code&gt;cat&lt;/code&gt; that file to see all PIDs inside.&lt;/p&gt;

&lt;p&gt;Some rules that apply to "adding processes to cgroups":&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If the added process already has child processes, child processes are not moved inside the cgroup, only the specified PID.&lt;/li&gt;
&lt;li&gt;Processes and their threads are added into the cgroup.&lt;/li&gt;
&lt;li&gt;Child processes created by a process &lt;strong&gt;inside&lt;/strong&gt; the cgroup, are also added automatically to the cgroup (and their PID to &lt;code&gt;cgroup.procs&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If we take this rules we can make a program that:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creates a new cgroup with an specified name&lt;/li&gt;
&lt;li&gt;Executes another process inheriting an fd ( with options &lt;code&gt;O_APPEND|O_WRONLY&lt;/code&gt;) to the &lt;code&gt;cgroup.procs&lt;/code&gt; file. Arguments are to be passed to the real executable process. This child process does:

&lt;ul&gt;
&lt;li&gt;Append it's PID to the fd 3 (first fd inherited)&lt;/li&gt;
&lt;li&gt;Closes the fd&lt;/li&gt;
&lt;li&gt;Calls &lt;code&gt;execve(2)&lt;/code&gt; (&lt;code&gt;golang.org/x/sys/unix.Exec&lt;/code&gt; in Go) with arguments from cmdline.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Child process is followed until it &lt;code&gt;fork(2)&lt;/code&gt;s and exits.&lt;/li&gt;
&lt;li&gt;Now their children (from &lt;code&gt;cgroup.procs&lt;/code&gt;) are watched.&lt;/li&gt;
&lt;li&gt;Exits with status from last process in cgroup&lt;/li&gt;
&lt;li&gt;Removes cgroup before exiting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;How do we "watch". First thing I thought is to create a ticker and &lt;code&gt;unix.Wait4&lt;/code&gt; with &lt;code&gt;WNOHANG&lt;/code&gt; but then came across a section on &lt;code&gt;cgroups(7)&lt;/code&gt; talking about the &lt;code&gt;cgroup.events&lt;/code&gt; file. So we could use &lt;code&gt;inotify(7)&lt;/code&gt; to watch them for changes and react to process changes inside the cgroup.&lt;/p&gt;

&lt;p&gt;Ok, this works, but what about the process tree? It still holds the same default behavior explained previously.&lt;/p&gt;
&lt;h2&gt;
  
  
  Changing the default behavior
&lt;/h2&gt;

&lt;p&gt;We simply call &lt;code&gt;prctl(2)&lt;/code&gt; with &lt;code&gt;PR_SET_CHILD_SUBREAPER&lt;/code&gt; to become a subreaper on the current tree so orphaned child processes are attached to us.&lt;/p&gt;

&lt;p&gt;But with great power comes great responsibility. If this process becomes a subreaper, it must &lt;code&gt;wait(2)&lt;/code&gt; child processes when appended. To achieve this we listen to our favourite signal, &lt;code&gt;SIGCHLD&lt;/code&gt;, wait processes and check if cgroup still contains at least 1 process. If cgroup is empty, our exit code will be the code from last process inside the cgroup.&lt;/p&gt;
&lt;h1&gt;
  
  
  Code &amp;amp; Run
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Main process (supervisor). &lt;a href="https://gitlab.com/mrvik/go-pid1/-/tree/master/cmd/run-in-cgroup"&gt;&lt;code&gt;cmd/run-in-cgroup&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Subprocess (executor). &lt;a href="https://gitlab.com/mrvik/go-pid1/-/blob/master/cmd/exec-into-cgroup/main.go"&gt;&lt;code&gt;cmd/exec-in-cgroup&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's run &lt;code&gt;dhclient&lt;/code&gt; on &lt;code&gt;eth0&lt;/code&gt;. This process forks and previously the service succeeded when main process exited, leaving the forked process untracked.&lt;/p&gt;

&lt;p&gt;We changed the service file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dhclient@eth0&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DHCP client for eth0&lt;/span&gt;
&lt;span class="na"&gt;exec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;run-in-cgroup&lt;/span&gt;
&lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;-name=dhclient-eth0&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;--&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dhclient&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;eth0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The result looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6cWnw2GY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5qtib25zf7zite1hlymi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6cWnw2GY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5qtib25zf7zite1hlymi.png" alt="pstree with forked dhclient"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;service-launcher&lt;/code&gt; detects dhclient as running because the monitor process is alive and trackable. Also &lt;code&gt;dhclient&lt;/code&gt; is a child of its monitor process so the tree is clean and there are no lost things floating around.&lt;/p&gt;

&lt;p&gt;Do you have any thoughts here? Feedback? Can we make this better? Write a comment or feel free to create an issue.&lt;/p&gt;

&lt;p&gt;Help is needed with the name, I don't feel good calling this thing &lt;code&gt;go-pid1&lt;/code&gt; (and my lack of creativity is notable, I thought &lt;code&gt;gp1&lt;/code&gt; 😅).&lt;/p&gt;

&lt;p&gt;On next article I'll be making it work in a complete system with Void + SDDM + KDE (or something simpler, but KDE should work).&lt;/p&gt;

&lt;p&gt;See you next time.&lt;/p&gt;

</description>
      <category>go</category>
      <category>linux</category>
      <category>systemsprogramming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Writing an init with Go (part 3, running it!)</title>
      <dc:creator>MrViK</dc:creator>
      <pubDate>Tue, 05 Jan 2021 16:36:59 +0000</pubDate>
      <link>https://dev.to/__mrvik__/writing-an-init-with-go-part-3-1m5f</link>
      <guid>https://dev.to/__mrvik__/writing-an-init-with-go-part-3-1m5f</guid>
      <description>&lt;p&gt;Hey there. Sorry for the delay after parts 1 and 2. I've been busy doing some changes, will make a 4th article about those.&lt;/p&gt;

&lt;p&gt;As said before, we'll be running this init on a qemu VM.&lt;/p&gt;

&lt;p&gt;While running it, a thing I have noticed (running getty only) is that we're used to &lt;code&gt;udev&lt;/code&gt; where all things Just Work™️ when plugged. &lt;code&gt;systemd&lt;/code&gt; has involved into all those things contributing to create mysticism around the PID 1 and what it does.&lt;/p&gt;

&lt;p&gt;No more theory, let's just dive inside this thing.&lt;br&gt;
For the test I used a VM with root on 9p (could be NFS, but was just another test) so the dev and test env are shared and I can push changes and reboot to get them (at first I did poweroff, mount, copy, unmount and run it again).&lt;/p&gt;

&lt;p&gt;I did test on 2 chroots, one with Arch Linux and another one with Void (it's init-agnostic so is easier to get things done). Also compiled a kernel to have all virtio modules builtin (Arch doesn't have a working udev and didn't bother to get one so this is the bruteforce solution).&lt;/p&gt;
&lt;h1&gt;
  
  
  Compiling
&lt;/h1&gt;

&lt;p&gt;Source is here: &lt;a href="https://gitlab.com/mrvik/go-pid1"&gt;https://gitlab.com/mrvik/go-pid1&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go has one of the fastest compilers, so you can't &lt;a href="https://xkcd.com/303/"&gt;slack off&lt;/a&gt;.&lt;br&gt;
We're using &lt;code&gt;make&lt;/code&gt; to manage all the targets so you need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;make (tested with GNU Make)&lt;/li&gt;
&lt;li&gt;go toolchain (package is called go or golang depending on the distro)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then &lt;code&gt;make all&lt;/code&gt; or simply &lt;code&gt;make&lt;/code&gt; and done, you have an &lt;code&gt;out&lt;/code&gt; dir with all the utils statically linked (as they are in pure Go).&lt;/p&gt;

&lt;p&gt;You should copy them to your VM under &lt;code&gt;/usr/local/bin&lt;/code&gt; to avoid conflicts and link &lt;code&gt;init&lt;/code&gt; and &lt;code&gt;service-launcher&lt;/code&gt; to root (service launcher path is hardcoded, this should be changed).&lt;/p&gt;

&lt;p&gt;There is a folder on project called &lt;code&gt;examples&lt;/code&gt;, copy it as &lt;code&gt;/etc/yml-services&lt;/code&gt; so we have something to talk about.&lt;/p&gt;
&lt;h2&gt;
  
  
  Void w/ go on PID 1
&lt;/h2&gt;

&lt;p&gt;Here is my &lt;code&gt;qemu&lt;/code&gt; cmdline:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;qemu-system-x86_64 &lt;span class="nt"&gt;-accel&lt;/span&gt; kvm &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-kernel&lt;/span&gt; ../linux-5.10.4/arch/x86/boot/bzImage &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-initrd&lt;/span&gt; root/boot/initramfs-linux-fallback.img &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-append&lt;/span&gt; &lt;span class="s2"&gt;"root=192.168.1.10 rootfstype=9p rootflags=aname=&lt;/span&gt;&lt;span class="nv"&gt;$PWD&lt;/span&gt;&lt;span class="s2"&gt;/void,msize=16000,version=9p2000.L,uname=root,access=user rw init=/init ip=dhcp loglevel=3"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-m&lt;/span&gt; 1024 &lt;span class="nt"&gt;-nic&lt;/span&gt; user,model&lt;span class="o"&gt;=&lt;/span&gt;virtio-net-pci &lt;span class="nt"&gt;-vga&lt;/span&gt; qxl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;kernel is from the compilation dir (latest stable release at the time of preparing this). Did also test with mine (vanilla &lt;code&gt;linux&lt;/code&gt; package from Arch) and worked, so Void's should also work.&lt;/li&gt;
&lt;li&gt;initrd comes from my system (yes, kernel version doesn't match, but it doesn't matter as all modules are already built in). It was created using &lt;code&gt;mkinitcpio&lt;/code&gt; with the &lt;code&gt;net&lt;/code&gt; hook to enable networking on the early userspace. Maybe this could be done with &lt;code&gt;dracut&lt;/code&gt; but, whatever, it works.&lt;/li&gt;
&lt;li&gt;append has a lot of joy. I'm using &lt;code&gt;v9fs&lt;/code&gt; as root so all those arguments are for that purpose and &lt;code&gt;ip=dhcp&lt;/code&gt; does what you already know. The most important thing here is &lt;code&gt;init=/init&lt;/code&gt; as it's the location where I did copy the &lt;code&gt;init&lt;/code&gt; target. Setting &lt;code&gt;loglevel&lt;/code&gt; is important to avoid being flooded with kernel logs.&lt;/li&gt;
&lt;li&gt;Other boring things are the memory, a virtio net device (needed for 9p on root) and the VGA model.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Y7W47tvR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uthnd8jq6v5yqjsz4f0s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Y7W47tvR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/uthnd8jq6v5yqjsz4f0s.png" alt="Just started"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And here we are! All lines printed by go-pid1 have the same format (from &lt;code&gt;log&lt;/code&gt; package). We can login and see what's going on here:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QJ-xJOOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/krorr72r6nuepjargtf6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QJ-xJOOY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/krorr72r6nuepjargtf6.png" alt="htop showing current service hierarchy"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;htop&lt;/code&gt; shows the current hierarchy, every process is child of &lt;code&gt;service-launcher&lt;/code&gt; (who has started everything present on &lt;code&gt;/etc/yml-services&lt;/code&gt; ending with &lt;code&gt;.yml&lt;/code&gt; or &lt;code&gt;.yaml&lt;/code&gt;. &lt;code&gt;dhclient&lt;/code&gt; is forking, so he's now child of PID 1 (see next article on how do we manage this now).&lt;/p&gt;

&lt;h2&gt;
  
  
  The control socket
&lt;/h2&gt;

&lt;p&gt;On Part 2 we mentioned &lt;code&gt;service-launcher&lt;/code&gt; exposes a socket so we can control daemons and a utility called &lt;code&gt;slc&lt;/code&gt; to connect to the socket w/o openbsd-netcat installed (&lt;code&gt;-U&lt;/code&gt; option support).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--qgNMgA4i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xs9dqtajmcfcp46cf9cc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--qgNMgA4i--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/xs9dqtajmcfcp46cf9cc.png" alt="slc list output"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;List contains currently loaded services and their state. It's worth noting that &lt;code&gt;dhclient&lt;/code&gt; shows as not started (not running) as this process has forked and main process exited. We cannot track it's descendents (see next article to track progress on this).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--7ZkeMGD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e8ky9mxhwqr56yfjkbhu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--7ZkeMGD7--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/e8ky9mxhwqr56yfjkbhu.png" alt="slc restart agetty@tty1 and journal for D-Bus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We can use the &lt;code&gt;restart&lt;/code&gt; command (also the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;stop&lt;/code&gt;) with a service to make the needed actions.&lt;br&gt;
Also, the &lt;code&gt;journal&lt;/code&gt; command shows logs for the specified service (we face a lot of "operation not permitted" errors w/ root on 9p).&lt;/p&gt;

&lt;p&gt;And it works. The socket can potentially run or expose any function from the service-launcher.&lt;/p&gt;

&lt;h1&gt;
  
  
  Run init systems for fun and... fun
&lt;/h1&gt;

&lt;p&gt;As we're not dropping capabilities nor doing any type of confining, we can virtually execute all processes.&lt;br&gt;
The only limitation is the PID. Init must be PID 1, but it's already coped.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;pid_namespaces(7)&lt;/code&gt; to the rescue! Look at &lt;a href="https://gitlab.com/mrvik/go-pid1/-/blob/master/cmd/run-on-pid-ns/main.go"&gt;&lt;code&gt;cmd/run-on-pid-ns&lt;/code&gt;&lt;/a&gt;. It will create a new &lt;code&gt;pid_namespace(7)&lt;/code&gt;, &lt;code&gt;time_namespace(7)&lt;/code&gt; and &lt;code&gt;mount_namespace(7)&lt;/code&gt;. The time namespace is just to isolate uptime inside the namespace. Mount namespace allows us to have a different procfs and whatever mounts does the other init system.&lt;/p&gt;

&lt;p&gt;Let's try it. The &lt;code&gt;examples/systemd.yml.disabled&lt;/code&gt; service starts &lt;code&gt;/sbin/init&lt;/code&gt; using &lt;code&gt;run-on-pid-ns&lt;/code&gt;. It's disabled by default, you may want to disable all other services (at least agetty to avoid clashes).&lt;/p&gt;

&lt;p&gt;We're now on Void so let's try runit first.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--t2y1RN8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0bvg7s7dq73w4bx4q2yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--t2y1RN8W--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0bvg7s7dq73w4bx4q2yb.png" alt="htop w/ go-pid1 and runit as a service"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Here we can see the full process hierarchy and /sbin/init is not PID 1. That's because &lt;code&gt;htop&lt;/code&gt; uses &lt;code&gt;/proc&lt;/code&gt; which didn't get remounted so we see the full hierarchy (but cannot kill them because the PID is not accessible heh!).&lt;/p&gt;

&lt;p&gt;So if we mount &lt;code&gt;procfs&lt;/code&gt; again, we get processes on the current namespace and &lt;code&gt;htop&lt;/code&gt; (and others) work properly.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--cmwjd8gs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fg5djsqpa4gonufehxjp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--cmwjd8gs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fg5djsqpa4gonufehxjp.png" alt="htop inside the ns w/ procfs mounted shows pids in the current ns"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Another caveat is the reboot/poweroff logic. We cannot do it from the init system as it won't be able to do the &lt;code&gt;reboot(2)&lt;/code&gt; syscall (blocked because it's working on a pid namespace). We can anyway do &lt;code&gt;slc reboot&lt;/code&gt; or &lt;code&gt;slc poweroff&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's try systemd now. &lt;code&gt;systemd&lt;/code&gt; is more complex and makes use of a lot of things, but works surprisingly well.&lt;br&gt;
I'm using Arch Linux to test as it uses systemd by default.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--blKqBOOB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bc4x9cvrm2bdvdmka5z2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--blKqBOOB--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/bc4x9cvrm2bdvdmka5z2.png" alt="htop with systemd as a service"&gt;&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;As you can see, it works but we can see all processes until we mount &lt;code&gt;procfs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This init is isolated on a mount namespace so, if we launch &lt;code&gt;htop&lt;/code&gt; from inside systemd and outside, we see the effect.&lt;/p&gt;

&lt;p&gt;Outside of the namespace&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--3Jm8dkt4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s0vbc38p27hh6bx99oas.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--3Jm8dkt4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/s0vbc38p27hh6bx99oas.png" alt="Outside the ns (agetty@tty1 on go-pid1)"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Inside of the namespace&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--Xc7nIFSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fnfxpejexb5hbvlyz5d1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--Xc7nIFSs--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/fnfxpejexb5hbvlyz5d1.png" alt="Screenshot_20210105_172029"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;/run&lt;/code&gt; dir is not remounted, so the socket exposed by &lt;code&gt;service-launcher&lt;/code&gt; is there and we can connect to it.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--T0LEgWc5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5d9as1g640xnvtf7j1yb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--T0LEgWc5--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/5d9as1g640xnvtf7j1yb.png" alt="connecting to the socket from the namespace"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  What's next
&lt;/h1&gt;

&lt;p&gt;We're missing some useful features &lt;code&gt;systemd&lt;/code&gt; has (ok, we're missing a lot).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Templates: so useful for things like &lt;code&gt;agetty@&amp;lt;tty&amp;gt;&lt;/code&gt; or &lt;code&gt;dhclient@&amp;lt;interface&amp;gt;&lt;/code&gt;. It's queued but not planned.&lt;/li&gt;
&lt;li&gt;Follow forking processes. Some processes do &lt;code&gt;fork(2)&lt;/code&gt; and we can't follow them. We're currently working here, stay tuned on the 4th article to follow the process.&lt;/li&gt;
&lt;li&gt;Better handle of mounts. Also, working on this, but w/o priority.&lt;/li&gt;
&lt;li&gt;Set machine hostname at start from &lt;code&gt;/etc/hostname&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Document commands on the socket.&lt;/li&gt;
&lt;li&gt;You may have missed the reload command (something like &lt;code&gt;daemon-reload&lt;/code&gt; on systemd. It's not currently implemented, but its planned.&lt;/li&gt;
&lt;li&gt;Fix race conditions on &lt;code&gt;service-launcher&lt;/code&gt;. More likely, the process state. This may be looked with the reload feature.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are some other features on systemd that we're not going to support like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Confining all processes on &lt;code&gt;cgroups&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Derived from previous point, do firewalling on &lt;code&gt;cgroups&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Securing folders against those processes (RO home directory or private tmp).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next article will address "How to follow slippery processes". Stay tuned for more.&lt;/p&gt;

</description>
      <category>go</category>
      <category>linux</category>
      <category>systemsprogramming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Writing an init with Go (part 2)</title>
      <dc:creator>MrViK</dc:creator>
      <pubDate>Fri, 01 Jan 2021 13:49:40 +0000</pubDate>
      <link>https://dev.to/__mrvik__/writing-an-init-with-go-part-2-1dnm</link>
      <guid>https://dev.to/__mrvik__/writing-an-init-with-go-part-2-1dnm</guid>
      <description>&lt;p&gt;Welcome to the second article on this series "Writing an init with Go".&lt;/p&gt;

&lt;p&gt;As mentioned on first article, we're making an init with a decoupled service-launcher. This way we keep the PID 1 small on features (just added a handler for SIGUSR2 to reboot with kexec today).&lt;/p&gt;

&lt;p&gt;Something being argued against decoupling service management form PID 1 (systemd has this coupled) is the service manager won't be able to track processes if they &lt;code&gt;fork(2)&lt;/code&gt; away. That's almost true and is a caveat on the current code.&lt;/p&gt;

&lt;h1&gt;
  
  
  Hands on
&lt;/h1&gt;

&lt;p&gt;The first thing that comes to our head is "we have to initialize all services" and that's true, but there is a prerequisite on that: filesystems are not mounted at this moment (or we don't expect anything but / to be mounted).&lt;br&gt;
Buut, first we need to watch signals so we exit properly when killed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Signals
&lt;/h2&gt;

&lt;p&gt;A very basic thing here: we watch for &lt;code&gt;SIGINT&lt;/code&gt; (PID 1 will kill us with that signal when a &lt;code&gt;reboot(2)&lt;/code&gt; is needed) in order to do the teardown. And &lt;code&gt;SIGCHLD&lt;/code&gt; is needed to notify process watchers to check if their process is alive (we call this the &lt;code&gt;deadChildrenChannel&lt;/code&gt; and will take over this on services section)&lt;/p&gt;

&lt;h2&gt;
  
  
  Filesystems
&lt;/h2&gt;

&lt;p&gt;So here we start the real work, we mount some needed tmpfs filesystems (&lt;code&gt;/dev&lt;/code&gt; and others are already mounted at the time &lt;code&gt;init&lt;/code&gt; is run). Our current approach is to exec &lt;code&gt;mount -a&lt;/code&gt; and done, everything in fstab is mounted now. This is too lazy, to be fair filesystems don't get too much love here. This is truly a point to improve.&lt;br&gt;
Call to unmount is deferred so it gets executed on main() exit (maybe a panic).&lt;/p&gt;

&lt;h2&gt;
  
  
  Services
&lt;/h2&gt;

&lt;p&gt;Here is where the fun begins. All this work is done on the &lt;code&gt;services&lt;/code&gt; package, in case you want to give it a look first.&lt;/p&gt;

&lt;p&gt;Each service is a yaml file because, who doesn't like yaml?&lt;br&gt;
Description of units is very basic and near of the underlying &lt;br&gt;
 &lt;code&gt;cmd/exec.Cmd&lt;/code&gt; struct (e.g. arguments are an array of strings and are separated from the &lt;code&gt;exec&lt;/code&gt; line, which will be looked on &lt;code&gt;$PATH&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;A service looks like this &lt;a href="https://gitlab.com/mrvik/go-pid1/-/blob/master/example/dbus.yml"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6zdtme4t--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/y1r75ksxewqwyd6hlzy7.png" alt="Simple service from example/dbus"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After all units are read, their names are mapped so it's easier to lookup dependencies by their name. (We'll talk about them on the Dependencies section).&lt;/p&gt;

&lt;p&gt;After units are read they are checked as they must have a valid config (no empty name, no empty exec line and valid restart value). Dependencies whose yaml cannot be parsed or config is invalid are skipped (not included on the dependencies slice) and reported to stderr.&lt;/p&gt;

&lt;p&gt;Then we move on...&lt;/p&gt;

&lt;h3&gt;
  
  
  Launching services
&lt;/h3&gt;

&lt;p&gt;Each service may have dependencies, so we start first the services w/o dependencies. Those are launched in parallel with a max goroutine count of &lt;code&gt;runtime.NumCPU()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Then the rest of services which are not started yet are launched. Their dependencies are looked on the services map and cached on a field of the &lt;code&gt;Service&lt;/code&gt; struct. If there are dependencies not found or a basic cyclic dependency chain is detected, dependency will be skipped with an error.&lt;/p&gt;

&lt;p&gt;Each &lt;code&gt;Service&lt;/code&gt; struct has an &lt;code&gt;Start&lt;/code&gt; function that gets called. Note that we call all this functions in parallel w/o any concurrency control (I mean, the number of goroutines being launched) because most of them will be sleeping while their dependencies get started.&lt;/p&gt;

&lt;p&gt;We're starting to talk about dependencies, so let's address them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependency management
&lt;/h3&gt;

&lt;p&gt;This is the most complex thing on this service launcher.&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--LyN55Dcu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0uar7u4szaz4vmqg56rq.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--LyN55Dcu--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/0uar7u4szaz4vmqg56rq.jpg" alt="Me trying to fix dependency management in service launcher"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Dependencies have a lot of theory but copying from the &lt;code&gt;systemd&lt;/code&gt; devs (given a service A that depends on B):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Service A has to be started, we ensure B is running (This is working)&lt;/li&gt;
&lt;li&gt;When service B is stopped, all services depending on it have to be stopped (including A). This is not working (you just shot your feet IMO). Anyway it would be cool but challenging to do this type of traceback finding dependents (we only look for dependencies ATM).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So basic dependency management is done. I came across 3 ways of handling dependencies before settling with the "launch every service with dependencies concurrently and wait for dependencies to be started (or errored) before we really start (or error on dependency failed) each service".&lt;/p&gt;

&lt;p&gt;After all dependencies are settled the service is started (call exec) but if one of them has an error (bad config or failed to start), service will exit with error w/o trying to start it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tracking children
&lt;/h3&gt;

&lt;p&gt;Why do we need tracking?&lt;br&gt;
Well, you may not like &lt;code&gt;agetty&lt;/code&gt; being killed after you log out. You may want it to be restarted so you can login again. We need to watch the &lt;code&gt;agetty&lt;/code&gt; process to achieve this.&lt;/p&gt;

&lt;p&gt;After a service is started, we have it's main process PID on the &lt;code&gt;os.Process&lt;/code&gt; type so we can track it. Tracking processes isn't free, the current approach is to have a goroutine for each service.&lt;br&gt;
This goroutine is almost always sleeping and waked up on demand, meaning that unless a process attached to &lt;code&gt;service-launcher&lt;/code&gt; is dead (we get SIGCHLD), no one will move a finger.&lt;/p&gt;

&lt;p&gt;Also tracker goroutines have a context. If a context exits, goroutine will return immediately.&lt;/p&gt;

&lt;p&gt;This goroutine has a given design due to some limitations on the &lt;code&gt;exec.Process&lt;/code&gt; type. That struct has a &lt;code&gt;Wait&lt;/code&gt; function, but it doesn't allow context cancellation and as such, we can't use it. Instead we use the &lt;code&gt;syscall.Wait4&lt;/code&gt; with &lt;code&gt;WNOHANG&lt;/code&gt; on flags. This way we can probe our process aliveness w/o locking until it exits and wait for another dead child to be reported from channel to check it again.&lt;br&gt;
It isn't the best approach but it seems simple.&lt;/p&gt;

&lt;p&gt;Who notifies of a "dead child"? The signal watcher receives from kernel a &lt;code&gt;SIGCHLD&lt;/code&gt; (but don't say the exact PID hah!) so we notify all &lt;code&gt;Service&lt;/code&gt;s so they can see if the dead children is theirs. Note that kernel won't be repareting dead children to our PID so one of the services must match. Then the watcher can start doing the needed actions.&lt;/p&gt;

&lt;p&gt;Needed actions are basic, but I think it's enough. You can specify a policy to restart the process, "always" (main process exists and we start it again. You may not like to do this on forking processes), "failed" (only restarted if main process exit!=0) or "never" (process is waited and it does nothing even on errors).&lt;/p&gt;

&lt;p&gt;As said, when a process &lt;code&gt;fork(2)&lt;/code&gt;s we're not notified and can't follow it. See the article by Lennart Poettering &lt;a href="http://0pointer.de/blog/projects/systemd.html"&gt;"Rethinking PID 1"&lt;/a&gt;, it has a lot of interesting things. He talked about using cgroups so we're able to control child processes of our current service.&lt;br&gt;
This project has not implemented cgroups still, but may do it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Killing processes
&lt;/h3&gt;

&lt;p&gt;The time has arrived. On teardown (or stop command received from socket, we'll be over the socket in a minute) we have to kill all services processes.&lt;/p&gt;

&lt;p&gt;Again, the current approach is to kill the main process. The expected behavior of a process when it starts another is to kill all of them and don't leave those processes floating as they won't be notified of the shutdown and may lose information or block an unmount operation.&lt;br&gt;
Cgroups may help (and &lt;code&gt;systemd&lt;/code&gt; has a good approach at killing all processes in the cgroup).&lt;/p&gt;

&lt;p&gt;So we're done with services. We parsed files, started them and also stopped them. Restarting services is simple. We stop the watcher (so we don't have to think on what will that goroutine do), call Stop function and then the Start function again.&lt;/p&gt;

&lt;h2&gt;
  
  
  The socket
&lt;/h2&gt;

&lt;p&gt;We're not using dbus nor anything fancy. Just an unix socket.&lt;br&gt;
Socket is exposed with mode 600 (owner is root OFC) on /run/service-launcher.sock (see &lt;a href="https://gitlab.com/mrvik/go-pid1/-/blob/master/socket/socket.go"&gt;socket/socket.go&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;You can connect to the socket using &lt;code&gt;openbsd-netcat&lt;/code&gt; and writing things or using the &lt;a href="https://gitlab.com/mrvik/go-pid1/-/blob/master/cmd/slc/scl.go"&gt;&lt;code&gt;slc&lt;/code&gt;&lt;/a&gt; included tool (&amp;lt;30 lines of Go) to send commands like &lt;code&gt;slc reboot&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Commands are very basic:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Power management: Kill PID 1 with needed signal to achieve this.

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;reboot&lt;/code&gt;: SIGINT&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;poweroff&lt;/code&gt;: SIGUSR1&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;kexec&lt;/code&gt;: SIGUSR2&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Service management

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;stop&lt;/code&gt;, &lt;code&gt;start&lt;/code&gt;, &lt;code&gt;restart&lt;/code&gt;: With service name as argument, do that action over a service.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;list&lt;/code&gt;: list all services and their brief status&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;status&lt;/code&gt;: get current status report for a given service (as argument).&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;Journal (we'll be over this on a sec)

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;journal&lt;/code&gt;: with the service as argument, get journal for that service (ordered by date asc)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the available commands at the moment. On a near future we need more control over journal and some other things (see the planned steps on the next article).&lt;/p&gt;

&lt;h2&gt;
  
  
  Journal
&lt;/h2&gt;

&lt;p&gt;All service managers need a journal to store the stdout/err of the started services, &lt;a href="https://www.commitstrip.com/en/2020/07/03/talk-to-me/?"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--AeengFkI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://www.commitstrip.com/wp-content/uploads/2020/07/Strip-Serveur-muet-650-finalenglish.jpg" alt="try debugging w/o logs heh!"&gt;&lt;/a&gt; try debugging without logs heh!.&lt;/p&gt;

&lt;p&gt;Journal here is in-memory, so it's volatile and won't retain anything between reboots (it does between service restarts).&lt;br&gt;
It's implementation is very basic and can be found at &lt;a href="https://gitlab.com/mrvik/go-pid1/-/blob/master/journal/journal.go"&gt;journal/journal.go&lt;/a&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  What did we (at least I did) learn?
&lt;/h1&gt;

&lt;p&gt;Making a service-launcher is fun and full of challenges. We may think at first it only launches processes, but the logic under it is huge and requires a lot of things.&lt;br&gt;
To be honest, side logic didn't receive too much love, so you may notice some bugs and sure, limitations.&lt;/p&gt;

&lt;h1&gt;
  
  
  What's next?
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Talk is cheap. Show me the code&lt;br&gt;
  ~ Linus Torvalds&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That's true, we left a lot of theory above. The repository is on &lt;a href="https://gitlab.com/mrvik/go-pid1"&gt;mrvik/go-pid1&lt;/a&gt; so you may want to take a look (and maybe try) this thing on a VM.&lt;/p&gt;

&lt;p&gt;On the next article we'll be trying it and seeing how it works (could we start systemd as just another service?) we'll be over it.&lt;/p&gt;

&lt;p&gt;I hope you liked this travel over what should we expect from a PID 1. Please, don't get me wrong, I'm not aiming to replace anything nor being just &lt;a href="https://xkcd.com/927/"&gt;another competing standard&lt;/a&gt; but a proof of concept of something new, decoupled from PID 1.&lt;/p&gt;

&lt;p&gt;See you next time!&lt;/p&gt;

</description>
      <category>go</category>
      <category>linux</category>
      <category>systemsprogramming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Writing an init with Go (part 1)</title>
      <dc:creator>MrViK</dc:creator>
      <pubDate>Wed, 30 Dec 2020 13:57:27 +0000</pubDate>
      <link>https://dev.to/__mrvik__/writing-an-init-with-go-part-1-3f35</link>
      <guid>https://dev.to/__mrvik__/writing-an-init-with-go-part-1-3f35</guid>
      <description>&lt;p&gt;Welcome to the first article of the series about making an init process with Go.&lt;/p&gt;

&lt;p&gt;So recently I was making some systems programming stuff with Go (&lt;a href="https://gitlab.com/mrvik/podman-manager"&gt;a Podman service manager&lt;/a&gt; in case you want to check it out).&lt;/p&gt;

&lt;p&gt;Then thought, let's write an init, how hard can it be?&lt;/p&gt;

&lt;h2&gt;
  
  
  Hotting up
&lt;/h2&gt;

&lt;p&gt;So, what is the PID 1 supposed to be on a Linux system?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wait zombie processes&lt;/li&gt;
&lt;li&gt;Start other needed services (not really attached to PID 1)&lt;/li&gt;
&lt;li&gt;Mount filesystems (also not really attached to PID 1)&lt;/li&gt;
&lt;li&gt;Do the teardown, sync filesystems and good night.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For sure you have been thinking how to do those things on your favourite language, at first sight it isn't so hard.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tracing the first lines
&lt;/h2&gt;

&lt;p&gt;From points before:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PID 1 does point 1 and 4 (partly). Then it launches a thing called "service launcher".&lt;/li&gt;
&lt;li&gt;Service Launcher (name is so original!). It

&lt;ul&gt;
&lt;li&gt;mounts filesystems (calls &lt;code&gt;mount -av&lt;/code&gt;) (BUG here, / is not remounted according to &lt;code&gt;/etc/fstab&lt;/code&gt; settings)&lt;/li&gt;
&lt;li&gt;listens on a unix socket (control socket)&lt;/li&gt;
&lt;li&gt;launches all services, watches them (only the main process, see last links for further info).&lt;/li&gt;
&lt;li&gt;watches signals&lt;/li&gt;
&lt;li&gt;does teardown (stop services and unmount fs)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Heavy load was put under service-manager.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hands on PID 1
&lt;/h2&gt;

&lt;p&gt;Check out the code at &lt;a href="https://gitlab.com/mrvik/go-pid1/-/tree/master/cmd/init"&gt;cmd/init&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ok, this one is simple. If you had a glance at the code, you may notice it's heavily based on &lt;code&gt;sinit&lt;/code&gt;, &lt;a href="http://git.suckless.org/sinit/file/sinit.c.html"&gt;the suckless init&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;First, we start the service daemon. This call may fail, but who's afraid of an early panic on the morning?&lt;/li&gt;
&lt;li&gt;Then we make sure ctrl+alt+del sends SIGINT to PID 1 instead of calling reboot routine in kernel.&lt;/li&gt;
&lt;li&gt;Catch signals. Which signals?

&lt;ul&gt;
&lt;li&gt;SIGUSR1 - poweroff&lt;/li&gt;
&lt;li&gt;SIGINT, SIGTERM - reboot&lt;/li&gt;
&lt;li&gt;SIGCHLD - Wait zombie processes.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;After signal catcher exits (or &lt;code&gt;service-launcher&lt;/code&gt; exits) it kills &lt;code&gt;service-launcher&lt;/code&gt;, then waits for it, does &lt;code&gt;syscall.Sync()&lt;/code&gt; and &lt;code&gt;syscall.Reboot()&lt;/code&gt; with selected option (from signal watcher).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only 3 explicit syscalls here (others are done from stdlib code).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;syscall.Sync&lt;/code&gt; will block until disks are synchronized.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syscall.Reboot&lt;/code&gt; requests changes in reboot logic from kernel. E.g. it changes ctrl+alt+del behavior, asks for a reboot, poweroff, halt, and so on.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;syscall.Wait4&lt;/code&gt;. Go allows to wait for processes from stdlib, so why do we do this from &lt;code&gt;syscall&lt;/code&gt; package?. That call allows us to pass WNOHANG avoiding a blocking call, we only check if is anybody dead and ready to be removed from process table.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Nothing too complicated here, overall experience was pretty straight-forward and didn't hit any limitation in the stdlib nor the language.&lt;/p&gt;

&lt;p&gt;Also, this can be compiled as an 1.6 MiB static binary (as it has no cgo calls).&lt;br&gt;
Binary size can be reduced by removing the "log" package but I didn't see any problem with that size.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;So, what about the service launcher? It looks promising&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Yes, it was hard and has a lot of concepts, so we'll get around it on the next article (part 2). Stay tuned.&lt;/p&gt;

&lt;p&gt;Third (and last) article we'll be running this on qemu and contain some final thoughts about the future of this thing.&lt;/p&gt;

&lt;p&gt;See you next time!&lt;/p&gt;

</description>
      <category>go</category>
      <category>linux</category>
      <category>systemsprogramming</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Writing a new screen autolocker, with Go</title>
      <dc:creator>MrViK</dc:creator>
      <pubDate>Mon, 05 Aug 2019 21:11:26 +0000</pubDate>
      <link>https://dev.to/__mrvik__/writing-a-new-screen-autolocker-with-go-44l9</link>
      <guid>https://dev.to/__mrvik__/writing-a-new-screen-autolocker-with-go-44l9</guid>
      <description>&lt;h2&gt;
  
  
  Initial thoughts
&lt;/h2&gt;

&lt;p&gt;Well, I've been using a tiling window manager for some weeks, but still there's something I'm not comfortable with; &lt;em&gt;the autolocker&lt;/em&gt;. I'm using &lt;em&gt;xautolock(1)&lt;/em&gt;, but it haven't implemented signal handling (USR1 to lock screen now, etc), so I'm thinking about creating my own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Initial planning
&lt;/h2&gt;

&lt;p&gt;So I'll write my own replacement for &lt;em&gt;xautolock(1)&lt;/em&gt;. What should the new software do:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Watch X11 idle time (should have an interface)&lt;/li&gt;
&lt;li&gt;Listen USR{1,2} signals&lt;/li&gt;
&lt;li&gt;Send desktop notifications&lt;/li&gt;
&lt;li&gt;Parse arguments from command line&lt;/li&gt;
&lt;li&gt;Low start times&lt;/li&gt;
&lt;li&gt;Have as few dependencies as possible&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I started looking on the &lt;em&gt;xautolock(1)&lt;/em&gt; source code to see what did it do to check the idle time, so I found &lt;a href="https://github.com/l0b0/xautolock/blob/master/src/engine.c#L47"&gt;this line&lt;/a&gt;. I know it's not the official source, but this is enough. Now it's time to look on the &lt;code&gt;man(1)&lt;/code&gt; pages for &lt;code&gt;XScreenSaver(3)&lt;/code&gt;. Ok, there's a library with the things I need, so I should use a language that can call C functions with a reasonable overhead.&lt;/p&gt;

&lt;p&gt;Here comes the inevitable &lt;strong&gt;choose the right language&lt;/strong&gt;. My possible choices were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Javascript&lt;/code&gt; -&amp;gt; My first programming language. I thought it first, but as the last resort.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;C&lt;/code&gt; -&amp;gt; The best language to call C functions from 😄. But I'm not so proficient on that lang. (Now I'm more confident writing things with it). Also, what about threads, I should learn how to use them, and it looks so complicated. Maybe another time.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Java&lt;/code&gt; -&amp;gt; I've written tons of (shitty) programs with it, but I don't and won't use them. Java was discarded from the beginning.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Rust&lt;/code&gt; -&amp;gt; A beautiful project to get started with Rust, but maybe it would not be a production-grade software.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Golang&lt;/code&gt; -&amp;gt; I've learning it but I haven't used it on a formal project. On the tests, some concepts caught my attention like simple concurrency with goroutines and channels. Also has a &lt;a href="https://golang.org/cmd/cgo/"&gt;simple calling between C and Go&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So &lt;code&gt;golang&lt;/code&gt; is the chosen one. Now it's time to design it.&lt;/p&gt;

&lt;p&gt;Let's take the above steps from initial planning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;X11 libraries docs are available under their man page, and functions are callable with cgo.&lt;/li&gt;
&lt;li&gt;Signal catching is easy with &lt;code&gt;os/signal&lt;/code&gt; and &lt;code&gt;syscall&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Desktop notifications can be achieved with &lt;a href="//github.com/mqu/go-notify"&gt;go-notify&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Parsing arguments from the command line is possible, but the standard library package is too basic. I ended up with &lt;code&gt;github.com/akamensky/argparse&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Golang has a runtime statically linked to the final binary, but the startup times are very low and doesn't have a hard impact on the performance.&lt;/li&gt;
&lt;li&gt;Well, the dependencies. LibX11 for &lt;code&gt;XOpenDisplay(3)&lt;/code&gt; and &lt;code&gt;xscrnsaver&lt;/code&gt; extension to get idle time info. Also depends on libnotify. Some distributions split the libraries and headers. Headers are needed then compiling.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, let's start. &lt;code&gt;take ...&lt;/code&gt; hmm, how do I name this thing? Sorry for the low effort name, but it's called &lt;code&gt;goautolock(1)&lt;/code&gt;. Ok, &lt;code&gt;take goautolock&amp;amp;&amp;amp;git init&lt;/code&gt;. (&lt;code&gt;take&lt;/code&gt; is a function defined on &lt;a href="https://github.com/robbyrussell/oh-my-zsh"&gt;oh-my-zsh&lt;/a&gt; that runs &lt;code&gt;mkdir&lt;/code&gt; with all parameters and does cd on the last one).&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing it
&lt;/h2&gt;

&lt;p&gt;With the initial version I faced my first race conditions. Those weren't causing unexpected behavior, but go expects memory accesses to be synced, so I improved some features to convert some flags to channels. This change improved the performance (e.g. waiting 2 seconds at max to lock screen after SIGUSR1 vs locking immediately after getting a message on a channel).&lt;br&gt;
That didn't fix all of the race conditions caused by the initial design, so a recent version implemented the mutexes provided by the go standard library.&lt;br&gt;
It suffered many changes from the first time written. Take a look at the functions calling C libraries.&lt;br&gt;
First time written:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--pfZedBfF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7rcdfsuhyfijuw9abavc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--pfZedBfF--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/7rcdfsuhyfijuw9abavc.png" alt="first implementation of C functions"&gt;&lt;/a&gt;&lt;br&gt;
Now looks like this:&lt;br&gt;
&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--SQ8EfirZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9jmkzancbrvn284isu8f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--SQ8EfirZ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://thepracticaldev.s3.amazonaws.com/i/9jmkzancbrvn284isu8f.png" alt="improved implementation"&gt;&lt;/a&gt;&lt;br&gt;
Note the calls to &lt;code&gt;C.free&lt;/code&gt; to fix memory leaks.&lt;br&gt;
When I got into signal handling, I thought: "There are 2 user defined signals without any special meaning, one of them will call the locker immediately, but ¿what about the other one?" So, I decided to stole a concept from LineageOS (formerly Cyanogen MOD) where you could add caffeine to prevent looking the screen for a time. The implementation on &lt;code&gt;goautolock(1)&lt;/code&gt; is simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Each SIGUSR2 adds X seconds to the lock delay. Take X like the value of &lt;code&gt;--caffeine&lt;/code&gt; option (defaulting to 10).&lt;/li&gt;
&lt;li&gt;After time is elapsed &lt;em&gt;and&lt;/em&gt; screen is locked, this extra delay is set to 0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Writing experience with golang
&lt;/h2&gt;

&lt;p&gt;The first impression I had is that golang has a very limited set of options (coming from JavaScript that allows you to do whatever you want). And the error handling (split on panic and error returns) was chaotic at the first glance, but when used to it, the experience is the most consistent I had.&lt;br&gt;
Also, it has some syntactic sugar like prepending &lt;code&gt;defer&lt;/code&gt; to a expression to make it run after the actual function has been finished. This allows you to free C variables not managed by the GC just after the allocation, so you don't have to find the original declaration when you see a call to &lt;code&gt;C.free&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Documentation
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;godoc&lt;/code&gt; is a very powerful utility for generating docs from source code, but this is a user utility, so the docs should be on the standard &lt;code&gt;man(1)&lt;/code&gt;. This was the most difficult part, using &lt;code&gt;groff(7)&lt;/code&gt; to create a man page. There are a lot of macros to format the output. Now the result is available at the &lt;code&gt;/docs&lt;/code&gt; folder on project (links on the bottom).&lt;/p&gt;

&lt;h2&gt;
  
  
  Packaging
&lt;/h2&gt;

&lt;p&gt;This is an utility for Linux, each distro has a different packaging system (I won't use flatpak or &lt;del&gt;spam&lt;/del&gt; snap for this).&lt;br&gt;
The first thing I did was to build a release binary on the GitLab pipeline.&lt;br&gt;
On the other hand, I want goautolock to be updated on my laptop when I do &lt;code&gt;pakku -Syu&lt;/code&gt;, so I uploaded it to the AUR. It was so easy using &lt;code&gt;ldd&lt;/code&gt; and &lt;code&gt;namcap&lt;/code&gt; and testing with &lt;code&gt;makepkg&lt;/code&gt; on a clean chroot.&lt;/p&gt;

&lt;p&gt;The other distro I use is Fedora. This gave me some headaches.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Package names aren't the same nor similar to ArchLinux.&lt;/li&gt;
&lt;li&gt;Devel packages are separated from the main ones.&lt;/li&gt;
&lt;li&gt;Which utility should I use to create a RPM following a spec file? rpkg? a Makefile? fedpkg? tito?&lt;/li&gt;
&lt;li&gt;The AUR counterpart (saving the distances) on Fedora is the &lt;em&gt;COPR&lt;/em&gt;. Let's start a repo!&lt;/li&gt;
&lt;li&gt;Wait, I want a RPM, what the hell is a SRPM?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After figuring out I should make another repo in &lt;a href="https://pagure.io"&gt;Fedora Pagure&lt;/a&gt; to upload the spec and source tarball, I started using rpkg to build the srpm. And after a lot of failed builds it's finally uploaded to &lt;em&gt;Fedora COPR&lt;/em&gt;. Then I started to pack some other projects, so everybody can easily use them.&lt;/p&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;p&gt;And here's the link to the project and some others if you want to contribute or propose something:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gitlab.com/mrvik/goautolock"&gt;GitLab&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aur.archlinux.org/packages/goautolock/"&gt;AUR&lt;/a&gt; for the fellow Arch users&lt;/li&gt;
&lt;li&gt;&lt;a href="https://pagure.io/goautolock"&gt;Pagure.io&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://copr.fedorainfracloud.org/coprs/mrvik/goautolock"&gt;Fedora COPR&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>go</category>
      <category>c</category>
      <category>linux</category>
      <category>x11</category>
    </item>
  </channel>
</rss>
