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
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
|
<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-rc: FAQ</title>
<meta name="Description" content="s6-rc: FAQ" />
<meta name="Keywords" content="s6-rc faq frequently asked questions" />
<!-- <link rel="stylesheet" type="text/css" href="//skarnet.org/default.css" /> -->
</head>
<body>
<p>
<a href="index.html">s6-rc</a><br />
<a href="//skarnet.org/software/">Software</a><br />
<a href="//skarnet.org/">skarnet.org</a>
</p>
<h1> s6-rc: Frequently Asked Questions </h1>
<a name="quickrecipes">
<h2> Quick recipes </h2>
</a>
<h3> How do I... </h3>
<h4> list every active service ? </h4>
<pre>s6-rc -a list </pre>
<h4> list every service in the current live database, active or not ? </h4>
<pre>s6-rc -d list </pre>
<p>or</p>
<pre>s6-rc-db list services</pre>
<h4> bring up service <em>foo</em> ? </h4>
<pre>s6-rc -u change foo</pre>
<h4> bring down everything ? </h4>
<pre>s6-rc -da change</pre>
<h4> print the "up" script for oneshot service <em>foo</em> ? </h4>
<pre>s6-rc-db -u script foo | xargs -0 printf "%s "</pre>
<h4> see what pipeline longrun service <em>foo</em> is a part of ? </h4>
<pre>s6-rc-db pipeline foo</pre>
<h4> see the list of all services that depend on <em>foo</em>,
directly or indirectly ? </h4>
<pre>s6-rc-db -d all-dependencies foo</pre>
<h4> see what services will restart if I update my live service database to
<em>newcompiled</em> ? </h4>
<pre>s6-rc-update -n newcompiled</pre>
<p>
The first line is the <a href="s6-rc.html">s6-rc</a> invocation that
will bring the old services down. The services that will stop are listed
after <tt>-- change</tt>.
The second line is the <a href="s6-rc.html">s6-rc</a> invocation that
will bring the new services up. The services that will start are listed
after <tt>-- change</tt>.
</p>
<a name="sourceformat">
<h2> The s6-rc-compile source format </h2>
</a>
<h3> The source format for
<a href="s6-rc-compile.html">s6-rc-compile</a> is not very convenient.
Why not put all the information for a service in a single file ? </h3>
<p>
Because parsing sucks. Writing parsers is an annoying, ungrateful task,
with significant risks of bugs and security holes;
and automatic parser generators produce big and inefficient code - and
they are not immune to bugs or security holes either. For
security, efficiency and maintainability reasons, I prefer to focus my
efforts on code that actually does stuff, not code that parses a text
file.
</p>
<p>
Using the filesystem as a key-value store is
a good technique to avoid parsing, and skarnet.org packages do it
everywhere: for instance,
<a href="//skarnet.org/software/s6/s6-envdir.html">s6-envdir</a>
uses the file name as a key and the file contents as a value.
The s6-rc-compile source format is just another instance of this
technique.
</p>
<p>
The source format generally plays well with automated tools, be it for
reading, as s6-rc-compile does, or for writing.
I fully expect it
to be used as the input (resp. the output) of some automated tools that
would convert
service definitions to (resp. from) another format, such as systemd
unit files, sysv-rc scripts or OpenRC scripts; at least the
s6-rc source format will make it easy on those tools.
</p>
<p>
And if you love configuration files, are ok with writing a parser (which is
indubitably easier to do in other languages than C), and want to write
a program that takes a text file, parses it and outputs a service
definition directory in the s6-rc-compile source format, it should also be
rather easy - please, feel free!
</p>
<h3> There are no "Provides:", no virtual services. What do I do
if I have several implementations for a service ? </h3>
<p>
Use bundles. Bundles are the solution to most of the questions in
the same vein.
</p>
<p>
Let's say you want to provide a ssh daemon, and have two possible
implementations, <em>opensshd</em> and <em>dropbear</em>, but you
want to provide a virtual service named <em>sshd</em>.
</p>
<p>
Define your two longruns, <em>opensshd</em> and <em>dropbear</em>;
then define a bundle named <em>sshd</em> that only contains your
default implementation, <em>opensshd</em>. Use the name <em>sshd</em>
in your dependencies. When you run
<a href="s6-rc-compile.html">s6-rc-compile</a>, all the dependencies
will resolve to <em>opensshd</em>, and the compiled service database
will consider <em>opensshd</em> to be the "real" service; but users
will still be able to run
<a href="s6-rc.html">s6-rc</a> commands involving <em>sshd</em>.
And if you want to change the default to <em>dropbear</em>, just
change the <em>sshd</em><tt>/contents</tt> file to <tt>dropbear</tt>,
recompile the database, and
run <a href="s6-rc-update.html">s6-rc-update</a>.
</p>
<p>
The advantage of proceeding this way is that online service
dependencies are kept very simple: dependencies are a directed
acyclic graph, which is easy to handle - that is the reason why
the compiled database is small, and why the
<a href="s6-rc.html">s6-rc</a> program is so small and fast.
There are "AND" dependencies, but no "OR" dependencies, which
would introduce great complexity both in data structures and in
the dependency resolution engine. s6-rc handles this complexity
<em>offline</em>.
</p>
<p>
You can use bundles to represent any collection of services, and
write all your dependencies using only bundle names if you want.
Bundles have multiple uses, and virtual services are definitely
one of them.
</p>
<a name="compiledmanagement">
<h2> Managing compiled databases </h2>
</a>
<h3> How do I safely update my compiled database? It is a directory, so
I cannot atomically replace my old database with the new one. </h3>
<p>
Use symbolic links. You can atomically replace a symbolic link, and the
s6-rc programs have been designed with that mechanism in mind. Here is
the simplest way of managing your compiled databases: (change the names
according to your preferences, but keep the same idea)
</p>
<ul>
<li> Make sure your <a href="s6-rc-init.html">s6-rc-init</a> invocation,
in your init scripts, always uses the <tt>/etc/s6-rc/compiled</tt> name
to refer to the database to use at boot time. </li>
<li> Make sure <tt>/etc/s6-rc/compiled</tt> is a <em>symbolic link</em>
that points to your current compiled database. For instance, the first
time you compile a service database, you could run:
<pre>
s6-rc-compile /etc/s6-rc/compiled-initial /etc/s6-rc/source-initial
ln -sf compiled-initial /etc/s6-rc/compiled
</pre> </li>
<li> When you compile a new service database, always compile it to a
unique name, preferrably in the same directory as your current compiled
database. You can for instance use a TAI64N timestamp, obtained by
<a href="//skarnet.org/software/s6-portable-utils/s6-clock.html">s6-clock</a>,
to create such a name:
<pre>
stamp=`s6-clock`
s6-rc-compile /etc/s6-rc/compiled-$stamp /etc/s6-rc/source-current
</pre> </li>
<li> To replace your current compiled database with the one you just
created, run <a href="s6-rc-update.html">s6-rc-update</a> on the new
name:
<pre>
s6-rc-update /etc/s6-rc/compiled-$stamp
</pre> </li>
<li> At this point, your current database is the new one, but if
your machine reboots, it will still use the old one (because
the <tt>/etc/s6-rc/compiled</tt> link is still pointing to the old one).
To ensure that the
new database will be used on the next boot, atomically update the link:
<pre>
olddb=`s6-linkname -f /etc/s6-rc/compiled`
s6-ln -nsf compiled-$stamp /etc/s6-rc/compiled
</pre>
The use of the
<a href="//skarnet.org/software/s6-portable-utils/s6-ln.html">s6-ln</a>
utility is recommended, because the
<a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/ln.html">ln</a>
standard actually <em>forbids</em> an atomic replacement, so utilities that
follow it to the letter, for instance, <tt>ln</tt> from GNU coreutils, cannot
be atomic: they first remove the old link, then create the new one. If you
do not have
<a href="//skarnet.org/software/s6-portable-utils/s6-ln.html">s6-ln</a>,
you need to perform an unintuitive workaround to get POSIX-compliant tools to
do the right thing:
<tt>ln -sf compiled-$stamp /etc/s6-rc/compiled/compiled &&
mv -f /etc/s6-rc/compiled/compiled /etc/s6-rc</tt> </li>
<li> Your current compiled database, stored in
<tt>/etc/s6-rc/compiled-$stamp</tt>, is now accessible via the
<tt>/etc/s6-rc/compiled</tt> symlink and will be used at boot time. You do
not need to modify your init scripts. If you wish, you can dispose of the
old database you just replaced:
<pre>
rm -rf $olddb
</pre> </li>
</ul>
<a name="switching">
<h2> Switching from another service manager </h2>
</a>
<h3> I have a collection of init scripts in another format,
but don't want to wait until the whole collection is converted
before switching to s6-rc. Is there a smooth way in ? </h3>
<p>
Yes.
</p>
<p>
If you are using a service manager such as sysv-rc or OpenRC,
you have a collection of init scripts that can be called with
at least <tt>start</tt> and <tt>stop</tt> arguments. You also
know dependencies between those scripts, or at least a
reasonable ordering.
</p>
<p>
You can automatically generate a source directory for
<a href="s6-rc-compile.html">s6-rc-compile</a>. For every
init script <tt>/etc/init.d/<em>foo</em></tt> that you have,
create a service definition directory named <em>foo</em>:
</p>
<ul>
<li> <tt><em>foo</em>/type</tt> contains <tt>oneshot</tt> </li>
<li> <tt><em>foo</em>/dependencies</tt> contains the list of
dependencies for <em>foo</em> </li>
<li> <tt><em>foo</em>/up</tt> contains <tt>/etc/init.d/<em>foo</em> start</tt>
if you're using sysv-rc, or <tt>rc-service start foo -D -q</tt> if you're
using OpenRC </li>
<li> <tt><em>foo</em>/down</tt> contains <tt>/etc/init.d/<em>foo</em> stop</tt>
if you're using sysv-rc, or <tt>rc-service stop foo -D -q</tt> if you're
using OpenRC </li>
</ul>
<p>
You can now run compile your s6-rc service database, and use the
<a href="s6-rc.html">s6-rc</a> engine as your service manager.
Transitions will use your original init scripts, and the supervision
features of <a href="//skarnet.org/software/s6/">s6</a> will
not be used, but you will get proper dependency tracking and
easy state changes.
</p>
<p>
Then, you can improve the database by changing services one by one, turning
them into longruns so daemons get supervised when applicable, rewriting them
into bundles calling more atomic services if needed, etc. That can be done
at your own pace, one service at a time, while still getting some benefits
from s6-rc; and if an iteration doesn't work, you can always roll back while
you fix it.
</p>
<h3> There are no runlevels in s6-rc. I like runlevels. </h3>
<p>
You have better than runlevels. You have bundles.
</p>
<p>
When writing your service database in source format, take note of
the common sets of services that you like to run together, what
other init systems sometimes call runlevels. For each of those
sets, define a bundle containing all those services. For instance,
you could define a <tt>runlevel-1</tt> bundle that contains only
a single getty, a <tt>runlevel-2</tt> bundle that contains only
your local services and no network, a <tt>runlevel-3</tt> bundle
that contains <tt>runlevel-2</tt> as well as network services,
and a <tt>runlevel-5</tt> bundle that contains <tt>runlevel-3</tt>
and your desktop. You can even create a <tt>runlevel-0</tt>
bundle that contains nothing at all!
</p>
<p>
In your boot script (<tt>/etc/rc.init</tt>, for instance, if
you're using
<a href="//skarnet.org/software/s6-linux-init/">s6-linux-init</a>),
after invoking
<a href="s6-rc-init.html">s6-rc-init</a>, just ask
<a href="s6-rc.html">s6-rc</a> to start the set of services you want up
by default: <tt>s6-rc change runlevel-5</tt>.
</p>
<p>
If you later want to change your current set of services, you can then tell
s6-rc to switch, using the <tt>-p</tt> option to make sure to stop services
you don't want up anymore: <tt>s6-rc -p change runlevel-2</tt>.
</p>
<p>
Bundles are easy to use, they're flexible, and they're powerful.
They give you the same level of functionality as runlevels would, and more.
You can even add bundles to compiled service databases - including the
live one - or remove bundles from them without having to recompile them:
that's what the <a href="s6-rc-bundle.html">s6-rc-bundle</a> utility is
for.
</p>
<p>
When in doubt, use bundles.
</p>
<h3> There are no intermediate states in s6-rc. There's just "up"
and "down", no "starting", no "failed", etc. Why ? </h3>
<p>
Because those intermediate states are unnecessary.
</p>
<p>
From the machine's point of view, things are simple: a service is
either up or it's not. If a service fails to start, then it's still
down. Note that it is recommended to write <em>transactional</em> oneshots
for this very reason: it is simple to try starting again a service that
failed to start, but it is hard to recover from a service that is
only "partially up" - and this is true whether you're using s6-rc
or another service manager.
</p>
<p>
Service managers that use intermediate states do so in order to keep
track of what they're doing and what they have done. But this
introduces needless complexity: the reality is that the service is
either up or down, it's either in the state you wanted it to be or
not. If it's in some other, weird, state, then the service scripts
have not been properly designed - they are not transactional.
</p>
<p>
s6-rc does not keep track of "failed" states: a service that fails
to start simply remains down, and
<a href="s6-rc.html">s6-rc</a> exits 1 to report that something
went wrong. To know what services failed to start, compare the
result of <tt>s6-rc -a list</tt> against your expected machine state.
</p>
<p>
The reason for this design is simple: if the
<a href="s6-rc.html">s6-rc</a> process is killed in the middle of a transition
while a service state is "starting", what should the next invocation do?
This is unclear, and the intermediate state introduces ambiguity where
there should not be. Also,
if there is a "failed" service, what should the next invocation do? Try
and restart it, or not? This depends on what the user wants; this is
policy, not mechanism. Simply reporting the error while keeping the
state as "down" allows users to apply their chosen policies - see below.
</p>
<p>
Keep it simple, stupid.
</p>
<a name="nopolicy">
<h2> Mechanism vs. policy </h2>
</a>
<h3> s6-rc feels bare: there are tools, but no wrappers, no pre-packaged
scripts to boot my machines, no default runlevels. By comparison, OpenRC
provides a complete default set of scripts ! </h3>
<p>
In the world of software development, it is important to distinguish
<em>mechanism</em> from <em>policy</em>. Mechanism is "how do I perform
the job", and should, theoretically, be addressed by software authors.
Policy is "what are the details of the job I perform, where should I
put my files, what conventions do I use", and should, theoretically, be
addressed by <em>Unix distributions</em>.
</p>
<p>
Like the rest of <a href="//skarnet.org/software/">skarnet.org
software</a>, s6-rc aims to provide <em>mechanism, not policy</em>:
it is OS-agnostic and distribution-agnostic. Providing boot scripts,
or anything of this kind, would go against this principle; it is
possible that a policy defined by software conflicts with a
policy defined by a distribution, for instance the provided boot
scripts do not match the distribution's needs, and so the distributors
have to patch the software!
</p>
<p>
The s6-rc tools only provide mechanism, so they can be used as is by
individual users, or by a distribution. They do not need to be patched.
It is up to distributions to provide their own policy surrounding those
tools, including complete service databases. It is literally the
distributors' job!
</p>
<p>
OpenRC is a different case, because it was developed <em>by</em> and
<em>for</em> a Linux distribution, so with that in mind, the OpenRC
developers did not have to think much about separating mechanism from policy.
It works very well for Gentoo and Gentoo-derived distributions; but it
requires adaptation and more work for the admin to use OpenRC outside of
that frame.
</p>
</body>
</html>
|