<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:base="https://www.brightbox.com/">
  <id>https://www.brightbox.com/blog/</id>
  <title>Brightbox Blog</title>
  <updated>2026-03-25T00:00:00Z</updated>
  <link rel="alternate" href="https://www.brightbox.com/blog/" type="text/html"/>
  <link rel="self" href="https://www.brightbox.com/blog/feed.xml" type="application/atom+xml"/>
  <author>
    <name>Brightbox</name>
    <uri>https://www.brightbox.com/</uri>
  </author>
  <entry>
    <id>tag:www.brightbox.com,2026-03-25:/blog/2026/03/25/kubernetes-updates/</id>
    <title type="html">Brightbox Kubernetes 1.35</title>
    <published>2026-03-25T00:00:00Z</published>
    <updated>2026-03-25T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2026/03/25/kubernetes-updates/" type="text/html"/>
    <content type="html">
&lt;figure class="content-image content-image--right content-image--no-border"&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/blog/kubernetes-logo.svg" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;p&gt;We’re now supporting Kubernetes through to version 1.35.&lt;/p&gt;

&lt;p&gt;That means new versions of our Kubernetes &lt;a href="https://github.com/brightbox/brightbox-cloud-controller-manager/"&gt;Cloud
Controller&lt;/a&gt;,
which provides tighter integration with Brightbox, adding Brightbox-specific
details to the nodes in Kubernetes, labelling them with the zone, image type and
the cloud addressing details and integrates with our Load Balancer service as an
ingress controller.&lt;/p&gt;

&lt;p&gt;And we’ve updated our &lt;a href="https://github.com/brightbox/kubernetes-cluster/"&gt;Kubernetes cluster terraform
manifests&lt;/a&gt; too, which helps
you &lt;a href="/docs/guides/kubernetes/deploy-kubernetes-on-brightbox-cloud/"&gt;setup and manage&lt;/a&gt; 
a full auto-scaling Kubernetes cluster directly on Brightbox Cloud.&lt;/p&gt;

&lt;p&gt;Kubernetes 1.35 brings a few really useful features but here’s a couple that
stand out to me:&lt;/p&gt;

&lt;h3 id="in-place-vertical-scaling-of-pods"&gt;In-place vertical scaling of pods&lt;/h3&gt;

&lt;p&gt;In-place vertical scaling of pods is now stable, so you can increase cpu and ram
on pods without having to restart them. Kubernetes makes it easy to avoid
downtime during restarts, but it’s still nice to be able to avoid them
sometimes!&lt;/p&gt;

&lt;h3 id="prefersamenode-traffic-distribution"&gt;PreferSameNode traffic distribution&lt;/h3&gt;

&lt;p&gt;PreferSameNode traffic distribution is now stable too, which can really help
with performance by keeping traffic within the same node when possible. Up until
now, in the extreme cases, we’ve been solving this problem by running some
services as sidecars which solves the problem but means you’re often
over-provisioning services. Also note that PreferClose has become
PreferSameZone.&lt;/p&gt;

&lt;h3 id="pod-level-resource-requests"&gt;Pod-Level Resource Requests&lt;/h3&gt;

&lt;p&gt;This actually came along with 1.34 and is still in beta in 1.35, but it’s useful
when you have a pod with multiple containers that make up one service and don’t
want to have to think about specifically allocating cpu or ram to them
individually. One example for us is running high load php apps, where we have
separate apache and php-fpm containers. It makes no sense to be
requesting/limiting apache cpu usage separately to php.&lt;/p&gt;

&lt;h3 id="ordered-namespace-deletion"&gt;Ordered namespace deletion&lt;/h3&gt;

&lt;p&gt;Again, this actually came up in 1.34 but it’s worth a mention. Deleting
namespaces should no longer result in stuck namespaces due to problems with
finalizers!&lt;/p&gt;

&lt;h2 id="upgrading"&gt;Upgrading&lt;/h2&gt;

&lt;p&gt;To upgrade your Kubernetes clusters, simply pull down the latest Terraform
configs &lt;a href="https://github.com/brightbox/kubernetes-cluster"&gt;from our repository&lt;/a&gt;
and run &lt;code&gt;terraform apply&lt;/code&gt;. Terraform will then upgrade each node in your cluster
one by one and shouldn’t cause any interruption in service. We generally
recommend upgrading to the latest point release for your current minor version
before upgrading the minor version. And if you’re behind on minor versions,
upgrade each minor version in turn - don’t skip minor versions!&lt;/p&gt;

&lt;p&gt;If your Kubernetes cluster is &lt;a href="/managed/"&gt;managed by us&lt;/a&gt;, then you don’t have to
do anything and we’ll arrange the upgrade for you.&lt;/p&gt;

&lt;h3 id="containerd-10-support-comes-to-an-end"&gt;containerd 1.0 support comes to an end&lt;/h3&gt;

&lt;p&gt;This is the last version of Kubernetes that supports containerd 1.x so you’ll
want to be thinking about upgrading all your nodes. Aiming for the upcoming
Ubuntu LTS 26.04 release will get you that, or consider upgrading containerd manually.&lt;/p&gt;

&lt;h2 id="k3s-lightweight-kubernetes"&gt;k3s: Lightweight Kubernetes&lt;/h2&gt;

&lt;p&gt;We’ve also been playing with the trimmed down lightweight Kubernetes project,
&lt;a href="https://k3s.io/"&gt;k3s&lt;/a&gt;. Our cloud controller works perfectly fine on k3s and
we’re working on some kubernetes manifests to build k3s clusters - coming soon!&lt;/p&gt;

&lt;h2 id="get-started"&gt;Get started&lt;/h2&gt;

&lt;p&gt;You can &lt;a href="https://cloud.brightbox.com/signup"&gt;sign up for Brightbox in just a couple of minutes&lt;/a&gt;
and get a &lt;em&gt;£50 free credit&lt;/em&gt; to play with Kubernetes.&lt;/p&gt;

&lt;p&gt;We’ve got a simple &lt;a href="/docs/guides/kubernetes/deploy-kubernetes-on-brightbox-cloud/"&gt;guide to deploying a Kubernetes
cluster&lt;/a&gt;
pre-configured with our cloud controller using Terraform.&lt;/p&gt;

&lt;h2 id="managed-kubernetes"&gt;Managed Kubernetes&lt;/h2&gt;

&lt;p&gt;If you’re looking for a UK-based fully-managed Kubernetes solution, &lt;a href="mailto:hello@brightbox.com"&gt;drop us a
line&lt;/a&gt;.&lt;/p&gt;

</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2025-01-14:/blog/2025/01/14/orbit-object-storage-updates/</id>
    <title type="html">Orbit Object Storage updates</title>
    <published>2025-01-14T00:00:00Z</published>
    <updated>2025-01-14T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2025/01/14/orbit-object-storage-updates/" type="text/html"/>
    <content type="html">
&lt;figure class="content-image content-image--no-border"&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/blog/orbit-blog-header.png" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;p&gt;&lt;a href="/cloud/storage/"&gt;Orbit&lt;/a&gt; is our highly available and durable object storage
service. It’s at the centre of so many of our services at Brightbox: all Cloud
Server and Cloud SQL snapshots, all our managed customer logs and backups, and
of course it is used directly by customers via its HTTP APIs to store various
assets.&lt;/p&gt;

&lt;p&gt;Orbit is based on the excellent OpenStack Swift project and last year we rolled
out some updates that add new features. We also added and upgraded a lot of
Orbit hardware that improved performance too.&lt;/p&gt;

&lt;h3 id="performance"&gt;Performance&lt;/h3&gt;

&lt;p&gt;We moved all container services to SSD which improved latency for most metadata
operations considerably, as well as allowing us to improve consistency with
faster replication updates.&lt;/p&gt;

&lt;p&gt;We were able to increase throughput on the object HDD storage systems and now
objects uploads are faster: in particular, larger object uploads are 1.65x
faster!&lt;/p&gt;

&lt;p&gt;And due to some cache tuning, we’ve also improved read times for “hot” objects.&lt;/p&gt;

&lt;p&gt;We’ve got more networking improvements planned for this year which should get us
another boost too. We’ll keep you posted.&lt;/p&gt;

&lt;h3 id="s3-compatibility"&gt;S3 Compatibility&lt;/h3&gt;

&lt;p&gt;The Swift API is well supported but we know that the S3 API has become a strong
defacto standard for storage systems over the last few years, particularly in
the Kubernetes world. Orbit’s S3 compatibility has now been expanded and
&lt;a href="https://docs.openstack.org/swift/wallaby/s3_compat.html#amazon-s3-operations"&gt;supports more S3
features&lt;/a&gt;
and with improved S3 performance.&lt;/p&gt;

&lt;h3 id="temporary-url-improvements"&gt;Temporary URL improvements&lt;/h3&gt;

&lt;p&gt;Temporary URLs allows the creation of a special URL that points to a specific
object and allows reads and/or writes to it for a limited amount of time without
requiring authentication credentials. This is perfect for when you want to
control access to objects via a separate application but still want Orbit to
handle the serving of the objects. And you can now limit access to a temporary
URL by IP address.&lt;/p&gt;

&lt;h3 id="and-more"&gt;And more…&lt;/h3&gt;

&lt;p&gt;And as ever, Orbit runs on our entirely UK owned and operated infrastructure and
stores 3 copies of each object on separate storage devices. Data is encrypted
in-flight and at-rest on disk.&lt;/p&gt;

&lt;p&gt;You can read and write data to Orbit containers using the standard OpenStack
Swift HTTP API, any number of libraries (Ruby, Node, PHP etc), the &lt;a href="/docs/guides/orbit/swift-cli/"&gt;Swift CLI
tool&lt;/a&gt; or even our simple &lt;a href="/docs/guides/orbit/sftp/"&gt;SFTP
service&lt;/a&gt;. Anything that can use OpenStack Swift can
use Orbit! We’ve got guides for many tools, such as for the &lt;a href="/docs/guides/orbit/restic/"&gt;Restic backup
tool&lt;/a&gt; and &lt;a href="/docs/guides/orbit/rclone/"&gt;Rclone&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id="try-orbit-on-brightbox"&gt;Try Orbit on Brightbox&lt;/h3&gt;

&lt;p&gt;You can &lt;a href="https://cloud.brightbox.com/signup"&gt;sign up for Brightbox&lt;/a&gt; in
just a couple of minutes and use your £50 free credit to play with Orbit. Give
it a go!&lt;/p&gt;

</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-10-10:/blog/2024/10/10/postgresql-17-cloudsql/</id>
    <title type="html">PostgreSQL 17 now available in Cloud SQL</title>
    <published>2024-10-10T00:00:00Z</published>
    <updated>2024-10-10T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/10/10/postgresql-17-cloudsql/" type="text/html"/>
    <content type="html">
&lt;figure class="content-image content-image--right content-image--no-border"&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/blog/postgresql-logo.png" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;p&gt;Today we’re announcing support for version 17 of &lt;a href="https://www.postgresql.org"&gt;PostgreSQL&lt;/a&gt;
in &lt;a href="/cloud/database/"&gt;Cloud SQL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;PostgreSQL 17 contains a number of new features and enhancements, including:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;New memory management system for &lt;code&gt;VACUUM&lt;/code&gt; that consumes up to 20x less memory&lt;/li&gt;
  &lt;li&gt;New SQL/JSON capabilities, including a new &lt;code&gt;JSON_TABLE()&lt;/code&gt; function which
converts JSON data into a standard PostgreSQL table&lt;/li&gt;
  &lt;li&gt;Various query performance improvements, including increased write throughput 
under high
concurrency&lt;/li&gt;
  &lt;li&gt;Several enhancements to logical replication&lt;/li&gt;
  &lt;li&gt;New &lt;code&gt;pg_maintain&lt;/code&gt; predefined role, which gives users permission to perform
maintenance operations&lt;/li&gt;
  &lt;li&gt;
&lt;code&gt;pg_basebackup&lt;/code&gt; now supports incremental backups&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Check out the official &lt;a href="https://www.postgresql.org/docs/current/release-17.html"&gt;release notes&lt;/a&gt;
for a complete list of updates and details.&lt;/p&gt;

&lt;p&gt;If you’re new to Brightbox, Cloud SQL is our simple database-as-a-service
which enables you to effortlessly create high performance PostgreSQL and MySQL 
database servers with automated backups and security updates as well as the
ability to &lt;a href="/docs/guides/control-panel/resize-cloud-sql/"&gt;instantly increase&lt;/a&gt;
RAM, CPU and storage resources when required.&lt;/p&gt;

&lt;p&gt;Cloud SQL features PostgreSQL support back to version 12, and still allow building
instances with older deprecated versions back to version 9.5 if required.&lt;/p&gt;

&lt;figure class="content-image "&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/blog/postgres-17-engine.png" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;h2 id="try-postgresql-17-on-brightbox-for-free"&gt;Try PostgreSQL 17 on Brightbox for free!&lt;/h2&gt;

&lt;p&gt;If you’re an existing customer you can start creating Postgres 17 instances
right away.&lt;/p&gt;

&lt;p&gt;If you’re not yet a customer you can &lt;a href="https://cloud.brightbox.com/signup"&gt;create a Brightbox account&lt;/a&gt;
in just a couple of minutes and get a £50 free credit to give Cloud SQL a try.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-09-18:/blog/2024/09/18/sharing-secrets-with-ssh-keys/</id>
    <title type="html">Sharing secrets with SSH keys</title>
    <published>2024-09-18T00:00:00Z</published>
    <updated>2024-09-18T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/09/18/sharing-secrets-with-ssh-keys/" type="text/html"/>
    <content type="html">&lt;p&gt;As part of our &lt;a href="https://www.brightbox.com/managed/"&gt;managed ops services&lt;/a&gt; we
often need to share sensitive information such as passwords and keys with
customers. &lt;a href="https://en.wikipedia.org/wiki/GNU_Privacy_Guard"&gt;GPG&lt;/a&gt; is commonly
used for this kind of thing, but if our customers aren’t already used to PGP
it’s a bit of a pain - particularly with all the key swapping, signing and trust
settings you often need to manage.&lt;/p&gt;

&lt;p&gt;However, most of our customers already have an SSH keypair that they use to
access their Brightbox servers and if we’re managing their servers we already
know their public keys. Can we use a customer’s SSH keys to encrypt messages for
them? Yes we can! And a &lt;a href="https://age-encryption.org/"&gt;tool called age&lt;/a&gt; makes
this easy - much eaiser than GPG.&lt;/p&gt;

&lt;p&gt;First, &lt;a href="https://github.com/FiloSottile/age?tab=readme-ov-file#installation"&gt;install
age&lt;/a&gt;. It’s
available natively for most modern Linux distros, BSDs, Windows and macOS so it
should only take a minute.&lt;/p&gt;

&lt;p&gt;Then to encrypt a message with an SSH public key run age with the
&lt;code&gt;--recipients-file&lt;/code&gt; argument (and use CTRL+D to finish the message of course):&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;age &lt;span class="nt"&gt;--recipients-file&lt;/span&gt; ~/keys/customer-ssh-key.pub &lt;span class="nt"&gt;--encrypt&lt;/span&gt; &lt;span class="nt"&gt;--armor&lt;/span&gt;

Hello world

&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHlMSFZYQSBjVkZC
YlRKcG1JcG8xTkFjb2pzOHdEVUoyaWFhWTNTaE1tZDNhd0VhQkQwCm1VMkFzd01H
QWYxREhlQXF6TTJGU3ZrMGhMTDFKb2ZhU2JTeUhLdnlhcEEKLS0tIDM5UWVDUWJz
WTZYZkZpTTVQcWE0UkM4aTRMNnIrUzJUMHVKVFVRWGorVXMKN4uVA5nSZP0uAWlH
xjHVbrO42sNUe3jiI+rHKP+epsWvtRl5rMlVfTVxDzUc
&lt;span class="nt"&gt;-----END&lt;/span&gt; AGE ENCRYPTED FILE-----&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;You can then paste the output in an email or a text chat session.&lt;/p&gt;

&lt;p&gt;And then for them to decrypt the message with their SSH private key:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;age &lt;span class="nt"&gt;--decrypt&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; .ssh/my-ssh-key.key

&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNzaC1lZDI1NTE5IHlMSFZYQSBjVkZC
YlRKcG1JcG8xTkFjb2pzOHdEVUoyaWFhWTNTaE1tZDNhd0VhQkQwCm1VMkFzd01H
QWYxREhlQXF6TTJGU3ZrMGhMTDFKb2ZhU2JTeUhLdnlhcEEKLS0tIDM5UWVDUWJz
WTZYZkZpTTVQcWE0UkM4aTRMNnIrUzJUMHVKVFVRWGorVXMKN4uVA5nSZP0uAWlH
xjHVbrO42sNUe3jiI+rHKP+epsWvtRl5rMlVfTVxDzUc
&lt;span class="nt"&gt;-----END&lt;/span&gt; AGE ENCRYPTED FILE-----

Hello world&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Of course age isn’t limited to short text messages and will encrypt large files
too, just don’t use the &lt;code&gt;--armor&lt;/code&gt; option it’ll generate a more compact binary
output.&lt;/p&gt;

&lt;h3 id="integration-with-github"&gt;Integration with GitHub&lt;/h3&gt;

&lt;p&gt;All GitHub users have an SSH key and if you know somebody’s GitHub username you
can encrypt things for them with age:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl https://github.com/johnl.keys | age &lt;span class="nt"&gt;--recipients-file&lt;/span&gt; - &lt;span class="nt"&gt;--armor&lt;/span&gt; message.txt&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="age-has-its-own-keys-too"&gt;Age has its own keys too&lt;/h3&gt;

&lt;p&gt;If you prefer to use separate keys, age does have its own key format and keys
can be generated with the &lt;code&gt;age-keygen&lt;/code&gt; tool. age keys are pretty neat and easy
to share:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;age-keygen 

&lt;span class="c"&gt;# created: 2024-09-18T10:53:06+01:00&lt;/span&gt;
&lt;span class="c"&gt;# public key: age1texszzr0vlrfvjug5gd4dgkla39j2swchvrgdmd8qg50wafnsqqs8ygqhp&lt;/span&gt;
AGE-SECRET-KEY-1YJ23M3V0LH2AXGHHNTE8GLPWAAP3HEFHT9Y6LYQRCWDP8VHXWC4Q3DJ5A6&lt;/code&gt;&lt;/pre&gt;

&lt;h3 id="passwords-instead-of-keys"&gt;Passwords instead of keys&lt;/h3&gt;

&lt;p&gt;You can also just forego the use of public and private keys and use symmetric
key encryption and choose a password when encrypting. If you don’t provide one,
age will randomly generate a password that you can easily read out to someone
in-person or over the phone:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;age &lt;span class="nt"&gt;--encrypt&lt;/span&gt; &lt;span class="nt"&gt;--passphrase&lt;/span&gt; &lt;span class="nt"&gt;--armor&lt;/span&gt;

Enter passphrase &lt;span class="o"&gt;(&lt;/span&gt;leave empty to autogenerate a secure one&lt;span class="o"&gt;)&lt;/span&gt;:

age: using autogenerated passphrase &lt;span class="s2"&gt;"object-feature-make-rather-add-coil-project-february-easily-goose"&lt;/span&gt;

Hello World

&lt;span class="nt"&gt;-----BEGIN&lt;/span&gt; AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IHNjcnlwdCByektjYXdid0M4MytRUmI1
U3lJYm13IDE4Ci83d1RqRUJ3S2Q1TkZpNndFZUhRVUVGcERpNUxCR3V4dStVOTMv
TC9xREkKLS0tIE44TG5tUm9SWXd5Z05jUDlVMXBJQjl5L29VcXlvOFJlai9xU0pV
Q1FVUjgKeSW+1umEBcBn8BZ983gdf3nG4yuzuJz2Vt6VluIEIbFBiDI2aDVEu8uW
jC+C
&lt;span class="nt"&gt;-----END&lt;/span&gt; AGE ENCRYPTED FILE-----&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And you can use the passhrase encryption to secure your age keys on-disk - age
recognises this and will prompt for the passphrase.&lt;/p&gt;

&lt;h3 id="managing-secrets-in-git-repositories"&gt;Managing secrets in git repositories&lt;/h3&gt;

&lt;p&gt;age can be used to encrypt files in git repositories using a tool called
&lt;a href="https://github.com/getsops/sops"&gt;SOPS&lt;/a&gt;. We use this for encrypting secrets in
&lt;a href="https://opentofu.org"&gt;OpenTofu&lt;/a&gt;/&lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; and
Kubernetes manifests. We’ll go into more detail on how to use that in a future
blog post.&lt;/p&gt;

&lt;h3 id="limitations"&gt;Limitations&lt;/h3&gt;

&lt;p&gt;SSH keys are usually used only for authentication, so people may not treat them
quite the same as a real encryption key - they may be more likely to share them,
or otherwise treat them as revokable.&lt;/p&gt;

&lt;p&gt;And SSH keys on hardware such as YubiKeys can’t be used to decrypt files.&lt;/p&gt;

&lt;p&gt;But otherwise, using SSH keys with age is a very convenient way of occasionally
sharing secrets.&lt;/p&gt;

</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-04-11:/blog/2024/04/11/flatcar-images/</id>
    <title type="html">Flatcar Container Linux official images now available</title>
    <published>2024-04-11T00:00:00Z</published>
    <updated>2024-04-11T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/04/11/flatcar-images/" type="text/html"/>
    <content type="html">
&lt;figure class="content-image content-image--right content-image--no-border"&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/docs/flatcar-logo.png" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;p&gt;&lt;a href="https://www.flatcar.org/"&gt;Flatcar Container Linux&lt;/a&gt; is a minimal, container-optimised Linux distribution, which is a direct descendent and spiritual successor of CoreOS Container Linux, which was &lt;a href="https://coreos.com/os/eol/#timeline"&gt;discontinued&lt;/a&gt; and replaced by Fedora CoreOS in 2020.&lt;/p&gt;

&lt;p&gt;Flatcar provides a &lt;a href="https://www.flatcar.org/docs/latest/migrating-from-coreos/"&gt;drop-in replacement&lt;/a&gt; and plans to continue following the philosophy pioneered by CoreOS, delivering a fully open source, minimal-footprint, secure by default and always up-to-date Linux distribution for running containers at scale.&lt;/p&gt;

&lt;p&gt;Recently, Flatcar began building and testing images for deployment on Brightbox and I’m pleased to announce that we’re now automatically importing the latest Stable, LTS and Beta releases as a result.&lt;/p&gt;

&lt;p&gt;The images are available now in our image library and integrate with our Amazon EC2-compatible metadata service, so they install your ssh keys automatically on boot and you can ssh straight in as the &lt;code&gt;core&lt;/code&gt; user.&lt;/p&gt;

&lt;p&gt;Like all of our other official linux distro images, they’re provided completely unmodified from the upstream source, so you can be sure there are no hidden surprises. If your systems work on the upstream images, they’ll work on ours.&lt;/p&gt;

&lt;p&gt;We’ve written a &lt;a href="/docs/guides/flatcar-getting-started/"&gt;guide to help you get started&lt;/a&gt; and you should be able to get a new Flatcar server with a basic configuration up and running in a few minutes.&lt;/p&gt;

&lt;p&gt;Check out Flatcar’s &lt;a href="https://www.flatcar.org/docs/latest/"&gt;documentation&lt;/a&gt; for more details on the available features and more advanced configuration topics.&lt;/p&gt;

&lt;p&gt;If there are other well-maintained linux distros that you’d like to see us offer in the cloud server image library, do &lt;a href="/about/contact/"&gt;let us know&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, if you’re not yet a Brightbox customer, you can &lt;a href="https://cloud.brightbox.com/signup"&gt;create an account&lt;/a&gt; in just 2 minutes and get £50 free trial credit to try the new images out.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-04-09:/blog/2024/04/09/resize-cloudsql/</id>
    <title type="html">Resizing Cloud SQL Database Instances</title>
    <published>2024-04-09T00:00:00Z</published>
    <updated>2024-04-09T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/04/09/resize-cloudsql/" type="text/html"/>
    <content type="html">&lt;p&gt;Last year we introduced the ability to
&lt;a href="/blog/2023/06/28/resize-cloud-servers/"&gt;resize Cloud Servers&lt;/a&gt;, which makes it
really quick and easy to increase RAM/CPU and SSD storage for your cloud
servers.&lt;/p&gt;

&lt;p&gt;I’m really pleased to say that we’ve now added the ability to resize Cloud SQL
instances too!&lt;/p&gt;

&lt;p&gt;&lt;a href="/cloud/database/"&gt;Cloud SQL is our database-as-a-service&lt;/a&gt; which enables you to
effortlessly create high performance MySQL and PostgreSQL database servers with
automated backups and security updates.&lt;/p&gt;

&lt;p&gt;So now if you need to add more RAM, CPU and SSD storage to your Cloud SQL
instance you can simply choose a larger database type and hit “Save”. The change
will happen instantly, but a restart is needed for the database engine to make
full use of the new resources. A restart is automatically scheduled for your
next maintenance window or you can choose to manually restart at your
own convenience using the new Restart action.&lt;/p&gt;

&lt;p&gt;We’ve written a simple guide to &lt;a href="/docs/guides/control-panel/resize-cloud-sql/"&gt;guide to resizing Cloud SQL instances&lt;/a&gt; so do check that out and as always if you have any questions
or feedback &lt;a href="/about/contact/"&gt;let us know&lt;/a&gt; and we’ll be happy to help.&lt;/p&gt;

&lt;figure class="content-image content-image--no-border"&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/docs/resize-cloud-sql-choose-type.png" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;p&gt;If you’re an existing customer you can start using the new resize feature
straightaway. If you’re not yet a customer, you can
&lt;a href="https://cloud.brightbox.com/signup"&gt;get signed up in 2 minutes&lt;/a&gt; and try out
Brightbox with a free £50 trial credit.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-02-02:/blog/2024/02/02/opentofu-support/</id>
    <title type="html">Exploring OpenToFu: A Fork of Terraform</title>
    <published>2024-02-02T00:00:00Z</published>
    <updated>2024-02-02T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/02/02/opentofu-support/" type="text/html"/>
    <content type="html">
&lt;figure class="content-image content-image--no-border"&gt;
  
    &lt;div class="content-image__container"&gt;
      &lt;img class="content-image__img" src="/images/blog/opentofu-logo.jpg" alt=""&gt;
    &lt;/div&gt;
  

&lt;/figure&gt;

&lt;p&gt;In the world of infrastructure as code (IaC), &lt;a href="https://www.terraform.io/"&gt;Terraform&lt;/a&gt; has been a
stalwart companion for nearly a decade. However, recent changes in its
licensing have left many in the community seeking alternatives. Enter
OpenToFu – a fork of Terraform that emerged as a result of
community-driven development.&lt;/p&gt;

&lt;p&gt;OpenToFu aims to be backward compatible with Terraform, making the
transition for existing users smooth. The familiar syntax, provider
ecosystem, and module structure are all present, ensuring that your
existing infrastructure code works as expected.&lt;/p&gt;

&lt;p&gt;You’ll be pleased to know that the existing Brightbox terraform driver
works flawlessly with the OpenToFu fork, giving Brightbox users the
choice as to which tool to use.&lt;/p&gt;

&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;

&lt;p&gt;OpenToFu is straightforward to &lt;a href="https://opentofu.org/docs/intro/install/"&gt;install in various
ways&lt;/a&gt;, and they provide a convenient
install script that takes care of the process for you:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="c"&gt;# Download the installer script:&lt;/span&gt;
curl &lt;span class="nt"&gt;--proto&lt;/span&gt; &lt;span class="s1"&gt;'=https'&lt;/span&gt; &lt;span class="nt"&gt;--tlsv1&lt;/span&gt;.2 &lt;span class="nt"&gt;-fsSL&lt;/span&gt; https://get.opentofu.org/install-opentofu.sh &lt;span class="nt"&gt;-o&lt;/span&gt; install-opentofu.sh

&lt;span class="c"&gt;# Give it execution permissions:&lt;/span&gt;
&lt;span class="nb"&gt;chmod&lt;/span&gt; +x install-opentofu.sh

&lt;span class="c"&gt;# Please inspect the downloaded script&lt;/span&gt;

&lt;span class="c"&gt;# Run the installer:&lt;/span&gt;
./install-opentofu.sh &lt;span class="nt"&gt;--install-method&lt;/span&gt; deb&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="initialising-the-manifests"&gt;Initialising the Manifests&lt;/h2&gt;

&lt;p&gt;As with Terraform, OpenToFu needs to install its support modules before it can run a plan. The command is very familiar:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;tofu init

Initializing the backend...

Initializing provider plugins...
- Finding brightbox/brightbox versions matching &lt;span class="s2"&gt;"~&amp;gt; 3.0"&lt;/span&gt;...
- Installing brightbox/brightbox v3.4.3...
- Installed brightbox/brightbox v3.4.3 &lt;span class="o"&gt;(&lt;/span&gt;signed, key ID 86A01C78D276AC7C&lt;span class="o"&gt;)&lt;/span&gt;

Providers are signed by their developers.
If you&lt;span class="s1"&gt;'d like to know more about provider signing, you can read about it here:
https://opentofu.org/docs/cli/plugins/signing/

OpenTofu has created a lock file .terraform.lock.hcl to record the provider
selections it made above. Include this file in your version control repository
so that OpenTofu can guarantee to make the same selections by default when
you run "tofu init" in the future.

OpenTofu has been successfully initialized!

You may now begin working with OpenTofu. Try running "tofu plan" to see
any changes that are required for your infrastructure. All OpenTofu commands
should now work.

If you ever set or change modules or backend configuration for OpenTofu,
rerun this command to reinitialize your working directory. If you forget, other
commands will detect it and remind you to do so if necessary.&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And that’s it. Swap &lt;code&gt;terraform&lt;/code&gt; for &lt;code&gt;tofu&lt;/code&gt; and you’re good to go. If
you need any further information the OpenTofu website has a &lt;a href="https://opentofu.org/docs/"&gt;good
documentation section&lt;/a&gt;&lt;/p&gt;

</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-01-17:/blog/2024/01/17/solving-dining-philosophers-with-systemd-part-3/</id>
    <title type="html">Solving the Dining Philosophers Problem with systemd - Part 3</title>
    <published>2024-01-17T00:00:00Z</published>
    <updated>2024-01-17T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/01/17/solving-dining-philosophers-with-systemd-part-3/" type="text/html"/>
    <content type="html">&lt;p&gt;This is part 3 of a 3-part series where we solve the &lt;a href="https://en.wikipedia.org/wiki/Dining_philosophers_problem"&gt;Dining
Philosophers&lt;/a&gt;
concurrency problem using just UNIX, systemd and some bash scripting!&lt;/p&gt;

&lt;p&gt;In &lt;a href="/blog/2024/01/11/solving-dining-philosophers-with-systemd-part-2"&gt;part
2&lt;/a&gt;
we created multiple philosophers using separate user ids, got them to
take their seat in the dining room and even pick up their initial forks&lt;sup id="fnref:forks" role="doc-noteref"&gt;&lt;a href="#fn:forks" class="footnote" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;p&gt;Now we need to implement the core part of the dining philosopher’s
problem - the conflict resolution layer - and that means we need an
appropriate algorithm.&lt;/p&gt;

&lt;p&gt;We’ll be using a variant of the Chandry/Misra “hygiene” solution. Our
initial setup of the forks ensures that the graph between the philosopher
nodes and the fork edges is acyclical. Each of the transformations from
now on will maintain that acyclical form, avoiding a deadlock.&lt;/p&gt;

&lt;p&gt;When a philosopher gets hungry (i.e. reaches the systemd hungry target), they
check if they have both forks. If not, they need to ask their neighbouring
philosophers for a fork.&lt;/p&gt;

&lt;p&gt;For each request for a fork they receive, they make a simple decision:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;if they are thinking and they have the fork, they delete the fork and
offer the fork to their neighbour by sending them a message&lt;/li&gt;
  &lt;li&gt;otherwise, they ignore the request&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For each fork offer received from a neighbour, a fork is created. Forks can only be
acquired as a result of these offers and thereby with the permission
of the neighbour. This both maintains the acyclical nature of the fork
graph and avoids starvation (systemically and metaphorically).&lt;/p&gt;

&lt;p&gt;Once a philosopher has two forks they enter the eating state and set a
time in the future when they will start thinking again.&lt;/p&gt;

&lt;p&gt;Before they enter the thinking state, the forks are deleted and they
are transferred to neighbours by sending them a fork offer message.&lt;/p&gt;

&lt;p&gt;Now to implement this in the Unix Programming Environment.&lt;/p&gt;

&lt;h2 id="the-veritable-sendmail-and-mailutils"&gt;The veritable sendmail and mailutils&lt;/h2&gt;

&lt;p&gt;The philosophers will send messages to each other using Unix mail, provided by
the sendmail daemon. This is how users communicated with each other on
multi-user Unix machines back in the good old days before social media.&lt;/p&gt;

&lt;p&gt;The sendmail daemon has a deserved reputation for being a devil to
configure properly. Fortunately, we can use it out of the box because
modern cloud servers are encased in system-level firewalls that prevent
external access.&lt;/p&gt;

&lt;p&gt;We’ll use sendmail’s rather useful ability to pipe received messages into a script.
By placing a mail processor command in the
&lt;code&gt;.forward&lt;/code&gt; file for each philosopher we create a simple, but effective,
event-driven message system.&lt;/p&gt;

&lt;p&gt;The mail processor uses a bit of &lt;code&gt;sed&lt;/code&gt; magic to strip out the subject
and isolate who the messages are from. Then for each pair, it
invokes a mail task and logs any output to the &lt;code&gt;local0&lt;/code&gt; syslog facility
with a ‘mail-tasks’ identifier so we can isolate them later.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;
&lt;span class="nb"&gt;sed&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; &lt;span class="s1"&gt;'/^From: /s/^From: .* &amp;lt;\(.*\)@.*$/\1/p
/^Subject: /s/^Subject: \(.*\)$/"\1"/p'&lt;/span&gt; |
    xargs &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 2 &lt;span class="nt"&gt;--&lt;/span&gt; /usr/local/bin/mail-tasks |
    logger &lt;span class="nt"&gt;-p&lt;/span&gt; local0.info &lt;span class="nt"&gt;-t&lt;/span&gt; mail-tasks&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Sending messages is a simple wrapper around the standard &lt;code&gt;mail&lt;/code&gt; command
that gives us the ‘fork-request’ and ‘fork-response’ messages we
need. First the ‘fork-request’&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# fork-request&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"$#"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; name..."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;64
&lt;span class="k"&gt;fi
for &lt;/span&gt;word
&lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Asking &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for the shared fork"&lt;/span&gt;
    mail &lt;span class="nt"&gt;-E&lt;/span&gt;&lt;span class="s1"&gt;'unset nullbodymsg'&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'Fork Request'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;lt; /dev/null
&lt;span class="k"&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and the ‘fork-response’:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# fork-response&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"$#"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 0 &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; name..."&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;64
&lt;span class="k"&gt;fi
for &lt;/span&gt;word
&lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sending &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; our shared fork"&lt;/span&gt;
    mail &lt;span class="nt"&gt;-E&lt;/span&gt;&lt;span class="s1"&gt;'unset nullbodymsg'&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; &lt;span class="s1"&gt;'Fork Response'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;word&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &amp;lt; /dev/null
&lt;span class="k"&gt;done&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="update-the-eating-service"&gt;Update the eating service&lt;/h2&gt;

&lt;p&gt;The first update we make is to the &lt;code&gt;eating-food&lt;/code&gt; service. When it starts,
we still run the &lt;code&gt;consumption&lt;/code&gt; script but now we want to run the
&lt;code&gt;fork-release&lt;/code&gt; script when the service shuts down. This nicely wraps
the eating timer without introducing any further systemd units.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-systemd code-highlight"&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;Eating Food
&lt;span class="nt"&gt;ConditionPathIsDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/home/share/dining-room
&lt;span class="nt"&gt;PartOf&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;eating.target

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="nt"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;oneshot
&lt;span class="nt"&gt;SyslogFacility&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;local0
&lt;span class="nt"&gt;RemainAfterExit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;yes
&lt;span class="nt"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/usr/local/bin/consumption
&lt;span class="c"&gt;# Send forks to neighbours after eating&lt;/span&gt;
&lt;span class="nt"&gt;ExecStop&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/usr/local/bin/fork-release&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We add the &lt;code&gt;RemainAfterExit&lt;/code&gt; setting to the unit so that it enters the
&lt;code&gt;active&lt;/code&gt; state and will be shut down (running the &lt;code&gt;ExecStop&lt;/code&gt; command)
when the timer triggers the transition to the &lt;code&gt;thinking&lt;/code&gt; target.&lt;/p&gt;

&lt;p&gt;The fork release script is very simple:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;# fork-release&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Releasing forks"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/forks/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Transferring forks"&lt;/span&gt;
neighbours | xargs &lt;span class="nt"&gt;-r&lt;/span&gt; response-fork&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and it neatly cleans up the spare fork that was left over from
the initialisation process, once that philosopher has completed their
first meal.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;neighbours&lt;/code&gt; script finds out who is in the seats on either side
of a philosopher, including from the head to the foot of the table.
It does this using a &lt;code&gt;count&lt;/code&gt; function that determines the number of
seats dynamically within the shell.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;# count fileglob&lt;/span&gt;
count&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
        &lt;span class="se"&gt;\*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;echo &lt;/span&gt;0
            &lt;span class="p"&gt;;;&lt;/span&gt;
        &lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$#&lt;/span&gt;
            &lt;span class="p"&gt;;;&lt;/span&gt;
    &lt;span class="k"&gt;esac&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;my-dining-seat&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;count &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diningroom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/seats/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;max &lt;span class="o"&gt;+&lt;/span&gt; base&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nv"&gt;right_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;seat &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;right_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;right_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;base &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nv"&gt;left_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;seat &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;left_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;left_index&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="the-fork-check"&gt;The fork check&lt;/h2&gt;

&lt;p&gt;The hungry timer is replaced by a &lt;code&gt;fork-check&lt;/code&gt; service. The unit has a
few interesting features:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-systemd code-highlight"&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;Fork Check
&lt;span class="nt"&gt;ConditionPathIsDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/home/share/dining-room
&lt;span class="nt"&gt;CollectMode&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;inactive-or-failed
&lt;span class="nt"&gt;OnSuccess&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;eating.target
&lt;span class="nt"&gt;OnFailure&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;fork-request.service
&lt;span class="nt"&gt;After&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;hungry.target

&lt;span class="k"&gt;[Service]&lt;/span&gt;
&lt;span class="nt"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;oneshot
&lt;span class="nt"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;/usr/local/bin/fork-check
&lt;span class="nt"&gt;SyslogFacility&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;local0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is a branch unit that triggers other processes depending on whether
the check succeeds or fails. We update &lt;code&gt;CollectMode&lt;/code&gt; so systemd garbage
collects the unit even if it has failed.&lt;/p&gt;

&lt;p&gt;When the check succeeds it transitions to the &lt;code&gt;eating&lt;/code&gt; target, and on
failure it requests the forks from neighbours.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;fork-check&lt;/code&gt; looks for two forks in the philosopher’s fork area and
returns a temporary failure if they are not there.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="nv"&gt;diningroom&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/share/dining-room
&lt;span class="nv"&gt;forkarea&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/forks
&lt;span class="nv"&gt;base&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;10

&lt;span class="nv"&gt;lfork&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;my-dining-seat&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;count &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diningroom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;/seats/&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;max &lt;span class="o"&gt;+&lt;/span&gt; base&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="nv"&gt;rfork&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;lfork &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;rfork&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-gt&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;max&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nv"&gt;rfork&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;base &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;fi

if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;forkarea&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;lfork&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt;
   &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;forkarea&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;rfork&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I have two forks"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="c"&gt;# Return EX_TEMPFAIL&lt;/span&gt;
&lt;span class="nb"&gt;exit &lt;/span&gt;75&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The core of &lt;code&gt;fork-request&lt;/code&gt; asks a neighbour for the fork if we don’t have it by sending
them a request message:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;# ask_for_fork fork_file fork_num neighbour_name&lt;/span&gt;
ask_for_fork&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I already have fork #&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I don't have fork #&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        request-fork &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;3&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="processing-the-mail-tasks"&gt;Processing the mail tasks&lt;/h2&gt;

&lt;p&gt;The mail-tasks script uses different branches based on the mail subject
to manage specific types of messages. This allows for easy extension
by assigning the task of handling a particular message to a specific
script. Adding new message types in the future simply involves adding
another branch in the case statement.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"$#"&lt;/span&gt; &lt;span class="nt"&gt;-ne&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Usage: &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;0&lt;/span&gt;&lt;span class="p"&gt;##*/&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; username subject"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;64
&lt;span class="k"&gt;fi

case&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$2&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="k"&gt;in
&lt;/span&gt;Fork&lt;span class="se"&gt;\ &lt;/span&gt;Request&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;exec &lt;/span&gt;mail-task-fork-request &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
Fork&lt;span class="se"&gt;\ &lt;/span&gt;Response&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;exec &lt;/span&gt;mail-task-fork-response &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;;;&lt;/span&gt;
&lt;span class="k"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unknown mail request &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;2&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; from &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;1&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Discarding mail"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;esac&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Each message handler is a self-contained script for each particular
task. A fork request is rejected if the philosopher is hungry or
eating. We ask systemd what state it is in a manner that is as atomic
as possible. If the philosopher is hungry, do a backup check to see if they have two
forks and can start eating:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Received fork request from &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="c"&gt;# Try to be as atomic as possible&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; is-active eating.target hungry.target
&lt;span class="k"&gt;then
    if &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; is-active hungry.target
    &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"while hungry."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I have priority"&lt;/span&gt;
        try_to_eat
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"while eating."&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I'll respond when I've finished eating."&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi
fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;otherwise, we’ll remove the fork and send it to their neighbour, after a
last-second check that the philosopher is still thinking.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;remove_fork&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-w&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forks/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;then&lt;/span&gt;
        &lt;span class="c"&gt;# Final check we're in the right state before removing forks&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; is-active eating.target hungry.target
        &lt;span class="k"&gt;then
            &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Transistioned away from thinking while processing"&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ignoring request"&lt;/span&gt;
            &lt;span class="nb"&gt;exit &lt;/span&gt;0
        &lt;span class="k"&gt;else
            &lt;/span&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forks/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
            &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Released fork #&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="k"&gt;fi
    else
        &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I don't have fork #&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
        &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Ignoring request"&lt;/span&gt;
        &lt;span class="nb"&gt;exit &lt;/span&gt;0
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The fork response is a little more straightforward. First, grab the fork that
has been sent through. Then, if the philosopher is hungry, see if they have both
forks and can start eating:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Received fork response from &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;other&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;left_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;grab_fork &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;elif&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;other&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;right_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;grab_fork &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;right_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;else
    &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Fork Response error"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Unexpected seat &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;other&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; for &lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I'm seat &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, left is &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;left_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;, right is &lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;right_index&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Discarding mail"&lt;/span&gt;
    &lt;span class="nb"&gt;exit &lt;/span&gt;0
&lt;span class="k"&gt;fi
if &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; &lt;span class="nt"&gt;--quiet&lt;/span&gt; is-active hungry.target
&lt;span class="k"&gt;then
        &lt;/span&gt;try_to_eat
&lt;span class="k"&gt;fi
&lt;/span&gt;&lt;span class="nb"&gt;exit &lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="installation"&gt;Installation&lt;/h2&gt;

&lt;p&gt;Now we’re ready to run the full simulation. To try it yourself, install
the code from the repo. Log in as an admin user on an Ubuntu server,
clone the repo and run the setup script&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-2q8eh:~&lt;span class="nv"&gt;$ &lt;/span&gt;git clone &lt;span class="nt"&gt;--depth&lt;/span&gt; 1 &lt;span class="nt"&gt;--branch&lt;/span&gt; part-3 git@github.com:brightbox/systemd-dining.git
Cloning into &lt;span class="s1"&gt;'systemd-dining'&lt;/span&gt;...
...
ubuntu@srv-2q8eh:~&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;systemd-dining/
ubuntu@srv-2q8eh:~/systemd-dining&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./setup.sh
Adding required packages
...
Installing simulation&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="run-the-simulation"&gt;Run the simulation&lt;/h2&gt;

&lt;p&gt;Create the philosophers:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-2q8eh:~/systemd-dining&lt;span class="nv"&gt;$ &lt;/span&gt;xargs &lt;span class="nt"&gt;-L&lt;/span&gt; 1 &lt;span class="nb"&gt;sudo&lt;/span&gt; /usr/local/sbin/create_philosopher &amp;lt; philosophers&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Open the dining room:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-5o1ic:~/systemd-dining&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;open_dining_room&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="stop-the-simulation"&gt;Stop the simulation&lt;/h2&gt;

&lt;p&gt;Close the dining room:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-5o1ic:~/systemd-dining&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;close_dining_room&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Remove the philosophers:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-2q8eh:~/systemd-dining&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;awk&lt;/span&gt; &lt;span class="s1"&gt;'{print $1}'&lt;/span&gt; philosophers | xargs &lt;span class="nb"&gt;sudo&lt;/span&gt; /usr/local/sbin/remove_philosopher&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="interrogate-the-simulation"&gt;Interrogate the simulation&lt;/h2&gt;

&lt;p&gt;While running, all of the Unix process tools are available. Of
particular use is the &lt;code&gt;journalctl&lt;/code&gt; command and the convenience script
&lt;code&gt;npcjournal&lt;/code&gt; which allows you to look at a single philosopher by user
name.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-4mujm:~&lt;span class="nv"&gt;$ &lt;/span&gt;npcjournal kant
Nov 27 18:49:31 fork-release[1531112]: Sending nietzsche our shared fork
Nov 27 18:49:31 contemplation[1531145]: Currently contemplating the perspective that &lt;span class="nb"&gt;time &lt;/span&gt;and space are not
Nov 27 18:49:31 contemplation[1531145]: external conditions but subjective forms of our intuition.
Nov 27 18:49:46 hunger[1531542]: Getting hungry...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or follow their activity:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-4mujm:~&lt;span class="nv"&gt;$ &lt;/span&gt;npcjournal &lt;span class="nt"&gt;-f&lt;/span&gt; hegel
Nov 29 12:07:41 consumption[802905]: Eating...
Nov 29 12:07:49 mail-tasks[803067]: Received fork request from socrates
Nov 29 12:07:49 mail-tasks[803067]: &lt;span class="k"&gt;while &lt;/span&gt;eating.
Nov 29 12:07:49 mail-tasks[803067]: I&lt;span class="s1"&gt;'ll respond when I'&lt;/span&gt;ve finished eating.
Nov 29 12:07:52 fork-release[803154]: Releasing forks
Nov 29 12:07:52 fork-release[803154]: Transferring forks
Nov 29 12:07:52 fork-release[803163]: Sending socrates our shared fork
Nov 29 12:07:53 fork-release[803163]: Sending mill our shared fork&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Perhaps you want to see what a philosopher has been thinking about:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-4mujm:~&lt;span class="nv"&gt;$ &lt;/span&gt;npcjournal &lt;span class="nt"&gt;--since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;today &lt;span class="nt"&gt;--identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;contemplation wittgenstein
Nov 29 00:01:14 contemplation[3981849]: Currently contemplating the view that philosophy is not a theory but
Nov 29 00:01:14 contemplation[3981849]: an activity, an approach to language that transforms our conceptual
Nov 29 00:01:14 contemplation[3981849]: framework.
Nov 29 00:02:11 contemplation[3983193]: Currently contemplating the belief that philosophy should be concerned
Nov 29 00:02:11 contemplation[3983193]: with clarifying language rather than solving metaphysical puzzles.
Nov 29 00:03:08 contemplation[3984372]: Currently contemplating the notion that the structure of language reflects
Nov 29 00:03:08 contemplation[3984372]: the structure of reality, and philosophical problems are linguistic
Nov 29 00:03:08 contemplation[3984372]: &lt;span class="k"&gt;in &lt;/span&gt;nature.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or maybe what messages have been sent by the mail system between the philosophers:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-4mujm:~&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;--since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;today &lt;span class="nt"&gt;--facility&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;mail
Nov 29 00:00:03 srv-4mujm sendmail[3980626]: 3AT003YC3980626: &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plato@srv-4mujm.gb1.brightbox.com, &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;113, &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
Nov 29 00:00:03 srv-4mujm sm-mta[3980627]: 3AT003KE3980627: &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;plato@srv-4mujm.gb1.brightbox.com&amp;gt;, &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;399, &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
Nov 29 00:00:03 srv-4mujm sendmail[3980626]: 3AT003YC3980626: &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;schlegel, &lt;span class="nv"&gt;ctladdr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plato@srv-4mujm.gb1.brightbox&amp;gt;
Nov 29 00:00:03 srv-4mujm sendmail[3980635]: 3AT003BX3980635: &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plato@srv-4mujm.gb1.brightbox.com, &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;113, &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
Nov 29 00:00:03 srv-4mujm sm-mta[3980628]: 3AT003KE3980627: &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;|/usr/local/bin/mail-processor, &lt;span class="nv"&gt;ctladdr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;schlegel&amp;gt;
Nov 29 00:00:03 srv-4mujm sm-mta[3980646]: 3AT003W33980646: &lt;span class="nv"&gt;from&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;plato@srv-4mujm.gb1.brightbox.com&amp;gt;, &lt;span class="nv"&gt;size&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;399, &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
Nov 29 00:00:03 srv-4mujm sendmail[3980635]: 3AT003BX3980635: &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;socrates, &lt;span class="nv"&gt;ctladdr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;plato@srv-4mujm.gb1.brightbox&amp;gt;
Nov 29 00:00:03 srv-4mujm sm-mta[3980649]: 3AT003W33980646: &lt;span class="nv"&gt;to&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;|/usr/local/bin/mail-processor, &lt;span class="nv"&gt;ctladdr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&amp;lt;socrates&amp;gt;o&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And if you can’t remember their name, we can look that up:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-4mujm:~&lt;span class="nv"&gt;$ &lt;/span&gt;finger &lt;span class="nt"&gt;-p&lt;/span&gt; wittgenstein
Login: wittgenstein   			Name: Ludwig Wittgenstein
Directory: /home/wittgenstein       	Shell: /bin/bash
Last login Sun Nov 26 08:12 &lt;span class="o"&gt;(&lt;/span&gt;UTC&lt;span class="o"&gt;)&lt;/span&gt; on pts/2
Mail forwarded to |/usr/local/bin/mail-processor
No mail.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Gathering the statistics from the journal for how long each philosopher
stays hungry is left as an exercise for the reader. Let me know how you
get on.&lt;/p&gt;

&lt;h2 id="epilogue"&gt;Epilogue&lt;/h2&gt;

&lt;p&gt;And there we have it, a short run around some of the older, esoteric
features of the Unix Programming Environment combined with some of the
newer systemd features. With these tools, we have built a Dining Philosophers
solution using little more than a few shell scripts. Not a compiler
in sight.&lt;/p&gt;

&lt;p&gt;If you come up with any potential improvements to the system, please
drop an issue or a pull request on the repo. I’m sure it can be improved
in many ways.&lt;/p&gt;

&lt;div class="footnotes" role="doc-endnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:forks" role="doc-endnote"&gt;
      &lt;p&gt;Again, we mean the eating utensils, not child processes. &lt;a href="#fnref:forks" class="reversefootnote" role="doc-backlink"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-01-11:/blog/2024/01/11/solving-dining-philosophers-with-systemd-part-2/</id>
    <title type="html">Solving the Dining Philosophers Problem with systemd - Part 2</title>
    <published>2024-01-11T00:00:00Z</published>
    <updated>2024-01-11T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/01/11/solving-dining-philosophers-with-systemd-part-2/" type="text/html"/>
    <content type="html">&lt;p&gt;This is part 2 of a 3-part series where we solve the &lt;a href="https://en.wikipedia.org/wiki/Dining_philosophers_problem"&gt;Dining
Philosophers&lt;/a&gt;
concurrency problem using just UNIX, systemd and some bash scripting!&lt;/p&gt;

&lt;p&gt;In &lt;a href="/blog/2024/01/10/solving-dining-philosophers-with-systemd-part-1/"&gt;part 1&lt;/a&gt;,
we created a simple state machine with systemd units and watched it
do its thing by interrogating the journal. For part 2, we’ll use the
multi-user capabilities of Linux and create a set of philosophers running
independent processes.&lt;/p&gt;

&lt;p&gt;Every process in Linux runs with a user identifier, plus a primary group
and a list of secondary groups the user identifier is part of. Access
control to resources within Linux is managed via this set of identifiers.&lt;/p&gt;

&lt;p&gt;Creating a philosopher is a matter of adding a user ID, a group ID,
and adding the user ID to a secondary group, &lt;code&gt;philosophers&lt;/code&gt;, which we’ll
use to control access to shared resources like dining seats and forks&lt;sup id="fnref:forks" role="doc-noteref"&gt;&lt;a href="#fn:forks" class="footnote" rel="footnote"&gt;1&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;addgroup philosophers
&lt;span class="nv"&gt;$ &lt;/span&gt;adduser &lt;span class="nt"&gt;--disabled-password&lt;/span&gt; &lt;span class="nt"&gt;--shell&lt;/span&gt; /bin/bash &lt;span class="nt"&gt;--gecos&lt;/span&gt; &lt;span class="s2"&gt;"Immanuel Kant"&lt;/span&gt; kant 
Adding user &lt;span class="sb"&gt;`&lt;/span&gt;kant&lt;span class="s1"&gt;' ...
Adding new group `kant'&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;1002&lt;span class="o"&gt;)&lt;/span&gt; ...
Adding new user &lt;span class="sb"&gt;`&lt;/span&gt;kant&lt;span class="s1"&gt;' (1001) with group `kant'&lt;/span&gt; ...
Creating home directory &lt;span class="sb"&gt;`&lt;/span&gt;/home/kant&lt;span class="s1"&gt;' ...
Copying files from `/etc/skel'&lt;/span&gt; ..
&lt;span class="nv"&gt;$ &lt;/span&gt;adduser kant philosophers
Adding user &lt;span class="sb"&gt;`&lt;/span&gt;kant&lt;span class="s1"&gt;' to group `philosophers'&lt;/span&gt; ...
Adding user kant to group philosophers
Done.
&lt;span class="nv"&gt;$ &lt;/span&gt;loginctl enable-linger kant&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can see the philosopher’s details using the finger command:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;finger kant
Login: kant           			Name: Immanuel Kant
Directory: /home/kant               	Shell: /bin/bash
Never logged &lt;span class="k"&gt;in&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
No mail.
No Plan.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now let’s add the units we created in part 1 to &lt;code&gt;/etc/systemd/user&lt;/code&gt;, so they are
available to everybody, and then we can start the new user thinking&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;machinectl shell kant@ /usr/bin/systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; start thinking.target
Connected to the &lt;span class="nb"&gt;local &lt;/span&gt;host. Press ^] three &lt;span class="nb"&gt;times &lt;/span&gt;within 1s to &lt;span class="nb"&gt;exit &lt;/span&gt;session.

Connection to the &lt;span class="nb"&gt;local &lt;/span&gt;host terminated.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;With the system running, we can look at what the philosopher has done by
interrogating the journal.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nv"&gt;_UID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;id&lt;/span&gt; &lt;span class="nt"&gt;-u&lt;/span&gt; kant&lt;span class="si"&gt;)&lt;/span&gt;
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Queued start job &lt;span class="k"&gt;for &lt;/span&gt;default target Main User Target.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Created slice User Application Slice.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Reached target Paths.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Reached target Timers.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Starting D-Bus User Message Bus Socket...
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on GnuPG network certificate management daemon.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on GnuPG cryptographic agent and passphrase cache &lt;span class="o"&gt;(&lt;/span&gt;access &lt;span class="k"&gt;for &lt;/span&gt;web browsers&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on GnuPG cryptographic agent and passphrase cache &lt;span class="o"&gt;(&lt;/span&gt;restricted&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on GnuPG cryptographic agent &lt;span class="o"&gt;(&lt;/span&gt;ssh-agent emulation&lt;span class="o"&gt;)&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on GnuPG cryptographic agent and passphrase cache.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on debconf communication socket.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on REST API socket &lt;span class="k"&gt;for &lt;/span&gt;snapd user session agent.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Listening on D-Bus User Message Bus Socket.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Reached target Sockets.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Reached target Basic System.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Reached target Main User Target.
Nov 24 07:09:21 srv-hy7z5 systemd[2995]: Startup finished &lt;span class="k"&gt;in &lt;/span&gt;142ms.
Nov 24 07:10:07 srv-hy7z5 systemd[2995]: Started Thinking Period.
Nov 24 07:10:07 srv-hy7z5 systemd[2995]: Starting Contemplation...
Nov 24 07:10:07 srv-hy7z5 contemplation[3018]: Currently contemplating the enigmatic nature of reality: the existential
Nov 24 07:10:07 srv-hy7z5 contemplation[3018]: grasp &lt;span class="k"&gt;for &lt;/span&gt;an elusive fork, the categorical imperative of simulated
Nov 24 07:10:07 srv-hy7z5 contemplation[3018]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 24 07:10:07 srv-hy7z5 contemplation[3018]: object.
Nov 24 07:10:07 srv-hy7z5 systemd[2995]: Finished Contemplation.
Nov 24 07:10:07 srv-hy7z5 systemd[2995]: Reached target Thinking....&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To stop the process, we can just get rid of the user&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;loginctl disable-linger kant
&lt;span class="nv"&gt;$ &lt;/span&gt;deluser kant philosophers
Removing user &lt;span class="sb"&gt;`&lt;/span&gt;kant&lt;span class="s1"&gt;' from group `philosophers'&lt;/span&gt; ...
Done.
&lt;span class="nv"&gt;$ &lt;/span&gt;deluser &lt;span class="nt"&gt;--remove-home&lt;/span&gt; kant
Looking &lt;span class="k"&gt;for &lt;/span&gt;files to backup/remove ...
Removing user &lt;span class="sb"&gt;`&lt;/span&gt;kant&lt;span class="s1"&gt;' ...
Warning: group `kant'&lt;/span&gt; has no more members.
Done.&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="customising-the-philosophers"&gt;Customising the philosophers&lt;/h2&gt;

&lt;p&gt;Of course, we don’t want to create philosophers one at a time and manually
start each one. We want the computer to take care of that. Plus
we want them to think about different things. Otherwise,
what’s the point of each of them having a unique identity?&lt;/p&gt;

&lt;p&gt;To give them some individuality, we can use the &lt;code&gt;.project&lt;/code&gt; file
in their home directories and store the things we want the philosopher
to think about there. Then we alter the contemplation script to select
a random line from &lt;code&gt;.project&lt;/code&gt; with the &lt;code&gt;shuf&lt;/code&gt; command.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;# /usr/local/bin/contemplation&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;default_thought&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"the enigmatic nature of reality: the existential grasp
for an elusive fork, the categorical imperative of simulated existence,
and the irony in questioning the true essence of the held object."&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s1"&gt;'Currently contemplating '&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.project"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;then
        &lt;/span&gt;&lt;span class="nb"&gt;shuf&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.project"&lt;/span&gt;
    &lt;span class="k"&gt;else
        &lt;/span&gt;&lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;default_thought&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="k"&gt;fi&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; | &lt;span class="nb"&gt;fmt
exit &lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="starting-the-simulation"&gt;Starting the simulation&lt;/h2&gt;

&lt;p&gt;Now we’re ready to create multiple philosophers each with a list of
things to think about. To start the simulation, we need them to take
their seat at the dining table and prepare to do battle over the forks
when they get hungry.&lt;/p&gt;

&lt;p&gt;We can signal that the simulation should start by creating a &lt;code&gt;dining-room&lt;/code&gt;
directory in a shared area. Once systemd sees this directory, the
philosopher can run a script to select their seat.&lt;/p&gt;

&lt;p&gt;Selecting seats is straightforward: we set the &lt;code&gt;setgid&lt;/code&gt; bit on a
&lt;code&gt;seat&lt;/code&gt; directory in &lt;code&gt;dining-room&lt;/code&gt; and make it writable by the group
&lt;code&gt;philosophers&lt;/code&gt;. This stops philosophers from overwriting each other’s
files. As each philosopher accesses the dining room, they take the next
seat available by trying to create a file in the shared area with the
seat number and incrementing until they succeed.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"The Dining Room is open. Looking for a seat."&lt;/span&gt;
&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;head&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;until &lt;/span&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diningroom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/seats/&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; 2&amp;gt;/dev/null
&lt;span class="k"&gt;do
    &lt;/span&gt;&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;$((&lt;/span&gt;seat+1&lt;span class="k"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;done
&lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"I have seat #&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Forks are allocated relative to the seat obtained. Each philosopher
creates a file to represent the fork on their right-hand side, which is numbered
one more than their seat number. The forks are created and maintained
in the philosopher’s home directory.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;grab_fork&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;touch&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forks/&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
    &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Acquired fork #&lt;/span&gt;&lt;span class="nv"&gt;$1&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
...
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating my fork store"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forks"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;HOME&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/forks"&lt;/span&gt;
grab_fork &lt;span class="k"&gt;$((&lt;/span&gt;seat+1&lt;span class="k"&gt;))&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;The head of the table, the philosopher in the lowest seat position, also
gets the fork on their left-hand side.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;seat&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;head&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;grab_fork &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;head&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Before starting, the philosopher waits until all others have taken their seats.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Waiting for everybody to take their seats"&lt;/span&gt;
fsnotifywait &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; close_write &lt;span class="nt"&gt;-t&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;WAIT_FOR_READY_SECS&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;diningroom&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/seats"&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$?&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="nt"&gt;-eq&lt;/span&gt; 2 &lt;span class="o"&gt;]&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Starting..."&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Those still awake at this point in the piece will have noticed that the
philosopher at the foot of the table appears to have a fork that cannot be
used or acquired by anybody else. The nature of this fork and whether
it exists if it cannot be acquired is precisely what the philosophers
contemplate when they are not thinking about something else. We deal with 
it in part 3.&lt;/p&gt;

&lt;p&gt;A path systemd unit and the associated service unit allow us to start
this selection process when the dining room is available.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ini code-highlight"&gt;&lt;span class="c"&gt;# select-seat.path
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Watch Dining Room&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nn"&gt;[Path]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;PathModified&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/share/dining-room&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nn"&gt;[Install]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And the service:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ini code-highlight"&gt;&lt;span class="c"&gt;# select-seat.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Seat Selection&lt;/span&gt;
&lt;span class="py"&gt;ConditionPathIsDirectory&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/home/share/dining-room&lt;/span&gt;
&lt;span class="py"&gt;Conflicts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;hungry.target eating.target&lt;/span&gt;
&lt;span class="py"&gt;OnSuccess&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;thinking.target&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nn"&gt;[Service]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/select-seat&lt;/span&gt;
&lt;span class="py"&gt;SyslogFacility&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;local0&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nn"&gt;[Install]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;WantedBy&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;default.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Here, we use an &lt;code&gt;OnSuccess&lt;/code&gt; entry to trigger the start of the simulation by
starting the thinking target after the select seat service has completed,
and a &lt;code&gt;Condition&lt;/code&gt; on the service to make sure it doesn’t try to start if
the dining room is missing.&lt;/p&gt;

&lt;p&gt;The path entry stays in place continuously so that it triggers whenever
the dining room is created.&lt;/p&gt;

&lt;p&gt;To get the user-level systemd to activate these units automatically, we need to
tie it into the default target by having the relevant symbolic links in
the systemd area. If we add these to the home directory skeleton then each
new user will get those links straight away and systemd will read them.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /etc/skel/.config/systemd/user/default.target.wants
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo ln&lt;/span&gt; &lt;span class="nt"&gt;-sf&lt;/span&gt; /etc/systemd/user/select-seat.path &lt;span class="se"&gt;\&lt;/span&gt;
    /etc/systemd/user/select-seat.service &lt;span class="se"&gt;\&lt;/span&gt;
    /etc/skel/.config/systemd/user/default.target.wants&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This is essentially what &lt;code&gt;systemctl --user enable &amp;lt;unit&amp;gt;&lt;/code&gt; usually does.&lt;/p&gt;

&lt;h2 id="create-the-users-and-off-we-go"&gt;Create the users and off we go&lt;/h2&gt;

&lt;p&gt;Now we’re ready to run the simulation.&lt;/p&gt;

&lt;p&gt;First, we need a &lt;code&gt;philosophers&lt;/code&gt; file containing the details of the users&lt;sup id="fnref:GECOS" role="doc-noteref"&gt;&lt;a href="#fn:GECOS" class="footnote" rel="footnote"&gt;2&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;kant "Immanuel Kant" "a real pissant" projects/kant
heidegger "Martin Heidegger" "boozy beggar" projects/heidegger
hegel "Wilhelm Friedrich Hegel" "lightweight" projects/hegel
hume "David Hume" "out-consumer" projects/hume
schlegel "Friedrich Schlegel" "schloshed" projects/schlegel
wittgenstein "Ludwig Wittgenstein" "beery swine" projects/wittgenstein
nietzsche "Friedrich Nietzsche" "teacher of the raising of the wrist" projects/nietzsche
socrates "Socrates" "permanently pissed" projects/socrates
mill "John Stuart Mill" "on half a pint of shandy was particularly ill" projects/mill
plato "Plato" "half a crate of whisky every day" projects/plato
aristotle "Aristotle" "bugger for the bottle" projects/aristotle
hobbes "Thomas Hobbes" "fond of his dram" projects/hobbes
descartes "Rene Descartes" "drunken fart" projects/descartes
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;along with all the setup scripts, project files and systemd
units. Fortunately, I’ve done that for you and you can just clone the
git repo onto a new Ubuntu LTS server:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git clone git@github.com:brightbox/systemd-dining.git&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and switch to the part 2 tag:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;git switch part-2&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;then run the setup to install the tools:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;system-dining
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; ./setup.sh&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Once the files are in place we can create the philiosophers:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;xargs &lt;span class="nt"&gt;-L&lt;/span&gt; 1 &lt;span class="nb"&gt;sudo&lt;/span&gt; /usr/local/sbin/create_philosopher &amp;lt; philosophers&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;and then open the dining room to start the simulation:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;open_dining_room&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then we can sit back and watch the simulation unfold by tailing the
journal:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt;
Nov 24 12:13:43 srv-hy7z5 systemd[5124]: Finished Seat Selection.
Nov 24 12:13:43 srv-hy7z5 systemd[4635]: Finished Seat Selection.
Nov 24 12:13:43 srv-hy7z5 systemd[5124]: &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="nt"&gt;-seat&lt;/span&gt;.service: Triggering &lt;span class="nv"&gt;OnSuccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; dependencies.
Nov 24 12:13:43 srv-hy7z5 systemd[4635]: &lt;span class="k"&gt;select&lt;/span&gt;&lt;span class="nt"&gt;-seat&lt;/span&gt;.service: Triggering &lt;span class="nv"&gt;OnSuccess&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; dependencies.
Nov 24 12:13:43 srv-hy7z5 systemd[4635]: Started Thinking Period.
Nov 24 12:13:43 srv-hy7z5 contemplation[5313]: Currently contemplating the belief that the value of actions is determined
Nov 24 12:13:43 srv-hy7z5 contemplation[5313]: by their contribution to individual self-interest and preservation.
Nov 24 12:13:43 srv-hy7z5 systemd[5124]: Started Thinking Period.
Nov 24 12:13:43 srv-hy7z5 systemd[5058]: Finished Contemplation.
Nov 24 12:13:43 srv-hy7z5 systemd[5058]: Reached target Thinking....
Nov 24 12:13:43 srv-hy7z5 systemd[4635]: Starting Contemplation...
Nov 24 12:13:43 srv-hy7z5 systemd[5124]: Starting Contemplation...
Nov 24 12:13:43 srv-hy7z5 contemplation[5317]: Currently contemplating the understanding that what can be said at all
Nov 24 12:13:43 srv-hy7z5 contemplation[5317]: can be said clearly, and what we cannot talk about, we must pass over
Nov 24 12:13:43 srv-hy7z5 contemplation[5317]: &lt;span class="k"&gt;in &lt;/span&gt;silence.
Nov 24 12:13:43 srv-hy7z5 systemd[4635]: Finished Contemplation.
Nov 24 12:13:43 srv-hy7z5 systemd[4635]: Reached target Thinking....
Nov 24 12:13:43 srv-hy7z5 contemplation[5319]: Currently contemplating the certainty that I exist as a thinking being:
Nov 24 12:13:43 srv-hy7z5 contemplation[5319]: &lt;span class="s1"&gt;'Cogito, ergo sum'&lt;/span&gt; - I think, therefore I am.
Nov 24 12:13:43 srv-hy7z5 systemd[5124]: Finished Contemplation.
Nov 24 12:13:43 srv-hy7z5 systemd[5124]: Reached target Thinking....&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;If we just want to look at a particular philosopher there is a
convenience script for that:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;npcjournal &lt;span class="nt"&gt;-f&lt;/span&gt; kant
Nov 24 12:13:41 contemplation[5309]: Currently contemplating the moral imperative that demands action based
Nov 24 12:13:41 contemplation[5309]: on universal principles, irrespective of personal desires or consequences.
Nov 24 12:14:21 hunger[5339]: Getting hungry...
Nov 24 12:14:22 consumption[5341]: Eating...
Nov 24 12:14:28 contemplation[5350]: Currently contemplating how the mind shapes our experience, moulding
Nov 24 12:14:28 contemplation[5350]: reality as we perceive it.
Nov 24 12:14:59 hunger[5387]: Getting hungry...
Nov 24 12:15:02 consumption[5400]: Eating...
Nov 24 12:15:26 contemplation[5432]: Currently contemplating the perspective that &lt;span class="nb"&gt;time &lt;/span&gt;and space are not
Nov 24 12:15:26 contemplation[5432]: external conditions but subjective forms of our intuition.&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;In this part, we’ve looked at the multi-user capability of Linux and
how we can use it to model multiple philosophers, each isolated from one
another in their own process space.&lt;/p&gt;

&lt;p&gt;We’ve created a shared area they all can access and used standard
filesystem tools to allow them all to access that shared space fairly.&lt;/p&gt;

&lt;p&gt;We’ve used the journal to see the entire simulation in action and
filtered it down just to look at what an individual philosopher is doing.&lt;/p&gt;

&lt;p&gt;Next, we need the philosophers to obtain the right number of forks
to eat with, which will require the philosophers to talk to each
other and negotiate. We’ll cover how to do that in Part 3.&lt;/p&gt;

&lt;div class="footnotes" role="doc-endnotes"&gt;
  &lt;ol&gt;
    &lt;li id="fn:forks" role="doc-endnote"&gt;
      &lt;p&gt;The eating utensils, not child processes. &lt;a href="#fnref:forks" class="reversefootnote" role="doc-backlink"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
    &lt;li id="fn:GECOS" role="doc-endnote"&gt;
      &lt;p&gt;The GECOS comment is, of course, their reputation after a particularly heavy session of ‘drinking philosophers’, a multi-threaded Java implementation which resulted in a particularly gnarly stack trace that even the combined brains of 13 great philosophers couldn’t interpret. &lt;a href="#fnref:GECOS" class="reversefootnote" role="doc-backlink"&gt;↩&lt;/a&gt;&lt;/p&gt;
    &lt;/li&gt;
  &lt;/ol&gt;
&lt;/div&gt;
</content>
  </entry>
  <entry>
    <id>tag:www.brightbox.com,2024-01-10:/blog/2024/01/10/solving-dining-philosophers-with-systemd-part-1/</id>
    <title type="html">Solving the Dining Philosophers Problem with systemd - Part 1</title>
    <published>2024-01-10T00:00:00Z</published>
    <updated>2024-01-10T00:00:00Z</updated>
    <link rel="alternate" href="https://www.brightbox.com/blog/2024/01/10/solving-dining-philosophers-with-systemd-part-1/" type="text/html"/>
    <content type="html">&lt;p&gt;Over the years, developers seem to have forgotten that Unix is
a Programming Environment and not just something you must install
to access your web server. Unix is a powerful multi-user system with
numerous tools that have stood the test of time and, with the advent of
systemd, we have a new tool that can easily model state machines. Now,
we can address a new class of problems using Unix alone.&lt;/p&gt;

&lt;p&gt;In this blog post series, we’ll solve the classic &lt;a href="https://en.wikipedia.org/wiki/Dining_philosophers_problem"&gt;“Dining
Philosophers”&lt;/a&gt;
concurrency problem with some of the forgotten mechanisms and tools within the
Unix Programming Environment, some advanced capabilities of systemd and a
sprinkling of shell scripts to glue it together - all without a single deadlock.&lt;/p&gt;

&lt;p&gt;Let’s start by building a simple state machine that moves between the &lt;code&gt;Thinking&lt;/code&gt;,
&lt;code&gt;Hungry&lt;/code&gt;, and &lt;code&gt;Eating&lt;/code&gt; states using the user-level systemd facilities.&lt;/p&gt;

&lt;h2 id="create-the-server"&gt;Create the server&lt;/h2&gt;

&lt;p&gt;In these examples, we’ll be using Ubuntu on Brightbox, but any modern distro
running systemd running anywhere should work.&lt;/p&gt;

&lt;p&gt;Create a server and log in with the default &lt;code&gt;ubuntu&lt;/code&gt; user:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;ssh ubuntu@public.srv-hgy08.gb1.brightbox.com
Welcome to Ubuntu 23.10 &lt;span class="o"&gt;(&lt;/span&gt;GNU/Linux 6.5.0-10-generic x86_64&lt;span class="o"&gt;)&lt;/span&gt;
...&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;When you log in, systemd automatically starts a user instance to manage
user-level units. You can view this via systemctl:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;ubuntu@srv-hgy08:~&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; status
● srv-hgy08
    State: running
    Units: 171 loaded &lt;span class="o"&gt;(&lt;/span&gt;incl. loaded aliases&lt;span class="o"&gt;)&lt;/span&gt;
     Jobs: 0 queued
   Failed: 0 units
    Since: Mon 2023-11-20 09:41:56 UTC&lt;span class="p"&gt;;&lt;/span&gt; 1min 19s ago
  systemd: 253.5-1ubuntu6
   CGroup: /user.slice/user-1000.slice/user@1000.service
           └─init.scope
             ├─857 /lib/systemd/systemd &lt;span class="nt"&gt;--user&lt;/span&gt;
             └─858 &lt;span class="s2"&gt;"(sd-pam)"&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;h2 id="create-the-states"&gt;Create the states&lt;/h2&gt;

&lt;p&gt;We can model the three states of the dining philosophers problem using
systemd target units. We can make the targets mutually exclusive by
stating that they conflict with each other:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ini code-highlight"&gt;&lt;span class="c"&gt;# thinking.target
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Thinking...&lt;/span&gt;
&lt;span class="py"&gt;Conflicts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;eating.target hungry.target&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;This means only one of the targets can remain active at any point in time. For
a target to transition to another, we need a timer unit.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ini code-highlight"&gt;&lt;span class="c"&gt;# thinking.timer
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Thinking Period&lt;/span&gt;
&lt;span class="py"&gt;PartOf&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;thinking.target&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nn"&gt;[Timer]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Unit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;hungry.target&lt;/span&gt;
&lt;span class="py"&gt;RandomizedDelaySec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;60&lt;/span&gt;
&lt;span class="py"&gt;OnActiveSec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;0&lt;/span&gt;
&lt;span class="py"&gt;AccuracySec&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;1us&lt;/span&gt;
&lt;span class="py"&gt;RemainAfterElapse&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;false&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We make the timer unit part of the target so that it stops at the same
time as the target. This combination of &lt;code&gt;RandomizedDelay&lt;/code&gt;, &lt;code&gt;OnActive&lt;/code&gt;,
and &lt;code&gt;Accuracy&lt;/code&gt; distributes the transition time from 0 to 60 seconds.&lt;/p&gt;

&lt;p&gt;We want to know what the philosopher is thinking about, which we can simulate by
running a service to call a script.&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ini code-highlight"&gt;&lt;span class="c"&gt;# contemplation.service
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Contemplation&lt;/span&gt;
&lt;span class="py"&gt;PartOf&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;thinking.target&lt;/span&gt;
&lt;span class="py"&gt;Before&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;hungry.target&lt;/span&gt;
&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nn"&gt;[Service]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;oneshot&lt;/span&gt;
&lt;span class="py"&gt;SyslogFacility&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;local0&lt;/span&gt;
&lt;span class="py"&gt;ExecStart&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;/usr/local/bin/contemplation&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We log the script’s output to a different syslog facility, which allows us
to find them easily in the log later. A &lt;code&gt;Before&lt;/code&gt; restriction ensures that the
philosopher logs their thoughts before dealing with their hunger.&lt;/p&gt;

&lt;p&gt;Create the contemplation script to output an appropriate thought:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-bash code-highlight"&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="c"&gt;# /usr/local/bin/contemplation&lt;/span&gt;
&lt;span class="nb"&gt;set&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt;

&lt;span class="nv"&gt;default_thought&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"the enigmatic nature of reality:
the existential grasp for an elusive fork, the categorical imperative
of simulated existence, and the irony in questioning the true essence
of the held object."&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s'&lt;/span&gt; &lt;span class="s1"&gt;'Currently contemplating '&lt;/span&gt;
    &lt;span class="nb"&gt;printf&lt;/span&gt; &lt;span class="s1"&gt;'%s\n'&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;default_thought&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt; | &lt;span class="nb"&gt;fmt
exit &lt;/span&gt;0&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Now, we update the target to pull in these new units:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-ini code-highlight"&gt;&lt;span class="c"&gt;# thinking.target
&lt;/span&gt;&lt;span class="nn"&gt;[Unit]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="py"&gt;Description&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;Thinking...&lt;/span&gt;
&lt;span class="py"&gt;Conflicts&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;eating.target hungry.target&lt;/span&gt;
&lt;span class="py"&gt;Wants&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;thinking.timer contemplation.service&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;And repeat the process for the eating and hungry targets.&lt;/p&gt;

&lt;h2 id="running-the-state-machine"&gt;Running the state machine&lt;/h2&gt;

&lt;p&gt;To start the simulation, put the files in &lt;code&gt;$HOME/.config/systemd/user&lt;/code&gt;
and activate the &lt;code&gt;thinking&lt;/code&gt; target:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; start thinking.target&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Look in the journal to see the simulation in progress:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt;
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Started thinking.timer - Thinking Period.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Stopped target eating.target - Eating....
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Stopped eating.timer - Eating Period.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Starting contemplation.service - Contemplation...
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: grasp &lt;span class="k"&gt;for &lt;/span&gt;an elusive fork, the categorical imperative of simulated
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 20 10:49:15 srv-hgy08 contemplation[2163]: object.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Finished contemplation.service - Contemplation.
Nov 20 10:49:15 srv-hgy08 systemd[1127]: Reached target thinking.target - Thinking....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Started hungry.timer - Hungry Period.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped target thinking.target - Thinking....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped thinking.timer - Thinking Period.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Starting hunger.service - Hunger...
Nov 20 10:49:17 srv-hgy08 hunger[2165]: Getting hungry...
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Finished hunger.service - Hunger.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Reached target hungry.target - Hungry....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Started eating.timer - Eating Period.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped target hungry.target - Hungry....
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Stopped hungry.timer - Hungry Period.
Nov 20 10:49:17 srv-hgy08 consumption[2166]: Eating...
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Starting eating-food.service - Eating Food...
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Finished eating-food.service - Eating Food.
Nov 20 10:49:17 srv-hgy08 systemd[1127]: Reached target eating.target - Eating....&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;We can filter out the state noise by looking just for the output from our
scripts:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;--facility&lt;/span&gt; local0
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: grasp &lt;span class="k"&gt;for &lt;/span&gt;an elusive fork, the categorical imperative of simulated
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 20 10:50:44 srv-hgy08 contemplation[2176]: object.
Nov 20 10:51:14 srv-hgy08 hunger[2177]: Getting hungry...
Nov 20 10:51:15 srv-hgy08 consumption[2178]: Eating...
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: grasp &lt;span class="k"&gt;for &lt;/span&gt;an elusive fork, the categorical imperative of simulated
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: object.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Or, if we want to see just one particular script, we can filter by
identifier:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;journalctl &lt;span class="nt"&gt;-f&lt;/span&gt; &lt;span class="nt"&gt;--identifier&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;contemplation
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 20 10:51:18 srv-hgy08 contemplation[2181]: object.
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: grasp &lt;span class="k"&gt;for &lt;/span&gt;an elusive fork, the categorical imperative of simulated
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 20 10:52:11 srv-hgy08 contemplation[2192]: object.
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: Currently contemplating the enigmatic nature of reality: the existential
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: grasp &lt;span class="k"&gt;for &lt;/span&gt;an elusive fork, the categorical imperative of simulated
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: existence, and the irony &lt;span class="k"&gt;in &lt;/span&gt;questioning the &lt;span class="nb"&gt;true &lt;/span&gt;essence of the held
Nov 20 10:52:37 srv-hgy08 contemplation[2197]: object.&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;To stop the simulation, stop the targets:&lt;/p&gt;

&lt;pre&gt;&lt;code class="language-shell code-highlight"&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;systemctl &lt;span class="nt"&gt;--user&lt;/span&gt; stop thinking.target eating.target hungry.target&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;or log out.&lt;/p&gt;

&lt;h2 id="summary"&gt;Summary&lt;/h2&gt;

&lt;p&gt;In this blog, we’ve created a simple state machine with systemd that moves
neatly between states over time and logs its activity in the journal. It’s
hardly a fully modelled philosopher, but it’s a good start.&lt;/p&gt;

&lt;p&gt;In the next part, we’ll take this simple model and expand it to cover
multiple philosophers, introducing the shared dining room where their
contemplations take place.&lt;/p&gt;
</content>
  </entry>
</feed>

