nis_main.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 1990-2003 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
*
* This is the main() module for the NIS+ service. It is compiled separately
* so that the service can parse certain options and initialize the database
* before starting up.
*/
#include <stdio.h>
#include <stdlib.h>
#include <syslog.h>
#include <signal.h>
#include <ucontext.h>
#include <time.h>
#include <string.h>
#include <wait.h>
#include <sys/resource.h>
#include <errno.h>
#include <dirent.h>
#include "nis_svc.h"
#include "nis_proc.h"
#include "log.h"
#include <stropts.h>
#include <poll.h>
#include <limits.h>
#include <rpcsvc/nis_dhext.h>
#include <ldap_parse.h>
#include <ldap_util.h>
#include <values.h>
#include <unistd.h>
/* Default size for the RPC credential cache */
#define CRED_CACHESZ_DEF 1024
#ifdef MEM_DEBUG
extern void init_malloc();
extern void xdump();
#endif
extern int (*__clear_directory_ptr)(nis_name);
extern int clear_directory(nis_name);
/* Defined in nis_log_common.c */
extern int need_checkpoint; /* The log is "dirty" */
int _rpcsvcdirty;
int force_checkpoint = 0; /* Set when we wish to force a c.p. op */
int checkpoint_all = 0; /* Set when we wish to cp entire db */
int readonly = 0; /* When true the service is "read only" */
int readonly_pid = 0; /* Our child who is watching out for us */
int hup_received = 0; /* To tell us when to exit */
int auth_verbose = 0; /* Messages on auth info */
int resolv_pid = 0; /* Resolv server pid */
extern unsigned long __maxloglen; /* maximum transaction log size */
unsigned int heap_start; /* sbrk(0) at start of time */
#ifdef DEBUG
int debug = 1;
#else
int debug = 0;
#endif
extern char *optarg;
extern nis_object* get_root_object();
extern cp_result *do_checkpoint();
extern void nis_prog_svc();
extern void ypprog_svc();
extern void ypprog_1();
/* routines for update timestamp cache */
extern void init_updatetime();
extern void *__nis_lock_db_directory(nis_name, int, int *, char *);
extern int __nis_ulock_db_directory(nis_name, int, int, char *);
/*
* Global state variables, these variables contain information about the
* state of the server.
*/
int verbose = 0; /* Verbose mode, LOG_INFO messages are */
/* generated for most functions. */
int root_server = 0; /* TRUE if this server is a root server */
int static_root = 0; /* TRUE if the network is partitioned. */
int resolv_flag = 0; /* Resolv on no host match */
/* List of directory names that need to */
/* be notified of updates */
int ping_pid = 0; /* Pinger pid for pinger process */
/* List of directory names that have */
/* pending updates */
/* List of directory names that have */
/* pending checkpoint */
static void
int proc;
{
hup_received = 1;
}
void
{
int pid;
int pending_updates = 0;
/* Check if any item on the list is due for update */
__nis_init_hash_table(&tmpupd, 0);
pending_updates = 1;
/* Save a copy on the local list */
/* Copy pointers OK */
(void) __nis_insert_item_mt(tmppp,
&tmpupd, 0);
}
#ifdef NIS_MT_DEBUG
else {
abort();
}
#endif /* NIS_MT_DEBUG */
}
}
if (!pending_updates) {
if (verbose)
"check_updaters: no pending updates");
return;
}
}
if (cons)
if (verbose) {
}
if (cons)
if (replica_update(pp)) {
&upd_list);
}
/* The 'tmpupd' list is purged later */
} else {
/*
* No use continuously retrying, so backoff
* exponentially.
*/
if (cons)
"check_updaters: unable to resync %s\n",
"check_updaters: unable to resync %s",
}
-1);
}
}
}
}
}
/*
* We just copied the 'item.name' and 'obj' pointers for the
* elements in the 'tmpupd' list, so we only need to free the
* list elements themselves.
*/
}
}
void
{
return;
}
gettimeofday(&ctime, 0);
if (cons)
/* save next pointer in case we remove it */
continue;
/*
* A successful return from ping_replicas() means that
* a ping request has been sent to the replicas. It does
* not ensures that replica has indeed received the ping
* request.
*/
if (!ping_replicas(pp))
}
}
#include <netconfig.h>
extern int __rpc_bindresvport_ipv6(int, struct sockaddr *, int *, int,
char *);
/*
* A modified version of svc_tp_create(). The difference is that
* nis_svc_tp_create() will try to bind to a privilege port if the
* the server is to emulate YP and it's using the INET[6] protocol family.
*
* The high level interface to svc_tli_create().
* It tries to create a server for "nconf" and registers the service
* with the rpcbind. It calls svc_tli_create();
*/
static SVCXPRT *
void (*dispatch)(); /* Dispatch function */
{
int fd;
"nis_svc_tp_create: invalid netconfig structure for prog %d vers %d",
}
if (fd == -1) {
"nis_svc_tp_create: could not open connection for %s",
}
(int *)NULL, 8,
}
}
}
"nis_svc_tp_create: Could not register prog %d vers %d on %s",
}
return (xprt);
}
/*
* Modified version of 'svc_create' that maintains list of handles.
* The only difference between this and svc_create is that
* 1. nis_svc_create maintains the list of handles created so that they
* can be reused later for re-registeration.
* This is required for nis_put_offline.
* 2. nis_svc_create uses the public netconfig interfaces, instead of
* private rpc interfaces.
*/
struct xlist {
};
/*
* Note: nis_xprtlist is only used in nis_svc_create() and nis_svc_reg()
* below. Since nis_svc_create() is used only in the initial,
* single-threaded, phase of the rpc.nisd, no synchronization is
* needed.
*/
/* A link list of all the handles */
static int
void (*dispatch)(); /* Dispatch function */
unsigned target_nc_flag; /* value of netconfig flag */
{
struct xlist *l;
int num = 0;
"nis_svc_create: could not get netconfig information");
return (0);
}
continue;
for (l = nis_xprtlist; l; l = l->next) {
/* Found an old one, use it */
"nis_svc_create: could not register prog %d vers %d on %s",
else
num++;
break;
}
}
/* It was not found. Now create a new one */
if (xprt) {
"nis_svc_create: no memory");
return (0);
}
l->next = nis_xprtlist;
nis_xprtlist = l;
num++;
}
}
}
/*
* In case of num == 0; the error messages are generated by the
* underlying layers; and hence not needed here.
*/
return (num);
}
/*
* Establish resync service for the specified directory, per the
* nisplusLDAPresyncService and nisplusLDAPdumpError configuration
* attributes.
*/
{
switch (ldapConfig.resyncService) {
case directory_locked:
if (!fullDump) {
/* Write lock the specified directory */
NULL) {
if (verbose)
"nis_put_offline: Error locking \"%s\"", dirname);
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
}
break;
}
/* Else, if full dump, fall through to 'from_copy' */
case from_copy:
/*
* Defer changes for the directory.
*/
break;
case from_live:
/* Nothing to do */
break;
default:
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
break;
}
return (stat);
}
/*
* Resume full service for specified directory
*/
{
int ret = 1;
switch (ldapConfig.resyncService) {
case directory_locked:
if (!fullDump) {
/* Unlock the directory */
dirname)) {
if (verbose)
"nis_put_online: Error unlocking \"%s\"", dirname);
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
}
break;
}
/* Else, if full dump, fall through to 'from_copy' */
case from_copy:
/* Commit or rollback */
if (commitAction == d_commit)
else if (commitAction == d_rollback)
else {
stat = DB_BADQUERY;
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
}
break;
case from_live:
/* Nothing to do */
break;
default:
#ifdef NIS_MT_DEBUG
abort();
#endif /* NIS_MT_DEBUG */
break;
}
return (stat);
}
static void
{
/* make sure the cache data is accurate */
}
static void
{
exit(0);
}
/*
* Loop thru mech list from security conf file and set
* the RPC GSS service name(s). Stop processing list if
* the classic AUTH_DES compat entry is encountered.
*/
static void
{
mechanism_t **mechs;
int slen;
mechanism_t **mpp;
char *lh = nis_local_host();
if (! lh) {
"can't set RPC GSS service name: can't get local host name");
return;
}
/* '@' + NUL = 2 */
sizeof (svc_name)) {
"can't set RPC GSS service name: svc_name bufsize too small");
return;
}
/* service names are of the form svc@server.dom */
/* remove trailing '.' */
if (AUTH_DES_COMPAT_CHK(mp))
break;
if (! VALID_MECH_ENTRY(mp)) {
"%s: invalid mechanism entry name '%s'",
continue;
}
0, NIS_PROG,
NIS_VERSION)) {
if (verbose)
"RPC GSS service name for mech '%s' set",
} else {
if (secure_level > 1) {
"can't set RPC GSS svc name '%s' for mech '%s': RPC GSS err = %d, sys err = %d",
} else {
if (verbose)
"can't set RPC GSS service name for mech '%s'",
}
}
}
return;
}
}
void
int argc;
char *argv[];
{
int status = 0, i, c;
char buf[80];
char logname[80];
struct stat s;
int pid;
int minfd;
int open_console = 0;
int sig_recvd;
int cred_cache = 0;
int rpc_irtimeout = -1;
int concurrency = -1;
char *ldapConfFile = 0;
char **ldapCLA = 0;
int numLA = 0;
/*
* increase the internal RPC server cache size to 1024.
* If it fails to increase, then just use the default (=128).
*/
int connmaxrec = -1;
int fdlmtremove = 8192;
/*
* We cannot use the shared directory cache yet (possible
* deadlock), so we start up the local cache.
*/
(void) __nis_CacheLocalInit(&next_refresh);
/* Set number of file descriptors to unlimited */
"unable to set RPC file descriptors to unlimited");
}
/*
* __clear_directory_ptr is a global defined in libnsl.
* We need to set this here so that clear_directory() be called from
* within nis_dump. This is part of the servers serving stale data
* bug fix. See 1179965.
*/
/*
* Make sure that files created by stdio do not have
* extra permission. We allow group read, but we don't
* allow world to read or write. We disallow write for
* obvious reasons, but also disallow read so that
* tables can't be read by world (thus bypassing the
* NIS+ access controls.
*/
(void) umask(027);
/*
* Process the command line arguments
*/
opterr = 0;
-1) {
switch (c) {
case 'h' : /* internal help screen */
break;
case 'T' :
exit(1);
}
break;
case 'C' :
open_console++;
break;
case 'F' :
break;
case 'A' :
auth_verbose++;
break;
case 'Y' :
emulate_yp = TRUE;
break;
case 'B' :
resolv_flag = TRUE;
break;
case 't' :
break;
case 'v' :
verbose = 1;
break;
case 'd' :
break;
case 'S' :
break;
case 'r' :
/* obsolete option */
root_server = -1;
"The -r option is obsolete and no longer necessary for root servers.\n");
break;
case 'f' :
break;
case 'p' :
"Illegal credential cache size.\n");
exit(1);
}
break;
case 'i' :
if (rpc_irtimeout < 0) {
"Illegal rpc inter-record timeout.\n");
exit(1);
}
break;
case 'L' :
if (max_children <= 0) {
exit(1);
}
break;
case 'z' :
break;
case 'D' :
debug = 1;
break;
case 'n':
break;
case 'm':
/* Config file name */
break;
case 'x':
/* Attribute assignment */
if (ldapCLA == 0) {
"Out of memory. realloc(%d) => NULL\n",
exit(1);
}
break;
case '?' :
"usage: rpc.nisd [ -ACDFhlv ] [ -Y [ -B [ -t netid ]]]\n");
"\t[ -d dictionary ] [ -L load ] [ -S level ]\n");
"[ -x attr=val] [ -z max record size]\n");
exit(1);
}
}
/*
* The "emulate YP" option can be requested on the command line
* or in the defaults file; command-line option overrides the
* file. The value from the defaults file (ENABLE_NIS_YP_EMULATION=),
* obtained via parseConfig above, only needs to be checked if
* command-line option -Y is NOT used.
*/
if (emulate_yp == FALSE)
/* Complete syntax checking now that defaults are processed. */
if (resolv_flag == TRUE) {
if (emulate_yp == FALSE) {
"Option -B requires option -Y also.\n");
exit(1);
}
}
if (resolv_flag == FALSE) {
"Option -t requires options -Y and -B also.\n");
exit(1);
}
}
if (! debug) {
switch (fork()) {
case -1:
exit(1);
case 0:
break;
default:
exit(0);
}
closelog();
closefrom(0);
(void) dup(1);
}
#ifdef MEM_DEBUG
init_xmalloc();
#endif
if (open_console == 1)
else if (open_console > 1)
gettimeofday(&start_time, 0);
master_pid = getpid();
if (verbose)
if (!cred_cache)
/* set the credential cache size */
"rpc.nisd: cannot set credential cache size to %d",
else
if (verbose)
"rpc.nisd: credential cache size set to %d",
/* set RPC inter-record timeout */
if (rpc_irtimeout >= 0)
"rpc.nisd: cannot set ir timeout to %d",
else
if (verbose)
"rpc.nisd: ir timeout set to %d seconds",
/* Parse LDAP mapping config */
{
if (stat == 1) {
"NIS+/LDAP mapping inactive");
} else if (stat != 0) {
"Aborting after NIS+/LDAP mapping parse error");
exit(1);
} else {
"NIS+/LDAP mapping parsed and initialized");
}
}
/*
* The data base functions use stdio. The stdio routines
* can only handle file descriptors up to 255. To make
* more of these low-numbered descriptors available, we
* bump up the file descriptor limit, and tell the RPC
* library (our other main source of file descriptors) to
* try to use descriptors numbered 256 and above.
*/
minfd = 256;
if (nis_local_directory() == NULL) {
if (debug)
else
exit(1);
}
if (debug) {
}
/*
* Set non-blocking mode, and establish maximum record size,
* for connection oriented RPC transports.
*/
{
/* If not set via the '-z' option, use the attribute value */
if (connmaxrec == -1)
if (connmaxrec < RPC_MAXDATASIZE) {
"Illegal max rpc record size specified %d, "
"setting to default %d",
}
"unable to set maximum RPC record size of %d",
}
}
/* Establish number of RPC service threads */
{
int mtmode = RPC_SVC_MT_AUTO;
/* If not set via the '-n' option, use the attribute value */
if (concurrency < 0)
/* Zero means three plus number of processors available */
if (concurrency <= 0) {
long numprc;
/*
* If the requested functionality isn't available,
* sysconf(3C) returns -1 and leaves errno unchanged.
* Hence, establish a good default errno.
*/
if (numprc <= 0) {
"Unable to determine number of "
"processors on-line; assuming one: %m");
numprc = 1;
}
}
}
"Could not set RPC concurrency = %d",
}
}
/*
* Fix for bug #1248972 - Block SIGCHLD in the parent
* thread, so all subsequent threads will inherit the
* same signal mask - i.e. block SIGCHLD.
*/
(void) sigemptyset(&new_mask);
if (debug) {
}
/*
* We're still single-threaded, so no need to acquire the
* 'setup_resolv' lock.
*/
if (resolv_flag)
&resolv_client, resolv_tp, 0);
if (!dict) {
perror("rpc.nisd");
"rpc.nisd: Unable to create NIS+ directory %s", buf);
exit(1);
}
} else {
perror("rpc.nisd");
"rpc.nisd: unable to stat NIS+ directory %s.", buf);
exit(1);
}
}
/*
* Handle the case for a host called data:
* - ONly the transaction log needs to be
* renamed.
* - the dict has already been massaged and
* named correctly.
*/
nis_leaf_of(nis_local_host())) == 0) {
char oldstr[NIS_MAXNAMELEN];
char newstr[NIS_MAXNAMELEN];
"Unable to rename NIS+ transaction log.");
exit(1);
}
/* No need to massage dict */
} else {
"Old and new dir structures cannot coexist.");
exit(1);
}
} else { /* Old, No New => massage dict. */
massage_dict = TRUE;
}
}
if (debug)
if (status == 0) {
if (debug)
else
exit(1);
}
/*
* Now, rename the `hostname` directory if necessary
* and massage the dictionary file. This must be done
* _after_ the dictionary has been initialiazed. Remember,
* the dictionary would have been initialized with dict name
* based on the old structure.
*
*/
if (massage_dict) {
char oldstr[NIS_MAXNAMELEN];
char newstr[NIS_MAXNAMELEN];
char newdict[NIS_MAXNAMELEN];
/* Massage the dictionary file */
"Unable to change database dictionary.");
exit(1);
}
/*
* Now, rename the old structure. This includes the following:
* - directory containing the tables.
* We don't worry about the dictionary file and its log
* since db_massage_dict() will take care of that for us.
* We also don't worry about the dictionary log file, since
* db_massage_dict() will checkpoint before it makes any
* changes.
*
* However, we do need to change the NIS+ transaction log.
*/
"Unable to rename directory structure.");
exit(1);
}
"Unable to rename NIS+ transaction log.");
exit(1);
}
/* Now, reinitialize the dictionary */
if (status == 0) {
if (debug)
"Unable to REinitialize %s\n", newdict);
else
newdict);
exit(1);
}
}
rootobj = get_root_object();
if (rootobj)
root_server = 1;
else if (root_server == -1) {
/* if -r option is specified in the command line */
root_server = 0;
"No root object present; running as non-root server.\n");
}
if (root_server) {
if (verbose)
exit(1);
}
if (debug) {
}
if (! status)
if (debug)
else
exit(1);
}
/* initialize the timestamp cache table */
if (debug) {
}
/* Initialize in-core list of directories served by this server */
/*
* If we crashed during update, directory_invalid will contain
* the name of the invalidated directory; otherwise, it will
* be NULL. (map_log sets this)
*/
if (invalid_directory) {
struct ticks t[1];
int drastic_measures = 0;
"directory %s corrupted during update; attempting recovery",
/* forge a ping item; fill just enought for replica_update */
NIS_SUCCESS) {
"recovery for %s failed; couldn't get directory object",
drastic_measures = 1;
} else {
dummy_ping->mtime = 0;
if (!replica_update(dummy_ping)) {
"recovery for %s failed; couldn't resync",
/*
* replica_update will also have invalidated
* this directory, but just to be sure...
*/
drastic_measures = 1;
}
}
if (drastic_measures) {
"Forcing resync by setting update time to 0 for %s",
"You may need to restore from backup");
} else
"Recovery for %s completed", invalid_directory);
}
i = loadLDAPdata();
if (i != 0) {
"Exiting after LDAP data load");
exit(i > 0 ? 0 : 1);
}
/* rpc registration */
if (debug) {
}
if (emulate_yp)
{
}
if (! i)
exit(1);
else if (verbose)
if (emulate_yp) {
if (! i)
exit(1);
else if (verbose)
"NIS service listening on %d transports.", i);
if (! i)
exit(1);
else if (verbose)
}
if (debug) {
}
{
int stat;
(void) pthread_attr_init(&attr);
servloop, 0)) != 0) {
"error %d creating servloop thread; exiting",
stat);
"error %d creating servloop thread; exiting\n",
stat);
exit(1);
}
(void) pthread_attr_destroy(&attr);
"servloop thread started; ready to roll...\n");
svc_run();
/* Not reached */
}
}
int
{
return (1);
}