From 4d15ad22a2c7b54fe377cddc69fbeca9a4c20fc1 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Thu, 31 Oct 2013 23:01:07 +0100 Subject: [PATCH] Manual: Expand the Development chapter --- nixos/doc/manual/configuration.xml | 2 +- nixos/doc/manual/development.xml | 884 +++++++++++++++--------- nixos/modules/installer/tools/tools.nix | 2 +- nixos/modules/misc/version.nix | 21 +- 4 files changed, 579 insertions(+), 330 deletions(-) diff --git a/nixos/doc/manual/configuration.xml b/nixos/doc/manual/configuration.xml index a8bda210dcf..9bca53ae904 100644 --- a/nixos/doc/manual/configuration.xml +++ b/nixos/doc/manual/configuration.xml @@ -1012,7 +1012,7 @@ manpage or the Nix manual. -
User management +
User management NixOS supports both declarative and imperative styles of user management. In the declarative style, users are specified in diff --git a/nixos/doc/manual/development.xml b/nixos/doc/manual/development.xml index d1b81b7497b..6bbccac6e5c 100644 --- a/nixos/doc/manual/development.xml +++ b/nixos/doc/manual/development.xml @@ -76,190 +76,132 @@ in nixos/ as packages.
-Extending NixOS +Writing NixOS modules -NixOS is based on a modular system for declarative configuration. - This system combines multiple modules to produce one - configuration. One of the module which compose your computer - configuration is /etc/nixos/configuration.nix. Other - modules are available under NixOS modules - directory +NixOS has a modular system for declarative configuration. This +system combines multiple modules to produce the +full system configuration. One of the modules that constitute the +configuration is /etc/nixos/configuration.nix. +Most of the others live in the nixos/modules +subdirectory of the Nixpkgs tree. -A module is a file which handles one specific part of the - configuration. This part of the configuration could correspond to - hardware, a service, network settings, or preferences. A module - configuration does not have to handle everything from scratch, it can base - its configuration on other configurations provided by other modules. Thus - a module can define options to setup its - configuration, and it can also declare options to be - fed by other modules. +Each NixOS module is a file that handles one logical aspect of +the configuration, such as a specific kind of hardware, a service, or +network settings. A module configuration does not have to handle +everything from scratch; it can use the functionality provided by +other modules for its implementation. Thus a module can +declare options that can be used by other +modules, and conversely can define options +provided by other modules in its own implementation. For example, the +module pam.nix +declares the option that allows +other modules (e.g. sshd.nix) +to define PAM services; and it defines the option + (declared by etc.nix) +to cause files to be created in +/etc/pam.d. - +In , we saw the following structure +of NixOS modules: -A module is a file which contains a Nix - expression. This expression should be either an expression which gets - evaluated into an attribute set or a function which returns an attribute - set. + +{ config, pkgs, ... }: -When the expression is a function, it should expect only one argument - which is an attribute set containing an attribute - named config and another attribute - named pkgs. The config attribute - contains the result of the merge of all modules. This attribute is - evaluated lazily, such as any Nix expression. For more details on how - options are merged, see the details in . - The pkgs attribute - contains nixpkgs attribute set of packages. This - attribute is necessary for declaring options. +{ option definitions +} + -Usual module content +This is actually an abbreviated form of module +that only defines options, but does not declare any. The structure of +full NixOS modules is shown in . + +Structure of NixOS modules { config, pkgs, ... }: { imports = - [ + [ paths of other modules ]; options = { - + option declarations }; config = { - + option definitions }; } - Illustrates - a module skeleton. +The meaning of each part is as follows. - This line makes the current Nix expression a function. This - line can be omitted if there is no reference to pkgs - and config inside the module. + This line makes the current Nix expression a function. The + variable pkgs contains Nixpkgs, while + config contains the full system configuration. + This line can be omitted if there is no reference to + pkgs and config inside the + module. - This list is used to enumerate path to other modules which are - declaring options used by the current module. In NixOS, default modules - are listed in the file modules/module-list.nix. - The default modules don't need to be added in the import list. + This list enumerates the paths to other NixOS modules that + should be included in the evaluation of the system configuration. + A default set of modules is defined in the file + modules/module-list.nix. These don't need to + be added in the import list. - This attribute set contains an attribute set of option - declaration. + The attribute options is a nested set of + option declarations (described below). - This attribute set contains an attribute set of option - definitions. If the module does not have any imported - modules or any option declarations, then this attribute set can be used - in place of its parent attribute set. This is a common case for simple - modules such - as /etc/nixos/configuration.nix. + The attribute config is a nested set of + option definitions (also described + below). - + shows a module that handles +the regular update of the “locate” database, an index of all files in +the file system. This module declares two options that can be defined +by other modules (typically the user’s +configuration.nix): + (whether the database should +be updated) and (when the +update should be done). It implements its functionality by defining +two options declared by other modules: + (the set of all systemd services) +and (the list of +commands to be executed periodically by cron). -A module defines a configuration which would be - interpreted by other modules. To define a configuration, a module needs - to provide option definitions. An option definition is a simple - attribute assignment. - -Option definitions are made in a declarative manner. Without - properties, options will always be defined with the same value. To - introduce more flexibility in the system, option definitions are guarded - by properties. - -Properties are means to introduce conditional values inside option - definitions. This conditional values can be distinguished in two - categories. The condition which are local to the current configuration - and conditions which are dependent on others configurations. Local - properties are mkIf - and mkAssert. Global properties - are mkOverride, mkDefault - and mkOrder. - -mkIf is used to remove the option definitions which - are below it if the condition is evaluated to - false. mkAssert expects the condition to be evaluated - to true otherwise it raises an error message. - -mkOverride is used to mask previous definitions if - the current value has a lower mask number. The mask value is 100 (default) - for any option definition which does not use this property. - Thus, mkDefault is just a short-cut with a higher mask - (1000) than the default mask value. This means that a module can set an - option definition as a preference, and still let another module defining - it with a different value without using any property. - -mkOrder is used to sort definitions based on the - rank number. The rank number will sort all options definitions before - giving the sorted list of option definition to the merge function defined - in the option declaration. A lower rank will move the definition to the - beginning and a higher rank will move the option toward the end. The - default rank is 100. - - - -A module may declare options which are used by - other module to change the configuration provided by the current module. - Changes to the option definitions are made with properties which are using - values extracted from the result of the merge of all modules - (the config argument). - -The config argument reproduce the same hierarchy of - all options declared in all modules. For each option, the result of the - option is available, it is either the default value or the merge of all - definitions of the option. - -Options are declared with the - function pkgs.lib.mkOption. This function expects an - attribute set which at least provides a description. A default value, an - example, a type, a merge function and a post-process function can be - added. - -Types are used to provide a merge strategy for options and to ensure - the type of each option definitions. They are defined - in pkgs.lib.types. - -The merge function expects a list of option definitions and merge - them to obtain one result of the same type. - -The post-process function (named apply) takes the - result of the merge or of the default value, and produce an output which - could have a different type than the type expected by the option. - - - -Locate Module Example +NixOS module for the “locate” service { config, pkgs, ... }: with pkgs.lib; -let - cfg = config.services.locate; - locatedb = "/var/cache/locatedb"; - logfile = "/var/log/updatedb"; - cmd =''root updatedb --localuser=nobody --output=${locatedb} > ${logfile}''; -in +let locatedb = "/var/cache/locatedb"; in { - imports = [ /path/to/nixpkgs/nixos/modules/services/scheduling/cron.nix ]; - options = { + services.locate = { + enable = mkOption { + type = types.bool; default = false; - example = true; - type = with types; bool; description = '' If enabled, NixOS will periodically update the database of files used by the locate command. @@ -267,35 +209,370 @@ in }; period = mkOption { - default = "15 02 * * *"; type = types.str; + default = "15 02 * * *"; description = '' This option defines (in the format used by cron) when the - locate database is updated. - The default is to update at 02:15 (at night) every day. + locate database is updated. The default is to update at + 02:15 at night every day. ''; }; + }; + }; - config = mkIf cfg.enable { - services.cron = { - enable = true; - systemCronJobs = "${cfg.period} root ${cmd}"; - }; + config = { + + systemd.services.update-locatedb = + { description = "Update Locate Database"; + path = [ pkgs.su ]; + script = + '' + mkdir -m 0755 -p $(dirname ${locatedb}) + exec updatedb --localuser=nobody --output=${locatedb} --prunepaths='/tmp /var/tmp /media /run' + ''; + }; + + services.cron.systemCronJobs = optional config.services.locate.enable + "${config.services.locate.period} root ${config.systemd.package}/bin/systemctl start update-locatedb.service"; + }; } - illustrates a module which handles - the regular update of the database which index all files on the file - system. This modules has option definitions to rely on the cron service - to run the command at predefined dates. In addition, this modules - provides option declarations to enable the indexing and to use different - period of time to run the indexing. Properties are used to prevent - ambiguous definitions of option (enable locate service and disable cron - services) and to ensure that no options would be defined if the locate - service is not enabled. +
Option declarations + +An option declaration specifies the name, type and description +of a NixOS configuration option. It is illegal to define an option +that hasn’t been declared in any module. A option declaration +generally looks like this: + + +options = { + name = mkOption { + type = type specification; + default = default value; + example = example value; + description = "Description for use in the NixOS manual."; + }; +}; + + + + +The function mkOption accepts the following arguments. + + + + + type + + The type of the option (see below). It may be omitted, + but that’s not advisable since it may lead to errors that are + hard to diagnose. + + + + + default + + The default value used if no value is defined by any + module. A default is not required; in that case, if the option + value is ever used, an error will be thrown. + + + + + example + + An example value that will be shown in the NixOS manual. + + + + + description + + A textual description of the option, in DocBook format, + that will be included in the NixOS manual. + + + + + + + +Here is a non-exhaustive list of option types: + + + + + types.bool + + A Boolean. + + + + + types.int + + An integer. + + + + + types.str + + A string. + + + + + types.lines + + A string. If there are multiple definitions, they are + concatenated, with newline characters in between. + + + + + types.path + + A path, defined as anything that, when coerced to a + string, starts with a slash. This includes derivations. + + + + + types.listOf t + + A list of elements of type t + (e.g., types.listOf types.str is a list of + strings). Multiple definitions are concatenated together. + + + + + types.attrsOf t + + A set of elements of type t + (e.g., types.attrsOf types.int is a set of + name/value pairs, the values being integers). + + + + + types.nullOr t + + Either the value null or something of + type t. + + + + + +You can also create new types using the function +mkOptionType. See +lib/types.nix in Nixpkgs for details. + +
+ + +
Option definitions + +Option definitions are generally straight-forward bindings of values to option names, like + + +config = { + services.httpd.enable = true; +}; + + +However, sometimes you need to wrap an option definition or set of +option definitions in a property to achieve +certain effects: + +Delaying conditionals + +If a set of option definitions is conditional on the value of +another option, you may need to use mkIf. +Consider, for instance: + + +config = if config.services.httpd.enable then { + environment.systemPackages = [ ... ]; + ... +} else {}; + + +This definition will cause Nix to fail with an “infinite recursion” +error. Why? Because the value of + depends on the value +being constructed here. After all, you could also write the clearly +circular and contradictory: + +config = if config.services.httpd.enable then { + services.httpd.enable = false; +} else { + services.httpd.enable = true; +}; + + +The solution is to write: + + +config = mkIf config.services.httpd.enable { + environment.systemPackages = [ ... ]; + ... +}; + + +The special function mkIf causes the evaluation of +the conditional to be “pushed down” into the individual definitions, +as if you had written: + + +config = { + environment.systemPackages = if config.services.httpd.enable then [ ... ] else []; + ... +}; + + + + + + +Setting priorities + +A module can override the definitions of an option in other +modules by setting a priority. All option +definitions that do not have the lowest priority value are discarded. +By default, option definitions have priority 1000. You can specify an +explicit priority by using mkOverride, e.g. + + +services.openssh.enable = mkOverride 10 false; + + +This definition causes all other definitions with priorities above 10 +to be discarded. The function mkForce is +equal to mkOverride 50. + + + +Merging configurations + +In conjunction with mkIf, it is sometimes +useful for a module to return multiple sets of option definitions, to +be merged together as if they were declared in separate modules. This +can be done using mkMerge: + + +config = mkMerge + [ # Unconditional stuff. + { environment.systemPackages = [ ... ]; + } + # Conditional stuff. + (mkIf config.services.bla.enable { + environment.systemPackages = [ ... ]; + }) + ]; + + + + + + +
+ + +
Important options + +NixOS has many options, but some are of particular importance to +module writers. + + + + + + + This set defines files in /etc. A + typical use is: + +environment.etc."os-release".text = + '' + NAME=NixOS + ... + ''; + + which causes a file named /etc/os-release + to be created with the given contents. + + + + + + + A set of shell script fragments that must be executed + whenever the configuration is activated (i.e., at boot time, or + after running nixos-rebuild switch). For instance, + +system.activationScripts.media = + '' + mkdir -m 0755 -p /media + ''; + + causes the directory /media to be created. + Activation scripts must be idempotent. They should not start + background processes such as daemons; use + for that. + + + + + + + This is the set of systemd services. Example: + +systemd.services.dhcpcd = + { description = "DHCP Client"; + wantedBy = [ "multi-user.target" ]; + after = [ "systemd-udev-settle.service" ]; + path = [ dhcpcd pkgs.nettools pkgs.openresolv ]; + serviceConfig = + { Type = "forking"; + PIDFile = "/run/dhcpcd.pid"; + ExecStart = "${dhcpcd}/sbin/dhcpcd --config ${dhcpcdConf}"; + Restart = "always"; + }; + }; + + which creates the systemd unit + dhcpcd.service. The option + determined which other units pull this + one in; multi-user.target is the default + target of the system, so dhcpcd.service will + always be started. The option + provides the main + command for the service; it’s also possible to provide pre-start + actions, stop scripts, and so on. + + + + + + + + If your service requires special UIDs or GIDs, you can + define them with these options. See for details. + + + + + +
+
@@ -306,50 +583,79 @@ in Building specific parts of NixOS - +With the command nix-build, you can build +specific parts of your NixOS configuration. This is done as follows: -$ nix-build /path/to/nixpkgs/nixos -A attr +$ cd /path/to/nixpkgs/nixos +$ nix-build -A config.option -where attr is an attribute in -/path/to/nixpkgs/nixos/default.nix. Attributes of interest include: +where option is a NixOS option with type +“derivation” (i.e. something that can be built). Attributes of +interest include: - config - The computer configuration generated from - the NIXOS_CONFIG environment variable (default - is /etc/nixos/configuration.nix) with the NixOS - default set of modules. + system.build.toplevel + + The top-level option that builds the entire NixOS system. + Everything else in your configuration is indirectly pulled in by + this option. This is what nixos-rebuild + builds and what /run/current-system points + to afterwards. + + A shortcut to build this is: + + +$ nix-build -A system + + - system - The derivation which build your computer system. It is - built by the command nixos-rebuild - build + system.build.manual.manual + The NixOS manual. - vm - The derivation which build your computer system inside a - virtual machine. It is built by the command nixos-rebuild - build-vm + system.build.etc + A tree of symlinks that form the static parts of + /etc. + + + system.build.initialRamdisk + system.build.kernel + + The initial ramdisk and kernel of the system. This allows + a quick way to test whether the kernel and the initial ramdisk + boot correctly, by using QEMU’s and + options: + + +$ nix-build -A config.system.build.initialRamdisk -o initrd +$ nix-build -A config.system.build.kernel -o kernel +$ qemu-system-x86_64 -kernel ./kernel/bzImage -initrd ./initrd/initrd -hda /dev/null + + + + + + + + system.build.nixos-rebuild + system.build.nixos-install + system.build.nixos-generate-config + + These build the corresponding NixOS commands. + + + - -Most parts of NixOS can be built through the config -attribute set. This attribute set allows you to have a view of the merged -option definitions and all its derivations. Important derivations are store -inside the option and can be listed with the -command nix-instantiate --xml --eval-only /path/to/nixpkgs/nixos -A -config.system.build - -
@@ -370,8 +676,7 @@ you have to set NIXOS_CONFIG before running nix-build to build the ISO. -$ export NIXOS_CONFIG=/path/to/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix -$ nix-build /path/to/nixpkgs/nixos -A config.system.build.isoImage +$ nix-build -A config.system.build.isoImage -I nixos-config=modules/installer/cd-dvd/installation-cd-minimal.nix @@ -386,23 +691,6 @@ $ mount -o loop -t iso9660 ./result/iso/cd.iso /mnt/iso
- - -
- -Testing/building the NixOS Manual - -A quick way to see if your documentation improvements -or option descriptions look good: - - -$ nix-build -A config.system.build.manual - - - -
- -
@@ -415,8 +703,7 @@ tedious, so here is a quick way to see if the installer works properly: -$ export NIXOS_CONFIG=/path/to/nixpkgs/nixos/modules/installer/cd-dvd/installation-cd-minimal.nix -$ nix-build /path/to/nixpkgs/nixos -A config.system.build.nixos-install +$ nix-build -A config.system.build.nixos-install $ dd if=/dev/zero of=diskimage seek=2G count=0 bs=1 $ yes | mke2fs -j diskimage $ mount -o loop diskimage /mnt @@ -430,91 +717,61 @@ $ ./result/bin/nixos-install -
+
Whole-system testing using virtual machines -Testing the <literal>initrd</literal> +Complete NixOS GNU/Linux systems can be tested in virtual +machines (VMs). This makes it possible to test a system upgrade or +configuration change before rebooting into it, using the +nixos-rebuild build-vm or nixos-rebuild +build-vm-with-bootloader command. -A quick way to test whether the kernel and the initial ramdisk -boot correctly is to use QEMU’s and - options: - - -$ nix-build /path/to/nixpkgs/nixos -A config.system.build.initialRamdisk -o initrd -$ nix-build /path/to/nixpkgs/nixos -A config.system.build.kernel -o kernel -$ qemu-system-x86_64 -kernel ./kernel/bzImage -initrd ./initrd/initrd -hda /dev/null - - - - -
- -
- - Whole-system testing using virtual machines - - - Complete NixOS GNU/Linux systems can be tested in virtual machines - (VMs). This makes it possible to test a system upgrade or - configuration change before rebooting into it, using the - nixos-rebuild build-vm or - nixos-rebuild build-vm-with-bootloader command. - - - - - - The tests/ directory in the NixOS source tree - contains several whole-system unit tests. - These tests can be runNixOS tests can be run both from - NixOS and from a non-NixOS GNU/Linux distribution, provided the - Nix package manager is installed. from the NixOS - source tree as follows: + +The tests/ directory in the NixOS source +tree contains several whole-system unit tests. +These tests can be runNixOS tests can be run both from +NixOS and from a non-NixOS GNU/Linux distribution, provided the Nix +package manager is installed. from the NixOS source +tree as follows: $ nix-build tests/ -A nfs.test - This performs an automated test of the NFS client and server - functionality in the Linux kernel, including file locking - semantics (e.g., whether locks are maintained across server - crashes). It will first build or download all the dependencies of - the test (e.g., all packages needed to run a NixOS VM). The test - is defined in - tests/nfs.nix. If the test succeeds, - nix-build will place a symlink - ./result in the current directory pointing at - the location in the Nix store of the test results (e.g., - screenshots, test reports, and so on). In particular, a - pretty-printed log of the test is written to - log.html, which can be viewed using a web - browser like this: +This performs an automated test of the NFS client and server +functionality in the Linux kernel, including file locking semantics +(e.g., whether locks are maintained across server crashes). It will +first build or download all the dependencies of the test (e.g., all +packages needed to run a NixOS VM). The test is defined in +tests/nfs.nix. If the test succeeds, +nix-build will place a symlink +./result in the current directory pointing at the +location in the Nix store of the test results (e.g., screenshots, test +reports, and so on). In particular, a pretty-printed log of the test +is written to log.html, which can be viewed using +a web browser like this: $ firefox result/log.html - + - - It is also possible to run the test environment interactively, - allowing you to experiment with the VMs. For example: +It is also possible to run the test environment interactively, +allowing you to experiment with the VMs. For example: $ nix-build tests/ -A nfs.driver $ ./result/bin/nixos-run-vms - The script nixos-run-vms starts the three - virtual machines defined in the NFS test using QEMU/KVM. The root - file system of the VMs is created on the fly and kept across VM - restarts in - ./hostname.qcow2. - +The script nixos-run-vms starts the three virtual +machines defined in the NFS test using QEMU/KVM. The root file system +of the VMs is created on the fly and kept across VM restarts in +./hostname.qcow2. - - Finally, the test itself can be run interactively. This is - particularly useful when developing or debugging a test: +Finally, the test itself can be run interactively. This is +particularly useful when developing or debugging a test: $ nix-build tests/ -A nfs.driver @@ -523,8 +780,7 @@ starting VDE switch for network 1 > - Perl statements can now be typed in to start or manipulate the - VMs: +Perl statements can now be typed in to start or manipulate the VMs: > startAll; @@ -537,65 +793,61 @@ starting VDE switch for network 1 > $client2->succeed("flock -n -s /data/lock true"); - The function testScript executes the entire - test script and drops you back into the test driver command line - upon its completion. This allows you to inspect the state of the - VMs after the test (e.g. to debug the test script). - +The function testScript executes the entire test +script and drops you back into the test driver command line upon its +completion. This allows you to inspect the state of the VMs after the +test (e.g. to debug the test script). - - This and other tests are continuously run on the Hydra - instance at nixos.org, which allows - developers to be notified of any regressions introduced by a NixOS - or Nixpkgs change. - +This and other tests are continuously run on the Hydra +instance at nixos.org, which allows +developers to be notified of any regressions introduced by a NixOS or +Nixpkgs change. - - The actual Nix programming interface to VM testing is in NixOS, - under - lib/testing.nix. This file defines a - function which takes an attribute set containing a - nixpkgs attribute (the path to a Nixpkgs - checkout), and a system attribute (the system - type). It returns an attribute set containing several utility - functions, among which the main entry point is - makeTest. - +The actual Nix programming interface to VM testing is in NixOS, +under +lib/testing.nix. This file defines a +function which takes an attribute set containing a +nixpkgs attribute (the path to a Nixpkgs checkout), +and a system attribute (the system type). It +returns an attribute set containing several utility functions, among +which the main entry point is makeTest. + - - The makeTest function takes a function similar to - that found in - tests/nfs.nix (discussed above). It - returns an attribute set containing (among others): +The makeTest function takes a function +similar to that found in +tests/nfs.nix (discussed above). It +returns an attribute set containing (among others): - + - - test - A derivation containing the test log as an HTML file, - as seen above, suitable for presentation in the Hydra continuous - build system. - + + test + A derivation containing the test log as an HTML + file, as seen above, suitable for presentation in the Hydra + continuous build system. + - - report - A derivation containing a code coverage report, with - meta-data suitable for Hydra. - + + report + A derivation containing a code coverage report, with + meta-data suitable for Hydra. + - - driver - A derivation containing scripts to run the VM test or - interact with the VM network interactively, as seen above. - - + + driver + A derivation containing scripts to run the VM test or + interact with the VM network interactively, as seen above. + + - - + + +
+ diff --git a/nixos/modules/installer/tools/tools.nix b/nixos/modules/installer/tools/tools.nix index 41c562a6f46..652bfa917df 100644 --- a/nixos/modules/installer/tools/tools.nix +++ b/nixos/modules/installer/tools/tools.nix @@ -103,7 +103,7 @@ in ]; system.build = { - inherit nixos-install nixos-generate-config nixos-option; + inherit nixos-install nixos-generate-config nixos-option nixos-rebuild; }; }; } diff --git a/nixos/modules/misc/version.nix b/nixos/modules/misc/version.nix index 86898bbb379..2fa95563e9a 100644 --- a/nixos/modules/misc/version.nix +++ b/nixos/modules/misc/version.nix @@ -58,18 +58,15 @@ with pkgs.lib; # Generate /etc/os-release. See # http://0pointer.de/public/systemd-man/os-release.html for the # format. - environment.etc = singleton - { source = pkgs.writeText "os-release" - '' - NAME=NixOS - ID=nixos - VERSION="${config.system.nixosVersion} (${config.system.nixosCodeName})" - VERSION_ID="${config.system.nixosVersion}" - PRETTY_NAME="NixOS ${config.system.nixosVersion} (${config.system.nixosCodeName})" - HOME_URL="http://nixos.org/" - ''; - target = "os-release"; - }; + environment.etc."os-release".text = + '' + NAME=NixOS + ID=nixos + VERSION="${config.system.nixosVersion} (${config.system.nixosCodeName})" + VERSION_ID="${config.system.nixosVersion}" + PRETTY_NAME="NixOS ${config.system.nixosVersion} (${config.system.nixosCodeName})" + HOME_URL="http://nixos.org/" + ''; };