Nameserver - A network available remote hash table


The nameserver essentially provides the function of a remote hash table; The contexts create logically separate hash tables. You use NameServer_Lookup*(...) to perform hash table lookups You use NameServer_Bind*(...) to perform hash table inserts

Semantics of operation

When the nameserver crashes and restarts, all of the hash tables are initialized to empty.

All of the bind and lookup operations are serialized and executed atomically at the nameserver.

The nameserver functions are not thread-safe, and should not be called from multiple threads at the same time. They can be safely (in theory, not tested) called in a serial manner from different threads.

The caller's context is the current context as defined below, and by the dynamic usage of the SetContext function.

The Bind and Lookup functions return 0 on success, and -1 on error.

The nameserver that is used defaults to an implementation defined constant (currently, but can be overridden by setting NS_HOSTNAME to some hostname where a nameserver is running.

Startup information from the nameserver can be generated by setting the environment variable NS_LOUD.

The initial context is set to the environment variable NS_CONTEXT, or the username of the owner of the process if NS_CONTEXT is not defined.


The nameserver library will automatically connect to the server, and initialize the context when either the Bind or Lookup functions are called. If NameServer_Finish() is called, the client will close the connection to the nameserver. The library will re-initialize if a bind or lookup function is called again.


When NameServer_Lookup*(...) executes at the nameserver, if input_name is bound in the caller's context, then the value it is bound to, and the version number are set in the output variables. If the input_name is not bound, then the output_name is set to NULL, the output_namelen is set to 0, and the version is set to ENS_VERSION_NOT_THERE


When NameServer_Bind*(...) executes at the nameserver, the input_name is bound to the output_name in the caller's context if one of three criteria is met:

  1. the previous_version is DONT_CARE
  2. the previous version is NOT_THERE, and the input_name is not currently bound in the caller's context.
  3. the previous version is $v$, and the input name is currently bound in the caller's context with version number $v$

If the name is bound, and the previous version is DONT_CARE or NOT_THERE, the bound version is set to 0; otherwise the bound version number is incremented by one modulo 1 billion.

If the output_name is NULL, and the name is bound, then the binding has been logically destroyed.

If the flag value is set to destroy binding on connection close, then when the connection to the nameserver is destroyed (for any reason, process exit, network problems) the names that were bound by that process are destroyed. If a name is re-bound without the flag being set, then the most recent setting of the flag is the one that will take precedence. Note that this option has fairly poor semantics, if the kernel panics, then the connection may not ever be torn down, and so the names won't go away. Setting this flag will cause something reasonable to happen in most cases, and something unpredicatable in others. The current default is to not use this mode unless the flag is set.

Get and Set Context

The Get and Set Context operations get and set the current context.


    int NameServer_Init(void); -- deprecated; 
    void NameServer_Finish(void);

    int NameServer_Lookup(char *input_name, 
                          byte **output_name,
                          int *output_namelen);
    int NameServer_Lookup_Version(char *input_name, 
                                  byte **output_name,
                                  int *output_namelen,
                                  u32 *output_version);

For the Lookup functions, output_name is allocated space, you should free it when done; You can set the output_version to NULL, and it will be ignored. Setting the input or output name or namelen variables to NULL will probably cause a crash.

    int NameServer_Bind(char *input_name, 
                        byte *output_name,
                        int output_namelen);
    int NameServer_Bind_Version(char *input_name, 
                                byte *output_name,
                                int output_namelen, 
                                u32 previous_version);
    int NameServer_Bind_Version_Flags(char *input_name, 
                                      byte *output_name, 
                                      int output_namelen, 
                                      u32 previous_version, 
                                      int flags);

As described eariler, bind to NULL to delete a value.

    char *NameServer_GetContext(void);
    void NameServer_SetContext(char *context);

GetContext returns the context -- you need to release the space for this (it has been malloced); SetContext duplicates the context, so you can go ahead and free it.


ENS_VERSION_DONT_CARE -- ignore the previous version number

ENS_VERSION_NOT_THERE -- require that the previous version not exist

ENS_FLAG_DESTROY_BINDING_ON_CONNECTION_CLOSE -- flag to destroy the variable on connection close. see the semantics description for the fairly bad semantics this has.


#define ENS_NOERROR(x) if ((x)!=0) {printf(``Nameserver error.\n'');exit(1);}



Changed the default behavior of the Bind and Bind_Version calls to set the ENS_FLAG_DESTROY_BINDING_ON_CONNECTION_CLOSE option, and emit a warning message. This behaviour will be reset soon after June 1.


Added NameServer_Finish(). Removed the need to call NameServer_Init(). Added a bunch of list functions, but they are undocumented (other than the header file) because it's not yet clear the interface is correct. The environment variable NS_SILENT is now ignored as the default behavior is to be silent unless NS_LOUD is set.


Modified the nameserver to be thread safe. It was going to be too hard to make sure that different clients of the nameserver didn't step on each other, and it was easier to just add the locking calls to the nameserver. The list functions export a lock/unlock interface because sometimes the lists need to remain constant over multiple calls. The list functions themselves do no locking. Each list has it's own lock.