NIS_DB(3)

NAME
nis_db, db_initialize, db_create_table, db_destroy_table, db_first_entry, db_next_entry, db_reset_next_entry, db_list_entries, db_remove_entry, db_add_entry, db_table_exists, db_unload_table, db_checkpoint, db_standby, db_free_result - NIS+ database backend library functions

SYNOPSIS
#include <rpcsvc/nis.h>
#include <rpcsvc/nis_db.h>
bool_t
db_initialize(const char *dictionary_pathname);
db_status
db_create_table(const char *table_name, const table_obj *table);
db_status
db_destroy_table(const char *table_name);
db_result *
db_first_entry(const char *table_name, const int numattrs, const nis_attr *attrs);
db_result *
db_next_entry(const char *table_name, const db_next_desc *next_handle);
db_result *
db_reset_next_entry(const char *table_name, const db_next_desc *next_handle);
db_result *
db_list_entries(const char *table_name, const int numattrs, const nis_attr *attrs);
db_result *
db_remove_entry(const char *table_name, const int numattrs, const nis_attr *attrs);
db_result *
db_add_entry(const char *table_name, const int numattrs, const nis_attr *attrs, const entry_obj *entry);
db_status
db_table_exists(const char *table_name);
db_status
db_checkpoint(const char *table_name);
db_status
db_standby(const char *table_name);
void
db_free_result(db_result *);

DESCRIPTION
The nis_db library is used by the NIS+ server, rpc.nisd(8), to manipulate NIS+ tables. If provides functions that permit the creation and destruction of tables, as well as the addition and removal or entries from tables.

Unlike NIS v2, which uses a simple key/data metaphor for its maps, NIS+ uses a relational model. The nis_db interface is subset of a complete relational database system. Only those functions required by the NIS+ server are provided.

The database supplied with the FreeBSD implementation of NIS+ uses the XDR File Format (XFF) for storing and managing its tables. XFF files are actually XDR data streams that have been written to a file in a way that allows the various structures stored in the file to be retrieved in a ramdom access fashion rather than sequentially. XFF is also log-based, which means that changes are first written to a log and then committed to the XFF file when the database is checkpointed. To speed up lookups, data which describes entries (and their relations to other entries) is kept in memory and managed using the Relation Cache Manager (RCM). The database is designed to optimize response time for retrievals in exchange for a small performance penalty for storage and removal operations. This is considered to be a reasonable tradeoff since rpc.nisd(8) spends far more time doing lookups than anything else. The library has been tested with tables containing up to 30,000 entries.

This library is not meant to be used by any NIS+ client utilities. All NIS+ clients that need to manipulate tables should instead use the functions described in nis_tables(3). The main reason for this restriction is that this library has no facitlties for handling concurrent access to a single database by multiple processes. This feature is provided instead by the NIS+ server, rpc.nisd(8). The underlying database functions may cache updates or removals in memory, which makes it difficult to sychronize changes among multiple processes without resorting to complex inter-process communication or shared memory techniques. This problem is solved by using a single rpc.nisd(8) as the database access server.

The nis_db library uses three NIS+ structures defined in <rpcsvc/nis.h>. These are table_obj, entry_obj and nis_attr. Most functions return either a db_status code or a pointer to a db_result structure (which also contains a db_status code as one of its members). The <rpcsvc/nis_db.h> header defines these structures as follows:

    enum db_status = { DB_SUCCESS, DB_NOTFOUND, DB_NOTUNIQUE,
    		   DB_BADTABLE, DB_BADQUERY, DB_BADOBJECT,
    		   DB_MEMORY_LIMIT, DB_STORAGE_LIMIT,
    		   DB_INTERNAL_ERROR };
    struct db_result {
    	db_status	status;		/* Result status */
    	db_next_desc	nextinfo;	/* Next entry descriptor */
    	struct {
    		u_int		objects_len;
    		entry_obj	*objects_val;
    	} objects;			/* A variable-sized array of
    					   entry_obj structures. */
    	unsigned long	ticks;		/* execution time of
    };					   function (in usecs) */

The nis_attr structure is defined in <rpcsvc/nis.h> as follows:

    struct nis_attr
    	char	*zattr_ndx;		/* Name of index */
    	struct {
    		u_int	zattr_val_len;	/* Length of attribute */
    		char 	*zattr_val_val;	/* Attribute value */
    	} zattr_val;
    };

Descriptions of the entry_obj and table obj structures can be found in nis_objects(3).

The db_next_desc structure returned by db_first_entry() or db_next_entry() should be treated by the caller as an opaque handle: it contains a pointer to data that is internal to the library that should not be modified. The caller can dispose of the handle using either db_reset_next_entry() or db_free_result() (the latter deallocates the entire db_result structure while the former clears only the db_next_desc pointer and its associated resources).

The objects array in the db_result structure will only contain objects if a called function returns a status of DB_SUCCESS. Each db_result structure is dynamically allocated and must be freed with db_free_result(). If there is insufficient memory available with which to create the db_result structure, functions that normally return db_result will instead return a NULL pointer.

db_initialize() is used to initialize the database. This function accepts a single argument which is the name of a catalog file. The catalog file is a small database which contains a list of all available tables and their corresponding paths. It will be created if it does not already exist. If it does exist and is valid, the database will read the list of tables from the catalog and open each one, caching its name and db(3) handle for later use. db_initialize() returns TRUE on success and FALSE on failure.

db_create_table() creates the table named by table_name with the attributes specified by the supplied table_obj structure. It returns a db_status code of DB_SUCCESS if the table was successfully created, or some other code indicating an error if the table creation failed.

db_destroy_table() destroys the table specified by the table_name argument. All open handles related to the table are closed and its entry is removed from the library's internal cache. Like db_create_table(), it returns a db_status code to indicate success or failure.

db_first_entry() returns a copy of the first entry in the specified table that matches the specified attributes. attrs is an array of nis_attr structures containing numattrs members. If no attributes are specified, the first entry in the table is returned. If the specified attributes don't match any entries in the table, the query fails and DB_NOTFOUND is returned. If the query is successful, the nextinfo structure in db_result will contain an opaque handle that can be used as the next_handle argument to db_next_entry() or db_reset_next_entry(). The behavior of db_first_entry() varies somewhat depending on the specified arguments:

1.  If the user specifies no attributes, db_first_entry() returns the very first entry in the table and sets nextinfo to point to the second entry. Subsequent calls to db_next_entry() can then be used to enumerate the entire contents of a table.

2.  If the user specifies attributes that don't match any entry in the table, db_first_entry() returns DB_NOTFOUND.

3.  If the user specifies arguments that match exactly one entry in the table, db_first_entry() returns that entry and sets nextinfo to point to the entry immediately following it. Subsequent calls to db_next_entry() can then be used to enumerate the contents of the table starting from the matched entry (as opposed to starting right from the beginning).

4.  If the user specifies attributes that match more than one entry, db_first_entry() returns the first matching entry and sets nextinfo to point to the next matching entry. Subsequent calls to db_next_entry can then be used to enumerate the list of matching entries.

The latter behavior is equivalent to using db_list_entries() except that the entries are retrieved from the table one at a time rather than all at once. The db_result structure returned by db_first_entry() can be freed with db_free_result().

db_next_entry() returns a copy of the next entry in the specified table as indicated by the next_handle structure. A call to db_first_entry() followed by successive calls to db_next_entry() can be used to enumerate the entire contents of a table or just those entries that match the attributes supplied to db_first_entry(). Like db_first_entry(), this function returns a nextinfo structure pointing to the next entry in the query. When the last matching entry has been reached, the returned structure will be empty. It is therefore not necessary to call db_reset_next_entry() once the end of the list has been reached. The returned db_result structure can be freed with db_free_result().

db_reset_next_entry() is used to terminate a db_first_entry() / db_next_entry() query before reaching the end of the list. Once a next_handle has been destroyed using db_reset_next_entry(), subsequent attempts to use the handle with db_next_entry() will return DB_BADQUERY. The db_result structure which contained the db_next_desc structure that was destroyed can subsequently be freed with db_free_result.

db_list_entries() retrieves copies of entries that match the specified attributes. If no entries match, it returns DB_NOTFOUND. attrs is an attay of nis_attr structures containing numattrs members. Th db_result structure returned by db_first_entry() can be freed with db_free_result().

db_remove_entry() removes entries that match the specified attributes from the specified table. The returned db_result structure can be freed with db_free_result(). attrs is an array of nis_attrs structures with numattrs members.

db_add_entry() adds a copy of the specified entry to the table specified by table_name. This function can also be used to replace entries: if the caller specifies a set of attributes that match an existing entry in the table, this entry will be removed and replaced with the new one. The specified attributes must match only one entry in the table: if more than one matching entry is found, a DB_NOTUNIQUE status is returned. If no entries match the specified attributes, the new entry is simply added. Note that this means that to add an entry, at least one attribute must be supplied even if it does not match anything: if no attributes are specified ( numattrs is zero), db_add_entry() returns DB_BADQUERY. Note that it is possible to add the exact same entry twice: the library does permit duplicate entries.

db_table_exists() provides an efficient method for determining whether or not the named table exists. This function can be used to quickly confirm the existence of a given table prior to making an actual query.

db_unload_table() is used to remove the specified table from the library's internal queue. This process deallocates the table's queue entry, flushes the table's RCM resources and closes all its file descriptors. The NIS+ server, rpc.nisd(8), monitors table access patterns and uses this function to deactivate tables that have not been used in a while, thus conserving system resources. It is not necessary to reload a table once it has been unloaded: any subsequent queries involving the specified table will cause the table to automatically be reopened and loaded back into the internal queue.

db_checkpoint(), in the context of this particular library implementation, causes all log entries to be merged with the main XFF table file. Normally, any changes to a table are first written to a log; the table file itself remains untouched. In the event of a crash, the library will image of the table back up to date. Corrupted or incomplete log entries are rejected. In this way, the table data is always maintained in a consistent state. In database parlance, db_checkpoint() is used to 'commit' the changes described in the log to the main table file. Once the log has been rolled forward, it is removed. When a table has been checkpointed, the table handle itself remains open and is retained in the library's internal queue.

db_free_result() frees any resources allocated by any of the nis_db functions that return a pointer to a db_result structure. If the db_result structure in question also contains nextinfo structure, db_free_result() will call db_reset_next_entry() to deallocate it.

db_standby() causes the file descriptors of the specified table to be closed. This has the additional effect of causing the underlying database routines to free up resources allocated to the database handle. The internal queue entry for the table remains in memory. Subsequent queries involving the specified table will cause it to automatically be reopened.

RETURN VALUES
The functions db_add_entry(), db_first_entry(), db_next_entry(), db_list_entries() and db_remove_entry() return a pointer to a db_result structure, unless there is not enough memory available to allocate such a structure, in which case they will return NULL. This structure will always contain at least a status code and may also contain an array of objects and/or a pointer to a nextinfo handle.

The db_create_table(), db_destroy_table(), db_unload_table(), db_standby() and db_checkpoint() functions return only a db_status code. This code is effectively nothing more than a signed integer.

db_free_result() returns nothing.

ERRORS
The functions in the nis_db library may return any of the following status codes:

[DB_SUCCESS]
    The query or operation completed successfully.

[DB_NOTFOUND]
    The specified name or entry was not found.

[DB_NOTUNIQUE]
    An attempt was made to remove an entry from a table that was not uniquely specified.

[DB_BADQUERY]
    The query submitted to the database was invalid (i.e. nonexistent fields were specified, an invalid table was specified, the attributes array was invalid or corrupt).

[DB_BADTABLE]
    The table was corrupted.

[DB_BADOBJECT]
    The fields of the specified entry do not conform to the fields of the table to which it is being added.

[DB_MEMORY_LIMIT]
    The database was unable to complete the requested operation due to a lack of available memory.

[DB_STORAGE_LIMIT]
    The database was unable to complete the requested operation due to a lack of disk space.

[DB_INTERNAL_ERROR]
    The database was unable to complete the requested operation due to some internal error (either a programming error or an unrecoverable exception).

STANDARDS
There are no standards with respect to NIS+. Sun is making everything up as they go.

COMPATIBILITY
This implementation of the nis_db library meets the specifications described in the Solaris 2.5.1 nis_db(3n) manual page however it has not been tested as a direct replacemnt for libnisdb.so on a Solaris system.

When asked to destroy a table that does not exist, the Solaris version of db_destroy_table() returns a db_status code of 9. This value is not defined anywhere in the NIS+ headers and its meaning is therefore unknown. This implementation of the library returns DB_BADTABLE instead.

BUGS
Deleting entries from committed XFF table files is something of an arduous task. RCM indexes entries in terms of their offset within an XFF file (in other words, RCM indexes are really seek pointers). If an entry is deleted and the XFF file compacted to reclaim the unused space, all the seek pointers that refer to entries after the deleted one must be fixed up to account for the deletion. While this is not hard to accomplish, it requires traversing all of the RCM indexes in order to find those which need to be updated.

SEE ALSO
nis_tables(3), nis(4)

The TI-RPCSRC 2.3 source code distribution.

HISTORY
This implementation of the nis_db library was written for and is scheduled to appear in a future release of FreeBSD 2.x as part of a complete, freely available NIS+ client and server package. This library was written entirely from scratch: no Sun code other than the publically available NIS+ header files was referenced.

AUTHOR
Bill Paul <wpaul@ctr.columbia.edu>