nis_xx_proc.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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 (c) 1991-2001 by Sun Microsystems, Inc.
* All rights reserved.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Ported from
* "@(#)nis_xx_proc.c 1.34 91/04/13 Copyr 1990 Sun Micro";
*
*
* This module contains various routines needed by the NIS version 3
* service and all of the replicate management routines.
* NB : It provides the routines that the dispatch function in nis_svc.c
* call. That file, nis_svc.c, is automatically generated and reflects the
* interface definition that is described in the nis.x file. When the
* nis.x file changes, you must make sure that any parameters that change
* get reflected in these routines.
*
*/
#include <stdio.h>
#include <string.h>
#include <syslog.h>
#include <stdlib.h>
#include <pwd.h>
#include <malloc.h>
#include <rpc/key_prot.h>
#include <rpcsvc/nis_callback.h>
#include <rpc/des_crypt.h>
#include "nis_svc.h"
#include "nis_proc.h"
#include "nis_mt.h"
/* Statistic variables */
extern struct ops_stats nisopstats[];
extern int nislookups;
extern int dircachecall;
extern int dircachemiss;
extern int dircachehit;
extern int __nis_ss_used;
extern int emulate_yp;
extern int resolv_flag;
extern nis_object *get_root_object();
static void rmdir_update(char *);
/*
* This is the signature function, it should sign the data in the
* buffer so that when the client cache routines pass it to the
* cache manager it can trust the answer it gets.
* returns 1 on success and 0 if some error.
*/
#ifdef SIGN_RESULTS
static int
{
unsigned char *digest;
unsigned int digestlen;
int err;
char srch[2048];
struct ticks t;
/*
* no need to sign for these special cases.
* these are used by nisinit, when the principal names haven't
* been set up.
* "multicast" should also be added when that is implemented
*/
return (0);
/* get the netname and the publickey of the requester machine */
if (verbose)
"__sign_off: host2netname failed for: %s", requester);
return (0);
}
return (0);
pkey[0] = 0;
/* we serve this directory so we "know" the public key. */
if (d_obj) {
}
} else {
}
if (pkey[0] == 0) {
"__sign_off: Unable to get publickey for: %s",
return (0);
}
/* get the conversation key from the keyserver */
if (verbose)
"__sign_off: Could not get conversation key for: %s",
return (0);
}
/* calculate the md5 checksum of XDR'ed data and encrypt it */
if (!digest)
return (0);
return (1);
}
#else
static int
{
return (1);
}
#endif
/*
* Return a Find Directory result.
*/
directory_obj *o;
{
if (! res) {
"find_directory_svc: Out of memory, location request aborted.");
return (&mem_err);
}
return (res);
}
/*
* If this is the answer, or "closer" to the
* name they want then just return it.
*/
if (o) {
add_cleanup((void (*)())XFREE,
"fd_res: data");
size1, XDR_ENCODE);
if (!status)
"Unable to encode resulting directory object.");
/* sign the result */
} else {
"find_directory_svc: Out of memory, location request aborted.");
return (res); }
}
return (res);
}
/*
* nis_finddirectory, this routine is called by clients that are looking for
* the names of machines serving the directory they are interested
* in. It responds with a directory object of the directory that the client
* is looking for or the directory object for a directory that is closer to
* the desired directory than this one is. If the server is the desired
* directory it returns an object containing itself.
* NB: There are three possible results :
* This Directory
* My Parent Directory
* A sub directory.
*
*/
{
char *in, *s, *t;
name_pos p;
int i_serve, i;
if (verbose)
"FINDDIR_SVC: Location request for directory %s",
/*
* If we serve this object, it seems a bit silly to look in the
* shared cache. Even if it's there (not at all certain, if it
* isn't our cold start directory), the shared cache copy is probably
* at best up-to-date with the real one. We use __directory_object()
* to check the directory list ('dl') cache, or to retrieve a copy
* from the real database.
*/
{
&tmpobj, 0);
if (dostatus == NIS_SUCCESS) {
}
}
/* Allocate some memory to hold our directory object */
if (! dobj) {
return (&mem_err);
}
/* Read the cache first (won't recurse) */
if (stat != NIS_SUCCESS) {
nis_sperrno(stat));
}
/*
* The location algorithm works as follows: start on the
* easy cases and then work up to the hard ones.
*/
if (p == BAD_NAME) {
} else if (p == SAME_NAME) {
/* Real easy, the cache returned the answer. */
"fd_res: dirobj");
}
s = nis_local_host();
/*
* Step 1. Next easiest
* If the cache result returned is above my directory, I cannot
* possibly serve it. And if the target name is below the
* cache result then we're one step closer to resolving it
* so just return the cache result.
*/
if ((p == LOWER_NAME) &&
== HIGHER_NAME)) {
"fd_res: dirobj");
}
/* determine whether I serve this directory */
if (nis_dir_cmp(s, t) == SAME_NAME)
break;
}
/*
* Step 2, a bit tougher.
* If the directory being located is either above us in
* the tree or above us and down another branch (a sibling), we
* have two options. If the directory the cache produced was one
* that I serve, we return our parent, otherwise we return the
* cached result. If the name is above us and we are the root
* server, we return an error or our parent object
*/
if ((p == HIGHER_NAME) || (p == NOT_SEQUENTIAL)) {
if (! i_serve) {
/* I don't serve it so it is a valid answer */
"fd_res: dobj (cache result)");
}
}
/* free data in the previous directory object */
if (root_server) {
/* Won't need the directory object anymore */
if (! obj)
NIS_NOSUCHNAME, NULL));
"fd_res: parent obj (cache result)");
else
} else {
if (stat == NIS_SUCCESS) {
(char *)dobj, "fd_res: dobj (parent)");
dobj));
} else {
}
}
}
if (root_server) {
NIS_NOSUCHNAME, NULL));
}
}
/*
* Step 3, toughest one
* By process of elimination I know the name is below
* some cache entry that got returned. That could be either
* something I serve or the pointer to my parent. In either
* case, I start searching my database from the name passed
* upward toward the root. Four things can happen
* 1) If I find the one we're looking for then great, I'll
* just return it.
* 2) If I never find any thing closer (ie a directory object
* in it's path then my parent can deal with it.
* 3) If I find a directory in the desired directories "path"
* that I don't serve, we pass the request on to those
* servers.
* 4) If I find a directory that I serve above it in the name
* space then the target directory cannot exist. Because
* if it did I would have found it's directory object
* before finding a directory that I serve.
*
* We start using the whole name and selectively pick off
* leaves until we're up to the our local directory.
*/
if (verbose)
/*
* POLICY : You can't link directories with
* LINK objects.
* ANSWER : Because the recursion could become
* infinite.
*/
if (verbose)
"Object %s isn't a directory.",
NULL);
} else {
/*
* we found a directory object in the "path"
* of the desired object.
*/
if (verbose)
"Found a directory %s",
if (p == SAME_NAME) {
NIS_SUCCESS, dobj);
/*
* It isn't the answer (#1) so let's see if we
* serve it. (#3 or #4)
*/
} else {
s = nis_local_host();
for (i = 0;
i++) {
t =
if (nis_dir_cmp(s, t)
== SAME_NAME)
break;
}
else
NIS_SUCCESS, dobj);
}
}
return (res);
NULL));
}
}
if (verbose) {
}
/* Option #2, let my parent deal with it. */
if (stat == NIS_SUCCESS) {
"fd_res: dobj (parent)");
}
}
/*
* nis_callback_svc
*
* Check on the state of a child process or thread who is calling back to the
* client.
*/
bool_t *
{
return (&res);
}
/* Get name of principal calling us */
/*
* If an unauthenticated user issued a nis_list() for a table on
* which "nobody" has read rights, then the callback rpc.nisd
* will be running on behalf of nobody. Hence, it is not appropriate
* for us to verify authentication; we just check that the
* principal behind the NIS_CALLBACK, and the one who initiated the
* callback, is one and the same. If the initator name is "nobody",
* anything goes.
*/
/* Obtain pid and principal on whose behalf pid is running */
id_pname[0] = '\0';
if (verbose)
"CALLBACK_SVC: ID = %ld, anon id = %ld, pname = %s, id_pname = %s",
/* Verify caller principal same as pid principal */
return (&res);
}
return (&res);
}
/* Names of the operations for statistics functions. */
static char *opnames[] = {
"NULLPROC", /* 0 */
"LOOKUP", /* 1 */
"ADD", /* 2 */
"MODIFY", /* 3 */
"REMOVE", /* 4 */
"LIST", /* 5 */
"ADDENTRY", /* 6 */
"MODENTRY", /* 7 */
"REMENTEY", /* 8 */
"FIRSTENTRY", /* 9 */
"NEXTENTRY", /* 10 */
"RSRVD1", /* 11 */
"FINDDIR", /* 12 */
"RSRVD2", /* 13 */
"STATUS", /* 14 */
"DUMPLOG", /* 15 */
"DUMP", /* 16 */
"CALLBACK", /* 17 */
"CPTIME", /* 18 */
"CHECKPOINT", /* 19 */
"PING", /* 20 */
"SERVSTATE", /* 21 */
"MKDIR", /* 22 */
"RMDIR", /* 23 */
"UPDKEYS" /* 24 */
};
static char *
prtop(p)
int p;
{
int i;
static char opbuf[80];
return (opbuf);
}
op = &(nisopstats[p]);
if (i)
return (opbuf);
}
extern struct timeval start_time;
static void
up_since(s)
char *s;
{
}
static void
{
}
#define CHECK_TAG_S_ACC(tag) \
reqstp)) { \
break; \
}
/*
* nis_status, return status on the nis server.
*/
{
int i, nt, j;
char *dirs; /* malloc:ed string for DIR_LIST */
int grpcachecall;
int grpcachemiss;
int grpcachehit;
extern int __nis_group_cache_stats(int *, int *, int *);
extern unsigned int heap_start;
if (verbose)
/* malloc memory for result */
return (NULL);
}
return (NULL);
}
for (i = 0; i < nt; i++) {
case TAG_UPTIME :
CHECK_TAG_S_ACC("TAG_UPTIME");
break;
case TAG_S_DCACHE :
CHECK_TAG_S_ACC("TAG_S_DCACHE");
if (dircachecall)
"C=%d:H=%d:M=%d:HR=%2.0f%%",
else
"C=0:H=0:M=0:HR=100%%");
break;
case TAG_S_STORAGE :
CHECK_TAG_S_ACC("TAG_S_STORAGE");
break;
case TAG_S_GCACHE :
CHECK_TAG_S_ACC("TAG_S_GCACHE");
&grpcachehit, &grpcachemiss) &&
grpcachecall != 0)
"C=%d:H=%d:M=%d:HR=%2.0f%%",
else
"C=0:H=0:M=0:HR=100%%");
break;
case TAG_OPSTATS :
CHECK_TAG_S_ACC("TAG_OPSTATS");
tag_data[0] = '\0';
for (j = 0; j < 24; j++) {
if (! nisopstats[j].calls)
continue;
}
} else {
}
break;
case TAG_HEAP :
CHECK_TAG_S_ACC("TAG_HEAP");
(unsigned int) sbrk(0) - heap_start);
break;
case TAG_DIRLIST:
CHECK_TAG_S_ACC("TAG_DIRLIST");
DIR_GETLIST, &dirs) == 0) {
}
break;
case TAG_NISCOMPAT:
CHECK_TAG_S_ACC("TAG_NISCOMPAT");
if (emulate_yp)
else
break;
case TAG_DNSFORWARDING:
CHECK_TAG_S_ACC("TAG_DNSFORWARDING");
if (resolv_flag)
else
break;
case TAG_SECURITY_LEVEL:
CHECK_TAG_S_ACC("TAG_SECURITY_LEVEL");
break;
case TAG_ROOTSERVER:
CHECK_TAG_S_ACC("TAG_ROOTSERVER");
if (root_server)
else
break;
default :
break;
}
else
return (NULL);
}
}
return (res);
}
/*
* nis_cptime()
*
* This function will return the timestamp of the last stable transaction
* from the server. All of the timestamps that are maintained in a cluster
* are generated by the master so there is no time skew. This function
* is used by the master to ping replicas and figure out whether it can
* truncate the log or not.
*/
uint_t *
{
if (verbose)
if (verbose)
"CPTIME_SVC: authorization error on \"%s\"", *argp);
res = 0;
return (&res);
}
return (&res);
}
/*
* nis_checkpoint()
*
* This function sets up a checkpoint to be done, as you might want to do
* if the log were getting too full. The parameter is the name of the
* directory you want to checkpoint. In addition to checkpointing the
* NIS+ log, it checkpoints and local database logs that may need this.
* Note that the actual checkpointing is done later, in the
* main server loop, after the server has forked a read-only child.
* Should not fork here because we're in the middle of an RPC.
*
*/
{
struct ticks t;
/* xxx static should be const */
if (verbose)
if (! res)
return (&mem_err);
if (verbose)
"CHECKPOINT_SVC: authorization error on \"%s\"",
*argp);
return (res);
}
__start_clock(0);
/*
* Maintain '*argp' in a list of directories to be checkpointed.
* and perform do_checkpoint_dir(*argp) over list when checkpointing
* eventually occurs.
* If argument is null string, checkpoint all.
*/
clear_checkpoint_list(); /* delete remembered items */
checkpoint_all = 1;
} else {
/* Otherwise, specified directory. make sure we serve it. */
if (stat != NIS_SUCCESS) {
return (res);
}
/* Only add to list if we are not going to cp entire db */
if (!checkpoint_all)
}
/*
* Set status to SUCCESS here to indicate that directory
* object is valid and will be checkpointed.
*/
force_checkpoint = 1;
/*
* The non-MT code will try to checkpoint immediately, so in order
* to preserve that behavior, we wake up the servloop() thread.
*/
return (res);
}
/*
* nis_dumplog, dump the log from the indicated timestamp on.
*/
{
char pname[1024];
int ns;
int i;
if (verbose)
"nis_dumplog_svc: replica asking for time 0. RESYNC.");
return (tmp);
}
/*
* Now we verify that a valid replica is actually asking for this data.
* NB: We look up the object from the master server to pickup the
* latest copy of the object. This lets us see new replicas as
* they are added.
*/
if (verbose)
"nis_dump_svc: No directory object, error %s.",
return (tmp);
}
/*
* Make sure it is a directory they want dumped.
*/
"nis_dump_svc: request to dump %s.%s which isn't a directory!",
return (tmp);
}
/*
* Make sure that we are the master server for that directory.
*/
return (tmp);
}
/*
* Check to see that a valid replica is asking for the dump
*/
if (secure_level) {
for (i = 1; (i < ns) &&
;
if (i == ns) {
if (verbose)
"nis_dump_svc: invalid replica '%s'", pname);
return (tmp);
}
}
if (verbose)
/*
* Pass the whole directory object so that entries_since()
* can add it to the ping list should it not return all
* entries this time.
*/
/* don't need this anymore */
if (verbose)
"nis_dumplog_svc: returning status of '%s', and %d deltas.",
return (tmp);
}
/*
* nis_dump_svc, dump the entire contents of the named directory.
*/
#define CB_BUF_SIZE 128
{
char pname[1024];
int stat;
int i;
int ns;
struct netbuf *rpc_origin;
if (verbose)
/*
* Start the series of checks that need to be met before dumping
* can begin.
*/
if (verbose)
return (res);
}
/*
* XXX Do we need to limit the number of dumping threads ?
* No, for now, we just rely on the serialization implicit
* in the use of the __nis_callback_lock mutex in nis_dump()
* (in libnsl).
*/
/*
* Now we verify that a valid replica is actually asking for this data.
* NB: We look up the object from the master server to pickup the
* latest copy of the object. This lets us see new replicas as
* they are added.
*/
if (verbose)
"nis_dump_svc: No directory object, error %s.",
return (res);
}
/*
* Make sure it is a directory they want dumped.
*/
"nis_dump_svc: request to dump %s.%s which isn't a directory!",
return (res);
}
/*
* Make sure that we are the master server for that directory.
*/
return (res);
}
/*
* Check to see that a valid replica is asking for the dump
*/
if (secure_level) {
for (i = 1; (i < ns) &&
;
if (i == ns) {
if (verbose)
"nis_dump_svc: invalid replica '%s'", pname);
return (res);
}
}
/*
* The replica supplied an address for the callback service.
* However, we may not be able to reach that address, so
* try the source address of the RPC request first.
*
* Note: The alt_endpoint array (which usually contains just
* a single element) is a copy of of org_endpoint, including
* pointers, except for the uaddr of one element. This uaddr
* contains a merged version of the RPC source address and the
* port number specified by the client in the callback data.
*
* The copying tp 'cbsrv' and 'epbuf' below is done so that we
* can free any allocated memory immediately, and won't have to
* worry about that later.
*/
epbuf = alt_endpoint[0];
}
/*
* XXX now, do we use the handle that was passed us? or do we use
* the one in the object?
*/
if (! cback) {
if (verbose)
"nis_dump_svc: unable to create callback.");
return (res);
}
/* don't need this anymore */
"nis_dump_svc: Current updates found, try later");
return (res);
} else if (ttime == 0)
"nis_dump_svc: directory %s has no update timestamp.",
else {
if (! le) {
return (res);
}
"dump logent->princp");
"dump logent->name");
}
"nis_dump_svc: memory allocation failed for %d bytes",
sizeof (*mtdumparg));
return (res);
}
(void) pthread_attr_init(&attr);
0) {
if (verbose)
"nis_dump_svc: created callback thread %d", tid);
"anonid");
sizeof (anonid_t));
} else {
"nis_dump_svc: no memory for callback id cookie");
}
if (verbose)
"nis_dump_svc: Parent thread returning");
} else {
if (verbose)
"nis_dump_svc: callback thread create failed: %d",
stat);
}
(void) pthread_attr_destroy(&attr);
return (res);
}
/*
* nis_ping()
*
* This function receives a "ping" and schedules an update session.
*/
void *
{
static int foo;
struct ticks t;
if (verbose)
if (verbose)
return (0);
}
/* Check to see if we're replicating the root object */
/*
* Note that we cannot check whether we have the root object
* because this ping might be asking to add me as replica
*/
err = NIS_SUCCESS;
} else {
}
else if (verbose)
/*
* The non-MT code would have tried to execute the resync
* immediately. In order to preserve those semantics, we wake
* up the servloop() thread now.
*/
return (&foo);
}
#define CHECK_TAG_ACC(tag) \
reqstp)) { \
break; \
}
/*
* This function allows the client to change various "state"
* bits in the server. The primary uses are to turn verbosity
* on and off, and to turn statistics on and off.
*/
{
int i;
if (verbose)
switch (TAG_TYPE(i)) {
case TAG_DEBUG :
CHECK_TAG_ACC("TAG_DEBUG");
verbose = 1;
verbose = 0;
else {
}
break;
case TAG_DCACHE_ONE:
CHECK_TAG_ACC("TAG_DCACHE_ONE");
} else {
}
break;
case TAG_DCACHE_ONE_REFRESH:
CHECK_TAG_ACC("TAG_DCACHE_ONE_REFRESH");
} else {
}
break;
case TAG_DCACHE_ALL:
/* This flushes _all_ directory objects */
CHECK_TAG_ACC("TAG_DCACHE_ALL");
break;
case TAG_TCACHE_ONE :
CHECK_TAG_ACC("TAG_TCACHE_ONE");
flush_tablecache(TAG_VAL(i));
} else {
}
break;
case TAG_TCACHE_ALL:
/* This flushes _all_ table caches */
CHECK_TAG_ACC("TAG_TCACHE_ALL");
break;
case TAG_GCACHE_ONE :
CHECK_TAG_ACC("TAG_GCACHE_ONE");
flush_groupcache(TAG_VAL(i));
} else {
}
break;
case TAG_GCACHE_ALL :
CHECK_TAG_ACC("TAG_GCACHE_ALL");
break;
case TAG_HEAP :
CHECK_TAG_ACC("TAG_HEAP");
#ifdef MEM_DEBUG
xdump();
#else /* MEM_DEBUG */
#endif /* MEM_DEBUG */
break;
case TAG_READONLY:
/* Disables updates, used for nisbackup(1M) */
CHECK_TAG_ACC("TAG_READONLY");
break;
}
readonly = 1;
break;
case TAG_READWRITE:
CHECK_TAG_ACC("TAG_READWRITE");
break;
}
readonly = 0;
break;
default :
TAG_TYPE(i));
break;
}
}
}
return (argp);
}
extern table_obj tbl_prototype;
/*
* nis_mkdir_svc()
*
* This function allows a client to "make" a directory table remotely.
* The purpose of this function is to allow for the addition of NIS+
* directories by an application rather than by hand. See the companion
* function below, nis_rmdir_svc(). Note: It _depends_ on the fact that
* if the directory object exists, and this server is included as one
* of the valid servers, then it is allowed to create the directory.
* This means that if the addition of the directory object to the namespace
* is managed, the subsequent creation of the table will just follow along.
* It also means that if your namespace is compromised you could create
* a bogus directory. During that time you can run in "max-secure" mode
* which disables both the make and remove directory functions.
*/
{
int i;
nis_name s;
name_pos p;
/* Go look for the object for this new directory. */
if (verbose)
if (readonly) {
"nis_mkdir_svc: readonly child called to mkdir, ignored.");
return (&result);
}
/* Check NIS_MKDIR access to parent directory */
if (verbose)
"MKDIR_SVC : authorization error on \"%s\"",
nis_domain_of(*argp));
return (&result);
}
/*
* We see if we can find the directory object for the directory
* that we are "making". If not then we abort.
* NOTE: The nis_lookup should recurse to our local version of
* __nis_core_lookup.
*/
/* Schedule this to be cleaned up when we return */
if (result != NIS_SUCCESS) {
if (verbose)
"nis_mkdir_svc: could not get the directory object for %s: %s",
return (&result);
}
/*
* Now we either have an object that is a directory object or
* we don't.
*/
*argp);
return (&result);
}
/*
* verify that we serve it.
*/
s = nis_local_host(); /* optimization */
break;
}
"nis_mkdir_svc: Attempt to add a directory %s which we don't serve!",
*argp);
result = NIS_NOT_ME;
return (&result);
}
if (p != LOWER_NAME) {
"nis_mkdir_svc: Attempt to create illegal directory %s",
*argp);
return (&result);
}
/*
* Now we check to see if we can read anything out of the
* directories database.
*/
if (result == NIS_NOSUCHTABLE) {
NIS_SUCCESS) {
return (&result);
}
/* Give it a stability timestamp. */
/* Add it to the list of directories that it serves */
}
if (verbose)
return (&result);
}
/*
* nis_rmdir_svc()
*
* This is the opposite of mkdir. It will iterate through a directory
* removing all tables in that directory, and then remove the directory
* itself. Beware recursing on yourself, since if this function makes
* a request to this server you will deadlock.
*
* Note, rmdir is a little weird. If we are removing the directory
* completely, (i.e out of the name space) then there won't be a
* directory object to verify it. The easy case is if we are just
* getting dropped of the list of servers that serve it.
*/
{
int i; /* Loop counter */
nis_name s; /* Another temporary */
*rrs; /* From removing entries */
*tlist;
int xid;
int complete_removal = 1;
char *principal = 0;
/* Go look for the object for this new directory. */
if (verbose)
if (readonly) {
"nis_rmdir_svc: readonly child called to rmdir, ignored.");
return (&result);
}
/*
* If 'rqstp' is NULL, we've been called internally, and supply the
* local principal.
*/
if (rqstp == 0)
/* Check NIS_RMDIR access to directory */
if (verbose)
"RMDIR_SVC : authorization error on \"%s\"", *argp);
return (&result);
}
/*
* First we get the directory object. If it doesn't exist we consider
* that to be an ok error since we could have been its only server etc.
*/
/* schedule a cleanup of these results */
return (&result);
/*
* Now determine whether or not the object we lookup up is a
* directory and that we no longer serve it.
*/
return (&result);
}
s = nis_local_host(); /* optimization */
"nis_rmdir_svc: Attempt to remove directory %s which we still serve!",
*argp);
result = NIS_NOT_ME;
return (&result);
}
}
/*
* A complete directory removal works by first removing the
* directory object from the name space and then doing
* individual 'rmdir' operations on each server.
*
* If directory object still exists, that means we are not
* doing a complete removal and hence cannot be its master.
*
* If directory object does not exist, we act as if we are
* master (because we cannot tell without the directory obj)
* and abort the removal if it contains subdirectories.
* Continuing with the removal in that case would leave
* the subdirectories orphaned and inaccessible.
*
* Note that we cannot tell if it is a replica trying
* to clean up after the directory has already been removed
* earlier, or it is doing a rmdir at the time or the remove.
*/
complete_removal = 0;
}
/*
* Now, we definitely know that the directory needs to be deleted.
* So we attempt the actual removal of the directory. We wish to remove
* every object and all tables in the directory. To accomplish this
* we need to iterate over the entire contents. This is accomplished
* by calling db_firstib() and if the object returned is a table, we
* delete the table contents. The entire operation is bracketed in
* a transaction to allow us to abort it if something goes wrong.
*/
return (&result);
/* table is empty */
"nis_rmdir_svc: Could not remove directory table %s: %s.",
}
rmdir_update(*argp);
return (&result);
return (&result);
}
error = NIS_SUCCESS;
if (! xid) {
return (&result);
}
/*
* If we are getting rid of a directory with subdirectories,
* abort.
*/
break;
}
ol.r = 1;
/* free this since we won't be using it */
break;
}
/*
* Note : We call firstib again, rather than
* calling nextib because our removing
* the entry may have screwed up the
* internal database ptrs. db_firstib()
* always works.
*/
}
}
}
if (error != NIS_SUCCESS)
break;
/* This remove will also destroy the table */
break;
}
/* free this because we aren't going to call nextib */
}
}
if (error != NIS_SUCCESS) {
"nis_rmdir_svc: Could not remove directory table %s: %s.",
} else {
"nis_rmdir_svc: Could not remove directory table %s: %s.",
}
rmdir_update(*argp);
}
return (&result);
}
/*
* If we serve the parent directory or the directory is
* the root, then do an update of that directory so that
* we see the change to the parent directory or root
* object. If we don't do this, then we will continue
* to see the removed directory in the database until
* the master server pings us.
*/
static
void
char *name;
{
if (rootobj &&
} else if (parent) {
}
}
/* if we can't get the directory object, we can't do an update */
goto skip_update;
/*
* If we are running on the master server, or if we don't
* serve the parent directory, then skip the update.
*/
goto skip_update;
/*
* If we get here, then we are a replica and we need to update
* the parent directory.
*/
/*
* Updating root object.
*/
} else {
/*
* Use a very large timestamp to force an
* update to the directory.
*/
}
if (rootobj)
if (lres)
/* Delete name from the list of directories that we serve */
}