Appendix C. IRIX Name Service Implementation

This appendix details the functioning of the IRIX name service implementation, called Unified Name Service (UNS), included with IRIX beginning with the 6.5 release. The IRIX name service is made up of a set of C library routines, cache files, a resolver daemon, and protocol libraries. Each of the elements is considered separately in some depth.

Topics in this appendix include:

For specific tasks associated with installing UNS, refer to the chapter on Unified Name Service in IRIX Admin: Networking and Mail .

Overview of UNS

The Unified Name Service (UNS) is a name service architecture consisting of a C library API, a cache database, a management daemon, and a number of protocol libraries. It permits new name service protocols to be added very simply by the creation of a shared library with a couple of well-defined entry points, while providing a system-wide cache of all results.

This name service API maintains library-level compatibility with previous releases, and no applications need to be recompiled. The protocol code, which existed in the specific API routines, has been moved out of the C library into separate shared libraries.

UNS Programming Steps

To make full use of the UNS name service, follow these steps:

  1. Understand the name service process and determine:

    • The network layout.

    • The configuration required.

    • The details of the resolve order as shown in the /etc/nsswitch.conf file as described in IRIX Admin: Networking and Mail.

    • The relationship between this name service and other name services installed.

  2. Configure and test the name service system as described in IRIX Admin: Networking and Mail.

  3. Add configuration information to the nsswitch.conf file as described in IRIX Admin: Networking and Mail.

  4. Write any special programming using the routines described in “UNS Library Routines.”

UNS Library Routines

Starting in IRIX 6.5, the standard name service API routines, which contained protocol code to directly converse with name service daemons, such as getpwname(), gethostent(), and getprotobynumber(), all remain but have been reimplemented as wrappers around two new library routines: ns_lookup(), which fetches a single item from a named table, and ns_list(), which enumerates an entire name service table.

When a C library routine such as gethostbyname() is called in an application, sufficient memory for the returned data structure is allocated, and the routine ns_lookup() is called with the key, together with a domain and the name of the table containing this information.

The ns_lookup() routine mmaps a global shared cache database corresponding to the table name, and attempts to look up the key in this database. If the lookup fails, then the routine opens a file associated with the key, table, and domain, and parses the data the way it has been done historically with flat configuration files. The file is generated on the fly by a cache miss daemon that acts as a user-level NFS file server.

The daemon determines the resolve order for the request, then calls routines in shared libraries for each of the protocols supported. Once the data is found, it is stored in the global shared cache database and a file is generated in memory using the format of the flat text file.

The gethostbyname() routine then parses the result into the appropriate data structure and returns.

getXbyY() Routine

Each getXbyY() style routine simply sets up a global memory buffer, calls ns_lookup() with a normalized key, the name of a map containing the data, and the domain in which the map lives; it then parses the results into a map-specific data structure. Reentrant routines of the form getXbyY_r(), which have been added, behave exactly as the getXbyY() routines, except that they use passed-in memory buffers instead of a global space. All of the standard routines are simply wrappers around the reentrant versions to reduce code space in the C library.

getXent() Routine

The getXent() style routines are wrappers around the ns_list() routine that provide a concatenation of all records in each of the supported back-end databases for a table in what appears to be a flat ASCII file. Reentrant routines of the form getXent_r(), which have been added, behave exactly as the getXent() routines, except that they use passed-in memory buffers instead of a global space. Again, all of the standard routines are simply wrappers around the reentrant versions in order to reduce space.

ns_lookup() Routine

The ns_lookup() routine mmaps the cache file for the given table if it has not already been opened, then attempts to look up the given key in the cache. The cache is a shared, multi-reader, multi-writer, hash database written specifically for this name service implementation, and named MDBM.

If the cache file cannot be opened, or the key does not already exist in the cache, then a separate daemon is contacted to act as the cache miss handler, locating the information within a name service and inserting it in the database. This daemon is contacted through the NFS protocol and the result of the lookup is returned to the client in the format of the flat system configuration file.

ns_list() Routine

The ns_list() routine contacts the daemon through the NFS protocol and asks for a concatenation file for a given domain and table, then returns a file pointer to this newly formed concatenation file. The getXent() wrapper routines then use stdio to walk through this file, parsing each line into a C data structure, and returning these sequentially. The getXent_r() routines are identical, and use the same file pointer, but they use passed-in buffer space to hold the return data instead of dynamically allocated space.

The arguments to ns_lookup are a table structure, the domain name for the query, a table name, a key for the query, a buffer to place the results in, and a length for this buffer. The table structure contains a database pointer, a time stamp lock pointer, and a flags field, which determines whether the cache file needs to be closed between calls. It returns an integer result of NS_SUCCESS, NS_NOTFOUND, or NS_FATAL

To see a definition of all return codes and structures, look in the /usr/include/ns_api.h header file.

The arguments to ns_list are the domain name, table name, and an optional protocol name. The routine returns a file pointer.

UNS Cache Files

The UNS cache files are multi-reader, multi-writer, mmapped hash database files. The new database, MDBM, is a simple, extremely fast, single-key, file format.

There is a cache file for each table maintained by the name service daemon in a well-known location. The C library routines always look for the cache files in the /var/ns/cache directory.

The cache files are writable only by root, and the C library routines always open the cache files as read-only. The cache files can be set to a fixed size, which allows them to be mapped once, then the file descriptor closes, and the mapping remains throughout the life of the process. If the caches are a variable size then they are remapped on each lookup unless the stayopen flag is given to the setXent() call associated with the table. This is similar behavior to the treatment of files in the historic file-only name service implementations.

Cache file entries are made up of a time_t, which can be compared to the current clock for timeouts, a time_t which is compared to the time stamp in the map structure, a status character to support negative caching, and the data. Timeouts are handled by all applications. When an application notices that the information is out of date, it requests new information from the daemon. When a cache file is opened with a fixed size, then the cache is split ahead of time to that size. Any time adding an element results in the splitting of the page, a shake function is called instead, to free up space for the new data.

The format of keys in the database is: 


where domain and protocol are not given if they are the default, and not specified in the lookup.

UNS Name Service Daemon Operation

The IRIX name service daemon, nsd, acts as a cache miss handler for the name service cache files, and lets all protocols speak with remote name servers. The protocol handlers are separated into protocol libraries, which are opened dynamically whenever the protocols are needed, according to the resolution order specified in the daemon configuration file. The nsd daemon implements a base set of functionality needed by the protocol libraries.

Name Service Configuration Files and Data Structures

The daemon behavior is completely controlled by the daemon configuration files. A configuration file exists for the client behavior in /etc/nsswitch.conf, and a similar file exists under /var/ns/domains/DOMAINNAME/nsswitch.conf for each domain supported by this daemon. If the file /etc/nsswitch.conf does not exist, a default configuration is used. Server-side domain directories must contain an nsswitch.conf file, or the domain is ignored.

The nsswitch.conf file is made up of lines in the format:

map: library library library

where each element in the line can have an attribute list associated with it in the format:

(attribute=value, attribute=value, attribute=value)

These attributes may also exist on a line alone, in which case they set the attributes on the domain. And a library may be followed by a control field of the form:


All of the data from nsswitch.conf is maintained in the daemon in four data structure trees:

  • A linked list of libraries that have been opened.

  • A linked list of cache files, one for each table.

  • A btree of file structures. (A btree() is a hash indexed binary tree.)

  • A set of attribute lists.

The library data is kept in a simple linked list; one structure for each protocol library that has been opened by the daemon. The structure contains the library name as found in the nsswitch.conf file, the pathname for the DSO, and an array of function pointers to each of the protocol library entry points.

The map structures are also kept in a simple linked list, and contain information about the cache files the daemon maintains. There is one entry per table that is inserted into the list the first time a request has been made for data from that table. In contains the name of the cache file, a pointer to the database structure, and information about the mapping. Cache files are closed and unmapped when the global shake function is called.

The majority of the information in the nsswitch.conf files is saved in an in-memory filesystem. Each data item is stored in a file structure and placed into a large global btree. The file structure contains a set of attributes, and possibly a pointer to a map structure containing information on the cache file that is updated when this file is changed, or to a library structure that contains the function pointers for changing this structure. The data field can either be data as read from the back-end databases or a directory list. The hash used for the btree is the file ID, which is simply a 32-bit unsigned value stored in the file structure.

The filesystem tree is rooted with a root file referenced by a global variable. Each nsswitch.conf file results in a new file structure (domain), and a reference in the root directory. Each line in the nsswitch.conf file results in a new file structure (table), and a reference in the corresponding domain directory. Each library on a line results in a new file structure (callout) and a reference in the table directory. Each directory file structure also contains a reference to the parent. When the reference count on a file goes to zero, it is removed, and the reference count is decremented for each file it points to. Removing the global reference on the root file effectively removes all files in the tree.

Attributes are stored in linked lists attached to file structures. Each attribute list is terminated by an empty structure referencing the attribute list of the parent directory. Searching an attribute list starts with the local attributes then follows the link to the parent list and so on. As a consequence, all attributes are inherited by the children. Attribute structures are separately reference counted, so that removal of a parent directory while a file is in use does not necessarily result in the removal of the attribute list it points to.

Understanding the UNS Runtime Loop

Once the configuration files have been read, the daemon falls into an infinite select loop waiting for input, then dispatching to handler routines. On startup the daemon opens a request socket for reading and sets up a handler for this file descriptor. Whenever the select loop wakes up with data on a file descriptor, the handler for the file descriptor is called. New descriptors can be added or removed at any time by the protocol library code using the utility routines nsd_callback_new() and nsd_callback_remove().

Only one callback is set up by default. This callback is the dispatch handler for the NFS protocols. A new packet is parsed as an NFS request, and is answered out of the in-memory filesystem. When a file is referenced that does not already exist in the tree, a new file structure is generated and placed into the tree. A list of callout libraries is inherited from the parent directory, and control is returned to the central loop, which walks the structure through each of the callout library routines until a result is obtained.

The loop through the callout list calls a callout procedure in one of the protocol libraries.

  • If the library routine returns the code NSD_OK, the request has been filled, and the input specific return procedure is called to return the results to the calling application.

  • If the library returns the NSD_ERROR code, then an error occurred while trying to handle the request and an error should be returned immediately to the client.

  • If a code of NSD_NEXT is returned, then the library did not find the result and the next callout procedure is called.

  • If the NSD_CONTINUE code is returned, the protocol routine had to send a request to an external daemon or is doing something that will take a long time, so the loop should start working on the next request. The protocol code now owns the request, so there must be a way for the request to start processing again in the future or a leak will occur. The two typical ways for this to continue are that a result comes in on a socket resulting in a handler being called, or a timeout occurs. At any time in the callout list, the default behavior of the return code may be overridden by an entry in the nsswitch.conf file. For instance, suppose the following line were in the configuration file:

    hosts: nis [notfound=return] files

    Instead of continuing on to the files callout when a result is not found in the NIS maps, an error is returned to the client. The files callout is called only if NIS is not running, or did not contain the requested record.

Handlers can be set up at any time by protocol code, but typically a socket is set up once during initialization for each library. Timeouts are usually placed on each forwarded request in case the remote agent fails to respond to the request within a reasonable time period. There is a global timeout list for the daemon's central select() loop. Each time select() is called, the next timeout is first popped off of the stack and used to determine what the select() timeout should be. If select() wakes up due to a timeout, the handler in the timeout structure is called. Handlers are created using the daemon routine nsd_callback_new(), and removed using nsd_callback_remove(). Timeouts are created using nsd_timeout_new(), and removed using nsd_timeout_remove().

Understanding UNS Utility Functions

The name service daemon contains a number of utility functions that should be used by protocol libraries . These include routines to manipulate return values, set up callbacks handlers for new file descriptors, set up timeouts on the central select loop, and handle errors. All prototypes for these functions are defined in /usr/include/nsdapi.h. 

  • Handling Results

    The nsd_set_result() function provides a convenient way to set the return status and data for a request.The function takes four arguments: a pointer to the file structure; a status code, which should be one of NS_SUCCESS, NS_NOTFOUND, NS_TRYAGAIN, NS_UNAVAIL, NS_BADREQ, and NS_FATAL; a pointer to the result string; the length of the result; and a function pointer to a routine to free this string if needed. There are three routines predefined: DYNAMIC, which is a pointer to the standard free() function in the C library; STATIC, which is a null pointer; VOLATILE, which results in nsd_set_result() copying the data into a new dynamically allocated buffer. It returns an integer which is either NSD_OK if successful or NSD_ERROR if unsuccessful. If a result already exists, it is freed using the existing free function pointer, and the new result is set.

    int nsd_set_result(nsd_file_t *, int, char *, int, nsd_free_proc *);

    The nsd_append_result() utility function is similar to the nsd_set_result() function, but it appends the given string to the end of an already existing result string if one exists. There is no need to pass a free routine, as this function always copies the data into a new dynamically allocated buffer.

    This function takes three arguments: a pointer to the request structure, a pointer to the result string to be appended, and the length of the string. It returns an integer that is NSD_OK on success, or NSD_ERROR when unsuccessful. On error, the current result string and code is unchanged.

    int nsd_append_result(nsd_file_t *, int, char *, int);

    The nsd_append_element() function is identical to the nsd_append_result() routine except that the result strings are joined by a newline character. This routine assumes that all result strings it is given are null terminated strings.

    int nsd_append_element(nsd_file_t *, int, char *, int);

  • Handling File Descriptor Callbacks

    The nsd_callback_new() function is used to set up a file descriptor callback for the daemon main loop. When select() wakes up with data on a file descriptor, the callback handler is looked up in a table, and the corresponding function is called. Protocol libraries can set up callbacks at any time for a file descriptor that they have opened. This routine registers the new handler function and causes select to wake up on new data waiting on the descriptor. If a handler was already registered for the descriptor, it is replaced.

    This function takes three arguments: an integer file descriptor, a pointer to the handler function, and a flag that contains options for what events the callback should be used. It should be made up of NSD_READ, NSD_WRITE, and NSD_EXCEPT. It returns a pointer to the handler function on success, or a null pointer on failure. The only cause for failure is that the file descriptor is out of range.

    nsd_callback_proc *nsd_callback_new(int, nsd_callback_proc *,unsigned);

    The nsd_callback_remove() function clears a handler from the list of file descriptors. This function takes one argument, which is the integer file descriptor, and returns an integer, which is NSD_OK or NSD_ERROR.

    int nsd_callback_remove(int);

    The nsd_callback_get() function returns the callback handler function pointer, given the integer file descriptor.

    nsd_callback_proc *nsd_callback_get(int);

  • Handling Timeouts

    The nsd_timeout_new() function is used to set up timeout handlers for the central select loop. Any time a protocol routine returns NS_CONTINUE, the routine should set up a timeout handler to continue the request processing.

    This function takes four arguments: a pointer to the file structure, an unsigned timeout value in milliseconds, a pointer to a timeout handler routine, and a pointer to any local data needed by the protocol code. It returns a pointer to the timeout structure on success, or a null pointer on failure. The local data pointer can be nil if the calling routine does not need data associated with the timeout.

    nsd_times_t *nsd_timeout_new(nsd_file_t *,long, 
    nsd_timeout_proc *, void *);

    The nsd_timeout_remove() function is called to remove a timeout from the timeout list. This is typically called when a protocol function receives a reply from a remote daemon, and no longer needs the select loop to timeout to continue processing.

    This function takes one argument, a pointer to the file structure, and returns an integer result, which is NSD_OK for success or NSD_ERROR for failure. Failure usually indicates that there was no matching timeout on the list.

    int nsd_timeout_remove(nsd_file_t *);

  • Handling Attributes

    The nsd_attr_store() routine adds an attribute to an attribute list. Attributes should be used instead of global variables when possible. Attribute lists are tied together from most specific to least specific, walking backwards up the daemon data structure tree.

    This function takes three arguments: a pointer to the pointer to the beginning of this attribute list, a pointer to a string for the key, and a pointer to a string for the data. It returns a pointer to the attribute structure if successful or a null pointer on error.

    nsd_attr_t *nsd_attr_store(nsd_attr_t **, char *, char *);

    The nsd_attr_delete() routine removes the attribute from the given list. Continuations to other lists are not followed, which means that if nsd_attr_fetch() were immediately called with this key, it may find a result.

    This function takes two arguments: a pointer to the pointer to the first attribute in the list and a pointer to the string for the key. It returns an integer, which is NSD_OK on success or NSD_ERROR if the attribute was not found.

    int nsd_attr_delete(nsd_attr_t **, char *);

    The nsd_attr_fetch() routine searches through an attribute list, following continuations to other lists, searching for a matching attribute. Key comparisons are case-insensitive.

    This function takes two arguments: a pointer to the beginning of the attribute list, and a pointer to the string for the key. It returns a pointer to the attribute structure if found or a null pointer on failure.

    nsd_attr_t *nsd_attr_fetch(nsd_attr_t *, char *);

    The three routines nsd_attr_fetch_long(), nsd_attr_fetch_string(), and nsd_attr_fetch_bool() are simple wrappers around nsd_attr_fetch(). They take a pointer to the attribute list, a string for the key, and a default value. The nsd_attr_fetch_long() routine also takes a radix. These routines return the value of the attribute interpreted as a long, string, or boolean, depending on the function called, or the default value if the key was not found.

    long nsd_attr_fetch_long(nsd_attr_t *, char *, int, long);
    char *nsd_attr_fetch_string(nsd_attr_t *, char *, char *);
    int nsd_attr_fetch_bool(nsd_attr_t *, char *, int);

  • Handling Memory 

    The nsd_shake() routine should be called to free up resources when allocating new resources fails. This results in a call to all of the protocol-specific shake() routines. This frees memory, close and unmap files, and generally tries to reduce the resources used. The name service daemon and many of the protocol libraries are aggressive about caching results, connections to files or remote daemons, and so on.

    This routine takes an integer level from 0 to 9, and returns no results.

    void nsd_shake(int);

    The three routines nsd_malloc(), nsd_calloc(), and nsd_strdup() are wrappers around the standard malloc(), calloc(), and free() routines, which call nsd_shake() on failure, then retry the allocation. The standard free() routine can be called to give up memory.

    void *nsd_malloc(size_t);
    void *nsd_calloc (size_t,size_t);
    char *nsd_strdup(char *);

  • Handling Access and Output

    The three routines nsd_open(), nsd_mdbm_open(), and nsd_mmap() are wrapper functions around open(), mdbm_open(), and mmap(). On failure they call the nsd_shake() function then try again. The standard routines close(), mdbm_close(), and mmap() can be used to close these files.

    int nsd_open(const char *, int, mode_t);
    MDBM *nsd_mdbm_open(const char *, int, mode_t, int);
    void *nsd_mmap(void *, size_t, int, int, int, off_t);

    The nsd_logprintf() routine takes the same arguments as printf() plus an integer level from 0 to 6, but results in a message to the log or to the console, depending on arguments to the daemon. If nsd receives a SIGUSR2 signal, it will cycle through the logging levels, thus allowing a developer, debugger to select the logging level without having to restart nsd. It should be used to print error messages. The log levels are defined in ns.daemon.h.

    void nsd_logprintf(int char *, ...);

How UNS Protocol Libraries Work

All of the name service protocol code that existed inside the API routines in the C library is in separate protocol libraries, which are used only by the name service daemon. Each library has a small set of entry points, which are used by the daemon command routines. These routines are init(), lookup(), list(), verify(), dump(), and shake().

Library Init Routine

The init() routine in a library is called when the library is first opened, and again whenever the daemon receives a SIGHUP signal. Typically, the init() procedure reads any protocol-specific configuration files, such as /etc/resolv.conf for DNS, and sets up any global data needed by the library, such as a list of domains or server addresses.

The init() procedure takes no arguments, and returns an integer, which is NSD_OK or NSD_ERROR.

int init(void);

The init() procedure may set up handlers for new requests of an alternative protocol-specific form, such as the nisserv library, which accepts Sun RPC requests for NIS version 2.

This routine may also set up handlers for results dealing with forwarded requests. Most of the name service protocols reformat the request into a different form and send it to another daemon, then set up a timeout and callback. When the results come back from the remote system, they go through this handler routine, which parses the results into an internal form again, and returns a successful result code to the main loop.

The init() routine may also create some false requests to take care of initialization that can happen asynchronously. The nis and nisserv callouts use this feature to register with portmap. They send off a packet to the portmap daemon, set up a handler and timeout, then give control back to the main loop so as not to hang if there are problems registering.

Library Lookup Routine

The lookup() routine is the most called of all routines in the name server and is the one that most people think of as the protocol. This routine converts the internal request format into a protocol-specific format and sends it to a remote daemon. When results come back, they are converted into an internal format again, and a status code is returned. It is up to the initial request handler to set up the reply.

The lookup() routine takes one file pointer argument and returns an integer, which is NSD_OK, NSD_ERROR, NSD_NEXT, or NSD_CONTINUE.

int lookup(nsd_file_t *);

In the simple case the lookup() routine simply fetches data out of a file, converts it into the proper format, and returns it immediately.

Library List Routine

The list() routine concatenates all records into an internal flat file. This is used by the getXent() routines or for administration.

The list() function takes one file pointer argument and returns an integer, which is NSD_OK, NSD_ERROR, NSD_NEXT, or NSD_CONTINUE.

int list(nsd_file_t *);

Library Dump Routine

The dump() routine is used for protocol debugging and outputs the current state of the library to the given file descriptor. When the daemon is sent a SIGUSR1 signal, it opens a file /usr/tmp/nsd.dump and writes its state, then calls each of the library routines to do the same.

void dump(int);

Library Verify Routine

The verify() routine checks that the results previously returned in lookup() or list() are still valid. The verify() function takes one file pointer and returns an integer which is NSD_OK or NSD_error.

int verify(nsd_file_t*)

Library Shake Routine

The shake() function is called when the daemon runs short of resources. This function frees up any resources used by the protocol library that are not needed. For instance, the files callout shake() function closes and unmaps all of the open files.

Any protocol routine that runs out of resources, such as attempting a malloc() that fails or failing to open a new file, should call the daemon utility function nsd_shake(), which frees any unneeded global data then calls each of the protocol-specific shake() functions. shake() is called with an integer priority from 0 through 9, determined by need. After calling nsd_shake(), the protocol routine tries again to do whatever failed before returning an error. The utility routines nsd_malloc(), nsd_calloc(), and nsd_strdup() do exactly this.

The shake() function takes one integer argument from 0 to 9 and returns an integer, which is NSD_OK or NSD_ERROR.

int shake(int);

Files Callout Library

The files library mmap() flat files into the daemon memory and searches through them for matching lines in the same fashion as the C library API fallback routines. The filename is determined by the map name, and the directory is determined by the domain name. By default this is /var/ns/domain/file or /etc/file for the .local domain. Either of these can be overridden using attributes “file” or “directory” attached to the files callout in the appropriate nsswitch.conf file.

The passwd.* map is special. For any line of the form [+-]@?[\S]+, it verifies the element by making a recursive call into the daemon and returning the NSD_NEXT code to the main loop. If the directive [notfound=return] is specified after the files callout in nsswitch.conf, the behavior is identical to the historic behavior of forcing calls into NIS, except that any library may follow files, not only NIS.

The list routine simply copies the entire mapped file into the result instead of attempting to do any parsing.

NIS Callout Library(Optional)

The nis library implements the client side of the Sun YP RPC protocol and the YPBIND protocol. Internal requests are reformatted into RPC requests and sent to a remote host, a callback and timeout are set up, and control is returned to the main daemon loop. When a response comes back to the socket owned by the nis library, a handler is called that parses the YP RPC result packet into the internal format and returns it to the client. Responses are mapped back to the original request structure using the XID field in the RPC header of the response packet.

The library also maintains a socket for incoming YPBIND RPC requests which are answered using data maintained by the nis library.

If any request comes in and the daemon has not already bound to a server, or if a request to a server times out, then a bind broadcast/multicast is sent out, and the request is held until the daemon is able to bind to a new server. If the daemon is unable to bind within a couple of seconds, an NS_TRYAGAIN status is returned to the client so that it will resend the request instead of falling back to local files. If the file/var/yp/domain/binding/ypservers exists, then the hosts listed in this file is sent unicast bind requests instead of a broadcast sent out.

The nis library fakes for maps that exist in the nsswitch.conf file but not in the NIS version 2 standard. These include services.bynumber, group.bymember, and rpc.byname. The library first attempts to look up data using these names, then falls back to stepping through the reverse map file if that fails.

The list() routine connects to the ypserv daemon using TCP, appending the entire results to the file data.

Nisserv Callout Library (Optional)

The nisserv callout library implements the server side of the Sun YP RPC protocol. It opens a socket on init on which it accepts new requests. It looks up these requests using the standard callout list, and replies to the requestor using the YP protocol.

When the YP_ALL request is received, it enumerates only the maps for which the boolean enumerate attribute is set. If this attribute is not set for any callout, it enumerates the MDBM database instead, provided the mdbm library is listed as a callout.

Note: yp_all simply enumerates the MDBM database, and is not supported for anything else. The internal data format needs to change before it can support the other databases.

DNS Callout Library

The dns library implements the client side of the Domain Name Service Protocol. New requests are converted from the internal format to a DNS packet format and sent to a remote server, then a timeout and callback is set up and control is given back to the main loop. When a response comes back from the server, it comes to a socket owned by the dns library and passes through a DNS response handler. The response is mapped back to the original request using the DNS header xid field, and the packet is parsed back into the internal format to be returned to the client.

The order for contacting servers is controlled by the resolv.conf file, or by the servers attribute attached to the dns callout in nsswitch.conf. The domain is the same as the request domain except in the case of the .local domain. When the .local domain is used, the domain in the dns request is determined by the domain or search fields in resolv.conf or by the domain attribute in nsswitch.conf.

The map hosts.byname is turned into a class IN, type A request to DNS. The map hosts.byaddr is turned into a class IN, type PTR request to DNS. Any other map is turned into a class IN, type TXT request to DNS using the DNS domain table.domain; where any “.” characters in the table are replaced with “_”. For instance a call for the key uucp; in the passwd.byname map for the domain results in a lookup of in the IN class, and returns a TXT type.

MDBM Callout Library

The mdbm library uses the MDBM database format to store data in local files. A set of parser scripts are provided to parse flat files into the databases. This supports a faster lookup method than the files library. The files default to /var/ns/domain/table.m for each table, or /etc/table.m in the .local domain. This can be overridden by setting the file attribute on the table in the appropriate nsswitch.conf file.

The list() command results in a mdbm_next() loop, appending each successive value to the end of the result.

Berkeley DB Callout Library

The berkeleydb library is a key-value database. This library supports arbitrary sized values and is faster than NDBM. The files default to /var/ns/domain/table.db or /etc/table.db for the local domain. This can be overridden by setting the file attribute on the table in the appropriate nsswitch.conf file.

The list() command results in a (DB*)db->seq() loop appending each successive value to the end of the result.

NDBM Callout Library

The ndbm library is the standard IRIX database mechanism, which is a key/value like hash files, and similar in functionality to the original DBM as well as Berkeley DB.

NFS Interface to UNS

The primary interface to the nsd daemon from the API routines is through the Network File System. The name service daemon acts as a user level NFS file server for an in-memory stacked filesystem. The daemon is mounted onto the local system at startup, and all the API routines simply open files in the filesystem tree managed by the name service daemon.

The name service daemon has a special mount command called nsmount. This command determines the port that the name service is running on, and the initial file handle for the requested domain directory then passes this to the kernel. Future versions of the NFS protocol will hopefully allow the name service daemon to be treated just like any other NFS server so that the regular mount command, automount, and autofs can be used.

It is possible to mount the name service daemon from another system, and this technique is expected to support large networks of systems and trees of domains. The administrator can explicitly restrict a portion of the namespace to the local host by setting the local attribute on the top element of the subtree. By default the .local domain sets the local attribute to true so other systems cannot read local passwords, and so on. The default location of the mount point is /ns/domain, where domain is the requested domain in the ns_lookup() or ns_list() routine. There is a special domain labeled .local that always exists that provides a system local domain to override any parent domain information. All of the API getXbyY() routines currently use the .local domain.

The daemon filesystem tree is organized as: /ns/domain/table/key, and there is a special domain, .local, to represent the local view of the namespace, and “dot” directories under each table to represent the callout libraries. To look up the login name uucp using the local namespace view, open the file /ns/.local/passwd.byname/uucp. If you want only the NIS entry for uucp, open /ns/.local/passwd.byname/.nis/uucp. The special key .all in a map returns a concatenation of all the records in a table, so opening the file /ns/.local/passwd.byname/.all gives you a giant passwd file containing all users in the local domain. Executing cat /ns/.local/passwd.byname/.nis/.all is equivalent to running ypcat passwd.byname. Or cat /ns/.local/passwd.byname/.files/.all is identical to cat /etc/passwd on most systems.

Removing a file in the filesystem maintained by the name service daemon results in the cached file structure being removed in the daemon. The directory entries cannot be removed. Instead this is done by editing the nsswitch.conf files and sending the daemon a SIGHUP signal. Attempting to remove a directory results in the timeout routine being run on that subdirectory so that all dynamic elements under that directory are removed.

In the IRIX operating system, extended attributes are supported on each name service file. The attributes on the file depend on the library that looked them up, but always include domain, table, key, timeout, library, version, and server. The timeout is the time in seconds since epoch that the cache entry disappears from the daemon. The library is the name of the library as given in nsswitch.conf, that provided the data in the file, and server is the address of the system that provided the data. The server may not be the actual authorized owner of the information, but is instead simply the system from which you got the information. These can be read using the attr command. For example, to get the source of a key, run:

attr -g library /ns/.local/passwd.byname/uucp 

Only the get and list functions work with the name service daemon. All information in the name server tree is read-only.