about summary refs log tree commit diff
path: root/doc/s6-linux-init-maker.html
blob: b7648ed23ba0c603576760e2d096f4f867b78e9a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
<html>
  <head>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <meta http-equiv="Content-Language" content="en" />
    <title>s6-linux-init: the s6-linux-init-maker program</title>
    <meta name="Description" content="s6-linux-init: the s6-linux-init-maker program" />
    <meta name="Keywords" content="s6 linux administration root init maker" />
    <!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
  </head>
<body>

<p>
<a href="index.html">s6-linux-init</a><br />
<a href="//skarnet.org/software/">Software</a><br />
<a href="//skarnet.org/">skarnet.org</a>
</p>

<h1> The <tt>s6-linux-init-maker</tt> program </h1>

<p>
<tt>s6-linux-init-maker</tt> reads configuration options on
the command line, and outputs a directory to place in the
root filesystem. That directory contains a script suitable
as an init program, as well as support file hierarchies to
get a complete
<a href="//skarnet.org/software/s6/">s6</a>
infrastructure running when the system is booted on that
script.
</p>

<p>
 s6-linux-init-maker only writes scripts. At boot time, these
scripts will call commands provided by other skarnet.org packages
such as
<a href="//skarnet.org/software/execline/">execline</a> or
<a href="//skarnet.org/software/s6/">s6</a>. It is the
responsibility of the administrator to make sure that all the
dependencies are properly installed at boot time, and that the
correct options have been given to s6-linux-init-maker so that
the programs are found <em>on the root filesystem of the
machine</em> - else the scripts will crash.
</p>

</p>

<h2> Interface and usage </h2>

<pre>
     s6-linux-init-maker \
       [ -c <em>basedir</em> ] \
       [ -l <em>tmpfsdir</em> ] \
       [ -b <em>execline_bindir</em> ] \
       [ -u <em>log_uid</em> -g <em>log_gid</em> | -U ] \
       [ -G <em>early_getty</em> ] \
       [ -2 <em>stage2</em> ] \
       [ -r ] \
       [ -Z ] <em>stage2_finish</em> \
       [ -3 <em>stage3</em> ] \
       [ -p <em>initial_path</em> ] \
       [ -m <em>initial_umask</em> ] \
       [ -t <em>timestamp_style</em> ] \
       [ -d <em>dev_style</em> ] \
       [ -s <em>env_store</em> ] \
       [ -e <em>initial_envvar</em> ] ... \
       <em>dir</em>
</pre>

<ul>
 <li> s6-linux-init-maker should be run as root. </li>
 <li> s6-linux-init-maker parses options on its command line. </li>
 <li> It writes data into a directory <em>dir</em>, which must not
exist beforehand. </li>
 <li> It exits 0 if everything went well, 100 if a user error occurred,
and 111 if a problem occurred during the creation of the directory
or its contents. </li>
</ul>

<p>
 <em>dir</em> should then be copied by the administrator to the place
declared as <em>basedir</em>. Be careful: it contains fifos, files with
precise uid/gid permissions, and files with non-standard access rights,
so be sure to copy it verbatim. The
<a href="//skarnet.org/software/s6-portable-utils/s6-hiercopy.html">s6-hiercopy</a>
tool can do it, as well as the GNU or busybox <tt>cp -a</tt> or <tt>mv</tt> commands.
</p>

<p>
 The <tt><em>basedir</em>/init</tt> script
is then suitable as a "stage 1" init program, i.e. the first program
run by the kernel. The administrator should make a symbolic link
from <tt>/sbin/init</tt> to <tt><em>basedir</em>/init</tt>; the
machine will then be ready to boot 
</p>

<h2> Boot sequence </h2>

<p>
 When the kernel boots, it runs the <tt><em>basedir</em>/init</tt> script,
also known as <em>stage 1</em>. and this is what happens:
</p>

<ul>
 <li> <em>stage 1</em> is an
<a href="//skarnet.org/software/execline/">execline</a> script, so
the first process run by the kernel is the
<a href="//skarnet.org/software/execline/execlineb.html">execlineb</a>
program launcher. </li>
 <li> <em>stage 1</em> mounts a
<a href="https://www.kernel.org/doc/Documentation/filesystems/tmpfs.txt">tmpfs</a>
filesystem on <em>tmpfsdir</em>. </li>
 <li> <em>stage 1</em> copies <tt><em>basedir</em>/run-image</tt> verbatim to
<em>tmpfsdir</em>. </li>
 <li> <em>stage 1</em> empties its environment, then reads a global set of environment variables from the
<tt><em>basedir</em>/env</tt>
<a href="//skarnet.org/software/s6/s6-envdir.html">environment directory</a>. </li>
 <li> <em>stage 1</em> forks a child that will block until
<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a> is running. </li>
 <li> <em>stage 1</em> executes, as process 1, into
<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>,
with <tt><em>tmpfsdir</em>/service</tt> as a
<a href="//skarnet.org/software/s6/scandir.html">scan directory</a>. </li>
 <li> This scan directory already contains at least one service, which is the
<em>catch-all logger</em>: error messages from the supervision tree, and
from services that do not have a dedicated logger, are handled by a
special <a href="//skarnet.org/software/s6/s6-log.html">s6-log</a>
instance and made available in <tt><em>tmpfsdir</em>/uncaught-logs</tt>
instead of clogging the system console. </li>
 <li> If the <tt>-G</tt> option has been given to s6-linux-init-maker, the
scan directory will also contain a service for an early getty. </li>
 <li> s6-svscan starts all the services defined in the scan directory,
and unblocks the child forked by <em>stage 1</em>. </li>
 <li> This child executes into <em>stage2</em>. </li>
</ul>

<p>
 <em>stage2</em> is the responsibility of the administrator - it will
not be written automatically!
It should
contain all the necessary initialization sequence to bring up a proper
system. When <em>stage2</em> is executed, the machine state is as follows:
</p>

<ul>
 <li> <em>stage2</em>'s working directory is <tt>/</tt> and its stdin
is <tt>/dev/null</tt>. Its
stdout and stderr both point either to <tt>/dev/console</tt> or to the pipe
to the catch-all logger, depending on the <tt>-r</tt> option. </li>
 <li> The system has a valid device directory mounted on <tt>/dev</tt>. </li>
 <li> Depending on the kernel boot command line, the root filesystem
may be in read-only mode. </li>
 <li> There is a tmpfs available for root only in <em>tmpfsdir</em>. </li>
 <li> <a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
is running as process 1. At any time, it is possible to make it supervise a long-lived
process by linking the appropriate
<a href="//skarnet.org/software/s6/servicedir.html">service directory</a>
into <tt><em>tmpfsdir</em>/service</tt>, then running the command
<tt>s6-svscanctl -a <em>tmpfsdir</em>/service</tt>. Services without a
dedicated logger will send their output to the catch-all logger. </li>
 <li> A getty service may already be available. The point of this early
getty is essentially to make it easier to debug if <em>stage2</em> fails. </li>
</ul>

<p>
 There is <em>nothing else</em>. In particular, no filesystem has been
mounted yet, including <tt>/proc</tt> and <tt>/sys</tt>; and no one-time
initialization
has been performed. The point of <em>stage 1</em> is only to make it
possible to run <em>stage2</em> with a logging infrastructure and a
supervision infrastructure already available, and all the
real machine and service initialization should happen in <em>stage2</em>.
</p>

<h2> Shutdown sequence </h2>

<ul>

 <li> A shutdown is performed when the administrator runs one of the
<a href="s6-halt.html">s6-halt</a>,
<a href="s6-poweroff.html">s6-poweroff</a> or
<a href="s6-reboot.html">s6-reboot</a> commands. </li>

 <li> Those commands send a signal to the
<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
process running as pid 1; this signal is caught and s6-svscan runs the
corresponding "signal handler" script that has been placed by
s6-linux-init-maker into the
<tt><em>basedir</em>/run-image/service/.s6-svscan</tt> directory (and that
has been copied at boot time to <tt><em>tmpfsdir</em>/service/.s6-svscan</tt>). </li>

 <li> That script first spawns the <em>stage2_finish</em> script, who
must have been written by the administrator. The purpose of
<em>stage2_finish</em> is to perform the high-level shutdown sequence
while the supervision tree is still alive. Typically, when using a
service manager, <em>stage2_finish</em> would tell the service manager
to bring all services down. When using
<a href="//skarnet.org/software/s6-rc/">s6-rc</a>, a typical
<em>stage2_finish</em> script just contains <tt>s6-rc -da change</tt>.
 More generally speaking, <em>stage2_finish</em> should undo what
<em>stage2</em> has done at boot time. </li>

 <li> The "signal handler" script then tells s6-svscan to exit via an
appropriate <a href="//skarnet.org/software/s6/s6-svscanctl.html">s6-svscanctl</a>
command: s6-svscan then executes into the <em>stage3</em> script, which, like
<em>stage2</em> and <em>stage2_finish</em>, is the responsibility of the
administrator. When <em>stage3</em> runs, the machine is in the following
state:

<ul>
 <li> The supervision tree has been torn down: it is not operational
anymore. (So, commands such as
<a href="//skarnet.org/software/s6-rc/s6-rc.html">s6-rc</a>, which
require a live supervision tree, will not work.)
 <li> <em>stage3</em> runs as process 1. Doing so makes it easier to recover
after killing all processes by <tt>kill -9 -1</tt> or
<a href="//skarnet.org/software/s6-portable-utils/s6-nuke.html">s6-nuke</a>. </li>
 <li> Its working directory is <tt>/</tt> and its stdin is <tt>/dev/null</tt> </li>
 <li> Its stdout and stderr are both <tt>/dev/console</tt> </li>
 <li> Depending on the exact configuration and what the administrator has
written in <em>stage2_finish</em>, there may or may not be
long-running services that remain alive. The catch-all logger and its
supervisor will <em>always</em> be alive; this is not a problem because they
do not hold any file descriptor to a filesystem that would need to be
unmounted. </li>
</ul> </li>

<li> The last command that <em>stage3</em> executes should be
<tt>s6-$1 -f</tt>, <tt>$1</tt> being the first argument that has been
given to it. This command will instantly execute the hard system halt,
poweroff or reboot that has initially been asked by the admin. </li>

</ul>

<p>
 The <tt>examples/</tt> subdirectory of the s6-linux-init package
contains an example of <tt>/etc/rc.init</tt>, <tt>/etc/rc.tini</tt>
and <tt>/etc/rc.shutdown</tt> scripts, suitable for
<em>stage2</em>, <em>stage2_finish</em> and <em>stage3</em>
respectively. Those scripts can practically be used as is if the machine
is managed by the <a href="//skarnet.org/software/s6-rc/">s6-rc</a>
service manager.
</p>

<h2> s6-linux-init-maker options </h2>

<ul>
 <li> <tt>-c</tt>&nbsp;<em>basedir</em>&nbsp;: at boot time, <em>stage 1</em>,
which should be accessible as <tt><em>basedir</em>/init</tt>,
will read its read-only data from <em>basedir</em>. After running
s6-linux-init-maker, the administrator should make sure to copy the
created directory <em>dir</em> to <em>basedir</em>. <em>basedir</em>
must be absolute. Default is
<strong><tt>/etc/s6-linux-init</tt></strong>. </li> <p />

 <li> <tt>-l</tt>&nbsp;<em>tmpfsdir</em>&nbsp;: at boot time, a tmpfs will
be mounted on <em>tmpfsdir</em>. The directory should already exist in
the root filesystem, and be empty. <em>tmpfsdir</em> must be absolute. Default is
<strong><tt>/run</tt></strong>. </li> <p />

 <li> <tt>-b</tt>&nbsp;<em>execline_bindir</em>&nbsp;: init is run by the kernel
without a PATH, and since it is a script, it is necessary to tell it where
to find the
<a href="//skarnet.org/software/execline/execlineb.html">execlineb</a>
launcher and the first few early commands before PATH can be set.
<em>execline_bindir</em> is the location where the execline binaries can be
found. It must be absolute. Default is
<strong><tt>/bin</tt></strong>. </li> <p />

 <li> <tt>-u</tt>&nbsp;<em>log_uid</em>&nbsp;: the catch-all
logger will run with the uid <em>log_uid</em>. Default is 0. <li> <p />

 <li> <tt>-g</tt>&nbsp;<em>log_gid</em>&nbsp;: the catch-all
logger will run with the gid <em>log_gid</em>. Default is 0. <li> <p />

 <li> <tt>-U</tt>&nbsp;: the correct <em>log_uid</em> and
<em>log_gid</em> values for the catch-all logger will be read from the
UID and GID environment variables that have been passed to
s6-linux-init-maker. This allows for invocations such as
<tt>s6-envuidgid nobody s6-linux-init-maker -U ...</tt> so that
the catch-all logger runs as the <tt>nobody</tt> user. Be aware that
this option is only safe when the user database on the
<em>boot-time</em> machine is the same as on the <em>run-time</em>
machine, else the catch-all logger may run with an unexpected uid
and gid. </li>

 <li> <tt>-G</tt>&nbsp;<em>early_getty</em>&nbsp;: if this option
is set, s6-linux-init-maker will define a service that will run
very early, before <em>stage2</em> is executed. This early service
should be a getty, to allow logins even if <em>stage2</em> fails.
<em>early_getty</em> should be a simple command line: for instance,
<tt>"/sbin/getty 38400 tty1"</tt>. By default, no early service
is defined. </li> <p />

 <li> <tt>-2</tt>&nbsp;<em>stage2</em>&nbsp;: <em>stage2</em> is
the location of the stage 2 script that will be run when the
system has an operational supervision tree. It must be absolute. Default is
<strong><tt>/etc/rc.init</tt></strong>. </li> <p />

 <li> <tt>-r</tt>&nbsp;: redirect. By default, <em>stage2</em> is
run with stdout and stderr pointing to <tt>/dev/console</tt>, so that
users can see what init scripts print. However, it may conflict
with an early getty, or be undesirable for other reasons. The
<tt>-r</tt> option redirects <em>stage2</em>'s stdout and stderr
to the catch-all logger, so the output will be made available
in the <tt><em>tmpfsdir</em>/uncaught-logs</tt> directory. </li> <p />

 <li> <tt>-Z</tt>&nbsp;<em>stage2_finish</em>&nbsp;:
<em>stage2_finish</em> is the location of the script that will be
run when s6-svscan receives a signal that tells it to stop the
machine, before it executes into <em>stage3</em>. It must be
absolute. Default is <strong><tt>/etc/rc.tini</tt></strong>.
Note that this script is run with its stdout and stderr
redirected to the <tt><em>tmpfsdir</em>/uncaught-logs</tt> logging
directory, so its output will not appear on the system's console. </li> <p />

 <li> <tt>-3</tt>&nbsp;<em>stage3</em>&nbsp;: <em>stage3</em> is
the location of the stage 3 script that will be run at the end of
the machine lifetime, when s6-svscan is told to terminate.
It must be absolute. Default is
<strong><tt>/etc/rc.shutdown</tt></strong>. </li> <p />

 <li> <tt>-p</tt>&nbsp;<em>initial_path</em>&nbsp;: the value to
set the PATH environment variable to, for all the starting processes.
This will be done as early as possible in <em>stage 1</em>. It is
absolutely necessary for
<a href="//skarnet.org/software/execline/">execline</a>,
<a href="//skarnet.org/software/s6/">s6</a>,
<a href="//skarnet.org/software/s6-portable-utils/">s6-portable-utils</a> and
<a href="//skarnet.org/software/s6-linux-utils/">s6-linux-utils</a>
binaries to be accessible via <em>initial_path</em>, else the machine
will not boot. Default is
<strong><tt>/usr/bin:/bin</tt></strong>. </li> <p />

 <li> <tt>-m</tt>&nbsp;<em>initial_umask</em>&nbsp;: the value of
the initial file umask for all the starting processes, in octal.
Default is
<strong><tt>022</tt></strong>. </li> <p />

 <li> <tt>-t</tt>&nbsp;<em>timestamp_style</em>&nbsp;: how
logs are timestamped by the catch-all logger. 0 means no
timestamp, 1 means
<a href="http://cr.yp.to/libtai/tai64.html">external TAI64N format</a>,
2 means
<a href="http://www.iso.org/iso/home/standards/iso8601.htm">ISO 8601 format</a>,
and 3 means both. Default is
<strong><tt>1</tt></strong>. </li> <p />

 <li> <tt>-d</tt>&nbsp;<em>dev_style</em>&nbsp;: how <tt>/dev</tt> is
handled on this system. 0 means a static <tt>/dev</tt>, 1 means
devtmpfs but not automounted by the kernel at boot time, and 2 means
devtmpfs automounted by the kernel at boot time. Default is
<strong><tt>2</tt></strong>. </li> <p />

 <li> <tt>-s</tt>&nbsp;<em>env_store</em>&nbsp;: stage 1 init sometimes
inherits a few environment variables from the kernel. It empties its
environment before spawning stage2 and executing into s6-svscan, in
order to prevent those "kernel" environment variables from leaking
into the whole process tree. However, sometimes those variables are
needed at a later time; in that case, giving the <tt>-s</tt> option
to s6-linux-init-maker makes stage 1 init dump the "kernel" environment
variables into the <em>env_store</em> directory, via the
<a href="//skarnet.org/software/s6-portable-utils/s6-dumpenv.html">s6-dumpenv</a>
program, before erasing them. <em>env_store</em> should obviously be
a writable directory, so it should be located under <em>tmpfsdir</em>!
If this option is not given (which is the default), the environment
inherited from the kernel isn't saved anywhere. </li> <p />

 <li> <tt>-e</tt>&nbsp;<em>initial_envvar</em>&nbsp;: this option
can be repeated. For every <em>initial_envvar</em>, s6-linux-init-maker
will adjust the global environment directory in <em>dir</em>/env.
<em>initial_envvar</em> must either be of the form <em>VAR</em>,
to make sure that <em>VAR</em> does not appear in the global
environment, or of the form <em>VAR=VALUE</em>, to add an
environment variable <em>VAR</em> with the value <em>VALUE</em>.
The TZ variable, for instance, is a good candidate to be set in
the global environment. </li> <p />
</ul>

<h2> Notes </h2>

<p>
 The difficult parts of
<a href="//skarnet.org/software/s6/s6-svscan-1.html">running
s6-svscan as process 1</a> are:
</p>

<ul>
 <li> The fact that the supervision tree requires writable directories,
so in order to accommodate read-only root filesystems, there needs to
be a tmpfs mounted before s6-svscan is run. </li>
 <li> The catch-22 coming for the need to redirect the supervision
tree's output away from <tt>/dev/console</tt> (which is fine for a
first process invocation but impractical for log management of a
whole process tree) and into a logger that is itself managed by the
supervision tree it's reading data from. </li>
</ul>

<p>
 The main benefit of s6-linux-init-maker is that it automates those
parts. This means that it has been designed for <em>real hardware</em>
where the above issues apply.
 If you are building an init system for a
virtual machine, a container, or anything similar that does not
have the <tt>/dev/console</tt> issue or the read-only rootfs issue,
you will probably not reap much benefit from using s6-linux-init-maker:
you could probably invoke
<a href="//skarnet.org/software/s6/s6-svscan.html">s6-svscan</a>
directly as your process 1, or build a script by hand, which
would result in a simpler init with less dependencies.
</p>

</body>
</html>