nis_db.c revision 49e7ca4919cec3229f6fab9730bafc7cf24dab23
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (the "License"). You may not use this file except in compliance
* with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Ported from
* "@(#)nis_db.c 1.42 91/03/21 Copyr 1990 Sun Micro";
*
* This module contains the glue routines between the actual database
* code and the NIS+ server. Presumably they are the routines that should
* be exported in the shared library, but they may be at too high a level.
*/
#include <stdio.h>
#include <syslog.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <malloc.h>
#include "nis_proc.h"
#include <lber.h>
#include <ldap.h>
#include "ldap_xdr.h"
#include "ldap_util.h"
#include "nisdb_mt.h"
#include "ldap_parse.h"
extern bool_t xdr_nis_object();
extern bool_t xdr_nis_name();
extern bool_t xdr_nis_oid();
extern char *relative_name();
extern db_status __db_defer(char *);
extern db_status __db_commit(char *);
extern db_status __db_rollback(char *);
extern db_status __db_configure(char *);
int nisPlusStat, int ldapStat,
int nisPlusStat, int ldapStat,
static int clear_checkpoint_list_nolock(void);
typedef struct table_list_entry {
char *table;
struct table_list_entry *next;
static table_list_entry_t *table_list = 0;
static int mark_for_sync(char *);
/*
* Locking of the database is implemented here. We use a two level locking
* mechanism:
*
* (1) Directories
*
* Write lock Must have full access to the directory,
* and prevent any other access.
*
* Read lock Make sure we get to do our stuff without
* interaction with someone that may modify
* the directory.
*
* (2) Tables
*
* Write lock Any operation that modifies the table or
* data in the table.
*
* Read lock Any other operation on the table.
*
* Much of this functionality relies on the implicit locking and condition
* signaling provided by our private implementation of NIS_HASH_TABLEs.
*/
/*
* If the database is MT safe, 'RO' should be '1' (meaning, really
* do just read-only locking); otherwise, it should be '-1' to always
* use exclusive locks.
*/
#define RO 1
#define WR -1
/* Stored in the 'dirlocks' list */
typedef struct {
} dirlock_t;
/* Stored in the 'tablelocks' list */
typedef struct {
} tablelock_t;
{ \
0 : 1; \
status = NIS_SUCCESS; \
if (trylock == -1) { \
status = NIS_TRYAGAIN; \
} else { \
status = NIS_SYSTEMERROR; \
} \
} \
}
{ \
0 : 1; \
status = NIS_SUCCESS; \
if (trylock == -1) { \
status = NIS_TRYAGAIN; \
} else { \
status = NIS_SYSTEMERROR; \
} \
} \
}
{ \
0 : 1; \
if (trylock == -1) { \
status = NIS_TRYAGAIN; \
} else { \
status = NIS_SYSTEMERROR; \
} \
return (res); \
} \
}
static const char *rootname = ".";
static char *
*(++name) == '\0') {
}
return (name);
}
void *
"%d: db directory %s lock deferred \"%s\"",
return (0);
}
/* Doesn't exist; create one */
return (0);
}
return (0);
}
readwrite) == 0) {
/*
* nis_insert_item() => 0 could mean that the item
* already exists (i.e., someone else created it),
* so we'll try to find it one last time.
*/
return (0);
}
}
name));
}
return (dirlock);
}
int
readwrite, 0)) != 0) {
/* Only remove if table list is empty and not in defer mode */
if (item != 0) {
}
"%d: db dir %s lock removed \"%s\"",
} else {
/*
* The __nis_lock_db_directory() call incremented the
* refcount on the item, and we've just incremented it
* again via __nis_find_item_mt(). Thus, we double the
* refcount to be released for __nis_release_item.
*/
2*readwrite)) {
"%d: db dir %s lock released \"%s\"",
} else {
"%d: error releasing dirlock item for \"%s\"",
pthread_self(), name));
}
}
return (1);
}
return (0);
}
/*
* Note that the 'trylock' semantics only apply to the directory in which
* the table resides, not to the table itself.
*/
int
name)) == 0)
return (0);
return (0);
}
return (0);
}
/* Some other thread may have created it */
name);
return (0);
}
}
/* If directory is in defer mode, make the table so as well */
} else {
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
"%d: defer DB error %d for \"%s\"",
pthread_self(), stat,
}
}
pthread_self(), name));
}
/*
* We've got the item, so we're done. Hang on to the read-only
* directory lock so that an exclusive access to the directory
* (probably a checkpoint) must wait for us to be done with the table.
*/
return (1);
}
int
/*
* At this point, we should be holding a read-only lock on the
* directory entry, so we just obtain it without any additional
* locking.
*/
if (dirlock == 0) {
return (0);
}
&dirlock->tablelocks);
if (item != 0) {
}
"%d: removed db table lock \"%s\"",
pthread_self(), tablename));
} else {
/*
* Normally, if the directory is in defer mode, the
* table would have been put in defer mode during the
* table lock. However, if the operation that acquired
* the lock also created the table, it couldn't be
* deferred back then, so we do it now instead.
*/
db_status s;
if (s == DB_SUCCESS) {
} else if (s != DB_NOTFOUND) {
"%d: defer DB error %d for \"%s\"",
pthread_self(), s,
}
}
"%d: released db %s table lock \"%s\"",
}
}
"%d: could not find %s lock (unlock) \"%s\"",
}
/*
* Set the directory (and any known tables) to be deferred.
* If there's a failure attempting to defer a table, we try
* to rollback those deferred so far, and return the failure.
*/
0)
return (DB_NOTFOUND);
/*
* If the directory isn't already in defer mode, make it so.
*/
tablelock_t *t;
db_status s;
/* Traverse table locks and set defer mode */
t != 0 && stat == DB_SUCCESS;
/*
* DB_NOTFOUND is OK; the tablelocks list is
* out of date. Don't mark the table deferred,
* though, because it doesn't exist in the DB.
*/
if (s == DB_SUCCESS) {
} else if (s != DB_NOTFOUND) {
/* Remember the first failure */
if (stat == DB_SUCCESS)
stat = s;
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
"%d: defer DB error %d for table \"%s\"",
pthread_self(), s,
}
}
}
if (stat != DB_SUCCESS) {
/* Try to rollback deferred tables */
t != 0;
if (s == DB_SUCCESS) {
} else {
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
"%d: rollback DB error %d for table \"%s\"",
pthread_self(), s,
}
}
}
}
}
"%d: db_defer: error unlocking \"%s\"",
pthread_self(), name));
}
return (stat);
}
static db_status
tablelock_t *t;
return (DB_BADQUERY);
return (DB_NOTFOUND);
/* Commit or rollback deferred table changes */
t != 0;
db_status s;
else
/*
* DB_NOTFOUND is OK; just means that the tablelocks
* list is out-of-date.
*/
if (s == DB_SUCCESS || s == DB_NOTFOUND) {
} else {
/* Keep first err */
if (stat == DB_SUCCESS)
stat = s;
"%d: %s DB error %d for table \"%s\"",
pthread_self(),
}
}
}
/*
* Even if there were errors un-deferring one or more of the tables,
* we still want to set the directory undeferred. Otherwise, we'd
* continue to defer any new (meaning a tablelocks entry added)
* tables.
*/
"%d: db_undefer: error unlocking \"%s\"",
pthread_self(), name));
}
return (stat);
}
}
}
/*
* Free resources associated with a db_result structure
*/
static void
{
int i;
if (dr == 0)
return;
/* Can't have valid objects */
return;
}
}
static void
{
/* We do not free obj here because it is cached in table_cache */
}
static nis_error
{
switch (dstatus) {
case DB_SUCCESS:
return (NIS_SUCCESS);
case DB_NOTFOUND:
return (NIS_NOTFOUND);
case DB_BADTABLE:
return (NIS_NOSUCHTABLE);
case DB_MEMORY_LIMIT:
return (NIS_NOMEMORY);
case DB_STORAGE_LIMIT:
return (NIS_NOFILESPACE);
case DB_NOTUNIQUE:
return (NIS_NAMEEXISTS);
case DB_BADQUERY:
return (NIS_BADREQUEST);
case DB_BADOBJECT:
return (NIS_BADOBJECT);
case DB_INTERNAL_ERROR:
default:
return (NIS_SYSTEMERROR);
}
}
/*
* This function converts the internal format entries of the DB into
* a list of nis_objects that the server understands. The object returned may
* be destroyed with nis_destroy_object();
*
* Notes : When listing directories the list function expects to see entry
* objects and this function will mangle regular objects into entry
* objects. The entry has one column which contains the binary
* equivalent of what the type would have been if it hadn't been
* mangled.
*
* When dumping directories we need the objects intact so we set mangle
* to false and return the real objects.
*/
static obj_list *
int *got,
int mangle) /* Make non-entry objects into psuedo entries */
{
int status, /* XDR op status */
curr_obj, /* Current Object */
i, j, mc;
unsigned long etype; /* for fake entries */
*got = 0; /* Number of objects decoded */
/* Do a db_lookup() so that cache is populated */
else
/* dbres is freed automatically during cleanup */
} else {
}
if (te != 0)
return (NULL);
}
curr_obj = 0;
for (i = 0; i < num; i++) {
if (! ep[i]) {
"cvt2object: NULL Object in database, ignored.");
continue;
}
/*
* Set up a memory stream pointing at the first column value
* which contains the XDR encoded NIS+ object. The second
* column contains the name of the NIS+ object.
*/
/* I'll return with the current list of objects */
if (te != 0)
return (oa);
}
/*
* Decode it into the object structure. If some fields
* are NULL, fill in appropriate values from the table
* nis_object structure.
*
* If the entry object has type "IN_DIRECTORY" then it
* is a NIS+ directory we are listing else it is
* an ENTRY Object. For ENTRY objects, we call our
* special xdr_nis_fetus_object() which knows how to
* reconstruct the entry object from the given info.
* XXX: _any_ other value we are hosed. If it is 0 or a
* NULL string, it denotes that it is an entry object.
*/
else
/*
* POLICY : What to do about undecodeable objects ?
* ANSWER : Leave it blank and continue. (soft failure)
*/
if (! status) {
"cvt2object: Corrupted object ('%s') in database %s",
continue;
}
/*
* If the entry object has type 0 or "IN_TABLE" then it
* is an entry object. If it has type "IN_DIRECTORY" then it
* is a NIS+ directory that we are listing.
* XXX: _any_ other value we are hosed.
*/
continue;
}
/*
* Set the column fields appropriately. Copy all the
* col entry pointers to the new entry object. We are
* mucking around with the list returned by
* db_list_entries - UNCLEAN, UNCLEAN!
*/
(sizeof (entry_col)));
if (te != 0)
RO);
return (oa);
}
for (j = 1; j < mc; j++) {
}
/* We set len to 1, so that other cols are not freed */
} else if (mangle) {
/* Convert this dir object into a "fake" entry object */
/* first free the object specific data */
/* Now fake a entry object */
if (te != 0)
RO);
return (oa);
}
if (te != 0)
RO);
return (oa);
}
if (te != 0)
RO);
return (oa);
}
}
(*got)++; /* Add one to the total */
}
if (te != 0)
return (oa);
}
/*
* Release hash item. Intended for use by the cleanup code, executed
* when the thread is about to return to the RPC dispatch.
*/
void
tableCacheReleaseRO(void *ptr) {
if (te != 0)
}
/*
* Destroy a table cache item.
*/
void
destroyTableCacheItem(void *item) {
if (ti != 0) {
}
}
/*
* Return value if allocation of nis_db_result return value fails. Must
* not be freed, of course, but that's OK since callers already expect
* that to happen through the cleanup code.
*/
/*
* The following routines implement the database operations on the namespace.
* The database only understands tables, so it is up these routines to
* "fake out" the database and to build a table that defines the namespace
* that we are serving. The format of this table is fixed. It consists of
* a table with two columns, the first being the object (XDR encoded)
* and the second being the "name" of the object.
*
* Note : All of these routines assume they are passed "full" NIS+ names
* Note : The fake entry structure is the way it is to be compatible with
* the cvt2object() function above.
*
* Lookup NIS+ objects in the namespace that are stored as entries in a
* table by the same name as the directory being served. These entries
* have two columns, column 0 is the object data and column 1 is the
* object name.
*/
{
int status;
char *table; /* The table path name */
int triedLDAP;
char *myself = "db_lookup_deferred";
if (verbose)
return (&__no_mem_nis_db_result);
if (!table_cache) {
table_cache = (NIS_HASH_TABLE *)
if (!table_cache) {
return (res);
}
}
/*
* Now we check the cache to see if we have it cached locally.
*/
if (te) {
if (verbose)
/* Defer release of item */
return (res);
}
/* This happens when we're looking for directory objects. */
if (! table) {
return (res);
}
if (verbose)
__start_clock(1);
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
triedLDAP = 0;
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
break;
} else {
int rd;
break;
/* Unlock while we (possibly) sleep */
if (rd) {
/*
* If we failed because there was
* no such entry in the directory,
* try to get the entry from LDAP.
* If successful, look in the dir
* again.
*/
NIS_NOTFOUND) {
int lstat;
char *intName;
triedLDAP = 1;
name);
if (intName == 0) {
break;
}
do {
tsd->nisPlusStat =
dstat =
rd = retrieveDone(
} while (!rd);
if (dstat == DB_SUCCESS)
continue;
else if (lstat ==
"%s: no LDAP data for \"%s\"",
name);
} else if (lstat !=
LDAP_SUCCESS) {
"%s: LDAP error for \"%s\": %s",
}
}
break;
}
}
}
}
/* ASSERT(dbres->objects.objects_len == 1); */
/*
* convert from XDR format that the DB returns to
* the nis_object format
*/
else {
if (! status) {
name);
}
}
}
int doRead;
__nis_buffer_t b = {0, 0};
/*
* If the object is read-mapped from LDAP, we don't want
* to cache it in the table_cache.
*/
useDeferred &&
!doRead) &&
/*
* Cache the table objects in the "org_dir"
* dir. We want to cache only the "org_dir" tables
* instead of caching all zillions of tables.
*/
if (te) {
"db_lookup result");
} else {
(void) __nis_insert_item_mt(te,
table_cache, 0);
if (verbose)
"Added %s to the table cache",
name);
}
} else {
}
} else {
}
if (b.buf != 0)
}
return (res);
}
}
/*
* __db_add()
*
* The internal version of db_add, this one doesn't add an entry into
* the log. This function converts the nis_object into a DB style object
* and then adds it to the database.
*/
int modop) /* Modify operation flag */
{
int i;
char *table;
char *myself = "__db_add";
if (verbose)
"__db_add: Unable to create database for NIS+ table %s: %s.",
return (res);
}
}
if (! table) {
return (NIS_BADNAME);
}
buf = __get_xdr_buf(i);
if (! i) {
return (NIS_SYSTEMERROR);
}
/* Now we cons up an entry for this object in the name space */
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
res = NIS_NOMEMORY;
break;
} else {
int rd;
status);
if (res == NIS_SUCCESS &&
break;
/* Unlock while we (possibly) sleep */
if (rd)
break;
}
}
}
switch (res) {
case NIS_NOMEMORY:
case NIS_NOFILESPACE:
case NIS_SYSTEMERROR:
case NIS_UNAVAIL:
break; /* these are expected */
case NIS_SUCCESS:
if (modop) {
/* Flush various cached objects */
case NIS_TABLE_OBJ:
break;
case NIS_DIRECTORY_OBJ:
break;
case NIS_GROUP_OBJ:
break;
default:
break;
}
}
(void) mark_for_sync(table);
break;
default:
break;
}
if (res == NIS_SUCCESS) {
}
return (res);
}
/*
* db_add()
*
* External wrapper for the real db_add function. This one creates a
* transaction log entry. The internal one skips the log transaction.
*/
int mod_op)
{
if (verbose) {
if (mod_op == 0)
else
}
if (mod_op)
else
add_update(&le);
__start_clock(1);
return (&res);
}
{
char *table;
int table_deleted = 0;
char *myself = "__db_remove";
if (! table) {
return (NIS_BADNAME);
}
/* First make sure the table is empty */
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
break;
} else {
int rd;
if (err == NIS_SUCCESS &&
break;
if (rd)
break;
}
}
}
return (NIS_NOMEMORY);
return (NIS_NOTEMPTY);
"__db_remove: Unable to destroy table %s: %s.",
else
table_deleted = 1;
} else {
/*
* POLICY : What should we do, remove the object?
* or abort() because the database is
* inconsistent.
* ANSWER : Notify the system operator, and continue
* to remove the table object.
*/
"__db_remove: table %s not in dictionary (err=%d)",
"__db_remove: Unable to destroy table %s: %s.",
else
table_deleted = 1;
}
}
if (! table) {
return (NIS_BADNAME);
}
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
res = NIS_NOMEMORY;
break;
} else {
int rd;
status);
if (res == NIS_SUCCESS &&
break;
if (rd)
break;
}
}
}
switch (res) {
case NIS_NOMEMORY:
case NIS_NOFILESPACE:
case NIS_SYSTEMERROR:
case NIS_UNAVAIL:
break; /* these are expected */
case NIS_SUCCESS:
/* Flush various cached objects */
case NIS_TABLE_OBJ:
break;
case NIS_DIRECTORY_OBJ:
break;
case NIS_GROUP_OBJ:
break;
default:
break;
}
(void) mark_for_sync(table);
break;
default:
"__db_remove: unexpected result from database %d",
break;
}
if (res == NIS_SUCCESS) {
}
name);
return (res);
}
{
if (verbose)
name);
add_update(&le);
__start_clock(1);
return (&res);
}
static nis_db_list_result __no_memory_db_list_result = {
0, 0, 0
};
/*
* db_list(table, numattrs, attrs)
*
* function to call the database list function. When called we know that
* object is "readable" by the principal.
*
*/
static nis_db_list_result *
int na, /* Number of attributes */
nis_attr *a, /* An array of attributes */
{
int i;
char *table; /* internal table name */
int got;
int nm = 0;
nis_object *o;
char *myself = "db_list_x";
if (res == 0)
return (&__no_memory_db_list_result);
}
got = 0;
if (! table) {
return (res);
}
if (verbose)
__start_clock(1);
return (res);
}
/* this happens on top directory in server's database */
} else {
return (res);
}
/* check for multival searches */
if (tobj) {
if (err != NIS_SUCCESS) {
return (res);
}
}
/*
* If we have regular attributes or if we have no attributes
* at all (na == 0 && nm == 0), search the normal way.
*/
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
break;
} else {
int rd;
LDAP_SUCCESS &&
tsd->nisPlusStat ==
break;
name);
if (rd)
break;
}
}
}
/* Process the entries into "objects" */
case NIS_NOMEMORY:
break;
case NIS_SUCCESS:
case NIS_CACHEEXPIRED:
/* Convert from database format to NIS+ object format */
if (got > 0) {
} else {
}
break;
case NIS_NOTFOUND:
case NIS_TRYAGAIN:
case NIS_UNAVAIL:
case NIS_NOSUCHNAME:
break;
default:
for (i = 0; i < na; i++) {
if (i != 0)
a[i].zattr_val.zattr_val_val,
a[i].zattr_val.zattr_val_len);
}
"Database search failed on %s, status = %d",
break;
}
if (verbose)
"db_list: returning status = %d, entries = %d",
} else {
}
/*
* If we have multival attributes and regular attributes,
* filter out the objects gotten above that don't match
* on the multival columns. If there were no regular
* attributes, then list based only on the multival columns.
*/
if (nm) {
if (na) {
}
}
} else {
/* na = 0 */
}
}
return (res);
}
int na, /* Number of attributes */
nis_attr *a) /* An array of attributes */
{
}
int na, /* Number of attributes */
nis_attr *a, /* An array of attributes */
{
}
/*
* This function has to release all of the components that were allocated
* by db_list above.
*/
void
{
int i;
return;
}
}
static nis_fn_result __no_memory_fn_result = {
0, {0}, 0
};
/*
* nis_fn_result *db_firstib(name)
*
* Get the "first" entry from a table. Note this function returns an opaque
* "cookie" that has what ever state the underlying database needs to
* find the next entry in a chain of entries. Since it is specific to the
* underlying database it is opaque to this interface.
*/
int na, /* Number of attributes */
nis_attr *a, /* Attribute list */
int flags, /* Mangle objects into entries */
char *table)
{
int got;
char *myself = "db_firstib";
if (res == 0)
return (&__no_memory_fn_result);
}
if (! table)
if (! table) {
return (res);
}
if (verbose)
table);
__start_clock(1);
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
break;
} else {
int rd;
break;
if (rd)
break;
}
}
}
case NIS_NOMEMORY:
case NIS_NOTFOUND:
case NIS_TRYAGAIN:
case NIS_UNAVAIL:
case NIS_NOSUCHNAME:
break; /* expected */
case NIS_BADREQUEST:
if ((flags & FN_NOERROR) == 0)
"db_firstib: Table: '%s', Bad Attribute",
name);
break;
case NIS_NOSUCHTABLE:
if ((flags & FN_NOERROR) == 0)
"db_firstib: Missing table '%s'", name);
break;
case NIS_SUCCESS:
case NIS_CACHEEXPIRED:
/* ASSERT(dbres->objects.objects_len == 1); */
/* Convert the entry into a NIS+ object */
} else {
}
/* Now clone the nextinfo cookie */
}
break;
default:
if ((flags & FN_NOERROR) == 0)
"db_firstib: Unexpected database result %d.",
break;
}
/* Don't need this anymore */
if (verbose)
return (res); /* Return it finally */
}
/*
* nis_fn_result *db_nextib(name, cookie)
*
* Get the "next" entry from a table.
*/
int flags,
char *table)
{
int got;
char *myself = "db_nextib";
if (res == 0)
return (&__no_memory_fn_result);
}
if (! table)
if (! table) {
return (res);
}
if (verbose)
__start_clock(1);
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (dbres == 0) {
break;
} else {
int rd;
break;
if (rd)
break;
}
}
}
case NIS_NOMEMORY:
case NIS_NOTFOUND:
case NIS_TRYAGAIN:
case NIS_UNAVAIL:
case NIS_NOSUCHNAME:
break;
case NIS_BADREQUEST:
if ((flags & FN_NOERROR) == 0)
"db_nextib: Bad Attribute in Table: '%s'",
name);
break;
case NIS_NOSUCHTABLE:
if ((flags & FN_NOERROR) == 0)
"db_nextib: Missing table object '%s'",
name);
break;
case NIS_SUCCESS:
case NIS_CACHEEXPIRED:
/* ASSERT(dbres->objects.objects_len == 1); */
} else {
}
/* Now clone the nextinfo cookie */
break;
default:
if ((flags & FN_NOERROR) == 0)
"db_nextib: Unexpected database result %d",
break;
}
/* Don't need this anymore */
if (verbose)
return (res); /* Return it finally */
}
/*
* db_flush()
*
* Flush any pending "next" entries from a list returned by firstib.
*/
void
{
char *table;
if (! table)
return;
if (verbose)
table);
{
int trylock = 1;
return;
}
}
}
}
/*
* Add an entry to a table, we assume we've already sanitized the
* data.
*/
static nis_error
int numattr, /* Number of attributes */
int skiplog, /* if true, don't log action */
int nosync)
{
*ec; /* our objects columns */
char *table;
char *myself = "__db_addib_x";
/* Get the number of columns and the pointer to their data */
return (NIS_NOMEMORY);
}
if (! table)
return (NIS_BADNAME);
if (verbose)
table);
/* Do a db_lookup() so that cache is populated */
"__db_addib: could not find table object for %s",
name);
return (NIS_NOSUCHTABLE); /* XXX: Error number */
}
/* dbres is freed automatically during cleanup */
} else
/* Build up our temporary entry object */
/* Copy the entry value pointers, offset by 1 */
for (i = 0; i < mc; i++)
/*
* To prevent the XDR function from making a copy of
* the entry columns, we set the columns structure to
* 0 (ie no column data)
*/
/* Make a fetus object from a FULL object */
if (te != 0)
return (NIS_NOMEMORY);
}
if (te != 0)
return (NIS_SYSTEMERROR);
}
/* Un-neuter it so that it can be properly freed */
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (skiplog)
else if (nosync)
else
if (dbres == 0) {
res = NIS_NOMEMORY;
break;
} else {
int rd;
status);
if (res == NIS_SUCCESS &&
break;
/* Unlock while we (possibly) sleep */
if (rd)
break;
}
}
}
switch (res) {
case NIS_NOMEMORY:
case NIS_NOFILESPACE:
case NIS_SYSTEMERROR:
case NIS_UNAVAIL:
break;
case NIS_SUCCESS:
(void) mark_for_sync(table);
break;
default:
break;
}
if (verbose)
if (res == NIS_SUCCESS) {
}
if (te != 0)
return (res);
}
int numattr, /* Number of attributes */
{
}
int numattr, /* Number of attributes */
{
}
int numattr, /* Number of attributes */
{
}
/*
* Add an entry to a table, we assume we've already sanitized the
* data.
*/
int numattr, /* Number of attributes */
{
/*
* First we create a fully specified entry list, with a
*/
if (res == 0)
return (&__no_memory_db_result);
return (res);
}
na++;
}
}
add_update(&le);
__start_clock(1);
return (res);
}
int nattr,
int nosync)
{
char *table;
char *myself = "__db_remib_x";
if (! table)
return (NIS_BADNAME);
if (verbose)
"__db_remib: Removing an entry from table %s", table);
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
/* Establish default status (success) */
if (nosync)
attrs);
else
if (dbres == 0) {
res = NIS_NOMEMORY;
break;
} else {
int rd;
status);
if (res == NIS_SUCCESS &&
break;
/* Unlock while we (possibly) sleep */
if (rd)
break;
}
}
}
switch (res) {
case NIS_NOMEMORY:
case NIS_NOFILESPACE:
case NIS_SYSTEMERROR:
case NIS_UNAVAIL:
break;
case NIS_SUCCESS:
(void) mark_for_sync(table);
break;
case NIS_NOTFOUND:
/* this occurs when applying log updates */
res = NIS_SUCCESS;
break;
default:
break;
}
if (verbose)
if (res == NIS_SUCCESS) {
}
return (res);
}
int nattr,
{
}
int nattr,
{
}
/*
* The remove operation. Since it can remove multiple entries we pass it
* the list for the log entries.
*/
int nattrs,
int numo,
{
int *amap;
/*
* First we create a fully specified entry list, with a
*/
if (res == 0)
return (&__no_memory_db_result);
na = 0; /* Actual attrs */
/* Cheat and allocate a "static" array */
return (res);
}
na++;
}
}
/* Add log updates for all of the entries that will be removed */
for (i = 0; i < numo; i++) {
for (j = 0; j < na; j++) {
}
add_update(&le);
}
__start_clock(1);
name);
return (res);
}
/*
* __create_table()
*
* Create the underlying database table that supports the current database.
*/
static nis_error
char *table,
{
int i;
/* Assume locking done by caller */
return (NIS_NOMEMORY);
for (i = 0; i < t->ta_cols.ta_cols_len; i++) {
break;
}
if (i == t->ta_cols.ta_cols_len) {
if (verbose)
"Cannot create table: %s: no searchable columns.", table);
return (NIS_BADOBJECT);
}
/* Copy the important parts of the table structure over */
/* Now shift the columns right by 1 */
}
}
/*
* do_checkpoint
*
* This function checkpoints the table named, or all tables
* that the server has if no table is named.
*
* NB: This can be time consuming so the server should fork
* a readonly child to handle requests while we're out here.
*
* This function returns NIS_SUCCESS if all tables were
* checkpointed and return NIS_PARTIAL otherwise.
*/
static cp_result *
{
int status;
char *table;
if (! res)
return (&mem_err);
else
if (verbose)
if (table == 0)
name = ".";
__start_clock(1);
if (status != DB_SUCCESS) {
"db_checkpoint: Unable to checkpoint table '%s' because %s",
} else
return (res);
}
/*
* do_checkpoint_dir
*
* This function checkpoints all of the tables in a named directory
* and the directory itself.
* This is accomplished by iterating over the directory and then
* checkpointing anything that looks like a table.
*
* NB: This can be time consuming so the server should have forked
* a read only child to handle requests while we're out here.
*
* This function returns returns NIS_SUCCESS if all tables were
* checkpointed and returne NIS_PARTIAL if only some of them were.
*/
static cp_result *
{
char *table;
char tblname[NIS_MAXNAMELEN];
if (! name)
return (NULL); /* name is required */
if (! res)
return (&mem_err);
if (verbose)
err = 0;
/* First, do directory itself. */
__start_clock(1);
if (verbose)
table);
if (status != DB_SUCCESS) {
"db_checkpoint: Unable to checkpoint '%s' because %s",
status)));
err++;
}
} else {
"db_checkpoint: Unable to checkpoint '%s' because %s",
err++;
}
}
/* Do each table or directory within directory. */
__start_clock(1);
if (verbose)
table);
tblname);
/*
* We ignore DB_BADTABLE errors because we might
* not serve the contents of the directory.
*/
if (status != DB_SUCCESS &&
status != DB_BADTABLE) {
"db_checkpoint: Unable to checkpoint '%s' because %s",
status)));
err++;
}
} else {
"db_checkpoint: Unable to checkpoint '%s' because %s",
err++;
}
}
/* note the call to nextib frees the old cookie! */
}
if (err)
else
return (res);
}
/*
* nis_checkpoint_svc maintains a list of directories to be
* checkpointed, call do_checkpoint_dir on items on that list.
* Otherwise, call do_checkpoint(NULL) to checkpoint entire database.
*/
int
{
/* No directories specified; checkpoint entire database. */
checkpoint_all = 0;
/* res is on cleanup list; no need to free. */
return (1);
}
"checkpoint_db: unable to completely checkpoint %s because %s",
}
/* ignore status. Can try checkpoint later if not complete. */
}
return (1);
}
int
checkpoint_table(char *name)
{
char *table;
int ret = 0;
{
}
if (status != DB_SUCCESS) {
"checkpoint_table: Unable to checkpoint table %s because %s",
} else {
ret = 1;
}
return (ret);
}
/*
* Remove all items from checkpoint list. List must be locked by caller.
*/
static int
{
}
return (1);
}
int
clear_checkpoint_list(void) {
int ret;
return (ret);
}
/*
* Returns NIS_SUCCESS if table exists; NIS_NOSUCHTABLE if it does not exist.
* Otherwise, return error returned by database.
*/
{
if (!table) {
return (NIS_BADNAME);
}
{
}
return (map_db_status_to_nis_status(dstatus));
}
{
char *myself = "db_create";
if (! table) {
return (NIS_BADNAME);
}
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
int rd;
/* Establish default status (success) */
if (dstatus == DB_SUCCESS &&
break;
if (rd)
break;
}
}
if (dstatus == DB_SUCCESS)
(void) mark_for_sync(table);
return (err);
}
{
char *myself = "db_destroy";
if (! table) {
return (NIS_BADNAME);
}
{
if (tsd == 0) {
"%s: No memory for TSD; unable to get full status for DB operation",
myself);
}
while (1) {
int rd;
/* Establish default status (success) */
if (dstatus == DB_SUCCESS &&
break;
if (rd)
break;
}
}
if (dstatus == DB_SUCCESS)
(void) mark_for_sync(table);
return (err);
}
static int
mark_for_sync(char *table) {
int ret = 0;
break;
}
table_list = te;
} else if (te == 0) {
"mark_for_sync: could not add sync entry for \"%s\"\n", table);
}
return (ret);
}
int
nis_db_sync_log(void) {
int ret = 0;
while ((te = table_list) != 0) {
if (verbose)
"nis_db_sync_log: error %d syncing \"%s\"",
ret++;
}
}
return (ret);
}
db_configure(char *table) {
return (map_db_status_to_nis_status(stat));
}
/*
* Given an input NIS+ and LDAP status, perform appropriate actions
* according to retrieveError/refreshError. May set '*outStat'.
*/
int
if (ldapStat == LDAP_NO_SUCH_OBJECT) {
*outStat = NIS_NOTFOUND;
return (1);
} else if (ldapStat != LDAP_SUCCESS) {
/* Tried LDAP, didn't work => retrieve error */
/* Use refresh error actions */
if ((ldapDBTableMapping.refreshError ==
nisPlusStat == NIS_CACHEEXPIRED) {
*outStat = NIS_SUCCESS;
return (1);
} else if (ldapDBTableMapping.refreshError ==
ref_retry ||
return (!__nis_retry_sleep(refreshRetry, 0));
} else if (ldapDBTableMapping.refreshError ==
return (1);
} else {
*outStat = NIS_TRYAGAIN;
return (1);
}
return (!__nis_retry_sleep(retrieveRetry, 0));
*outStat = NIS_TRYAGAIN;
return (1);
*outStat = NIS_UNAVAIL;
return (1);
return (1);
}
} else if (nisPlusStat != NIS_SUCCESS) {
if (nisPlusStat == NIS_CACHEEXPIRED &&
*outStat = NIS_SUCCESS;
else
*outStat = nisPlusStat;
return (1);
}
/* Both NIS+ and LDAP status OK => done */
return (1);
}
/*
* Given an input NIS+ and LDAP status, perform appropriate actions
* according to storeError. May set '*outStat'.
*/
int
if (ldapStat != LDAP_SUCCESS) {
return (!__nis_retry_sleep(storeRetry, 0));
return (1);
*outStat = NIS_UNAVAIL;
return (1);
}
} else if (nisPlusStat != NIS_SUCCESS) {
*outStat = nisPlusStat;
return (1);
}
/* Both NIS+ and LDAP status OK => done */
return (1);
}
extern int loadAllLDAP(int, void *, db_status *);
/*
* Up-/down-loads LDAP data, depending on the value of
* ldapConfig.initialUpdate. Returns 0 if the rpc.nisd
* can start serving data, 1 if it should exit, -1 if
* there was an error during the load.
*/
int
loadLDAPdata(void) {
void *prevCookie, *cookie = 0;
char *myself = "loadLDAPdata";
while (1) {
switch (ldapConfig.initialUpdate) {
case ini_none:
return (0);
case from_ldap:
case from_ldap_update_only:
"Down-loading data from LDAP");
prevCookie = cookie;
break;
case to_ldap:
case to_ldap_update_only:
"Up-loading data to LDAP");
prevCookie = cookie;
break;
default:
"%s: Unknown initial update option %d",
return (-1);
}
/*
* If we're done (rd != 0), tell the caller if the rpc.nisd
* should exit or continue running.
*/
if (rd) {
"%s: LDAP status 0x%x, DB status %d",
return (-1);
}
switch (ldapConfig.initialUpdate) {
case from_ldap:
case to_ldap:
"LDAP load done; continuing");
return (0);
case from_ldap_update_only:
case to_ldap_update_only:
"LDAP load done; terminating");
return (1);
default:
"LDAP load internal error; terminating");
return (-1);
}
}
/*
* If the value of the cookie has changed, we've managed
* to move on to another mapping, so we reset the retry
* structures.
*/
if (cookie != prevCookie) {
}
}
/* NOTREACHED */
return (-1);
}