From 09750ce3525129a676dec4f579aea3ad2eca1b19 Mon Sep 17 00:00:00 2001
From: Laurent Bercot
Date: Mon, 9 Jan 2023 11:09:14 +0000
Subject: Add instances implementation (still needs testing)
Signed-off-by: Laurent Bercot
---
doc/index.html | 10 ++
doc/instances.html | 121 ++++++++++++++++++
doc/s6-instance-control.html | 65 ++++++++++
doc/s6-instance-create.html | 92 ++++++++++++++
doc/s6-instance-delete.html | 72 +++++++++++
doc/s6-instance-maker.html | 203 ++++++++++++++++++++++++++++++
doc/s6-log.html | 2 +-
doc/s6-svc.html | 7 +-
doc/s6-usertree-maker.html | 6 +-
doc/servicedir.html | 7 ++
package/deps.mak | 9 ++
package/modes | 3 +
package/targets.mak | 5 +-
src/instance/deps-exe/s6-instance-control | 1 +
src/instance/deps-exe/s6-instance-create | 2 +
src/instance/deps-exe/s6-instance-delete | 2 +
src/instance/s6-instance-control.c | 79 ++++++++++++
src/instance/s6-instance-create.c | 97 ++++++++++++++
src/instance/s6-instance-delete.c | 61 +++++++++
src/instance/s6-instance-maker.c | 2 +-
src/libs6/s6_supervise_link_names.c | 3 +-
src/supervision/s6-svc.c | 45 +++++--
22 files changed, 881 insertions(+), 13 deletions(-)
create mode 100644 doc/instances.html
create mode 100644 doc/s6-instance-control.html
create mode 100644 doc/s6-instance-create.html
create mode 100644 doc/s6-instance-delete.html
create mode 100644 doc/s6-instance-maker.html
create mode 100644 src/instance/deps-exe/s6-instance-control
create mode 100644 src/instance/deps-exe/s6-instance-create
create mode 100644 src/instance/deps-exe/s6-instance-delete
create mode 100644 src/instance/s6-instance-control.c
create mode 100644 src/instance/s6-instance-create.c
create mode 100644 src/instance/s6-instance-delete.c
diff --git a/doc/index.html b/doc/index.html
index c0c8403..8b87d71 100644
--- a/doc/index.html
+++ b/doc/index.html
@@ -271,6 +271,16 @@ synchronization.
The s6-usertree-maker program
+ Management of dynamic instances
+
+
+
Timed lock acquisition
diff --git a/doc/instances.html b/doc/instances.html
new file mode 100644
index 0000000..0586fdb
--- /dev/null
+++ b/doc/instances.html
@@ -0,0 +1,121 @@
+
+
+
+
+
+ s6: dynamic instantiation
+
+
+
+
+
+
+
+s6
+Software
+skarnet.org
+
+
+ Dynamic instantiation under s6
+
+
+ A instanced service is a parameterized service that you want to
+run several copies of, with only the parameter changing. Each copy of the
+service is called an instance.
+
+
+
+ With s6, a service directory can only
+handle one process at a time. So, if we want instanced services, there
+will have to be one service directory per instance, always.
+
+
+
+ Static instantiation means that the set of possible instances
+is finite and known in advance. With s6, it means that all the service
+directories for all possible instances are created, typically by a
+preprocessor, and instances are treated like regular services.
+
+
+
+ Dynamic instantiation means that instances are created
+on demand instead of preallocated. Starting with version 2.11.2.0, s6
+provides a few tools to help users set up and manage dynamically
+instanced services.
+
+
+ How to make a dynamically instanced service under s6
+
+
+ - Write a template for a service directory that would run under
+s6-supervise.
+The run script should take the name of the instance as its
+first argument; the finish script should take the name of the
+instance as its third argument.
+ - Call the s6-instance-maker program
+with this template as first argument, and a path dir as second
+argument. s6-instance-maker will create
+a service directory in dir. This is an offline tool: it does not
+interact with any currently active services or supervision trees.
+ - Supervise dir by adding it to your regular
+scan directory. This will be your instanced
+service, but it's not running any instances yet. It is, instead, a nested
+supervision tree - the instanced service is an
+s6-svscan process that will supervise all the
+instances.
+ - Create and delete instances at will with the
+s6-instance-create and
+s6-instance-delete programs. Instances
+are regular supervised processes; you can control them with
+s6-instance-control. These tools are
+online: they work with live service directories (i.e. that are
+being supervised by s6-supervise). They
+are really syntactic sugar around the
+s6-svlink,
+s6-svunlink and
+s6-svc programs; they provide you with the
+same functionality but allow you to address individual instances via the
+instanced service name (the service directory running the nested
+supervision tree) and the instance name.
+
+
+ Internal workings
+
+
+This section is not normative; users should not rely on it. It is only
+here for informational purposes.
+
+
+
+ - The service directory created by s6-instance-maker
+has two specifics subdirectories in it: instance and instances. They
+are initially empty, except that the template service directory is stored in
+instances/.template.
+ - When the service is active, there is an s6-svscan
+process running on instance.
+ - s6-instance-create makes a copy of
+instances/.template into instances/name/tt>, and
+s6-svlinks instances/name/tt> to
+instance. When it returns, there is an s6-supervise
+process running on instance/name, and the instance may be up
+or not depending on the given options.
+ - s6-instance-control is syntactic sugar
+around s6-svc on instance/name.
+ - s6-instance-delete is syntactic sugar
+around s6-svunlink on instance/name.
+
+
+ Notes
+
+
+ - This implementation of dynamic instances may seem expensive: it
+creates one s6-svscan process per
+instanced service, and one s6-supervise
+process per instance. However, remember that these processes use very
+little private memory, so having additional copies of them is far less
+expensive than it looks. It's really a convenient way to implement the
+feature by reusing existing code.
+
+
+
+
diff --git a/doc/s6-instance-control.html b/doc/s6-instance-control.html
new file mode 100644
index 0000000..cd76688
--- /dev/null
+++ b/doc/s6-instance-control.html
@@ -0,0 +1,65 @@
+
+
+
+
+
+ s6: the s6-instance-control program
+
+
+
+
+
+
+
+s6
+Software
+skarnet.org
+
+
+ The s6-instance-control program
+
+
+s6-instance-control sends commands to a running instance of an
+instanced service.
+
+
+ Interface
+
+
+ s6-instance-control [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyoduDUxOr ] servicedir name
+
+
+
+ - s6-instance-control expects a running, supervised
+instanced service in servicedir,
+as well as an existing instance of this service named name.
+ - It sends the given series of commands to the supervisor monitoring
+the name instance.
+ - It exits 0.
+
+
+ Exit codes
+
+
+ - 0: success
+ - 99: with one of the -w options, timed out while waiting for the command to complete
+ - 100: wrong usage
+ - 111: system call failed
+
+
+ Options
+
+
+ The options, and the commands they represent, are exactly the same as the ones
+understood by s6-svc.
+
+
+ In fact, s6-instance-control is
+nothing more than a call to s6-svc on the service
+directory representing the name instance. It is syntactic sugar so
+the user does not have to depend on the internal representation of instances
+and the location of instances' service directories.
+
+
+
+
diff --git a/doc/s6-instance-create.html b/doc/s6-instance-create.html
new file mode 100644
index 0000000..c81dfe4
--- /dev/null
+++ b/doc/s6-instance-create.html
@@ -0,0 +1,92 @@
+
+
+
+
+
+ s6: the s6-instance-create program
+
+
+
+
+
+
+
+s6
+Software
+skarnet.org
+
+
+ The s6-instance-create program
+
+
+s6-instance-create creates a new instance of a currently supervised
+instanced service.
+
+
+ Interface
+
+
+ s6-instance-create [ -d | -D ] [ -P ] [ -f ] [ -t timeout ] servicedir name
+
+
+
+ - s6-instance-create expects a running, supervised
+instanced service in servicedir.
+This service typically has been created by linking the result of an
+s6-instance-maker invocation into
+an existing scan directory.
+ - s6-instance-create creates a new instance of that service, named
+name. Depending on the given options, it may start it
+immediately, or keep it down until a later
+s6-instance-control invocation.
+ - It waits for the new instance to be ready to take commands from
+s6-instance-control.
+ - It exits 0.
+
+
+ Exit codes
+
+
+ - 0: success
+ - 99: timeout while waiting for the instance supervisor to start
+ - 100: wrong usage
+ - 111: system call failed
+
+
+ Options
+
+
+ - -d : down. The instance supervisor will be started, but the instance
+itself will remain down. Any down file for the instance will be
+deleted. By default, if neither the -d nor -D options have
+been given, the supervisor auto-starts the instance as soon as it runs.
+ - -D : down, and stay down. The instance supervisor will be started,
+but the instance itself will remain down. A down file
+will be created for the instance. By default, if neither the -d nor -D options have
+been given, the supervisor auto-starts the instancece as soon as it runs.
+ - -P : public. Everyone will be able to subscribe to the
+instance supervisor's notification. By default, only processes running with the same gid
+as the instanced service can subscribe to it.
+ - -f : force permissions. You should never need to use this
+option, it is only there for testing purposes.
+ - -t timeout : if the instance supervisor has not started
+after timeout milliseconds, s6-instance-create will print a message
+to stderr and exit 99. By default, timeout is 0, which means no time
+limit.
+
+
+ Notes
+
+
+ - s6-instance-create is similar to
+s6-svlink, because it uses the same underlying
+library functions. Under the hood, an instance is a regular service running
+on a supervision tree that is specific to the instanced service, and
+s6-instance-create adds a service directory to that tree and ensures it gets
+supervised.
+ - If the template for the service is logged, then s6-instance-create will
+wait until supervisors have been spawned for both the instance and its logger.
+
+
+
+
diff --git a/doc/s6-instance-delete.html b/doc/s6-instance-delete.html
new file mode 100644
index 0000000..28eca9c
--- /dev/null
+++ b/doc/s6-instance-delete.html
@@ -0,0 +1,72 @@
+
+
+
+
+
+ s6: the s6-instance-delete program
+
+
+
+
+
+
+
+s6
+Software
+skarnet.org
+
+
+ The s6-instance-delete program
+
+
+s6-instance-delete deletes an existing instance of a currently supervised
+instanced service.
+
+
+ Interface
+
+
+ s6-instance-delete [ -X ] [ -t timeout ] servicedir name
+
+
+
+ - s6-instance-delete expects a running, supervised
+instanced service in servicedir,
+as well as an existing instance of this service named name
+(it doesn't matter if the instance is up or down).
+ - It deletes the name instance.
+ - It exits 0.
+
+
+ Exit codes
+
+
+ - 0: success
+ - 100: wrong usage
+ - 111: system call failed
+
+
+ Options
+
+
+ - -X : don't wait. s6-instance-delete will exit right away,
+without waiting for the instance (and its supervisor) to properly disappear.
+ - -t timeout : if the instance supervisor has not exited
+after timeout milliseconds, s6-instance-delete will still exit.
+By default, timeout is 0, which means no time limit.
+
+
+ Notes
+
+
+ - s6-instance-delete is similar to
+s6-svunlink, because it uses the same underlying
+library functions. Under the hood, an instance is a regular service running
+on a supervision tree that is specific to the instanced service, and
+s6-instance-delete removes a service directory from that tree.
+ - If the template for the service is logged, then s6-instance-delete will
+delete both the instance and its logger.
+
+
+
+
diff --git a/doc/s6-instance-maker.html b/doc/s6-instance-maker.html
new file mode 100644
index 0000000..2eb0f4e
--- /dev/null
+++ b/doc/s6-instance-maker.html
@@ -0,0 +1,203 @@
+
+
+
+
+
+ s6: the s6-instance-maker program
+
+
+
+
+
+
+
+s6
+Software
+skarnet.org
+
+
+ The s6-instance-maker program
+
+
+s6-instance-maker creates a service directory
+implementing an instanced service. Give it a
+templated service directory describing how to run an instance of a service,
+and it will create a different service directory that can launch and
+manage several instances; each of these instances will be running a
+copy of the service directory you gave.
+
+
+
+ Alternatively, s6-instance-maker can create source definition directories
+for the s6-rc service manager.
+
+
+ Interface
+
+
+ s6-instance-maker \
+ [ -c maxinstances ] \
+ [ -r service[/logger[/pipeline]] ] \
+ [ -u user ] \
+ [ -l loguser ] \
+ [ -L logdir ] \
+ [ -t stamptype ] \
+ [ -n nfiles ] \
+ [ -s filesize ] \
+ [ -S maxsize ] \
+ [ -P prefix ] \
+ template dir
+
+
+
+s6-instance-maker creates a service directory in dir. The
+created service will be a supervisor for a set of instances (initially empty)
+each running a copy of the service directory given in template.
+
+
+
+s6-instance-maker is an offline tool: it is run before you need
+instances. Once the created service directory is live, i.e. there is a
+supervisor running on it, then you can create, delete, or control
+individual instances via the
+s6-instance-create,
+s6-instance-delete and
+s6-instance-control online
+tools, that work with active services.
+
+
+ Exit codes
+
+
+ - 0: success
+ - 100: wrong usage
+ - 111: system call failed
+
+
+ Options
+
+
+ - -c max : Plan for a maximum of max
+instances. Default is 500. You can't set it lower than 2 or
+higher than 90000. If your template service directory is logged, it's unadvisable
+to set this above the default.
+
+ - -r service[/logger[/pipeline]] :
+create s6-rc source definition directories.
+When this option is given, dir is not created as a service directory, but
+as a directory containing at least one service: dir/service.
+dir is suitable as a source argument to
+s6-rc-compile. If
+a logger part is given, then a second service, dir/logger,
+is also created, as a consumer for dir/service, and the -L
+option must also be used, to provide a directory to store the logs into. If the
+/pipeline part is also given, pipeline
+is used as a name for a bundle containing both service and logger.
+When the -r option is not given at all, dir is a regular service
+directory for direct inclusion (or linking) in a host
+scan directory, and if the -L option is given
+then the logger for the instance supervisor and all its instances is declared in
+dir/log).
+
+ - -u user : run the instance supervisor, and all
+of the instances, as user user. This option should only be used when the
+supervision tree that will host the instanced service is run as root. The default
+is that the service runs as the same user as the host supervision tree.
+
+ - -l loguser : run the logger of the instance
+supervisor, if any (see -L below) as user loguser. This option
+should only be used when the
+supervision tree that will host the instanced service is run as root. The default
+is that the logger runs as the same user as the host supervision tree.
+
+ - -L logdir : make the service logged via
+s6-log, and ensure its log messages go into logdir.
+Error messages from the instance supervisor as well as from every instance will
+be logged to logdir. If this option is not given, these error messages
+will fall through to the host supervision tree's catch-all logger, if any,
+or standard error otherwise.
+The options listed below are only used to configured the logger and are meaningless
+if -L is not given.
+
+ - -t stamptype : how
+logs are timestamped in logdir. 0 means no timestamp, 1 means
+external TAI64N format,
+2 means
+ISO 8601 format,
+and 3 means both. Default is 1.
+
+ - -n nfiles : maximum number of archive files
+in logdir. Default is 10.
+
+ - -s filesize : maximum size of the current
+file (and archive files) in logdir. Default is 1000000.
+
+ - -S maxsize : maximum total size of the
+archives in logdir. Default is 0,
+meaning no limits apart from those enforced by the -n and
+-s options.
+
+ - -P prefix : when logging to logdir,
+prefix logged lines with the prefix string. Default is no prefix.
+
+
+ The templated service directory
+
+
+ template should be a directory that looks exactly like a service
+directory. It will not be live, i.e. at no point will template
+itself be supervised; instead, a copy of it is stored under dir
+(and a copy of that copy will be used for every instance of the service).
+You can safely move or delete template after running
+s6-instance-maker.
+
+
+
+ To differentiate between instances, the run and finish
+script in template should take one additional argument (the
+first argument for run and the third argument for finish).
+This argument will be the name of the instance, as provided by the
+s6-instance-create invocation.
+
+
+ Logging
+
+
+ The service is logged: its stderr and stdout are piped to an
+s6-log process running as loguser and
+writing to the logdir directory. This logger is the catch-all logger
+for the supervision tree owned by user; it is recommended to make
+loguser distinct from user, and to have logdir
+in a place that is not under the control of user.
+If user wants to keep control of their logs, they can declare a
+logger for each of their services.
+
+
+
+ If template has a log subdirectory, then each instance
+will have its own dedicated logger. The run and finish
+scripts for the logger of an instance named name will be called
+with an additional argument of name/log. They should
+make use of this, to ensure loggers are properly differentiated between
+instances: for instance, it is not possible to run several
+s6-log processes on the same log directory,
+so an instance logger script containing an invocation of s6-log on a fixed
+logdir will fail as soon as there are 2 instances.
+
+
+ Notes
+
+
+ - s6-instance-maker makes use of the fact that
+execline scripts are much
+easier to generate programmatically and to harden than shell scripts, so it is only
+built if s6 is built with execline
+support - i.e. the --disable-execline switch has not been given
+to configure.
+ - If s6-instance-maker encounters failure (and exits 111), it does not clean up
+the directories it created. Make sure to always test s6-usertree-maker's return code
+and clean up after it if needed.
+
+
+
+
diff --git a/doc/s6-log.html b/doc/s6-log.html
index 4f2b99f..a76f9aa 100644
--- a/doc/s6-log.html
+++ b/doc/s6-log.html
@@ -233,7 +233,7 @@ printed on every output line. For instance, a pfoobar: directive means
that the next action directives should prepend every line with foobar:
(plus a space) before outputting it. Note that a prefix is always printed
after the timestamps, if any. To remove a prefix for the next action
-directives, use p.
+directives, use a standalone p.
!processor: registers
execlineb -Pc processor as a processor for the next logdirs;
execlineb must be found in s6-log's PATH. This directive is only
diff --git a/doc/s6-svc.html b/doc/s6-svc.html
index 62e93f9..b8d3c4d 100644
--- a/doc/s6-svc.html
+++ b/doc/s6-svc.html
@@ -28,7 +28,7 @@ knowing their PIDs, and without using horrible hacks such as .pid files.
Interface
- s6-svc [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyoduxOr ] servicedir
+ s6-svc [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyoduDUxOr ] servicedir
@@ -59,8 +59,13 @@ a SIGTERM (by default) then a SIGCONT (to make sure even stopped processes
receive the signal aimed to kill them) and do not restart it.
The SIGTERM default can be changed by editing the ./down-signal
file in the service directory.
+ -D : down, and create a ./down file so the
+service does not restart automatically if the supervisor dies.
-u : up. If the supervised process is down, start it.
Automatically restart it when it dies.
+ -U : up, and remove any ./down file that may
+exist, in order to make sure the service is automatically restarted even
+if the supervisor dies.
-x : exit. When the service is asked to be down and
the supervised process dies, s6-supervise will exit too. This command should
normally never be used on a working system. Note that if this command is
diff --git a/doc/s6-usertree-maker.html b/doc/s6-usertree-maker.html
index b62727a..29175d6 100644
--- a/doc/s6-usertree-maker.html
+++ b/doc/s6-usertree-maker.html
@@ -21,7 +21,7 @@
s6-usertree-maker creates a service directory
implementing a service that runs an s6-svscan
-instance owned by a given user, on a scan directory
+process owned by a given user, on a scan directory
belonging to that user. It is meant to help admins deploy systems where
each user has their own supervision subtree, rooted in the main supervision
tree owned by root.
@@ -45,6 +45,7 @@ for the s6-rc service manager.
[ -n nfiles ] \
[ -s filesize ] \
[ -S maxsize ] \
+ [ -P prefix ] \
user logdir dir
@@ -123,6 +124,9 @@ file (and archive files) in logdir. Default is 1000000
archives in the logdir. Default is 0,
meaning no limits apart from those enforced by the -n and
-s options.
+
+ -P prefix : when logging to logdir,
+prefix logged lines with the prefix string.
Operation of the service
diff --git a/doc/servicedir.html b/doc/servicedir.html
index 4ae06c2..89a272d 100644
--- a/doc/servicedir.html
+++ b/doc/servicedir.html
@@ -209,6 +209,13 @@ created by s6-supervise if it does not exist.
is the rendez-vous point for listeners, where s6-supervise
will send notifications when the service goes up or down.
+ Optional directories named instance
+and instances. Those are internal subdirectories created by
+s6-instance maker in a templated service
+directory. Outside of instanced services, these directories should never
+appear, and you should never create them manually.
+
+
An optional service directory named log. If it exists and foo
is in a scandir, and s6-svscan
runs on that scandir, then two services are monitored: foo and
diff --git a/package/deps.mak b/package/deps.mak
index 7aad077..c881c5e 100644
--- a/package/deps.mak
+++ b/package/deps.mak
@@ -42,6 +42,9 @@ src/fdholder/s6-fdholder-setdump.o src/fdholder/s6-fdholder-setdump.lo: src/fdho
src/fdholder/s6-fdholder-store.o src/fdholder/s6-fdholder-store.lo: src/fdholder/s6-fdholder-store.c src/include/s6/fdholder.h
src/fdholder/s6-fdholder-transferdump.o src/fdholder/s6-fdholder-transferdump.lo: src/fdholder/s6-fdholder-transferdump.c src/include/s6/fdholder.h
src/fdholder/s6-fdholderd.o src/fdholder/s6-fdholderd.lo: src/fdholder/s6-fdholderd.c src/include/s6/accessrules.h src/include/s6/fdholder.h
+src/instance/s6-instance-control.o src/instance/s6-instance-control.lo: src/instance/s6-instance-control.c src/include/s6/config.h
+src/instance/s6-instance-create.o src/instance/s6-instance-create.lo: src/instance/s6-instance-create.c src/include/s6/supervise.h
+src/instance/s6-instance-delete.o src/instance/s6-instance-delete.lo: src/instance/s6-instance-delete.c src/include/s6/supervise.h
src/instance/s6-instance-maker.o src/instance/s6-instance-maker.lo: src/instance/s6-instance-maker.c src/include/s6/auto.h src/include/s6/config.h
src/libs6/ftrig1_free.o src/libs6/ftrig1_free.lo: src/libs6/ftrig1_free.c src/libs6/ftrig1.h
src/libs6/ftrig1_make.o src/libs6/ftrig1_make.lo: src/libs6/ftrig1_make.c src/libs6/ftrig1.h
@@ -217,6 +220,12 @@ s6-fdholder-transferdump: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
s6-fdholder-transferdump: src/fdholder/s6-fdholder-transferdump.o ${LIBS6}
s6-fdholderd: EXTRA_LIBS := -lskarnet ${SOCKET_LIB} ${SYSCLOCK_LIB}
s6-fdholderd: src/fdholder/s6-fdholderd.o ${LIBS6}
+s6-instance-control: EXTRA_LIBS := -lskarnet
+s6-instance-control: src/instance/s6-instance-control.o
+s6-instance-create: EXTRA_LIBS := -lskarnet
+s6-instance-create: src/instance/s6-instance-create.o ${LIBS6}
+s6-instance-delete: EXTRA_LIBS := -lskarnet
+s6-instance-delete: src/instance/s6-instance-delete.o ${LIBS6}
s6-instance-maker: EXTRA_LIBS := -lskarnet
s6-instance-maker: src/instance/s6-instance-maker.o libs6auto.a.xyzzy
ifeq ($(strip $(STATIC_LIBS_ARE_PIC)),)
diff --git a/package/modes b/package/modes
index a0ee4ae..a76c1e1 100644
--- a/package/modes
+++ b/package/modes
@@ -66,3 +66,6 @@ s6-fdholder-transferdump 0755
s6-fdholder-transferdumpc 0755
s6-usertree-maker 0755
s6-instance-maker 0755
+s6-instance-create 0755
+s6-instance-delete 0755
+s6-instance-control 0755
diff --git a/package/targets.mak b/package/targets.mak
index 2f6f81a..8e58122 100644
--- a/package/targets.mak
+++ b/package/targets.mak
@@ -56,7 +56,10 @@ s6-fdholder-getdump \
s6-fdholder-setdump \
s6-fdholder-transferdump \
s6-applyuidgid \
-s6-setuidgid
+s6-setuidgid \
+s6-instance-create \
+s6-instance-delete \
+s6-instance-control
LIBEXEC_TARGETS := s6lockd-helper
diff --git a/src/instance/deps-exe/s6-instance-control b/src/instance/deps-exe/s6-instance-control
new file mode 100644
index 0000000..e7187fe
--- /dev/null
+++ b/src/instance/deps-exe/s6-instance-control
@@ -0,0 +1 @@
+-lskarnet
diff --git a/src/instance/deps-exe/s6-instance-create b/src/instance/deps-exe/s6-instance-create
new file mode 100644
index 0000000..08815d9
--- /dev/null
+++ b/src/instance/deps-exe/s6-instance-create
@@ -0,0 +1,2 @@
+${LIBS6}
+-lskarnet
diff --git a/src/instance/deps-exe/s6-instance-delete b/src/instance/deps-exe/s6-instance-delete
new file mode 100644
index 0000000..08815d9
--- /dev/null
+++ b/src/instance/deps-exe/s6-instance-delete
@@ -0,0 +1,2 @@
+${LIBS6}
+-lskarnet
diff --git a/src/instance/s6-instance-control.c b/src/instance/s6-instance-control.c
new file mode 100644
index 0000000..899ab89
--- /dev/null
+++ b/src/instance/s6-instance-control.c
@@ -0,0 +1,79 @@
+/* ISC license. */
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#define USAGE "s6-instance-control [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyroduDUxOX ] service instance"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+#define DATASIZE 63
+
+int main (int argc, char const **argv)
+{
+ char const **fullargv = argv ;
+ size_t namelen ;
+ PROG = "s6-instance-control" ;
+
+ {
+ subgetopt l = SUBGETOPT_ZERO ;
+ unsigned int datalen = 1 ;
+ unsigned int timeout = 0 ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "abqhkti12pcyroduxOT:w:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'a' :
+ case 'b' :
+ case 'q' :
+ case 'h' :
+ case 'k' :
+ case 't' :
+ case 'i' :
+ case '1' :
+ case '2' :
+ case 'p' :
+ case 'c' :
+ case 'y' :
+ case 'r' :
+ case 'o' :
+ case 'd' :
+ case 'u' :
+ case 'D' :
+ case 'U' :
+ case 'x' :
+ case 'O' : if (datalen++ >= DATASIZE) strerr_dief1x(100, "too many commands") ; break ;
+ case 'T' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ;
+ case 'w' : if (!memchr("dDuUrR", l.arg[0], 6)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ }
+
+ if (argc < 2) dieusage() ;
+ namelen = strlen(argv[1]) ;
+ if (!argv[0][0]) strerr_dief1x(100, "invalid service name") ;
+ if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], namelen, " \t\f\r\n", 5) < 5)
+ strerr_dief1x(100, "invalid instance name") ;
+
+ {
+ size_t svlen = strlen(argv[0]) ;
+ char fn[svlen + 11 + namelen] ;
+ memcpy(fn, argv[0], svlen) ;
+ memcpy(fn + svlen, "/instance/", 10) ;
+ memcpy(fn + svlen + 10, argv[1], namelen + 1) ;
+ argv[0] = fn ;
+ argv[1] = 0 ;
+ fullargv[0] = S6_BINPREFIX "s6-svc" ;
+ xexec(fullargv) ;
+ }
+}
diff --git a/src/instance/s6-instance-create.c b/src/instance/s6-instance-create.c
new file mode 100644
index 0000000..2e97618
--- /dev/null
+++ b/src/instance/s6-instance-create.c
@@ -0,0 +1,97 @@
+/* ISC license. */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#define USAGE "s6-instance-create [ -d | -D ] [ -f ] [ -P ] [ -t timeout ] service instancename"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+static inline void checkinstanced (char const *s) /* chdirs */
+{
+ int fd, r ;
+ size_t len = strlen(s) ;
+ char fn[len + 10] ;
+ memcpy(fn, s, len) ;
+ memcpy(fn + len, "/instance", 10) ;
+ if (chdir(fn) == -1) strerr_diefu2sys(111, "chdir to ", fn) ;
+ fd = open_read(S6_SVSCAN_CTLDIR "/lock") ;
+ if (fd < 0) strerr_diefu3sys(111, "open ", fn, "/" S6_SVSCAN_CTLDIR "/lock") ;
+ r = fd_islocked(fd) ;
+ if (r < 0) strerr_diefu3sys(111, "check lock on ", fn, "/" S6_SVSCAN_CTLDIR "/lock") ;
+ if (!r) strerr_dief2x(1, "instanced service not running on ", s) ;
+ fd_close(fd) ;
+}
+
+static void cleanup (char const *s)
+{
+ int e = errno ;
+ rm_rf(s) ;
+ errno = e ;
+}
+
+int main (int argc, char const *const *argv)
+{
+ tain tto = TAIN_INFINITE_RELATIVE ;
+ size_t namelen ;
+ uint32_t options = 16 ;
+ PROG = "s6-instance-create" ;
+ {
+ unsigned int t = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "dDfPt:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'd' : options |= 12 ; break ;
+ case 'D' : options |= 4 ; options &= ~8U ; break ;
+ case 'f' : options |= 1 ; break ;
+ case 'P' : options |= 2 ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&tto, t) ;
+ }
+ if (argc < 2) dieusage() ;
+
+ namelen = strlen(argv[1]) ;
+ if (!argv[0][0]) strerr_dief1x(100, "invalid service path") ;
+ if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], namelen, " \t\f\r\n", 5) < 5)
+ strerr_dief1x(100, "invalid instance name") ;
+ checkinstanced(argv[0]) ;
+
+ tain_now_set_stopwatch_g() ;
+ tain_add_g(&tto, &tto) ;
+
+ {
+ char sv[namelen + 14] ;
+ memcpy(sv, "../instances/", 13) ;
+ memcpy(sv + 13, argv[1], namelen + 1) ;
+ if (!hiercopy("../instances/.template", sv))
+ {
+ cleanup(sv) ;
+ strerr_diefu5sys(111, "copy ", argv[0], "/instances/.template to ", argv[0], sv+2) ;
+ }
+ if (s6_supervise_link_names_g(".", (char const *const *)&sv, argv + 1, 1, options, &tto) == -1)
+ {
+ cleanup(sv) ;
+ strerr_diefu4sys(errno == ETIMEDOUT ? 99 : 111, "creatre instance of ", argv[0], " named ", argv[1]) ;
+ }
+ }
+ return 0 ;
+}
diff --git a/src/instance/s6-instance-delete.c b/src/instance/s6-instance-delete.c
new file mode 100644
index 0000000..fc1337a
--- /dev/null
+++ b/src/instance/s6-instance-delete.c
@@ -0,0 +1,61 @@
+/* ISC license. */
+
+#include
+#include
+#include
+#include
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#define USAGE "s6-instance-delete [ -X ] [ -t timeout ] service instancename"
+#define dieusage() strerr_dieusage(100, USAGE)
+
+int main (int argc, char const *const *argv)
+{
+ tain tto = TAIN_INFINITE_RELATIVE ;
+ uint32_t options = 1 ;
+ PROG = "s6-instance-delete" ;
+ {
+ unsigned int t = 0 ;
+ subgetopt l = SUBGETOPT_ZERO ;
+ for (;;)
+ {
+ int opt = subgetopt_r(argc, argv, "Xt:", &l) ;
+ if (opt == -1) break ;
+ switch (opt)
+ {
+ case 'X' : options &= ~1U ; break ;
+ case 't' : if (!uint0_scan(l.arg, &t)) dieusage() ; break ;
+ default : dieusage() ;
+ }
+ }
+ argc -= l.ind ; argv += l.ind ;
+ if (t) tain_from_millisecs(&tto, t) ;
+ }
+ if (argc < 2) dieusage() ;
+ if (!argv[0][0]) strerr_dief1x(100, "invalid service path") ;
+ if (!argv[1][0] || argv[1][0] == '.' || byte_in(argv[1], strlen(argv[1]), " \t\f\r\n", 5) < 5)
+ strerr_dief1x(100, "invalid instance name") ;
+
+ tain_now_set_stopwatch_g() ;
+ tain_add_g(&tto, &tto) ;
+
+ {
+ size_t svlen = strlen(argv[0]) ;
+ char sc[svlen + 10] ;
+ memcpy(sc, argv[0], svlen) ;
+ memcpy(sc + svlen, "/instance", 10) ;
+ if (s6_supervise_unlink_names_g(sc, argv + 1, 1, options, &tto) == -1)
+ strerr_diefu4sys(111, "prepare deletion of instance ", argv[1], " of service ", argv[0]) ;
+ }
+
+ return 0 ;
+}
diff --git a/src/instance/s6-instance-maker.c b/src/instance/s6-instance-maker.c
index 20f557f..e70e569 100644
--- a/src/instance/s6-instance-maker.c
+++ b/src/instance/s6-instance-maker.c
@@ -37,7 +37,7 @@ static int write_run (buffer *b, void *data)
size_t l ;
char fmt[UINT_FMT] ;
l = uint_fmt(fmt, t->maxinstances) ;
- if (buffer_puts(b, EXECLINE_EXTBINPREFIX "execlineb -S1\n\n"
+ if (buffer_puts(b, EXECLINE_EXTBINPREFIX "execlineb -P\n\n"
EXECLINE_EXTBINPREFIX "fdmove -c 2 1\n") < 0) return 0 ;
if (t->user)
{
diff --git a/src/libs6/s6_supervise_link_names.c b/src/libs6/s6_supervise_link_names.c
index 9af573f..de8e5c5 100644
--- a/src/libs6/s6_supervise_link_names.c
+++ b/src/libs6/s6_supervise_link_names.c
@@ -42,6 +42,7 @@ static uint16_t registerit (ftrigr_t *a, char *fn, size_t len, gid_t gid, uint32
bit 1: make event/ public
bit 2: don't start the service
bit 3: remove down files after starting supervisors
+ bit 4: allow links to relative paths
*/
int s6_supervise_link_names (char const *scdir, char const *const *servicedirs, char const *const *names, size_t n, uint32_t options, tain const *deadline, tain *stamp)
@@ -118,7 +119,7 @@ int s6_supervise_link_names (char const *scdir, char const *const *servicedirs,
}
fn[len] = 0 ;
strcpy(lname + scdirlen + 1, names[i]) ;
- if (servicedirs[i][0] != '/')
+ if (!(options & 16) && servicedirs[i][0] != '/')
{
rpsa.len = 0 ;
if (sarealpath(&rpsa, servicedirs[i]) < 0 || !stralloc_0(&rpsa)) goto err ;
diff --git a/src/supervision/s6-svc.c b/src/supervision/s6-svc.c
index 7e00f17..5982248 100644
--- a/src/supervision/s6-svc.c
+++ b/src/supervision/s6-svc.c
@@ -7,28 +7,31 @@
#include
#include
#include
+#include
#include
#include
#include
-#define USAGE "s6-svc [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyroduxOX ] servicedir"
+#define USAGE "s6-svc [ -wu | -wU | -wd | -wD | -wr | -wR ] [ -T timeout ] [ -abqhkti12pcyroduDUxOX ] servicedir"
#define dieusage() strerr_dieusage(100, USAGE)
#define DATASIZE 63
int main (int argc, char const *const *argv)
{
- char data[DATASIZE+1] = "-" ;
+ size_t len ;
+ int downfile = -1 ;
unsigned int datalen = 1 ;
unsigned int timeout = 0 ;
+ char data[DATASIZE+1] = "-" ;
char updown[3] = "-\0" ;
PROG = "s6-svc" ;
{
subgetopt l = SUBGETOPT_ZERO ;
for (;;)
{
- int opt = subgetopt_r(argc, argv, "abqhkti12pcyroduxOT:w:", &l) ;
+ int opt = subgetopt_r(argc, argv, "abqhkti12pcyroduDUxOT:w:", &l) ;
if (opt == -1) break ;
switch (opt)
{
@@ -55,6 +58,20 @@ int main (int argc, char const *const *argv)
data[datalen++] = opt ;
break ;
}
+ case 'D' :
+ {
+ if (datalen >= DATASIZE) strerr_dief1x(100, "too many commands") ;
+ data[datalen++] = 'd' ;
+ downfile = 1 ;
+ break ;
+ }
+ case 'U' :
+ {
+ if (datalen >= DATASIZE) strerr_dief1x(100, "too many commands") ;
+ data[datalen++] = 'u' ;
+ downfile = 0 ;
+ break ;
+ }
case 'T' : if (!uint0_scan(l.arg, &timeout)) dieusage() ; break ;
case 'w' :
{
@@ -69,13 +86,14 @@ int main (int argc, char const *const *argv)
}
if (!argc) dieusage() ;
if (argc > 1) strerr_warnw1x("ignoring extra arguments") ;
+ len = strlen(argv[0]) ;
+ if (!len) strerr_dief1x(100, "invalid service path") ;
if (updown[1] == 'U' || updown[1] == 'R')
{
- size_t arglen = strlen(argv[0]) ;
- char fn[arglen + 17] ;
- memcpy(fn, argv[0], arglen) ;
- memcpy(fn + arglen, "/notification-fd", 17) ;
+ char fn[len + 17] ;
+ memcpy(fn, argv[0], len) ;
+ memcpy(fn + len, "/notification-fd", 17) ;
if (access(fn, F_OK) < 0)
{
if (errno != ENOENT) strerr_diefu2sys(111, "access ", fn) ;
@@ -84,6 +102,19 @@ int main (int argc, char const *const *argv)
}
}
+ if (downfile >= 0)
+ {
+ char fn[len + 6] ;
+ memcpy(fn, argv[0], len) ;
+ memcpy(fn + len, "/down", 6) ;
+ if (downfile)
+ {
+ if (!openwritenclose_unsafe(fn, "", 0))
+ strerr_diefu2sys(111, "touch ", fn) ;
+ }
+ else unlink_void(fn) ;
+ }
+
if (updown[1])
{
char const *newargv[11] ;
--
cgit v1.2.3