2024-01-28T14:03:59+01:00Definitions with shared hidden variables in Gerbil SchemeLeah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20240127-2241332024-01-27T23:41:33+01:002024-01-27T23:41:33+01:00
<p>It is well known that a Scheme function definition is merely defining
a lambda function to a variable:</p>
<pre><code>(define (hypot a b)
(sqrt (+ (* a a) (* b b))))
</code></pre>
<p>This function could be written just as well:</p>
<pre><code>(define hypot
(lambda (a b)
(sqrt (+ (* a a) (* b b)))))
</code></pre>
<p>Occasionally, we need this explicit style when a function needs to
keep around some internal state:</p>
<pre><code>(define generate-id
(let ((i 0))
(lambda ()
(set! i (+ i 1))
i)))
(generate-id) ;=> 1
(generate-id) ;=> 2
(generate-id) ;=> 3
</code></pre>
<p>A problem arises when we need multiple functions to share the state,
let’s say we also want a <code>reset-id</code> function:</p>
<pre><code>(define i 0)
(define (generate-id)
(set! i (+ i 1))
i)
(define (reset-id)
(set! i 0))
(generate-id) ;=> 1
(generate-id) ;=> 2
(generate-id) ;=> 3
(reset-id)
(generate-id) ;=> 1
(generate-id) ;=> 2
</code></pre>
<p>Here, I had to make <code>i</code> a global variable to allow two toplevel
functions to access it. This is ugly, of course. When you did deeper
into the scheme specs, you may find <code>define-values</code>, which lets us write:</p>
<pre><code>(define-values (generate-id reset-id)
(let ((i 0))
(values
(lambda ()
(set! i (+ i 1))
i)
(lambda ()
(set! i 0)))))
</code></pre>
<p>This hides <code>i</code> successfully from the outer world, but at what cost.
The programming language Standard ML has a nice feature to write this
idiomatically, namely <code>local</code>:</p>
<pre><code>local
val i = ref 0
in
fun generate_id() = (i := !i + 1; !i)
fun reset_id() = i := 0
end
</code></pre>
<p>Here, the binding of <code>i</code> is not visible to the outer world as well.
It would be nice to have this in Scheme too, I thought.
Racket <a href="https://docs.racket-lang.org/reference/local.html">provides it</a>,
but the implementation is <a href="https://github.com/racket/racket/blob/master/racket/collects/racket/private/local.rkt">quite hairy</a>.
Since I mostly use <a href="https://cons.io/">Gerbil Scheme</a> these days, I
thought perhaps I can reuse the module system. In Gerbil,
we could also write:</p>
<pre><code>(module ids
(export generate-id reset-id)
(def i 0)
(def (generate-id)
(set! i (+ i 1))
i)
(def (reset-id)
(set! i 0)))
</code></pre>
<p>It is used like this:</p>
<pre><code>(import ids)
(generate-id) ;=> 1
(generate-id) ;=> 2
(generate-id) ;=> 3
(reset-id)
(generate-id) ;=> 1
(generate-id) ;=> 2
</code></pre>
<p>So, let’s use the module system of Gerbil (which supports nested
modules) to implement <code>local</code>. As a first step, we need to write a
macro to define a new module and immediately import it. Since all
Gerbil modules need to have a name, we’ll make up new names using
<code>gensym</code>:</p>
<pre><code>(import :std/sugar)
(defrule (local-module body ...)
(with-id ((mod (gensym 'mod)))
(begin
(module mod
body ...)
(import mod))))
</code></pre>
<p>Here, we use the <code>with-id</code> macro to transform <code>mod</code> into a freshly
generated symbol. <code>defrule</code> is just a shortcut for regular Scheme
<code>syntax-rules</code>, which guarantees a hygienic macro. We can use the
identifier <code>mod</code> inside the body without problems:</p>
<pre><code>> (local-module (export mod) (def (mod) 42))
> (mod)
42
> mod
#<procedure #27 mod378#mod>
</code></pre>
<p>The internal representation of the function shows us it was defined in
a generated module. Re-running <code>local-module</code> verifies that fresh
modules are generated.</p>
<p>Now to the second step. We want a macro that takes local bindings and
a body and only exports the definitions of the body. Using the Gerbil
export modifier <code>except-out</code>, we can export everything but the local
bindings; this is good enough for us.</p>
<p>Thanks to the power of <code>syntax-rules</code>, this is now straight forward to
state:</p>
<pre><code>(defrule (local ((var val) ...) body ...)
(local-module
(export (except-out #t var ...))
(def var val) ...
body ...))
</code></pre>
<p>We rewrite all let-style binding pairs into definitions as well, but
we make sure not to export their names.</p>
<p>Now, we can write our running example like this:</p>
<pre><code>(local ((i 0))
(def (generate-id)
(set! i (+ i 1))
i)
(def (reset-id)
(set! i 0)))
(generate-id) ;=> 1
(generate-id) ;=> 2
(generate-id) ;=> 3
(reset-id)
(generate-id) ;=> 1
(generate-id) ;=> 2
</code></pre>
<p>But perhaps you prefer the slightly less magical way of using modules
directly (which is also what many recommend to do in Standard ML).
Still, creating modules by macros for fine-grained scoping is a good trick
to have up your sleeve.</p>
<p><strong>[Addendum 2024-01-28:]</strong> Drew Crampsie showed me another trick with
nested modules that will help improve the <code>local</code> macro: when you
<code>import</code> a module into another, the bindings are not re-exported by
<code>(export #t)</code> (which means “export all defined identifiers”). (You
can use <code>(export (import: module))</code> if you really wanted this.)
This means we don’t need the <code>except-out</code> trick, but we can just use
two nested modules instead:</p>
<pre><code>(defrule (local ((var val) ...) body ...)
(local-module
(local-module
(export #t)
(def var val) ...)
(export #t)
body ...))
</code></pre>
<p>The inner module contains all the local bindings, the outer one the
visible bindings made in the body. Now, definitions in the body
can also shadow local bindings (not that this is very useful):</p>
<pre><code>(local ((var 5))
(def (get)
var)
(def var 6))
(get) ;=> 6
</code></pre>
<p><small>NP: Dead Moon—Play With Fire</small></p>
Merry Christmas!Leah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20231224-1420312023-12-24T15:20:31+01:002023-12-24T15:20:31+01:00
<p style="margin: 1em; font-weight: bold; text-align: center">
<a href="https://www.youtube.com/watch?v=jzk2E4UXFFs">
<img src="https://img.youtube.com/vi/jzk2E4UXFFs/0.jpg"><br>
The Christmas Duck Song
</a>
</p>
<p style="margin: 1em; position: relative: top: 10px; font-size: 140%; font-weight: bold; text-align: center">
Frohe Weihnachten, ein schönes Fest, und einen guten Rutsch ins neue Jahr
wünscht euch<br>Leah Neukirchen
</p>
<p style="margin: 1em; font-size: 140%; font-weight: bold; text-align: center">
Merry Christmas and a Happy New Year!
</p>
<p><small>NP: ANOHNI and the Johnsons—You Be Free</small></p>
On division by threeLeah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20231125-2316562023-11-26T00:16:56+01:002023-11-26T00:16:56+01:00
<p>I drafted this article in Feburary for a blog project that never came
to realization, but as it’s a nice little article so I’ll just post it here:</p>
<p>I was thoroughly nerdsniped by <a href="https://inbox.vuxu.org/tuhs/20230302013628.8E40618C07B@mercury.lcs.mit.edu/">a recent
post</a>
on The Unix Heritage Society mailing list, where this anecdote was
mentioned by Noel Chiappa:</p>
<blockquote>
<p>Steve Ward told another oral story which I’m pretty sure is true,
though. They ask the candidate to design a state machine (or
digital logic, I forget which) which can tell if a number is
divisible by three (I think I have the details correct, but I’m not
absolutely certain). So they describe one – and then point out that
you can feed the number in from either end (most or least
significant end first) – and proves that it will work either way!
The committee was blown away.</p>
</blockquote>
<p>Since this sounded like an interesting statement, I tried to find a proof myself
and found two solutions, one using automata theory and one using ordinary math.</p>
<p>Read as a PDF: <a href="https://leahneukirchen.org/blog/graphics/div3.pdf">On division by three</a>.</p>
<p><small>NP: Lightning Bolt—No Rest for the Obsessed</small></p>
Das PunktesystemLeah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20231016-1849592023-10-16T20:49:59+02:002023-10-16T20:49:59+02:00
<style>
.sc { font-variant: small-caps; text-transform: lowercase; }
</style>
<p>Ein Entwurfsmuster für kollektiv verwaltete Räume.
(Entworfen um 2011 von Leah Neukirchen.)</p>
<h4><span class="sc">DAS PUNKTESYSTEM</span></h4>
<p><em>Kontext:</em> Verschiedene Waren sollen gegen ein Entgelt zur Verfügung
gestellt werden können.</p>
<p><em>Problem:</em> Die Waren haben unterschiedliche Preise, der Verkauf soll
zudem keinen Verlust erzeugen.</p>
<p><em>Verwandte Muster:</em></p>
<ul>
<li><span class="sc">STRICHLISTE</span>, falls alles die gleichen Preise hat.</li>
<li><span class="sc">KASSE DES VERTRAUENS</span>, falls keine Vorzahlung erwünscht ist.</li>
</ul>
<p><em>Lösung:</em> Die Einführung eines <span class="sc">PUNKTESYSTEMS</span>.
Dies besteht aus einem
zentral angebrachten Blatt Papier auf dem jeder Nutzende eine Zeile
belegt (Stammnutzer können eine Zeile in der Vorlage vorgedruckt bekommen).</p>
<p>Jede Zeile besteht aus z.B. drei Kästchen übereinander. Es wird ein
Preis pro Kästchen spezifiziert (z.B. 33ct). Nutzende können nun in
eine <span class="sc">KASSE DES VERTRAUENS</span> einzahlen, und die
vorbezahlten Kästchen mit einem Stift markieren. Bei der Entnahme von
Waren wird bezahlt, in dem man die entsprechende Anzahl Kästchen
ausmalt. Dank der Vorkasse können Waren auf Vorrat eingekauft werden.
Es ist darauf zu achten, dass niemand bei der Kasse Schulden macht.</p>
<p>Sind sehr viele Zeilen gefüllt, so wird ein neues Blatt ausgedruckt,
und die geleisteten Vorzahlungen werden in neue Zeilen übertragen.</p>
<img src="https://leahneukirchen.org/blog/graphics/punktesystem.png" alt="Ein Bild der Vorlage" width="100%" />
<p><small>NP: Mrs. Piss—You Took Everything</small></p>
A conservative extension of ISO 8601 to support fractional daysLeah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20230420-2023362023-04-20T22:23:36+02:002023-04-20T22:23:36+02:00
<p>You probably have seen
<a href="https://en.wikipedia.org/wiki/ISO_8601">ISO 8601 timestamps</a> with fractional
seconds, such as this one:</p>
<pre><code>% date --iso-8601=ns
2023-04-20T18:45:11,094052607+02:00
</code></pre>
<p>However, many people don’t know ISO 8601 also allows for fractional
minutes and hours!</p>
<p>According to the standard, these timestamps are equivalent (rounded to a second):</p>
<pre><code>2023-04-20T18:45:11
2023-04-20T18:45,18333
2023-04-20T18,75305
</code></pre>
<p>Note that in contrast to common scientific usage, the decimal part is
recommended to be separated by <em>a comma</em> and not a full stop, although
the latter is permitted too.</p>
<p>However, the standard does not specify the obvious next
generalization, that is, allowing <em>fractional days</em>. I thus propose to
extend ISO 8601 in the following way, which does not change the
meaning of valid existing representations:</p>
<blockquote>
<p>The local time representation (after the optional time designator)
may consist of only a decimal fraction, which then is interpreted as a
multiple of 24 hours.</p>
</blockquote>
<p>Thus, we can write the above timestamp also like this:</p>
<pre><code>2023-04-20T,78137
2023-04-20,78137
</code></pre>
<p>Now, why would one want this? Essentially, there are three reasons:</p>
<p>First, it’s cute and an obvious extension of the existing format.</p>
<p>Second, it allows representing times of the
<a href="https://en.wikipedia.org/wiki/French_Republican_calendar">French Republican Calendar</a>
in a natural way, which uses a decimal system as well: in this calendar,
the day is divided into 10 hours of 100 minutes and 100 seconds each.
Thus, the digits align directly to a decimal fraction of the whole day.
The above timestamp is then (computed using
<a href="https://leahneukirchen.org/dotfiles/bin/fdate">fdate</a>):</p>
<pre><code>Primidi, 1 Floréal CCXXXI (231) 7:81:37
</code></pre>
<p>Note that we use local time here, not Paris time. If you insist on
using Paris solar time, you need to offset 9 ISO minutes and 21 ISO seconds,
which can be approximated as</p>
<pre><code>2023-04-20T,77350+0009
</code></pre>
<p>Note that ISO 8601 does not allow for specifying offsets from UTC in
seconds (another obvious oversight).</p>
<p>Finally, the mechanism also supports the use of
<a href="https://en.wikipedia.org/wiki/Swatch_Internet_Time">Swatch Internet Time</a>,
a late 90s decimal time system. Here, the day is divided into 1000 <em>beats</em>,
and the offset is fixed UTC+1 (for the Swatch headquarters in Biel):</p>
<pre><code>2023-04-20T,739+0100
</code></pre>
<p>This is a bit more verbose than <code>@739</code> but at least it’s an
international standard already!</p>
<p><small>NP: Tristan Brusch feat. Annett Louisan—Kein Problem</small></p>
Merry Christmas!Leah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20221224-1351362022-12-24T14:51:36+01:002022-12-24T14:51:36+01:00
<p style="margin: 1em; font-weight: bold; text-align: center">
<img alt="Picture of Kropotkin with a Christmas hat" src="https://leahneukirchen.org/blog/graphics/xmas-22.jpg" />
</p>
<p style="margin: 1em; position: relative: top: 10px; font-size: 140%; font-weight: bold; text-align: center">
Frohe Weihnachten, ein schönes Fest, und einen guten Rutsch ins neue Jahr
wünscht euch<br>Leah Neukirchen
</p>
<p style="margin: 1em; font-size: 140%; font-weight: bold; text-align: center">
Merry Christmas and a Happy New Year!
</p>
<p><small>NP: First Aid Kit—Fallen Snow</small></p>
50 blank pages, or: black-box debugging of PDF rendering in printersLeah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20221011-1546182022-10-11T17:46:18+02:002022-10-11T17:46:18+02:00
<p>I was happily typesetting a book with <a href="https://wiki.contextgarden.net/Main_Page">ConTeXt</a> and its <a href="https://github.com/contextgarden/luametatex">new engine LMTX</a>
when I decided to print a few pages to see if I got the sizes right.
Since I despise printer daemons, I directly print stuff over the network
<a href="https://leahneukirchen.org/dotfiles/bin/cupless">using a little script</a>.</p>
<p>To my surprise, I just got a blank page out.</p>
<p>As the title of this post suggests, it won’t be the only blank page.
This is the story of me debugging PDF generation in LMTX.</p>
<p>Giving it a closer look, the page wasn’t entirely blank. I could see
the cutmarks I added to indicate the page size. During creation, I
used MuPDF as a previewer—it’s lightweight and stays out of my way.
But apparently the PDF was broken, so I tried a few other previewers.
Evince and Firefox pdf.js rendered it fine. I looked at Okular and
xpdf, and it came out nicely as well. At a later point, I even
installed ancient Acrobat 7(!) and it would display as intended.</p>
<p>I tried the other printer in our university office. Another blank page.</p>
<p>Two different vendors, yet they both fail to print a simple PDF?</p>
<p>I secretly hoped some previewer would also render a blank page. Then
I could just compile its source code on my machine and throw all kinds
of debugging tools at it… but they all worked.</p>
<p>I tried converting the PDF to PDF with Ghostscript, and then it
printed fine. So the PDF couldn’t be too wrong. But I wanted to fix
it directly.</p>
<p>So how do you debug a PDF that gets printed wrongly but seems to be
fine else?</p>
<p>My first intuition was to make a PDF that works, and then look at the
differences. So I created a simple document and ran it through the
previous ConTeXt version, called MKIV. This version uses
<a href="https://www.luatex.org/">LuaTeX</a> as an engine. It printed fine. (No
nobody’s surprise—I would have discovered this years ago else.)</p>
<p>I put both PDFs through various PDF validators, but they all said both
where good.</p>
<p>Time to dig deeper.
I disabled PDF compression and looked at both PDF files in a text editor.
Sure, there were a lot of little differences. But fundamentally? Pretty much
the same.</p>
<p>… I looked at the first printout again. Not only the page marks
were printed, but actually the tiny page numbers inside them were too!
I checked the PDF, and saw it uses two fonts (using <code>pdffonts</code>):</p>
<pre><code>NSOLKP+TeXGyreSchola-Regular CID Type 0C Identity-H yes yes yes 4 0
FFMARX+DejaVuSansMono CID TrueType Identity-H yes yes yes 5 0
</code></pre>
<p>The page numbers use the DejaVu Sans font, which is supplied in TrueType format.
I changed the main font of my test document to DejaVu
Sans, and voilà: it printed fine. I was very happy about this, as it
meant LMTX can generate printable PDF files in principle. But for its
default font (Latin Roman) and the font I wanted to print in (TeX Gyre
Schola), there apprently was an issue.</p>
<p>I knew the basics of PDF from decades ago when I wrote a PDF generator
from scratch. (I never got around doing more than putting a few
characters on a page, though.)
Now it was time to learn about the PDF font formats.</p>
<p>Both the MKIV and the LMTX engine use the “CID Type 0C” font format
these days, which embeds only the actually used glyphs from an
OpenType font into the PDF. I pulled out the CID fonts
from the PDF (using <code>mutool extract</code>). While <code>file</code> didn’t recognize
the file format, luckily FontForge could open it fine. (As I learned later,
FontForge can open the PDF directly and import its fonts.)</p>
<p>I noticed a first difference: while MKIV (and thus LuaTeX) spread out
the glyphs over the positions, LMTX nicely arranged the used glyphs
starting from code point 1. I had already contacted Hans Hagen, the
main developer behind ConTeXt, and we wondered whether starting the glyphs
from 31 would help… again it rendered nicely on all previewers, but still
printed blank pages.</p>
<p>I had the strong suspicion that the font embedding was the problem.
To verify this hypothesis, I manually fiddled the LMTX font into the
MKIV document (this was easy because it was smaller, so I just had to
add some padding to make the document be valid again), adjusted some
code points in the PDF, and it would render glyphs on the screen. But
it would not print. So now I was fairly sure that the font stream was the
culprit, and not some other part of the PDF.</p>
<p>After more research, I found a tool to dump a CID font in a readable
format: <a href="https://github.com/janpe2/CFFDump">CFFDump</a>. This small Java
program turned out to be essential for tracking down the bug.</p>
<p>It generates a dump that looks like this:</p>
<pre><code>% CFF Dump Output
% File: font-0008.cid
--------------------------------------------------------------------------------
Header (0x00000000):
major: 1
minor: 0
hdrSize: 4
offSize: 4
--------------------------------------------------------------------------------
Name INDEX (0x00000004):
count: 1, offSize: 1
[0]: (CLLXEY+LMRoman12-Regular)
--------------------------------------------------------------------------------
Top DICT INDEX (0x00000021):
count: 1, offSize: 1
[0] (0x00000026):
<<
/ROS << /Registry (Adobe) /Ordering (Identity) /Supplement 0 >>
/CIDCount 15
/FamilyName (LMRoman12) % SID 392
/FullName (LMRoman12-Regular) % SID 393
/Weight (Normal) % SID 394
/FontBBox [-422 -280 1394 1127]
/isFixedPitch false
/ItalicAngle 0
/UnderlinePosition -175
/UnderlineThickness 44
/CharstringType 2
/FontMatrix [0.001 0 0 0.001 0 0]
/StrokeWidth 0
/CharStrings 257 % offset
/charset 220 % offset
/FDArray 1751 % offset
/FDSelect 249 % offset
/Private [23 1728] % [size offset]
% ----- Following entries are missing, so they get default values: -----
/PaintType 0 % default
/CIDFontVersion 0 % default
/CIDFontRevision 0 % default
/CIDFontType 0 % default
>>
</code></pre>
<p>And it goes on and on, detailing all the things specified in the font.</p>
<p>Inevitably, I had to dig into the internals of CFF fonts, that is Adobe’s
<a href="https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf">Technical Note #5176</a>.</p>
<p>I carefully compared the dump of the working MKIV font with the broken
LMTX font… and didn’t find substantial differences. Sure, one
copied a few more metadata fields, and the other had more font fields
set, but mostly to values that were the default anyway. Nothing that
seemed to be related to our bug. And also, the various PDF viewers
rendered the document fine, so there couldn’t have been a major
mistake there.</p>
<p>By now I had learned about the design of LMTX, and luckily I saw that
all parts of this font embedding were written in quite
<a href="https://bitbucket.org/phg/context-mirror/src/e94fa4dc30ec28a6727aa85e17aaac18b76aeadb/tex/context/base/mkxl/lpdf-emb.lmt#lines-1129">straight-forward Lua code</a>
that I could easily modify, so experiments
were easy. Unfortunately, I didn’t have a printer at home so I had to
annoy some of my friends to do test prints for me. They printed a lot
of blank pages…</p>
<p>But I just couldn’t track down the problem. A reasonable person would
have given up ages ago and just fed the PDF through Ghostscript before
printing, but I wanted to get to the bottom of the thing; and I also
wanted this new TeX engine to produce working documents out of the box.</p>
<p>In my time as a software developer, one thing I learned about
debugging is that if a thing takes a long time to debug, it can be for two
reasons: either the cause is much more simple than you thought,
or it’s much more complicated.</p>
<p>I chose violence. I corrupted the CID font in various ways… the
printer would stop working and printed an error message instead. Some
printers have an internal error log, but before these experiments it was empty.</p>
<p>Perhaps the document wasn’t wrong, but the printer software was?
But by now we could reproduce the issue with a bunch of printers—how
can they all have the same issue?</p>
<p>After some wrong attempts related to font hinting, I was out of ideas and
decided to kill all fields one by one and check if it made any
difference.</p>
<p>I deleted the <code>/FontMatrix</code> entry and… suddenly it printed nicely.</p>
<p>Now, the font matrix is a feature of CFF fonts to encode their scaling
and shearing factors. It’s a 2x3 matrix that encodes an affine
transformation (perhaps you know this from SVG). The details
don’t matter, but in practice you only have two values set and they
determine the font size relative to the sizes used in the font drawing
instructions. By default, the font matrix is <code>[0.001 0 0 0.001 0 0]</code>,
meaning that moving by 1000 units will move by 1 PostScript point on paper.</p>
<p>I was happy, but I also was very confused: of all things, why exactly
did <em>that</em> fix it? I noticed earlier that the MKIV document didn’t
have the font matrix set, but I also looked at the Ghostscript output
and there it worked fine. Even more so, LMTX set the font matrix to
its default value! <em>It shouldn’t make a difference at all!</em></p>
<p>Gone this far, I wasn’t satisfied without a real answer.
I wondered if LMTX encoded the font matrix the wrong way,
but after digging into the spec for that
(<a href="https://www.pdfa.org/norm-refs/5177.type2_.pdf">Technical Note #5177</a>)
and double checking, it seemed fine. The working Ghostscript PDF used exactly
the same byte sequence to encode the font matrix.</p>
<p>Staring some more at CFFDump output, I finally noticed what
Ghostscript did differently: the CFF had <em>two</em> font matrices defined!
CFF allows defining a font matrix in the “Top DICT INDEX”
as well as the “Font DICT INDEX”.</p>
<p>And while the “Top DICT INDEX” was the same that we used,
<code>[0.001 0 0 0.001 0 0]</code>, the one in the “Font DICT INDEX” was
<code>[1 0 0 1 0 0]</code>, i.e. the identity matrix. I added this matrix
to LMTX output, and finally the PDF printed properly.</p>
<p>Still, this was a surprise. Why would explicitly setting the font
matrix to its default value change the behavior? It turns out the
reason for this is an interaction between <em>both</em> of these default values.
Unfortunately, it seems to be not specified by Adobe.
I found a similar <a href="https://bugs.ghostscript.com/show_bug.cgi?id=690724#c12">bug in Ghostscript</a> that explains the reasonable thing to do:</p>
<pre><code>1) If both Top DICT and Font DICT does _not_ have FontMatrix, then Top DICT = [0.001 0 0 0.001 0 0], Font DICT
= [1 0 0 1 0 0]. (Or, Top DICT = (absent), Font DICT = [0.001 0 0 0.001 0 0] then let '/CIDFont defineresource'
make Top DICT = [0.001 0 0 0.001 0 0], Font DICT = [1 0 0 1 0 0].)
2) If Top DICT has FontMatrix and Font DICT doesn't, then Top DICT = (supplied matrix), Font DICT = [1 0 0 1 0 0].
3) If Top DICT does not have FontMatrix but Font DICT does, then Top DICT = [1 0 0 1 0 0], Font DICT =
(supplied matrix). (Or, Top DICT = (absent), Font DICT = (supplied matrix) then let '/CIDFont defineresource'
make Top DICT = [0.001 0 0 0.001 0 0], Font DICT = (supplied matrix 1000 times larger). I think this is better.)
4) If both Top DICT and Font DICT _does_ have FontMatrix, then Top DICT = (supplied matrix), Font DICT =
(supplied matrix).
</code></pre>
<p>All previewers seem to have adapted this algorithm.
But certain older printers botched step 2.
They end up with <em>two</em> font matrices <code>[0.001 0 0 0.001 0 0]</code>
that are multiplied together, which ends up printing your document at
a thousandth of its size;
i.e. you get a blank page. But note that it’s a perfectly valid PDF!</p>
<p>We thus had two ways to fix the bug: write no font matrix at all, or
write both of them. I was first learning towards the latter, and do
it as Ghostscript does, but we found an issue with FontForge that it
will render the fonts internally at 1000x the size and thus consume a
lot more memory. Since we did not find a need to use a non-default
font matrix, we decided to go with the former: no font matrix at all.
After all, it worked fine for LuaTeX all those years, too.</p>
<p>(Why did this issue not affect the TrueType font? It’s embedded
in a different format that only has a single scaling factor and has no
concept of a font matrix.)</p>
<p>A trial print of the PDF on many printers is on-going but seems to be
very promising so far, so that this fix (essentially,
deletion of one line of code) will be shipped soon in a ConTeXT snapshot
for general availability.</p>
<p>I would like to thank Hans Hagen for not giving up on helping me with
this, and all my friends that test-printed some page for me
and/or had to hear me talking about nothing else for a week or so.</p>
<p><small>NP: Rites of Spring—All Through A Life</small></p>
Note taking in Emacs with howmLeah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20220326-1640572022-03-26T17:40:57+01:002022-03-26T17:40:57+01:00
<h2>Prelude and Motivation</h2>
<p>After trying out and fiddling with a plethora of existing and
self-written software to organize my notes, I have decided I need to
stop experimentation and choose a solution that is sufficient, but
most importantly, one that I actually will use and have migrated all
my existing notes to. Note taking systems are not an end unto
themselves.</p>
<p>Roughly speaking, my essential requirements are:</p>
<ul>
<li>Something that works with plain text, and ideally supports Markdown
as that is the syntax I publish most things in and I am most
familiar with.</li>
<li>Something that can be used from Emacs, because that’s where I do
most my text editing and writing.</li>
<li>Something that stores a note per file.
This has just proved to be the most future-proof way.</li>
<li>Some basic means of connecting notes.</li>
</ul>
<p>I found I don’t need these things:</p>
<ul>
<li>Support for direct HTML publishing, as I realized that most of my
notes are written for myself and I’ll put them up somewhere else for
publishing. (This usually involves prior editing anyway.)</li>
<li>Having a fancy UI and graph displays. I consider these goodies I
can easily go without.</li>
<li>Specialized productivity features like to-do lists, date scheduling
or time tracking. These are out of scope for my use: I use a
regular calendar for things with a deadline (which I’m blessed to
have very few of) and will stick to simple to-do lists for my
personal projects.</li>
</ul>
<p>Many people would recommend me <a href="https://orgmode.org/">org-mode</a> now,
but I’ve never been a fan of its clunky syntax and I really don’t need
most of the features. <a href="https://www.orgroam.com/">org-roam</a> at first
looked promising but linking between notes is quite complicated and the
database dependency seems to be overkill for a personal note taking
system on modern hardware.</p>
<p>I decided to settle on <a href="http://howm.osdn.jp/">howm</a>, an Emacs mode
that is not very well-known in the Western world but has gained a
certain following in Japan.</p>
<p>It’s a roughly 20-year-old Emacs mode that’s still being used and
maintained by its original author Kazuyuki Hiraoka, so I have
confidence it will be around for some more time. You can format notes
however you like, so I can use Markdown as I prefer. Notes are one
file per note by default (but see below). It actually has features
for date scheduling and to-do lists, but I won’t go deeper into them
for now. Its code is reasonable simple and well structured, so I was
able to extend it in a few ways easily, as I’ll detail at the end of
this post.</p>
<p>What really sold me was this quote I found on the mailing list:</p>
<blockquote>
<p><q>I cannot show a good general guide because I’m lazy, loose, and bad at
tidying. I’ve already given up well-ordered notes. Howm may be
suitable for those who are tired from strict systems.</q><br />
— Kazuyuki Hiraoka</p>
</blockquote>
<p>Such an undogmatic system is just right for my purposes.</p>
<h2>How howm works</h2>
<p>Since there are very few resources in English that explain howm
(apart from this
<a href="https://web.archive.org/web/20060409123241/http://dto.twu.net/HowmTutorial.howm.html">2006 article</a>
that got lost in time), I shall give a quick introduction for the
interested reader:</p>
<p>howm is short for “Hitori Otegaru Wiki Modoki”, which roughly
translates to “Single-user Easy Wiki Mode”.</p>
<p>The basic feature set of howm is very simple, and can be condensed
into the mantra “write fragmentarily and read collectively”.
Essentially, howm provides an Emacs minor mode for marking text to
trigger certain searches. Since it is a minor mode, you can use
whatever major mode you like to use for your writing (e.g., I currently use
<code>markdown-mode</code>).</p>
<p>Howm notes are kept in a directory hierarchy that you can organize how
you wish; by default a date-based system is used and filenames include
the current timestamp at creation. This provides unique and sensible
note identifiers, so I stick to it. You also can create explicitly
named note files, but I haven’t found a use for them yet.</p>
<p>There are two essential kinds of markup howm cares about:
note titles and links. By default, titles are marked up
by putting a ‘<code>=</code>’ at the beginning of the line, but this can be configured
(and must be done so before loading <code>howm-mode</code>(!)).
The benefit of adding a title to a note is that you can have
the summary buffer show titles instead of matching lines,
which can be helpful to get a better overview of search results.
(The creator of howm is sceptical of titling all notes, I think it
very much depends the average length of your notes.
There is no requirement to use titles.)</p>
<p>There are two kinds of links supported by howm, namely <strong>goto</strong> and
<strong>come-from</strong> (in a nod to INTERCAL). <strong>goto</strong> links are forward
references and written like this:</p>
<pre><code>>>> howm
</code></pre>
<p>Pressing return on this line when <code>howm-mode</code> is enabled will show a
list of all occurences of the word <code>howm</code> in your notes directory.</p>
<p>In contrast, a <strong>come-from</strong> link is written like this:</p>
<pre><code><<< howm
</code></pre>
<p>And this will cause the word <code>howm</code> in any howm-mode buffer to be
underlined and trigger a search where the buffer with <code><<< howm</code>
will appear first.</p>
<p>Thus, compared to most contemporary hypertext systems, we not only
have a means of explicitly linking to other notes, but also for
creating implicit contextual links—which can help you find connections
between your notes that you didn’t see yet…</p>
<p>It is straightforward to implement something like <code>#tags</code> or <code>WikiWords</code>
using these features, if you wish to do so.</p>
<p>Additionally, howm provides an inline link syntax <code>[[...]]</code> that works
like <code>>>></code>
but can appear within a line. I make a suggestion below how to turn
it into a direct link to the first page with the given title; but for
now I decided not to use this very much.</p>
<p>The line-based nature of the <code>>>></code> syntax prevents usage for “inline”
links. After giving it some thought, I consider it a strength for a
note-taking system to make forward links related to a paragraph and
not part of a sentence. Additionally, it also makes the plain text
easier to read as the link target is not interleaved into the text.
(Compare with the use of reference-style links in Markdown.)</p>
<p>An aside: howm actually supports multiple notes per file by having
multiple title lines in a file. The search summary will always show
the title directly before the matching line. You can use <code>C-c , C</code> to
create a new note in the current buffer. I don’t use this much, but I
think it could be useful for glossary files that contain many short
notes. Some people also use this for keeping multiple daily notes in
a single file.</p>
<h2>Using howm</h2>
<p>Howm provides two main features to access notes: the menu and the
summary buffer. The howm menu (shown with <code>C-c , ,</code>) provides a very
customizable view into your howm notes. By default it shows your
schedule, recent notes, and random notes. You can access many howm
features with a single keypress from the menu. Since I don’t use the
scheduling feature, I mostly access howm from the summary buffer
instead.</p>
<p><img src="https://leahneukirchen.org/blog/graphics/howm-summary.png" alt="The howm summary buffer" /></p>
<p>The howm summary buffer shows the result of a search (<code>C-c , g</code>), a
list of recent notes (<code>C-c , l</code>), or an overview of all notes (<code>C-c , a</code>).
It is very convenient to see the matches and you get a preview
of the note when you move the cursor to a search result. Typing RET
will open the note for editing. Typing <code>T</code> will toggle between
displaying matching lines or the titles of notes with matches.</p>
<p>In the summary buffer, you can also type <code>@</code> and read all matching
notes in a concatenated way, so you get the full context of all notes
at once.</p>
<h2>Setting up and customizing howm</h2>
<p>Basic setup is reasonably well documented in English, but I’ll
summarize it here. You can get howm from ELPA these days, so
installing is very easy. You should set some variables to configure
it according to your needs:</p>
<pre><code>;; Directory configuration
(setq howm-home-directory "~/prj/howm/")
(setq howm-directory "~/prj/howm/")
(setq howm-keyword-file (expand-file-name ".howm-keys" howm-home-directory))
(setq howm-history-file (expand-file-name ".howm-history" howm-home-directory))
(setq howm-file-name-format "%Y/%m/%Y-%m-%d-%H%M%S.md")
</code></pre>
<p>Here, we decide that <code>~/prj/howm</code> is the base directory for our howm
notes, and we also put the two auxiliary files howm uses there.
Additionally, we change the default name format to end with <code>.md</code>
(which also turns on markdown-mode by default).</p>
<p>Next, we want to use <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>
for searching howm. For my usage, plain GNU grep would be sufficient,
but I want to use ripgrep in the next step too, so for consistency
let’s use it for all searches:</p>
<pre><code>;; Use ripgrep as grep
(setq howm-view-use-grep t)
(setq howm-view-grep-command "rg")
(setq howm-view-grep-option "-nH --no-heading --color never")
(setq howm-view-grep-extended-option nil)
(setq howm-view-grep-fixed-option "-F")
(setq howm-view-grep-expr-option nil)
(setq howm-view-grep-file-stdin-option nil)
</code></pre>
<p>The next addition is interactive search with ripgrep (<code>C-c , r</code>).
This is the most useful feature I added to howm myself.
I think it provides a great way to interact with your notes, as you get
instant feedback from your search terms, and can stop searching as
soon as you found what you were looking for.
I used <code>counsel-rg</code> as an inspiration for this, and we turn the ripgrep matches
into a regular howm summary buffer for further consumption.</p>
<pre><code>;; counsel-rg for howm
(defun howm-list--counsel-rg (match)
(if (string= match "")
(howm-list-all)
(if (or (null ivy--old-cands)
(equal ivy--old-cands '("No matches found")))
(message "No match")
(let ((howm-view-use-grep
#'(lambda (str file-list &optional fixed-p force-case-fold)
(mapcar
(lambda (cand)
(if (string-match "\\`\\(.*\\):\\([0-9]+\\):\\(.*\\)\\'" cand)
(let ((file (match-string-no-properties 1 cand))
(line (match-string-no-properties 2 cand))
(match-line (match-string-no-properties 3 cand)))
(list (expand-file-name file howm-directory)
(string-to-number line)
match-line))))
ivy--old-cands))))
(howm-search ivy--old-re t)
(riffle-set-place
(1+ (cl-position match ivy--old-cands :test 'string=)))))))
(defun howm-counsel-rg ()
"Interactively grep for a string in your howm notes using rg."
(interactive)
(let ((default-directory howm-directory)
(counsel-ag-base-command counsel-rg-base-command)
(counsel-ag-command (counsel--format-ag-command "--glob=!*~" "%s")))
(ivy-read "Search all (rg): "
#'counsel-ag-function
:dynamic-collection t
:keymap counsel-ag-map
:action #'howm-list--counsel-rg
:require-match t
:caller 'counsel-rg)))
(define-key global-map (concat howm-prefix "r") 'howm-counsel-rg))
</code></pre>
<p>Next, I tweak some sorting settings. I want the “recent” view to list
files by mtime (so that recently edited files appear on top), but the
“all” view should be sorted by creation date.</p>
<pre><code>;; Default recent to sorting by mtime
(advice-add 'howm-list-recent :after #'howm-view-sort-by-mtime)
;; Default all to sorting by creation, newest first
(advice-add 'howm-list-all :after #'(lambda () (howm-view-sort-by-date t)))
</code></pre>
<p>A great usability enhancement is buffer renaming: since howm file names
are a bit unwieldy (like <code>~/prj/howm/2022/03/2022-03-25-162227.md</code>)
you can use these two lines to rename note buffers according to their
title, which makes switching between multiple notes more convenient.</p>
<pre><code>;; Rename buffers to their title
(add-hook 'howm-mode-hook 'howm-mode-set-buffer-name)
(add-hook 'after-save-hook 'howm-mode-set-buffer-name)
</code></pre>
<p>Another personal preference is enabling
<a href="https://elpa.gnu.org/packages/orgalist.html">orgalist-mode</a>, which I
like for shuffling around Markdown lists.</p>
<pre><code>(add-hook 'howm-mode-hook 'orgalist-mode)
</code></pre>
<p>Finally we fix an anti-feature in howm: by default, it binds C-h to
the same binding as backspace, but this is only useful on legacy
terminals (and even then Emacs does the translation). I wouldn’t
really mind, but it breaks the Emacs help feature, so we unbind C-h
for the modes:</p>
<pre><code>(define-key howm-menu-mode-map "\C-h" nil)
(define-key riffle-summary-mode-map "\C-h" nil)
(define-key howm-view-contents-mode-map "\C-h" nil)
</code></pre>
<p>My configuration ends with three definitions of <code>action-lock</code>, the howm
mechanism for marking text active and do something on RET. Two of
them are related to the reference management software
<a href="https://www.zotero.org/">Zotero</a>, which I use for organizing papers,
and enable me to link to articles in my Zotero database by URL
or BibTeX identifier:</p>
<pre><code>;; zotero://
(add-to-list 'action-lock-default-rules
(list "\\<zotero://\\S +" (lambda (&optional dummy)
(browse-url (match-string-no-properties 0)))))
;; @bibtex
(add-to-list 'action-lock-default-rules
(list "\\s-\\(@\\([a-zA-Z0-9:-]+\\)\\)\\>"
(lambda (&optional dummy)
(browse-url (concat "zotero://select/items/bbt:"
(match-string-no-properties 2))))
1))
</code></pre>
<p>Finally, as mentioned above, this is how to make <code>[[...]]</code>
wiki-links directly point to the first page with that title, skipping
the summary buffer:</p>
<pre><code>;; make wiki-links jump to single title hit if possible
(add-to-list 'action-lock-default-rules
(list howm-wiki-regexp
(lambda (&optional dummy)
(let ((s (match-string-no-properties howm-wiki-regexp-pos)))
;; letting create-p be nil here, howm-keyword-search-subr
;; should check create-p after open-unique-p
(howm-keyword-search (concat "= " s) nil t)))
howm-wiki-regexp-hilit-pos))
</code></pre>
<p>The whole configuration is part of my
<a href="https://leahneukirchen.org/dotfiles/.emacs">.emacs</a> file.</p>
<h2>Future ideas</h2>
<p>One thing I want to implement but didn’t yet get around to is support
for searching notes using <a href="https://github.com/Genivia/ugrep">ugrep</a>,
which has a nifty boolean search mode that applies to whole files, so
you can do searches that are not limited to a line context
(e.g. <code>hoge|fuga -piyo</code> finds all notes that mention hoge or fuga, but
don’t contain piyo).</p>
<p>I may also look into the scheduling features of howm, but I direct you to
the terse <a href="https://howm.osdn.jp/README.html">README</a> for now,
if you’re curious.</p>
<p>Anyway, I hope this was interesting and perhaps encourages you to look into
howm, an Emacs mode that I feel doesn’t receive the attention it deserves.</p>
<p><small>NP: Bob Dylan—What Was It You Wanted</small></p>
How to check you're in the initial pid namespace?Leah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20220108-2155582022-01-08T22:55:58+01:002022-01-08T22:55:58+01:00
<p>It all started with a simple question: how can a Linux process
determine whether it is the init process of a freshly booted system?</p>
<p>A dozen years ago, the Unix textbook answer to this would have been:
well, if its process id (pid) is 1, then it is <code>init</code> by definition.</p>
<div align="center" style="text-align: center; padding: 0.5em;">⁂</div>
<p>These days, things are not that simple anymore. Containerization
creates situations where pid is 1, but the process runs, well, in a
container. In Linux, this is realized by using a feature called “pid
namespaces”. The clone(2) syscall can take the flag <code>CLONE_NEWPID</code>
(“since Linux 2.6.24”), which puts the new process into a new pid
namespace. This means that this process will have pid 1 <em>inside</em> the
pid namespace, but outside (i.e. in the parent pid namespace), the
process has a regular pid. Various Linux API transparently translate
pids between these namespaces.</p>
<p>The pid namespaces form a hierarchy, and the one at the very top is
called “initial pid namespace”.</p>
<p>You can use the tool unshare(1) to play with pid namespaces:</p>
<pre><code>% unshare --fork --map-root-user --pid bash -c 'echo $$'
1
</code></pre>
<p>This is a way to spawn (as a regular user!) a process that has pid 1,
at least, that’s what it looks like to the process.</p>
<p>We can try to find some evidence that we’re a freshly booted <code>init</code>, but
none of it is really conclusive:</p>
<ul>
<li>Our user id is 0, we are root (necessary but not sufficient of course).</li>
<li><code>$TERM</code> should be <code>linux</code>; trivial to override.</li>
<li><code>$BOOT_IMAGE</code> is set, but this depends on the boot loader.</li>
<li>System uptime is “low”, but it takes the initrd boot time into account.
Our non-root <code>init</code> could be spawned in a container at boot time.</li>
</ul>
<p>There are also some indicators the process runs in a container using
one of the popular solutions such as <code>docker</code> or <code>podman</code>:</p>
<ul>
<li>The process has a lot of supplementary groups already.</li>
<li>If we were put inside a cgroup, reading <code>/proc/1/cgroup</code> will indicate it.</li>
<li>The file <code>/.dockerenv</code> exists.</li>
</ul>
<p>But there are still situations, such as the <code>unshare</code> call above,
where all of these things may not be true.</p>
<p>Therefore I tried to find the ultimate way to detect whether we are
in the initial pid namespace.</p>
<p>I started to research this and quickly found the ioctl(2)
<code>NS_GET_PARENT</code> which seemed to be useful: “Returns a file descriptor
that refers to the parent namespace of the namespace referred to by
fd.” However, it is useless for this purpose:</p>
<pre><code>EPERM The requested namespace is outside of the caller's
namespace scope. This error can occur if, for example,
the owning user namespace is an ancestor of the caller's
current user namespace. It can also occur on attempts to
obtain the parent of the initial user or PID namespace.
</code></pre>
<p>Of course, it makes a lot of sense that we cannot get a handle to the
surrounding pid namespace, as this would make the encapsulation
provided by namespaces futile. However, coalescing these two error
conditions (namespace is outside the caller namespace, and namespace
is initial pid namespace) doesn’t make our life easier.</p>
<p>So, we need to bring out bigger guns in. I searched the kernel source for
<a href="https://elixir.bootlin.com/linux/v5.15/A/ident/init_pid_ns">occurrences of <code>init_pid_ns</code></a>,
as this namespace is called in the Linux source code. There are not
too many occurrences we can rely on. The taskstats module limits the
<code>TASKSTATS_CMD_ATTR_REGISTER_CPUMASK</code> command to the initial pid
namespace only, but to use this requires speaking the netlink
interface, which is terrible.
Also, the behavior could change in future versions.</p>
<p>One interesting, and viable approach, is
<a href="https://elixir.bootlin.com/linux/v5.15/source/kernel/pid_namespace.c#L300">this limitation</a>
of the reboot(2) syscall: only some <code>LINUX_REBOOT_CMD_*</code> commands are allowed
to be sent inside a nested pid namespace. Now, we need to find a
“harmless” command to call reboot(2) with to test this! (Obviously,
only being able to suspend the machine from the initial pid namespace
is not a very useful check…) There are two commands that do
not do much harm: <code>LINUX_REBOOT_CMD_CAD_{ON,OFF}</code> will toggle the action
that Ctrl-Alt-Delete performs. Unfortunately, it is impossible to
read the state of this flag, making this test a destructive operation
still. (But if you are pid 1, you may want to set it anyway, so you get
pid namespace detection for free.)</p>
<div align="center" style="text-align: center; padding: 0.5em;">⁂</div>
<p>So I kept looking for other ways until I realized there’s a quite
natural property to check for, and that is to find out if there are
<em>kernel threads</em> in the pid namespace. Kernel threads are spawned by
the kernel in the initial pid namespace and help perform certain
asynchronous actions the kernel has to do, subject to process
scheduling. As far as I know, kernel threads never occur in a nested
pid namespace, and at least the parent process of kernel threads,
<code>kthreadd</code>, will always exist. Conveniently, it also always has pid 2.</p>
<p>Thus, we just need to figure out if pid 2 is a kernel thread! Note
that just checking whether pid 2 <em>exists</em> is cheap, but racy: the
container runtime could have spawned another process before we are
scheduled to do the check, and this process will as well get pid 2 then.</p>
<p>Luckily, kernel threads have quite a few special properties, that are
of different difficulty to check from a C program:</p>
<ul>
<li><code>/proc/PID/cmdline</code> is empty (not a good indicator, user space processes
can clear it too).</li>
<li>kernel threads have parent pid 0 (requires parsing <code>/proc/PID/stat</code>,
which <a href="https://unix.stackexchange.com/a/409493">everyone gets wrong</a>
the first time, or <code>/proc/PID/status</code>).</li>
<li>kernel threads have no <code>Vm*</code> data in <code>/proc/PID/status</code>.</li>
<li>kernel threads have the flag <code>PF_KTHREAD</code> set (requires parsing
<code>/proc/PID/stat</code> again).</li>
<li>kernel threads have an empty symlink for <code>/proc/PID/exe</code>.</li>
</ul>
<p>I decided to go with the latter. On Linux, empty symlinks are
impossible to create as a user, so we just need to check that and we’re
done, right?</p>
<p>On a regular file system, using lstat(2) would have filled <code>st_size</code>
with the length of the symlink. But on a <code>procfs</code>, lstat is not to be
trusted, and even non-empty symlinks have <code>st_size</code> equal to 0.
We thus really need to use the readlink(2) syscall to read the link.
After doing this, you will notice that it returns <code>ENOENT</code>… exactly
the same as if pid 2 <em>did not exist!</em></p>
<p>We therefore need another check, to verify that pid 2 <em>does exist.</em>
Luckily, here a lstat on <code>/proc/2/exe</code> file is fine. It must return zero.</p>
<p>Note that you need to do these operations in <em>exactly this order</em>, else
you are subject to race conditions again: the only reason this works
is that if pid 2 is <code>kthreadd</code>, it will not have terminated before the
lstat check (because it cannot terminate).</p>
<p>[Addendum 2023-09-17: vmann points out that this is still racy: a container
can spawn a new pid 2 between the lstat and the readlink call.
Please use one of the more complicated approaches mentioned above!]</p>
<p>Therefore, readlink(2) failing with <code>ENOENT</code> and lstat(2) succeeding
is exactly the combination required to check pid 2 is <code>kthreadd</code>,
which implies there are kernel threads in our pid namespace, which
implies that we are in the initial namespace.</p>
<p>Phew, this went deeper than expected.</p>
<p><small>NP: David Bowie—Lazarus</small></p>
Merry Christmas!Leah Neukirchenhttps://leahneukirchen.org/leah@vuxu.orgtag:chneukirchen@yahoo.de,2004-06:chris-blogs-x-20211224-1513242021-12-24T16:13:24+01:002021-12-24T16:13:24+01:00
<p style="margin: 1em; font-weight: bold; text-align: center">
<img width="500" alt="Picture of a cat in front of a christmas tree"
src="https://leahneukirchen.org/blog/graphics/xmas-21.jpg" />
</p>
<p style="margin: 1em; position: relative: top: 10px; font-size: 150%; font-weight: bold; text-align: center">
Frohe Weihnachten, ein schönes Fest, und einen guten Rutsch ins neue Jahr
wünscht euch<br>Leah Neukirchen
</p>
<p style="margin: 1em; font-size: 150%; font-weight: bold; text-align: center">
Merry Christmas and a Happy New Year!
</p>
<p><small>NP: Sade—Keep Looking</small></p>