nislib.c revision 61961e0f20c7637a3846bb39786bb9dffa91dfb9
/*
* 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"
/*
* nislib.c
*
* This module contains the user visible functions for lookup, and list,
* add_entry/remove_entry/modify_entry, and mkdir, rmdir, and checkpoint.
* nis server. It should be broken up into at least three separate modules
*
*/
#include "mt.h"
#include <syslog.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include <errno.h>
#include "nis_clnt.h"
#include "nis_local.h"
/*
* This is *NOT* used in /usr/lib/libnsl.so.1, but has to be here so a program
* like "sendmail" can access it BEFORE it makes ANY getXXbyYY calls.
*/
extern const nis_result __nomem_nis_result;
/*
* __nis_nss_lookup_policy: set's the __nis_force_hard_lookups flag
* to HARD_LOOKUP or SOFT_LOOKUP depending upon the argument. It will
* be OR-ed into the nis_list() calls in the
* NIS+ Name Service Switch backend /usr/lib/nss_nisplus.so.1
*/
unsigned int
__nis_nss_lookup_policy(unsigned int in)
{
unsigned int old;
if (in == HARD_LOOKUP)
else if (in == SOFT_LOOKUP)
else {
old = 0;
}
return (old);
}
/* maximum number of times to loop on NIS_NOTMASTER error */
#define MAX_NOTMASTER 5
/*
* Prototypes for static functions.
*/
ib_request *, uint_t,
int (*)(nis_name, nis_object *, void *),
void *);
int (*)(), void *);
extern int __nis_debug_rpc;
extern int __nis_debug_calls;
extern FILE *__nis_debug_file;
/*
* nis_freeresult()
*
* This function calls the XDR free function for the user to free up the
* memory associated with a result structure. NB: It isn't a macro because
* it needs to be exposed to the client. Internally the xdr routine should
* be used to save a procedure call.
*/
void
{
if (res == &__nomem_nis_result)
return;
}
void
{
if (lres) {
}
}
/*
* nis_bind_dir()
*
* This function is used to return a binding to a candidate server.
* It has two side effects :
* 1) It keeps track of time in the cache code
* 2) it optionally mallocs a NIS+ result structure.
*
* 'name' contains the name of the object that will be operated on.
* If the operation is a lookup or a table search, then this code
* returns a directory object that describes that objects directory
* and thus a server that will have knowledge about the object.
*
* However, since the directory object, and the directory itself
* can be on two different machines, when listing directories we
* attempt to bind to the name passed. This only occurs when nis_list
* is called and the search criteria is NULL (which is the only legal
* search on a directory).
*
*/
char *dname,
int parent_first,
{
if (parent_first) {
if (stat != NIS_SUCCESS)
} else {
if (stat != NIS_SUCCESS)
}
return (stat);
}
{
return (stat);
}
/*
* __nis_path_list()
*
* This internal function will list all of the tables in a path.
* if the ALL_RESULTS flag is set, it keeps on going, and going.
* otherwise it returns on the first match.
*
* NB: The nis_list() function initializes the request's search
* criteria, we just swap in table names.
*
* All possible returns from call to NIS_IBLIST (all NIS_xxx)
*
* Successful returns
*
* CBRESULTS, SUCCESS, S_SUCCESS - found something
* NOTFOUND, PARTIAL, - looked but no data.
* PERMISSION - can't read the table
*
* Errors that generate syslog warnings
* NOTMASTER, NOT_ME, BADNAME, NOSUCHTABLE, NOSUCHNAME
* BADATTRIBUTE, INVALIDOBJ
*
* Errors that fail silently but change final result to "soft"
* NOMEMORY, TRYAGAIN, SYSTEMERROR, BADOBJECT
*
* Fatal errors :
* NOCALLBACK
*/
static nis_result *
int sf, /* search first object */
unsigned int flags, /* Flags to this function */
void *cbdata)
{
char pathbuf[NIS_MAXPATHLEN];
char firstpath[NIS_MAXNAMELEN];
int tnum, /* # of tables to search */
i, j, /* counters */
cur_obj, /* obj, link counters */
soft_error = 0; /* error detected */
unsigned int aticks = 0, /* profiling vars */
dticks = 0,
cticks = 0,
zticks = 0;
int num_objs,
total_objs; /* # of objects returned */
struct obj_lists { /* returned objects from each */
int len;
/* construct a list of tables to search */
if (sf) {
NIS_MAXPATHDEPTH - 1);
tnum++;
} else
/* Take any existing objects from the result passed in */
/*
* Either search until a match is found, or if ALL_RESULTS is
* set, search until path is exhausted.
*/
for (i = 0; i < tnum; i++) {
/* Ignore non-fully qualified names in path */
"nis_list: non fully qualified name in table path, %s, ignored.\n",
table);
continue;
}
/* swap in the table name from the path */
/* prepare to receive the objects returned */
if (cback) {
(void) mutex_lock(&__nis_callback_lock);
cback);
(void) mutex_unlock(&__nis_callback_lock);
} else
cback);
case NIS_SUCCESS :
/* put these into the array */
total_objs += num_objs;
/* zero this so freeresult won't free them */
/* fall through to the CBRESULTS code */
/*FALLTHROUGH*/
case NIS_CBRESULTS :
case NIS_CBERROR :
break;
case NIS_PARTIAL :
case NIS_PERMISSION :
case NIS_NOTMASTER :
/* these errors, just break */
break;
case NIS_LINKNAMEERROR : /* message generated above */
soft_error = TRUE;
break;
case NIS_NOT_ME :
case NIS_RPCERROR :
case NIS_NAMEUNREACHABLE :
/* generate message and set soft_error */
"nis_list: NIS+ error %s encountered on name %s in table %s.%s's path.",
soft_error = TRUE;
break;
case NIS_NOTFOUND :
case NIS_BADNAME :
case NIS_NOSUCHTABLE :
case NIS_NOSUCHNAME :
case NIS_BADATTRIBUTE :
case NIS_INVALIDOBJ :
/* generate message but don't set soft_error */
"nis_list: NIS+ error %s encountered on name %s in table %s.%s's path.",
break;
default :
soft_error = TRUE;
break;
}
/*
* POLICY : When one table in a path is unreachable,
* should we continue on or stop with an error?
* ANSWER : Continue on. Loss of a portion of the namespace
* should not cause disruptions in all of the namespace.
* NB: This can have interesting side effects such that a
* name may suddenly change "value" because it is being
* resolved from a different place.
*
* If we're not returning all results and we've had a
* successful call, we just return those results.
*/
if (((flags & ALL_RESULTS) == 0) &&
/* reset so that caller does not free it */
/* return same result structure back to them */
return (res);
}
/* otherwise just free local result (we've got the objs) */
}
/* name is already freed so null this out */
/*
* At this point, we've either exhausted the list of tables
* (total_objs == 0), or we've asked for all results so the
* ret_objs[] array has some data in it (total_objs > 0)
* if soft_error is set we will adjust our result status
* appropriately.
*/
if (total_objs) {
/* now build a list of objects that should be returned */
for (i = 0; i < (tnum+1); i++) {
continue;
}
return (res);
}
/* copyout all objects into this new array */
cur_obj = 0;
for (i = 0; i < (tnum+1); i++) {
continue;
}
if (cur_obj)
else
} else {
if (cback)
else
}
return (res);
}
/*
* nis_lookup()
*
* This is the main lookup function of the name service. It will look
* for the named object and return it. If the object was a link and
* the flag FOLLOW_LINKS was set it will look up the item named by
* the LINK, if that is an indexed name the lookup may return multiple
* objects. If the name is not fully qualified and EXPAND_NAME is set
* this function will expand the name into several candidate names.
*/
{
int i;
unsigned int aticks = 0,
cticks = 0,
dticks = 0,
zticks = 0;
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
}
if (__nis_debug_calls)
return (res);
}
if (! namelist) {
if (__nis_debug_calls)
return (res);
}
for (i = 0; namelist[i]; i++) {
/*
* All of the errors that indicate the name
* is bound.
* NB: We include the "nis_list" errors as well
* as the core_lookup call could have followed
* a link into a table operation.
*/
case NIS_SUCCESS :
case NIS_PARTIAL :
case NIS_CBRESULTS :
case NIS_CBERROR :
case NIS_CLNTAUTH :
case NIS_SRVAUTH :
case NIS_PERMISSION :
case NIS_LINKNAMEERROR:
case NIS_NOTMASTER :
if (__nis_debug_calls)
return (res);
default :
if (nis_err == NIS_SUCCESS)
}
}
if (nis_err == NIS_SUCCESS) {
}
if (__nis_debug_calls)
return (res);
}
/*
* nis_list()
*
* This function takes a "standard" NIS name with embedded search criteria
* and does a list on the object.
*/
int (*cback)(), /* Callback function. */
void *cbdata) /* Callback private data */
{
aticks = 0,
dticks = 0,
cticks = 0;
int i, done;
/* start the client profiling clock */
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
(void) fprintf(__nis_debug_file,
}
if (stat != NIS_SUCCESS) {
if (__nis_debug_calls)
return (res);
}
/*
* process the ALL_RESULTS flag specially. First fetch
* the table object to get the path, then call path list
* to read all of the data. Note if we returned the object
* on a list we could save an RPC here.
*/
if (flags & ALL_RESULTS) {
if (__nis_debug_calls)
return (res);
}
/* Note : can't do all results on directory obj. */
if (__nis_debug_calls)
return (res);
}
if (__nis_debug_calls)
return (res);
}
/*
* Normal requests. The server will return NIS_PARTIAL
* if we specify a search criteria and the table exists
* but the entry does not exist within the table. We
* need to handle this return value by checking for a
* table path in nis_list_partial().
*/
if (cback) {
(void) mutex_lock(&__nis_callback_lock);
(void) mutex_unlock(&__nis_callback_lock);
} else
} else {
if (! namelist) {
if (__nis_debug_calls)
return (res);
}
/* replace with the candidate name */
if (cback) {
(void) mutex_lock(&__nis_callback_lock);
cback);
(void) mutex_unlock(&__nis_callback_lock);
} else
cback);
/*
* All of the errors that indicate the name
* is bound.
*/
case NIS_SUCCESS :
case NIS_CBRESULTS :
case NIS_CBERROR :
case NIS_CLNTAUTH :
case NIS_SRVAUTH :
case NIS_PERMISSION :
case NIS_NOTMASTER :
done = 1;
break;
default :
if (nis_err == NIS_SUCCESS)
break;
}
}
if (! done) {
if (nis_err == NIS_SUCCESS) {
}
}
}
/*
* Returns from __nis_core_lookup :
* NIS_PARTIAL, found the name but didn't match any entries.
* NIS_CLNTAUTH, Found table, couldn't authenticate callback
* NIS_SRVAUTH Found table, couldn't authenticate server
* NIS_PERMISSION Found table, couldn't read it.
* NIS_NOTMASTER Found table, wasn't master (master requested)
* NIS_RPCERROR unable to communicate with service.
* NIS_XXX Error somewhere.
*
*/
if (__nis_debug_calls)
return (res);
}
/*
* Deal with a "PARTIAL" result. Given a NIS name of
* [search-criteria].table-name, this occurs when the
* server found an object whose name was 'table-name' but
* either the object couldn't be searched because it was the
* wrong type or the search resulted in no results.
*
* If the object that matched 'table-name' was a LINK object
* core lookup will have followed it for us.
*
* We increment a local copy of the statistics and then reset
* them in 'res' before returning.
*/
static
int (*cback)(), /* Callback function. */
void *cbdata) /* Callback private data */
{
/*
* POLICY : What is the error when you search a
* a DIRECTORY and the results are no entries ?
* ANSWER : A NOT FOUND error, assuming the server
* did not return a "bad attribute" error, AND
* we do _NOT_ return the directory object that the
* server returned.
*/
/* If the object that matched 'table-name' was a LINK object */
/* core lookup will have followed it for us. If it's not */
/* found there and we somehow came to nis_list_partial() */
/* we need to return NIS_NOTFOUND */
/*
* This shouldn't happen because the server should
* catch it when it attempts the search.
*/
} else {
/*
* Now we know its a table object and that our search failed.
*/
/* free up the table object */
/* ticks are updated by __nis_path_list */
} else {
/*
* If a search criteria was specified, indicate
* that we didn't find the entry. If there was
* no search criteria, then we return success
* (i.e., table was listed successfully, but
* there were no entries in the table).
*/
else if (cback)
else
}
}
return (res);
}
/*
* Make a call to a nis+ server based on the call state in 'state'.
* We loop until we either get a response or until we can no longer
* get a client handle to any server.
*/
{
for (;;) {
break;
}
if (__nis_debug_rpc)
if (__nis_debug_rpc)
if (status == RPC_SUCCESS)
break;
}
return (err);
}
/*
* nis_nameops()
*
* This generic function calls all of the name operations.
*/
static nis_result *
{
int times = 0;
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
}
if (obj) {
/*
* Enforce correct name policy on NIS+ objects stored
* into the namespace. This code insures that zo_name
* and zo_domain are correct.
*/
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
>= sizeof (ndomain))
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
sizeof (ndomain))
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
}
if (obj) {
} else {
}
return (nis_make_error(NIS_NOMEMORY, 0, 0, 0, 0));
times++ < MAX_NOTMASTER)
goto again;
if (err != NIS_SUCCESS)
if (obj) {
}
return (res);
}
/*
* nis_add()
*
* This function will add an object to the namespace. If it is a
* table type object the server will create a table for it as well.
*/
{
if (__nis_debug_calls) {
}
if (__nis_debug_calls)
return (res);
}
static void
{
}
}
}
/*
* nis_remove()
*
* This function will remove an object from the namespace. If it is a
* table type object the server will destroy the table for it as well.
*/
{
if (__nis_debug_calls) {
}
if (__nis_debug_calls)
return (res);
}
/*
* nis_modify()
*
* This function will modify an object in the namespace.
*/
{
if (__nis_debug_calls) {
}
if (__nis_debug_calls)
return (res);
}
/*
* The cookie has the name of the server (null-terminated), followed
* by the "real" cookie from the NIS server. We make a copy of the
* server name, copy the "real" cookie to the beginning of the buffer,
* and then adjust the cookie length. A zero-length cookie means
* that the cookie is not valid and we return 0.
*/
static
char *
{
char *s;
return (0);
return (0);
}
return (s);
}
/*
* We store a server name in a cookie along with the "real"
* cookie from the NIS server. The new cookie will include
* the server name (null-terminated) with the "real" cookie
* following it. If we can't allocate memory, we set the
* cookie length to 0 to indicate that it is an invalid cookie.
*/
static
void
{
char *p;
if (p == 0) {
return;
}
}
/*
* nis_ibops()
*
* This generic function calls all of the table operations.
*
* Note that although we use a virtual circuit, there are no keepalives.
* Because of this, the length of the timeout is vital, and we attempt
* to tune it here for the various operations.
*/
/* a single modify has been seen to take as long as 180 sec. */
/* netgroup.org_dir.ssi has 48K entries, and will take almost this long */
static nis_result *
{
int make_cookie = FALSE;
char *server_name = NULL;
int times = 0;
extern char *__nis_server_name(nis_call_state *);
/*
* Enforce correct name policy on objects stored into
* tables. This code insures that zo_name and zo_domain
* are correct.
*/
sizeof (nname))
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
sizeof (ndomain))
return (nis_make_error(NIS_BADNAME, 0, 0, 0, 0));
}
return (nis_make_error(NIS_NOMEMORY, 0, 0, 0, 0));
/* determine the timeout (heuristic) */
switch (func) {
case NIS_IBMODIFY:
flags = MASTER_ONLY;
break;
case NIS_IBREMOVE:
flags = MASTER_ONLY;
else
break;
case NIS_IBFIRST:
make_cookie = TRUE;
break;
case NIS_IBNEXT:
make_cookie = TRUE;
if (server_name == 0)
return (nis_make_error(NIS_NOMEMORY, 0,
__stop_clock(CLOCK_CLIENT), 0, 0));
break;
default:
flags = MASTER_ONLY;
break;
}
times++ < MAX_NOTMASTER)
goto again;
if (make_cookie) {
if (server_name == NULL)
if (server_name != NULL)
}
if (server_name != NULL)
if (obj) {
}
if (call_err)
return (res);
}
/*
* nis_add_entry()
*
* This function will add an entry to the named NIS table.
*/
{
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
(void) fprintf(__nis_debug_file,
}
if (stat != NIS_SUCCESS) {
if (__nis_debug_calls)
return (res);
}
if (__nis_debug_calls)
return (res);
}
/*
* nis_remove_entry()
*
* This function will remove an entry to the named NIS table.
*/
{
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
(void) fprintf(__nis_debug_file,
}
if (stat != NIS_SUCCESS) {
if (__nis_debug_calls)
return (res);
}
if (__nis_debug_calls)
return (res);
}
/*
* nis_modify_entry()
*
* This function will modify an entry to the named NIS table.
*/
{
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
(void) fprintf(__nis_debug_file,
}
if (stat != NIS_SUCCESS) {
if (__nis_debug_calls)
return (res);
}
if (__nis_debug_calls)
return (res);
}
/*
* nis_first_entry()
*
* This function will fetch the "first" entry in a table.
*/
{
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
table);
}
if (stat != NIS_SUCCESS) {
if (__nis_debug_calls)
return (res);
}
__stop_clock(CLOCK_CLIENT), 0, 0);
if (__nis_debug_calls)
return (res);
}
/* XXX at this point we should put the server in the cookie. */
/* free up memory associated with request */
if (__nis_debug_calls)
return (res);
}
/*
* nis_next_entry()
*
* This function will fetch the "first" entry in a table.
*/
{
(void) __start_clock(CLOCK_CLIENT);
if (__nis_debug_calls) {
}
if (stat != NIS_SUCCESS) {
if (__nis_debug_calls)
return (res);
}
/* free up memory associated with request */
if (__nis_debug_calls)
return (res);
}
{
int times = 0;
(void) __start_clock(CLOCK_CLIENT);
(void) __stop_clock(CLOCK_CLIENT);
return (NULL);
}
times++ < MAX_NOTMASTER)
goto again;
if (call_err != NIS_SUCCESS)
return (res);
}
/*
* nis_mkdir()
*
* This function is designed to allow a client to remotely create
* a directory on a NIS server. When the server is contacted, it
* will look up the directory object and determine if it should
* really execute this command and if it should then everythings
* cool. It returns an error if it can't create the directory.
*/
{
if (__nis_debug_calls) {
}
if (call_err != NIS_SUCCESS)
if (__nis_debug_calls) {
nis_sperrno(err));
}
return (err);
}
/*
* nis_rmdir()
*
* This function is designed to allow a client to remotely remove
* a directory on a NIS server. When the server is contacted, it
* will look up the directory object and determine if it should
* really execute this command and if it should then everythings
* cool. It returns an error if it can't remove the directory.
*/
{
if (__nis_debug_calls) {
}
if (call_err != NIS_SUCCESS)
if (__nis_debug_calls) {
nis_sperrno(err));
}
return (err);
}
{
if (__nis_debug_calls) {
}
(xdrproc_t)0, (char *)0);
if (call_err != NIS_SUCCESS)
if (__nis_debug_calls) {
nis_sperrno(err));
}
return (err);
}
/*
* A version of nis_list that takes a callback function, but doesn't do
* callbacks over the wire (it gets the objects in the reply and then
* feeds them to the callback function itself).
*/
int (*cback)(), /* Callback function. */
void *cbdata) /* Callback private data */
{
int no;
nis_object *o;
char *tab;
int i;
/*
* Do list without callbacks
*/
return (0);
/*
* Run callback locally
*/
if (cback)
case NIS_SUCCESS:
case NIS_S_SUCCESS:
/*
* Always at least one object on success
*/
/*
* Figure out the table name
*/
tab++;
tab++;
} else
/*
* Run callback
*/
for (i = 0; i < no; i++) {
break;
}
/*
* Free objects
*/
for (i = 0; i < no; i++)
xdr_free(xdr_nis_object, (char *)&(o[i]));
/*
* Fixup result
*/
break;
};
return (res);
}