Initial commit
This commit is contained in:
commit
5fecac210f
|
@ -0,0 +1,18 @@
|
|||
Copyright (c) 2020 Sander van der Burg
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
@ -0,0 +1,458 @@
|
|||
Nix-based process management framework
|
||||
======================================
|
||||
This repository contains a very experimental prototype implementation of a
|
||||
operating system and process manager agnostic Nix-based process managed
|
||||
framework that can be used to run multiple instances of services on a single
|
||||
machine, using Nix to deliver and isolate all required package dependencies and
|
||||
configuration files.
|
||||
|
||||
Features:
|
||||
* It uses *simple conventions* to specify system configurations: function
|
||||
definitions (corresponding to constructors), function invocations (that
|
||||
compose running process instances from constructors) and Nix profiles (that
|
||||
assembles multiple process configurations into one package).
|
||||
* The ability to deploy *multiple instances* of processes.
|
||||
* Deploying processes/services as an *unprivileged* user.
|
||||
* Operating system and process manager *agnostic* -- it can be used on any
|
||||
operating system that supports the Nix package manager and works with a
|
||||
variety of process managers.
|
||||
* Advanced concepts and features, such as namespaces and cgroups, are not
|
||||
required.
|
||||
|
||||
Supported process managers
|
||||
==========================
|
||||
Currently, the following process managers are supported:
|
||||
|
||||
* `sysvinit`: sysvinit scripts, also known as [LSB Init scripts](https://wiki.debian.org/LSBInitScripts)
|
||||
* `bsdrc`: BSD [rc scripts](https://www.freebsd.org/doc/en_US.ISO8859-1/articles/rc-scripting/index.html)
|
||||
* `supervisord`: [supervisord](http://supervisord.org)
|
||||
* `systemd`: [systemd](https://www.freedesktop.org/wiki/Software/systemd)
|
||||
* `launchd`: [launchd](https://www.launchd.info)
|
||||
* `cygrunsrv`: Cygwin's [cygrunsrv](http://web.mit.edu/cygwin/cygwin_v1.3.2/usr/doc/Cygwin/cygrunsrv.README)
|
||||
|
||||
Prerequisites
|
||||
=============
|
||||
To use this framework, you first need to install:
|
||||
|
||||
* [The Nix package manager](http://nixos.org/nix)
|
||||
* [The Nixpkgs collection](http://nixos.org/nixpkgs)
|
||||
* [Dysnomia](http://github.com/svanderburg/dysnomia), if you want to manage users and groups
|
||||
|
||||
Installation
|
||||
============
|
||||
First, make a Git clone of this repository.
|
||||
|
||||
The next step is installing the build tool:
|
||||
|
||||
```bash
|
||||
$ cd tools
|
||||
$ nix-env -f default.nix -iA build
|
||||
```
|
||||
|
||||
Then, at least one tool that deploys a configuration for a supported process
|
||||
manager must be installed.
|
||||
|
||||
For example, to work with sysvinit scripts, you must install:
|
||||
|
||||
```bash
|
||||
$ nix-env -f default.nix -iA sysvinit
|
||||
```
|
||||
|
||||
To work with a different process manager, you should replace `sysvinit` with
|
||||
any of the supported process managers listed above.
|
||||
|
||||
Usage
|
||||
=====
|
||||
For each kind of process you need to write a Nix expression that constructs
|
||||
its configuration from sources and its dependencies. This can be done in a
|
||||
process manager-specific and a process manager-agnostic way.
|
||||
|
||||
Then you need to create a constructors and processes expression.
|
||||
|
||||
The processes expression can be used by a deploy tool that works with a
|
||||
specific process manager.
|
||||
|
||||
Writing a process manager-specific process management configuration
|
||||
-------------------------------------------------------------------
|
||||
The following expression is an example of a configuration that deploys
|
||||
a sysvinit script that can be used to control a simple web application
|
||||
process that just returns a static HTML page:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript, runtimeDir}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = import ../../webapp;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createSystemVInitScript {
|
||||
name = instanceName;
|
||||
inherit instanceName;
|
||||
process = "${webapp}/bin/webapp";
|
||||
args = [ "-D" ];
|
||||
environment = {
|
||||
PORT = port;
|
||||
PID_FILE = "${runtimeDir}/${instanceName}.pid";
|
||||
};
|
||||
|
||||
runlevels = [ 3 4 5 ];
|
||||
|
||||
user = instanceName;
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
A process expression defines a nested function:
|
||||
|
||||
* The outer function (first line) allows properties to be configured that apply
|
||||
to all process instances, such as the the function that constructs sysvinit
|
||||
scripts (`createSystemVInitScript`) and the `runtimeDir` in which PID files
|
||||
are stored (on most systems this defaults to: `/var/run`).
|
||||
* The inner function (second line) refers to all instance specific parameters.
|
||||
To allow multiple instances to co-exist, instance parameters must be
|
||||
configured in such a way that they no longer conflict. For example, if we
|
||||
assign two unique TCP `port` numbers and we append the process name with a
|
||||
unique suffix, we can run two instances of the web application at the same
|
||||
time.
|
||||
|
||||
In the body, we invoke the `createSystemVInitScript` function to declaratively
|
||||
construct a sysvinit script:
|
||||
|
||||
* The `process` parameter specifies which process should be managed. From this
|
||||
parameter, the generator will automatically derive `start`, `stop`, `restart`
|
||||
and `reload` activities.
|
||||
* The `args` parameter specifies the command line parameters propagated to the
|
||||
process. The `-D` parameter specifies that the webapp process should run in
|
||||
daemon mode (i.e. the process spawns another process that keeps running in
|
||||
the background and then terminates immediately).
|
||||
* The `environment` attribute set defines all environment variables that the
|
||||
webapp process needs -- `PORT` is used to specify the TCP port it should
|
||||
listen to and `PID_FILE` specifies the path to the PID file that stores
|
||||
the process ID (PID) of the daemon process
|
||||
* To allow multiple processes to co-exist, we must make sure that each has
|
||||
a unique PID file name. We can use the `instanceName` parameter to specify
|
||||
what name this PID file should have. By default, it gets the same name as
|
||||
the process name.
|
||||
* The `runlevels` parameter specifies in which run levels the process should
|
||||
be started (in the above example: 3, 4, and 5. It will automatically
|
||||
configure the init system to stop the process in the other runlevels:
|
||||
0, 1, 2, and 6.
|
||||
* It is also typically not recommended to run a service as root user. The
|
||||
`credentials` attribute specifies which group and user account need to be
|
||||
created. The `user` parameter specifies as which user the process needs to
|
||||
run at.
|
||||
|
||||
The `createSystemVInitScript` function support many more parameters than
|
||||
described in the example above. For example, it is also possible to directly
|
||||
specify how activities should be implemented.
|
||||
|
||||
It can also be used to specify dependencies on other sysvinit scripts -- the
|
||||
system will automatically derive the sequence numbers so that they are activated
|
||||
and deactivated in the right order.
|
||||
|
||||
In addition to sysvinit, there are also functions that can be used to create
|
||||
configurations for the other supported process managers, e.g.
|
||||
`createSystemdUnit`, `createSupervisordProgram`, `createBSDRCScript`. Check
|
||||
the implementations in `nixproc/create-managed-process` for more information.
|
||||
|
||||
Writing a process manager-agnostic process management configuration
|
||||
-------------------------------------------------------------------
|
||||
This repository contains generator functions for a variety of process managers.
|
||||
What you will notice is that they require parameters that looks quite similar.
|
||||
|
||||
When it is desired to target multiple process managers, it is also possible to
|
||||
write a process manager-agnostic configuration from which a variety of
|
||||
configurations can be generated.
|
||||
|
||||
This expression is a process manager-agnostic version of the previous example:
|
||||
|
||||
```nix
|
||||
{createManagedProcess, runtimeDir}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = import ../../webapp;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
description = "Simple web application";
|
||||
inherit instanceName;
|
||||
|
||||
# This expression can both run in foreground or daemon mode.
|
||||
# The process manager can pick which mode it prefers.
|
||||
process = "${webapp}/bin/webapp";
|
||||
daemonArgs = [ "-D" ];
|
||||
|
||||
environment = {
|
||||
PORT = port;
|
||||
PID_FILE = "${runtimeDir}/${instanceName}.pid";
|
||||
};
|
||||
user = instanceName;
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, we invoke `createManagedProcess` to construct a
|
||||
configuration for a process manager. It captures similar properties that
|
||||
are described in the sysvinit-specific configuration, as shown in the previous
|
||||
example.
|
||||
|
||||
To allow it to target a variety of process managers, we must specify:
|
||||
|
||||
* How the process can be started in foreground mode. The `process`
|
||||
parameter gets translated to `foregroundProcess` and `daemon`. The former
|
||||
specifies how the service should be started as a foreground process and the
|
||||
latter how it should start as a daemon.
|
||||
* The `daemonArgs` parameter specifies which command-line parameters the process
|
||||
should take
|
||||
|
||||
Under the hood, the `createManagedProcess` function invokes a generator function
|
||||
which calls the corresponding process manager-specific create function.
|
||||
|
||||
The `createManagedProcess` abstraction function does not support all
|
||||
functionality that the process manager-specific abstraction functions provide --
|
||||
It only supports a common subset. To get non-standardized functionality working,
|
||||
you can also define `overrides`, that augments the generated function parameters
|
||||
with process manager-specific parameters.
|
||||
|
||||
In the above example, we define an override to specify the `runlevels`. Runlevels
|
||||
is a concept only supported by sysvinit scripts.
|
||||
|
||||
Writing a constructors expression
|
||||
---------------------------------
|
||||
As shown in the previous sections, a process configuration is a nested function.
|
||||
To be able to deploy a certain process configuration, it needs to be composed
|
||||
twice.
|
||||
|
||||
The common parameters (the outer function) are composed in a so-called
|
||||
constructors expression, that has the following structure:
|
||||
|
||||
```nix
|
||||
{ pkgs
|
||||
, stateDir
|
||||
, logDir
|
||||
, runtimeDir
|
||||
, tmpDir
|
||||
, forceDisableUserChange
|
||||
, processManager
|
||||
}:
|
||||
|
||||
let
|
||||
createManagedProcess = import ../../nixproc/create-managed-process/agnostic/create-managed-process-universal.nix {
|
||||
inherit pkgs runtimeDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
in
|
||||
{
|
||||
webapp = import ./webapp.nix {
|
||||
inherit createManagedProcess runtimeDir;
|
||||
};
|
||||
|
||||
nginxReverseProxy = import ./nginx-reverse-proxy.nix {
|
||||
inherit createManagedProcess stateDir logDir runtimeDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv writeTextFile nginx;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The above Nix expression defines a function that takes common state
|
||||
configuration parameters that applies to all services:
|
||||
|
||||
* `pkgs` refers to the Nixpkgs collection
|
||||
* `stateDir` refers to the base directory where all variable data needs to be
|
||||
stored. The default on most systems is `/var`.
|
||||
* `runtimeDir` refers to the base directory where all PID files are stored
|
||||
* `tmpDir` refers to the base directory where all temp files are stored
|
||||
* `forceDisableUserChange` can be used to globally switch of the creation of
|
||||
users and groups and changing users.
|
||||
* `processManager` specifies which process manager we want to use (when it is
|
||||
desired to do process manager agnostic deployments).
|
||||
|
||||
In the body, the function returns an attribute set in which every value refers
|
||||
to a constructor function that can be used to construct process instances.
|
||||
|
||||
The `webapp` attribute refers to a constructor function that can be used to
|
||||
construct one or more running `webapp` processes. It takes the common parameters
|
||||
that it requires as function arguments.
|
||||
|
||||
The constructors attribute facilitates multiple constructor functions.
|
||||
`nginxReverseProxy` refers to a process configuration that launches the
|
||||
[Nginx](http://nginx.com) HTTP server and configures it to act as a reverse
|
||||
proxy for an arbitrary number of web application processes.
|
||||
|
||||
Writing a processes expression
|
||||
------------------------------
|
||||
The processes Nix expression makes it possible to construct one or more
|
||||
instances of processes:
|
||||
|
||||
```nix
|
||||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, processManager
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
webapp1 = rec {
|
||||
port = 5000;
|
||||
dnsName = "webapp1.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
webapp2 = rec {
|
||||
port = 5001;
|
||||
dnsName = "webapp2.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
port = 8080;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp1 webapp2 ];
|
||||
inherit port;
|
||||
} {};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
The above Nix expression is a function in which the parameters (again) specify
|
||||
common state configuration parameters. It makes a reference the constructors
|
||||
expression shown in the previous example.
|
||||
|
||||
The function body returns an attribute set that defines three process
|
||||
instances:
|
||||
|
||||
* `webapp1` is a web application process that listens on TCP port 5000
|
||||
* `webapp2` is a web application process that listens on TCP port 5001
|
||||
* `nginxReverseProxy` is an Nginx server that forwards requests to the
|
||||
web application processes. If the virtual host is `webapp1.local` then the
|
||||
first `webapp1` process responds, if the virtual host is `webapp2.local` then
|
||||
the second process (`webapp2`) responds.
|
||||
|
||||
Building a process configurations profile
|
||||
-----------------------------------------
|
||||
We can build all required packages and generate all configuration artifacts for
|
||||
a specific process manager by running the following command:
|
||||
|
||||
```bash
|
||||
$ nixproc-build --process-manager sysvinit processes.nix
|
||||
result/
|
||||
```
|
||||
|
||||
The above command generates sysvinit scripts and start and stop symlinks to
|
||||
ensure that the webapp processes are started before the Nginx reverse proxy.
|
||||
|
||||
The `--process-manager` parameter can be changed to generate configuration files
|
||||
for different process managers. For example, if we would use
|
||||
`--process-manager systemd` then the resulting Nix profile contains a collection
|
||||
of systemd unit configuration files.
|
||||
|
||||
Deploying a process configurations profile
|
||||
------------------------------------------
|
||||
In addition to generating configuration files that can be consumed by a process
|
||||
manager, we can also invoke the process manager to deploy all process defined in
|
||||
our process Nix expression.
|
||||
|
||||
The following command automatically starts all sysvinit scripts (and stop all
|
||||
obsolete sysvinit scripts, in case of an upgrade):
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-switch processes.nix
|
||||
```
|
||||
|
||||
Consult the help pages of the corresponding process manager specific tools to
|
||||
get a better understanding on how they work.
|
||||
|
||||
Changing the state directories
|
||||
------------------------------
|
||||
By default, the all processes use the `/var` directory as a base directory to
|
||||
store all state. This location can be adjusted by using the `--state-dir`
|
||||
parameter.
|
||||
|
||||
The following command deploys all process instances and stores their state in
|
||||
`/home/sander/var`:
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-switch --state-dir /home/sander/var processes.nix
|
||||
```
|
||||
|
||||
Similarly, it is also possible to adjust the base locations of the runtime
|
||||
files, log files and temp files, if desired.
|
||||
|
||||
Deploying process instances as an unprivileged user
|
||||
---------------------------------------------------
|
||||
It is also possible to do unprivileged user deployments. Unfortunately,
|
||||
unprivileged users cannot create new groups and/or users or change permissions
|
||||
of running processes.
|
||||
|
||||
This can be globally disabled with the `--force-disable-user-change` parameter.
|
||||
|
||||
The following command makes it possible to deploy all processes as an
|
||||
unprivileged user:
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-switch --state-dir /home/sander/var --force-disable-user-change processes.nix
|
||||
```
|
||||
|
||||
Examples
|
||||
========
|
||||
This repository contains three example systems, that can be found in the
|
||||
`examples/` folder:
|
||||
|
||||
* `webapps-sysvinit` is a processes configuration set using the example `webapp`
|
||||
process described in this README, with `nginx` reverse proxies. The
|
||||
configuration is specifically written to use sysvinit as a process manager.
|
||||
* `webapps-agnostic` is the same as the previous example, but using a process
|
||||
manager agnostic configuration. It can be used to target all process managers
|
||||
that this toolset supports.
|
||||
* `services-agnostic` is a process manager-agnostic configuration set of common
|
||||
system services: Apache HTTP server, MySQL, PostgreSQL and Apache Tomcat.
|
||||
|
||||
License
|
||||
=======
|
||||
The contents of this package is available under the same license as Nixpkgs --
|
||||
the [MIT](https://opensource.org/licenses/MIT) license.
|
|
@ -0,0 +1,34 @@
|
|||
{createManagedProcess, apacheHttpd}:
|
||||
{instanceSuffix ? "", configFile, initialize ? ""}:
|
||||
|
||||
let
|
||||
instanceName = "httpd${instanceSuffix}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
inherit instanceName initialize;
|
||||
|
||||
process = "${apacheHttpd}/bin/httpd";
|
||||
args = [ "-f" configFile ];
|
||||
foregroundProcessExtraArgs = [ "-DFOREGROUND" ];
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${group}" = {};
|
||||
};
|
||||
users = {
|
||||
"${user}" = {
|
||||
inherit group;
|
||||
description = "Apache HTTP daemon user";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{ pkgs
|
||||
, stateDir
|
||||
, logDir
|
||||
, runtimeDir
|
||||
, tmpDir
|
||||
, forceDisableUserChange
|
||||
, processManager
|
||||
}:
|
||||
|
||||
let
|
||||
createManagedProcess = import ../../nixproc/create-managed-process/agnostic/create-managed-process-universal.nix {
|
||||
inherit pkgs runtimeDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
in
|
||||
{
|
||||
apache = import ./apache.nix {
|
||||
inherit createManagedProcess;
|
||||
inherit (pkgs) apacheHttpd;
|
||||
};
|
||||
|
||||
static-webapp-apache = import ./static-webapp-apache.nix {
|
||||
inherit createManagedProcess logDir runtimeDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv apacheHttpd writeTextFile;
|
||||
};
|
||||
|
||||
mysql = import ./mysql.nix {
|
||||
inherit createManagedProcess stateDir runtimeDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv mysql;
|
||||
};
|
||||
|
||||
postgresql = import ./postgresql.nix {
|
||||
inherit createManagedProcess stateDir runtimeDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv postgresql;
|
||||
};
|
||||
|
||||
tomcat = import ./tomcat.nix {
|
||||
inherit createManagedProcess stateDir runtimeDir tmpDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv;
|
||||
jre = pkgs.jre8;
|
||||
tomcat = pkgs.tomcat9;
|
||||
};
|
||||
|
||||
simple-appserving-tomcat = import ./simple-appserving-tomcat.nix {
|
||||
inherit createManagedProcess stateDir runtimeDir tmpDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv;
|
||||
jre = pkgs.jre8;
|
||||
tomcat = pkgs.tomcat9;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
{createManagedProcess, stdenv, mysql, stateDir, runtimeDir, forceDisableUserChange}:
|
||||
{port ? 3306, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
instanceName = "mysqld${instanceSuffix}";
|
||||
dataDir = "${stateDir}/db/${instanceName}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
inherit instanceName user;
|
||||
|
||||
initialize = ''
|
||||
mkdir -m0700 -p ${dataDir}
|
||||
mkdir -m0700 -p ${runtimeDir}
|
||||
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
chown ${user}:${group} ${dataDir}
|
||||
''}
|
||||
|
||||
if [ ! -e "${dataDir}/mysql" ]
|
||||
then
|
||||
${mysql}/bin/mysql_install_db --basedir=${mysql} --datadir=${dataDir} ${if forceDisableUserChange then "" else "--user=${user}"}
|
||||
fi
|
||||
'';
|
||||
|
||||
foregroundProcess = "${mysql}/bin/mysqld";
|
||||
foregroundProcessArgs = [ "--basedir" mysql "--datadir" dataDir "--port" port "--socket" "${runtimeDir}/${instanceName}.sock" ]
|
||||
++ stdenv.lib.optionals (!forceDisableUserChange) [ "--user" user ];
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${group}" = {};
|
||||
};
|
||||
users = {
|
||||
"${user}" = {
|
||||
inherit group;
|
||||
description = "MySQL user";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
{createManagedProcess, stdenv, postgresql, stateDir, runtimeDir, forceDisableUserChange}:
|
||||
{port ? 5432, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
instanceName = "postgresql${instanceSuffix}";
|
||||
dataDir = "${stateDir}/db/${instanceName}/data";
|
||||
socketDir = "${runtimeDir}/${instanceName}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
in
|
||||
createManagedProcess rec {
|
||||
name = instanceName;
|
||||
inherit instanceName user;
|
||||
initialize = ''
|
||||
mkdir -m0700 -p ${socketDir}
|
||||
mkdir -m0700 -p ${dataDir}
|
||||
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
chown ${user}:${group} ${socketDir}
|
||||
chown ${user}:${group} ${dataDir}
|
||||
''}
|
||||
|
||||
if [ ! -e "${dataDir}/PG_VERSION" ]
|
||||
then
|
||||
${postgresql}/bin/initdb -D ${dataDir} --no-locale
|
||||
fi
|
||||
'';
|
||||
|
||||
process = "${postgresql}/bin/postgres";
|
||||
args = [ "-D" dataDir "-p" port "-k" socketDir ];
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${group}" = {};
|
||||
};
|
||||
users = {
|
||||
"${user}" = {
|
||||
inherit group;
|
||||
description = "PostgreSQL user";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
|
||||
instructions.start = {
|
||||
activity = "Starting";
|
||||
instruction = ''
|
||||
${initialize}
|
||||
${postgresql}/bin/pg_ctl -D ${dataDir} -o "-p ${toString port} -k ${socketDir}" start
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, processManager ? "sysvinit"
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
static-webapp-apache = rec {
|
||||
port = 8080;
|
||||
|
||||
pkg = constructors.static-webapp-apache {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
mysql = rec {
|
||||
port = 3307;
|
||||
|
||||
pkg = constructors.mysql {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
postgresql = rec {
|
||||
port = 6432;
|
||||
|
||||
pkg = constructors.postgresql {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
simple-appserving-tomcat = rec {
|
||||
httpPort = 8081;
|
||||
|
||||
pkg = constructors.simple-appserving-tomcat {
|
||||
inherit httpPort;
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
{createManagedProcess, stdenv, tomcat, jre, stateDir, runtimeDir, tmpDir, forceDisableUserChange}:
|
||||
{instanceSuffix ? "", serverPort ? 8005, httpPort ? 8080, httpsPort ? 8443, ajpPort ? 8009}:
|
||||
|
||||
let
|
||||
tomcatConfigFiles = stdenv.mkDerivation {
|
||||
name = "tomcat-config-files";
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
cd $out
|
||||
|
||||
mkdir conf
|
||||
cp ${tomcat}/conf/* conf
|
||||
sed -i \
|
||||
-e 's|<Server port="8005" shutdown="SHUTDOWN">|<Server port="${toString serverPort}" shutdown="SHUTDOWN">|' \
|
||||
-e 's|<Connector port="8080" protocol="HTTP/1.1"|<Connector port="${toString httpPort}" protocol="HTTP/1.1"|' \
|
||||
-e 's|redirectPort="8443"|redirectPort="${toString httpsPort}"|' \
|
||||
-e 's|<Connector port="8009" protocol="AJP/1.3"|<Connector port="${toString ajpPort}" protocol="AJP/1.3"|' \
|
||||
conf/server.xml
|
||||
|
||||
mkdir webapps
|
||||
cp -av ${tomcat.webapps}/webapps/* webapps
|
||||
'';
|
||||
};
|
||||
in
|
||||
import ./tomcat.nix {
|
||||
inherit createManagedProcess stdenv tomcat jre stateDir runtimeDir tmpDir forceDisableUserChange;
|
||||
} {
|
||||
inherit tomcatConfigFiles instanceSuffix;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
{createManagedProcess, stdenv, apacheHttpd, writeTextFile, logDir, runtimeDir, forceDisableUserChange}:
|
||||
{instanceSuffix ? "", port ? 80}:
|
||||
|
||||
let
|
||||
instanceName = "httpd${instanceSuffix}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
|
||||
modules = [
|
||||
"mpm_prefork"
|
||||
"authn_file"
|
||||
"authn_core"
|
||||
"authz_host"
|
||||
"authz_groupfile"
|
||||
"authz_user"
|
||||
"authz_core"
|
||||
"access_compat"
|
||||
"auth_basic"
|
||||
"reqtimeout"
|
||||
"filter"
|
||||
"mime"
|
||||
"log_config"
|
||||
"env"
|
||||
"headers"
|
||||
"setenvif"
|
||||
"version"
|
||||
"unixd"
|
||||
"status"
|
||||
"autoindex"
|
||||
"alias"
|
||||
"dir"
|
||||
];
|
||||
|
||||
apacheLogDir = "${logDir}/${instanceName}";
|
||||
in
|
||||
import ./apache.nix {
|
||||
inherit createManagedProcess apacheHttpd;
|
||||
} {
|
||||
inherit instanceSuffix;
|
||||
|
||||
initialize = ''
|
||||
mkdir -m0700 -p ${apacheLogDir}
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
chown ${user}:${group} ${apacheLogDir}
|
||||
''}
|
||||
'';
|
||||
|
||||
configFile = writeTextFile {
|
||||
name = "httpd.conf";
|
||||
text = ''
|
||||
ErrorLog "${apacheLogDir}/error_log"
|
||||
PidFile "${runtimeDir}/${instanceName}.pid"
|
||||
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
User ${user}
|
||||
Group ${group}
|
||||
''}
|
||||
|
||||
ServerName localhost
|
||||
ServerRoot ${apacheHttpd}
|
||||
|
||||
Listen ${toString port}
|
||||
|
||||
${stdenv.lib.concatMapStrings (module: ''
|
||||
LoadModule ${module}_module ${apacheHttpd}/modules/mod_${module}.so
|
||||
'') modules}
|
||||
|
||||
ServerAdmin root@localhost
|
||||
|
||||
DocumentRoot "${./webapp}"
|
||||
'';
|
||||
};
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
{createManagedProcess, stdenv, tomcat, jre, stateDir, runtimeDir, tmpDir, forceDisableUserChange}:
|
||||
{instanceSuffix ? "", tomcatConfigFiles}:
|
||||
|
||||
let
|
||||
instanceName = "tomcat${instanceSuffix}";
|
||||
baseDir = "${stateDir}/${instanceName}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
pidFile = "${runtimeDir}/${instanceName}.pid";
|
||||
in
|
||||
createManagedProcess rec {
|
||||
name = "tomcat";
|
||||
inherit instanceName user pidFile;
|
||||
process = "${tomcat}/bin/catalina.sh";
|
||||
args = [ "run" ];
|
||||
environment = {
|
||||
JRE_HOME = jre;
|
||||
CATALINA_TMPDIR = tmpDir;
|
||||
CATALINA_BASE = baseDir;
|
||||
CATALINA_PID = pidFile;
|
||||
};
|
||||
initialize = ''
|
||||
if [ ! -d "${baseDir}" ]
|
||||
then
|
||||
mkdir -p ${baseDir}/logs
|
||||
cd ${baseDir}
|
||||
|
||||
cp -av ${tomcatConfigFiles}/* .
|
||||
chmod -R u+w .
|
||||
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
chown -R ${user}:${group} ${baseDir}
|
||||
''}
|
||||
fi
|
||||
'';
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${group}" = {};
|
||||
};
|
||||
users = {
|
||||
"${user}" = {
|
||||
inherit group;
|
||||
description = "Tomcat user";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
instructions.start = {
|
||||
activity = "Starting";
|
||||
instruction = ''
|
||||
${initialize}
|
||||
${tomcat}/bin/startup.sh
|
||||
'';
|
||||
};
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE html>
|
||||
|
||||
<html>
|
||||
<head>
|
||||
<title>Hello world!</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>Hello world!</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,29 @@
|
|||
{ pkgs
|
||||
, stateDir
|
||||
, logDir
|
||||
, runtimeDir
|
||||
, tmpDir
|
||||
, forceDisableUserChange
|
||||
, processManager
|
||||
, webappMode # set to 'foreground' to make them all foreground process, 'daemon' to make them all daemons. null is to pick best option for the selected processManager
|
||||
}:
|
||||
|
||||
let
|
||||
createManagedProcess = import ../../nixproc/create-managed-process/agnostic/create-managed-process-universal.nix {
|
||||
inherit pkgs runtimeDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
|
||||
webappExpr = if webappMode == "foreground" then ./webapp-fg.nix
|
||||
else if webappMode == "daemon" then ./webapp-daemon.nix
|
||||
else ./webapp.nix;
|
||||
in
|
||||
{
|
||||
webapp = import webappExpr {
|
||||
inherit createManagedProcess runtimeDir;
|
||||
};
|
||||
|
||||
nginxReverseProxy = import ./nginx-reverse-proxy.nix {
|
||||
inherit createManagedProcess stateDir logDir runtimeDir forceDisableUserChange;
|
||||
inherit (pkgs) stdenv writeTextFile nginx;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{infrastructure}:
|
||||
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{infrastructure}:
|
||||
|
||||
{
|
||||
webapp = [ infrastructure.test1 ];
|
||||
nginxReverseProxy = [ infrastructure.test2 ];
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Disnix VirtualHosts example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
This web application is unavailable at the moment!
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
test1.properties.hostname = "test1";
|
||||
test2.properties.hostname = "test2";
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
let
|
||||
nixproc-generate-config = (import ../../tools {}).generate-config;
|
||||
in
|
||||
{
|
||||
test1 = {pkgs, ...}:
|
||||
|
||||
{
|
||||
dysnomia = {
|
||||
extraContainerProperties = {
|
||||
managed-process = {
|
||||
processManager = "systemd";
|
||||
NIX_PATH = "/root/.nix-defexpr/channels:nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
services.disnix.enable = true;
|
||||
services.openssh.enable = true;
|
||||
networking.firewall.enable = false;
|
||||
environment.systemPackages = [ pkgs.pythonPackages.supervisor nixproc-generate-config ];
|
||||
};
|
||||
|
||||
test2 = {pkgs, ...}:
|
||||
|
||||
{
|
||||
dysnomia = {
|
||||
extraContainerProperties = {
|
||||
managed-process = {
|
||||
processManager = "sysvinit";
|
||||
NIX_PATH = "/root/.nix-defexpr/channels:nixpkgs=/nix/var/nix/profiles/per-user/root/channels/nixos:nixos-config=/etc/nixos/configuration.nix:/nix/var/nix/profiles/per-user/root/channels";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
services.disnix.enable = true;
|
||||
services.openssh.enable = true;
|
||||
networking.firewall.enable = false;
|
||||
environment.systemPackages = [ pkgs.pythonPackages.supervisor nixproc-generate-config ];
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
test1 = {pkgs, ...}:
|
||||
{
|
||||
deployment.targetEnv = "virtualbox";
|
||||
deployment.virtualbox.memorySize = 4096; # megabytes
|
||||
};
|
||||
|
||||
test2 = {pkgs, ...}:
|
||||
{
|
||||
deployment.targetEnv = "virtualbox";
|
||||
deployment.virtualbox.memorySize = 4096; # megabytes
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,84 @@
|
|||
{createManagedProcess, stdenv, writeTextFile, nginx, runtimeDir, stateDir, logDir, forceDisableUserChange}:
|
||||
{port ? 80, webapps ? [], instanceSuffix ? ""}:
|
||||
interDeps:
|
||||
|
||||
let
|
||||
instanceName = "nginx${instanceSuffix}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
|
||||
nginxStateDir = "${stateDir}/${instanceName}";
|
||||
dependencies = webapps ++ (builtins.attrValues interDeps);
|
||||
|
||||
generateNginxConf = daemon:
|
||||
writeTextFile {
|
||||
name = "nginx.conf";
|
||||
text = ''
|
||||
error_log ${nginxStateDir}/logs/error.log;
|
||||
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
user ${user} ${group};
|
||||
''}
|
||||
|
||||
${if daemon then ''
|
||||
pid ${runtimeDir}/${instanceName}.pid;
|
||||
'' else ''
|
||||
daemon off;
|
||||
''}
|
||||
|
||||
events {
|
||||
worker_connections 190000;
|
||||
}
|
||||
|
||||
http {
|
||||
${stdenv.lib.concatMapStrings (dependency: ''
|
||||
upstream webapp${toString dependency.port} {
|
||||
server localhost:${toString dependency.port};
|
||||
}
|
||||
'') webapps}
|
||||
|
||||
${stdenv.lib.concatMapStrings (dependencyName:
|
||||
let
|
||||
dependency = builtins.getAttr dependencyName interDeps;
|
||||
in
|
||||
''
|
||||
upstream webapp${toString dependency.port} {
|
||||
server ${dependency.target.properties.hostname}:${toString dependency.port};
|
||||
}
|
||||
'') (builtins.attrNames interDeps)}
|
||||
|
||||
# Fallback virtual host displaying an error page. This is what users see
|
||||
# if they connect to a non-deployed web application.
|
||||
# Without it, nginx redirects to the first available virtual host, giving
|
||||
# unpredictable results. This could happen while an upgrade is in progress.
|
||||
|
||||
server {
|
||||
listen ${toString port};
|
||||
server_name aaaa;
|
||||
root ${./errorpage};
|
||||
}
|
||||
|
||||
${stdenv.lib.concatMapStrings (dependency: ''
|
||||
server {
|
||||
listen ${toString port};
|
||||
server_name ${dependency.dnsName};
|
||||
|
||||
location / {
|
||||
proxy_pass http://webapp${toString dependency.port};
|
||||
}
|
||||
}
|
||||
'') dependencies}
|
||||
}
|
||||
'';
|
||||
};
|
||||
in
|
||||
import ./nginx.nix {
|
||||
inherit createManagedProcess stdenv nginx stateDir forceDisableUserChange;
|
||||
} {
|
||||
inherit instanceSuffix;
|
||||
|
||||
dependencies = map (webapp: webapp.pkg) dependencies;
|
||||
|
||||
daemonConfigFile = generateNginxConf true;
|
||||
foregroundConfigFile = generateNginxConf false;
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
{createManagedProcess, stdenv, nginx, stateDir, forceDisableUserChange}:
|
||||
{daemonConfigFile, foregroundConfigFile, dependencies ? [], instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
instanceName = "nginx${instanceSuffix}";
|
||||
user = instanceName;
|
||||
group = instanceName;
|
||||
nginxLogDir = "${stateDir}/${instanceName}/logs";
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
description = "Nginx";
|
||||
initialize = ''
|
||||
mkdir -p ${nginxLogDir}
|
||||
${stdenv.lib.optionalString (!forceDisableUserChange) ''
|
||||
chown ${user}:${group} ${nginxLogDir}
|
||||
''}
|
||||
'';
|
||||
process = "${nginx}/bin/nginx";
|
||||
args = [ "-p" "${stateDir}/${instanceName}" ];
|
||||
foregroundProcessExtraArgs = [ "-c" foregroundConfigFile ];
|
||||
daemonExtraArgs = [ "-c" daemonConfigFile ];
|
||||
|
||||
inherit dependencies instanceName;
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${group}" = {};
|
||||
};
|
||||
users = {
|
||||
"${user}" = {
|
||||
inherit group;
|
||||
description = "Nginx user";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, processManager ? "sysvinit"
|
||||
, webappMode ? null
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager webappMode;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
webapp1 = rec {
|
||||
port = 5000;
|
||||
dnsName = "webapp1.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "1";
|
||||
};
|
||||
};
|
||||
|
||||
webapp2 = rec {
|
||||
port = 5001;
|
||||
dnsName = "webapp2.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "2";
|
||||
};
|
||||
};
|
||||
|
||||
webapp3 = rec {
|
||||
port = 5002;
|
||||
dnsName = "webapp3.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "3";
|
||||
};
|
||||
};
|
||||
|
||||
webapp4 = rec {
|
||||
port = 5003;
|
||||
dnsName = "webapp4.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "4";
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
port = 8080;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp1 webapp2 webapp3 webapp4 ];
|
||||
inherit port;
|
||||
} {};
|
||||
};
|
||||
|
||||
webapp5 = rec {
|
||||
port = 6002;
|
||||
dnsName = "webapp5.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "5";
|
||||
};
|
||||
};
|
||||
|
||||
webapp6 = rec {
|
||||
port = 6003;
|
||||
dnsName = "webapp6.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "6";
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy2 = rec {
|
||||
port = 8081;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp5 webapp6 ];
|
||||
inherit port;
|
||||
instanceSuffix = "2";
|
||||
} {};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, processManager
|
||||
, webappMode ? null
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager webappMode;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
webapp = rec {
|
||||
port = 5000;
|
||||
dnsName = "webapp.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
port = 8080;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp ];
|
||||
inherit port;
|
||||
} {};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
{ pkgs, distribution, invDistribution, system
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, processManager ? null # "sysvinit"
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange processManager;
|
||||
webappMode = null;
|
||||
};
|
||||
|
||||
processType =
|
||||
if processManager == null then "managed-process"
|
||||
else if processManager == "sysvinit" then "sysvinit-script"
|
||||
else if processManager == "systemd" then "systemd-unit"
|
||||
else if processManager == "supervisord" then "supervisord-program"
|
||||
else throw "Unknown process manager: ${processManager}";
|
||||
in
|
||||
rec {
|
||||
webapp = rec {
|
||||
name = "webapp";
|
||||
port = 5000;
|
||||
dnsName = "webapp.local";
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
};
|
||||
type = processType;
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
name = "nginxReverseProxy";
|
||||
port = 8080;
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
inherit port;
|
||||
};
|
||||
dependsOn = {
|
||||
inherit webapp;
|
||||
};
|
||||
type = processType;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{createManagedProcess, runtimeDir}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = import ../webapp;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
description = "Simple web application";
|
||||
inherit instanceName;
|
||||
|
||||
# This expression only specifies how to run webapp in daemon mode
|
||||
daemon = "${webapp}/bin/webapp";
|
||||
daemonArgs = [ "-D" ];
|
||||
|
||||
environment = {
|
||||
PORT = port;
|
||||
PID_FILE = "${runtimeDir}/${instanceName}.pid";
|
||||
};
|
||||
user = instanceName;
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{createManagedProcess, runtimeDir}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = import ../webapp;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
description = "Simple web application";
|
||||
inherit instanceName;
|
||||
|
||||
# This expression only specifies how to run the webapp in foreground mode
|
||||
foregroundProcess = "${webapp}/bin/webapp";
|
||||
|
||||
environment = {
|
||||
PORT = port;
|
||||
};
|
||||
user = instanceName;
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{createManagedProcess, runtimeDir}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = import ../../webapp;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createManagedProcess {
|
||||
name = instanceName;
|
||||
description = "Simple web application";
|
||||
inherit instanceName;
|
||||
|
||||
# This expression can both run in foreground or daemon mode.
|
||||
# The process manager can pick which mode it prefers.
|
||||
process = "${webapp}/bin/webapp";
|
||||
daemonArgs = [ "-D" ];
|
||||
|
||||
environment = {
|
||||
PORT = port;
|
||||
PID_FILE = "${runtimeDir}/${instanceName}.pid";
|
||||
};
|
||||
user = instanceName;
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
overrides = {
|
||||
sysvinit = {
|
||||
runlevels = [ 3 4 5 ];
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{ pkgs
|
||||
, stateDir
|
||||
, logDir
|
||||
, runtimeDir
|
||||
, tmpDir
|
||||
, forceDisableUserChange
|
||||
}:
|
||||
|
||||
let
|
||||
createSystemVInitScript = import ../../nixproc/create-managed-process/sysvinit/create-sysvinit-script.nix {
|
||||
inherit (pkgs) stdenv writeTextFile daemon;
|
||||
inherit runtimeDir tmpDir forceDisableUserChange;
|
||||
|
||||
createCredentials = import ../../nixproc/create-credentials {
|
||||
inherit (pkgs) stdenv;
|
||||
};
|
||||
|
||||
initFunctions = import ../../nixproc/create-managed-process/sysvinit/init-functions.nix {
|
||||
basePackages = [ pkgs.coreutils pkgs.gnused pkgs.inetutils pkgs.gnugrep pkgs.sysvinit ];
|
||||
inherit (pkgs) stdenv fetchurl;
|
||||
inherit runtimeDir;
|
||||
};
|
||||
};
|
||||
in
|
||||
{
|
||||
webapp = import ./webapp.nix {
|
||||
inherit createSystemVInitScript runtimeDir;
|
||||
};
|
||||
|
||||
nginxReverseProxy = import ./nginx-reverse-proxy.nix {
|
||||
inherit createSystemVInitScript stateDir logDir runtimeDir;
|
||||
inherit (pkgs) stdenv writeTextFile nginx;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
{infrastructure}:
|
||||
|
||||
{
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{infrastructure}:
|
||||
|
||||
{
|
||||
webapp = [ infrastructure.test1 ];
|
||||
nginxReverseProxy = [ infrastructure.test2 ];
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Disnix VirtualHosts example</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
This web application is unavailable at the moment!
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
test1 = {pkgs, ...}:
|
||||
|
||||
{
|
||||
services.disnix.enable = true;
|
||||
services.openssh.enable = true;
|
||||
networking.firewall.enable = false;
|
||||
};
|
||||
|
||||
test2 = {pkgs, ...}:
|
||||
|
||||
{
|
||||
services.disnix.enable = true;
|
||||
services.openssh.enable = true;
|
||||
networking.firewall.enable = false;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{
|
||||
test1 = {pkgs, ...}:
|
||||
{
|
||||
deployment.targetEnv = "virtualbox";
|
||||
deployment.virtualbox.memorySize = 4096; # megabytes
|
||||
};
|
||||
|
||||
test2 = {pkgs, ...}:
|
||||
{
|
||||
deployment.targetEnv = "virtualbox";
|
||||
deployment.virtualbox.memorySize = 4096; # megabytes
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
{createSystemVInitScript, stdenv, writeTextFile, nginx, runtimeDir, stateDir, logDir}:
|
||||
{port ? 80, webapps ? [], instanceSuffix ? ""}:
|
||||
interDeps:
|
||||
|
||||
let
|
||||
instanceName = "nginx${instanceSuffix}";
|
||||
nginxStateDir = "${stateDir}/${instanceName}";
|
||||
dependencies = webapps ++ (builtins.attrValues interDeps);
|
||||
in
|
||||
import ./nginx.nix {
|
||||
inherit createSystemVInitScript nginx instanceSuffix;
|
||||
stateDir = nginxStateDir;
|
||||
|
||||
dependencies = map (webapp: webapp.pkg) dependencies;
|
||||
|
||||
configFile = writeTextFile {
|
||||
name = "nginx.conf";
|
||||
text = ''
|
||||
error_log ${nginxStateDir}/logs/error.log;
|
||||
pid ${runtimeDir}/${instanceName}.pid;
|
||||
|
||||
events {
|
||||
worker_connections 190000;
|
||||
}
|
||||
|
||||
http {
|
||||
${stdenv.lib.concatMapStrings (dependency: ''
|
||||
upstream webapp${toString dependency.port} {
|
||||
server localhost:${toString dependency.port};
|
||||
}
|
||||
'') webapps}
|
||||
|
||||
${stdenv.lib.concatMapStrings (dependencyName:
|
||||
let
|
||||
dependency = builtins.getAttr dependencyName interDeps;
|
||||
in
|
||||
''
|
||||
upstream webapp${toString dependency.port} {
|
||||
server ${dependency.target.properties.hostname}:${toString dependency.port};
|
||||
}
|
||||
'') (builtins.attrNames interDeps)}
|
||||
|
||||
# Fallback virtual host displaying an error page. This is what users see
|
||||
# if they connect to a non-deployed web application.
|
||||
# Without it, nginx redirects to the first available virtual host, giving
|
||||
# unpredictable results. This could happen while an upgrade is in progress.
|
||||
|
||||
server {
|
||||
listen ${toString port};
|
||||
server_name aaaa;
|
||||
root ${./errorpage};
|
||||
}
|
||||
|
||||
${stdenv.lib.concatMapStrings (dependency: ''
|
||||
server {
|
||||
listen ${toString port};
|
||||
server_name ${dependency.dnsName};
|
||||
|
||||
location / {
|
||||
proxy_pass http://webapp${toString dependency.port};
|
||||
}
|
||||
}
|
||||
'') dependencies}
|
||||
}
|
||||
'';
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
{createSystemVInitScript, nginx, configFile, stateDir, dependencies ? [], instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
instanceName = "nginx${instanceSuffix}";
|
||||
in
|
||||
createSystemVInitScript {
|
||||
name = instanceName;
|
||||
description = "Nginx";
|
||||
initialize = ''
|
||||
mkdir -p ${stateDir}/logs
|
||||
'';
|
||||
process = "${nginx}/bin/nginx";
|
||||
args = [ "-c" configFile "-p" stateDir ];
|
||||
runlevels = [ 3 4 5 ];
|
||||
|
||||
inherit dependencies instanceName;
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
webapp1 = rec {
|
||||
port = 5000;
|
||||
dnsName = "webapp1.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "1";
|
||||
};
|
||||
};
|
||||
|
||||
webapp2 = rec {
|
||||
port = 5001;
|
||||
dnsName = "webapp2.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "2";
|
||||
};
|
||||
};
|
||||
|
||||
webapp3 = rec {
|
||||
port = 5002;
|
||||
dnsName = "webapp3.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "3";
|
||||
};
|
||||
};
|
||||
|
||||
webapp4 = rec {
|
||||
port = 5003;
|
||||
dnsName = "webapp4.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "4";
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
port = 8080;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp1 webapp2 webapp3 webapp4 ];
|
||||
inherit port;
|
||||
} {};
|
||||
};
|
||||
|
||||
webapp5 = rec {
|
||||
port = 6002;
|
||||
dnsName = "webapp5.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "5";
|
||||
};
|
||||
};
|
||||
|
||||
webapp6 = rec {
|
||||
port = 6003;
|
||||
dnsName = "webapp6.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
instanceSuffix = "6";
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy2 = rec {
|
||||
port = 8081;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp5 webapp6 ];
|
||||
inherit port;
|
||||
instanceSuffix = "2";
|
||||
} {};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
webapp = rec {
|
||||
port = 5000;
|
||||
dnsName = "webapp.local";
|
||||
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
};
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
port = 8080;
|
||||
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
webapps = [ webapp ];
|
||||
inherit port;
|
||||
} {};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
{ pkgs, distribution, invDistribution, system
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? true
|
||||
}:
|
||||
|
||||
let
|
||||
constructors = import ./constructors.nix {
|
||||
inherit pkgs stateDir runtimeDir logDir tmpDir forceDisableUserChange;
|
||||
};
|
||||
in
|
||||
rec {
|
||||
webapp = rec {
|
||||
name = "webapp";
|
||||
port = 5000;
|
||||
dnsName = "webapp.local";
|
||||
pkg = constructors.webapp {
|
||||
inherit port;
|
||||
};
|
||||
type = "sysvinit-script";
|
||||
};
|
||||
|
||||
nginxReverseProxy = rec {
|
||||
name = "nginxReverseProxy";
|
||||
port = 8080;
|
||||
pkg = constructors.nginxReverseProxy {
|
||||
inherit port;
|
||||
};
|
||||
dependsOn = {
|
||||
inherit webapp;
|
||||
};
|
||||
type = "sysvinit-script";
|
||||
};
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{createSystemVInitScript, runtimeDir}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = import ../../webapp;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createSystemVInitScript {
|
||||
name = instanceName;
|
||||
inherit instanceName;
|
||||
process = "${webapp}/bin/webapp";
|
||||
args = [ "-D" ];
|
||||
environment = {
|
||||
PORT = port;
|
||||
PID_FILE = "${runtimeDir}/${instanceName}.pid";
|
||||
};
|
||||
|
||||
runlevels = [ 3 4 5 ];
|
||||
|
||||
user = instanceName;
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
{stdenv}:
|
||||
{groups, users}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "credentials";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/dysnomia-support/groups
|
||||
|
||||
${stdenv.lib.concatMapStrings (groupname:
|
||||
let
|
||||
group = builtins.getAttr groupname groups;
|
||||
in
|
||||
''
|
||||
cat > $out/dysnomia-support/groups/${groupname} <<EOF
|
||||
${stdenv.lib.concatMapStrings (propertyName:
|
||||
let
|
||||
value = builtins.getAttr propertyName group;
|
||||
in
|
||||
"${propertyName}=${stdenv.lib.escapeShellArg value}\n"
|
||||
) (builtins.attrNames group)}
|
||||
EOF
|
||||
''
|
||||
) (builtins.attrNames groups)}
|
||||
|
||||
mkdir -p $out/dysnomia-support/users
|
||||
|
||||
${stdenv.lib.concatMapStrings (username:
|
||||
let
|
||||
user = builtins.getAttr username users;
|
||||
in
|
||||
''
|
||||
cat > $out/dysnomia-support/users/${username} <<EOF
|
||||
${stdenv.lib.concatMapStrings (propertyName:
|
||||
let
|
||||
value = builtins.getAttr propertyName user;
|
||||
in
|
||||
"${propertyName}=${stdenv.lib.escapeShellArg value}\n"
|
||||
) (builtins.attrNames user)}
|
||||
EOF
|
||||
''
|
||||
) (builtins.attrNames users)}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
{buildEnv}:
|
||||
{processes, processManager}:
|
||||
|
||||
let
|
||||
buildSysVInitEnv = import ../sysvinit/build-sysvinit-env.nix {
|
||||
inherit buildEnv;
|
||||
};
|
||||
|
||||
buildSystemdEnv = import ../systemd/build-systemd-env.nix {
|
||||
inherit buildEnv;
|
||||
};
|
||||
|
||||
buildSupervisordEnv = import ../supervisord/build-supervisord-env.nix {
|
||||
inherit buildEnv;
|
||||
};
|
||||
|
||||
buildBSDRCEnv = import ../bsdrc/build-bsdrc-env.nix {
|
||||
inherit buildEnv;
|
||||
};
|
||||
|
||||
buildLaunchdEnv = import ../launchd/build-launchd-env.nix {
|
||||
inherit buildEnv;
|
||||
};
|
||||
|
||||
buildCygrunsrvEnv = import ../cygrunsrv/build-cygrunsrv-env.nix {
|
||||
inherit buildEnv;
|
||||
};
|
||||
|
||||
buildProcessEnvFun =
|
||||
if processManager == "sysvinit" then buildSysVInitEnv
|
||||
else if processManager == "systemd" then buildSystemdEnv
|
||||
else if processManager == "supervisord" then buildSupervisordEnv
|
||||
else if processManager == "bsdrc" then buildBSDRCEnv
|
||||
else if processManager == "launchd" then buildLaunchdEnv
|
||||
else if processManager == "cygrunsrv" then buildCygrunsrvEnv
|
||||
else throw "Unknown process manager: ${processManager}";
|
||||
in
|
||||
buildProcessEnvFun {
|
||||
inherit processes;
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{stdenv}:
|
||||
{name, ...}@properties:
|
||||
|
||||
let
|
||||
configJSON = builtins.toJSON properties;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name configJSON;
|
||||
passAsFile = [ "configJSON" ];
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
cp $configJSONPath $out/${name}.json
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{ configFile
|
||||
, processManager
|
||||
, system ? builtins.currentSystem
|
||||
, pkgs ? import <nixpkgs> { inherit system; }
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
}:
|
||||
|
||||
let
|
||||
createManagedProcessFromConfig = configFile:
|
||||
let
|
||||
createManagedProcess = import ./create-managed-process-universal.nix {
|
||||
inherit pkgs runtimeDir tmpDir forceDisableUserChange processManager;
|
||||
};
|
||||
|
||||
properties = builtins.fromJSON (builtins.readFile configFile);
|
||||
|
||||
normalizedProperties = properties // pkgs.stdenv.lib.optionalAttrs (properties ? dependencies) {
|
||||
dependencies = map (dependency: createManagedProcessFromConfig "${dependency}/${builtins.substring 33 (builtins.stringLength dependency) (baseNameOf dependency)}.json") properties.dependencies;
|
||||
};
|
||||
in
|
||||
createManagedProcess normalizedProperties;
|
||||
in
|
||||
createManagedProcessFromConfig configFile
|
|
@ -0,0 +1,98 @@
|
|||
{pkgs, runtimeDir, tmpDir, forceDisableUserChange ? false, processManager ? null}:
|
||||
|
||||
let
|
||||
basePackages = [
|
||||
pkgs.coreutils
|
||||
pkgs.gnused
|
||||
pkgs.gnugrep
|
||||
pkgs.inetutils
|
||||
];
|
||||
|
||||
createCredentials = import ../../create-credentials {
|
||||
inherit (pkgs) stdenv;
|
||||
};
|
||||
|
||||
createSystemVInitScript = import ../sysvinit/create-sysvinit-script.nix {
|
||||
inherit (pkgs) stdenv writeTextFile daemon;
|
||||
inherit createCredentials runtimeDir tmpDir forceDisableUserChange;
|
||||
|
||||
initFunctions = import ../sysvinit/init-functions.nix {
|
||||
inherit (pkgs) stdenv fetchurl;
|
||||
inherit runtimeDir;
|
||||
basePackages = basePackages ++ [ pkgs.sysvinit ];
|
||||
};
|
||||
};
|
||||
|
||||
generateSystemVInitScript = import ./generate-sysvinit-script.nix {
|
||||
inherit createSystemVInitScript;
|
||||
inherit (pkgs) stdenv;
|
||||
};
|
||||
|
||||
createSystemdService = import ../systemd/create-systemd-service.nix {
|
||||
inherit (pkgs) writeTextFile stdenv;
|
||||
inherit createCredentials basePackages forceDisableUserChange;
|
||||
};
|
||||
|
||||
generateSystemdService = import ./generate-systemd-service.nix {
|
||||
inherit createSystemdService;
|
||||
inherit (pkgs) stdenv writeTextFile;
|
||||
};
|
||||
|
||||
createSupervisordProgram = import ../supervisord/create-supervisord-program.nix {
|
||||
inherit (pkgs) writeTextFile stdenv;
|
||||
inherit (pkgs.pythonPackages) supervisor;
|
||||
inherit createCredentials basePackages forceDisableUserChange runtimeDir;
|
||||
};
|
||||
|
||||
generateSupervisordProgram = import ./generate-supervisord-program.nix {
|
||||
inherit createSupervisordProgram runtimeDir;
|
||||
inherit (pkgs) stdenv writeTextFile;
|
||||
};
|
||||
|
||||
createBSDRCScript = import ../bsdrc/create-bsdrc-script.nix {
|
||||
inherit (pkgs) writeTextFile stdenv;
|
||||
inherit createCredentials forceDisableUserChange runtimeDir;
|
||||
|
||||
rcSubr = import ../bsdrc/rcsubr.nix {
|
||||
inherit (pkgs) stdenv;
|
||||
inherit forceDisableUserChange;
|
||||
};
|
||||
};
|
||||
|
||||
generateBSDRCScript = import ../agnostic/generate-bsdrc-script.nix {
|
||||
inherit createBSDRCScript;
|
||||
inherit (pkgs) stdenv;
|
||||
};
|
||||
|
||||
createLaunchdDaemon = import ../launchd/create-launchd-daemon.nix {
|
||||
inherit (pkgs) writeTextFile stdenv;
|
||||
inherit createCredentials forceDisableUserChange;
|
||||
};
|
||||
|
||||
generateLaunchdDaemon = import ../agnostic/generate-launchd-daemon.nix {
|
||||
inherit (pkgs) stdenv writeTextFile;
|
||||
inherit createLaunchdDaemon runtimeDir;
|
||||
};
|
||||
|
||||
createCygrunsrvParams = import ../cygrunsrv/create-cygrunsrv-params.nix {
|
||||
inherit (pkgs) writeTextFile stdenv;
|
||||
};
|
||||
|
||||
generateCygrunsrvParams = import ../agnostic/generate-cygrunsrv-params.nix {
|
||||
inherit (pkgs) stdenv writeTextFile;
|
||||
inherit createCygrunsrvParams runtimeDir;
|
||||
};
|
||||
in
|
||||
import ./create-managed-process.nix {
|
||||
inherit processManager;
|
||||
inherit (pkgs) stdenv;
|
||||
|
||||
generateProcessFun =
|
||||
if processManager == "sysvinit" then generateSystemVInitScript
|
||||
else if processManager == "systemd" then generateSystemdService
|
||||
else if processManager == "supervisord" then generateSupervisordProgram
|
||||
else if processManager == "bsdrc" then generateBSDRCScript
|
||||
else if processManager == "launchd" then generateLaunchdDaemon
|
||||
else if processManager == "cygrunsrv" then generateCygrunsrvParams
|
||||
else throw "Unknown process manager: ${processManager}";
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
{ generateProcessFun, processManager, stdenv }:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# A more human-readable description of the process
|
||||
, description ? name
|
||||
# Shell commands that specify how the state should be initialized
|
||||
, initialize ? ""
|
||||
# Path to a process to execute (both in foreground and daemon mode)
|
||||
, process ? null
|
||||
# Generic command-line parameters propagated to the process
|
||||
, args ? []
|
||||
# The executable that needs to run to start the process is daemon mode
|
||||
, daemon ? process
|
||||
# Extra arguments appended to args when the process runs in daemon mode
|
||||
, daemonExtraArgs ? []
|
||||
# Command-line arguments propagated to the daemon
|
||||
, daemonArgs ? (args ++ daemonExtraArgs)
|
||||
# A name that uniquely identifies each process instance. It is used to generate a unique PID file.
|
||||
, instanceName ? null
|
||||
# Path to a PID file that the system should use to manage the process. If null, it will use a default path.
|
||||
, pidFile ? null
|
||||
# The executable that needs to run to start the process in foreground mode
|
||||
, foregroundProcess ? process
|
||||
# Extra arguments appended to args when the process runs in foreground mode
|
||||
, foregroundProcessExtraArgs ? []
|
||||
# Command-line arguments propagated to the foreground process
|
||||
, foregroundProcessArgs ? (args ++ foregroundProcessExtraArgs)
|
||||
# Specifies which packages need to be in the PATH
|
||||
, path ? []
|
||||
# An attribute set specifying arbitrary environment variables
|
||||
, environment ? {}
|
||||
# If not null, the current working directory will be changed before executing any activities
|
||||
, directory ? null
|
||||
# If not null, the umask will be changed before executing any activities
|
||||
, umask ? null
|
||||
# If not null, the nice level be changed before executing any activities
|
||||
, nice ? null
|
||||
# Specifies as which user the process should run. If null, the user privileges will not be changed.
|
||||
, user ? null
|
||||
# Dependencies on other processes. Typically, this specification is used to derive the activation order.
|
||||
, dependencies ? []
|
||||
# Specifies which groups and users that need to be created.
|
||||
, credentials ? {}
|
||||
# Specifies process manager specific properties that augmented to the generated function parameters
|
||||
, overrides ? {}
|
||||
}@properties:
|
||||
|
||||
let
|
||||
createAgnosticConfig = import ./create-agnostic-config.nix {
|
||||
inherit stdenv;
|
||||
};
|
||||
in
|
||||
if processManager == null then createAgnosticConfig properties
|
||||
else generateProcessFun {
|
||||
inherit name description initialize daemon daemonArgs instanceName pidFile foregroundProcess foregroundProcessArgs path environment directory umask nice user dependencies credentials;
|
||||
overrides = if builtins.hasAttr processManager overrides then builtins.getAttr processManager overrides else {};
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
{ createBSDRCScript, stdenv }:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
}:
|
||||
|
||||
# TODO: umask
|
||||
|
||||
createBSDRCScript (stdenv.lib.recursiveUpdate ({
|
||||
inherit name environment path directory nice dependencies;
|
||||
inherit user instanceName credentials;
|
||||
|
||||
command = if daemon != null then daemon else foregroundProcess;
|
||||
commandIsDaemon = daemon != null;
|
||||
commandArgs = if daemon != null then daemonArgs else foregroundProcessArgs;
|
||||
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
} // stdenv.lib.optionalAttrs (initialize != "") {
|
||||
commands.start.pre = initialize;
|
||||
}) overrides)
|
|
@ -0,0 +1,57 @@
|
|||
{ createCygrunsrvParams
|
||||
, stdenv
|
||||
, writeTextFile
|
||||
, runtimeDir ? "/var/run"
|
||||
}:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
}:
|
||||
|
||||
# TODO: credentials
|
||||
# TODO: directory unused
|
||||
# TODO: umask unused
|
||||
# TODO: nice unused
|
||||
# TODO: user unused
|
||||
|
||||
let
|
||||
generateForegroundWrapper = import ./generate-foreground-wrapper.nix {
|
||||
inherit stdenv writeTextFile;
|
||||
};
|
||||
in
|
||||
createCygrunsrvParams (stdenv.lib.recursiveUpdate ({
|
||||
inherit name environment dependencies;
|
||||
|
||||
environmentPath = path;
|
||||
|
||||
path = if foregroundProcess != null then
|
||||
if initialize == "" then foregroundProcess
|
||||
else generateForegroundWrapper {
|
||||
wrapDaemon = false;
|
||||
executable = foregroundProcess;
|
||||
inherit name initialize runtimeDir pidFile stdenv;
|
||||
}
|
||||
else generateForegroundWrapper {
|
||||
wrapDaemon = true;
|
||||
executable = daemon;
|
||||
inherit name initialize runtimeDir pidFile stdenv;
|
||||
};
|
||||
|
||||
args = if foregroundProcess != null then foregroundProcessArgs else daemonArgs;
|
||||
}) overrides)
|
|
@ -0,0 +1,83 @@
|
|||
{stdenv, writeTextFile}:
|
||||
{ name
|
||||
, wrapDaemon
|
||||
, initialize
|
||||
, executable
|
||||
, stdenv
|
||||
, runtimeDir
|
||||
, instanceName ? null
|
||||
, pidFile ? (if instanceName == null then null else "${runtimeDir}/${instanceName}.pid")
|
||||
}:
|
||||
|
||||
let
|
||||
_pidFile = if pidFile == null then "${runtimeDir}/$(basename ${executable}).pid" else pidFile;
|
||||
in
|
||||
writeTextFile {
|
||||
name = "${name}-foregroundwrapper.sh";
|
||||
text = ''
|
||||
#! ${stdenv.shell} -e
|
||||
|
||||
${initialize}
|
||||
|
||||
${if wrapDaemon then ''
|
||||
export _TOP_PID=$$
|
||||
|
||||
# Handle to SIGTERM and SIGINT signals and forward them to the daemon process
|
||||
_term()
|
||||
{
|
||||
trap "exit 0" TERM
|
||||
kill -TERM "$pid"
|
||||
kill $_TOP_PID
|
||||
}
|
||||
|
||||
_interrupt()
|
||||
{
|
||||
kill -INT "$pid"
|
||||
}
|
||||
|
||||
trap _term SIGTERM
|
||||
trap _interrupt SIGINT
|
||||
|
||||
# Start process in the background as a daemon
|
||||
${executable} "$@"
|
||||
|
||||
# Wait for the PID file to become available. Useful to work with daemons that don't behave well enough.
|
||||
count=0
|
||||
|
||||
while [ ! -f "${_pidFile}" ]
|
||||
do
|
||||
if [ $count -eq 10 ]
|
||||
then
|
||||
echo "It does not seem that there isn't any pid file! Giving up!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting for ${_pidFile} to become available..."
|
||||
sleep 1
|
||||
|
||||
((count=count++))
|
||||
done
|
||||
|
||||
# Determine the daemon's PID by using the PID file
|
||||
pid=$(cat ${_pidFile})
|
||||
|
||||
# Wait in the background for the PID to terminate
|
||||
${if stdenv.isDarwin then ''
|
||||
lsof -p $pid +r 3 &>/dev/null &
|
||||
'' else if stdenv.isLinux || stdenv.isCygwin then ''
|
||||
tail --pid=$pid -f /dev/null &
|
||||
'' else if stdenv.isBSD || stdenv.isSunOS then ''
|
||||
pwait $pid &
|
||||
'' else throw "Don't know how to wait for process completion on system: ${stdenv.system}"}
|
||||
|
||||
# Wait for the blocker process to complete. We use wait, so that bash can still
|
||||
# handle the SIGTERM and SIGINT signals that may be sent to it by a process
|
||||
# manager
|
||||
blocker_pid=$!
|
||||
wait $blocker_pid
|
||||
'' else ''
|
||||
exec "${executable}" "$@"
|
||||
''}
|
||||
'';
|
||||
executable = true;
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
{ createLaunchdDaemon
|
||||
, stdenv
|
||||
, writeTextFile
|
||||
, runtimeDir ? "/var/run"
|
||||
}:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
}:
|
||||
|
||||
let
|
||||
generateForegroundWrapper = import ./generate-foreground-wrapper.nix {
|
||||
inherit stdenv writeTextFile;
|
||||
};
|
||||
|
||||
Program = if foregroundProcess != null then
|
||||
if initialize == "" then foregroundProcess
|
||||
else generateForegroundWrapper ({
|
||||
wrapDaemon = false;
|
||||
executable = foregroundProcess;
|
||||
inherit name initialize runtimeDir stdenv;
|
||||
} // stdenv.lib.optionalAttrs (instanceName != null) {
|
||||
inherit instanceName;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
})
|
||||
else generateForegroundWrapper ({
|
||||
wrapDaemon = true;
|
||||
executable = daemon;
|
||||
inherit name initialize runtimeDir stdenv;
|
||||
} // stdenv.lib.optionalAttrs (instanceName != null) {
|
||||
inherit instanceName;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
});
|
||||
ProgramArguments = [ Program ] ++ (if foregroundProcess != null then foregroundProcessArgs else daemonArgs);
|
||||
|
||||
daemonConfig = createLaunchdDaemon (stdenv.lib.recursiveUpdate ({
|
||||
inherit name credentials Program;
|
||||
} // stdenv.lib.optionalAttrs (ProgramArguments != [ Program ]) {
|
||||
inherit ProgramArguments;
|
||||
} // stdenv.lib.optionalAttrs (environment != {}) {
|
||||
EnvironmentVariables = environment;
|
||||
} // stdenv.lib.optionalAttrs (path != []) {
|
||||
inherit path;
|
||||
} // stdenv.lib.optionalAttrs (directory != null) {
|
||||
WorkingDirectory = directory;
|
||||
} // stdenv.lib.optionalAttrs (umask != null) {
|
||||
Umask = umask;
|
||||
} // stdenv.lib.optionalAttrs (nice != null) {
|
||||
Nice = nice;
|
||||
} // stdenv.lib.optionalAttrs (user != null) {
|
||||
UserName = user;
|
||||
}) overrides);
|
||||
in
|
||||
if dependencies == [] then daemonConfig else
|
||||
builtins.trace "WARNING: dependencies have been specified for process: ${name}, but launchd has no notion of process dependencies. Proper activation ordering cannot be guaranteed!" daemonConfig
|
|
@ -0,0 +1,11 @@
|
|||
{ stdenv, writeTextFile }:
|
||||
{ name, initialize }:
|
||||
|
||||
writeTextFile {
|
||||
name = "${name}-prestart";
|
||||
executable = true;
|
||||
text = ''
|
||||
#! ${stdenv.shell} -e
|
||||
${initialize}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
{ createSupervisordProgram, stdenv, writeTextFile, runtimeDir }:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
}:
|
||||
|
||||
let
|
||||
generateForegroundWrapper = import ./generate-foreground-wrapper.nix {
|
||||
inherit stdenv writeTextFile;
|
||||
};
|
||||
|
||||
command = if foregroundProcess != null then
|
||||
(if initialize == ""
|
||||
then foregroundProcess
|
||||
else generateForegroundWrapper ({
|
||||
wrapDaemon = false;
|
||||
executable = foregroundProcess;
|
||||
inherit name initialize runtimeDir stdenv;
|
||||
} // stdenv.lib.optionalAttrs (instanceName != null) {
|
||||
inherit instanceName;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
})) + " ${toString foregroundProcessArgs}"
|
||||
else (generateForegroundWrapper ({
|
||||
wrapDaemon = true;
|
||||
executable = daemon;
|
||||
inherit name initialize runtimeDir stdenv;
|
||||
} // stdenv.lib.optionalAttrs (instanceName != null) {
|
||||
inherit instanceName;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
})) + " ${toString daemonArgs}";
|
||||
in
|
||||
createSupervisordProgram (stdenv.lib.recursiveUpdate ({
|
||||
inherit name command path environment dependencies credentials;
|
||||
} // stdenv.lib.optionalAttrs (umask != null) {
|
||||
inherit umask;
|
||||
} // stdenv.lib.optionalAttrs (nice != null) {
|
||||
inherit nice;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
} // stdenv.lib.optionalAttrs (user != null) {
|
||||
inherit user;
|
||||
}) overrides)
|
|
@ -0,0 +1,52 @@
|
|||
{ createSystemdService, stdenv, writeTextFile }:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
}:
|
||||
|
||||
let
|
||||
generatePreStartScript = import ./generate-prestart-script.nix {
|
||||
inherit stdenv writeTextFile;
|
||||
};
|
||||
in
|
||||
createSystemdService (stdenv.lib.recursiveUpdate {
|
||||
inherit name path environment dependencies credentials;
|
||||
|
||||
Unit = {
|
||||
Description = description;
|
||||
};
|
||||
Service = {
|
||||
ExecStart = if foregroundProcess != null then "${foregroundProcess} ${toString foregroundProcessArgs}" else "${daemon} ${toString daemonArgs}";
|
||||
Type = if foregroundProcess != null then "simple" else "forking";
|
||||
} // stdenv.lib.optionalAttrs (initialize != "") {
|
||||
ExecStartPre = stdenv.lib.optionalString (user != null) "+" + generatePreStartScript {
|
||||
inherit name initialize;
|
||||
};
|
||||
} // stdenv.lib.optionalAttrs (directory != null) {
|
||||
WorkingDirectory = directory;
|
||||
} // stdenv.lib.optionalAttrs (umask != null) {
|
||||
UMask = umask;
|
||||
} // stdenv.lib.optionalAttrs (nice != null) {
|
||||
Nice = nice;
|
||||
} // stdenv.lib.optionalAttrs (foregroundProcess == null && pidFile != null) {
|
||||
PIDFile = pidFile;
|
||||
} // stdenv.lib.optionalAttrs (user != null) {
|
||||
User = user;
|
||||
};
|
||||
} overrides)
|
|
@ -0,0 +1,32 @@
|
|||
{ createSystemVInitScript, stdenv }:
|
||||
|
||||
{ name
|
||||
, description
|
||||
, initialize
|
||||
, daemon
|
||||
, daemonArgs
|
||||
, instanceName
|
||||
, pidFile
|
||||
, foregroundProcess
|
||||
, foregroundProcessArgs
|
||||
, path
|
||||
, environment
|
||||
, directory
|
||||
, umask
|
||||
, nice
|
||||
, user
|
||||
, dependencies
|
||||
, credentials
|
||||
, overrides
|
||||
}:
|
||||
|
||||
createSystemVInitScript (stdenv.lib.recursiveUpdate ({
|
||||
inherit name description path environment directory umask nice dependencies credentials;
|
||||
inherit instanceName initialize user;
|
||||
|
||||
process = if daemon != null then daemon else foregroundProcess;
|
||||
processIsDaemon = daemon != null;
|
||||
args = if daemon != null then daemonArgs else foregroundProcessArgs;
|
||||
} // stdenv.lib.optionalAttrs (pidFile != null) {
|
||||
inherit pidFile;
|
||||
}) overrides)
|
|
@ -0,0 +1,30 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "bsdrc";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "rc.d";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
}
|
|
@ -0,0 +1,237 @@
|
|||
{ writeTextFile
|
||||
, stdenv
|
||||
, createCredentials
|
||||
|
||||
# Path to the rc.subr script
|
||||
, rcSubr ? "/etc/rc.subr"
|
||||
# Specifies which command are builtin. This is to determine which extra commands may have been provided.
|
||||
, builtinCommands ? [ "start" "stop" "reload" "restart" "status" "poll" "rcvar" ]
|
||||
# Specifies the default signal used for reloading a process
|
||||
, defaultReloadSignal ? "HUP"
|
||||
# Specifies the default signal used for stopping a process
|
||||
, defaultStopSignal ? "TERM"
|
||||
# Default run time directory where PID files are stored
|
||||
, runtimeDir ? "/var/run"
|
||||
# Specifies whether user changing functionality should be disabled or not
|
||||
, forceDisableUserChange ? false
|
||||
}:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# The variable suffix that indicates whether a service has been enabled or not.
|
||||
, rcvar ? "enabled"
|
||||
# An attribute set defining default values for configuration environment variables
|
||||
, rcvarsDefaults ? {}
|
||||
# The command that executes a daemon
|
||||
, command ? null
|
||||
# The command-line parameters passed to the command
|
||||
, commandArgs ? []
|
||||
# Specifies whether the command daemonizes or not. If the command is not a daemon, it gets daemonized by the generator
|
||||
, commandIsDaemon ? true
|
||||
# Specifies the signal that needs to be sent to reload a process
|
||||
, reloadSignal ? "HUP"
|
||||
# Specifies the signal that needs to be sent to stop a process
|
||||
, stopSignal ? "TERM"
|
||||
# Specifies which packages need to be in the PATH
|
||||
, environment ? {}
|
||||
# An attribute set specifying arbitrary environment variables
|
||||
, path ? []
|
||||
# A name that uniquely identifies each process instance. It is used to generate a unique PID file.
|
||||
, instanceName ? null
|
||||
# Path to a PID file that the system should use to manage the process. If null, it will use a default path.
|
||||
, pidFile ? (if instanceName == null then null else "${runtimeDir}/${instanceName}.pid")
|
||||
# If not null, the nice level be changed before executing any activities
|
||||
, nice ? null
|
||||
# If not null, the current working directory will be changed before executing any activities
|
||||
, directory ? null
|
||||
# Specifies as which user the process should run. If null, the user privileges will not be changed.
|
||||
, user ? null
|
||||
# Defines files that must be readable before running the start method
|
||||
, requiredFiles ? []
|
||||
# Defines directories that must exist before running the start method
|
||||
, requiredDirs ? []
|
||||
# Defines external environment variables this script depends on
|
||||
, requiredVars ? []
|
||||
# Perform checkyesno on each of the list variables before running the start method.
|
||||
, requiredModules ? []
|
||||
# Specifies the implementation of arbitrary commands
|
||||
, commands ? {}
|
||||
# If set to true the rc script accepts an arbitrary number of parameters. If set to false, it only accepts one.
|
||||
, flexibleParameters ? false
|
||||
# Specifies which feature the script requires. This is used by the rc init system to determine the proper activation order
|
||||
, requires ? []
|
||||
# Specifies which features the script provides. By default, it simply considers it name a feature.
|
||||
, provides ? [ "${name}" ]
|
||||
# Keywords to be display in the comments section
|
||||
, keywords ? []
|
||||
# A list of bsd rc scripts that this script depends on
|
||||
, dependencies ? []
|
||||
# Specifies which groups and users that need to be created.
|
||||
, credentials ? {}
|
||||
}:
|
||||
|
||||
# TODO:
|
||||
# umask
|
||||
# other properties. see rc.subr manpage
|
||||
|
||||
assert command == null -> commands ? start && commands ? stop;
|
||||
|
||||
let
|
||||
extraCommands = builtins.attrNames (removeAttrs commands builtinCommands);
|
||||
|
||||
_user = if forceDisableUserChange then null else user;
|
||||
|
||||
_command = if commandIsDaemon then command else "daemon";
|
||||
_commandArgs = if commandIsDaemon then commandArgs else
|
||||
stdenv.lib.optionals (pidFile != null) [ "-p" pidFile ]
|
||||
++ [ command ]
|
||||
++ commandArgs;
|
||||
|
||||
_requires = map (dependency: dependency.name) dependencies ++ requires;
|
||||
|
||||
envFile = if environment == {} then null else writeTextFile {
|
||||
name = "${name}-envfile";
|
||||
text = stdenv.lib.concatMapStrings (name:
|
||||
let
|
||||
value = builtins.getAttr name environment;
|
||||
in
|
||||
''${name}=${stdenv.lib.escapeShellArg value}
|
||||
''
|
||||
) (builtins.attrNames environment);
|
||||
};
|
||||
|
||||
rcScript = writeTextFile {
|
||||
inherit name;
|
||||
executable = true;
|
||||
text = ''
|
||||
#!/bin/sh
|
||||
''
|
||||
+ stdenv.lib.optionalString (provides != []) ''
|
||||
# PROVIDE: ${toString provides}
|
||||
''
|
||||
+ stdenv.lib.optionalString (_requires != []) ''
|
||||
# REQUIRE: ${toString _requires}
|
||||
''
|
||||
+ stdenv.lib.optionalString (keywords != []) ''
|
||||
# KEYWORD: ${toString keywords}
|
||||
'' +
|
||||
''
|
||||
. ${rcSubr}
|
||||
|
||||
name="${name}"
|
||||
'' + stdenv.lib.optionalString (rcvar != null) ''
|
||||
rcvar=''${name}_${rcvar}
|
||||
''
|
||||
+ ''
|
||||
|
||||
load_rc_config $name
|
||||
${stdenv.lib.concatMapStrings (rcvarName: ''
|
||||
: ''${name}_${rcvarName}:=${toString builtins.getAttr rcvarName rcvarsDefaults}
|
||||
'') (builtins.attrNames rcvarsDefaults)}
|
||||
|
||||
''
|
||||
+ stdenv.lib.optionalString (_command != null) ''
|
||||
command=${_command}
|
||||
''
|
||||
+ stdenv.lib.optionalString (_commandArgs != []) ''
|
||||
command_args="${toString _commandArgs}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (requiredDirs != []) ''
|
||||
required_dirs="${toString requiredDirs}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (requiredFiles != []) ''
|
||||
required_files="${toString requiredFiles}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (requiredVars != []) ''
|
||||
required_vars="${toString requiredVars}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (requiredModules != []) ''
|
||||
required_modules="${toString requiredModules}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (pidFile != null) ''
|
||||
pidfile="${pidFile}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (reloadSignal != defaultReloadSignal) ''
|
||||
sig_reload="${reloadSignal}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (stopSignal != defaultStopSignal) ''
|
||||
sig_stop="${stopSignal}"
|
||||
''
|
||||
+ stdenv.lib.optionalString (nice != null) ''
|
||||
${name}_nice=${toString nice}
|
||||
''
|
||||
+ stdenv.lib.optionalString (directory != null) ''
|
||||
${name}_chdir=${directory}
|
||||
''
|
||||
+ stdenv.lib.optionalString (_user != null) ''
|
||||
${name}_user=${_user}
|
||||
''
|
||||
+ stdenv.lib.optionalString (envFile != null) ''
|
||||
${name}_env_file=${envFile}
|
||||
''
|
||||
+ stdenv.lib.optionalString (extraCommands != []) ''
|
||||
extra_commands="${toString extraCommands}"
|
||||
''
|
||||
+ stdenv.lib.concatMapStrings (commandName:
|
||||
let
|
||||
command = builtins.getAttr commandName commands;
|
||||
in
|
||||
stdenv.lib.optionalString (command ? pre) ''${commandName}_precmd=''${name}_pre${commandName}
|
||||
''
|
||||
+ stdenv.lib.optionalString (command ? implementation) ''${commandName}_cmd=''${name}_${commandName}
|
||||
''
|
||||
+ stdenv.lib.optionalString (command ? post) ''${commandName}_postcmd=''${name}_post${commandName}
|
||||
''
|
||||
) (builtins.attrNames commands)
|
||||
+ stdenv.lib.optionalString (path != []) ''
|
||||
|
||||
PATH="${builtins.concatStringsSep ":" (map(package: "${package}/bin") path)}:$PATH"
|
||||
export PATH
|
||||
''
|
||||
+ "\n"
|
||||
+ stdenv.lib.concatMapStrings (commandName:
|
||||
let
|
||||
command = builtins.getAttr commandName commands;
|
||||
in
|
||||
''
|
||||
${stdenv.lib.optionalString (command ? pre) ''
|
||||
${name}_pre${commandName}()
|
||||
{
|
||||
${command.pre}
|
||||
}
|
||||
''}
|
||||
${stdenv.lib.optionalString (command ? implementation) ''
|
||||
${name}_${commandName}()
|
||||
{
|
||||
${command.implementation}
|
||||
}
|
||||
''}
|
||||
${stdenv.lib.optionalString (command ? post) ''
|
||||
${name}_post${commandName}()
|
||||
{
|
||||
${command.post}
|
||||
}
|
||||
''}
|
||||
''
|
||||
) (builtins.attrNames commands)
|
||||
+ ''
|
||||
run_rc_command "${if flexibleParameters then "$@" else "$1"}"
|
||||
'';
|
||||
};
|
||||
|
||||
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name;
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $out/etc/rc.d
|
||||
cd $out/etc/rc.d
|
||||
ln -s ${rcScript} ${name}
|
||||
|
||||
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
||||
ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support
|
||||
''}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
{stdenv, forceDisableUserChange}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "rc.subr";
|
||||
src = /etc/rc.subr;
|
||||
# Disable the limits command when we want to deploy processes as an unprivileged user
|
||||
buildCommand = if forceDisableUserChange then ''
|
||||
sed -e 's|limits -C $_login_class $_limits||' $src > $out
|
||||
'' else ''
|
||||
cp $src $out
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "cygrunsrv";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "cygrunsrv-env";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
}
|
|
@ -0,0 +1,112 @@
|
|||
{ stdenv
|
||||
, writeTextFile
|
||||
|
||||
# Prefix that is in front of all Windows services generated by this function
|
||||
, prefix ? "nix-process-"
|
||||
}:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# A more human readable name that identifies the process
|
||||
, displayName ? "${prefix}${name}"
|
||||
# Path to the executable to run
|
||||
, path
|
||||
# Command-line arguments propagated to the executable
|
||||
, args ? []
|
||||
# An attribute set specifying arbitrary environment variables
|
||||
, environment ? {}
|
||||
# Specifies whether this service needs to be automatically started or not.
|
||||
# 'manual' indicates manual start, 'auto' indicates automatic start
|
||||
, type ? "auto"
|
||||
# Specifies as which user the process should run. If null, the user privileges will not be changed.
|
||||
, user ? null
|
||||
# The password of the user so that the user privileges can be changed
|
||||
, password ? null
|
||||
# File where the stdin should read from. null indicates that no file should be read
|
||||
, stdin ? null
|
||||
# File where the stdout should write to. null discards output
|
||||
, stdout ? null
|
||||
# File where the stderr should write to. null discards output
|
||||
, stderr ? null
|
||||
# The signal that needs to be sent to the process to terminate it
|
||||
, terminateSignal ? "TERM"
|
||||
# Indicates whether the process should be terminated on shutdown
|
||||
, terminateOnShutdown ? false
|
||||
# Dependencies on other Windows services. The service manager makes sure that dependencies are activated first.
|
||||
, dependencies ? []
|
||||
# Specifies which packages need to be in the PATH
|
||||
, environmentPath ? []
|
||||
}:
|
||||
|
||||
let
|
||||
_environment = stdenv.lib.optionalAttrs (environmentPath != []) {
|
||||
PATH = builtins.concatStringsSep ":" (map (package: "${package}/bin") environmentPath); # Augment path environment variable, if applicable
|
||||
} // environment;
|
||||
|
||||
cygrunsrvConfig = writeTextFile {
|
||||
name = "${prefix}${name}-cygrunsrv-params";
|
||||
text = ''
|
||||
--path
|
||||
${path}
|
||||
--disp
|
||||
${displayName}
|
||||
''
|
||||
+ stdenv.lib.optionalString (type != "auto") ''
|
||||
--type
|
||||
${type}
|
||||
''
|
||||
+ stdenv.lib.optionalString (args != []) ''
|
||||
--args
|
||||
${builtins.concatStringsSep " " (map (arg: stdenv.lib.escapeShellArg arg) args)}
|
||||
''
|
||||
+
|
||||
stdenv.lib.concatMapStrings (variableName:
|
||||
let
|
||||
value = builtins.getAttr variableName _environment;
|
||||
in
|
||||
''
|
||||
--env
|
||||
'${variableName}=${stdenv.lib.escape [ "'" ] (toString value)}'
|
||||
'') (builtins.attrNames _environment)
|
||||
+ stdenv.lib.optionalString (user != null) ''
|
||||
--user
|
||||
${user}
|
||||
''
|
||||
+ stdenv.lib.optionalString (password != null) ''
|
||||
--passwd
|
||||
${password}
|
||||
''
|
||||
+ stdenv.lib.optionalString (stdin != null) ''
|
||||
--stdin
|
||||
${stdin}
|
||||
''
|
||||
+ stdenv.lib.optionalString (stdout != null) ''
|
||||
--stdout
|
||||
${stdout}
|
||||
''
|
||||
+ stdenv.lib.optionalString (stderr != null) ''
|
||||
--stderr
|
||||
${stderr}
|
||||
''
|
||||
+ stdenv.lib.optionalString (terminateSignal != "TERM") ''
|
||||
--termsig
|
||||
${terminateSignal}
|
||||
''
|
||||
+ stdenv.lib.optionalString terminateOnShutdown ''
|
||||
--shutdown
|
||||
''
|
||||
+ stdenv.lib.concatMapStrings (dependency: ''
|
||||
--dep
|
||||
${dependency.name}
|
||||
'') dependencies;
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "${prefix}${name}";
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $out
|
||||
ln -s ${cygrunsrvConfig} $out/${prefix}${name}-cygrunsrvparams
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "launchd";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
buildEnv {
|
||||
name = "launchd";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
}
|
|
@ -0,0 +1,96 @@
|
|||
{ writeTextFile
|
||||
, stdenv
|
||||
, createCredentials
|
||||
|
||||
# Specifies whether user changing functionality should be disabled or not
|
||||
, forceDisableUserChange ? false
|
||||
# Prefix that is in front of all launchd plist files generated by this function
|
||||
, prefix ? "org.nixos."
|
||||
}:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# Specifies which packages need to be in the PATH
|
||||
, path ? []
|
||||
# Specifies which groups and users that need to be created.
|
||||
, credentials ? {}
|
||||
# The remaining parameters are directly translated to plist XML properties.
|
||||
# Possible configuration options can be found here: https://www.launchd.info
|
||||
, ...
|
||||
}@args:
|
||||
|
||||
let
|
||||
environment = stdenv.lib.optionalAttrs (path != []) {
|
||||
PATH = builtins.concatStringsSep ":" (map (package: "${package}/bin") path); # Augment path environment variable, if applicable
|
||||
} // stdenv.lib.mapAttrs (name: value: toString value) args.EnvironmentVariables or {}; # Convert all environment variables to strings
|
||||
|
||||
label = if args ? Label then args.Label else "${prefix}${name}";
|
||||
|
||||
properties = {
|
||||
Label = label;
|
||||
} // removeAttrs args ([ "name" "path" "credentials" ] ++ stdenv.lib.optional forceDisableUserChange "UserName") // stdenv.lib.optionalAttrs (environment != {}) {
|
||||
EnvironmentVariables = environment;
|
||||
};
|
||||
|
||||
attrsToPList = attrs:
|
||||
"<dict>\n"
|
||||
+ stdenv.lib.concatMapStrings (name:
|
||||
let
|
||||
value = builtins.getAttr name attrs;
|
||||
in
|
||||
''
|
||||
<key>${name}</key>
|
||||
${exprToPList value}
|
||||
''
|
||||
) (builtins.attrNames attrs)
|
||||
+ "</dict>\n";
|
||||
|
||||
listToPList = list:
|
||||
"<array>\n"
|
||||
+ stdenv.lib.concatMapStrings (value: exprToPList value + "\n") list
|
||||
+ "</array>\n";
|
||||
|
||||
exprToPList = expr:
|
||||
let
|
||||
exprType = builtins.typeOf expr;
|
||||
in
|
||||
if exprType == "bool" then
|
||||
if expr then "<true/>" else "<false/>"
|
||||
else if exprType == "int" then "<integer>${toString expr}</integer>"
|
||||
else if exprType == "float" then "<real>${toString expr}</real>"
|
||||
else if exprType == "string" then "<string>${expr}</string>"
|
||||
else if exprType == "set" then
|
||||
if stdenv.lib.isDerivation expr
|
||||
then "<string>${expr}</string>"
|
||||
else attrsToPList expr
|
||||
else if exprType == "list" then listToPList expr
|
||||
else if exprType == "null" then ""
|
||||
else if exprType == "lambda" then throw "Cannot convert a lambda to a plist property"
|
||||
else "${expr}";
|
||||
|
||||
launchdDaemonConfig = writeTextFile {
|
||||
name = "${label}.plist";
|
||||
text = ''
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
|
||||
<plist version="1.0">
|
||||
${exprToPList properties}
|
||||
</plist>
|
||||
'';
|
||||
};
|
||||
|
||||
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name;
|
||||
buildCommand = ''
|
||||
mkdir -p $out/Library/LaunchDaemons
|
||||
ln -s ${launchdDaemonConfig} $out/Library/LaunchDaemons/${label}.plist
|
||||
|
||||
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
||||
ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support
|
||||
''}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "supervisord";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "supervisord.d";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
postBuild = ''
|
||||
cp ${./supervisord.conf} $out/supervisord.conf
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
{writeTextFile, stdenv, createCredentials, supervisor, basePackages, forceDisableUserChange ? false, runtimeDir}:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# Indicates whether we want to use the pidproxy
|
||||
, useProxy ? false
|
||||
# Command line instruction to execute
|
||||
, command ? null
|
||||
# Name of the PID file that contains the PID of the running process
|
||||
, pidFile ? "${name}.pid"
|
||||
# Specifies which packages need to be in the PATH
|
||||
, path ? []
|
||||
# An attribute set specifying arbitrary environment variables
|
||||
, environment ? {}
|
||||
# List of supervisord programs that this configuration depends on. This is used to derive the activation order.
|
||||
, dependencies ? []
|
||||
# Specifies which groups and users that need to be created.
|
||||
, credentials ? {}
|
||||
# The remainder of the parameters directly translate to the properties described in: http://supervisord.org/configuration.html
|
||||
, ...
|
||||
}@params:
|
||||
|
||||
let
|
||||
properties = removeAttrs params ([ "name" "command" "useProxy" "pidFile" "path" "environment" "dependencies" "credentials" ] ++ stdenv.lib.optional forceDisableUserChange "user");
|
||||
|
||||
priority = if dependencies == [] then 1
|
||||
else builtins.head (builtins.sort (a: b: a > b) (map (dependency: dependency.priority) dependencies)) + 1;
|
||||
|
||||
_command = (stdenv.lib.optionalString useProxy "${supervisor}/bin/pidproxy ${runtimeDir}/${pidFile} ") + command;
|
||||
|
||||
_environment = {
|
||||
PATH = builtins.concatStringsSep ":" (map (package: "${package}/bin") (basePackages ++ path));
|
||||
} // environment;
|
||||
|
||||
confFile = writeTextFile {
|
||||
name = "${name}.conf";
|
||||
text = ''
|
||||
[program:${name}]
|
||||
command=${_command}
|
||||
priority=${toString priority}
|
||||
''
|
||||
+ (if _environment == {} then "" else "environment=" + stdenv.lib.concatMapStringsSep "," (name:
|
||||
let
|
||||
value = builtins.getAttr name _environment;
|
||||
in
|
||||
"${name}=\"${stdenv.lib.escape [ "\"" ] (toString value)}\""
|
||||
) (builtins.attrNames _environment)) +
|
||||
"\n"
|
||||
+ stdenv.lib.concatMapStrings (name:
|
||||
let
|
||||
value = builtins.getAttr name properties;
|
||||
in
|
||||
''${name}=${toString value}
|
||||
''
|
||||
) (builtins.attrNames properties);
|
||||
};
|
||||
|
||||
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name priority;
|
||||
buildCommand = ''
|
||||
mkdir -p $out/conf.d
|
||||
ln -s ${confFile} $out/conf.d/${name}.conf
|
||||
|
||||
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
||||
ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support
|
||||
''}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
[supervisord]
|
||||
|
||||
[include]
|
||||
files=conf.d/*
|
||||
|
||||
[inet_http_server]
|
||||
port = 127.0.0.1:9001
|
||||
|
||||
[rpcinterface:supervisor]
|
||||
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
|
|
@ -0,0 +1,30 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "systemd";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "systemd";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
}
|
|
@ -0,0 +1,117 @@
|
|||
{ writeTextFile
|
||||
, stdenv
|
||||
, createCredentials
|
||||
, basePackages
|
||||
|
||||
# Specifies whether user changing functionality should be disabled or not
|
||||
, forceDisableUserChange ? false
|
||||
# Prefix that is in front of all systemd units generated by this function
|
||||
, prefix ? "nix-process-"
|
||||
}:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# An attribute set specifying arbitrary environment variables
|
||||
, environment ? {}
|
||||
# List of supervisord services that this configuration depends on.
|
||||
# These properties are translated to Wants= and After= properties to ensure
|
||||
# proper activation ordering and that the dependencies are started first
|
||||
, dependencies ? []
|
||||
# Specifies which packages need to be in the PATH
|
||||
, path ? []
|
||||
# Specifies which groups and users that need to be created.
|
||||
, credentials ? {}
|
||||
# The remainder of the parameters directly get translated to sections and properties
|
||||
# See: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
|
||||
# and: https://www.freedesktop.org/software/systemd/man/systemd.service.html
|
||||
, ...
|
||||
}@args:
|
||||
|
||||
let
|
||||
sections = removeAttrs args [ "name" "environment" "dependencies" "path" "credentials" ];
|
||||
|
||||
_environment = {
|
||||
PATH = builtins.concatStringsSep ":" (map (package: "${package}/bin") (basePackages ++ path));
|
||||
} // environment;
|
||||
|
||||
generateEnvironmentVariables = environment:
|
||||
stdenv.lib.concatMapStrings (name:
|
||||
let
|
||||
value = builtins.getAttr name _environment;
|
||||
in
|
||||
''Environment=${name}=${toString value}
|
||||
''
|
||||
) (builtins.attrNames _environment);
|
||||
|
||||
mapDependencies = dependencies:
|
||||
if dependencies == [] then ""
|
||||
else
|
||||
''
|
||||
Wants=${toString (map (dependency: "${dependency.name}.service") dependencies)}
|
||||
After=${toString (map (dependency: "${dependency.name}.service") dependencies)}
|
||||
'';
|
||||
|
||||
generateSection = {title, properties}:
|
||||
''
|
||||
|
||||
[${title}]
|
||||
${stdenv.lib.concatMapStrings (name:
|
||||
let
|
||||
value = builtins.getAttr name properties;
|
||||
in
|
||||
if forceDisableUserChange && (name == "User" || name == "Group") then "" else # Don't change user privileges when we force it to be disabled
|
||||
''${name}=${toString value}
|
||||
''
|
||||
) (builtins.attrNames properties)}''
|
||||
+ (if title == "Service" then generateEnvironmentVariables _environment else "")
|
||||
+ (if title == "Unit" then mapDependencies dependencies else "");
|
||||
|
||||
generateSections = sections:
|
||||
stdenv.lib.concatMapStrings (title:
|
||||
let
|
||||
properties = builtins.getAttr title sections;
|
||||
in
|
||||
generateSection {
|
||||
inherit title properties;
|
||||
}
|
||||
) (builtins.attrNames sections);
|
||||
|
||||
service = writeTextFile {
|
||||
name = "${name}.service";
|
||||
text = ''
|
||||
${generateSections sections}
|
||||
${stdenv.lib.optionalString (!(sections ? Service) && _environment != {}) ''
|
||||
[Service]
|
||||
|
||||
${generateEnvironmentVariables _environment}''}
|
||||
${stdenv.lib.optionalString (!(sections ? Unit) && dependencies != []) ''
|
||||
|
||||
[Unit]
|
||||
${mapDependencies dependencies}
|
||||
''}
|
||||
'';
|
||||
};
|
||||
|
||||
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "${prefix}${name}";
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $out/etc/systemd/system
|
||||
ln -s ${service} $out/etc/systemd/system/${prefix}${name}.service
|
||||
|
||||
${stdenv.lib.optionalString (dependencies != []) ''
|
||||
mkdir -p $out/etc/systemd/system/${prefix}${name}.service.wants
|
||||
|
||||
${stdenv.lib.concatMapStrings (dependency: ''
|
||||
ln -s ${dependency}/etc/systemd/system/${dependency.name}.service $out/etc/systemd/system/${prefix}${name}.service.wants
|
||||
'') dependencies}
|
||||
''}
|
||||
|
||||
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
||||
ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support
|
||||
''}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,457 @@
|
|||
A Nix and sysvinit-based process management framework
|
||||
=====================================================
|
||||
This sub project contains a function abstraction that makes it possible to
|
||||
generate sysvinit scripts with Nix. With this function it is possible to easily
|
||||
manage *process deployments* -- Nix takes care of deploying the executable in
|
||||
isolation in the Nix store, and the sysvinit script is used to manage the
|
||||
lifecycle of the process.
|
||||
|
||||
Advantages of this approach:
|
||||
* Works on any Linux system with the Nix package manager installed
|
||||
* Can be used for unprivileged process deployments
|
||||
* No additional process dependencies required -- Nix arranges all package
|
||||
dependencies, and that is all you need.
|
||||
|
||||
Usage
|
||||
=====
|
||||
This sub project has a variety of use cases.
|
||||
|
||||
Composing the createSystemVInitScript function
|
||||
----------------------------------------------
|
||||
To construct sysvinit scripts, you must first compose the
|
||||
`createSystemVInitScript` function and provide it some global settings.
|
||||
These global settings apply to all sysvinit scripts that are generated with it.
|
||||
|
||||
The following partial Nix expression shows how to compose this function with
|
||||
the most common settings:
|
||||
|
||||
```nix
|
||||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
}:
|
||||
|
||||
let
|
||||
createSystemVInitScript = import ./create-sysvinit-script.nix {
|
||||
inherit (stdenv) stdenv writeTextFile daemon;
|
||||
inherit runtimeDir tmpDir forceDisableUserChange;
|
||||
|
||||
initFunctions = import ./init-functions.nix {
|
||||
basePackages = [ pkgs.coreutils pkgs.gnused pkgs.inetutils pkgs.gnugrep pkgs.sysvinit ];
|
||||
inherit (pkgs) stdenv;
|
||||
inherit runtimeDir;
|
||||
};
|
||||
|
||||
createCredentials = import ./create-credentials.nix {
|
||||
inherit (pkgs) stdenv;
|
||||
};
|
||||
};
|
||||
in
|
||||
...
|
||||
```
|
||||
|
||||
In the above Nix code fragment, we provide the following global configuration
|
||||
settings:
|
||||
|
||||
* We need to provide a number of mandatory package dependencies, such as
|
||||
`stdenv`, `writeTextFile` and `daemon`.
|
||||
* `runtimeDir` specifies the location where all PID files reside
|
||||
* `tmpDir` specifies the location of the temp directory
|
||||
* `forceDisableUserChange` globally disables user switches. For production
|
||||
environments, this should be disabled so that processes can run more securely
|
||||
as an unprivileged user. For development environments it may be useful to
|
||||
enable this feature so that you manage all processes without having super user
|
||||
privileges.
|
||||
* The `initFunctions` parameter refers to a function invocation that deploys
|
||||
a package with the `init-functions` script. This script provides standard
|
||||
LSB functionality to manage processes.
|
||||
* The `createCredentials` composes the function that can be used to configure
|
||||
a Dysnomia configuration file so that users and groups can be created on
|
||||
activation and discarded on deactivation.
|
||||
|
||||
In addition to the common settings shown above, there are also a number of
|
||||
unconventional parameters. For common use scenarios, the default values suffice.
|
||||
You only need to adjust them in special circumstances:
|
||||
|
||||
* `initialInstructions` specify the global initial instructions added to any
|
||||
sysvinit script.
|
||||
* `startDaemon` specifies the command-line instruction to start a daemon.
|
||||
* `startProcessAsDaemon` specifies the command-line instruction to start a
|
||||
foreground process as a daemon.
|
||||
* `startDaemon` specifies the command-line instruction to stop a daemon.
|
||||
* `reloadDaemon` specifies the command-line instruction to reload a daemon.
|
||||
* `evaluateCommand` specifies the command-line instruction that displays the
|
||||
status of the previously executed shell instruction.
|
||||
* `statusCommand` specifies the command that retrieves the status of the daemon
|
||||
* `restartActivity` specifies the implementation of the restart activity, that
|
||||
is common to all process-oriented sysvinit scripts.
|
||||
* `supportedRunLevels` referts to a list that iterates all supported runlevels.
|
||||
* `minSequence` specifies the minimum start sequence number.
|
||||
* `maxSequence` specifies the maximum start sequence number.
|
||||
|
||||
Creating a sysvinit script
|
||||
--------------------------
|
||||
After composing the `createSystemVInitScript` function, we can write Nix
|
||||
expressions that build sysvinit scripts.
|
||||
|
||||
### Specifying activities
|
||||
|
||||
The following Nix expression is a straight forward example demonstrating how we
|
||||
can manage the Nginx web server:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript, nginx, configFile, stateDir}:
|
||||
|
||||
createSystemVInitScript {
|
||||
name = "nginx";
|
||||
description = "Nginx";
|
||||
activities = {
|
||||
start = ''
|
||||
mkdir -p ${stateDir}/logs
|
||||
log_info_msg "Starting Nginx..."
|
||||
loadproc ${nginx}/bin/nginx -c ${configFile} -p ${stateDir}
|
||||
evaluate_retval
|
||||
'';
|
||||
stop = ''
|
||||
log_info_msg "Stopping Nginx..."
|
||||
killproc ${nginx}/bin/nginx
|
||||
evaluate_retval
|
||||
'';
|
||||
reload = ''
|
||||
log_info_msg "Reloading Nginx..."
|
||||
killproc ${nginx}/bin/nginx -HUP
|
||||
evaluate_retval
|
||||
'';
|
||||
restart = ''
|
||||
$0 stop
|
||||
sleep 1
|
||||
$0 start
|
||||
'';
|
||||
status = "statusproc ${nginx}/bin/nginx";
|
||||
};
|
||||
runlevels = [ 3 4 5 ];
|
||||
}
|
||||
```
|
||||
|
||||
The above Nix expression composes a sysvinit script to manage the life-cycle of
|
||||
the Nginx server:
|
||||
|
||||
* The `name` parameter specifies the name of the sysvinit script
|
||||
* The `description` specifies the description field shown in the meta
|
||||
information section.
|
||||
* The `activities` parameter refers to an attribute set that specifies the
|
||||
implementation of each activity in bash code.
|
||||
* The `runlevels` parameter specifies in which runlevels we want to start this
|
||||
script. It will automatically compose symlinks with the appropriate start
|
||||
sequence numbers in rc.d directories for the corresponding runlevels. An
|
||||
implication is that this function will automatically compose stop rc.d
|
||||
symlinks for the remaining runlevels.
|
||||
It will stop sysvinit scripts in exactly the opposite of the start order.
|
||||
|
||||
### Specifying instructions
|
||||
|
||||
Many sysvinit scripts implement activities that consist of a description line,
|
||||
followed by a command, followed by displaying the status, e.g.:
|
||||
|
||||
```bash
|
||||
log_info_msg "Starting Nginx..."
|
||||
loadproc ${nginx}/bin/nginx -c ${configFile} -p ${stateDir}
|
||||
evaluate_retval
|
||||
```
|
||||
|
||||
It is possible to reduce this boilerplate code by using the instructions
|
||||
facility:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript, nginx, configFile, stateDir}:
|
||||
|
||||
createSystemVInitScript {
|
||||
name = "nginx";
|
||||
description = "Nginx";
|
||||
instructions = {
|
||||
start = {
|
||||
activity = "Starting";
|
||||
instruction = ''
|
||||
mkdir -p ${stateDir}/logs
|
||||
loadproc ${nginx}/bin/nginx -c ${configFile} -p ${stateDir}
|
||||
'';
|
||||
};
|
||||
stop = {
|
||||
activity = "Stopping";
|
||||
instruction = "killproc ${nginx}/bin/nginx";
|
||||
};
|
||||
reload = {
|
||||
activity = "Reloading";
|
||||
instruction = "killproc ${nginx}/bin/nginx -HUP";
|
||||
};
|
||||
};
|
||||
activities = {
|
||||
status = "statusproc ${nginx}/bin/nginx";
|
||||
};
|
||||
runlevels = [ 3 4 5 ];
|
||||
}
|
||||
```
|
||||
|
||||
In the above Nix expression we have replaced the `start`, `stop`, and `reload`
|
||||
reload activities, with `instructions`. For these instructions, the description
|
||||
line is automatically derived from the `description` parameter and augmented with
|
||||
the instruction displaying the status.
|
||||
|
||||
### Specifying daemons to manage
|
||||
|
||||
It is possible to reduce the amount of boilerplate code even further --
|
||||
in many scenarios we want to manage a process. The kind of activities that you
|
||||
need are typically the same -- `start`, `stop`, `reload` (if applicable),
|
||||
`status` and `restart`:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript, nginx, configFile, stateDir}:
|
||||
|
||||
createSystemVInitScript {
|
||||
name = "nginx";
|
||||
description = "Nginx";
|
||||
initialize = ''
|
||||
mkdir -p ${stateDir}/logs
|
||||
'';
|
||||
process = "${nginx}/bin/nginx";
|
||||
args = [ "-c" configFile "-p" stateDir ];
|
||||
runlevels = [ 3 4 5 ];
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, we do not specify any activities or instructions.
|
||||
Instead, we specify the `process` we want to run and the command-line
|
||||
instructions (`args`). From these properties, the generator will automatically
|
||||
generate all relevant activities.
|
||||
|
||||
### Managing foreground processes
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript, port ? 5000}:
|
||||
|
||||
let
|
||||
webapp = (import ./webapp {}).package;
|
||||
in
|
||||
createSystemVInitScript {
|
||||
name = "webapp";
|
||||
process = "${webapp}/lib/node_modules/webapp/app.js";
|
||||
processIsDaemon = false;
|
||||
runlevels = [ 3 4 5 ];
|
||||
environment = {
|
||||
PORT = port;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
By default, sysvinit scripts expect processes to daemonize -- a process forks
|
||||
another process that keeps running in the background and then the parent process
|
||||
terminates. Most common system software, e.g. web servers, DBMS servers,
|
||||
provides this kind of functionality.
|
||||
|
||||
Application software, however, that are typically implemented in more
|
||||
"higher-level languages" than C, do not have this ability out of the box.
|
||||
|
||||
It is also possible to invoke libslack's
|
||||
[daemon](http://www.libslack.org/daemon/) command to let a foreground process
|
||||
daemonize -- when setting `processIsDaemon` to `false` (the default is: `true`),
|
||||
the generator will automatically invoke the `daemon` command to accomplish this.
|
||||
|
||||
The `environment` parameter can be used to set additional environment variables.
|
||||
In the above example, it is used to specify to which TCP port the process should
|
||||
bind to.
|
||||
|
||||
### Specifying process dependencies
|
||||
|
||||
Processes may also communicate with other processes by using some kind of IPC
|
||||
mechanism, such as Unix domain sockets. In such cases, a process might depend on
|
||||
the activation of another process.
|
||||
|
||||
For example, the `nginx` server could act as a reverse proxy for the `webapp`.
|
||||
This means that the `webapp` process should be activated first, otherwise users
|
||||
might get a 502 bad gateway error.
|
||||
|
||||
We can augment the Nginx reverse proxy with a dependency parameter:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript, nginx, configFile, stateDir, webapp}:
|
||||
|
||||
createSystemVInitScript {
|
||||
name = "nginx";
|
||||
description = "Nginx";
|
||||
initialize = ''
|
||||
mkdir -p ${stateDir}/logs
|
||||
'';
|
||||
process = "${nginx}/bin/nginx";
|
||||
args = [ "-c" configFile "-p" stateDir ];
|
||||
runlevels = [ 3 4 5 ];
|
||||
dependencies = [ webapp ];
|
||||
}
|
||||
```
|
||||
|
||||
The above example passed the `webapp` process as a dependency (through the
|
||||
`dependencies` parameter) to the Nginx process. The generator makes sure that
|
||||
the Nginx process gets a higher start sequence number and a lower stop sequence
|
||||
number than the `webapp` process, ensuring that it starts in the right order
|
||||
and stops in the reverse order.
|
||||
|
||||
### Managing multiple process instances
|
||||
|
||||
In addition to deploying single instances of processes, it is also possible to
|
||||
have multiple instances of processes. For example, the Nginx reverse proxy can
|
||||
forward incoming requests to multiple instances of the `webapp`.
|
||||
|
||||
sysvinit scripts use PID files to control daemons. Normally, PID files have the
|
||||
same name as the executable. When running multiple instances, we must make sure
|
||||
that every instance gets a unique PID, otherwise all instances might get
|
||||
killed.
|
||||
|
||||
We can adjust the Nix expression of `webapp` with an `instanceName` parameter:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript}:
|
||||
{instanceSuffix ? "", port ? 5000}:
|
||||
|
||||
let
|
||||
webapp = (import ./webapp {}).package;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createSystemVInitScript {
|
||||
name = instanceName;
|
||||
inherit instanceName;
|
||||
process = "${webapp}/lib/node_modules/webapp/app.js";
|
||||
processIsDaemon = false;
|
||||
runlevels = [ 3 4 5 ];
|
||||
environment = {
|
||||
PORT = port;
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, we define a nested function in which the outer function
|
||||
header (first line) refers to configuration properties applying to all process
|
||||
instances.
|
||||
|
||||
The inner function header (second line) refers to instance properties:
|
||||
* `instanceSuffix` can be used to construct a unique name for each `webapp`
|
||||
instance called in a variable called `instanceName`.
|
||||
* `port` is a parameter that specifies to which TCP port the webapp should
|
||||
listen to. This port value should be unique for each `webapp` instance.
|
||||
|
||||
The `instanceName` parameter instructs the sysvinit script generator to create
|
||||
a unique PID file for each running instance making it possible to control
|
||||
instances individually.
|
||||
|
||||
### Managing user credentials
|
||||
|
||||
When deploying processes as system administrator, it is typically
|
||||
insecure/unsafe to spawn processes as a root user.
|
||||
|
||||
The following expression instructs the generator to run the `webapp` as a unique
|
||||
unprivileged user:
|
||||
|
||||
```nix
|
||||
{createSystemVInitScript}:
|
||||
{port, instanceSuffix ? ""}:
|
||||
|
||||
let
|
||||
webapp = (import ./webapp {}).package;
|
||||
instanceName = "webapp${instanceSuffix}";
|
||||
in
|
||||
createSystemVInitScript {
|
||||
name = instanceName;
|
||||
inherit instanceName;
|
||||
process = "${webapp}/lib/node_modules/webapp/app.js";
|
||||
processIsDaemon = false;
|
||||
runlevels = [ 3 4 5 ];
|
||||
environment = {
|
||||
PORT = port;
|
||||
};
|
||||
user = instanceName;
|
||||
|
||||
credentials = {
|
||||
groups = {
|
||||
"${instanceName}" = {};
|
||||
};
|
||||
users = {
|
||||
"${instanceName}" = {
|
||||
group = instanceName;
|
||||
description = "Webapp";
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
In the above example, the `credentials` parameter generates a configuration file
|
||||
that Dysnomia can use to create or discard groups and users. The `user` parameter
|
||||
instructs the script to switch privileges to the given user.
|
||||
|
||||
### Disable user switching
|
||||
|
||||
For production deployments, switching user privileges is useful, but for
|
||||
experimentation as an unprivileged user it is not.
|
||||
|
||||
It is also possible to globally disable user switching, by setting the
|
||||
`forceDisableUserChange` to `true` in the example that composes the
|
||||
`createSystemVInitScript` function.
|
||||
|
||||
### Other settings
|
||||
|
||||
The `createSystemVInitScript` has a variety of other configuration properties
|
||||
not shown here, such as:
|
||||
|
||||
* Specifying the `umask` for default file permissions
|
||||
* Specifying the nice level of the process with `nice`
|
||||
* Change the current working directory with `directory`
|
||||
* Adding additional packages to the sysvinit script's PATH, via `paths`
|
||||
* Removing generated sysvinit script activities with `removeActivities`
|
||||
|
||||
Deploying a collection of processes
|
||||
-----------------------------------
|
||||
We can use the `nixproc-sysvinit-switch` command to build and activate all
|
||||
processes in a processes expression:
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-switch processes.nix
|
||||
```
|
||||
|
||||
The `nixproc-sysvinit-switch` command will activate the processes in the right
|
||||
order (this means it will ensure that `webapp` gets activated before `nginx`).
|
||||
|
||||
If we have already deployed a collection of processes then it does a comparison
|
||||
with the previous deployment, and only deactivates obsolete processes and
|
||||
activates new processes:
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-switch processes.nix
|
||||
```
|
||||
|
||||
In the above case, `rcswitch` will only deactivate obsolete processes and
|
||||
activate new processes, making redeployments significantly faster.
|
||||
|
||||
Running activities on a collection of processes
|
||||
-----------------------------------------------
|
||||
In addition to deploying a collection of processes or upgrading a collection of
|
||||
processes, it is also possible to run a certain activity on all sysvinit
|
||||
scripts in the last deployed Nix profile:
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-runactivity status
|
||||
```
|
||||
|
||||
The above command will show the statuses of all processes in the provided Nix
|
||||
profile.
|
||||
|
||||
By default, `nixproc-sysvinit-runactivity` will examine scripts in start
|
||||
activation order.
|
||||
|
||||
We can also specify that we want to examine all scripts in the reverse order.
|
||||
This is particularly useful to stop all services without breaking process
|
||||
dependencies:
|
||||
|
||||
```bash
|
||||
$ nixproc-sysvinit-runactivity -r stop
|
||||
```
|
|
@ -0,0 +1,30 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
, stateDir ? "/var"
|
||||
, runtimeDir ? "${stateDir}/run"
|
||||
, logDir ? "${stateDir}/log"
|
||||
, tmpDir ? (if stateDir == "/var" then "/tmp" else "${stateDir}/tmp")
|
||||
, forceDisableUserChange ? false
|
||||
, exprFile
|
||||
}@args:
|
||||
|
||||
let
|
||||
processesFun = import exprFile;
|
||||
|
||||
processesFormalArgs = builtins.functionArgs processesFun;
|
||||
|
||||
processesArgs = builtins.intersectAttrs processesFormalArgs (args // {
|
||||
processManager = "sysvinit";
|
||||
});
|
||||
|
||||
processes = processesFun processesArgs;
|
||||
in
|
||||
pkgs.buildEnv {
|
||||
name = "rc.d";
|
||||
paths = map (processName:
|
||||
let
|
||||
process = builtins.getAttr processName processes;
|
||||
in
|
||||
process.pkg
|
||||
) (builtins.attrNames processes);
|
||||
}
|
|
@ -0,0 +1,247 @@
|
|||
{ stdenv
|
||||
, writeTextFile
|
||||
, daemon
|
||||
, initFunctions
|
||||
, createCredentials
|
||||
|
||||
# Instructions that are carried out before anything else gets executed
|
||||
, initialInstructions ? ". ${initFunctions}"
|
||||
# Command that specifies how to start a daemon
|
||||
, startDaemon ? "start_daemon"
|
||||
# Path to the executable that daemonizes a foreground process
|
||||
, startProcessAsDaemon ? "${daemon}/bin/daemon"
|
||||
# Command that specifies how to stop a daemon
|
||||
, stopDaemon ? "killproc"
|
||||
# Command that specifies how to reload a daemon
|
||||
, reloadDaemon ? "killproc"
|
||||
# Command that evaluates the return value of the previous command
|
||||
, evaluateCommand ? "evaluate_retval"
|
||||
# Command that shows the status of a daemon
|
||||
, statusCommand ? "statusproc"
|
||||
# Default run time directory where PID files are stored
|
||||
, runtimeDir ? "/var/run"
|
||||
# Directory in which temp files are stored
|
||||
, tmpDir ? "/tmp"
|
||||
# Specifies the implementation of the restart activity that is used for all sysvinit scripts. Typically, there is little reason to change it.
|
||||
, restartActivity ? ''
|
||||
$0 stop
|
||||
sleep 1
|
||||
$0 start
|
||||
''
|
||||
# Specifies which runlevels are supported
|
||||
, supportedRunlevels ? stdenv.lib.range 0 6
|
||||
# The minimum start/stop sequence number
|
||||
, minSequence ? 0
|
||||
# The maximum start/stop sequence number
|
||||
, maxSequence ? 99
|
||||
# Specifies whether user changing functionality should be disabled or not
|
||||
, forceDisableUserChange ? false
|
||||
}:
|
||||
|
||||
{
|
||||
# A name that identifies the process instance
|
||||
name
|
||||
# A name that uniquely identifies each process instance. It is used to generate a unique PID file.
|
||||
, instanceName ? null
|
||||
# A description that is added to the comments section
|
||||
, description ? name
|
||||
# Global instructions that are executed before any activity gets executed
|
||||
, globalInstructions ? ""
|
||||
# If not null, the umask will be changed before executing any activities
|
||||
, umask ? null
|
||||
# If not null, the nice level be changed before executing any activities
|
||||
, nice ? null
|
||||
# If not null, the current working directory will be changed before executing any activities
|
||||
, directory ? null
|
||||
# Specifies which packages need to be in the PATH
|
||||
, path ? []
|
||||
# An attribute set specifying arbitrary environment variables
|
||||
, environment ? {}
|
||||
# Shell instructions that specify how the state of the process should be initialized
|
||||
, initialize ? ""
|
||||
# A high level property to specify what process needs to be managed. From this property, the start, stop, reload, status and restart activities are derived.
|
||||
, process ? null
|
||||
# Specifies that the process is a daemon. If a process is not a daemon, then the generator will automatically daemonize it.
|
||||
, processIsDaemon ? true
|
||||
# Command-line arguments passed to the process.
|
||||
, args ? []
|
||||
# Path to a PID file that the system should use to manage the process. If null, it will use a default path.
|
||||
, pidFile ? (if instanceName == null then null else if user == null || user == "root" || forceDisableUserChange then "${runtimeDir}/${instanceName}.pid" else "${tmpDir}/${instanceName}.pid")
|
||||
# Specifies as which user the process should run. If null, the user privileges will not be changed.
|
||||
, user ? null
|
||||
# Specifies which signal should be send to the process to reload its configuration
|
||||
, reloadSignal ? "-HUP"
|
||||
# Specifies arbitrary instructions to carry out. Before each instruction the activity will be printed, and after the execution it will evaluate the return status
|
||||
, instructions ? {}
|
||||
# Specifies the raw implementation of each activity.
|
||||
, activities ? {}
|
||||
# Specifies activities to remove from the generated activities attribute set
|
||||
, removeActivities ? []
|
||||
# Specifies in which runlevels the script should be started. From this value, the script will automatically be stopped in the remaining runlevels
|
||||
, runlevels ? []
|
||||
# Specifies in which runlevels the script should be started.
|
||||
, defaultStart ? []
|
||||
# Specifies in which runlevels the script should be stopped.
|
||||
, defaultStop ? []
|
||||
# A list of sysvinit scripts that this scripts depends on. This is used to automatically derive the start and stop sequence. Dependencies will be started first and stopped last.
|
||||
, dependencies ? []
|
||||
# Specifies which groups and users that need to be created.
|
||||
, credentials ? {}
|
||||
}:
|
||||
|
||||
let
|
||||
# Enumerates the activities in a logical order -- the common activities first, then the remaining activities in alphabetical order
|
||||
enumerateActivities = activities:
|
||||
stdenv.lib.optional (activities ? start) "start"
|
||||
++ stdenv.lib.optional (activities ? stop) "stop"
|
||||
++ stdenv.lib.optional (activities ? reload) "reload"
|
||||
++ stdenv.lib.optional (activities ? restart) "restart"
|
||||
++ stdenv.lib.optional (activities ? status) "status"
|
||||
++ builtins.filter (activityName: activityName != "start" && activityName != "stop" && activityName != "reload" && activityName != "restart" && activityName != "status" && activityName != "*") (builtins.attrNames activities)
|
||||
++ stdenv.lib.optional (activities ? "*") "*";
|
||||
|
||||
_user = if forceDisableUserChange then null else user;
|
||||
|
||||
_instructions = (stdenv.lib.optionalAttrs (process != null) {
|
||||
start = {
|
||||
activity = "Starting";
|
||||
instruction =
|
||||
initialize +
|
||||
(if processIsDaemon then "${startDaemon} ${stdenv.lib.optionalString (pidFile != null) "-f -p ${pidFile}"} ${stdenv.lib.optionalString (nice != null) "-n ${nice}"} ${stdenv.lib.optionalString (_user != null) "su ${_user} -c '"} ${process} ${toString args} ${stdenv.lib.optionalString (_user != null) "'"}"
|
||||
else "${startProcessAsDaemon} -U -i ${if pidFile == null then "-P ${runtimeDir} -n $(basename ${process})" else "-F ${pidFile}"} ${stdenv.lib.optionalString (_user != null) "-u ${_user}"} -- ${process} ${toString args}");
|
||||
};
|
||||
stop = {
|
||||
activity = "Stopping";
|
||||
instruction = "${stopDaemon} ${stdenv.lib.optionalString (pidFile != null) "-p ${pidFile}"} ${process}";
|
||||
};
|
||||
reload = {
|
||||
activity = "Reloading";
|
||||
instruction = "${reloadDaemon} ${stdenv.lib.optionalString (pidFile != null) "-p ${pidFile}"} ${process} ${reloadSignal}";
|
||||
};
|
||||
}) // instructions;
|
||||
|
||||
_activities =
|
||||
let
|
||||
convertedInstructions = stdenv.lib.mapAttrs (name: instruction:
|
||||
''
|
||||
log_info_msg "${instruction.activity} ${description}..."
|
||||
${instruction.instruction}
|
||||
${evaluateCommand}
|
||||
''
|
||||
) _instructions;
|
||||
|
||||
defaultActivities = stdenv.lib.optionalAttrs (process != null) {
|
||||
status = "${statusCommand} ${stdenv.lib.optionalString (pidFile != null) "-p ${pidFile}"} ${process}";
|
||||
restart = restartActivity;
|
||||
} // {
|
||||
"*" = ''
|
||||
echo "Usage: $0 {${builtins.concatStringsSep "|" (builtins.filter (activityName: activityName != "*") (enumerateActivities _activities))}}"
|
||||
exit 1
|
||||
'';
|
||||
};
|
||||
in
|
||||
removeAttrs (convertedInstructions // defaultActivities // activities) removeActivities;
|
||||
|
||||
_defaultStart = if runlevels != [] then stdenv.lib.intersectLists runlevels supportedRunlevels
|
||||
else defaultStart;
|
||||
|
||||
_defaultStop = if runlevels != [] then stdenv.lib.subtractLists _defaultStart supportedRunlevels
|
||||
else defaultStop;
|
||||
|
||||
_environment = stdenv.lib.optionalAttrs (path != []) {
|
||||
PATH = "${builtins.concatStringsSep ":" (map(package: "${package}/bin" ) path)}:$PATH";
|
||||
} // environment;
|
||||
|
||||
initdScript = writeTextFile {
|
||||
inherit name;
|
||||
executable = true;
|
||||
text = ''
|
||||
#! ${stdenv.shell}
|
||||
|
||||
## BEGIN INIT INFO
|
||||
# Provides: ${name}
|
||||
''
|
||||
+ stdenv.lib.optionalString (_defaultStart != []) "# Default-Start: ${toString _defaultStart}\n"
|
||||
+ stdenv.lib.optionalString (_defaultStop != []) "# Default-Stop: ${toString _defaultStop}\n"
|
||||
+ stdenv.lib.optionalString (dependencies != []) ''
|
||||
# Should-Start: ${toString (map (dependency: dependency.name) dependencies)}
|
||||
# Should-Stop: ${toString (map (dependency: dependency.name) dependencies)}
|
||||
''
|
||||
+ ''
|
||||
# Description: ${description}
|
||||
## END INIT INFO
|
||||
|
||||
${initialInstructions}
|
||||
${globalInstructions}
|
||||
''
|
||||
+ stdenv.lib.optionalString (umask != null) ''
|
||||
umask ${umask}
|
||||
''
|
||||
+ stdenv.lib.optionalString (directory != null) ''
|
||||
cd ${directory}
|
||||
''
|
||||
+ stdenv.lib.concatMapStrings (name:
|
||||
let
|
||||
value = builtins.getAttr name _environment;
|
||||
in
|
||||
''
|
||||
export ${name}=${stdenv.lib.escapeShellArg value}
|
||||
''
|
||||
) (builtins.attrNames _environment)
|
||||
+ ''
|
||||
|
||||
case "$1" in
|
||||
${stdenv.lib.concatMapStrings (activityName:
|
||||
let
|
||||
instructions = builtins.getAttr activityName _activities;
|
||||
in
|
||||
''
|
||||
${activityName})
|
||||
${instructions}
|
||||
;;
|
||||
|
||||
''
|
||||
) (enumerateActivities _activities)}
|
||||
esac
|
||||
'';
|
||||
};
|
||||
|
||||
startSequenceNumber =
|
||||
if dependencies == [] then minSequence
|
||||
else builtins.head (builtins.sort (a: b: a > b) (map (dependency: dependency.sequence) dependencies)) + 1;
|
||||
|
||||
stopSequenceNumber = maxSequence - startSequenceNumber + minSequence;
|
||||
|
||||
sequenceNumberToString = number:
|
||||
if number < 10 then "0${toString number}"
|
||||
else toString number;
|
||||
|
||||
credentialsSpec = if credentials == {} || forceDisableUserChange then null else createCredentials credentials;
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
inherit name;
|
||||
|
||||
sequence = startSequenceNumber;
|
||||
|
||||
buildCommand = ''
|
||||
mkdir -p $out/etc/rc.d
|
||||
cd $out/etc/rc.d
|
||||
|
||||
mkdir -p init.d
|
||||
ln -s ${initdScript} init.d/${name}
|
||||
|
||||
${stdenv.lib.concatMapStrings (runlevel: ''
|
||||
mkdir -p rc${toString runlevel}.d
|
||||
ln -s ../init.d/${name} rc${toString runlevel}.d/S${sequenceNumberToString startSequenceNumber}${name}
|
||||
'') _defaultStart}
|
||||
|
||||
${stdenv.lib.concatMapStrings (runlevel: ''
|
||||
mkdir -p rc${toString runlevel}.d
|
||||
ln -s ../init.d/${name} rc${toString runlevel}.d/K${sequenceNumberToString stopSequenceNumber}${name}
|
||||
'') _defaultStop}
|
||||
|
||||
${stdenv.lib.optionalString (credentialsSpec != null) ''
|
||||
ln -s ${credentialsSpec}/dysnomia-support $out/dysnomia-support
|
||||
''}
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
{stdenv, fetchurl, basePackages, runtimeDir}:
|
||||
|
||||
let
|
||||
basePath = builtins.concatStringsSep ":" (map (package: "${package}/bin") basePackages);
|
||||
|
||||
src = fetchurl {
|
||||
url = http://www.linuxfromscratch.org/lfs/downloads/9.0/lfs-bootscripts-20190524.tar.xz;
|
||||
sha256 = "0975wmghhh7j5qify0m170ba2d7vl0km7sw05kclnmwpgivimb38";
|
||||
};
|
||||
in
|
||||
stdenv.mkDerivation {
|
||||
name = "init-functions";
|
||||
|
||||
buildCommand = ''
|
||||
tar xfv ${src} lfs-bootscripts-20190524/lfs/lib/services/init-functions
|
||||
|
||||
sed \
|
||||
-e "s|/bin:/usr/bin:/sbin:/usr/sbin|${basePath}|" \
|
||||
-e "s|/bin/sh|${stdenv.shell}|" \
|
||||
-e "s|/bin/echo|echo|" \
|
||||
-e "s|/bin/head|head|" \
|
||||
-e "s|/var/run|${runtimeDir}|" \
|
||||
-e "s|/run/bootlog|${runtimeDir}/bootlog|" \
|
||||
lfs-bootscripts-20190524/lfs/lib/services/init-functions > $out
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-bsdrc-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@sed@|$(type -p sed)|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
${./nixproc-bsdrc-switch.in} > $out/bin/nixproc-bsdrc-switch
|
||||
chmod +x $out/bin/nixproc-bsdrc-switch
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
${./nixproc-bsdrc-runactivity.in} > $out/bin/nixproc-bsdrc-runactivity
|
||||
chmod +x $out/bin/nixproc-bsdrc-runactivity
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
# Shows the usage of this command to the user
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] ACTIVITY [PATH]
|
||||
|
||||
This command runs a deployment activity on all BSD rc scripts in a Nix profile.
|
||||
The scripts are traversed in the right dependency order.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-r, --reverse Traverse the sysvinit scripts in the reverse order
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:rh -l profile:,reverse,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-r|--reverse)
|
||||
reverse=1
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
activity="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
if [ "$activity" = "" ]
|
||||
then
|
||||
echo "No activity specified!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
# Execute the activities
|
||||
|
||||
rcpath="$oldProfilePath/etc/rc.d"
|
||||
|
||||
if [ "$reverse" = "1" ]
|
||||
then
|
||||
for i in $(rcorder $rcpath/* | tail -r)
|
||||
do
|
||||
$i $activity
|
||||
done
|
||||
else
|
||||
for i in $(rcorder $rcpath/*)
|
||||
do
|
||||
$i $activity
|
||||
done
|
||||
fi
|
|
@ -0,0 +1,197 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
# Shows the usage of this command to the user
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command starts all BSD rc scripts in the provided Nix profile and
|
||||
optionally deactivates all obsolete sysvinit scripts in the previous Nix
|
||||
profile generation.
|
||||
|
||||
If the provided path is a file then it is considered a Nix expression that
|
||||
produces a Nix profile. If the provided path is a directory, then it is
|
||||
considered a pre-built Nix profile.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the BSD rc scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--enable-at-boot Configures the rc scripts so that they are started at
|
||||
boot
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
BSDRC_TARGET_DIR Directory in which the BSD rc scripts reside (defaults to:
|
||||
/usr/local/etc/rc.d)
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,enable-at-boot,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--enable-at-boot)
|
||||
enableAtBoot=1
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
BSDRC_TARGET_DIR=${BSDRC_TARGET_DIR:-/usr/local/etc/rc.d}
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
buildProfile bsdrc
|
||||
|
||||
rcnew="$profilePath/etc/rc.d"
|
||||
rcold="$oldProfilePath/etc/rc.d"
|
||||
|
||||
# Determine paths of old scripts
|
||||
|
||||
oldscripts=()
|
||||
|
||||
if [ -d "$rcold" ]
|
||||
then
|
||||
echo "Using previous Nix profile: $rcold" >&2
|
||||
|
||||
for i in $(rcorder $rcold/* | tail -r)
|
||||
do
|
||||
currentPath=$(readlink -f $i)
|
||||
oldscripts+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new scripts
|
||||
|
||||
newscripts=()
|
||||
|
||||
for i in $(rcorder $rcnew/*)
|
||||
do
|
||||
currentPath=$(readlink -f $i)
|
||||
newscripts+=($currentPath)
|
||||
done
|
||||
|
||||
# Create new groups and users
|
||||
createNewGroups
|
||||
createNewUsers
|
||||
|
||||
# Stop and remove obsolete scripts
|
||||
|
||||
for i in $(rcorder $rcold/* | tail -r)
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${newscripts[@]}"
|
||||
then
|
||||
if [ "$enableAtBoot" = "1" ]
|
||||
then
|
||||
scriptName="$(basename $i)"
|
||||
/usr/local/etc/$scriptName stop || true
|
||||
@sed@ -i -e "/^${scriptName}_enabled=YES"'$'"/d" /etc/rc.conf
|
||||
rm -f /usr/local/etc/$scriptName
|
||||
else
|
||||
"$i" onestop
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Install and start new scripts
|
||||
|
||||
for i in $(rcorder $rcnew/*)
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${oldscripts[@]}"
|
||||
then
|
||||
if [ "$enableAtBoot" = "1" ]
|
||||
then
|
||||
scriptName="$(basename $i)"
|
||||
ln -sfn $rcnew/$scriptName /usr/local/etc
|
||||
echo "${scriptName}_enabled=YES" >> /etc/rc.conf
|
||||
/usr/local/etc/$scriptName start
|
||||
else
|
||||
"$i" onestart
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete obsolete users and groups
|
||||
deleteObsoleteUsers
|
||||
deleteObsoleteGroups
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
|
@ -0,0 +1,15 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-build-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@readlink@|$(type -p readlink)|" \
|
||||
-e "s|@NIXPROC@|${../../nixproc}|" \
|
||||
${./nixproc-build.in} > $out/bin/nixproc-build
|
||||
chmod +x $out/bin/nixproc-build
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# Shows the usage of this command to the user
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command builds a Nix profile containing multiple sysvinit scripts, and
|
||||
their start and stop symlinks.
|
||||
|
||||
Options:
|
||||
-P, --process-manager=MANAGER
|
||||
Process manager to build for
|
||||
--state-dir Changes the directory in which the state of the processes
|
||||
are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--no-out-link Do not create a symlink to the output path
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o P:h -l process-manager:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,no-out-link,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-P|--process-manager)
|
||||
processManager="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--argstr stateDir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--argstr runtimeDir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDir="--argstr logDir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDir="--argstr tmpDir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--arg forceDisableUserChange true"
|
||||
;;
|
||||
--no-out-link)
|
||||
noOutLinkArg="--no-out-link"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
if [ "$processManager" = "" ]
|
||||
then
|
||||
echo "No process manager specified!" >&2
|
||||
exit 1
|
||||
else
|
||||
processManagerArg="--argstr processManager $processManager"
|
||||
fi
|
||||
|
||||
if [ "$1" = "" ]
|
||||
then
|
||||
echo "No processes expression provided!" >&2
|
||||
exit 1
|
||||
else
|
||||
exprFile=$(@readlink@ -f "$1")
|
||||
fi
|
||||
|
||||
# Validate the given options
|
||||
|
||||
if [ "$NIXPROC_STATE_DIR" != "" ]
|
||||
then
|
||||
stateDirArg="--argstr stateDir $NIXPROC_STATE_DIR"
|
||||
fi
|
||||
|
||||
if [ "$NIXPROC_RUNTIME_DIR" != "" ]
|
||||
then
|
||||
runtimeDirArg="--argstr stateDir $NIXPROC_RUNTIME_DIR"
|
||||
fi
|
||||
|
||||
if [ "$NIXPROC_LOG_DIR" != "" ]
|
||||
then
|
||||
logDirArg="--argstr logDir $NIXPROC_LOG_DIR"
|
||||
fi
|
||||
|
||||
if [ "$NIXPROC_TMP_DIR" != "" ]
|
||||
then
|
||||
tmpDirArg="--argstr tmpDir $NIXPROC_TMP_DIR"
|
||||
fi
|
||||
|
||||
if [ "$NIXPROC_FORCE_DISABLE_USER_CHANGE" = "1" ]
|
||||
then
|
||||
forceDisableUserChangeArg="--arg forceDisableUserChange true"
|
||||
fi
|
||||
|
||||
NIXPROC=${NIXPROC:-@NIXPROC@}
|
||||
|
||||
# Build the profile
|
||||
nix-build $stateDirArg $runtimeDirArg $logDirArg $tmpDirArg $forceDisableUserChangeArg $noOutLinkArg $showTraceArg $processManagerArg --argstr exprFile "$exprFile" $NIXPROC/create-managed-process/$processManager/build-$processManager-env.nix
|
|
@ -0,0 +1,96 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
containsElement()
|
||||
{
|
||||
local element match="$1"
|
||||
shift
|
||||
|
||||
for element
|
||||
do
|
||||
[[ "$element" == "$match" ]] && return 0
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
checkNixStateDir()
|
||||
{
|
||||
NIX_STATE_DIR=${NIX_STATE_DIR:-/nix/var/nix}
|
||||
}
|
||||
|
||||
checkProfile()
|
||||
{
|
||||
profile=${profile:-processes}
|
||||
}
|
||||
|
||||
buildProfile()
|
||||
{
|
||||
local processManager="$1"
|
||||
profilePath=$(nixproc-build --process-manager $processManager $stateDirArg $runtimeDirArg $logDirArg $tmpDirArg $forceDisableUserChangeArg $showTraceArg --no-out-link "$path")
|
||||
}
|
||||
|
||||
composeOldProfilePath()
|
||||
{
|
||||
if [ "$oldProfilePath" = "" ]
|
||||
then
|
||||
oldProfilePath="$NIX_STATE_DIR/profiles/$profile"
|
||||
fi
|
||||
}
|
||||
|
||||
setNixProfile()
|
||||
{
|
||||
nix-env -p "$NIX_STATE_DIR/profiles/$profile" --set "$profilePath"
|
||||
}
|
||||
|
||||
createNewGroups()
|
||||
{
|
||||
for groupfile in $profilePath/dysnomia-support/groups/*
|
||||
do
|
||||
local groupname="$(basename $groupfile)"
|
||||
|
||||
if [ ! -f "$oldProfilePath/dysnomia-support/groups/$groupname" ]
|
||||
then
|
||||
echo dysnomia-addgroups "$profilePath/dysnomia-support/groups/$groupname"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
createNewUsers()
|
||||
{
|
||||
for userfile in $profilePath/dysnomia-support/users/*
|
||||
do
|
||||
local username="$(basename $userfile)"
|
||||
|
||||
if [ ! -f "$oldProfilePath/dysnomia-support/users/$username" ]
|
||||
then
|
||||
echo dysnomia-addusers "$profilePath/dysnomia-support/users/$username"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
deleteObsoleteUsers()
|
||||
{
|
||||
for userfile in $oldProfilePath/dysnomia-support/users/*
|
||||
do
|
||||
local username="$(basename $userfile)"
|
||||
|
||||
if [ ! -f "$profilePath/dysnomia-support/users/$username" ]
|
||||
then
|
||||
echo dysnomia-delusers "$oldProfilePath/dysnomia-support/users/$username"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
deleteObsoleteGroups()
|
||||
{
|
||||
for groupfile in $oldProfilePath/dysnomia-support/groups/*
|
||||
do
|
||||
local groupname="$(basename $groupfile)"
|
||||
|
||||
if [ ! -f "$profilePath/dysnomia-support/groups/$groupname" ]
|
||||
then
|
||||
echo dysnomia-delgroups "$oldProfilePath/dysnomia-support/groups/$groupname"
|
||||
fi
|
||||
done
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-cygrunsrv-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
${./nixproc-cygrunsrv-switch.in} > $out/bin/nixproc-cygrunsrv-switch
|
||||
chmod +x $out/bin/nixproc-cygrunsrv-switch
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,167 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command updates the Windows services configuration so that obsolete
|
||||
services will be stoppped and new services will be started.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
buildProfile cygrunsrv
|
||||
|
||||
# Determine paths of old param files
|
||||
|
||||
oldparams=()
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
for i in $oldProfilePath/*-cygrunsrvparams
|
||||
do
|
||||
currentPath=$(readlink -f "$i")
|
||||
oldparams+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new param files
|
||||
|
||||
newparams=()
|
||||
|
||||
for i in $profilePath/*-cygrunsrvparams
|
||||
do
|
||||
currentPath=$(readlink -f "$i")
|
||||
newparams+=($currentPath)
|
||||
done
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
# Stop and remove obsolete services
|
||||
|
||||
for i in $oldProfilePath/*-cygrunsrvparams
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${newparams[@]}"
|
||||
then
|
||||
serviceName="$(basename $i -cygrunsrvparams)"
|
||||
cygrunsrv --stop $serviceName
|
||||
cygrunsrv --remove $serviceName
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
||||
|
||||
# Install all services in the new configuration
|
||||
for i in $profilePath/*-cygrunsrvparams
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${oldparams[@]}"
|
||||
then
|
||||
serviceName="$(basename $i -cygrunsrvparams)"
|
||||
cat $i | xargs -d '\n' /bin/echo cygrunsrv --install $serviceName
|
||||
fi
|
||||
done
|
||||
|
||||
# Start all services in the new configuration
|
||||
for i in $profilePath/*-cygrunsrvparams
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${oldparams[@]}"
|
||||
then
|
||||
serviceName="$(basename $i -cygrunsrvparams)"
|
||||
cygrunsrv --start $serviceName
|
||||
fi
|
||||
done
|
|
@ -0,0 +1,37 @@
|
|||
{ pkgs ? import <nixpkgs> { inherit system; }
|
||||
, system ? builtins.currentSystem
|
||||
}:
|
||||
|
||||
rec {
|
||||
build = import ./build {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
generate-config = import ./generate-config {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
bsdrc = import ./bsdrc {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
cygrunsrv = import ./cygrunsrv {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
launchd = import ./launchd {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
supervisord = import ./supervisord {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
systemd = import ./systemd {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
|
||||
sysvinit = import ./sysvinit {
|
||||
inherit (pkgs) stdenv getopt;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-generate-config";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@NIXPROC@|${../../nixproc}|" \
|
||||
${./nixproc-generate-config.in} > $out/bin/nixproc-generate-config
|
||||
chmod +x $out/bin/nixproc-generate-config
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,111 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
# Shows the usage of this command to the user
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
Takes a JSON representation of a managed process configuration and translates
|
||||
it to a process manager specific configuration.
|
||||
|
||||
Options:
|
||||
-P, --process-manager=MANAGER
|
||||
Process manager to build for
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--no-out-link Do not create a symlink to the output path
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o P:h -l process-manager:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,no-out-link,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-P|--process-manager)
|
||||
processManager="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--no-out-link)
|
||||
noOutLinkArg="--no-out-link"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
configFile="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
if [ "$configFile" = "" ]
|
||||
then
|
||||
echo "A config file must be provided!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "$processManager" = "" ]
|
||||
then
|
||||
echo "No process manager was specified!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
NIXPROC=${NIXPROC:-@NIXPROC@}
|
||||
|
||||
# Build the configuration
|
||||
nix-build $NIXPROC/create-managed-process/agnostic/create-managed-process-from-config.nix --arg configFile $configFile --argstr processManager $processManager $stateDirArg $runtimeDirArg $logDirArg $tmpDirArg $forceDisableUserChangeArg $noOutLinkArg $showTraceArg
|
|
@ -0,0 +1,15 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-launchd-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
-e "s|@readlink@|$(type -p readlink)|" \
|
||||
${./nixproc-launchd-switch.in} > $out/bin/nixproc-launchd-switch
|
||||
chmod +x $out/bin/nixproc-launchd-switch
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,173 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command repopulates a folder with launchd plist files and updates the
|
||||
configuration so that obsolete services will be stoppped and new services will
|
||||
be started.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
LAUNCHD_TARGET_DIR Directory in which the plist configuration files are
|
||||
managed (defaults to: /Library/LaunchDaemons)
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
LAUNCHD_TARGET_DIR=${LAUNCHD_TARGET_DIR:-/Library/LaunchDaemons}
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
buildProfile launchd
|
||||
|
||||
# Determine paths of old plists
|
||||
|
||||
oldplists=()
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
for i in $oldProfilePath/Library/LaunchDaemons/*.plist
|
||||
do
|
||||
currentPath=$(@readlink@ -f "$i")
|
||||
oldplists+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new plists
|
||||
|
||||
newplists=()
|
||||
|
||||
for i in $profilePath/Library/LaunchDaemons/*.plist
|
||||
do
|
||||
currentPath=$(@readlink@ -f "$i")
|
||||
newplists+=($currentPath)
|
||||
done
|
||||
|
||||
# Create new groups and users
|
||||
createNewGroups
|
||||
createNewUsers
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
# Stop and remove obsolete plists
|
||||
|
||||
for i in $oldProfilePath/Library/LaunchDaemons/*.plist
|
||||
do
|
||||
if ! containsElement "$(@readlink@ -f "$i")" "${newplists[@]}"
|
||||
then
|
||||
launchctl stop "$(basename "$i" .plist)"
|
||||
unitTargetPath="$LAUNCHD_TARGET_DIR/$(basename "$i")"
|
||||
launchctl unload "$unitTargetPath"
|
||||
rm -f "$unitTargetPath"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Start all plists in the new configuration
|
||||
for i in $profilePath/Library/LaunchDaemons/*.plist
|
||||
do
|
||||
if ! containsElement "$(@readlink@ -f "$i")" "${oldplists[@]}"
|
||||
then
|
||||
unitTargetPath="$LAUNCHD_TARGET_DIR/$(basename "$i")"
|
||||
cp "$(@readlink@ -f "$i")" "$unitTargetPath"
|
||||
launchctl load -w "$unitTargetPath"
|
||||
launchctl start "$(basename "$i" .plist)"
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete obsolete users and groups
|
||||
deleteObsoleteUsers
|
||||
deleteObsoleteGroups
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
|
@ -0,0 +1,32 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-supervisord-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@readlink@|$(type -p readlink)|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
-e "s|@supervisordchecks@|${./supervisordchecks}|" \
|
||||
${./nixproc-supervisord-start.in} > $out/bin/nixproc-supervisord-start
|
||||
chmod +x $out/bin/nixproc-supervisord-start
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@readlink@|$(type -p readlink)|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
-e "s|@supervisordchecks@|${./supervisordchecks}|" \
|
||||
${./nixproc-supervisord-switch.in} > $out/bin/nixproc-supervisord-switch
|
||||
chmod +x $out/bin/nixproc-supervisord-switch
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@readlink@|$(type -p readlink)|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
-e "s|@supervisordchecks@|${./supervisordchecks}|" \
|
||||
${./nixproc-supervisord-deploy-stateless.in} > $out/bin/nixproc-supervisord-deploy-stateless
|
||||
chmod +x $out/bin/nixproc-supervisord-deploy-stateless
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command starts supervisord with a provided configuration and set of
|
||||
services in foreground mode. When the configuration changes, supervisord and all
|
||||
services need to be restarted.
|
||||
|
||||
To do more efficient (but stateful) upgrades, use
|
||||
\`nixproc-supervisord-switch'.
|
||||
|
||||
Options:
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o h -l state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
profilePath=$(nixproc-build --process-manager supervisord $stateDirArg $runtimeDirArg $logDirArg $tmpDirArg $forceDisableUserChangeArg $showTraceArg --no-out-link "$path")
|
||||
|
||||
# Create groups and users
|
||||
dysnomia-addgroups "$profilePath"
|
||||
dysnomia-addusers "$profilePath"
|
||||
|
||||
# Start supervisord in foreground mode
|
||||
supervisord -n -c "$profilePath/supervisord.conf"
|
||||
|
||||
# Discard groups and users
|
||||
dysnomia-delusers "$profilePath"
|
||||
dysnomia-delgroups "$profilePath"
|
|
@ -0,0 +1,92 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command starts supervisord with an empty configuration that can be
|
||||
populated with services by running \`nixproc-supervisord-switch'.
|
||||
|
||||
Options:
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
SUPERVISORD_CONF_DIR Directory if which the supervisord.conf resides
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o h -l state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
source @supervisordchecks@
|
||||
|
||||
checkSupervisordConfDir
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
profilePath=$(nixproc-build --process-manager supervisord $stateDirArg $runtimeDirArg $logDirArg $tmpDirArg $forceDisableUserChangeArg $showTraceArg --no-out-link "$path")
|
||||
|
||||
# Create the supervisord config directory and start supervisord in foreground mode
|
||||
mkdir -p "$SUPERVISORD_CONF_DIR/conf.d"
|
||||
cp "$profilePath/supervisord.conf" "$SUPERVISORD_CONF_DIR"
|
||||
supervisord -n -c "$SUPERVISORD_CONF_DIR/supervisord.conf"
|
|
@ -0,0 +1,178 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command repopulates the conf.d sub directory of a supervisord configuration
|
||||
and updates the live configuration so that obsolete services will be stopped and
|
||||
new services will be activated.
|
||||
|
||||
If the provided path is a file then it is considered a Nix expression that
|
||||
produces a Nix profile. If the provided path is a directory, then it is
|
||||
considered a pre-built Nix profile.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
SUPERVISORD_CONF_DIR Directory if which the supervisord.conf resides
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
source @supervisordchecks@
|
||||
|
||||
checkSupervisordConfDir
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
buildProfile supervisord
|
||||
|
||||
# Determine paths of old units
|
||||
|
||||
oldunits=()
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
for i in $oldProfilePath/conf.d/*.conf
|
||||
do
|
||||
currentPath=$(@readlink@ -f "$i")
|
||||
oldunits+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new units
|
||||
|
||||
newunits=()
|
||||
|
||||
for i in $profilePath/conf.d/*.conf
|
||||
do
|
||||
currentPath=$(@readlink@ -f "$i")
|
||||
newunits+=($currentPath)
|
||||
done
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
# Remove obsolete units
|
||||
|
||||
for i in $oldProfilePath/conf.d/*.conf
|
||||
do
|
||||
if ! containsElement "$(@readlink@ -f "$i")" "${newunits[@]}"
|
||||
then
|
||||
unitTargetPath="$SUPERVISORD_CONF_DIR/conf.d/$(basename "$i")"
|
||||
rm -f "$unitTargetPath"
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Add new units
|
||||
|
||||
for i in $profilePath/conf.d/*.conf
|
||||
do
|
||||
if ! containsElement "$(@readlink@ -f "$i")" "${oldunits[@]}"
|
||||
then
|
||||
ln -sfn "$(@readlink@ -f "$i")" $SUPERVISORD_CONF_DIR/conf.d/$(basename "$i")
|
||||
fi
|
||||
done
|
||||
|
||||
# Create new groups and users
|
||||
createNewGroups
|
||||
createNewUsers
|
||||
|
||||
# Reload and update the supervisord configuration
|
||||
supervisorctl reread
|
||||
supervisorctl update
|
||||
|
||||
# Delete obsolete users and groups
|
||||
deleteObsoleteUsers
|
||||
deleteObsoleteGroups
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
|
@ -0,0 +1,10 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
checkSupervisordConfDir()
|
||||
{
|
||||
if [ "$SUPERVISORD_CONF_DIR" = "" ]
|
||||
then
|
||||
echo "Please set SUPERVISORD_CONF_DIR to the directory where the supervisord.conf configuration resides!" >&2
|
||||
exit 1
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-systemd-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
${./nixproc-systemd-switch.in} > $out/bin/nixproc-systemd-switch
|
||||
chmod +x $out/bin/nixproc-systemd-switch
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command repopulates a folder with systemd configuration files and updates
|
||||
the configuration so that obsolete services will be stoppped and new services
|
||||
will be started.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
SYSTEMD_TARGET_DIR Directory in which the unit configuration files are
|
||||
managed (defaults to: /etc/systemd/system)
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
SYSTEMD_TARGET_DIR=${SYSTEMD_TARGET_DIR:-/etc/systemd/system}
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
# Build the environment with supervisord config files
|
||||
buildProfile systemd
|
||||
|
||||
# Determine paths of old units
|
||||
|
||||
oldunits=()
|
||||
|
||||
if [ -d "$oldProfilePath" ]
|
||||
then
|
||||
for i in $oldProfilePath/etc/systemd/system/*.service
|
||||
do
|
||||
currentPath=$(readlink -f "$i")
|
||||
oldunits+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new units
|
||||
|
||||
newunits=()
|
||||
|
||||
for i in $profilePath/etc/systemd/system/*.service
|
||||
do
|
||||
currentPath=$(readlink -f "$i")
|
||||
newunits+=($currentPath)
|
||||
done
|
||||
|
||||
# Create new groups and users
|
||||
createNewGroups
|
||||
createNewUsers
|
||||
|
||||
if [ "$oldProfilePath" != "" ]
|
||||
then
|
||||
# Stop obsolete units
|
||||
|
||||
for i in $oldProfilePath/etc/systemd/system/*.service
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${newunits[@]}"
|
||||
then
|
||||
systemctl stop "$(basename "$i")"
|
||||
fi
|
||||
done
|
||||
|
||||
# Remove obsolete units
|
||||
|
||||
for i in $oldProfilePath/etc/systemd/system/*.service
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${newunits[@]}"
|
||||
then
|
||||
unitTargetPath="$SYSTEMD_TARGET_DIR/$(basename "$i")"
|
||||
rm -f "$unitTargetPath"
|
||||
|
||||
if [ -d "$i.wants" ]
|
||||
then
|
||||
rm -f "$unitTargetPath.wants"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Add new units
|
||||
|
||||
for i in $profilePath/etc/systemd/system/*.service
|
||||
do
|
||||
if ! containsElement "$(readlink -f "$i")" "${oldunits[@]}"
|
||||
then
|
||||
if [ -d "$i.wants" ]
|
||||
then
|
||||
ln -sfn "$(readlink -f "$i.wants")" $SYSTEMD_TARGET_DIR
|
||||
fi
|
||||
|
||||
ln -sfn "$(readlink -f "$i")" $SYSTEMD_TARGET_DIR/$(basename "$i")
|
||||
fi
|
||||
done
|
||||
|
||||
# Reload the systemd configuration
|
||||
systemctl daemon-reload
|
||||
|
||||
# Start all units in the new configuration
|
||||
for i in $profilePath/etc/systemd/system/*.service
|
||||
do
|
||||
systemctl start "$(basename "$i")"
|
||||
done
|
||||
|
||||
# Delete obsolete users and groups
|
||||
deleteObsoleteUsers
|
||||
deleteObsoleteGroups
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
|
@ -0,0 +1,22 @@
|
|||
{stdenv, getopt}:
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "nixproc-sysvinit-tools";
|
||||
buildCommand = ''
|
||||
mkdir -p $out/bin
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
-e "s|@sysvinitchecks@|${./sysvinitchecks}|" \
|
||||
${./nixproc-sysvinit-switch.in} > $out/bin/nixproc-sysvinit-switch
|
||||
chmod +x $out/bin/nixproc-sysvinit-switch
|
||||
|
||||
sed -e "s|/bin/bash|$SHELL|" \
|
||||
-e "s|@getopt@|${getopt}/bin/getopt|" \
|
||||
-e "s|@commonchecks@|${../commonchecks}|" \
|
||||
-e "s|@sysvinitchecks@|${./sysvinitchecks}|" \
|
||||
${./nixproc-sysvinit-runactivity.in} > $out/bin/nixproc-sysvinit-runactivity
|
||||
chmod +x $out/bin/nixproc-sysvinit-runactivity
|
||||
'';
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
# Shows the usage of this command to the user
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] ACTIVITY [PATH]
|
||||
|
||||
This command runs a deployment activity on all sysvinit scripts in a Nix
|
||||
profile. The scripts are traversed in sequence order.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-r, --reverse Traverse the sysvinit scripts in the reverse order
|
||||
--runlevel=LEVEL Specifies which runlevel to activate (defaults to the
|
||||
runlevel of the system)
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:rh -l profile:,reverse,runlevel:,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-r|--reverse)
|
||||
reverse=1
|
||||
;;
|
||||
--runlevel)
|
||||
runlevel="$2"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
activity="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
if [ "$activity" = "" ]
|
||||
then
|
||||
echo "No activity specified!" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
source @sysvinitchecks@
|
||||
|
||||
checkRunlevel
|
||||
|
||||
# Execute the activities
|
||||
|
||||
rcpath="$oldProfilePath/etc/rc.d/rc${runlevel}.d"
|
||||
|
||||
if [ "$reverse" = "1" ]
|
||||
then
|
||||
for i in $(ls $rcpath/S* | sort -r)
|
||||
do
|
||||
$i $activity
|
||||
done
|
||||
else
|
||||
for i in $(ls $rcpath/S*)
|
||||
do
|
||||
$i $activity
|
||||
done
|
||||
fi
|
|
@ -0,0 +1,181 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
shopt -s nullglob
|
||||
|
||||
# Shows the usage of this command to the user
|
||||
|
||||
showUsage()
|
||||
{
|
||||
cat <<EOF
|
||||
Usage: $0 [OPTION] PATH
|
||||
|
||||
This command starts all sysvinit scripts in the provided Nix profile and
|
||||
optionally deactivates all obsolete sysvinit scripts in the previous Nix
|
||||
profile generation.
|
||||
|
||||
If the provided path is a file then it is considered a Nix expression that
|
||||
produces a Nix profile. If the provided path is a directory, then it is
|
||||
considered a pre-built Nix profile.
|
||||
|
||||
Options:
|
||||
-p, --profile=NAME Name of the Nix profile that stores the sysvinit scripts
|
||||
(defaults to: processes)
|
||||
-o, --old-profile=PATH
|
||||
Path to the previously deployed Nix profile (by default,
|
||||
it gets auto detected)
|
||||
--runlevel=LEVEL Specifies which runlevel to activate (defaults to the
|
||||
runlevel of the system)
|
||||
--state-dir Changes the directory in which the state of the
|
||||
processes are stored
|
||||
--runtime-dir Changes the directory in which the PID files are stored
|
||||
--log-dir Changes the directory in which the log files are stored
|
||||
--tmp-dir Changes the directory in which temp files are stored
|
||||
--force-disable-user-change
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
--show-trace Shows a trace of the output
|
||||
-h, --help Shows the usage of this command
|
||||
|
||||
Environment:
|
||||
NIX_STATE_DIR Overrides the location of the Nix state directory
|
||||
NIXPROC_STATE_DIR Changes the directory in which the state of the
|
||||
processes is stored
|
||||
NIXPROC_RUNTIME_DIR Changes the directory in which the PID files are stored
|
||||
NIXPROC_LOG_DIR Changes the directory in which log files are stored
|
||||
NIXPROC_TMP_DIR Changes the directory in which temp files are stored
|
||||
NIXPROC_FORCE_DISABLE_USER_CHANGE
|
||||
Forces to not create users, groups or change user
|
||||
permissions
|
||||
EOF
|
||||
}
|
||||
|
||||
# Parse valid argument options
|
||||
|
||||
PARAMS=`@getopt@ -n $0 -o p:o:h -l profile:,old-profile:,state-dir:,runtime-dir:,log-dir:,tmp-dir:,force-disable-user-change,runlevel:,show-trace,help -- "$@"`
|
||||
|
||||
if [ $? != 0 ]
|
||||
then
|
||||
showUsage
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Evaluate valid options
|
||||
|
||||
eval set -- "$PARAMS"
|
||||
|
||||
while [ "$1" != "--" ]
|
||||
do
|
||||
case "$1" in
|
||||
-p|--profile)
|
||||
profile="$2"
|
||||
;;
|
||||
-o|--old-profile)
|
||||
oldProfilePath="$2"
|
||||
;;
|
||||
--state-dir)
|
||||
stateDirArg="--state-dir $2"
|
||||
;;
|
||||
--runtime-dir)
|
||||
runtimeDirArg="--runtime-dir $2"
|
||||
;;
|
||||
--log-dir)
|
||||
logDirArg="--log-dir $2"
|
||||
;;
|
||||
--tmp-dir)
|
||||
tmpDirArg="--tmp-dir $2"
|
||||
;;
|
||||
--force-disable-user-change)
|
||||
forceDisableUserChangeArg="--force-disable-user-change"
|
||||
;;
|
||||
--runlevel)
|
||||
runlevel="$2"
|
||||
;;
|
||||
--show-trace)
|
||||
showTraceArg="--show-trace"
|
||||
;;
|
||||
-h|--help)
|
||||
showUsage
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
|
||||
shift
|
||||
done
|
||||
|
||||
shift
|
||||
|
||||
path="$1"
|
||||
|
||||
# Validate the given options
|
||||
|
||||
source @commonchecks@
|
||||
|
||||
checkNixStateDir
|
||||
checkProfile
|
||||
composeOldProfilePath
|
||||
|
||||
source @sysvinitchecks@
|
||||
|
||||
checkRunlevel
|
||||
|
||||
# Build the environment with sysvinit scripts
|
||||
buildProfile sysvinit
|
||||
|
||||
rcnew="$profilePath/etc/rc.d/rc${runlevel}.d"
|
||||
rcold="$oldProfilePath/etc/rc.d/rc${runlevel}.d"
|
||||
|
||||
# Determine paths of old scripts
|
||||
|
||||
oldscripts=()
|
||||
|
||||
if [ -d "$rcold" ]
|
||||
then
|
||||
echo "Using previous Nix profile: $rcold" >&2
|
||||
|
||||
for i in $(ls $rcold/S* | sort -r)
|
||||
do
|
||||
currentPath=$(readlink -f $i)
|
||||
oldscripts+=($currentPath)
|
||||
done
|
||||
fi
|
||||
|
||||
# Determine paths of new scripts
|
||||
|
||||
newscripts=()
|
||||
|
||||
for i in $(ls $rcnew/S*)
|
||||
do
|
||||
currentPath=$(readlink -f $i)
|
||||
newscripts+=($currentPath)
|
||||
done
|
||||
|
||||
# Create new groups and users
|
||||
createNewGroups
|
||||
createNewUsers
|
||||
|
||||
# Stop obsolete scripts
|
||||
|
||||
for i in ${oldscripts[@]}
|
||||
do
|
||||
if ! containsElement "$i" "${newscripts[@]}"
|
||||
then
|
||||
$i stop
|
||||
fi
|
||||
done
|
||||
|
||||
# Start new scripts
|
||||
|
||||
for i in ${newscripts[@]}
|
||||
do
|
||||
if ! containsElement "$i" "${oldscripts[@]}"
|
||||
then
|
||||
$i start
|
||||
fi
|
||||
done
|
||||
|
||||
# Delete obsolete users and groups
|
||||
deleteObsoleteUsers
|
||||
deleteObsoleteGroups
|
||||
|
||||
# Set new profile
|
||||
setNixProfile
|
|
@ -0,0 +1,9 @@
|
|||
#!/bin/bash -e
|
||||
|
||||
checkRunlevel()
|
||||
{
|
||||
if [ "$runlevel" = "" ]
|
||||
then
|
||||
runlevel=$(runlevel | cut -d ' ' -f2)
|
||||
fi
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
CC = cc
|
||||
|
||||
all:
|
||||
$(CC) $(CFLAGS) -c daemonize.c
|
||||
$(CC) $(CFLAGS) -c service.c
|
||||
$(CC) $(CFLAGS) $(LDFLAGS) daemonize.o service.o main.c -lmicrohttpd -o webapp
|
||||
|
||||
install: all
|
||||
install -d -m755 $(PREFIX)/bin
|
||||
install -m755 webapp $(PREFIX)/bin
|
||||
|
||||
clean:
|
||||
rm -f *.o
|
||||
rm -f webapp
|
|
@ -0,0 +1,18 @@
|
|||
Test web application
|
||||
====================
|
||||
This is a very simple test web application that can run in foreground mode and
|
||||
daemon mode. Its only purpose is to return a very simple static HTML page.
|
||||
|
||||
The most interesting part of this example is probably the daemonize
|
||||
infrastructure (`daemonize.h`, `daemonize.c`) -- I have been trying to closely
|
||||
follow systemd's recommendations for implementing traditional SysV daemons
|
||||
(more info:`man 7 daemon`) sticking myself to POSIX standards as much as
|
||||
possible.
|
||||
|
||||
To keep the code as clear as possible, I have encapsulated each recommended
|
||||
step into a function abstraction, and every failure yields a distinct error
|
||||
code so that we can easily trace the origins of the error.
|
||||
|
||||
The daemonize infrastructure is very generic -- you only need to provide a
|
||||
pointer to a function that initializes the daemon's state and a pointer to
|
||||
a function that runs the main loop.
|
|
@ -0,0 +1,259 @@
|
|||
#include "daemonize.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define NULL_DEV_FILE "/dev/null"
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
#define BUFFER_SIZE 10
|
||||
|
||||
static int close_non_standard_file_descriptors(void)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
struct rlimit rlim;
|
||||
int num_of_fds = getrlimit(RLIMIT_NOFILE, &rlim);
|
||||
|
||||
if(num_of_fds == -1)
|
||||
return FALSE;
|
||||
|
||||
for(i = 3; i < num_of_fds; i++)
|
||||
close(i);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int reset_signal_handlers_to_default(void)
|
||||
{
|
||||
#if defined _NSIG
|
||||
unsigned int i;
|
||||
|
||||
for(i = 1; i < _NSIG; i++)
|
||||
{
|
||||
if(i != SIGKILL && i != SIGSTOP)
|
||||
signal(i, SIG_DFL);
|
||||
}
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int clear_signal_mask(void)
|
||||
{
|
||||
sigset_t set;
|
||||
|
||||
return((sigemptyset(&set) == 0)
|
||||
&& (sigprocmask(SIG_SETMASK, &set, NULL) == 0));
|
||||
}
|
||||
|
||||
static int create_pid_file(const char *pid_file)
|
||||
{
|
||||
pid_t my_pid = getpid();
|
||||
char my_pid_str[10];
|
||||
int fd;
|
||||
|
||||
sprintf(my_pid_str, "%d", my_pid);
|
||||
|
||||
if((fd = open(pid_file, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR | S_IWUSR)) == -1)
|
||||
return FALSE;
|
||||
|
||||
if(write(fd, my_pid_str, strlen(my_pid_str)) == -1)
|
||||
return FALSE;
|
||||
|
||||
close(fd);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static int attach_standard_file_descriptors_to_null(void)
|
||||
{
|
||||
int null_fd_read, null_fd_write;
|
||||
|
||||
return(((null_fd_read = open(NULL_DEV_FILE, O_RDONLY)) != -1)
|
||||
&& (dup2(null_fd_read, STDIN_FILENO) != -1)
|
||||
&& ((null_fd_write = open(NULL_DEV_FILE, O_WRONLY)) != -1)
|
||||
&& (dup2(null_fd_write, STDOUT_FILENO) != -1)
|
||||
&& (dup2(null_fd_write, STDERR_FILENO) != -1));
|
||||
}
|
||||
|
||||
static void notify_parent_process(int writefd, DaemonStatus message)
|
||||
{
|
||||
char byte = (char)message;
|
||||
while(write(writefd, &byte, 1) == 0);
|
||||
close(writefd);
|
||||
}
|
||||
|
||||
static pid_t fork_daemon_process(int writefd, const char *pid_file, void *data, int (*initialize_daemon) (void *data), int (*run_main_loop) (void *data))
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if(pid == 0)
|
||||
{
|
||||
if(!attach_standard_file_descriptors_to_null())
|
||||
{
|
||||
notify_parent_process(writefd, STATUS_CANNOT_ATTACH_STD_FDS_TO_NULL);
|
||||
exit(STATUS_CANNOT_ATTACH_STD_FDS_TO_NULL);
|
||||
}
|
||||
|
||||
umask(0);
|
||||
|
||||
if(chdir("/") == -1)
|
||||
{
|
||||
notify_parent_process(writefd, STATUS_CANNOT_CHDIR);
|
||||
exit(STATUS_CANNOT_CHDIR);
|
||||
}
|
||||
|
||||
if(!create_pid_file(pid_file))
|
||||
{
|
||||
notify_parent_process(writefd, STATUS_CANNOT_CREATE_PID_FILE);
|
||||
exit(STATUS_CANNOT_CREATE_PID_FILE);
|
||||
}
|
||||
|
||||
if(!initialize_daemon(data))
|
||||
{
|
||||
notify_parent_process(writefd, STATUS_CANNOT_INIT_DAEMON);
|
||||
unlink(pid_file);
|
||||
exit(STATUS_CANNOT_INIT_DAEMON);
|
||||
}
|
||||
|
||||
notify_parent_process(writefd, STATUS_INIT_SUCCESS);
|
||||
|
||||
int exit_status = run_main_loop(data);
|
||||
|
||||
if(unlink(pid_file) == 0)
|
||||
exit(STATUS_CANNOT_UNLINK_PID_FILE);
|
||||
|
||||
exit(exit_status);
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static pid_t fork_helper_process(int pipefd[2], const char *pid_file, void *data, int (*initialize_daemon) (void *data), int (*run_main_loop) (void *data))
|
||||
{
|
||||
pid_t pid = fork();
|
||||
|
||||
if(pid == 0)
|
||||
{
|
||||
close(pipefd[0]); /* Close unneeded read-end */
|
||||
|
||||
if(setsid() == -1)
|
||||
{
|
||||
notify_parent_process(pipefd[1], STATUS_CANNOT_SET_SID);
|
||||
exit(STATUS_CANNOT_SET_SID);
|
||||
}
|
||||
|
||||
/* Fork again, so that the terminal can not be acquired again */
|
||||
if(fork_daemon_process(pipefd[1], pid_file, data, initialize_daemon, run_main_loop) == -1)
|
||||
{
|
||||
notify_parent_process(pipefd[1], STATUS_CANNOT_FORK_DAEMON_PROCESS);
|
||||
exit(STATUS_CANNOT_FORK_DAEMON_PROCESS);
|
||||
}
|
||||
|
||||
exit(0); /* Exit the helper process, so that the daemon process gets adopted by PID 1 */
|
||||
}
|
||||
|
||||
return pid;
|
||||
}
|
||||
|
||||
static DaemonStatus wait_for_notification_message(int readfd)
|
||||
{
|
||||
char buf[BUFFER_SIZE];
|
||||
ssize_t bytes_read = read(readfd, buf, 1);
|
||||
|
||||
if(bytes_read == -1)
|
||||
return STATUS_CANNOT_READ_FROM_PIPE;
|
||||
else if(bytes_read == 0)
|
||||
return STATUS_UNKNOWN_DAEMON_ERROR;
|
||||
else
|
||||
return buf[0];
|
||||
}
|
||||
|
||||
DaemonStatus daemonize(const char *pid_file, void *data, int (*initialize_daemon) (void *data), int (*run_main_loop) (void *data))
|
||||
{
|
||||
int pipefd[2];
|
||||
|
||||
if(!close_non_standard_file_descriptors())
|
||||
return STATUS_CANNOT_CLOSE_NON_STD_FDS;
|
||||
|
||||
if(!reset_signal_handlers_to_default())
|
||||
return STATUS_CANNOT_RESET_SIGNAL_HANDLERS;
|
||||
|
||||
if(!clear_signal_mask())
|
||||
return STATUS_CANNOT_CLEAR_SIGNAL_MASK;
|
||||
|
||||
if(pipe(pipefd) == -1)
|
||||
return STATUS_CANNOT_CREATE_PIPE;
|
||||
else
|
||||
{
|
||||
if(fork_helper_process(pipefd, pid_file, data, initialize_daemon, run_main_loop) == -1)
|
||||
return STATUS_CANNOT_FORK_HELPER_PROCESS;
|
||||
else
|
||||
{
|
||||
DaemonStatus exit_status;
|
||||
|
||||
close(pipefd[1]); /* Close unneeded write end */
|
||||
exit_status = wait_for_notification_message(pipefd[0]);
|
||||
close(pipefd[0]);
|
||||
return exit_status;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void print_daemon_status(DaemonStatus status, FILE *file)
|
||||
{
|
||||
switch(status)
|
||||
{
|
||||
case STATUS_INIT_SUCCESS:
|
||||
fprintf(file, "Daemon initialized successfully!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_ATTACH_STD_FDS_TO_NULL:
|
||||
fprintf(file, "Cannot attach standard file descriptors to " NULL_DEV_FILE " !\n");
|
||||
break;
|
||||
case STATUS_CANNOT_CHDIR:
|
||||
fprintf(file, "Cannot change current working directory to /\n");
|
||||
break;
|
||||
case STATUS_CANNOT_CREATE_PID_FILE:
|
||||
fprintf(file, "Cannot create PID file!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_INIT_DAEMON:
|
||||
fprintf(file, "Cannot initialize the daemon!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_UNLINK_PID_FILE:
|
||||
fprintf(file, "Cannot unlink PID file!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_CLOSE_NON_STD_FDS:
|
||||
fprintf(file, "Cannot close the non standard file descriptors!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_RESET_SIGNAL_HANDLERS:
|
||||
fprintf(file, "Cannot reset signal handlers!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_CLEAR_SIGNAL_MASK:
|
||||
fprintf(file, "Cannot clear signal mask!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_CREATE_PIPE:
|
||||
fprintf(file, "Cannot create pipe!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_FORK_HELPER_PROCESS:
|
||||
fprintf(file, "Cannot fork helper process!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_READ_FROM_PIPE:
|
||||
fprintf(file, "Cannot read from pipe!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_SET_SID:
|
||||
fprintf(file, "Cannot set session ID!\n");
|
||||
break;
|
||||
case STATUS_CANNOT_FORK_DAEMON_PROCESS:
|
||||
fprintf(file, "Cannot fork daemon process!\n");
|
||||
break;
|
||||
case STATUS_UNKNOWN_DAEMON_ERROR:
|
||||
fprintf(file, "Unknown daemon error!\n");
|
||||
break;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef __DAEMONIZE_H
|
||||
#define __DAEMONIZE_H
|
||||
#include <stdio.h>
|
||||
|
||||
typedef enum
|
||||
{
|
||||
STATUS_INIT_SUCCESS = 0x0,
|
||||
STATUS_CANNOT_ATTACH_STD_FDS_TO_NULL = 0x1,
|
||||
STATUS_CANNOT_CHDIR = 0x2,
|
||||
STATUS_CANNOT_CREATE_PID_FILE = 0x3,
|
||||
STATUS_CANNOT_INIT_DAEMON = 0x4,
|
||||
STATUS_CANNOT_UNLINK_PID_FILE = 0x5,
|
||||
STATUS_CANNOT_CLOSE_NON_STD_FDS = 0x6,
|
||||
STATUS_CANNOT_RESET_SIGNAL_HANDLERS = 0x7,
|
||||
STATUS_CANNOT_CLEAR_SIGNAL_MASK = 0x8,
|
||||
STATUS_CANNOT_CREATE_PIPE = 0x9,
|
||||
STATUS_CANNOT_FORK_HELPER_PROCESS = 0xa,
|
||||
STATUS_CANNOT_READ_FROM_PIPE = 0xb,
|
||||
STATUS_CANNOT_SET_SID = 0xc,
|
||||
STATUS_CANNOT_FORK_DAEMON_PROCESS = 0xd,
|
||||
STATUS_UNKNOWN_DAEMON_ERROR = 0xe
|
||||
}
|
||||
DaemonStatus;
|
||||
|
||||
DaemonStatus daemonize(const char *pid_file, void *data, int (*initialize_daemon) (void *data), int (*run_main_loop) (void *data));
|
||||
|
||||
void print_daemon_status(DaemonStatus status, FILE *file);
|
||||
|
||||
#endif
|
|
@ -0,0 +1,11 @@
|
|||
with import <nixpkgs> {};
|
||||
|
||||
stdenv.mkDerivation {
|
||||
name = "webapp";
|
||||
src = ./.;
|
||||
buildInputs = [ libmicrohttpd ];
|
||||
CFLAGS = "-I${libmicrohttpd.dev}/include";
|
||||
LDFLAGS = "-L${libmicrohttpd}/lib";
|
||||
makeFlags = "PREFIX=$(out)";
|
||||
dontStrip = true;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "service.h"
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
unsigned int i;
|
||||
int run_as_daemon = FALSE;
|
||||
int port;
|
||||
char *port_value = getenv("PORT");
|
||||
|
||||
if(port_value == NULL)
|
||||
{
|
||||
fprintf(stderr, "We need a PORT environment variable that specifies to which port the HTTP service should bind to!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
port = atoi(port_value);
|
||||
|
||||
for(i = 1; i < argc; i++)
|
||||
{
|
||||
if(strcmp(argv[i], "-D") == 0)
|
||||
run_as_daemon = TRUE;
|
||||
}
|
||||
|
||||
if(run_as_daemon)
|
||||
{
|
||||
char *pid_file = getenv("PID_FILE");
|
||||
|
||||
if(pid_file == NULL)
|
||||
{
|
||||
fprintf(stderr, "We need the PID_FILE environment variable that specifies the path to a file storing the PID of the daemon process!\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
return run_daemon(port, pid_file);
|
||||
}
|
||||
else
|
||||
return run_foreground_process(port);
|
||||
}
|
|
@ -0,0 +1,158 @@
|
|||
#include "service.h"
|
||||
#include <microhttpd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
|
||||
#define TRUE 1
|
||||
#define FALSE 0
|
||||
|
||||
#define PAGE_TEMPLATE "<!DOCTYPE html>\n"\
|
||||
"<html>\n"\
|
||||
" <head>\n"\
|
||||
" <title>Simple test webapp</title>\n"\
|
||||
" </head>\n"\
|
||||
" <body>\n"\
|
||||
" Simple test webapp listening on port: %d\n"\
|
||||
" </body>\n"\
|
||||
"</html>\n"
|
||||
|
||||
static int ahc_echo(void *cls, struct MHD_Connection *connection, const char *url, const char *method, const char *version, const char *upload_data, size_t *upload_data_size, void **ptr)
|
||||
{
|
||||
static int dummy;
|
||||
const char *page = cls;
|
||||
struct MHD_Response *response;
|
||||
int ret;
|
||||
|
||||
if(strcmp(method, "GET") != 0)
|
||||
return MHD_NO; /* unexpected method */
|
||||
|
||||
if(&dummy != *ptr)
|
||||
{
|
||||
/* The first time only the headers are valid,
|
||||
do not respond in the first round... */
|
||||
*ptr = &dummy;
|
||||
return MHD_YES;
|
||||
}
|
||||
|
||||
if (*upload_data_size != 0)
|
||||
return MHD_NO; /* upload data in a GET!? */
|
||||
|
||||
*ptr = NULL; /* clear context pointer */
|
||||
|
||||
response = MHD_create_response_from_buffer(strlen(page), (void*)page, MHD_RESPMEM_PERSISTENT);
|
||||
ret = MHD_queue_response(connection, MHD_HTTP_OK, response);
|
||||
|
||||
MHD_destroy_response(response);
|
||||
return ret;
|
||||
}
|
||||
|
||||
volatile int terminated = FALSE;
|
||||
|
||||
static void handle_shutdown(int signum)
|
||||
{
|
||||
terminated = TRUE;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
char *page;
|
||||
struct MHD_Daemon *daemon;
|
||||
}
|
||||
DaemonWrapper;
|
||||
|
||||
static DaemonWrapper *create_daemon_wrapper(int port)
|
||||
{
|
||||
DaemonWrapper *wrapper = (DaemonWrapper*)malloc(sizeof(DaemonWrapper));
|
||||
wrapper->page = (char*)malloc(sizeof(PAGE_TEMPLATE) + 10);
|
||||
|
||||
sprintf(wrapper->page, PAGE_TEMPLATE, port);
|
||||
|
||||
wrapper->daemon = MHD_start_daemon(MHD_USE_THREAD_PER_CONNECTION, port, NULL, NULL, &ahc_echo, wrapper->page, MHD_OPTION_END);
|
||||
|
||||
if(wrapper->daemon == NULL)
|
||||
{
|
||||
free(wrapper->page);
|
||||
free(wrapper);
|
||||
return NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
signal(SIGINT, handle_shutdown);
|
||||
signal(SIGTERM, handle_shutdown);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
}
|
||||
|
||||
static void delete_daemon_wrapper(DaemonWrapper *wrapper)
|
||||
{
|
||||
if(wrapper != NULL)
|
||||
{
|
||||
MHD_stop_daemon(wrapper->daemon);
|
||||
free(wrapper->page);
|
||||
free(wrapper);
|
||||
}
|
||||
}
|
||||
|
||||
static int run_main_loop(void)
|
||||
{
|
||||
/* Loop until termination request was received */
|
||||
while(!terminated)
|
||||
sleep(1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int run_foreground_process(int port)
|
||||
{
|
||||
DaemonWrapper *wrapper = create_daemon_wrapper(port);
|
||||
|
||||
if(wrapper == NULL)
|
||||
{
|
||||
fprintf(stderr, "Cannot start HTTP service!\n");
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
int exit_status = run_main_loop();
|
||||
delete_daemon_wrapper(wrapper);
|
||||
return exit_status;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int port;
|
||||
DaemonWrapper *wrapper;
|
||||
}
|
||||
DaemonConfig;
|
||||
|
||||
static int init_http_service(void *data)
|
||||
{
|
||||
DaemonConfig *config = (DaemonConfig*)data;
|
||||
config->wrapper = create_daemon_wrapper(config->port);
|
||||
|
||||
return (config->wrapper != NULL);
|
||||
}
|
||||
|
||||
static int run_http_service(void *data)
|
||||
{
|
||||
return run_main_loop();
|
||||
}
|
||||
|
||||
DaemonStatus run_daemon(int port, const char *pid_file)
|
||||
{
|
||||
DaemonConfig config;
|
||||
config.port = port;
|
||||
|
||||
DaemonStatus exit_status = daemonize(pid_file, &config, init_http_service, run_http_service);
|
||||
if(exit_status != STATUS_INIT_SUCCESS)
|
||||
print_daemon_status(exit_status, stderr);
|
||||
return exit_status;
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
#ifndef __SERVICE_H
|
||||
#define __SERVICE_H
|
||||
#include "daemonize.h"
|
||||
|
||||
int run_foreground_process(int port);
|
||||
|
||||
DaemonStatus run_daemon(int port, const char *pid_file);
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue