{"id":24527577,"url":"https://github.com/offlinehacker/ncd","last_synced_at":"2025-08-23T18:04:27.071Z","repository":{"id":2746800,"uuid":"3743070","full_name":"offlinehacker/NCD","owner":"offlinehacker","description":"The Network Configuration Daemon, is a daemon and programming language for configuration of network interfaces and other aspects of the operating system made by Ambrož Bizjak.","archived":false,"fork":false,"pushed_at":"2012-03-17T19:32:06.000Z","size":2611,"stargazers_count":4,"open_issues_count":0,"forks_count":4,"subscribers_count":1,"default_branch":"ipv6-offlinehacker","last_synced_at":"2025-08-16T03:19:03.924Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://code.google.com/p/badvpn/wiki/NCD","language":"C","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/offlinehacker.png","metadata":{"files":{"readme":"README.wiki","changelog":"ChangeLog","contributing":null,"funding":null,"license":"COPYING","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"security/BEncryption.c","support":null}},"created_at":"2012-03-16T21:08:06.000Z","updated_at":"2021-01-03T22:30:00.000Z","dependencies_parsed_at":"2022-09-08T05:50:20.298Z","dependency_job_id":null,"html_url":"https://github.com/offlinehacker/NCD","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/offlinehacker/NCD","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offlinehacker%2FNCD","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offlinehacker%2FNCD/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offlinehacker%2FNCD/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offlinehacker%2FNCD/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/offlinehacker","download_url":"https://codeload.github.com/offlinehacker/NCD/tar.gz/refs/heads/ipv6-offlinehacker","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/offlinehacker%2FNCD/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271760492,"owners_count":24816430,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-01-22T06:19:36.786Z","updated_at":"2025-08-23T18:04:27.018Z","avatar_url":"https://github.com/offlinehacker.png","language":"C","funding_links":[],"categories":[],"sub_categories":[],"readme":"#summary NCD - powerful and extensible network interface configuration\n#labels linux,network,interface,configuration,hotplug,dhcp,flexible,powerful\n\n= Contents =\n\n\u003cwiki:toc max_depth=\"1\" /\u003e\n\n= Introduction =\n\nNCD, the Network Configuration Daemon, is a daemon and programming language for configuration of network interfaces and other aspects of the operating system. It implements various functionalities as built-in modules, which may be used from an NCD program wherever and for whatever purpose the user needs them. This modularity makes NCD extremely flexible and extensible. It does a very good job with hotplugging in various forms, like USB network interfaces and link detection for wired devices. New features can be added by implementing statements as C-language modules using a straightforward interface.\n\nNCD is only available for Linux. It is included in the BadVPN software package.\n\n= Why ? =\n\nIf you use Linux on a desktop system, you must have heard of a program called !NetworkManager. It is a program that is meant to take care of network configuration on the OS. It is designed to be extremely easy and simple to use; it has a GUI with a wireless network list, easy wireless and IP configuration, support for VPN, etc..\n\nThis all seems good. Until you want to use two VPNs at the same time. Or add some nontrivial iptables rules. Or tell it not to mess with the network interface called eth1. Or setup a bridge interface. Basically, it's a hardcoded mess, and if you want it to do something the designers haven't specifically foreseen and implemented, better go crying right away.\n\nOther network configuration frameworks have similar problems. The Gentoo init scripts, for example, can't even do DHCP right (it times out and forgets about the interface forever)!\n\nMy conclusion was that, for more powerful and extensible network configuration, the existing systems need to be thrown away; a new system better suited to the needs of power users needs to be designed. So here comes NCD!\n\n= Basics =\n\nNCD is used by writing an NCD program (the \"configuration file\"), then starting the `badvpn-ncd` program as a daemon (usually at system boot time). NCD will then keep running the supplied program, until it is requested to terminate (usually at shutdown time). NCD is designed to work in the background without any need to communicate with the user, and to automatically recover from any transient problems. The goal is to make things (e.g. network access) \"just work\", while still allowing dynamic and complex configuration.\n\n= Model of execution =\n\nAn NCD program (see example below) consists of a set of so-called *processes*. Globally, the processes can be considered to execute in parallel. Each process consists of a list of *statements*.\n\nWhen NCD starts up, all statements are *uninitialized*. Then the first statement in every process is *inizialized*. An initialized statement is first in the *down* state. It may at any time transition into the *up* state. When it does so, the next statement in the process is initialized, and when this one transitions up, the next one is initialized, etc..\n\nA statement (A) that is initialized and up can, at any time, go back into the down state. When this happens, NCD will cease initializing statements in the containing process, and will begin *deinitializing* statements, from the *bottom up*, until all statements following statement A are uninitialized; it will then wait for the offending statement A to go back up, and continue initializing statements from that point.\n\nThe deinitialization of a statement often has the *reverse effect* of initialization; for example, if a statement added a routing table entry on initialization, it will remove this same routing table entry on deinitialization. This design is very suitable for various configuration tasks, as it avoids possible leaks. This design makes it trivially easy to, for example, make sure than when a network cable is pulled out, the associated routes and IP addresses are removed.\n\nWhen a statement is initialized, it is provided with a list of *arguments* which tell the underlying module specifically what it is supposed to do. An argument is either a string or a list of strings and lists. Arguments can either be provided literally in the statement specification, or they may refer to *variables*, whose values are provided by preceding statements in the process. Variables are key to the power of NCD.\n\nA statement is specified as `statement(arg1, ..., argN);`. Optionally, it can be given an identifier: `statement(arg1, ..., argN) identifier;`.\n\nA variable is used by passing `object.variable` as an argument. Here, `object` is an identifier of one of the statements in the process, declared somewhere above (later statements with that name shadow the earlier); `variable` is the name of the variable within this statement. Additionally, if only `object` is passed, it refers to the empty string variable within `object` (thus, you're still passing a *value* (string, list), *not* a reference to the statement).\n\nWhen NCD is requested to terminate, it starts deinitializing all processes from the bottom up, like described above, and this deinitialization continues until all statements in all processes are deinitialized. NCD then exits.\n\nThe NCD interpreter by itself does not provide any control constructs. These are however implemented as statements which communicate behind the back of the interpreter. An example of this are the `provide()` and `depend()` statements.\n\n= Complete example =\n\nThis is an example NCD program that works with a single wired network interface and uses DHCP to obtain the IP address, default route and DNS servers.\n\n{{{\nprocess lan {\n    # Set device.\n    var(\"eth0\") dev;\n\n    # Wait for device, set it up, and wait for network cable.\n    net.backend.waitdevice(dev);\n    net.up(dev);\n    net.backend.waitlink(dev);\n\n    # DHCP configuration.\n    # net.ipv4.dhcp() will block here until it obtaines an IP address.\n    # Note that it will only obtain the IP address, and *not* assign it;\n    # we do that with a separate command below.\n    net.ipv4.dhcp(dev) dhcp;\n\n    # Check IP address - make sure it's not local.\n    # If you have other reserved subnets around, check for those too.\n    ip_in_network(dhcp.addr, \"127.0.0.0\", \"8\") test_local;\n    ifnot(test_local);\n\n    # Assign IP address, as obtained by DHCP.\n    net.ipv4.addr(dev, dhcp.addr, dhcp.prefix);\n\n    # Add default route, as obtained by DHCP.\n    net.ipv4.route(\"0.0.0.0\", \"0\", dhcp.gateway, \"20\", dev);\n\n    # Configure DNS servers, as obtained by DHCP.\n    net.dns(dhcp.dns_servers, \"20\");\n}\n}}}\n\nThe above code, when read from top to bottom, simply specifies which operations are to be performed in order to reach the desired state. However note that each NCD program has an implicit deinitialization semantic. Here, for instance, pulling the network cable out will **automatically** remove the DNS servers, remove the default route and remove the IP address from the network interface.\n\n= More examples =\n\nSee [NCD_examples] for more complex examples. NCD is capable of much more than the above program may suggest. For example, it can handle multiple network interfaces with priorities for Internet access, it can work with wireless networks and BadVPN network interfaces.\n\nThere is also an alternative introduction to NCD, and more examples, which include BadVPN interfaces: http://code.google.com/p/badvpn/source/browse/trunk/ncd/README .\n\n= Requirements =\n\nNCD requires various programs during execution. In particular:\n\n  * *iproute2* (`ip` command) is needed by the `net.up`, `net.ipv4.addr` and `net.ipv4.route` modules. Not all distributions come with that; Gentoo for example doesn't.\n\n  * *udev \u003e=171* is needed for `net.backend.waitdevice` and `net.watch_interfaces`.\n\n= Running it =\n\nFor installation instructions see [Installation].\n\n== Disabling existing network configuration ==\n\nBefore you start NCD, you have to stop any existing network configuration system to avoid interference.\n\n  * Gentoo: stop `NetworkManager` init script, stop `net.` init scripts, except `net.lo`.\n  * Ubuntu: stop !NetworkManager using `initctl stop network-manager`\n\nAlso, !NetworkManager has a habit of not deconfiguring interfaces when stopped. If you had !NetworkManager running:\n\n  * Kill dhclient: `killall dhclient`\n  * Remove IP addresses: `ip addr del \u003caddr\u003e/\u003cprefix\u003e dev \u003ciface\u003e`\n  * Set down: `ip link set \u003ciface\u003e down`\n\n== Testing from command line == \n\nOnce you're sure your interfaces are deconfigured and there is nothing that could interfere, then try the program (`/etc/ncd.conf`) out by running (as root):\n{{{\nbadvpn-ncd --config-file /etc/ncd.conf\n}}}\n\nNCD will print status messages as it executes your program. If it's not working as expected, these can help you with debugging it.\n\n== Automatically ==\n\nIf you installed BadVPN via a package manager, NCD is integrated into your distro's init system, to use `/etc/ncd.conf` as the NCD program. To use NCD by default, you will have to permanently disable existing network configurations, and have NCD start on boot instead.\n\nTo disable existing network configurations:\n\n  * Gentoo: Disable `NetworkManager` init script. Delete `net.` init scripts (which are symlinks to `net.lo`), except `net.lo` itself, to prevent Gentoo from autoconfiguring interfaces.\n  * Ubuntu: Disable !NetworkManager by editing `/etc/init/network-manager.conf`, commenting the two `start on` lines, or by uninstalling the `network-manager` package.\n\nTo have NCD start on boot:\n\n  * Gentoo: Enable the `/etc/init.d/badvpn-ncd` init script: `rc-update add badvpn-ncd default`\n  * Arch: Enable the `/etc/rc.d/badvpn-ncd` init script by adding it to `DAEMONS` in `/etc/rc.conf`. Make sure it comes after syslog.\n  * Ubuntu: Use the `/etc/init/badvpn-ncd.conf` Upstart script. The script is disabled by default; the `start on` line inside it is commented out. Uncomment it to have it start on boot. However be aware that `apt-get` will start it regardless when it installs the `badvpn` package (the default `/etc/ncd.conf` is a no-op to avoid damage here). You can also manually control the service using `initctl \u003cstart/stop/restart/status\u003e badvpn-ncd`. \n\n= Module documentation =\n\nIndividual statement types (modules) are described briefly in the headers of their source files, under ncd/modules/ in the BadVPN source code: http://code.google.com/p/badvpn/source/browse/#svn%2Ftrunk%2Fncd%2Fmodules .\n\n= NCD lanaguage introduction =\n\nHere's a quick introduction to the programming language of NCD.\n\n== Diagnostic output ==\n\nThe [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/print.c println() and rprintln()] statements provide diagnostic output to standard outout. `println()` prints a message on initialization, and `rprintln()` prints a message on deinitialization.\n\n{{{\nprocess foo {\n    println(\"Starting up, please wait...\");\n    rprintln(\"Goodbye World!\");\n\n    sleep(\"500\", \"300\"); # sleeps 500ms on init and 300ms on deinit\n\n    println(\"Hello World!\");\n    rprintln(\"Shutting down, please wait...\");\n}\n}}}\n\nThis should result in something like this:\n{{{\n$ badvpn-ncd --loglevel warning --config-file hello.ncd\nStarting up, please wait...\n\u003c 500ms passes \u003e\nHello World!\n\u003c you hit CTRL+C \u003e\nShutting down, please wait...\n\u003c 300ms passes \u003e\nGoodbye World!\n$ \n}}}\n\n== Dependencies ==\n\nThe [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/depend.c provide() and depend()] statements implement dependencies.\n\nSuppose we want to wait for a network device, and have some kind of service that works with it:\n\n{{{\nprocess foo {\n    var(\"eth1\") dev;\n    net.backend.waitdevice(dev);\n\n    println(\"X: started on device \", dev);\n    rprintln(\"X: stopped on device \", dev);\n\n    # do something with device...\n}\n}}}\n\nHowever, what if, after the device is available, we want to run _two services in parallel_?\n\n{{{\nprocess foo {\n    var(\"eth1\") dev;\n    net.backend.waitdevice(dev);\n\n    provide(\"DEVICE\");\n}\n\nprocess device_service_x {\n    depend(\"DEVICE\") dep;\n\n    println(\"X: started on device \", dep.dev);\n    rprintln(\"X: stopped on device \", dep.dev);\n\n    # do something with device...\n}\n\nprocess device_service_y {\n    depend(\"DEVICE\") dep;\n\n    println(\"Y: started on device \", dep.dev);\n    rprintln(\"Y: stopped on device \", dep.dev);\n\n    # do something with device...\n}\n}}}\n\nNote how the service processes access the `dev` variable within process `foo` through the dependency. A `depend()` allows access to any variable as seen from the point of the matched `provide()`.\n\nBecause of how the NCD interpreter works, `provide()` should usually be the *last statement* in a process. Otherwise, when something before `provide()` goes down, a latter statement could take some time to deinitialize, and during this time, the depending processes may continue initalizing and may request variables through their `depend()`-s - which will fail, because `provide()` is scheduled for deinitialization and cant't resolve variables.\n\nIf you wanted to have some statements after a `provide()`, you should instead make a new process that `depend()`-s on this same `provide()` and put the statements there.\n\nThere are however exceptions to this rule. For example, the following code wakes up two parallel processes, but then merges back. It's safe because when process `main` is requested to terminate after it merged back, the two parallel processes have initialized completely and won't resolve any variables.\n\n{{{\nprocess main {\n    var(\"eth1\") dev;\n    net.backend.waitdevice(dev);\n\n    provide(\"DEVICE\");\n    depend(\"X_DONE\");\n    depend(\"Y_DONE\");\n\n    println(\"up\");\n    rprintln(\"down\");\n}\n\nprocess device_service_x {\n    depend(\"DEVICE\") dep;\n\n    println(\"X: started on device \", dep.dev);\n    rprintln(\"X: stopped on device \", dep.dev);\n\n    sleep(\"1000\", \"2000\");\n\n    provide(\"X_DONE\");\n}\n\nprocess device_service_y {\n    depend(\"DEVICE\") dep;\n\n    println(\"Y: started on device \", dep.dev);\n    rprintln(\"Y: stopped on device \", dep.dev);\n\n    sleep(\"2000\", \"1000\");\n\n    provide(\"Y_DONE\");\n}\n\n}}}\n\n== Branching (simple) ==\n\nNCD does not provide any nested control structures, such as `if` and `for`, as found in many languages; instead, execution control is done with special-purpose statements.\n\nA simple way to branch is using [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/depend.c provide() and depend()] statements.\n\n{{{\nprocess test {\n   # We will be branching based on the value of 'x' here.\n   # If 'x' equals \"foo\", we go one way, else the other way.\n   var(\"bar\") x;\n\n   # Produce a value which is either \"true\" or \"false\", indicating\n   # which way to branch.\n   strcmp(x, \"foo\") is_foo;\n\n   # Branch ... and wait for the branch to finish.\n   provide(\"branch\");\n   depend(\"branch_done\") br;\n\n   # Print the message.\n   println(br.msg);\n}\n\nprocess test_foo {\n   depend(\"branch\") dep;\n   if(dep.is_foo); # if(cond): if cond=\"true\", proceed; else block indefinitely\n\n   var(\"x was foo!\") msg;\n\n   provide(\"branch_done\");\n}\n\nprocess test_other {\n   depend(\"branch\") dep;\n   ifnot(dep.is_foo); # ifnot(cond): if cond!=\"true\", proceed; else block indefinitely\n\n   var(\"x was NOT foo!\") msg;\n\n   provide(\"branch_done\");\n}\n}}}\n\nHowever, this is an *inferior* way to branch, because the dependency names (`branch` and `branch_done`) have global scope. This means that each branch needs its unique set of dependency names, so as not to conflict with unrelated `provide()` and `depend()` statements.\n\nA proper way to branch is using the `call()` statement (read on).\n\n== Process templates ==\n\nMany of NCD's execution control features rely on a feature called *process templates*. A process template is written like a process, but using the `template` keyword instead of `process`. Unlike a regular process, which starts up automatically and is terminated automatically on NCD shutdown, a process template by itself does nothing. Instead, special statements are used to dynamically create *template processes* out of process templates.\n\n{{{\n# Does nothing by itself.\ntemplate my_template {\n    println(\"If I'm saying this, I'm running from a template process!\");\n}\n}}}\n\nA template process, however, is an *actual process*; its code is that of the process template is was created from. Unlike regular processes, template processes do not terminate automatically when NCD is requested to shut down; termination of a template process needs to be requested by its controlling code (often by the same statement that created it).\n\nBecause all template processes created out of the same process template have the same code, there needs to be a way to distinguish them. All statements that create template processes take a list of arguments, which is accessible from resulting template processes via `_argN` special variables. Additionally, the code creating a template process can provide other special variables.\n\nSome statements that create template processes are `call()`, `foreach()` and `process_manager()`. Read on to learn about them.\n\n== Calling templates ==\n\nThe effect of [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/call.c call()] is mostly equivalent to embedding the body of the called process template into the place of `call()`, including correct reverse execution.\n\n{{{\nprocess foo {\n    println(\"Saying hello...\");\n    call(\"say_hello\", {});\n    println(\"Successfully said hello!\");\n}\n\ntemplate say_hello {\n    println(\"Hello!\");\n    rprintln(\"Goodbye...\");\n}\n}}}\n\nAdditionally, with `call()`, you can:\n  * Pass arguments to the template process. These can be accessed from the template process via the `_argN` special variables (N-th argument, starting with zero), and the `_args` special variable (list of arguments).\n  * Access caller's objects from within the called template process via `_caller`.\n  * Once the called template process has initialized, access its objects through the name of the `call()` statement.\n\nThe following example demonstrates these features.\n\n{{{\nprocess foo {\n    var(\"Hello\") x;\n    call(\"make_msg\", {\"Good\", \"World\"}) c;\n    println(c.msg); # Prints: HelloGoodWorld\n}\n\ntemplate make_msg {\n    concat(_caller.x, _arg0, _arg1) msg;\n}\n}}}\n\nNote that it is impossible to define new statements from within the NCD programming language. The only way to define a new statement is to extend NCD by implementing the statement in C language using NCD's module interface. However, when considering a new statement, you should always first try to implement the same functionality in the NCD programming language, which would usually involve using process templates in some way.\n\nYou can compare the NCD interpreter and its programming language to a CPU and its machine language. Similarly to how you can't add new machine instructions to the CPU without changing its hardware (or microcode), you can't add new statements to NCD without changing its source code. On the other hand, like you _can_ write subroutines and call them using machine instructions, you can write process templates for NCD and call them using the `call()` statement.\n\n== Branching (correct) ==\n\nThe `call()` statement can (and should) be used for branching. This works by building the name of the called template dynamically based on runtime values. The following program demonstrates this.\n\n{{{\nprocess test {\n   # We will be branching based on the value of 'x' here.\n   # If 'x' equals \"foo\", we go one way, else the other way.\n   var(\"bar\") x;\n\n   # Produce a value which is either \"true\" or \"false\", indicating\n   # which way to branch.\n   strcmp(x, \"foo\") is_foo;\n\n   # Build template name based on the value of is_foo.\n   concat(\"branch_foo_\", is_foo) branch_template;\n\n   # Branch.\n   call(branch_template, {}) c;\n\n   # Print the message.\n   println(c.msg);\n}\n\ntemplate branch_foo_true {\n    var(\"x was foo!\") msg;\n}\n\ntemplate branch_foo_false {\n    var(\"x was NOT foo!\") msg;\n}\n}}}\n\nCompared to simple branching using `provide()` and `depend()`, this branching mechanism is safe to use from template processes.\n\n== Multi-way branching ==\n\nThe [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/choose.c choose()] statement together with `call()` can provide \"if, else if, ..., else\" style branching.\n\n{{{\nprocess foo {\n    var(\"false\") is_x;\n    var(\"true\") is_y;\n    var(\"false\") is_z;\n\n    # If is_x then do_x, else if is_y then do_y, else if is_z then do_z, else do_other.\n    choose({{is_x, \"do_x\"}, {is_y, \"do_y\"}, {is_z, \"do_z\"}}, \"do_other\") func;\n    call(func, {});\n}\n\ntemplate do_x {\n    println(\"Doing x\");\n}\n\ntemplate do_y {\n    println(\"Doing y\");\n}\n\ntemplate do_z {\n    println(\"Doing z\");\n}\n\ntemplate do_other {\n    println(\"Doing other\");\n}\n}}}\n\n== One-way branching ==\n\nIt is possible to do a one-way branch by giving `call()` `\"\u003cnone\u003e\"` as the template name to make it do nothing, possibly using `choose()`:\n\n{{{\nprocess foo {\n    var(\"false\") is_x;\n\n    # If is_x, then call do_x, else do nothing.\n    choose({{is_x, \"do_x\"}}, \"\u003cnone\u003e\") func;\n    call(func, {});\n}\n\ntemplate do_x {\n    println(\"Doing x\");\n}\n}}}\n\n== Foreach ==\n\n[http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/foreach.c foreach()] does something for each element of a list. It is mostly equivalent to putting multiple `call()` statements one after another, but it allows the elements to be specified dynamically in form of a list.\n\n{{{\nprocess foo {\n    var(\"World\") world;\n    foreach({\"A\", \"B\", \"C\"}, \"foreach_func\", {\"Hello\", \"Goodbye\"});\n    println(\"Everyone said hello!\");\n}\n\ntemplate foreach_func {\n    var(_arg0) hello;\n    var(_arg1) goodbye;\n\n    println(_elem, \": \", hello, _caller.world);\n    rprintln(_elem, \": \", goodbye, _caller.world);\n}\n}}}\n\nThis results in the following:\n\n{{{\nA: HelloWorld\nB: HelloWorld\nC: HelloWorld\nEveryone said hello!\n\u003c you hit CTRL+C \u003e\nC: GoodbyeWorld\nB: GoodbyeWorld\nA: GoodbyeWorld\n}}}\n\nThe template process created by `foreach()` for every element of the list can access its element using `_elem`. Additionally, like `call()`, it can access arguments passed to `foreach()`, and any variable or object `X` as seen from the point of `foreach()` via `_caller.X`.\n\nThe template processes are managed by `foreach()` in the same way as the NCD interpreter initializes and deinitializes statements within a process. In particular, if a statement in one of the template processes goes down, `foreach()` will pause this process, deinitialize any following processes from the bottom up, and only then continue the process where a statement went down (assuming no prior statements went down).\n\nThis means that you can use `foreach()` to acquire a set of resources at once. For example, to wait for to all network interfaces on a list to appear:\n\n{{{\nprocess foo {\n    list(\"eth0\", \"eth1\") ifaces;\n    foreach(ifaces, \"wait_link\", {});\n\n    println(\"all devices exits\");\n    rprintln(\"all devices no longer exist (or terminating)\");\n\n    # Configure those interfaces all at once here.\n}\n\ntemplate wait_device {\n    net.backend.waitdevice(_elem);\n}\n}}}\n\n== Method-like statements ==\n\nNote how the arguments of statements in NCD are restricted to plain data, and there is no concept of a reference to an object. Sometimes, however, it is needed for two or more statements to cooperate in their implementation, and you might need to tell one statement which existing related statement to cooperate with.\n\nNCD solves this with *method-like statements*. These statements are just like regular statements, except that they refer to some existing statement, and their implementation uses this information somehow. A method-like statement is written by preceding the method name with an object identifier and an arrow `-\u003e`. The following example demonstrates the [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/list.c list]::contains() statement.\n\n{{{\nprocess foo {\n    list(\"First\", \"Second\", \"Third\") l;\n    l-\u003econtains(\"Second\") c_second;\n    println(c_second); # Prints: true\n    l-\u003econtains(\"Fourth\") c_fourth;\n    println(c_fourth); # Prints: false\n}\n}}}\n\nNote that the `list::contains()` statements here only use the referred list object when they initialize, and not after that. In general, however, method-like statements can cooperate with the referred object however long they like; they can also go up and down like regular statements can.\n\nThe restriction on arguments being plain data applies not only to statement arguments, but on template process arguments too. For example, the following code is *incorrect*:\n\n{{{\nprocess foo {\n    # Make a list object using the list() statement.\n    # http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/list.c\n    list(\"First\", \"Second\") l;\n\n    # Call a template, which supposedly operates on the list object (not).\n    # Here, \"l\" really means \"value of variable named (empty string) in object l\".\n    # The value is a list value, per documentation of list().\n    call(\"contains\", {l, \"Second\"}) c;\n\n    println(c.result);\n}\n\ntemplate contains {\n    # Incorrect; there is no object named _arg0! (but there is a special *variable* named _arg0)\n    _arg0-\u003econtains(_arg1) result;\n}\n}}}\n\nIn this case, the error can be corrected by having the template process construct its own list object:\n\n{{{\nprocess foo {\n    list(\"First\", \"Second\") l;\n    call(\"contains\", {l, \"Second\"}) c;\n    println(c.result);\n}\n\ntemplate contains {\n    listfrom(_arg0) mylist; # like list(), only it concatenates the list arguments\n    mylist-\u003econtains(_arg1) result; # all good: mylist is a list object with ::contains method\n}\n}}}\n\nOn the other hand, if it's truly necessary to invoke a method-like statement on an object of the caller, this can be done by going through `_caller`:\n\n{{{\nprocess foo {\n    list(\"First\", \"Second\") l;\n    call(\"contains\", {\"Second\"}) c;\n    println(c.result);\n}\n\ntemplate contains {\n    _caller.l-\u003econtains(_arg0) result;\n}\n}}}\n\nIt's easy to see that how the above approach is clumsy to work with, because the template expects the object to have a predefined name. This can be fixed using the `alias()` statement, described next.\n\n== Aliases ==\n\nThe [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/alias.c alias()] statement allows a group of variables and objects to be referred to using a new name.\n\n{{{\nprocess foo {\n    list(\"hello\", \"world\") x;\n    alias(\"x\") y;\n    concatv(y) msg;\n    println(msg, y.length); # Prints: helloworld2\n    y-\u003econtains(\"world\") c; # Method calls are forwarded too.\n    println(c);\n}\n}}}\n\nNotice how the target of the alias is given as a string. When the `alias()` statement initializes, the target is not resolved in any way. In fact, all that `alias()` does is forward variable and object resolution requests by prepending the target string, plus possibly a dot, to the requested name, and resolving it from its point of view.\n\nAn important use of `alias()` is to simulate passing actual objects through a `call()` (compared to passing just data), such that the called process can invoke the object's methods. Here's the last example from the description of `call()`, fixed to allow specifying the object name:\n\n{{{\nprocess foo {\n    list(\"First\", \"Second\") l;\n    call(\"contains\", {\"_caller.l\", \"Second\"}) c;\n    println(c.result);\n}\n\ntemplate contains {\n    alias(_arg0) passed_list;\n    passed_list-\u003econtains(_arg1) result;\n}\n}}}\n\n== Multi-dependencies ==\n\nThe [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/multidepend.c multiprovide() and multidepend()] statements implement dependencies similar to `provide()` and `depend()`. Contrary to `provide()`, which will shout errors if another `provide()` for the same name is already active, it is possible to have multiple parallel `multiprovide()`s satisfying a single `multidepend()`. On the other side, a `multidepend()` will always attempt to bind itself to one of the `multiprovide()`s that satisfy it. It will always choose the best one based on its preference list, possibly un-binding itself from an existing `multiprovide()`.\n\nThe following example illustrates the behavior of `multiprovide()` and `multidepend()`:\n\n{{{\nprocess resource1 {\n    var(\"Resource 1\") name;\n    sleep(\"2000\", \"0\");\n    multiprovide(\"RESOURCE_1\");\n}\n\nprocess resource2 {\n    var(\"Resource 2\") name;\n    sleep(\"4000\", \"0\");\n    multiprovide(\"RESOURCE_2\");\n}\n\nprocess dependency {\n    # I want either \"RESOURCE_2\" or \"RESOURCE_1\",\n    # but I always prefer the former.\n    multidepend({\"RESOURCE_2\", \"RESOURCE_1\"}) dep;\n\n    println(\"Bound to \", dep.name);\n    rprintln(\"Unbound from \", dep.name);\n}\n}}}\n\nThis will result in the following:\n\n{{{\n\u003c NCD starts \u003e\n\u003c 2 seconds pass \u003e\nBound to Resource 1\n\u003c 2 more seconds pass \u003e\nUnbound from Resource 1\nBound to Resource 2\n}}}\n\nAs can be seen, unlike `depend()`, which only goes down when its bound `provide()` is broken, `multidepend()` also goes down when a better `multiprovide()` comes in.\n\nNote that the namespaces for dependency names of `provide()`/`depend()` and of `multiprovide()`/`multidepend()` are separate.\n\n== Event-reporting modules ==\n\nMany statements in NCD can be considered to report events. However, until now, events were reported only in form of a module going up or down. Some examples of this are `net.backend.waitdevice()`, `net.ipv4.dhcp()` and `depend()`. Sometimes, however, events may need to be treated as just events, without a natural correspondence to the up/down state of a statement.\n\nGeneral event reporting facilities in NCD are implemented in form of a statement which behaves as follows:\n\n  # When an event occurs, the statement goes up and exposes the information about the event via its variables.\n  # The statement has a `::nextevent()` method that is called to indicate that the current event has been handled; this makes the event reporting statement go back down, waiting for the next event.\n\nThe following example demonstrates the [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/sys_evdev.c sys_evdev()] statement, which reports events from a Linux event device (evdev).\n\n{{{\nprocess main {\n    sys.evdev(\"/dev/input/by-id/usb-BTC_USB_Multimedia_Keyboard-event-kbd\") evdev;\n    println(\"Event: \", evdev.type, \" \", evdev.value, \" \", evdev.code);\n    rprintln(\"... handled.\");\n    evdev-\u003enextevent();\n}\n}}}\n\nTo run this, you have to provide an existing `-event-` (!) device, and you need read permission on the device. If all goes well, this will result in NCD spamming the console with something in form of the following (the values here correspond to what is defined in the `linux/input.h` header file):\n\n{{{\nEvent: EV_MSC 458792 MSC_SCAN\n... handled.\nEvent: EV_KEY 0 KEY_ENTER\n... handled.\nEvent: EV_SYN 0 unknown\n... handled.\n}}}\n\nNote how the above example executes in a conceptually different way to what has been demonstrated before. In previous examples, processes were meant to advance forward towards a goal, and only revert to a previous state when something goes wrong. In this case, however, the process reverts as soon as `nextevent()` is reached, deinitializing all statements between `nextevent()` and `sys.evdev()`. While it may not appear as such at first look, it is really a *loop*. (feel free to indent the statements following `sys.evdev()` if you like it better that way)\n\n== Process manager ==\n\nEvent-reporting modules are not of much use without a useful way to handle events. The [http://code.google.com/p/badvpn/source/browse/trunk/ncd/modules/process_manager.c process_manager()] module provides the ability to spawn new processes in response to events. The following example demonstrates the behavior of process_manager():\n\n{{{\nprocess foo {\n    process_manager() mgr;\n    rprintln(\"Destroying manager...\");\n    println(\"Starting A\");\n    mgr-\u003estart(\"processA\", \"template_for_A\", {});\n    println(\"Starting B\");\n    mgr-\u003estart(\"processB\", \"template_for_B\", {});\n    println(\"Started all!\");\n}\n\ntemplate template_for_A {\n    println(\"A: starting\");\n    rprintln(\"A: died\");\n    sleep(\"1000\", \"1000\");\n    println(\"A: finished\");\n    rprintln(\"A: dying\");\n}\n\ntemplate template_for_B {\n    println(\"B: starting\");\n    rprintln(\"B: died\");\n    sleep(\"2000\", \"2000\");\n    println(\"B: finished\");\n    rprintln(\"B: dying\");\n}\n}}}\n\nThis will result in this:\n\n{{{\n$ badvpn-ncd --loglevel warning --config-file manager1.ncd\nStarting A\nA: starting\nStarting B\nB: starting\nStarted all!\n\u003c 1 second passes \u003e\nA: finished\n\u003c 1 second passes \u003e\nB: finished\n\u003c you hit CTRL+C \u003e\nDestroying manager...\nB: dying\nA: dying\n\u003c 1 second passes \u003e\nA: died\n\u003c 1 second passes \u003e\nB: died\n$ \n}}}\n\nSee how start() creates a new process and gives control to it; however, as soon as the created process cannot continue (in this case, entering sleep()), control is returned to the process that called start(). In fact, start() is done after it has spawned the process - it will not do anything from that point on, and will *not* stop the process when deinitializing. Instead, process_manager() will stop its processes when deinitializing, waiting for them to terminate.\n\nProcesses created via process_manager() can also be stopped explicitly using the stop() method, by providing the same process identifier as in the corresponding start() call:\n\n{{{\nprocess foo {\n    process_manager() mgr;\n    mgr-\u003estart(\"processA\", \"template_for_A\", {});\n    mgr-\u003estop(\"processA\");\n    println(\"Foo done.\");\n}\n\ntemplate template_for_A {\n    println(\"A: starting\");\n    rprintln(\"A: died\");\n    sleep(\"1000\", \"3000\");\n    println(\"A: started\"); # never called\n}\n}}}\n\nThis will produce the following:\n\n{{{\n$ badvpn-ncd --loglevel warning --config-file manager2.ncd\nA: starting\nFoo done.\n\u003c 3 seconds pass \u003e\nA: died\n}}}\n\nIn this case, start() spawns the process, which proceeds to sleep(), at which point control is returned to the `foo` process. This one then  calls stop(), triggering the deinitialization of the process that was just spawned, requesting its sleep() statement to terminate. sleep() again returns control to `foo`, which prints \"Foo done.\". After 3 seconds, sleep() finally deinitializes.\n\n== Handling events with process manager ==\n\nprocess_manager() can be used in combination with event reporting modules, in particular with those that report the presence of hardware devices, to automatically create processes that configure them:\n\n{{{\nprocess main {\n    process_manager() mgr;\n    \n    # Wait for network interface event (interface added/removed).\n    net.watch_interfaces() watcher;\n\n    println(\"Event: interface \", watcher.devname, \" \", watcher.event_type);\n\n    # Dispatch event.\n    concat(\"interface_event_\", watcher.event_type) func;\n    call(func, {watcher.devname});\n\n    # Finish handling this event.\n    watcher-\u003enextevent();\n}\n\ntemplate interface_event_added {\n    var(_arg0) dev;\n    _caller.mgr-\u003estart(dev, \"interface_worker\", {dev});\n}\n\ntemplate interface_event_removed {\n    var(_arg0) dev;\n    _caller.mgr-\u003estop(dev);\n}\n\ntemplate interface_worker {\n    var(_arg0) dev;\n\n    println(dev, \": starting\");\n    rprintln(dev, \": died\");\n\n    # Here comes your GENERIC interface configuration code.\n}\n}}}\n\nThis results in automatic starting and stopping of network interface configuration processes based which network interfaces exist in the system at any given time. Be aware that an `interface_worker` will be created for *all* network interfaces, including the loopback, wired and wireless interfaces. To configure them properly, branching must be used.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofflinehacker%2Fncd","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fofflinehacker%2Fncd","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fofflinehacker%2Fncd/lists"}