The http6d service in the regular services collection is an example of a typical and commonly used TCP service.
Its start
, stop
, and restart
programs are fairly trivial and straightforward.
The meat of the service is its run
and service
programs.
The service could be arranged as a single run
program.
However, that does not permit adjustment of the service program, including its command-line arguments, whilst the service is up.
Having a separate service
program, that is chained to for each new TCP connection, permits an administrator to adjust the main service program without taking the service down.
Of course, this has to be done carefully, because a live service could run the service
program at any time, including halfway through when a text editor is updating the script.
So always edit a "service.new" script and then, after setting execute permission, atomically rename it to service
.
The run
and service
programs are as follows:
#!/bin/nosh #HTTP socket capable of single-stack IPV6 and IPV6-mapped IPV4 tcp-socket-listen --backlog 2 --combine4and6 "::0" http envuidgid publicfile hardlimit -o 20 -d 500000 softlimit -o hard -d hard tcp-socket-accept --connection-limit 16 ucspi-socket-rules-check ./service
#!/bin/nosh #HTTP service over IPv4/IPv6 using Bernstein's publicfile sh -c 'exec httpd ~publicfile/public'
Like many of the pre-packaged services, these scripts are in fact automatically generated from a description file that is in the source package.
(In fact, that description is a slight variant on a systemd unit file.)
Both invoke nosh
as their script interpreter, and comprise a series of utility commands, each chain-loading to the next, passing along what program arguments it has not itself absorbed.
In the run
program:
nosh
chain loads tcp-socket-listen.
tcp-socket-listen
opens a TCP socket, binds it to a wildcard IP address and the well-known TCP port for HTTP, and then chain loads envuidgid.
envuidgid
reads the system's account database for the user "publicfile", sets up some environment variables for later use, and chain loads softlimit.
softlimit
adjusts some kernel resource limits for the current process, and chain loads hardlimit.
hardlimit
adjusts some kernel resource limits for the current process, and chain loads tcp-socket-accept.
tcp-socket-accept
sits waiting for TCP connections on the listening socket that was opened for it, forking a new process for each accepted connection and chain loading to ucspi-socket-rules-check in that process.
ucspi-socket-rules-check
checks a local database, in the service directory, for access controls on the connecting client IP address, and if the client is permitted access chains to the service
program.
In the service
program:
nosh chain loads the service program proper, which is Bernstein's publicfile.
publicfile picks up the environment variables earlier set by envuidgid, changes to the data root directory, drops superuser privileges, and serves HTTP requests over the TCP connection.
The amount of setup in the run
and service
scripts depends from what the service program itself does, of course.
If publicfile did not change working directory and root directory, for example, one could do so with the chain-loading chdir
and chroot
commands.
Some run
programs pass tcp-socket-listen the --systemd-compatibility
option, if the program that accepts the socket connections require systemd's LISTEN_FDS protocol for knowing which open file descriptor is the listening TCP socket.
This protocol also permits one to build up a list of listening sockets, for the final accepting service program to use, by simple dint of chaining multiple programs such as tcp-socket-listen, udp-socket-listen, and local-stream-socket-listen one after another.
Looking at the rest of the service bundle, one finds the standard things for a "server" service. The service is auto-started (when enabled) by the "server" system target. It is stopped by the "shutdown" system target. It requires for correct operation that all services in the "basic" system target be started, before it itself is started. Its output is sent to a logger service.