diff --git a/lib/default.nix b/lib/default.nix
index 626a751cb10a1..d699817b7da93 100644
--- a/lib/default.nix
+++ b/lib/default.nix
@@ -34,6 +34,9 @@ let
options = callLibs ./options.nix;
types = callLibs ./types.nix;
+ # NixOS
+ nixos = callLibs ../nixos/nixos-core.nix;
+
# constants
licenses = callLibs ./licenses.nix;
systems = callLibs ./systems;
diff --git a/nixos/modules/config/binsh.nix b/nixos/modules/config/binsh.nix
new file mode 100644
index 0000000000000..5fac1295cd1e4
--- /dev/null
+++ b/nixos/modules/config/binsh.nix
@@ -0,0 +1,40 @@
+{ config, lib, utils, pkgs, ... }:
+
+with lib;
+
+let
+
+ cfg = config.environment;
+in
+{
+ options = {
+ environment.binsh = mkOption {
+ default = "${config.system.build.binsh}/bin/sh";
+ defaultText = literalExpression ''"''${config.system.build.binsh}/bin/sh"'';
+ example = literalExpression ''"''${pkgs.dash}/bin/dash"'';
+ type = types.path;
+ visible = false;
+ description = ''
+ The shell executable that is linked system-wide to
+ /bin/sh. Please note that NixOS assumes all
+ over the place that shell to be Bash, so override the default
+ setting only if you know exactly what you're doing.
+ '';
+ };
+ };
+
+
+ config = {
+ system.build.binsh = pkgs.bashInteractive;
+
+ system.activationScripts.binsh = stringAfter [ "stdio" ]
+ ''
+ # Create the required /bin/sh symlink; otherwise lots of things
+ # (notably the system() function) won't work.
+ mkdir -m 0755 -p /bin
+ ln -sfn "${cfg.binsh}" /bin/.sh.tmp
+ mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
+ '';
+
+ };
+}
diff --git a/nixos/modules/config/environment-variables.nix b/nixos/modules/config/environment-variables.nix
new file mode 100644
index 0000000000000..a708dd7a0386a
--- /dev/null
+++ b/nixos/modules/config/environment-variables.nix
@@ -0,0 +1,27 @@
+{ config, lib, ... }:
+let
+ inherit (lib)
+ mkOption
+ types
+ isList
+ mapAttrs
+ concatStringsSep
+ ;
+in
+{
+ options = {
+ environment.variables = mkOption {
+ default = { };
+ example = { EDITOR = "nvim"; VISUAL = "nvim"; };
+ description = ''
+ A set of environment variables used in the global environment.
+ These variables will be set on shell initialisation (e.g. in /etc/profile).
+ The value of each variable can be either a string or a list of
+ strings. The latter is concatenated, interspersed with colon
+ characters.
+ '';
+ type = with types; attrsOf (either str (listOf str));
+ apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
+ };
+ };
+}
diff --git a/nixos/modules/config/shells-environment.nix b/nixos/modules/config/shells-environment.nix
index ae3f618e273c3..8b18d9e40b2ef 100644
--- a/nixos/modules/config/shells-environment.nix
+++ b/nixos/modules/config/shells-environment.nix
@@ -30,21 +30,9 @@ in
{
- options = {
+ imports = [ ./environment-variables.nix ];
- environment.variables = mkOption {
- default = {};
- example = { EDITOR = "nvim"; VISUAL = "nvim"; };
- description = ''
- A set of environment variables used in the global environment.
- These variables will be set on shell initialisation (e.g. in /etc/profile).
- The value of each variable can be either a string or a list of
- strings. The latter is concatenated, interspersed with colon
- characters.
- '';
- type = with types; attrsOf (either str (listOf str));
- apply = mapAttrs (n: v: if isList v then concatStringsSep ":" v else v);
- };
+ options = {
environment.profiles = mkOption {
default = [];
@@ -134,20 +122,6 @@ in
type = types.bool;
};
- environment.binsh = mkOption {
- default = "${config.system.build.binsh}/bin/sh";
- defaultText = literalExpression ''"''${config.system.build.binsh}/bin/sh"'';
- example = literalExpression ''"''${pkgs.dash}/bin/dash"'';
- type = types.path;
- visible = false;
- description = ''
- The shell executable that is linked system-wide to
- /bin/sh. Please note that NixOS assumes all
- over the place that shell to be Bash, so override the default
- setting only if you know exactly what you're doing.
- '';
- };
-
environment.shells = mkOption {
default = [];
example = literalExpression "[ pkgs.bashInteractive pkgs.zsh ]";
@@ -163,8 +137,6 @@ in
config = {
- system.build.binsh = pkgs.bashInteractive;
-
# Set session variables in the shell as well. This is usually
# unnecessary, but it allows changes to session variables to take
# effect without restarting the session (e.g. by opening a new
@@ -210,15 +182,6 @@ in
''}
'';
- system.activationScripts.binsh = stringAfter [ "stdio" ]
- ''
- # Create the required /bin/sh symlink; otherwise lots of things
- # (notably the system() function) won't work.
- mkdir -m 0755 -p /bin
- ln -sfn "${cfg.binsh}" /bin/.sh.tmp
- mv /bin/.sh.tmp /bin/sh # atomically replace /bin/sh
- '';
-
};
}
diff --git a/nixos/modules/config/system-path-core.nix b/nixos/modules/config/system-path-core.nix
new file mode 100644
index 0000000000000..9fe1c72d32db7
--- /dev/null
+++ b/nixos/modules/config/system-path-core.nix
@@ -0,0 +1,87 @@
+{ config, lib, pkgs, ... }:
+let
+ inherit (lib)
+ mkOption
+ types
+ literalExpression
+ ;
+in
+{
+ options = {
+
+ environment = {
+
+ systemPackages = mkOption {
+ type = types.listOf types.package;
+ default = [ ];
+ example = literalExpression "[ pkgs.firefox pkgs.htop ]";
+ description = ''
+ The set of packages that appear in
+ /run/current-system/sw. These packages are
+ automatically available to all users, and are
+ automatically updated every time you rebuild the system
+ configuration. (The latter is the main difference with
+ installing them in the default profile,
+ /nix/var/nix/profiles/default.
+ '';
+ };
+
+ pathsToLink = mkOption {
+ type = types.listOf types.str;
+ # Note: We need `/lib' to be among `pathsToLink' for NSS modules
+ # to work.
+ default = [ ];
+ example = [ "/" ];
+ description = "List of directories to be symlinked in /run/current-system/sw.";
+ };
+
+ extraOutputsToInstall = mkOption {
+ type = types.listOf types.str;
+ default = [ ];
+ example = [ "doc" "info" "devdoc" ];
+ description = "List of additional package outputs to be symlinked into /run/current-system/sw.";
+ };
+
+ extraSetup = mkOption {
+ type = types.lines;
+ default = "";
+ description = "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out.";
+ };
+
+ };
+
+ system = {
+
+ path = mkOption {
+ internal = true;
+ description = ''
+ The packages you want in the boot environment.
+ '';
+ };
+
+ };
+
+ };
+ config = {
+ environment.pathsToLink = [
+ "/bin"
+ ];
+
+ system.path = pkgs.buildEnv {
+ name = "system-path";
+ paths = config.environment.systemPackages;
+ inherit (config.environment) pathsToLink extraOutputsToInstall;
+ ignoreCollisions = true;
+ # !!! Hacky, should modularise.
+ # outputs TODO: note that the tools will often not be linked by default
+ postBuild =
+ ''
+ # Remove wrapped binaries, they shouldn't be accessible via PATH.
+ find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete
+
+ ${config.environment.extraSetup}
+ '';
+ };
+
+ };
+}
diff --git a/nixos/modules/config/system-path.nix b/nixos/modules/config/system-path.nix
index 6ff4ec2921cf8..0b62a442f0b38 100644
--- a/nixos/modules/config/system-path.nix
+++ b/nixos/modules/config/system-path.nix
@@ -51,25 +51,12 @@ let
in
{
+ imports = [ ./system-path-core.nix ];
+
options = {
environment = {
- systemPackages = mkOption {
- type = types.listOf types.package;
- default = [];
- example = literalExpression "[ pkgs.firefox pkgs.thunderbird ]";
- description = ''
- The set of packages that appear in
- /run/current-system/sw. These packages are
- automatically available to all users, and are
- automatically updated every time you rebuild the system
- configuration. (The latter is the main difference with
- installing them in the default profile,
- /nix/var/nix/profiles/default.
- '';
- };
-
defaultPackages = mkOption {
type = types.listOf types.package;
default = defaultPackages;
@@ -93,39 +80,6 @@ in
'';
};
- pathsToLink = mkOption {
- type = types.listOf types.str;
- # Note: We need `/lib' to be among `pathsToLink' for NSS modules
- # to work.
- default = [];
- example = ["/"];
- description = "List of directories to be symlinked in /run/current-system/sw.";
- };
-
- extraOutputsToInstall = mkOption {
- type = types.listOf types.str;
- default = [ ];
- example = [ "doc" "info" "devdoc" ];
- description = "List of additional package outputs to be symlinked into /run/current-system/sw.";
- };
-
- extraSetup = mkOption {
- type = types.lines;
- default = "";
- description = "Shell fragments to be run after the system environment has been created. This should only be used for things that need to modify the internals of the environment, e.g. generating MIME caches. The environment being built can be accessed at $out.";
- };
-
- };
-
- system = {
-
- path = mkOption {
- internal = true;
- description = ''
- The packages you want in the boot environment.
- '';
- };
-
};
};
@@ -135,8 +89,7 @@ in
environment.systemPackages = requiredPackages ++ config.environment.defaultPackages;
environment.pathsToLink =
- [ "/bin"
- "/etc/xdg"
+ [ "/etc/xdg"
"/etc/gtk-2.0"
"/etc/gtk-3.0"
"/lib" # FIXME: remove and update debug-info.nix
@@ -155,25 +108,11 @@ in
"/share/thumbnailers"
];
- system.path = pkgs.buildEnv {
- name = "system-path";
- paths = config.environment.systemPackages;
- inherit (config.environment) pathsToLink extraOutputsToInstall;
- ignoreCollisions = true;
- # !!! Hacky, should modularise.
- # outputs TODO: note that the tools will often not be linked by default
- postBuild =
- ''
- # Remove wrapped binaries, they shouldn't be accessible via PATH.
- find $out/bin -maxdepth 1 -name ".*-wrapped" -type l -delete
-
- if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
- $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
- fi
-
- ${config.environment.extraSetup}
- '';
- };
+ environment.extraSetup = ''
+ if [ -x $out/bin/glib-compile-schemas -a -w $out/share/glib-2.0/schemas ]; then
+ $out/bin/glib-compile-schemas $out/share/glib-2.0/schemas
+ fi
+ '';
};
}
diff --git a/nixos/modules/config/update-users-groups.pl b/nixos/modules/config/update-users-groups.pl
index 232f886789d38..cadae90437450 100644
--- a/nixos/modules/config/update-users-groups.pl
+++ b/nixos/modules/config/update-users-groups.pl
@@ -306,6 +306,7 @@ sub parseUser {
my $uid = getpwnam "root";
my $gid = getgrnam "shadow";
my $path = "/etc/shadow";
+ $gid = 0 unless defined $gid;
(chown($uid, $gid, $path) || die "Failed to change ownership of $path: $!") unless $is_dry;
}
diff --git a/nixos/modules/config/user-profiles.nix b/nixos/modules/config/user-profiles.nix
new file mode 100644
index 0000000000000..db86debf8cd74
--- /dev/null
+++ b/nixos/modules/config/user-profiles.nix
@@ -0,0 +1,25 @@
+{ config, lib, pkgs, ... }:
+let
+ inherit (lib)
+ mapAttrs'
+ filterAttrs
+ ;
+in
+{
+ config = {
+ environment.etc = mapAttrs' (_: { packages, name, ... }: {
+ name = "profiles/per-user/${name}";
+ value.source = pkgs.buildEnv {
+ name = "user-environment";
+ paths = packages;
+ inherit (config.environment) pathsToLink extraOutputsToInstall;
+ inherit (config.system.path) ignoreCollisions postBuild;
+ };
+ }) (filterAttrs (_: u: u.packages != []) config.users.users);
+
+ environment.profiles = [
+ "$HOME/.nix-profile"
+ "/etc/profiles/per-user/$USER"
+ ];
+ };
+}
diff --git a/nixos/modules/config/users-groups.nix b/nixos/modules/config/users-groups.nix
index a34d281434185..aea0ad1e229aa 100644
--- a/nixos/modules/config/users-groups.nix
+++ b/nixos/modules/config/users-groups.nix
@@ -442,12 +442,30 @@ in {
(cfg: if cfg.security.initialRootPassword == "!"
then null
else cfg.security.initialRootPassword))
+ ../misc/ids.nix
+ ../misc/extra-arguments.nix
+ ../misc/assertions.nix
+ ../system/activation/activation-script.nix
+ ../config/system-path-core.nix
];
###### interface
options = {
+ users.defaultUserShell = lib.mkOption {
+ description = ''
+ This option defines the default shell assigned to user
+ accounts. This can be either a full system path or a shell package.
+
+ This must not be a store path, since the path is
+ used outside the store (in particular in /etc/passwd).
+ '';
+ example = literalExpression "pkgs.zsh";
+ type = types.either types.path types.shellPackage;
+ default = "/bin/false";
+ };
+
users.mutableUsers = mkOption {
type = types.bool;
default = true;
@@ -579,21 +597,6 @@ in {
# Install all the user shells
environment.systemPackages = systemShells;
- environment.etc = (mapAttrs' (_: { packages, name, ... }: {
- name = "profiles/per-user/${name}";
- value.source = pkgs.buildEnv {
- name = "user-environment";
- paths = packages;
- inherit (config.environment) pathsToLink extraOutputsToInstall;
- inherit (config.system.path) ignoreCollisions postBuild;
- };
- }) (filterAttrs (_: u: u.packages != []) cfg.users));
-
- environment.profiles = [
- "$HOME/.nix-profile"
- "/etc/profiles/per-user/$USER"
- ];
-
assertions = [
{ assertion = !cfg.enforceIdUniqueness || (uidsAreUnique && gidsAreUnique);
message = "UIDs and GIDs must be unique!";
diff --git a/nixos/modules/misc/nixpkgs.nix b/nixos/modules/misc/nixpkgs.nix
index 08bc4398555b7..9098ae53790af 100644
--- a/nixos/modules/misc/nixpkgs.nix
+++ b/nixos/modules/misc/nixpkgs.nix
@@ -64,6 +64,8 @@ let
in
{
+ imports = [ ./assertions.nix ];
+
options.nixpkgs = {
pkgs = mkOption {
diff --git a/nixos/modules/misc/nixpkgs/test.nix b/nixos/modules/misc/nixpkgs/test.nix
new file mode 100644
index 0000000000000..c2fe3657d4206
--- /dev/null
+++ b/nixos/modules/misc/nixpkgs/test.nix
@@ -0,0 +1,8 @@
+{ lib, stdenv }:
+lib.recurseIntoAttrs {
+ invokeNixpkgsSimple =
+ (lib.nixos.core ({ config, modules, ... }: {
+ imports = [ modules.invokeNixpkgs ];
+ nixpkgs.system = stdenv.hostPlatform.system;
+ }))._module.args.pkgs.hello;
+}
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index 8cb7c39005c52..50ab06af3193e 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -1,4 +1,5 @@
[
+ ./config/binsh.nix
./config/debug-info.nix
./config/fonts/fontconfig.nix
./config/fonts/fontdir.nix
@@ -35,6 +36,7 @@
./config/system-path.nix
./config/terminfo.nix
./config/unix-odbc-drivers.nix
+ ./config/user-profiles.nix
./config/users-groups.nix
./config/vte.nix
./config/zram.nix
@@ -342,6 +344,7 @@
./services/databases/opentsdb.nix
./services/databases/pgmanage.nix
./services/databases/postgresql.nix
+ ./services/databases/postgresql-systemd.nix
./services/databases/redis.nix
./services/databases/riak.nix
./services/databases/victoriametrics.nix
@@ -1111,6 +1114,9 @@
./services/x11/xfs.nix
./services/x11/xserver.nix
./system/activation/activation-script.nix
+ ./system/activation/bootable.nix
+ ./system/activation/specialisation.nix
+ ./system/activation/system-with-activation.nix
./system/activation/top-level.nix
./system/boot/binfmt.nix
./system/boot/emergency-mode.nix
@@ -1140,9 +1146,10 @@
./system/boot/stage-2.nix
./system/boot/systemd.nix
./system/boot/systemd-nspawn.nix
+ ./system/boot/systemd-user-activation.nix
./system/boot/timesyncd.nix
./system/boot/tmp.nix
- ./system/etc/etc.nix
+ ./system/etc/etc-activation.nix
./tasks/auto-upgrade.nix
./tasks/bcache.nix
./tasks/cpu-freq.nix
diff --git a/nixos/modules/programs/shadow.nix b/nixos/modules/programs/shadow.nix
index 963cd8853dbbb..2dff92d6b06a9 100644
--- a/nixos/modules/programs/shadow.nix
+++ b/nixos/modules/programs/shadow.nix
@@ -58,18 +58,6 @@ in
options = {
- users.defaultUserShell = lib.mkOption {
- description = ''
- This option defines the default shell assigned to user
- accounts. This can be either a full system path or a shell package.
-
- This must not be a store path, since the path is
- used outside the store (in particular in /etc/passwd).
- '';
- example = literalExpression "pkgs.zsh";
- type = types.either types.path types.shellPackage;
- };
-
};
diff --git a/nixos/modules/services/databases/postgresql-container.nix b/nixos/modules/services/databases/postgresql-container.nix
new file mode 100644
index 0000000000000..664ce2c5fddea
--- /dev/null
+++ b/nixos/modules/services/databases/postgresql-container.nix
@@ -0,0 +1,32 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+ cfg = config.services.postgresql;
+
+ cmd = pkgs.writeScriptBin "postgresql-cmd" ''
+ #!${pkgs.runtimeShell}
+ set -eux
+ ${cfg.preStartCommands}
+ (export MAINPID=1; ${cfg.postStartCommands}) &
+ exec postgres
+ '';
+in
+
+{
+ imports = [
+ ./postgresql.nix
+ ../../config/environment-variables.nix
+ ../../config/binsh.nix
+ ];
+ config = {
+ environment.variables.PGDATA = cfg.dataDir;
+ environment.systemPackages = [
+ cfg.postgresqlWithExtraPlugins
+ pkgs.busybox
+ cmd
+ ];
+ services.postgresql.enable = true;
+ services.postgresql.enableTCPIP = true;
+ };
+}
diff --git a/nixos/modules/services/databases/postgresql-systemd.nix b/nixos/modules/services/databases/postgresql-systemd.nix
new file mode 100644
index 0000000000000..5d0d675e9439e
--- /dev/null
+++ b/nixos/modules/services/databases/postgresql-systemd.nix
@@ -0,0 +1,56 @@
+{ config, lib, pkgs, ... }:
+
+with lib;
+let
+ cfg = config.services.postgresql;
+
+ groupAccessAvailable = versionAtLeast cfg.package.version "11.0";
+
+in
+{
+ config = mkIf cfg.enable {
+ systemd.services.postgresql =
+ { description = "PostgreSQL Server";
+
+ wantedBy = [ "multi-user.target" ];
+ after = [ "network.target" ];
+
+ environment.PGDATA = cfg.dataDir;
+
+ path = [ cfg.postgresqlWithExtraPlugins ];
+
+ preStart = cfg.preStartCommands;
+
+ # Wait for PostgreSQL to be ready to accept connections.
+ postStart = cfg.postStartCommands;
+
+ serviceConfig = mkMerge [
+ { ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
+ User = "postgres";
+ Group = "postgres";
+ RuntimeDirectory = "postgresql";
+ Type = if versionAtLeast cfg.package.version "9.6"
+ then "notify"
+ else "simple";
+
+ # Shut down Postgres using SIGINT ("Fast Shutdown mode"). See
+ # http://www.postgresql.org/docs/current/static/server-shutdown.html
+ KillSignal = "SIGINT";
+ KillMode = "mixed";
+
+ # Give Postgres a decent amount of time to clean up after
+ # receiving systemd's SIGINT.
+ TimeoutSec = 120;
+
+ ExecStart = "${cfg.postgresqlWithExtraPlugins}/bin/postgres";
+ }
+ (mkIf (cfg.dataDir == "/var/lib/postgresql/${cfg.package.psqlSchema}") {
+ StateDirectory = "postgresql postgresql/${cfg.package.psqlSchema}";
+ StateDirectoryMode = if groupAccessAvailable then "0750" else "0700";
+ })
+ ];
+
+ unitConfig.RequiresMountsFor = "${cfg.dataDir}";
+ };
+ };
+}
diff --git a/nixos/modules/services/databases/postgresql.nix b/nixos/modules/services/databases/postgresql.nix
index 2919022496a36..6b282dd6042d4 100644
--- a/nixos/modules/services/databases/postgresql.nix
+++ b/nixos/modules/services/databases/postgresql.nix
@@ -6,11 +6,6 @@ let
cfg = config.services.postgresql;
- postgresql =
- if cfg.extraPlugins == []
- then cfg.package
- else cfg.package.withPackages (_: cfg.extraPlugins);
-
toStr = value:
if true == value then "yes"
else if false == value then "no"
@@ -25,13 +20,12 @@ let
touch $out
'';
- groupAccessAvailable = versionAtLeast postgresql.version "11.0";
-
in
{
imports = [
(mkRemovedOptionModule [ "services" "postgresql" "extraConfig" ] "Use services.postgresql.settings instead.")
+ ../../misc/meta.nix
];
###### interface
@@ -42,6 +36,72 @@ in
enable = mkEnableOption "PostgreSQL Server";
+ preStartCommands = mkOption {
+ description = ''
+ The init-agnostic script to run before postgresql starts.
+ '';
+ internal = true;
+ default = ''
+ if ! test -e ${cfg.dataDir}/PG_VERSION; then
+ # Cleanup the data directory.
+ rm -f ${cfg.dataDir}/*.conf
+
+ # Initialise the database.
+ initdb -U ${cfg.superUser} ${concatStringsSep " " cfg.initdbArgs}
+
+ # See postStart!
+ touch "${cfg.dataDir}/.first_startup"
+ fi
+
+ ln -sfn "${configFile}/postgresql.conf" "${cfg.dataDir}/postgresql.conf"
+ ${optionalString (cfg.recoveryConfig != null) ''
+ ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \
+ "${cfg.dataDir}/recovery.conf"
+ ''}
+ '';
+ };
+
+ postStartCommands = mkOption {
+ description = ''
+ The init-agnostic script to run after postgresql starts.
+ '';
+ internal = true;
+ default = ''
+ PSQL="psql --port=${toString cfg.port}"
+
+ while ! $PSQL -d postgres -c "" 2> /dev/null; do
+ if ! kill -0 "$MAINPID"; then exit 1; fi
+ sleep 0.1
+ done
+
+ if test -e "${cfg.dataDir}/.first_startup"; then
+ ${optionalString (cfg.initialScript != null) ''
+ $PSQL -f "${cfg.initialScript}" -d postgres
+ ''}
+ rm -f "${cfg.dataDir}/.first_startup"
+ fi
+ '' + optionalString (cfg.ensureDatabases != []) ''
+ ${concatMapStrings (database: ''
+ $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"'
+ '') cfg.ensureDatabases}
+ '' + ''
+ ${concatMapStrings (user: ''
+ $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
+ ${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
+ $PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"'
+ '') user.ensurePermissions)}
+ '') cfg.ensureUsers}
+ '';
+ };
+
+ postgresqlWithExtraPlugins = mkOption {
+ internal = true;
+ default =
+ if cfg.extraPlugins == []
+ then cfg.package
+ else cfg.package.withPackages (_: cfg.extraPlugins);
+ };
+
package = mkOption {
type = types.package;
example = literalExpression "pkgs.postgresql_11";
@@ -321,101 +381,13 @@ in
users.groups.postgres.gid = config.ids.gids.postgres;
- environment.systemPackages = [ postgresql ];
+ environment.systemPackages = [ cfg.postgresqlWithExtraPlugins ];
environment.pathsToLink = [
"/share/postgresql"
];
- system.extraDependencies = lib.optional (cfg.checkConfig && pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) configFileCheck;
-
- systemd.services.postgresql =
- { description = "PostgreSQL Server";
-
- wantedBy = [ "multi-user.target" ];
- after = [ "network.target" ];
-
- environment.PGDATA = cfg.dataDir;
-
- path = [ postgresql ];
-
- preStart =
- ''
- if ! test -e ${cfg.dataDir}/PG_VERSION; then
- # Cleanup the data directory.
- rm -f ${cfg.dataDir}/*.conf
-
- # Initialise the database.
- initdb -U ${cfg.superUser} ${concatStringsSep " " cfg.initdbArgs}
-
- # See postStart!
- touch "${cfg.dataDir}/.first_startup"
- fi
-
- ln -sfn "${configFile}/postgresql.conf" "${cfg.dataDir}/postgresql.conf"
- ${optionalString (cfg.recoveryConfig != null) ''
- ln -sfn "${pkgs.writeText "recovery.conf" cfg.recoveryConfig}" \
- "${cfg.dataDir}/recovery.conf"
- ''}
- '';
-
- # Wait for PostgreSQL to be ready to accept connections.
- postStart =
- ''
- PSQL="psql --port=${toString cfg.port}"
-
- while ! $PSQL -d postgres -c "" 2> /dev/null; do
- if ! kill -0 "$MAINPID"; then exit 1; fi
- sleep 0.1
- done
-
- if test -e "${cfg.dataDir}/.first_startup"; then
- ${optionalString (cfg.initialScript != null) ''
- $PSQL -f "${cfg.initialScript}" -d postgres
- ''}
- rm -f "${cfg.dataDir}/.first_startup"
- fi
- '' + optionalString (cfg.ensureDatabases != []) ''
- ${concatMapStrings (database: ''
- $PSQL -tAc "SELECT 1 FROM pg_database WHERE datname = '${database}'" | grep -q 1 || $PSQL -tAc 'CREATE DATABASE "${database}"'
- '') cfg.ensureDatabases}
- '' + ''
- ${concatMapStrings (user: ''
- $PSQL -tAc "SELECT 1 FROM pg_roles WHERE rolname='${user.name}'" | grep -q 1 || $PSQL -tAc 'CREATE USER "${user.name}"'
- ${concatStringsSep "\n" (mapAttrsToList (database: permission: ''
- $PSQL -tAc 'GRANT ${permission} ON ${database} TO "${user.name}"'
- '') user.ensurePermissions)}
- '') cfg.ensureUsers}
- '';
-
- serviceConfig = mkMerge [
- { ExecReload = "${pkgs.coreutils}/bin/kill -HUP $MAINPID";
- User = "postgres";
- Group = "postgres";
- RuntimeDirectory = "postgresql";
- Type = if versionAtLeast cfg.package.version "9.6"
- then "notify"
- else "simple";
-
- # Shut down Postgres using SIGINT ("Fast Shutdown mode"). See
- # http://www.postgresql.org/docs/current/static/server-shutdown.html
- KillSignal = "SIGINT";
- KillMode = "mixed";
-
- # Give Postgres a decent amount of time to clean up after
- # receiving systemd's SIGINT.
- TimeoutSec = 120;
-
- ExecStart = "${postgresql}/bin/postgres";
- }
- (mkIf (cfg.dataDir == "/var/lib/postgresql/${cfg.package.psqlSchema}") {
- StateDirectory = "postgresql postgresql/${cfg.package.psqlSchema}";
- StateDirectoryMode = if groupAccessAvailable then "0750" else "0700";
- })
- ];
-
- unitConfig.RequiresMountsFor = "${cfg.dataDir}";
- };
+ system.checks = lib.optional (cfg.checkConfig && pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) configFileCheck;
};
diff --git a/nixos/modules/system/activation/activation-script.nix b/nixos/modules/system/activation/activation-script.nix
index 4a32387db8da5..d35df203b068e 100644
--- a/nixos/modules/system/activation/activation-script.nix
+++ b/nixos/modules/system/activation/activation-script.nix
@@ -103,6 +103,8 @@ in
{
+ imports = [ ./top-level.nix ];
+
###### interface
options = {
@@ -137,6 +139,23 @@ in
};
};
+ system.activation.externalActivationScript = mkOption {
+ description = ''
+ Activation script for systems that are not managed by users of the system.
+
+ In this scenario, the activation script does not need to be part of the
+ "top-level" system store path.
+
+ In the case of an image, this allows activation-only dependencies to
+ be omitted from the runtime dependencies.
+
+ In case of a mutable system, it may allow a few dependencies to be
+ to be garbage collected after activation.
+ '';
+ type = types.package;
+ readOnly = true;
+ };
+
system.dryActivationScript = mkOption {
description = "The shell script that is to be run when dry-activating a system.";
readOnly = true;
@@ -238,33 +257,14 @@ in
rmdir --ignore-fail-on-non-empty /usr/bin /usr
'';
- system.activationScripts.specialfs =
- ''
- specialMount() {
- local device="$1"
- local mountPoint="$2"
- local options="$3"
- local fsType="$4"
-
- if mountpoint -q "$mountPoint"; then
- local options="remount,$options"
- else
- mkdir -m 0755 -p "$mountPoint"
- fi
- mount -t "$fsType" -o "$options" "$device" "$mountPoint"
- }
- source ${config.system.build.earlyMountScript}
- '';
+ system.activation.externalActivationScript =
+ pkgs.writeScript "activate" (
+ lib.strings.replaceStrings
+ ["@out@"]
+ ["${config.system.build.toplevel}"]
+ config.system.activationScripts.script
+ );
- systemd.user = {
- services.nixos-activation = {
- description = "Run user-specific NixOS activation";
- script = config.system.userActivationScripts.script;
- unitConfig.ConditionUser = "!@system";
- serviceConfig.Type = "oneshot";
- wantedBy = [ "default.target" ];
- };
- };
};
}
diff --git a/nixos/modules/system/activation/bootable.nix b/nixos/modules/system/activation/bootable.nix
new file mode 100644
index 0000000000000..20bcca824e975
--- /dev/null
+++ b/nixos/modules/system/activation/bootable.nix
@@ -0,0 +1,79 @@
+{ config, lib, pkgs, ... }:
+let
+ inherit (lib)
+ mkIf
+ mkOption
+ optionalString
+ types
+ ;
+
+ kernelPath = "${config.boot.kernelPackages.kernel}/" +
+ "${config.system.boot.loader.kernelFile}";
+ initrdPath = "${config.system.build.initialRamdisk}/" +
+ "${config.system.boot.loader.initrdFile}";
+in
+{
+ options = {
+ system.boot.loader.id = mkOption {
+ internal = true;
+ default = "";
+ description = ''
+ Id string of the used bootloader.
+ '';
+ };
+
+ system.boot.loader.kernelFile = mkOption {
+ internal = true;
+ default = pkgs.stdenv.hostPlatform.linux-kernel.target;
+ defaultText = lib.literalExpression "pkgs.stdenv.hostPlatform.linux-kernel.target";
+ type = types.str;
+ description = ''
+ Name of the kernel file to be passed to the bootloader.
+ '';
+ };
+
+ system.boot.loader.initrdFile = mkOption {
+ internal = true;
+ default = "initrd";
+ type = types.str;
+ description = ''
+ Name of the initrd file to be passed to the bootloader.
+ '';
+ };
+ };
+
+ config = {
+ # Containers don't have their own kernel or initrd. They boot
+ # directly into stage 2.
+ system.systemBuilderCommands = mkIf (!config.boot.isContainer) ''
+ if [ ! -f ${kernelPath} ]; then
+ echo "The bootloader cannot find the proper kernel image."
+ echo "(Expecting ${kernelPath})"
+ false
+ fi
+
+ ln -s ${kernelPath} $out/kernel
+ ln -s ${config.system.modulesTree} $out/kernel-modules
+ ${optionalString (config.hardware.deviceTree.package != null) ''
+ ln -s ${config.hardware.deviceTree.package} $out/dtbs
+ ''}
+
+ echo -n "$kernelParams" > $out/kernel-params
+
+ ln -s ${initrdPath} $out/initrd
+
+ ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
+
+ ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
+
+ echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
+ '';
+ system.systemBuilderAttrs = {
+ kernelParams = config.boot.kernelParams;
+ installBootLoader =
+ config.system.build.installBootLoader
+ or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
+
+ };
+ };
+}
diff --git a/nixos/modules/system/activation/specialisation.nix b/nixos/modules/system/activation/specialisation.nix
new file mode 100644
index 0000000000000..ae732dfc607c1
--- /dev/null
+++ b/nixos/modules/system/activation/specialisation.nix
@@ -0,0 +1,81 @@
+{ config, lib, pkgs, extendModules, noUserModules, ... }:
+
+let
+ inherit (lib)
+ concatStringsSep
+ mapAttrs
+ mapAttrsToList
+ mkOption
+ types
+ ;
+
+ # This attribute is responsible for creating boot entries for
+ # child configuration. They are only (directly) accessible
+ # when the parent configuration is boot default. For example,
+ # you can provide an easy way to boot the same configuration
+ # as you use, but with another kernel
+ # !!! fix this
+ children =
+ mapAttrs
+ (childName: childConfig: childConfig.configuration.system.build.toplevel)
+ config.specialisation;
+
+in
+{
+ config = {
+ system.systemBuilderCommands = lib.mkIf (config.specialisation != { }) ''
+ mkdir $out/specialisation
+ ${concatStringsSep "\n"
+ (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)}
+ '';
+ };
+
+ options = {
+
+ specialisation = mkOption {
+ default = { };
+ example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
+ description = ''
+ Additional configurations to build. If
+ inheritParentConfig is true, the system
+ will be based on the overall system configuration.
+
+ To switch to a specialised configuration
+ (e.g. fewJobsManyCores) at runtime, run:
+
+
+ # sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test
+
+ '';
+ type = types.attrsOf (types.submodule (
+ local@{ ... }:
+ let
+ extend =
+ if local.config.inheritParentConfig
+ then extendModules
+ else noUserModules.extendModules;
+ in
+ {
+ options.inheritParentConfig = mkOption {
+ type = types.bool;
+ default = true;
+ description = "Include the entire system's configuration. Set to false to make a completely differently configured system.";
+ };
+
+ options.configuration = mkOption {
+ default = { };
+ description = ''
+ Arbitrary NixOS configuration.
+
+ Anything you can add to a normal NixOS configuration, you can add
+ here, including imports and config values, although nested
+ specialisations will be ignored.
+ '';
+ visible = "shallow";
+ inherit (extend { modules = [ ./no-clone.nix ]; }) type;
+ };
+ }
+ ));
+ };
+ };
+}
diff --git a/nixos/modules/system/activation/system-with-activation.nix b/nixos/modules/system/activation/system-with-activation.nix
new file mode 100644
index 0000000000000..9d8627ccd7d7e
--- /dev/null
+++ b/nixos/modules/system/activation/system-with-activation.nix
@@ -0,0 +1,32 @@
+{ config, lib, pkgs, ... }:
+{
+ config = {
+ system.systemBuilderAttrs = {
+ activationScript = config.system.activationScripts.script;
+ dryActivationScript = config.system.dryActivationScript;
+ };
+
+ system.systemBuilderCommands = ''
+ echo "$activationScript" > $out/activate
+ echo "$dryActivationScript" > $out/dry-activate
+ substituteInPlace $out/activate --subst-var out
+ substituteInPlace $out/dry-activate --subst-var out
+ chmod u+x $out/activate $out/dry-activate
+ unset activationScript dryActivationScript
+ ${pkgs.stdenv.shell} -n $out/activate
+ ${pkgs.stdenv.shell} -n $out/dry-activate
+
+ mkdir -p $out/bin
+ export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
+ substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
+ chmod +x $out/bin/switch-to-configuration
+ ${lib.optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
+ if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
+ echo "switch-to-configuration syntax is not valid:"
+ echo "$output"
+ exit 1
+ fi
+ ''}
+ '';
+ };
+}
diff --git a/nixos/modules/system/activation/top-level.nix b/nixos/modules/system/activation/top-level.nix
index 501998fa399e2..f8208bc23d3ed 100644
--- a/nixos/modules/system/activation/top-level.nix
+++ b/nixos/modules/system/activation/top-level.nix
@@ -3,91 +3,19 @@
with lib;
let
-
-
- # This attribute is responsible for creating boot entries for
- # child configuration. They are only (directly) accessible
- # when the parent configuration is boot default. For example,
- # you can provide an easy way to boot the same configuration
- # as you use, but with another kernel
- # !!! fix this
- children =
- mapAttrs
- (childName: childConfig: childConfig.configuration.system.build.toplevel)
- config.specialisation;
-
systemBuilder =
- let
- kernelPath = "${config.boot.kernelPackages.kernel}/" +
- "${config.system.boot.loader.kernelFile}";
- initrdPath = "${config.system.build.initialRamdisk}/" +
- "${config.system.boot.loader.initrdFile}";
- in ''
+ ''
mkdir $out
- # Containers don't have their own kernel or initrd. They boot
- # directly into stage 2.
- ${optionalString (!config.boot.isContainer) ''
- if [ ! -f ${kernelPath} ]; then
- echo "The bootloader cannot find the proper kernel image."
- echo "(Expecting ${kernelPath})"
- false
- fi
-
- ln -s ${kernelPath} $out/kernel
- ln -s ${config.system.modulesTree} $out/kernel-modules
- ${optionalString (config.hardware.deviceTree.package != null) ''
- ln -s ${config.hardware.deviceTree.package} $out/dtbs
- ''}
-
- echo -n "$kernelParams" > $out/kernel-params
-
- ln -s ${initrdPath} $out/initrd
-
- ln -s ${config.system.build.initialRamdiskSecretAppender}/bin/append-initrd-secrets $out
-
- ln -s ${config.hardware.firmware}/lib/firmware $out/firmware
- ''}
-
- echo "$activationScript" > $out/activate
- echo "$dryActivationScript" > $out/dry-activate
- substituteInPlace $out/activate --subst-var out
- substituteInPlace $out/dry-activate --subst-var out
- chmod u+x $out/activate $out/dry-activate
- unset activationScript dryActivationScript
- ${pkgs.stdenv.shell} -n $out/activate
- ${pkgs.stdenv.shell} -n $out/dry-activate
-
- cp ${config.system.build.bootStage2} $out/init
- substituteInPlace $out/init --subst-var-by systemConfig $out
-
ln -s ${config.system.build.etc}/etc $out/etc
ln -s ${config.system.path} $out/sw
- ln -s "$systemd" $out/systemd
- echo -n "$configurationName" > $out/configuration-name
- echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
echo -n "$nixosLabel" > $out/nixos-version
- echo -n "${config.boot.kernelPackages.stdenv.hostPlatform.system}" > $out/system
-
- mkdir $out/specialisation
- ${concatStringsSep "\n"
- (mapAttrsToList (name: path: "ln -s ${path} $out/specialisation/${name}") children)}
-
- mkdir $out/bin
- export localeArchive="${config.i18n.glibcLocales}/lib/locale/locale-archive"
- substituteAll ${./switch-to-configuration.pl} $out/bin/switch-to-configuration
- chmod +x $out/bin/switch-to-configuration
- ${optionalString (pkgs.stdenv.hostPlatform == pkgs.stdenv.buildPlatform) ''
- if ! output=$($perl/bin/perl -c $out/bin/switch-to-configuration 2>&1); then
- echo "switch-to-configuration syntax is not valid:"
- echo "$output"
- exit 1
- fi
- ''}
echo -n "${toString config.system.extraDependencies}" > $out/extra-dependencies
+ ${config.system.systemBuilderCommands}
+
${config.system.extraSystemBuilderCmds}
'';
@@ -96,31 +24,22 @@ let
# kernel, systemd units, init scripts, etc.) as well as a script
# `switch-to-configuration' that activates the configuration and
# makes it bootable.
- baseSystem = pkgs.stdenvNoCC.mkDerivation {
+ baseSystem = pkgs.stdenvNoCC.mkDerivation ({
name = "nixos-system-${config.system.name}-${config.system.nixos.label}";
preferLocalBuild = true;
allowSubstitutes = false;
buildCommand = systemBuilder;
inherit (pkgs) coreutils;
- systemd = config.systemd.package;
shell = "${pkgs.bash}/bin/sh";
su = "${pkgs.shadow.su}/bin/su";
utillinux = pkgs.util-linux;
- kernelParams = config.boot.kernelParams;
- installBootLoader =
- config.system.build.installBootLoader
- or "echo 'Warning: do not know how to make this configuration bootable; please enable a boot loader.' 1>&2; true";
- activationScript = config.system.activationScripts.script;
- dryActivationScript = config.system.dryActivationScript;
nixosLabel = config.system.nixos.label;
- configurationName = config.boot.loader.grub.configurationName;
-
# Needed by switch-to-configuration.
perl = pkgs.perl.withPackages (p: with p; [ FileSlurp NetDBus XMLParser XMLTwig ]);
- };
+ } // config.system.systemBuilderAttrs);
# Handle assertions and warnings
@@ -141,98 +60,42 @@ in
imports = [
(mkRemovedOptionModule [ "nesting" "clone" ] "Use `specialisation.«name» = { inheritParentConfig = true; configuration = { ... }; }` instead.")
(mkRemovedOptionModule [ "nesting" "children" ] "Use `specialisation.«name».configuration = { ... }` instead.")
+ ../build.nix
+ ../../misc/assertions.nix
+ ../../misc/version.nix
+ ../../misc/label.nix
+ ../../config/system-path-core.nix
];
options = {
- system.build = mkOption {
- internal = true;
- default = {};
- type = types.attrs;
- description = ''
- Attribute set of derivations used to setup the system.
- '';
- };
-
- specialisation = mkOption {
- default = {};
- example = lib.literalExpression "{ fewJobsManyCores.configuration = { nix.buildCores = 0; nix.maxJobs = 1; }; }";
+ system.copySystemConfiguration = mkOption {
+ type = types.bool;
+ default = false;
description = ''
- Additional configurations to build. If
- inheritParentConfig is true, the system
- will be based on the overall system configuration.
-
- To switch to a specialised configuration
- (e.g. fewJobsManyCores) at runtime, run:
-
-
- # sudo /run/current-system/specialisation/fewJobsManyCores/bin/switch-to-configuration test
-
+ If enabled, copies the NixOS configuration file
+ (usually /etc/nixos/configuration.nix)
+ and links it from the resulting system
+ (getting to /run/current-system/configuration.nix).
+ Note that only this single file is copied, even if it imports others.
'';
- type = types.attrsOf (types.submodule (
- local@{ ... }: let
- extend = if local.config.inheritParentConfig
- then extendModules
- else noUserModules.extendModules;
- in {
- options.inheritParentConfig = mkOption {
- type = types.bool;
- default = true;
- description = "Include the entire system's configuration. Set to false to make a completely differently configured system.";
- };
-
- options.configuration = mkOption {
- default = {};
- description = ''
- Arbitrary NixOS configuration.
-
- Anything you can add to a normal NixOS configuration, you can add
- here, including imports and config values, although nested
- specialisations will be ignored.
- '';
- visible = "shallow";
- inherit (extend { modules = [ ./no-clone.nix ]; }) type;
- };
- })
- );
};
- system.boot.loader.id = mkOption {
+ system.systemBuilderCommands = mkOption {
+ type = types.lines;
internal = true;
default = "";
description = ''
- Id string of the used bootloader.
- '';
- };
-
- system.boot.loader.kernelFile = mkOption {
- internal = true;
- default = pkgs.stdenv.hostPlatform.linux-kernel.target;
- defaultText = literalExpression "pkgs.stdenv.hostPlatform.linux-kernel.target";
- type = types.str;
- description = ''
- Name of the kernel file to be passed to the bootloader.
+ This code will be added to the builder creating the system store path.
'';
};
- system.boot.loader.initrdFile = mkOption {
+ system.systemBuilderAttrs = mkOption {
+ type = types.attrsOf types.unspecified;
internal = true;
- default = "initrd";
- type = types.str;
+ default = {};
description = ''
- Name of the initrd file to be passed to the bootloader.
- '';
- };
-
- system.copySystemConfiguration = mkOption {
- type = types.bool;
- default = false;
- description = ''
- If enabled, copies the NixOS configuration file
- (usually /etc/nixos/configuration.nix)
- and links it from the resulting system
- (getting to /run/current-system/configuration.nix).
- Note that only this single file is copied, even if it imports others.
+ Derivation attributes that will be passed to the top level system builder.
'';
};
@@ -250,8 +113,19 @@ in
default = [];
description = ''
A list of packages that should be included in the system
- closure but not otherwise made available to users. This is
- primarily used by the installation tests.
+ closure but not otherwise made available to users.
+ '';
+ };
+
+ system.checks = mkOption {
+ type = types.listOf types.package;
+ default = [];
+ description = ''
+ A list of packages that are added as dependencies of the activation
+ script build, for the purpose of validating the configuration.
+
+ Unlike system.extraDependencies, these paths do not
+ become part of the runtime closure of the system.
'';
};
@@ -284,10 +158,7 @@ in
system.name = mkOption {
type = types.str;
- default =
- if config.networking.hostName == ""
- then "unnamed"
- else config.networking.hostName;
+ # when using full NixOS or importing the network-interfaces.nix module.
defaultText = literalExpression ''
if config.networking.hostName == ""
then "unnamed"
@@ -313,8 +184,16 @@ in
"$out/configuration.nix"
'';
+ system.systemBuilderAttrs.passedTests = concatStringsSep " " config.system.checks;
+
system.build.toplevel = system;
+ # Traditionally, the option default contained the logic for taking this from
+ # the network.hostName option, which is expected to define it at
+ # mkOptionDefault priority. However, we'd also like to have a default when
+ # network.hostName is not imported.
+ system.name = mkOverride ((mkOptionDefault {}).priority + 100) "unnamed";
+
};
}
diff --git a/nixos/modules/system/boot/loader/grub/grub.nix b/nixos/modules/system/boot/loader/grub/grub.nix
index 8db271f871352..b8c450ec62e74 100644
--- a/nixos/modules/system/boot/loader/grub/grub.nix
+++ b/nixos/modules/system/boot/loader/grub/grub.nix
@@ -724,6 +724,11 @@ in
boot.loader.supportsInitrdSecrets = true;
+ system.systemBuilderAttrs.configurationName = cfg.configurationName;
+ system.systemBuilderCommands = ''
+ echo -n "$configurationName" > $out/configuration-name
+ '';
+
system.build.installBootLoader =
let
install-grub-pl = pkgs.substituteAll {
diff --git a/nixos/modules/system/boot/stage-2.nix b/nixos/modules/system/boot/stage-2.nix
index f6b6a8e4b0b44..eb89c0b864646 100644
--- a/nixos/modules/system/boot/stage-2.nix
+++ b/nixos/modules/system/boot/stage-2.nix
@@ -104,5 +104,15 @@ in
system.build.bootStage2 = bootStage2;
+ system.systemBuilderAttrs = {
+ systemd = config.systemd.package;
+ };
+ system.systemBuilderCommands = ''
+ cp ${config.system.build.bootStage2} $out/init
+ substituteInPlace $out/init --subst-var-by systemConfig $out
+ ln -s "$systemd" $out/systemd
+ echo -n "systemd ${toString config.systemd.package.interfaceVersion}" > $out/init-interface-version
+ '';
+
};
}
diff --git a/nixos/modules/system/boot/systemd-user-activation.nix b/nixos/modules/system/boot/systemd-user-activation.nix
new file mode 100644
index 0000000000000..a0e49f6fd4728
--- /dev/null
+++ b/nixos/modules/system/boot/systemd-user-activation.nix
@@ -0,0 +1,15 @@
+{ config, lib, pkgs, ... }:
+
+{
+ config = {
+ systemd.user = {
+ services.nixos-activation = {
+ description = "Run user-specific NixOS activation";
+ script = config.system.userActivationScripts.script;
+ unitConfig.ConditionUser = "!@system";
+ serviceConfig.Type = "oneshot";
+ wantedBy = [ "default.target" ];
+ };
+ };
+ };
+}
diff --git a/nixos/modules/system/build.nix b/nixos/modules/system/build.nix
new file mode 100644
index 0000000000000..a2a2e02f81a2e
--- /dev/null
+++ b/nixos/modules/system/build.nix
@@ -0,0 +1,16 @@
+{ lib, ... }:
+let
+ inherit (lib) mkOption types;
+in {
+ options = {
+ # TODO: replace by submodule with free-form type
+ system.build = mkOption {
+ internal = true;
+ default = {};
+ type = types.attrs;
+ description = ''
+ Attribute set of derivations used to set up the system.
+ '';
+ };
+ };
+}
diff --git a/nixos/modules/system/etc/etc-activation.nix b/nixos/modules/system/etc/etc-activation.nix
new file mode 100644
index 0000000000000..7801049501860
--- /dev/null
+++ b/nixos/modules/system/etc/etc-activation.nix
@@ -0,0 +1,12 @@
+{ config, lib, ... }:
+let
+ inherit (lib) stringAfter;
+in {
+
+ imports = [ ./etc.nix ];
+
+ config = {
+ system.activationScripts.etc =
+ stringAfter [ "users" "groups" ] config.system.build.etcActivationCommands;
+ };
+}
diff --git a/nixos/modules/system/etc/etc.nix b/nixos/modules/system/etc/etc.nix
index 6cc8c341e6dfa..ed552fecec53a 100644
--- a/nixos/modules/system/etc/etc.nix
+++ b/nixos/modules/system/etc/etc.nix
@@ -66,6 +66,8 @@ in
{
+ imports = [ ../build.nix ];
+
###### interface
options = {
@@ -188,14 +190,12 @@ in
config = {
system.build.etc = etc;
-
- system.activationScripts.etc = stringAfter [ "users" "groups" ]
+ system.build.etcActivationCommands =
''
# Set up the statically computed bits of /etc.
echo "setting up /etc..."
${pkgs.perl.withPackages (p: [ p.FileSlurp ])}/bin/perl ${./setup-etc.pl} ${etc}/etc
'';
-
};
}
diff --git a/nixos/modules/system/etc/test.nix b/nixos/modules/system/etc/test.nix
new file mode 100644
index 0000000000000..dd15bd8066eb7
--- /dev/null
+++ b/nixos/modules/system/etc/test.nix
@@ -0,0 +1,69 @@
+{ lib
+, coreutils
+, fakechroot
+, fakeroot
+, nixosModule
+, runCommand
+, util-linux
+, vmTools
+, writeText
+}:
+let
+ node = lib.nixos.core ({ config, modules, ... }: {
+ imports = [ nixosModule modules.etc ];
+ environment.etc."passwd" = {
+ text = passwdText;
+ };
+ environment.etc."hosts" = {
+ text = hostsText;
+ mode = "0751";
+ };
+ });
+ passwdText = ''
+ root:x:0:0:System administrator:/root:/run/current-system/sw/bin/bash
+ '';
+ hostsText = ''
+ 127.0.0.1 localhost
+ ::1 localhost
+ # testing...
+ '';
+in
+lib.recurseIntoAttrs {
+ test-etc-vm =
+ vmTools.runInLinuxVM (runCommand "test-etc-vm" { } ''
+ mkdir -p /etc
+ ${node.config.system.build.etcActivationCommands}
+ set -x
+ [[ -L /etc/passwd ]]
+ diff /etc/passwd ${writeText "expected-passwd" passwdText}
+ [[ 751 = $(stat --format %a /etc/hosts) ]]
+ diff /etc/hosts ${writeText "expected-hosts" hostsText}
+ set +x
+ touch $out
+ '');
+
+ # fakeroot is behaving weird
+ test-etc-fakeroot =
+ runCommand "test-etc"
+ {
+ nativeBuildInputs = [
+ fakeroot
+ fakechroot
+ # for chroot
+ coreutils
+ # fakechroot needs getopt, which is provided by util-linux
+ util-linux
+ ];
+ fakeRootCommands = ''
+ mkdir -p /etc
+ ${node.config.system.build.etcActivationCommands}
+ diff /etc/hosts ${writeText "expected-hosts" hostsText}
+ touch $out
+ '';
+ } ''
+ mkdir fake-root
+ export FAKECHROOT_EXCLUDE_PATH=/dev:/proc:/sys:${builtins.storeDir}:$out
+ fakechroot fakeroot chroot $PWD/fake-root bash -c 'source $stdenv/setup; eval "$fakeRootCommands"'
+ '';
+
+}
diff --git a/nixos/modules/tasks/filesystems.nix b/nixos/modules/tasks/filesystems.nix
index 225bcbe58e017..523b6a147cd0c 100644
--- a/nixos/modules/tasks/filesystems.nix
+++ b/nixos/modules/tasks/filesystems.nix
@@ -241,6 +241,24 @@ in
system.build.fileSystems = fileSystems;
system.build.earlyMountScript = makeSpecialMounts (toposort fsBefore (attrValues config.boot.specialFileSystems)).result;
+ system.activationScripts.specialfs =
+ ''
+ specialMount() {
+ local device="$1"
+ local mountPoint="$2"
+ local options="$3"
+ local fsType="$4"
+
+ if mountpoint -q "$mountPoint"; then
+ local options="remount,$options"
+ else
+ mkdir -m 0755 -p "$mountPoint"
+ fi
+ mount -t "$fsType" -o "$options" "$device" "$mountPoint"
+ }
+ source ${config.system.build.earlyMountScript}
+ '';
+
boot.supportedFilesystems = map (fs: fs.fsType) fileSystems;
# Add the mount helpers to the system path so that `mount' can find them.
diff --git a/nixos/modules/tasks/network-interfaces.nix b/nixos/modules/tasks/network-interfaces.nix
index 49901cda848df..9e8af022d420c 100644
--- a/nixos/modules/tasks/network-interfaces.nix
+++ b/nixos/modules/tasks/network-interfaces.nix
@@ -1222,6 +1222,12 @@ in
}
];
+ system.name = lib.mkOptionDefault (
+ if config.networking.hostName == ""
+ then "unnamed"
+ else config.networking.hostName
+ );
+
boot.kernelModules = [ ]
++ optional hasVirtuals "tun"
++ optional hasSits "sit"
diff --git a/nixos/nixos-core.nix b/nixos/nixos-core.nix
new file mode 100644
index 0000000000000..9266bc63bafbf
--- /dev/null
+++ b/nixos/nixos-core.nix
@@ -0,0 +1,41 @@
+{ lib }:
+
+let
+ inherit (lib.nixos) publicModules evalModules core;
+in {
+ publicModules = {
+ # Attributes that refer to modules that are unique to certain kinds of systems.
+ # For instance, some systems are bootable, some can rebuild themselves, whereas
+ # need only very minimal facilities.
+ # These form the public interface to the module graph.
+
+ invokeNixpkgs = ./modules/misc/nixpkgs.nix;
+ invokeNixpkgsImpure = { modules, ... }: {
+ imports = [ modules.invokeNixpkgs ];
+ nixpkgs.system = builtins.currentSystem;
+ };
+
+ etc = ./modules/system/etc/etc.nix;
+ etcActivation = ./modules/system/etc/etc-activation.nix;
+
+ users = ./modules/config/users-groups.nix;
+ };
+
+ evalModules = {
+ prefix ? [],
+ modules ? [],
+ specialArgs ? {},
+ }: lib.evalModules {
+ inherit prefix;
+ modules = modules;
+ specialArgs = {
+ modulesPath = builtins.toString ../modules;
+ modules = publicModules;
+ } // specialArgs;
+ };
+
+ core = module: evalModules {
+ modules = [ module ];
+ };
+
+}
diff --git a/nixos/tests/all-tests.nix b/nixos/tests/all-tests.nix
index 63be67892017b..b3611fc39cfd9 100644
--- a/nixos/tests/all-tests.nix
+++ b/nixos/tests/all-tests.nix
@@ -122,6 +122,7 @@ in
enlightenment = handleTest ./enlightenment.nix {};
env = handleTest ./env.nix {};
ergo = handleTest ./ergo.nix {};
+ etc = pkgs.callPackage ../modules/system/etc/test.nix { };
etcd = handleTestOn ["x86_64-linux"] ./etcd.nix {};
etcd-cluster = handleTestOn ["x86_64-linux"] ./etcd-cluster.nix {};
etebase-server = handleTest ./etebase-server.nix {};
@@ -322,6 +323,7 @@ in
nix-serve-ssh = handleTest ./nix-serve-ssh.nix {};
nixops = handleTest ./nixops/default.nix {};
nixos-generate-config = handleTest ./nixos-generate-config.nix {};
+ nixpkgs = pkgs.callPackage ../modules/misc/nixpkgs/test.nix { };
node-red = handleTest ./node-red.nix {};
nomad = handleTest ./nomad.nix {};
novacomd = handleTestOn ["x86_64-linux"] ./novacomd.nix {};
@@ -379,6 +381,7 @@ in
postgis = handleTest ./postgis.nix {};
postgresql = handleTest ./postgresql.nix {};
postgresql-wal-receiver = handleTest ./postgresql-wal-receiver.nix {};
+ postgresql-container = handleTest ./postgresql-container.nix {};
powerdns = handleTest ./powerdns.nix {};
power-profiles-daemon = handleTest ./power-profiles-daemon.nix {};
pppd = handleTest ./pppd.nix {};
diff --git a/nixos/tests/docker-tools.nix b/nixos/tests/docker-tools.nix
index 19ebed3ebd0bd..e793c2b9a5626 100644
--- a/nixos/tests/docker-tools.nix
+++ b/nixos/tests/docker-tools.nix
@@ -413,5 +413,11 @@ import ./make-test-python.nix ({ pkgs, ... }: {
"docker rmi layered-image-with-path",
)
+ with subtest("etc"):
+ docker.succeed("${examples.etc} | docker load")
+ docker.succeed("docker run --rm etc-img | grep -i hello")
+ docker.succeed("docker run --rm etc-img cat /etc/foo | grep 'foo: bar'")
+ docker.succeed("docker image rm etc-img:latest")
+
'';
})
diff --git a/nixos/tests/postgresql-container.nix b/nixos/tests/postgresql-container.nix
new file mode 100644
index 0000000000000..4d978a54149da
--- /dev/null
+++ b/nixos/tests/postgresql-container.nix
@@ -0,0 +1,79 @@
+{ system ? builtins.currentSystem,
+ config ? {},
+ pkgs ? import ../.. { inherit system config; }
+}:
+
+with import ../lib/testing-python.nix { inherit system pkgs; };
+with pkgs.lib;
+
+let
+ test-sql = pkgs.writeText "postgresql-test" ''
+ CREATE EXTENSION pgcrypto; -- just to check if lib loading works
+ CREATE TABLE sth (
+ id int
+ );
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ INSERT INTO sth (id) VALUES (1);
+ CREATE TABLE xmltest ( doc xml );
+ INSERT INTO xmltest (doc) VALUES ('ok'); -- check if libxml2 enabled
+ '';
+
+in makeTest {
+ name = "postgresql-container";
+ meta = with pkgs.lib.maintainers; {
+ maintainers = [ roberth ];
+ };
+
+ machine = {...}:
+ {
+ virtualisation.docker = {
+ enable = true;
+ };
+ virtualisation.memorySize = 4096;
+ virtualisation.diskSize = 4096;
+ environment.systemPackages = [
+ # add postgresql for its client; psql
+ pkgs.postgresql
+ ];
+ };
+
+ testScript = ''
+ def check_count(statement, lines):
+ return 'test $(psql --host=127.0.0.1 --username=postgres postgres -tAc "{}"|wc -l) -eq {}'.format(
+ statement, lines
+ )
+
+
+ machine.start()
+ machine.wait_for_unit("docker.socket")
+ machine.succeed("${pkgs.dockerTools.examples.postgresql} | docker load")
+ machine.succeed("docker run -d -p5432:5432 --name pg --restart=always --stop-signal SIGINT nixpkgs-postgresql")
+ machine.succeed("docker logs --tail 1000 -f pg >&2 &")
+
+ with subtest("Postgresql comes up"):
+ machine.wait_until_succeeds(
+ "cat ${test-sql} | psql --host=127.0.0.1 --username=postgres"
+ )
+
+ with subtest("Postgresql survives restart"):
+ machine.shutdown()
+ import time
+ time.sleep(2)
+ machine.start()
+ machine.wait_until_succeeds("""
+ echo 'SELECT 1;' | psql --host=127.0.0.1 --username=postgres
+ """)
+
+ machine.fail(check_count("SELECT * FROM sth;", 3))
+ machine.succeed(check_count("SELECT * FROM sth;", 5))
+ machine.fail(check_count("SELECT * FROM sth;", 4))
+ machine.succeed(check_count("SELECT xpath('/test/text()', doc) FROM xmltest;", 1))
+
+ machine.shutdown()
+ '';
+
+ }
+
diff --git a/pkgs/build-support/docker/default.nix b/pkgs/build-support/docker/default.nix
index 9a20df57777cd..543087c123304 100644
--- a/pkgs/build-support/docker/default.nix
+++ b/pkgs/build-support/docker/default.nix
@@ -871,7 +871,7 @@ rec {
${optionalString enableFakechroot ''fakechroot chroot $PWD/old_out ''}fakeroot bash -c '
source $stdenv/setup
${optionalString (!enableFakechroot) ''cd old_out''}
- eval "$fakeRootCommands"
+ (eval "$fakeRootCommands")
tar \
--sort name \
--numeric-owner --mtime "@$SOURCE_DATE_EPOCH" \
diff --git a/pkgs/build-support/docker/examples.nix b/pkgs/build-support/docker/examples.nix
index f2d4f809ae4e3..6c7926c3bc0b7 100644
--- a/pkgs/build-support/docker/examples.nix
+++ b/pkgs/build-support/docker/examples.nix
@@ -559,6 +559,46 @@ rec {
includeStorePaths = false;
};
+ etc =
+ let
+ inherit (pkgs) lib;
+ nixosCore = (lib.nixos.core ({ config, modules, ... }: {
+ imports = [
+ pkgs.nixosModule
+ modules.etcActivation
+ modules.users
+ ];
+ environment.etc."foo" = {
+ text = ''
+ foo: bar
+ '';
+ mode = "0456";
+ };
+ environment.systemPackages = [
+ pkgs.hello
+ pkgs.coreutils
+ ];
+ users.users.foo = { isSystemUser = true; group = "foo"; };
+ users.groups.foo = {};
+ users.users.nobody.shell = "/run/current-system/sw/bin/false";
+ users.users.root.shell = "/run/current-system/sw/bin/false";
+ users.users.foo.shell = "/run/current-system/sw/bin/false";
+ users.defaultUserShell = "/run/current-system/sw/bin/false";
+ }));
+ in pkgs.dockerTools.streamLayeredImage {
+ name = "etc-img";
+ tag = "latest";
+ enableFakechroot = true;
+ fakeRootCommands = ''
+ mkdir -p /etc /run /nix/var/nix/gcroots
+ ${nixosCore.config.system.activation.externalActivationScript}
+ chmod -R a-w /
+ '';
+ config.Cmd = [ "hello" ];
+ config.Env = [ "PATH=/run/current-system/sw/bin" ];
+ config.User = "foo";
+ } // { nixos = nixosCore; };
+
# Example export of the bash image
exportBash = pkgs.dockerTools.exportImage { fromImage = bash; };
diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix
index 9bcd0a7e2ed1c..0f6d60dc2b159 100644
--- a/pkgs/top-level/all-packages.nix
+++ b/pkgs/top-level/all-packages.nix
@@ -32626,6 +32626,20 @@ with pkgs;
in
c.config.system.build // c;
+ /*
+ A NixOS module that sets the `pkgs` module argument.
+ */
+ nixosModule = { lib, options, ... }: {
+ config =
+ if options?nixpkgs.pkgs then {
+ # legacy / nixpkgs.nix style
+ nixpkgs.pkgs = pkgs;
+ }
+ else {
+ # minimal
+ _module.args.pkgs = pkgs;
+ };
+ };
/*
* Run a NixOS VM network test using this evaluation of Nixpkgs.