nis_subr_proc.c revision 7d85b7b5115de498e6f60a86dd08f5b4fff85f27
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
/*
*
* This module contains subroutines that are used by the NIS+ service but
* _not_ the NIS+ clients. NIS+ client routines are in the library module
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <stdio.h>
#include <syslog.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
#include <time.h>
#include <pwd.h>
#include <thread.h>
#include <rpc/auth_sys.h>
#include <rpc/auth_des.h>
#include <rpcsvc/nis_callback.h>
#include <rpcsvc/nis_dhext.h>
#include <errno.h>
#include <limits.h>
#include "nis_svc.h"
/* nis_proc.h includes nis_mt.h */
#include "nis_proc.h"
#include "log.h"
#include "ldap_parse.h"
#include "ldap_util.h"
#include "nisdb_mt.h"
extern void __nis_destroy_callback();
char *dname);
/* routines for registering server's availability wrt given dirctory */
extern nis_name __nis_local_root();
struct directory_item {
};
typedef struct directory_item dirItem_t;
DECLRWLOCK(dl);
/*
* Release cached directory object. Intended for use by the cleanup
* code, executed when the thread is about to return to the RPC dispatch.
*/
void
dirCacheReleaseRO(void *ptr) {
struct directory_item *d = ptr;
if (d != 0)
}
void
dirCacheReleaseWR(void *ptr) {
struct directory_item *d = ptr;
if (d != 0)
}
/*
* Called when function is done with the directory_item, but release
* should be deferred until the thread is done with the current RPC
* request, or exits.
*/
void
if (d != 0) {
if (wr) {
} else {
}
}
}
/*
* Destroy a directory cache item.
*/
void
destroyDirCacheItem(void *item) {
struct directory_item *d = item;
if (d != 0) {
if (d->dl_stamps != 0)
if (d->dl_obj != 0)
nis_destroy_object(d->dl_obj);
free(d);
}
}
/*
* free_dlitem()
*
* This function will remove a directory_item from the cache. The memory
* will be re-used when no thread is referencing it anymore.
*/
static void
struct directory_item *dd;
{
if (dd != 0)
}
/*
* 1. Flush server's local directory cache.
* 2. Flush cachemgr's cache, using one of the following
* a. given directory object
* b. if none given and directory is in local cache, use its dir object.
* c. if directory is not in local cache, look it up from cachemgr,
* if found, delete it.
* 3. Flush the associated table cache.
*/
void
{
struct directory_item *dd;
int cachemgr_updated = 0;
if (verbose)
/* Flush given directory object from cachemgr. */
if (dobj) {
cachemgr_updated = 1;
}
/* Update server's local directory cache. */
if (dl) {
if (dd) {
if (cachemgr_updated == 0) {
cachemgr_updated = 1;
}
}
}
/* Update cachemgr if have not done so yet. */
if (cachemgr_updated == 0) {
}
}
/*
* Since we cache the table for this dir object,
* we will flush that away as well.
*/
}
void
{
struct ticks t;
if (verbose)
/*
* Refresh it from the master.
* XXX: We may have some scalability problems
* if all servers go to the master at the same time. Hopefully
* directory objects would not be updated very frequently, so it
* should not be such a big problem.
*/
}
/*
* Flush all cached directory objects from dircache and cachemgr.
*/
void
{
if (verbose)
/*
* Since a destructor is in effect for this list,
* we have nothing to do; 'dd' will be freed when
* it's no longer in use.
*/
}
}
void
{
struct directory_item *dd;
if (verbose)
if (dl) {
if (dd) {
}
}
}
/*
* Flushes the local TABLE cache. Called when the database changes.
*/
void
{
struct table_item *ti;
if (verbose)
if (table_cache) {
/*
* We've established an item destructor for the table_cache,
* so the item will be destroyed automatically when it's
* no longer referenced.
*/
}
}
/*
* Flushes all TABLE caches.
*/
void
{
struct table_item *ti;
if (verbose)
if (table_cache) {
/*
* We've established an item destructor for the
* table_cache, so the item will be destroyed
* automatically when it's no longer referenced.
*/
}
}
}
/*
* Flushes a specific item from the group cache.
*/
void
{
if (verbose)
}
/*
* Flushes all group caches.
*/
void
{
if (verbose)
}
extern enum clnt_stat __nis_cast_proc();
/*
* A convenient wrapper around __nis_cast_proc().
*/
{
int i;
int nsrv;
return (FALSE);
for (i = 0; i < nsrv; i++) {
}
return (1);
}
/*
* We need this function, since the root object is stored as a directory
* obj structure and not a nis_object. directory_obj strucs don't have the
* domain name.
*
* Note: Synchronization of access to 'tbl' is the responsibility of the
* caller.
*/
void
char *buf;
{
if (pp) {
if (verbose)
switch (ldapConfig.updateBatching) {
case upd_none:
/*
* We're not accumulating updates, so we only allow
* the mtime to go backwards.
*/
}
break;
case bounded_accumulate:
/* Leave pp->mtime unchanged */
break;
case accumulate:
default:
}
break;
}
/*
* Reset update time and backoff delta, since it appears
* that we can talk to the master.
*/
return;
}
if (! pp) {
if (verbose)
"add_pingitem: Couldn't add '%s' to pinglist (no memory)",
buf);
return;
}
if (verbose)
"add_pingitem: Couldn't add '%s' to pinglist (no memory)",
buf);
return;
}
if (dir)
else
/* Update time (utime) == 0 means resync ASAP */
if (verbose)
}
void
{
char buf[1024];
return;
}
/*
* Only to be called from update_dl_item() via nis_scan_table()
*
* If the master server of the directory in the hash entry matches the
* master that isn't responding reset the expiry time to some point in
* the future. This is only applied to directories due to expire before
* the new expiry time.
*/
struct reset_dl_expires_args {
};
static bool_t
{
if (verbose)
"expiry for directory %s to %d",
} else {
if (verbose)
"directory %s, expiry %d is later",
}
} else {
if (verbose)
"directory %s as master (%s) doesn't match",
}
/* Return false as true would terminate the scan */
return (FALSE);
}
/*
* Updates directory item with given information.
* If directory requires update, add it to 'upd_list'.
* If directory is root directory, check whether root object requires
* update.
*/
static void
struct directory_item *dd,
enum name_pos p)
{
struct reset_dl_expires_args funcarg;
return;
/*
* Add it to the list of directories served by this
* server if not already listed.
*/
else { /* replica */
/*
* schedule update for directory contents but don't
* try too hard.
*/
/*
* If timestamp == 0, (i.e., failed to retrieve a timestamp
* from the master), force the directory object to expire 30
* minutes later so that we can check the master again. In
* the meantime, we will use the expired directory cache.
*
* We use nis_scan_table_mt() to reset the expiry time on
* all cached directories from this master server.
*/
if (timestamp == 0) {
if (verbose)
"unable to get timestamp for %s "
"from master %s",
if (verbose)
/* Update the hash list */
/*
* The current entry may not be on the hash
* list yet so we explicitly reset it here.
*/
return;
}
/*
* We have a valid timestamp from the master. Check and
* update upd_list accordingly.
*/
}
/* If root object, schedule update for object itself too */
((rootname = __nis_local_root()) != 0) &&
&upd_list);
}
}
}
/*
* These statistics are used to calculate the effectiveness of the
* directory object cache. You can retrieve them with a TAG_DIRCACHE
* in the stats call.
*/
int dircachecall = 0,
dircachehit = 0,
dircachemiss = 0;
int is_master; /* Need we be the master ? */
nis_object **obj;
{
}
stat ++; \
/*
* __directory_object()
* This function is used to search for directory objects that describe
* directories that this server serves. When called it will either
* return a directory object which describes a directory that is served
* or NULL if the directory is not served by this server.
* It takes three parameters, name, ticks and is_master. Name is
* presumed to be the NIS+ name of the directory we're looking for, ticks
* points to a ticks structure that we can fill in with some statistics
* and is_master indicates that not only should we serve the directory
* but also we must be the master server for that directory.
*/
int is_master; /* Need we be the master ? */
nis_object **obj;
int msg;
{
register struct directory_item *dd;
int lookup_flags = 0;
enum name_pos p;
int serve;
/*
* A sanity check, if nis_name_of() returns NULL then either the
* name passed is identical to the local directory (in which case
* we can't possibly serve it unless we are a root server of a
* local root), or the name isn't in our tree at all (shares
* no common path). In which case we can't possibly serve it.
*/
return (NIS_BADNAME); /* illegal name */
return (NIS_BADNAME);
/*
* XXX intermediate workaround, until the next source
* crank. The is_master "boolean" gets expanded here to
* be a set of bit flags. In particular, the new flag
* "NO_CACHE" is used to identify when the function calling
* this function wants it to retrieve a new copy of the
* directory object from the cache.
*/
/* get latest copy of directory object */
}
if (! dl) {
if (!dl) {
return (NIS_NOMEMORY);
}
}
/* some statistics stuff ... */
if (verbose)
/*
* Now we check the cache to see if we have it cached locally.
*/
/*
* If we found it in the cache, and we're asking for the
* "no cache" version, then we flush the cache and
* cause it to be re-looked up.
*/
}
if (dd) {
if (verbose)
/*
* Found it in the cache. First we check to see if it
* has expired, and then we check to see if we are the
* master server. (we must serve it, otherwise it wouldn't
* be in our cache!)
*
* Check the expiration date on the object
*/
dirDeferRelease(dd, 0);
return (NIS_SUCCESS); /* Here it is ... */
} else {
/* Should never happen */
return (NIS_NOT_ME);
}
dirDeferRelease(dd, 0);
return (NIS_SUCCESS); /* Here it is ... */
} else {
return (NIS_NOTMASTER); /* Not master */
}
}
/* Else it is expired so we're refreshing it. */
}
if (! dd) {
if (verbose)
}
/*
* At this point we either don't have it in our cache (dd == NULL)
* or it is expired (dd != NULL) so we have to go looking for it
* from either inside (our database) or outside (our parent).
* First, we check to see if we are a root server and they want
* the root directory.
*/
"__directory_object: internal database error.");
/*
* (db result-> no such table )
* Ask the "parent" of "name" since this should not
* recurse to the same server, we don't
* worry about screwing up the current
* server clock (clock(2)). But we will
* get ticks back from the other server
* so we return those in the ticks structure.
*/
if (dd) {
/* It no longer exists */
}
} else {
if (msg)
"__directory_object: Failed to lookup %s, status %s",
}
} else if (dd) {
/* It no longer exists */
}
/*
* At this point, if d_obj is null then we couldn't find
* it. We check to see if we have an expired cached version
* and if so, log a warning and go ahead and use it. If
* we don't have a cached version we just return NOTFOUND.
*/
if (! d_obj) {
if (dd) {
"__directory_object: Using expired directory object for %s", name);
dirDeferRelease(dd, 0);
return (NIS_S_SUCCESS);
} else {
return (NIS_NOT_ME);
}
} else
return (NIS_NOTFOUND);
}
/* If we found one, and it isn't a directory abort */
if (msg)
name);
/* If the cache had one toss it out */
if (dd) {
}
return (NIS_BADOBJECT);
}
if (dd) {
/*
* We could re-acquire the item with a write lock,
* and update it in place. However, in order to reduce
* contention with other threads that might be using
* the directory item, we instead remove it from the list,
* and insert a new one with the updated info.
*/
dd = 0;
}
if (dd) {
sizeof (ulong_t));
/*
* Some other thread got there before us.
* Look in the cache for a new copy.
*/
if (dd != 0) {
} else {
/*
* It's disappeared. Inform the
* caller.
*/
return (NIS_NOTFOUND);
}
}
}
/*
* Ok we found the directory object, do we really serve it ?
* Check to see if this server is supposed to serve this
* domain. (Must be master to manipulate the name space)
*/
if (dd) {
} else {
}
/* Check the master requirement */
if (is_master) {
if (serve != 1) {
if (dd)
else
return (NIS_NOTMASTER);
} else {
dirDeferRelease(dd, 0);
return (NIS_SUCCESS);
}
}
if (serve) {
dirDeferRelease(dd, 0);
return (NIS_SUCCESS);
}
if (dd)
else
return (NIS_NOT_ME);
}
/*
* Returns the NIS+ principal name of the person making the request
* XXX This is set up to use Secure RPC only at the moment, it should
* be possible for any authentication scheme to be incorporated if it
* has a "full name" that we can return as the principal name.
*/
struct creditem {
char pname[1024];
};
static void
{
"add_cred_item: principal name too long '%s'",
pname);
return;
}
if (old) {
return;
}
}
static int
{
if (toremove =
&credtbl)) {
}
return (0);
}
return (1);
}
/*
* Check security conf file for classic des compat entry.
*
* Return TRUE if entry found or if no valid mech entries
* exist, else return FALSE.
*/
static bool_t
auth_des_permitted(void)
{
mechanism_t **mechs;
mechanism_t **mpp;
if (AUTH_DES_COMPAT_CHK(mp)) {
return (TRUE);
}
}
return (FALSE);
} else
return (TRUE);
}
/*
* Check conf file list of mech entries to see if this raw cred mechanism
* type is permitted. Stop processing if the "des" compat entry is reached.
*
* Return TRUE on validation, else FALSE.
*/
static bool_t
{
mechanism_t **mechs;
mechanism_t **mpp;
if (AUTH_DES_COMPAT_CHK(mp))
break;
return (TRUE);
}
}
}
}
return (FALSE);
}
static char *
flavor2str(int flavor)
{
switch (flavor) {
case AUTH_SYS:
return ("SYS");
case AUTH_DES:
return ("DES");
case RPCSEC_GSS:
return ("GSS");
default:
return ("unknown");
}
}
/*
* Given a RPC netname, return the NIS+ principal.
*/
void
char *name, /* NIS+ principal (out) */
{
struct authsys_parms *au;
struct authdes_cred *ad;
char *rmtdomain;
int message;
if (req) {
} else {
"RPC request is NULL: returning '%s'", nobody);
return;
}
if (message) {
"nis_getprincipal: flavor = NONE: returning '%s'", nobody);
}
return;
if (secure_level > 1) {
if (message) {
"nis_getprincipal: flavor = SYS: returning '%s'",
nobody);
}
return;
}
if (! rmtdomain)
if (message) {
"nis_getprincipal: flavor = SYS: returning '%s'", name);
}
return;
}
"[auth_name=\"%d\", auth_type=LOCAL], cred.org_dir.%s",
(char *)nis_local_directory() : rmtdomain);
}
if (message) {
"nis_getprincipal: AUTH_DES not permitted: returning '%s'",
nobody);
}
return;
}
if (message)
"nis_getprincipal: flavor = DES: returning from cache '%s'",
name);
return;
}
if (rmtdomain) {
rmtdomain++;
"[auth_name=%s, auth_type=DES], cred.org_dir.%s",
sizeof (netname));
}
} else {
if (auth_verbose) {
"nis_getprincipal: flavor = DES: returning '%s'",
nobody);
}
return;
}
} else if (flavor == RPCSEC_GSS) {
void *cookie;
if (message) {
"nis_getprincipal: GSS getcred failure: returning '%s'",
nobody);
}
return;
}
if (message)
"nis_getprincipal: RPC GSS mechanism '%s' not permitted: returning nobody",
: "NULL");
return;
}
netname) < 0) {
"nis_getprincipal: can't extract netname from RPC GSS cred: returning nobody");
return;
}
if (message)
"nis_getprincipal: flavor = GSS: returning '%s' from cache",
name);
return;
}
if (rmtdomain) {
rmtdomain++;
sizeof (alias))) {
"nis_getprincipal: GSS mechname '%s' not found: returning nobody",
return;
}
if (alias[0] != '\0') {
(void) __nis_mechalias2authtype(alias,
sizeof (auth_type));
"[auth_name=%s, auth_type=%s], cred.org_dir.%s",
}
} else {
"nis_getprincipal: no alias was found for mechname '%s': returning 'nobody'",
return;
}
} else {
if (auth_verbose) {
"nis_getprincipal: flavor = GSS: returning '%s'",
nobody);
}
return;
}
} else {
"nis_getprincipal: flavor = %d(unknown): returning '%s'",
return;
}
if (message)
"nis_getprincipal: calling list with name '%s' and type '%s'",
else /* AUTH_SYS */
"nis_getprincipal: calling list with uid (LOCAL) '%d'",
if (message)
"nis_getprincipal: error doing nis_list: %s",
} else {
"nis_getprincipal: buffer overflow, returning '%s'", nobody);
return;
}
if (message)
"nis_getprincipal: caching '%s'/'%s'",
}
}
if (message)
"nis_getprincipal: flavor = %s: returning : '%s'",
}
/*
* __can_do()
* This function returns true if the given principal has the right to
* do the requested function on the given object. It could be a define
* if that would save time. At the moment it is a function.
* NOTE: It recursively calls NIS by doing the lookup on the group if
* the conditional gets that far.
*
* N.B. If the principal passed is 'null' then we're recursing and don't
* need to check it. (we always let ourselves look at the data)
*/
int
unsigned long right; /* The Access right we desire */
{
if ((secure_level == 0) || (*pr == 0))
return (1);
}
/* BEGIN CSTYLED */
/*
* Get an xdr buffer
*/
u_char *
int size; /* Size in bytes of the buffer */
{
#ifdef local_buf
#endif
}
/* END CSTYLED */
/*
* Get a string buffer
*/
char *
int size; /* Size in bytes of the buffer */
{
#ifdef local_buf
#endif
}
/*
* get an array of entry columns
*/
int n; /* required array size */
{
#ifdef local_buf
#endif
sizeof (entry_col), n));
}
/*
* get an array of table columns
*/
int n; /* required array size */
{
#ifdef local_buf
#endif
sizeof (table_col), n));
}
/*
* get an array of nis attributes
*/
nis_attr *
__get_attrs(n)
int n; /* required array size */
{
#ifdef local_buf
#endif
sizeof (nis_attr), n));
}
/*
* Stability functions. These functions check to see if a transaction
* has been propogated to all replicas.
*
* nis_isstable()
* This function checks to see if an update has been propogated to all
* of the replicates for the given domain. If so, the log code will be
* free to delete it, otherwise we will continue to hold it until the
* replicate picks up the change. If we can't figure out if it is stable
* or not we return 0 and hold onto it.
*/
int
int first;
{
int i;
struct directory_item *dd;
struct ticks t;
else
/*
* get object for the domain, if this is the first time we're
* "visitin" this domain then get a fresh copy (NO_CACHE)
*/
if (first)
else
/*
* If we aren't the master any more, or the directory doesn't exist
* anymore we can toss this update.
*/
(err == NIS_BADNAME))
return (TRUE);
/*
* If we encountered a problem trying to get the directory object
* we hold on to the update.
*/
return (FALSE);
/*
* If we look at the object and there is only one server then
* the update has by definition propogated.
*/
return (TRUE);
/*
* Otherwise we go ahead and check to see if it is stable.
* In the single-threaded case, the find_item will always work
* if the __directory_object() call worked. Not necessarily true
* in MT case, where other threads may do work in between
* __directory_object() and nis_find_item().
*/
if (!dd)
return (TRUE);
/* Fetch the time from the replica */
}
}
}
return (ret);
}
/*
* add_fenceposts()
*
* This function will put "fenceposts" into the log for each directory we
* serve. This makes the "lastupdate()" function faster and is essential
* for directories that have been stable long enough that there are no
* delta's left.
*/
void
{
int i;
struct directory_item *dd;
nis_local_host()) == SAME_NAME) {
break;
}
}
}
}
}
/*
* apply_update()
*
* This function applies a log entry that we've received from the master
* for a given domain to the log and the database.
*/
static nis_error
{
char *msg;
if (cons) {
}
case ADD_NAME :
msg = "Failed to add object ";
break;
case MOD_NAME_NEW :
msg = "Failed to modify object ";
break;
case REM_NAME :
msg = "Failed to remove object ";
break;
/* For old modified objects, just add this to the log */
case MOD_NAME_OLD :
break;
case ADD_IBASE :
msg = "Failed to add entry ";
break;
case REM_IBASE :
msg = "Failed to remove entry ";
break;
/* ignore these as they are both NOPs */
case UPD_STAMP :
case LOG_NOP :
break;
default :
msg = "Illegal transaction type on ";
break;
}
if (status != NIS_SUCCESS) {
if (cons)
}
return (status);
}
extern table_obj tbl_prototype;
/*
* clear_directory()
*
* This function will restores a directory to "virgin" status. This is
* accomplished by deleting any databases (tables) that are contained in
* that directory, then destroying and recreating the database for the
* directory.
*/
int
{
int i, n;
if (n < 1 || n > sizeof (namebuf)) {
"clear_directory: buffer (%d) insufficient for \"%s.%s\" (%d)",
sizeof (namebuf),
2 /* dot and NUL */);
continue;
}
"clear_directory: Could not destroy table \"%s\": %s.",
} else {
/* Not a table, so just remove the object */
if (status != NIS_SUCCESS)
"clear_directory: Could not remove object \"%s\": %s",
}
}
"clear_directory: could not get object list for \"%s\": %s",
"<nil>");
return (0);
}
/*
* else (tables->status == NIS_NOTFOUND), which is fine by us;
* someone already did our work.
*/
"Could not destroy table %s: %s.",
/* XXX recover? */
}
/* Make a clean version. */
"Could not create table %s: %s.",
/* XXX recover? */
}
if (verbose)
return (1);
}
/*
* nis_makename()
*
* This function prints out a nice name for a objects.
*/
void
char *str;
{
int i;
int ncols;
return;
}
for (i = 0; i < ncols; i++) {
}
}
}
}
/*
* During full resyncs, repl_objs caches the table objects, so that
* we don't have to look them up every time we add an entry to
* the table.
*/
/* This is the callback for nis_dump (Full Resyncs) */
int
void *x;
{
int release_pp = 0;
}
/*
* Note :
* Once entered this if block returns to the caller.
*/
if (! pp) {
} else {
release_pp = 1;
}
if (tobj) {
if (verbose) {
}
/* Build a fully specified name from the entry */
"update_directory: out of memory resync aborted.");
repl_stats.errors++;
if (release_pp)
return (1);
}
na++;
}
}
if (status == NIS_SUCCESS) {
"update_directory : %d objects, still running.",
if (release_pp)
return (0);
} else {
"replica_update (update_directory): adding %s returned %s",
repl_stats.errors++;
if (release_pp)
return (1);
}
} else {
"replica_update (update_directory): adding entry to %s returned %s",
repl_stats.errors++;
if (release_pp)
return (1);
}
}
/*
* At this point it isn't an ENTRY object so we're not adding
* it to a table.
*/
if (verbose) {
}
if (status == NIS_SUCCESS) {
if (release_pp)
-1);
return (0);
} else {
/*
* xxx sometimes clear_directory doesn't successfully
* clear out tables (bug), this tries again before
* giving up on the table object.
*/
if (status == NIS_SUCCESS) {
return (0);
}
}
}
repl_stats.errors++;
if (release_pp)
-1);
return (1);
}
}
/*
* Make sure the directory exists, if it doesn't then we've
* missed a create somewhere or we were just added as a replica
* of the directory.
*/
int
{
switch (status) {
case NIS_NOSUCHTABLE:
NIS_SUCCESS) {
"verify_table_exists: cannot create table for %s:%s.",
return (0);
}
break;
case NIS_SUCCESS:
break;
default:
"verify_table_exists: unexpected database error for %s: %s.",
return (0);
}
return (1);
}
/*
* Update root object (creating it if not there before) or remove root object
* depending on whether we're still listed as a root replica.
* Affects root_server flag.
* Returns 1 if successful; 0 otherwise.
*/
int
{
nis_result *res = 0;
int readok;
char *myself = "root_replica_update";
/* If we're reading the root dir from LDAP, do nothing */
#ifdef NIS_LDAP_DEBUG
#else
#endif /* NIS_LDAP_DEBUG */
"%s: Mapping root dir object from LDAP", myself);
return (1);
}
"root_replica_update: update failed '%s': could not fetch object from master.",
root_dir);
return (0); /* failed, try again later. */
}
if (verbose)
"root_replica_update: updating '%s'",
root_dir);
return (0); /* failed, try again later. */
}
} else {
if (verbose)
"root_replica_update: removing '%s'",
root_dir);
}
return (1);
}
/*
* Return 1 if all objects in the directory 'dirname' are read mapped
* from LDAP, 0 otherwise. For table objects, the definition of "all
* objects" includes the table object itself, as well as entried in
* the table.
*
* Most of the code to retrieve a directory listing and traverse same
* was borrowed from clear_directory().
*/
static int
int i, n, ok;
char *myself = "isAllMapped";
#ifdef NIS_LDAP_DEBUG
#else
#endif /* NIS_LDAP_DEBUG */
"%s: Unable to get directory list for \"%s\": %s",
return (0);
}
int readok;
if (n < 1 || n > sizeof (namebuf)) {
#ifdef NIS_LDAP_DEBUG
#else
#endif /* NIS_LDAP_DEBUG */
"%s: Buffer size (%d) insufficient for \"%s.%s\" (need %d)",
ok = 0;
continue;
}
readok == 0) {
#ifdef NIS_LDAP_DEBUG
#else
#endif /* NIS_LDAP_DEBUG */
"%s: No read mapping for obj \"%s\"",
ok = 0;
continue;
}
readok == 0)) {
#ifdef NIS_LDAP_DEBUG
#else
#endif /* NIS_LDAP_DEBUG */
"%s: No read mapping for entries in \"%s\"",
ok = 0;
continue;
}
}
return (ok);
}
/*
* Returns 1 if the log_entry:s 'l1' and 'l2' pertain to the same
* entry object, 0 otherwise.
*
* Since this function works on log entries alone, it may not
* correctly handle cases where different attribute selector lists
* resolve to the same entry. For example, it's quite possible that
* '[key1=abc]tab.org_dir' and '[key2=def]tab.org_dir' unambiguously
* specify the same entry, but we wouldn't know without looking
* in the DB (and even that wouldn't work if one of the operations
* is an ADD, in which case the entry migh tnot exist yet).
*/
static int
int i;
return (0);
/* Must both be entry objects */
return (0);
return (0);
/* Same table name */
return (0);
/* Same number of entry selector attributes */
return (0);
/*
* we require that they're in the same order.
*/
return (0);
return (0);
0)
return (0);
}
return (1);
}
/* define buffer size for ctime_r() */
#define CTBUFSIZ 26
/*
* replica_update();
*
* This function is used by the replicas to keep themselves up to date
* with the master server. When called, it iteratively removes the names
* of directories that have changed from it's list.
*
* Note: replica_update() is called either from main() before we go MT,
* of from check_updaters() with an exclusive lock on the 'upd_list'.
* Thus, there's no need to enforce exclusion in replica_update()
* itself.
*/
int
{
extern void __nis_freelogresult(log_result *);
/* timestamp used to write back into log when full resync is done */
/* If a full dump fails, is it unsafe to return (i.e., try again) ? */
char *myself = "replica_update";
if (verbose)
if (cons)
/* check to see if we're replicating the root object */
if (root_object_p(name)) {
return (root_replica_update());
}
"host %s thinks that it is the replica for %s, but it is the master!",
nis_local_host(), name);
return (1);
}
if (verbose)
if (verify_table_exists(name) == 0) {
return (0); /* failed, try again later. */
}
/*
* If every object in the directory, including table entries, are
* mapped from LDAP, we just get the update time from the master,
* and write an UPDATE time stamp to trans.log.
*/
if (isAllMapped(name)) {
"%s: All objects in \"%s\" mapped from LDAP; "
"setting update time to %s",
CTBUFSIZ));
return (1);
} else {
"%s: Unable to get update time for \"%s\" from \"%s\"",
/* Fall through to normal resync */
}
}
/*
* We now intend do things that might want to update the trans.log.
* While the actual transactions are protected by begin_transaction()/
* end_transaction(), we don't want to have some other thread changing
* the trans.log under our feet from this point on, so we acquire
* a write lock on the trans.log.
*/
if (lstat != 0)
return (0);
/*
* If ttime is non-zero, then get the delta from the master using
* nis_dumplog().
* If ttime is zero, then go directly to the full resync section.
* There is no need to even try to get the delta from the master.
*/
/* get the delta from transaction log */
if (cons)
"replica_update: dumping master's log.\n");
return (0);
}
"replica_update: Couldn't contact '%s' serving '%s' for an update.",
return (0);
}
"replica_update: master server is busy, will try later.");
return (0);
}
int skipped = 0;
if (verbose)
"replica_update: %d updates from '%s'",
if (cons)
"replica_update: %d updates from '%s'\n",
if (nument == 0) {
"replica_update: master server returned NIS_SUCCESS, but 0 updates!");
return (1);
}
/*
* If there are objects in the directory that are mapped
* from LDAP, we intend to skip those during the resync.
* This means that the time stamp of the last update in
* trans.log after the resync isn't necessarily the same
* as the last update known to the master. Hence, we
* save the latter in 'xttime'.
*/
/* invalidate this directory in case of crash */
"%s: Temporarily setting update time for "
"\"%s\" to 0x%lx",
begin_update();
if (dbStatus != DB_SUCCESS) {
"replica_update: offline DB error %d for \"%s\"",
}
for (i = 0; i < nument; i++) {
doingModify = 0;
/*
* Is the object mapped from LDAP ? If so,
* skip the update, but consider the
* application of the change a success.
*/
io = 0;
case NIS_DIRECTORY_OBJ:
case NIS_GROUP_OBJ:
case NIS_TABLE_OBJ:
case NIS_LINK_OBJ:
io = 1;
/* Fall through to entry obj */
case NIS_ENTRY_OBJ:
readok != 0);
break;
default:
shouldskip = 0;
break;
}
if (shouldskip) {
#ifdef NIS_LDAP_DEBUG
#else
#endif /* NIS_LDAP_DEBUG */
"%s: skipping LDAP-mapped entry for \"%s\"",
skipped++;
continue;
}
/*
* If the current operation is an entry
* removal, and the next one is an add
* of the same entry, tell the DB that
* we're in a modify, so that it can
* LDAP update.
*/
i < nument-1 &&
doingModify = 1;
}
/*
* If we had informed the DB that this was
* part of a modify, and we've done the
* add (or the log update failed), turn
* off the modify indicaton.
*/
if (doingModify &&
nerr != NIS_SUCCESS)) {
__nisdb_get_tsd()->doingModify = 0;
doingModify = 0;
}
if (nerr != NIS_SUCCESS) {
if (cons)
"Failed to apply update.\n");
"replica_update: Unable to apply update.");
repl_stats.errors++;
break;
}
if (cons)
"Add update #%d to trans.log\n", i+1);
if (add_update_nosync(&(le[i]),
(void **)&curr_upd) == 0) {
repl_stats.errors++;
break;
} else {
if (cons)
"Added update to trans.log.\n");
}
}
if (repl_stats.errors == 0) {
if (cons)
"Successfully updated.\n");
end_update();
FALSE);
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
nis_db_sync_log(); /* *.log */
sync_log(); /* trans.log */
if (curr_upd != 0)
else
ttime = 0;
/*
* This should never happen, otherwise
* this can force the replica to go
* into full resync.
*/
"replica_update: WARNING: last_update(%s) returned 0!",
name);
/*
* If we skipped updates because the object
* was mapped from LDAP, 'ttime' may not be
* valid, so we use the time from the master
* that we saved in 'xttime'.
*/
"%s: Setting update time for \"%s\" to %s",
return (1);
} else {
if (cons)
FALSE);
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
nis_db_sync_log(); /* *.log */
sync_log(); /* trans.log */
return (0);
}
}
"replica_update: nis_dumplog failed: srv='%s', dir='%s', err='%s'",
if (cons)
"replica_update: nis_dumplog failed: srv='%s', dir='%s', err='%s'\n",
return (0);
}
} /* end of delta update */
/*
* Our log and the masters are sufficently out of date that we
* need to completely resync.
*/
if (verbose) {
if (cons)
}
/*
* The following code handles full dump (resync) from the
* master. Full dump only happens if the local timestamp
* returned from last_update():
* 1) is 0 (zero), or
* 2) cannot be found in on the master.
*/
if (dbStatus != DB_SUCCESS) {
"replica_update: offline DB error %d for \"%s\"",
/* Haven't actually done anything, so return failure */
return (0);
}
/* clear_directory(name); */ /* we might serve stale data */
/*
* Note we *don't* transact this because if we fail we'll simply
* restart from the beginning. However, we hang on to the trans.log
* lock in order to prevent other threads from performing updates.
*/
/*
* The case where all objects in the directory were mapped from LDAP
* was already handled above. If we get here, it may be that _some_
* of the objects are mapped from LDAP, but we also know that we're
* out-of-sync with the master for at least one object.
*
* The usual reason that we get to this point is that this is a
* newly created replica, and we'd probably like to build up its
* disk database ASAP. For this reason, and in the interest of
* simplicity (don't want to make libnsl, where nis_dump() lives,
* aware of LDAP mapping), we let the full dump get everything
* from the master, whether or not it's mapped from LDAP.
*/
if (lres) {
}
/*
* Free up the cache of table objects
*/
/*
* If lres indicates success, mark the error,
* but continue so that repl_objs will be
* cleaned up. The status in lres will
* be checked below.
*/
}
}
if (lres)
"replica_update: master server busy, rescheduling the resync.");
if (ttime != 0L) {
ttime = 0;
"%s: Setting update time for \"%s\" to 0x%lx",
}
/*
* Note, we are not addressing the case when several replicas
* are initialized simultaneously i.e several nismkdir -s
* are executed before a nisping.
*/
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
if (__nis_retry_sleep(&dumpRetry,
"replica_update: trying a full resync again");
goto try_again;
} else {
/*
* Returning zero makes sure that this directory is not
* deleted from the upd_list. We'll try again after our
* poll times out.
*/
return (0);
}
}
if (ttime != 0L) {
ttime = 0;
"%s: Setting update time for \"%s\" to 0x%lx",
}
/*
* Put back online, since the directory would not have been
* cleared.
*/
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
if (__nis_retry_sleep(&dumpRetry,
"replica_update: trying a full resync again");
goto try_again;
} else {
return (0); /* don't delete from upd_list */
}
"%s: nis_dump failed: srv='%s', dir='%s', "
if (ttime != 0L) {
ttime = 0;
"%s: Setting update time for \"%s\" to 0x%lx",
}
/*
* Put back online, nothing more we can do
*/
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
return (1); /* do delete from upd_list */
"replica_update: nis_dump failed: srv='%s', dir='%s', err='%s'",
if (ttime != 0L) {
ttime = 0;
"%s: Setting update time for \"%s\" to 0x%lx",
}
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
}
/*
* Wait before retrying, abort dump if we've used up the
* allowed number of attempts. However, if rollback failed,
* we disregard 'dumpRetry.attempts' and always continue
* trying (since the data may not only be stale, but
* inconsistent or corrupted).
*/
if (!__nis_retry_sleep(&dumpRetry,
(dbStatus != DB_SUCCESS))) {
"replica_update: Used %d dump attempts for \"%s\", serving stale data",
return (0);
}
dbStatus == DB_SUCCESS) {
if (dbStatus != DB_SUCCESS) {
"replica_update: offline DB error %d for \"%s\"",
}
}
goto try_again;
} else if (repl_stats.errors) {
"replica_update: errors during resync : srv='%s', dir='%s'",
xttime = 0;
if (ttime != 0L) {
ttime = 0;
"%s: Setting update time for \"%s\" to 0x%lx",
}
if (dbStatus != DB_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
}
/*
* Wait before retrying, abort dump if we've used up the
* allowed number of attempts. However, if rollback failed,
* we disregard 'dumpRetry.attempts' and always continue
* trying (since the data may not only be stale, but
* inconsistent or corrupted).
*/
if (!__nis_retry_sleep(&dumpRetry,
(dbStatus != DB_SUCCESS))) {
"replica_update: Used %d dump attempts for \"%s\", serving stale data",
return (0);
}
dbStatus == DB_SUCCESS) {
if (dbStatus != DB_SUCCESS) {
"replica_update: offline DB error %d for \"%s\"",
}
}
goto try_again;
}
/*
* Mark the now resync'd directory as stable.
* This is done by comparing the latest timestamp of
* all the objects we've seen with the latest timestamp
* from the master. This is required because the master
* stamps "remove" operations but they won't show up
* on a full resync.
*/
} else {
"replica_update: downrev version of NIS+ service serving dir %s as master.",
name);
}
if (cons)
CTBUFSIZ));
if (verbose)
"replica_update: directory %s updated", name);
/* Add it to the list of directories that it serves */
if (dbStatus != NIS_SUCCESS) {
"replica_update: online DB error %d for \"%s\"",
}
/*
* The full resync is now complete. We need to put the
* current timestamp for the directory in the local
* transaction log.
* NOTE: variable "xttime" contains the valid timestamp
* for the directory. It should never be 0. However,
* if we do get this, we'll have to assume that there
* were some problem and we should force another resync.
*/
/*
* Always stamp with valid timestamp.
* We only stamp with timestamp of 0 if there
* isn't already a timestamp of 0 in the
* transaction log.
*/
"%s: Setting update time for \"%s\" after "
"full resync to %s",
}
if (xttime == 0L)
"replica_update: timestamp=0 after full resync completed!");
return (1);
}
static void
{
if (verbose)
if (verbose)
thr_exit(0); /* thread simply exits */
}
/*
* ping_replicas()
*
* This function will send a "ping" to the replicas for a given directory
* indicating that they should be ready to get updates to the database.
*
* It creates a thread to send the ping.
* NOTE: The main thread destroys the ping_item "pung" when
* ping_replicas() returns. To avoid problems, the
* ping_item is first cloned. The cloned ping_item
* must be destroyed in the thread.
*
* Once the thr_create is successful the operation is presumed to complete.
*
*/
int
{
int error;
if (sigemptyset(&new_mask) != 0) {
if (verbose)
"ping_replicas: Error (%d) zeroing mask for %s",
return (FALSE);
}
if (verbose)
"ping_replicas: Error (%d) setting mask for %s",
return (FALSE);
}
(void *(*)(void *))ping_replicas_thread, (void *)pung,
THR_DETACHED, &tid);
if (verbose)
"ping_replicas: Error (%d) restoring mask for %s",
}
if (error) {
if (verbose)
"ping_replicas: Error (%d) creating ping thread for %s",
return (FALSE);
}
if (tid) {
if (verbose)
"ping_replicas: Created ping thread %d for %s",
return (TRUE);
} else
return (FALSE);
}
/*
* Recursion Safe versions of the lookup internals.
*
* These functions are the internal functions that are used by the
* lookup and list functions in the library. When the server is linked
* against libnsl, these functions replace those in the library and make
* it safe for use to call nis_lookup() or nis_list().
*/
/*
* nis_local_lookup()
*
* Lookup the requested information by calling the services listsvc or
* lookup_svc entry points.
*/
int list_op; /* list semantics */
void *cbdata; /* Callback data */
int (*cback)(); /* Callback (for list calls) */
{
int i;
if (verbose)
return (NULL);
/*
* Depending on name or list_op either
* list it or look it up in the namespace.
*/
if (verbose)
} else {
if (name)
/* sanity check on object name */
if (verbose)
"nis_local_lookup: bad name '%s'",
return (res);
}
if (verbose)
}
/*
* Now duplicate the result for the client so that the
* client can call nis_freeresult() with impunity.
*/
sizeof (nis_object));
(void) nis_clone_object(
}
}
}
return (res);
}
/*
* we_serve()
*
* return TRUE if an RPC call to this directory might call us
* back.
*/
int
{
int ns, i;
if (flags & MASTER_ONLY)
ns = 1;
else
/*
* XXX NB: if the server name doesn't match but the
* address does, then we'll miss this check and recurse
* anyway. Sigh.
*/
for (i = 0; i < ns; i++)
== SAME_NAME)
return (1);
return (0);
}
/*
* nis_core_lookup()
*
* The bones of the lookup and list function, this function binds to, then
* calls the appropriate NIS+ server for the given name. If the HARD_LOOKUP
* flag is set it is ignored because the server cannot afford to block.
*
* NB: This function now follows links if flags is set up correctly. THis
* localized this policy to this function and eliminated about 4
* implementations of the same code in other modules.
*/
int list_op; /* list semantics */
void *cbdata; /* Callback data */
int (*cback)(); /* Callback (for list calls) */
{
int parent_first;
if (verbose)
/*
* AS THE SERVICE, we don't support callbacks
*/
return (nis_make_error(NIS_NOCALLBACK, 0, 0, 0, 0));
}
parent_first = 0;
else
parent_first = 1;
if (err != NIS_SUCCESS) {
return (nis_make_error(err, 0, 0, 0, 0));
}
/*
* special handling for root replicas.
*
* If the following conditions are met :
* 1) the directory we're going to query == our domain name
* 2) we allegedly serve it.
* 3) we don't have the "root_server" flag set
* Then we are a root_replica and we need to shunt this
* request to the master root server who will read the
* root object and return it to us.
*/
flags |= MASTER_ONLY;
return (nis_make_error(NIS_FAIL, 0, 0, 0, 0));
}
}
/*
* As a server, we may serve the indicated directory, if we
* do then we *DON'T* do an RPC, rather we just call our
* selves. Note this *can* recurse.
*/
/*
* We supposedly serve the directory, but we don't
* have a database for it (or the table), yet.
* Try the master for now, until we get the
* directory back in sync. Of course, we shouldn't
* do this if we are the master.
*/
}
} else {
}
return (res);
}
/*
* __nis_finddirectory()
*
* This function will ask one of the servers of the given directory
* where some unknown directory "name" is. This function is called
* from within the __nis_CacheBind() code so is generally not needed by
* the client. If the directory cannot be found it returns NULL.
*/
{
int i, ns;
enum name_pos p;
if (verbose)
if (root_server && p == HIGHER_NAME) {
return (res);
}
/*
* special case the root replica servers.
*/
/*
* If this server is on the list and we have a database for it,
* just call the svc routine
*/
for (i = 0; i < ns; i++) {
/* Make sure we've got a database for it. */
if (tab_status == NIS_NOSUCHTABLE) {
else
}
switch (tab_status) {
case NIS_SUCCESS:
/*
* Note, we "clone" the find directory result
* because the cache client code will free it.
*/
return (res);
}
return (NULL);
case NIS_NOT_ME:
/* not master */
goto try_srvlist;
default:
/* _fd_res puts returned obj on rags list */
NULL);
/* clone fd_result for cache to free */
return (res);
return (NULL);
}
}
}
/* Make an RPC call to locate the directory. */
return (res);
}
int
{
int i;
char *name = nis_local_host();
for (i = 0; i < nsrv; i++) {
return (1);
}
return (0);
}
dup_fdres(o, n)
fd_result *o; /* The result to duplicate */
fd_result *n; /* Where to put the data */
{
#ifdef DEBUG
if (verbose)
#endif
*n = *o;
if (o->source)
if (o->dir_data.dir_data_val) {
n->dir_data.dir_data_val = (char *)
if (! n->dir_data.dir_data_val) {
return (NULL);
}
o->dir_data.dir_data_len);
}
if (o->signature.signature_val) {
n->signature.signature_val = (char *)
if (! n->signature.signature_val) {
if (n->dir_data.dir_data_val)
return (NULL);
}
o->signature.signature_len);
}
return (n);
}
int
{
root_server = 1;
return (1);
} else {
return (0);
}
}
{
/*
* XXX Maybe this can be cached and updated whenever
* update_root_object is called instead of being reread ???
*/
return (d_obj);
}
int
{
}
int
{
root_server = 0;
return (1);
}